aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Schneppe <christian@pix-art.de>2016-11-19 23:07:54 +0100
committerChristian Schneppe <christian@pix-art.de>2016-11-19 23:07:54 +0100
commitd783cec97084a12873ca62b5fcd64620056ec01b (patch)
tree65e63d03dd4ab7a834c343ba593c8cd8917620cb
parent969ba45c56adaaa056f04464cd98aec115c9611b (diff)
reformat code
-rw-r--r--art/conversations_baloon.svg624
-rw-r--r--art/conversations_mono.svg442
-rw-r--r--art/conversations_mono_dashed.svg548
-rw-r--r--art/ic_read_indicator.svg75
-rw-r--r--art/ic_received_indicator.svg4
-rw-r--r--art/ic_send_cancel_away.svg65
-rw-r--r--art/ic_send_cancel_dnd.svg65
-rw-r--r--art/ic_send_cancel_offline.svg65
-rw-r--r--art/ic_send_cancel_online.svg65
-rw-r--r--art/ic_send_location_away.svg65
-rw-r--r--art/ic_send_location_dnd.svg65
-rw-r--r--art/ic_send_location_offline.svg65
-rw-r--r--art/ic_send_location_online.svg65
-rw-r--r--art/ic_send_photo_away.svg72
-rw-r--r--art/ic_send_photo_dnd.svg72
-rw-r--r--art/ic_send_photo_offline.svg72
-rw-r--r--art/ic_send_photo_online.svg72
-rw-r--r--art/ic_send_picture_away.svg67
-rw-r--r--art/ic_send_picture_dnd.svg67
-rw-r--r--art/ic_send_picture_offline.svg67
-rw-r--r--art/ic_send_picture_online.svg67
-rw-r--r--art/ic_send_text_away.svg79
-rw-r--r--art/ic_send_text_dnd.svg79
-rw-r--r--art/ic_send_text_offline.svg82
-rw-r--r--art/ic_send_text_online.svg79
-rw-r--r--art/ic_send_video_away.svg72
-rw-r--r--art/ic_send_video_dnd.svg72
-rw-r--r--art/ic_send_video_offline.svg72
-rw-r--r--art/ic_send_video_online.svg72
-rw-r--r--art/ic_send_voice_away.svg65
-rw-r--r--art/ic_send_voice_dnd.svg65
-rw-r--r--art/ic_send_voice_offline.svg65
-rw-r--r--art/ic_send_voice_online.svg65
-rw-r--r--art/ic_verified_fingerprint.svg65
-rw-r--r--art/md_switch_thumb_disable.svg205
-rw-r--r--art/md_switch_thumb_off_normal.svg198
-rw-r--r--art/md_switch_thumb_off_pressed.svg207
-rw-r--r--art/md_switch_thumb_on_normal.svg189
-rw-r--r--art/md_switch_thumb_on_pressed.svg212
-rw-r--r--art/message_bubble_received.svg205
-rw-r--r--art/message_bubble_received_warning.svg205
-rw-r--r--art/message_bubble_received_white.svg205
-rw-r--r--art/message_bubble_sent.svg205
-rw-r--r--art/message_bubble_sent_white.svg205
-rw-r--r--art/omemo_logo.svg420
-rw-r--r--art/play_video.svg73
-rw-r--r--libs/MemorizingTrustManager/AndroidManifest.xml15
-rw-r--r--libs/MemorizingTrustManager/ant.properties3
-rw-r--r--libs/MemorizingTrustManager/build.gradle46
-rw-r--r--libs/MemorizingTrustManager/build.xml5
-rw-r--r--libs/MemorizingTrustManager/example/AndroidManifest.xml21
-rw-r--r--libs/MemorizingTrustManager/example/ant.properties4
-rw-r--r--libs/MemorizingTrustManager/example/build.gradle28
-rw-r--r--libs/MemorizingTrustManager/example/build.xml5
-rw-r--r--libs/MemorizingTrustManager/example/project.properties1
-rw-r--r--libs/MemorizingTrustManager/example/res/layout/mtmexample.xml64
-rw-r--r--libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/JULHandler.java258
-rw-r--r--libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/MTMExample.java232
-rw-r--r--libs/MemorizingTrustManager/project.properties1
-rw-r--r--libs/MemorizingTrustManager/src/de/duenndns/ssl/MTMDecision.java10
-rw-r--r--libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingActivity.java114
-rw-r--r--libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingTrustManager.java1325
-rw-r--r--libs/audiowife/build.gradle8
-rw-r--r--libs/audiowife/src/main/AndroidManifest.xml3
-rw-r--r--libs/audiowife/src/main/java/nl/changer/audiowife/AudioWife.java1
-rw-r--r--libs/audiowife/src/main/res/drawable/gray_border_wo_padding.xml5
-rw-r--r--libs/audiowife/src/main/res/layout/aw_player.xml2
-rw-r--r--libs/emojicon/AndroidManifest.xml7
-rw-r--r--libs/emojicon/lint.xml3
-rw-r--r--libs/emojicon/project.properties1
-rw-r--r--libs/emojicon/res/drawable/ic_emoji_nature_light.xml18
-rw-r--r--libs/emojicon/res/drawable/ic_emoji_objects_light.xml15
-rw-r--r--libs/emojicon/res/drawable/ic_emoji_people_light.xml15
-rw-r--r--libs/emojicon/res/drawable/ic_emoji_places_light.xml15
-rw-r--r--libs/emojicon/res/drawable/ic_emoji_recent_light.xml15
-rw-r--r--libs/emojicon/res/drawable/ic_emoji_symbols_light.xml15
-rw-r--r--libs/emojicon/res/drawable/orca_composer_attach_camera_button.xml3
-rw-r--r--libs/emojicon/res/drawable/orca_composer_attach_location_button.xml10
-rw-r--r--libs/emojicon/res/drawable/orca_composer_attach_photo_button.xml3
-rw-r--r--libs/emojicon/res/drawable/orca_composer_popup_button.xml3
-rw-r--r--libs/emojicon/res/drawable/orca_composer_popup_button_active.xml3
-rw-r--r--libs/emojicon/res/drawable/orca_emoji_backspace_front_button.xml4
-rw-r--r--libs/emojicon/res/drawable/orca_emoji_more_front_button.xml4
-rw-r--r--libs/emojicon/res/drawable/orca_emoji_tab_background.xml3
-rw-r--r--libs/emojicon/res/drawable/orca_emoji_tab_dark_background.xml6
-rw-r--r--libs/emojicon/res/layout/emojicon_grid.xml3
-rw-r--r--libs/emojicon/res/layout/emojicon_item.xml29
-rw-r--r--libs/emojicon/res/layout/emojicons.xml171
-rw-r--r--libs/emojicon/res/values/attrs.xml3
-rw-r--r--libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiAdapter.java21
-rw-r--r--libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconGridView.java68
-rw-r--r--libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconHandler.java2
-rw-r--r--libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconRecents.java4
-rw-r--r--libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconRecentsGridView.java38
-rw-r--r--libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java13
-rw-r--r--libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconsPopup.java740
-rw-r--r--libs/emojicon/src/main/AndroidManifest.xml7
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiAdapter.java21
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconGridView.java68
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconHandler.java2
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecents.java4
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsGridView.java38
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java13
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconsPopup.java740
-rw-r--r--libs/emojicon/src/main/res/drawable/ic_emoji_nature_light.xml18
-rw-r--r--libs/emojicon/src/main/res/drawable/ic_emoji_objects_light.xml15
-rw-r--r--libs/emojicon/src/main/res/drawable/ic_emoji_people_light.xml15
-rw-r--r--libs/emojicon/src/main/res/drawable/ic_emoji_places_light.xml15
-rw-r--r--libs/emojicon/src/main/res/drawable/ic_emoji_recent_light.xml15
-rw-r--r--libs/emojicon/src/main/res/drawable/ic_emoji_symbols_light.xml15
-rw-r--r--libs/emojicon/src/main/res/drawable/orca_composer_attach_camera_button.xml3
-rw-r--r--libs/emojicon/src/main/res/drawable/orca_composer_attach_location_button.xml10
-rw-r--r--libs/emojicon/src/main/res/drawable/orca_composer_attach_photo_button.xml3
-rw-r--r--libs/emojicon/src/main/res/drawable/orca_composer_popup_button.xml3
-rw-r--r--libs/emojicon/src/main/res/drawable/orca_composer_popup_button_active.xml3
-rw-r--r--libs/emojicon/src/main/res/drawable/orca_emoji_backspace_front_button.xml4
-rw-r--r--libs/emojicon/src/main/res/drawable/orca_emoji_more_front_button.xml4
-rw-r--r--libs/emojicon/src/main/res/drawable/orca_emoji_tab_background.xml3
-rw-r--r--libs/emojicon/src/main/res/drawable/orca_emoji_tab_dark_background.xml6
-rw-r--r--libs/emojicon/src/main/res/layout/emojicon_grid.xml3
-rw-r--r--libs/emojicon/src/main/res/layout/emojicon_item.xml29
-rw-r--r--libs/emojicon/src/main/res/layout/emojicons.xml171
-rw-r--r--libs/emojicon/src/main/res/values/attrs.xml3
-rw-r--r--src/free/java/de/pixart/messenger/services/PushManagementService.java26
-rw-r--r--src/main/AndroidManifest.xml188
-rw-r--r--src/main/java/de/pixart/messenger/Config.java2
-rw-r--r--src/main/java/de/pixart/messenger/OmemoActivity.java16
-rw-r--r--src/main/java/de/pixart/messenger/crypto/OtrService.java486
-rw-r--r--src/main/java/de/pixart/messenger/crypto/PgpDecryptionService.java24
-rw-r--r--src/main/java/de/pixart/messenger/crypto/PgpEngine.java536
-rw-r--r--src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java188
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java2090
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/CryptoFailedException.java6
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java6
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/NoSessionsCreatedException.java2
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/OnMessageCreatedCallback.java2
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/SQLiteAxolotlStore.java834
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java446
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java218
-rw-r--r--src/main/java/de/pixart/messenger/crypto/sasl/Anonymous.java30
-rw-r--r--src/main/java/de/pixart/messenger/crypto/sasl/DigestMd5.java140
-rw-r--r--src/main/java/de/pixart/messenger/crypto/sasl/External.java36
-rw-r--r--src/main/java/de/pixart/messenger/crypto/sasl/Plain.java32
-rw-r--r--src/main/java/de/pixart/messenger/crypto/sasl/SaslMechanism.java107
-rw-r--r--src/main/java/de/pixart/messenger/crypto/sasl/ScramSha1.java423
-rw-r--r--src/main/java/de/pixart/messenger/crypto/sasl/Tokenizer.java120
-rw-r--r--src/main/java/de/pixart/messenger/entities/AbstractEntity.java18
-rw-r--r--src/main/java/de/pixart/messenger/entities/Account.java1258
-rw-r--r--src/main/java/de/pixart/messenger/entities/Blockable.java14
-rw-r--r--src/main/java/de/pixart/messenger/entities/Bookmark.java312
-rw-r--r--src/main/java/de/pixart/messenger/entities/Contact.java1059
-rw-r--r--src/main/java/de/pixart/messenger/entities/Conversation.java1888
-rw-r--r--src/main/java/de/pixart/messenger/entities/DownloadableFile.java148
-rw-r--r--src/main/java/de/pixart/messenger/entities/ListItem.java38
-rw-r--r--src/main/java/de/pixart/messenger/entities/Message.java5
-rw-r--r--src/main/java/de/pixart/messenger/entities/MucOptions.java1320
-rw-r--r--src/main/java/de/pixart/messenger/entities/Presence.java170
-rw-r--r--src/main/java/de/pixart/messenger/entities/PresenceTemplate.java134
-rw-r--r--src/main/java/de/pixart/messenger/entities/Presences.java212
-rw-r--r--src/main/java/de/pixart/messenger/entities/Roster.java148
-rw-r--r--src/main/java/de/pixart/messenger/entities/ServiceDiscoveryResult.java648
-rw-r--r--src/main/java/de/pixart/messenger/entities/Transferable.java2
-rw-r--r--src/main/java/de/pixart/messenger/entities/TransferablePlaceholder.java59
-rw-r--r--src/main/java/de/pixart/messenger/generator/AbstractGenerator.java182
-rw-r--r--src/main/java/de/pixart/messenger/generator/IqGenerator.java687
-rw-r--r--src/main/java/de/pixart/messenger/generator/MessageGenerator.java400
-rw-r--r--src/main/java/de/pixart/messenger/generator/PresenceGenerator.java94
-rw-r--r--src/main/java/de/pixart/messenger/http/HttpConnectionManager.java142
-rw-r--r--src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java662
-rw-r--r--src/main/java/de/pixart/messenger/http/HttpUploadConnection.java388
-rw-r--r--src/main/java/de/pixart/messenger/parser/AbstractParser.java130
-rw-r--r--src/main/java/de/pixart/messenger/parser/IqParser.java652
-rw-r--r--src/main/java/de/pixart/messenger/parser/MessageParser.java1218
-rw-r--r--src/main/java/de/pixart/messenger/parser/PresenceParser.java454
-rw-r--r--src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java2510
-rw-r--r--src/main/java/de/pixart/messenger/persistance/FileBackend.java1828
-rw-r--r--src/main/java/de/pixart/messenger/persistance/OnPhoneContactsMerged.java2
-rw-r--r--src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java188
-rw-r--r--src/main/java/de/pixart/messenger/services/AlarmReceiver.java2
-rw-r--r--src/main/java/de/pixart/messenger/services/AvatarService.java802
-rw-r--r--src/main/java/de/pixart/messenger/services/CheckAppVersionService.java2
-rw-r--r--src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java104
-rw-r--r--src/main/java/de/pixart/messenger/services/EventReceiver.java28
-rw-r--r--src/main/java/de/pixart/messenger/services/ExportLogsService.java328
-rw-r--r--src/main/java/de/pixart/messenger/services/MessageArchiveService.java764
-rw-r--r--src/main/java/de/pixart/messenger/services/NotificationService.java8
-rw-r--r--src/main/java/de/pixart/messenger/services/XmppConnectionService.java7565
-rw-r--r--src/main/java/de/pixart/messenger/ui/AboutPreference.java20
-rw-r--r--src/main/java/de/pixart/messenger/ui/AbstractSearchableListItemActivity.java202
-rw-r--r--src/main/java/de/pixart/messenger/ui/BlockContactDialog.java84
-rw-r--r--src/main/java/de/pixart/messenger/ui/BlocklistActivity.java102
-rw-r--r--src/main/java/de/pixart/messenger/ui/ChangePasswordActivity.java194
-rw-r--r--src/main/java/de/pixart/messenger/ui/ChooseContactActivity.java428
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java1238
-rw-r--r--src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java996
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationActivity.java3394
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationFragment.java2630
-rw-r--r--src/main/java/de/pixart/messenger/ui/EditAccountActivity.java2200
-rw-r--r--src/main/java/de/pixart/messenger/ui/EditMessage.java161
-rw-r--r--src/main/java/de/pixart/messenger/ui/EnterJidDialog.java214
-rw-r--r--src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java192
-rw-r--r--src/main/java/de/pixart/messenger/ui/ManageAccountActivity.java688
-rw-r--r--src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java547
-rw-r--r--src/main/java/de/pixart/messenger/ui/RecordingActivity.java254
-rw-r--r--src/main/java/de/pixart/messenger/ui/SetPresenceActivity.java400
-rw-r--r--src/main/java/de/pixart/messenger/ui/SettingsActivity.java468
-rw-r--r--src/main/java/de/pixart/messenger/ui/SettingsFragment.java80
-rw-r--r--src/main/java/de/pixart/messenger/ui/ShareLocationActivity.java90
-rw-r--r--src/main/java/de/pixart/messenger/ui/ShareWithActivity.java602
-rw-r--r--src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java4
-rw-r--r--src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java100
-rw-r--r--src/main/java/de/pixart/messenger/ui/StartConversationActivity.java4
-rw-r--r--src/main/java/de/pixart/messenger/ui/StartUI.java4
-rw-r--r--src/main/java/de/pixart/messenger/ui/TimePreference.java178
-rw-r--r--src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java606
-rw-r--r--src/main/java/de/pixart/messenger/ui/UiCallback.java6
-rw-r--r--src/main/java/de/pixart/messenger/ui/VerifyOTRActivity.java770
-rw-r--r--src/main/java/de/pixart/messenger/ui/WelcomeActivity.java24
-rw-r--r--src/main/java/de/pixart/messenger/ui/XmppActivity.java2410
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java74
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java116
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/KnownHostsAdapter.java108
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java302
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java1874
-rw-r--r--src/main/java/de/pixart/messenger/ui/forms/FormBooleanFieldWrapper.java114
-rw-r--r--src/main/java/de/pixart/messenger/ui/forms/FormFieldFactory.java35
-rw-r--r--src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java152
-rw-r--r--src/main/java/de/pixart/messenger/ui/forms/FormJidSingleFieldWrapper.java54
-rw-r--r--src/main/java/de/pixart/messenger/ui/forms/FormTextFieldWrapper.java138
-rw-r--r--src/main/java/de/pixart/messenger/ui/forms/FormWrapper.java98
-rw-r--r--src/main/java/de/pixart/messenger/ui/widget/ClickableMovementMethod.java56
-rw-r--r--src/main/java/de/pixart/messenger/ui/widget/Switch.java102
-rw-r--r--src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java14
-rw-r--r--src/main/java/de/pixart/messenger/utils/CryptoHelper.java370
-rw-r--r--src/main/java/de/pixart/messenger/utils/DNSHelper.java464
-rw-r--r--src/main/java/de/pixart/messenger/utils/ExceptionHandler.java32
-rw-r--r--src/main/java/de/pixart/messenger/utils/ExceptionHelper.java195
-rw-r--r--src/main/java/de/pixart/messenger/utils/ExifHelper.java4
-rw-r--r--src/main/java/de/pixart/messenger/utils/FileUtils.java282
-rw-r--r--src/main/java/de/pixart/messenger/utils/GeoHelper.java120
-rw-r--r--src/main/java/de/pixart/messenger/utils/MimeUtils.java15
-rw-r--r--src/main/java/de/pixart/messenger/utils/OnPhoneContactsLoadedListener.java2
-rw-r--r--src/main/java/de/pixart/messenger/utils/PRNGFixes.java519
-rw-r--r--src/main/java/de/pixart/messenger/utils/PhoneHelper.java238
-rw-r--r--src/main/java/de/pixart/messenger/utils/SSLSocketHelper.java102
-rw-r--r--src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java78
-rw-r--r--src/main/java/de/pixart/messenger/utils/SocksSocketFactory.java84
-rw-r--r--src/main/java/de/pixart/messenger/utils/UIHelper.java556
-rw-r--r--src/main/java/de/pixart/messenger/utils/XmlHelper.java18
-rw-r--r--src/main/java/de/pixart/messenger/utils/Xmlns.java10
-rw-r--r--src/main/java/de/pixart/messenger/utils/XmppUri.java271
-rw-r--r--src/main/java/de/pixart/messenger/utils/video/MediaController.java2
-rw-r--r--src/main/java/de/pixart/messenger/utils/video/TextureRenderer.java28
-rw-r--r--src/main/java/de/pixart/messenger/utils/video/Track.java6
-rw-r--r--src/main/java/de/pixart/messenger/xml/Element.java348
-rw-r--r--src/main/java/de/pixart/messenger/xml/Tag.java186
-rw-r--r--src/main/java/de/pixart/messenger/xml/TagWriter.java162
-rw-r--r--src/main/java/de/pixart/messenger/xml/XmlReader.java202
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnAdvancedStreamFeaturesLoaded.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnBindListener.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnContactStatusChanged.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnIqPacketReceived.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnKeyStatusUpdated.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnMessageAcknowledged.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnMessagePacketReceived.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnPresencePacketReceived.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnStatusChanged.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnUpdateBlocklist.java16
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/XmppConnection.java3142
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/chatstate/ChatState.java46
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/forms/Data.java174
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/forms/Field.java116
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jid/InvalidJidException.java4
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jid/Jid.java398
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleCandidate.java250
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java2038
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnectionManager.java290
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleInbandTransport.java424
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java374
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleTransport.java12
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/OnFileTransmissionStatusChanged.java4
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/OnJinglePacketReceived.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/OnPrimaryCandidateFound.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/OnTransportConnected.java4
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Content.java242
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/JinglePacket.java150
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Reason.java12
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/pep/Avatar.java172
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractAcknowledgeableStanza.java40
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractStanza.java82
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/IqPacket.java110
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/MessagePacket.java164
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/PresencePacket.java6
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/csi/ActivePacket.java8
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/csi/InactivePacket.java8
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/AckPacket.java10
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/EnablePacket.java10
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/RequestPacket.java8
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/ResumePacket.java12
-rw-r--r--src/main/res/drawable/actionbar_tab_indicator.xml16
-rw-r--r--src/main/res/drawable/es_slidingpane_shadow.xml2
-rw-r--r--src/main/res/drawable/grey.xml2
-rw-r--r--src/main/res/drawable/greybackground.xml2
-rw-r--r--src/main/res/drawable/infocard_border.xml7
-rw-r--r--src/main/res/drawable/message_border.xml2
-rw-r--r--src/main/res/drawable/snackbar.xml2
-rw-r--r--src/main/res/drawable/switch_back_off.xml12
-rw-r--r--src/main/res/drawable/switch_back_on.xml8
-rw-r--r--src/main/res/drawable/switch_thumb.xml14
-rw-r--r--src/main/res/layout-w945dp/fragment_conversations_overview.xml5
-rw-r--r--src/main/res/layout/ab_title.xml4
-rw-r--r--src/main/res/layout/account_row.xml14
-rw-r--r--src/main/res/layout/actionview_search.xml6
-rw-r--r--src/main/res/layout/activity_about.xml2
-rw-r--r--src/main/res/layout/activity_change_password.xml191
-rw-r--r--src/main/res/layout/activity_choose_contact.xml2
-rw-r--r--src/main/res/layout/activity_contact_details.xml14
-rw-r--r--src/main/res/layout/activity_edit_account.xml46
-rw-r--r--src/main/res/layout/activity_fullscreen_message.xml3
-rw-r--r--src/main/res/layout/activity_muc_details.xml146
-rw-r--r--src/main/res/layout/activity_publish_profile_picture.xml173
-rw-r--r--src/main/res/layout/activity_recording.xml8
-rw-r--r--src/main/res/layout/activity_set_presence.xml16
-rw-r--r--src/main/res/layout/activity_share_locaction.xml23
-rw-r--r--src/main/res/layout/activity_show_locaction.xml10
-rw-r--r--src/main/res/layout/activity_start_conversation.xml2
-rw-r--r--src/main/res/layout/activity_trust_keys.xml17
-rw-r--r--src/main/res/layout/activity_updater.xml16
-rw-r--r--src/main/res/layout/activity_verify_otr.xml296
-rw-r--r--src/main/res/layout/captcha.xml5
-rw-r--r--src/main/res/layout/certificate_information.xml41
-rw-r--r--src/main/res/layout/contact.xml17
-rw-r--r--src/main/res/layout/contact_key.xml9
-rw-r--r--src/main/res/layout/conversation_list_row.xml36
-rw-r--r--src/main/res/layout/dialog_block_contact.xml3
-rw-r--r--src/main/res/layout/dialog_show_password.xml3
-rw-r--r--src/main/res/layout/form_boolean.xml11
-rw-r--r--src/main/res/layout/form_text.xml13
-rw-r--r--src/main/res/layout/fragment_conversation.xml6
-rw-r--r--src/main/res/layout/fragment_conversations_overview.xml10
-rw-r--r--src/main/res/layout/join_conference_dialog.xml18
-rw-r--r--src/main/res/layout/keys_card.xml27
-rw-r--r--src/main/res/layout/list_item_tag.xml3
-rw-r--r--src/main/res/layout/magic_create.xml29
-rw-r--r--src/main/res/layout/manage_accounts.xml3
-rw-r--r--src/main/res/layout/message_received.xml18
-rw-r--r--src/main/res/layout/message_sent.xml19
-rw-r--r--src/main/res/layout/message_status.xml25
-rw-r--r--src/main/res/layout/password.xml3
-rw-r--r--src/main/res/layout/presence_template.xml56
-rw-r--r--src/main/res/layout/quickedit.xml4
-rw-r--r--src/main/res/layout/share_with.xml2
-rw-r--r--src/main/res/layout/show_location_infowindow.xml5
-rw-r--r--src/main/res/layout/simple_list_item.xml21
-rw-r--r--src/main/res/menu/attachment_choices.xml14
-rw-r--r--src/main/res/menu/change_presence.xml6
-rw-r--r--src/main/res/menu/choose_contact.xml4
-rw-r--r--src/main/res/menu/conference_context.xml6
-rw-r--r--src/main/res/menu/contact_context.xml10
-rw-r--r--src/main/res/menu/contact_details.xml12
-rw-r--r--src/main/res/menu/conversations.xml52
-rw-r--r--src/main/res/menu/editaccount.xml22
-rw-r--r--src/main/res/menu/encryption_choices.xml12
-rw-r--r--src/main/res/menu/manageaccounts.xml32
-rw-r--r--src/main/res/menu/manageaccounts_context.xml12
-rw-r--r--src/main/res/menu/message_context.xml24
-rw-r--r--src/main/res/menu/muc_details.xml10
-rw-r--r--src/main/res/menu/muc_details_context.xml22
-rw-r--r--src/main/res/menu/omemo_key_context.xml4
-rw-r--r--src/main/res/menu/publish_avatar.xml2
-rw-r--r--src/main/res/menu/select_multiple.xml2
-rw-r--r--src/main/res/menu/share_with.xml4
-rw-r--r--src/main/res/menu/showlocation.xml2
-rw-r--r--src/main/res/menu/start_conversation.xml6
-rw-r--r--src/main/res/menu/verification_choices.xml4
-rw-r--r--src/main/res/menu/verify_otr.xml8
-rw-r--r--src/main/res/values-v21/dimens.xml4
-rw-r--r--src/main/res/values/arrays.xml170
-rw-r--r--src/main/res/values/attrs.xml54
-rw-r--r--src/main/res/values/colors.xml28
-rw-r--r--src/main/res/values/dimens.xml10
-rw-r--r--src/main/res/values/ids.xml4
-rw-r--r--src/main/res/values/strings.xml1336
-rw-r--r--src/main/res/values/themes.xml7
-rw-r--r--src/main/res/xml/file_paths.xml4
-rw-r--r--src/main/res/xml/preferences.xml4
-rw-r--r--src/open/java/de/pixart/messenger/services/PushManagementService.java26
-rw-r--r--src/playstore/AndroidManifest.xml21
-rw-r--r--src/playstore/java/de/pixart/messenger/services/InstanceIdService.java12
-rw-r--r--src/playstore/java/de/pixart/messenger/services/PushManagementService.java188
-rw-r--r--src/playstore/java/de/pixart/messenger/services/PushMessageReceiver.java14
391 files changed, 40018 insertions, 42129 deletions
diff --git a/art/conversations_baloon.svg b/art/conversations_baloon.svg
index 076aa20eb..b7d4a6777 100644
--- a/art/conversations_baloon.svg
+++ b/art/conversations_baloon.svg
@@ -1,288 +1,154 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" 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="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="57mm" height="57mm" viewBox="0 0 201.96849 201.96849"
- id="svg4211" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="conversations_baloon.svg">
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- showgrid="false"
- fit-margin-top="0"
- fit-margin-left="0"
- fit-margin-right="0"
- fit-margin-bottom="0"
- showguides="false"
- inkscape:zoom="2.2196812"
- inkscape:cx="39.109276"
- inkscape:cy="132.27753"
- inkscape:window-width="1600"
- inkscape:window-height="836"
- inkscape:window-x="0"
- inkscape:window-y="27"
- inkscape:window-maximized="1"
- inkscape:current-layer="layer8" />
- <defs
- id="defs4213">
- <linearGradient
- osb:paint="solid"
- id="linearGradient5393">
- <stop
- id="stop5395"
- offset="0"
- style="stop-color:#ffffff;stop-opacity:1;" />
- </linearGradient>
- <clipPath
- id="clipPath4831"
- clipPathUnits="userSpaceOnUse">
- <circle
- style="display:inline;opacity:1;fill:#a00e00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
- id="circle4833"
- cx="883.16943"
- cy="677.19611"
- r="229.80969" />
- </clipPath>
- <clipPath
- id="clipPath4859"
- clipPathUnits="userSpaceOnUse">
- <circle
- style="display:inline;opacity:1;fill:#a00e00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
- id="circle4861"
- cx="883.16943"
- cy="677.19611"
- r="229.80969" />
- </clipPath>
- <clipPath
- id="clipPath5624"
- clipPathUnits="userSpaceOnUse">
- <g
- style="display:inline"
- id="g5626"
- transform="matrix(0.3835576,0,0,0.3835576,-250.60108,-156.11014)">
- <path
- sodipodi:nodetypes="ccsssc"
- inkscape:connector-curvature="0"
- id="path5628"
- d="m 1120.8042,772.36056 -118.0025,103.66316 118.5792,46.01918 c 8.4859,3.29325 19.6524,7.94481 27.2622,0.71376 7.3868,-7.01907 5.6502,-14.13839 3.0935,-24.54095 z"
- style="display:inline;fill:#4caf50;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
- <circle
- transform="matrix(1.0878566,0,0,1.0878566,-57.401992,-79.686482)"
- clip-path="url(#clipPath4859)"
- r="229.80969"
- cy="677.19611"
- cx="883.16943"
- id="circle5630"
- style="display:inline;opacity:1;fill:#4caf50;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
- </g>
- </clipPath>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath10653">
- <g
- style="display:inline"
- id="g10655"
- transform="matrix(0.3835576,0,0,0.3835576,-250.60108,-156.11015)"
- inkscape:export-xdpi="100"
- inkscape:export-ydpi="100">
- <path
- sodipodi:nodetypes="ccsssc"
- inkscape:connector-curvature="0"
- id="path10657"
- d="m 1120.8042,772.36056 -118.0025,103.66316 118.5792,46.01918 c 8.4859,3.29325 19.6524,7.94481 27.2622,0.71376 7.3868,-7.01907 5.6502,-14.13839 3.0935,-24.54095 z"
- style="display:inline;fill:#4caf50;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
- <circle
- transform="matrix(1.0878566,0,0,1.0878566,-57.401992,-79.686482)"
- clip-path="url(#clipPath4859)"
- r="229.80969"
- cy="677.19611"
- cx="883.16943"
- id="circle10659"
- style="display:inline;opacity:1;fill:#4caf50;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
- </g>
- </clipPath>
- <radialGradient
- inkscape:collect="always"
- xlink:href="#linearGradient3913"
- id="radialGradient3883"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(0.68662089,-0.30388739,0.24146012,0.54605188,-300.74233,-264.46964)"
- cx="262.33273"
- cy="945.23846"
- fx="262.33273"
- fy="945.23846"
- r="185.49754" />
- <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>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath5315">
- <g
- inkscape:export-ydpi="100"
- inkscape:export-xdpi="100"
- transform="matrix(0.3835576,0,0,0.3835576,-246.60108,-156.11013)"
- id="g5317"
- style="display:inline;fill:#08183e;fill-opacity:1">
+<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ 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="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="57mm" height="57mm"
+ viewBox="0 0 201.96849 201.96849" id="svg4211" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="conversations_baloon.svg">
+ <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0"
+ showgrid="false" fit-margin-top="0" fit-margin-left="0" fit-margin-right="0"
+ fit-margin-bottom="0" showguides="false" inkscape:zoom="2.2196812" inkscape:cx="39.109276"
+ inkscape:cy="132.27753" inkscape:window-width="1600" inkscape:window-height="836"
+ inkscape:window-x="0" inkscape:window-y="27" inkscape:window-maximized="1"
+ inkscape:current-layer="layer8" />
+ <defs id="defs4213">
+ <linearGradient osb:paint="solid" id="linearGradient5393">
+ <stop id="stop5395" offset="0" style="stop-color:#ffffff;stop-opacity:1;" />
+ </linearGradient>
+ <clipPath id="clipPath4831" clipPathUnits="userSpaceOnUse">
+ <circle
+ style="display:inline;opacity:1;fill:#a00e00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="circle4833" cx="883.16943" cy="677.19611" r="229.80969" />
+ </clipPath>
+ <clipPath id="clipPath4859" clipPathUnits="userSpaceOnUse">
+ <circle
+ style="display:inline;opacity:1;fill:#a00e00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="circle4861" cx="883.16943" cy="677.19611" r="229.80969" />
+ </clipPath>
+ <clipPath id="clipPath5624" clipPathUnits="userSpaceOnUse">
+ <g style="display:inline" id="g5626"
+ transform="matrix(0.3835576,0,0,0.3835576,-250.60108,-156.11014)">
+ <path sodipodi:nodetypes="ccsssc" inkscape:connector-curvature="0" id="path5628"
+ d="m 1120.8042,772.36056 -118.0025,103.66316 118.5792,46.01918 c 8.4859,3.29325 19.6524,7.94481 27.2622,0.71376 7.3868,-7.01907 5.6502,-14.13839 3.0935,-24.54095 z"
+ style="display:inline;fill:#4caf50;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <circle transform="matrix(1.0878566,0,0,1.0878566,-57.401992,-79.686482)"
+ clip-path="url(#clipPath4859)" r="229.80969" cy="677.19611" cx="883.16943"
+ id="circle5630"
+ style="display:inline;opacity:1;fill:#4caf50;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ </clipPath>
+ <clipPath clipPathUnits="userSpaceOnUse" id="clipPath10653">
+ <g style="display:inline" id="g10655"
+ transform="matrix(0.3835576,0,0,0.3835576,-250.60108,-156.11015)"
+ inkscape:export-xdpi="100" inkscape:export-ydpi="100">
+ <path sodipodi:nodetypes="ccsssc" inkscape:connector-curvature="0" id="path10657"
+ d="m 1120.8042,772.36056 -118.0025,103.66316 118.5792,46.01918 c 8.4859,3.29325 19.6524,7.94481 27.2622,0.71376 7.3868,-7.01907 5.6502,-14.13839 3.0935,-24.54095 z"
+ style="display:inline;fill:#4caf50;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <circle transform="matrix(1.0878566,0,0,1.0878566,-57.401992,-79.686482)"
+ clip-path="url(#clipPath4859)" r="229.80969" cy="677.19611" cx="883.16943"
+ id="circle10659"
+ style="display:inline;opacity:1;fill:#4caf50;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ </clipPath>
+ <radialGradient inkscape:collect="always" xlink:href="#linearGradient3913"
+ id="radialGradient3883" gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.68662089,-0.30388739,0.24146012,0.54605188,-300.74233,-264.46964)"
+ cx="262.33273" cy="945.23846" fx="262.33273" fy="945.23846" r="185.49754" />
+ <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>
+ <clipPath clipPathUnits="userSpaceOnUse" id="clipPath5315">
+ <g inkscape:export-ydpi="100" inkscape:export-xdpi="100"
+ transform="matrix(0.3835576,0,0,0.3835576,-246.60108,-156.11013)" id="g5317"
+ style="display:inline;fill:#08183e;fill-opacity:1">
+ <path
+ style="display:inline;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 1120.8042,772.36056 -118.0025,103.66316 118.5792,46.01918 c 8.4859,3.29325 19.6524,7.94481 27.2622,0.71376 7.3868,-7.01907 5.6502,-14.13839 3.0935,-24.54095 z"
+ id="path5319" inkscape:connector-curvature="0" sodipodi:nodetypes="ccsssc" />
+ <circle
+ style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="circle5321" cx="883.16943" cy="677.19611" r="229.80969"
+ clip-path="url(#clipPath4859)"
+ transform="matrix(1.0878566,0,0,1.0878566,-57.401992,-79.686482)" />
+ </g>
+ </clipPath>
+ <clipPath clipPathUnits="userSpaceOnUse" id="clipPath6882">
+ <path inkscape:connector-curvature="0" id="path6884"
+ d="M 99.88867,-2.3837657e-4 A 95.889392,95.889392 0 0 0 4,95.888436 95.889392,95.889392 0 0 0 99.88867,191.77906 95.889392,95.889392 0 0 0 142.59375,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.7793,95.888466 95.889392,95.889392 0 0 0 99.88867,-2.0837657e-4 Z"
+ style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </clipPath>
+ <clipPath clipPathUnits="userSpaceOnUse" id="clipPath6886">
+ <path inkscape:connector-curvature="0" id="path6888"
+ d="M 99.88867,-2.3837657e-4 A 95.889392,95.889392 0 0 0 4,95.888436 95.889392,95.889392 0 0 0 99.88867,191.77906 95.889392,95.889392 0 0 0 142.59375,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.7793,95.888466 95.889392,95.889392 0 0 0 99.88867,-2.0837657e-4 Z"
+ style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </clipPath>
+ <clipPath clipPathUnits="userSpaceOnUse" id="clipPath6890">
+ <path inkscape:connector-curvature="0" id="path6892"
+ d="M 99.88867,-2.3837657e-4 A 95.889392,95.889392 0 0 0 4,95.888436 95.889392,95.889392 0 0 0 99.88867,191.77906 95.889392,95.889392 0 0 0 142.59375,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.7793,95.888466 95.889392,95.889392 0 0 0 99.88867,-2.0837657e-4 Z"
+ style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </clipPath>
+ <clipPath clipPathUnits="userSpaceOnUse" id="clipPath6894">
+ <path inkscape:connector-curvature="0" id="path6896"
+ d="M 99.88867,-2.3837657e-4 A 95.889392,95.889392 0 0 0 4,95.888436 95.889392,95.889392 0 0 0 99.88867,191.77906 95.889392,95.889392 0 0 0 142.59375,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.7793,95.888466 95.889392,95.889392 0 0 0 99.88867,-2.0837657e-4 Z"
+ style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </clipPath>
+ <clipPath clipPathUnits="userSpaceOnUse" id="clipPath6898">
+ <path inkscape:connector-curvature="0" id="path6900"
+ d="M 99.88867,-2.3837657e-4 A 95.889392,95.889392 0 0 0 4,95.888436 95.889392,95.889392 0 0 0 99.88867,191.77906 95.889392,95.889392 0 0 0 142.59375,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.7793,95.888466 95.889392,95.889392 0 0 0 99.88867,-2.0837657e-4 Z"
+ style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </clipPath>
+ <clipPath clipPathUnits="userSpaceOnUse" id="clipPath6902">
+ <path inkscape:connector-curvature="0" id="path6904"
+ d="M 99.88867,-2.3837657e-4 A 95.889392,95.889392 0 0 0 4,95.888436 95.889392,95.889392 0 0 0 99.88867,191.77906 95.889392,95.889392 0 0 0 142.59375,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.7793,95.888466 95.889392,95.889392 0 0 0 99.88867,-2.0837657e-4 Z"
+ style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </clipPath>
+ <clipPath clipPathUnits="userSpaceOnUse" id="clipPath6906">
+ <path inkscape:connector-curvature="0" id="path6908"
+ d="M 99.88867,-2.3837657e-4 A 95.889392,95.889392 0 0 0 4,95.888436 95.889392,95.889392 0 0 0 99.88867,191.77906 95.889392,95.889392 0 0 0 142.59375,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.7793,95.888466 95.889392,95.889392 0 0 0 99.88867,-2.0837657e-4 Z"
+ style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </clipPath>
+ <clipPath clipPathUnits="userSpaceOnUse" id="clipPath6910">
+ <path inkscape:connector-curvature="0" id="path6912"
+ d="M 99.88867,-2.3837657e-4 A 95.889392,95.889392 0 0 0 4,95.888436 95.889392,95.889392 0 0 0 99.88867,191.77906 95.889392,95.889392 0 0 0 142.59375,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.7793,95.888466 95.889392,95.889392 0 0 0 99.88867,-2.0837657e-4 Z"
+ style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </clipPath>
+ <filter inkscape:collect="always" style="color-interpolation-filters:sRGB" id="filter5640"
+ x="-0.012227737" width="1.0244555" y="-0.011780591" height="1.0235612">
+ <feGaussianBlur inkscape:collect="always" stdDeviation="0.9782166"
+ id="feGaussianBlur5642" />
+ </filter>
+ <clipPath clipPathUnits="userSpaceOnUse" id="clipPath5745">
+ <path inkscape:connector-curvature="0" id="path5747"
+ d="M 99.908581,-2.3831968e-4 A 95.889392,95.889392 0 0 0 4.0199102,95.888436 95.889392,95.889392 0 0 0 99.908581,191.77906 95.889392,95.889392 0 0 0 142.61366,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 L 183.8285,142.24002 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.79921,95.888466 95.889392,95.889392 0 0 0 99.908581,-2.0831968e-4 Z"
+ style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </clipPath>
+ </defs>
+ <metadata id="metadata4216">
+ <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:groupmode="layer" id="layer9" inkscape:label="shaddow"
+ transform="translate(-4,2.6816164)" style="display:inline">
+ <path inkscape:connector-curvature="0" id="path6914"
+ d="M 104.88867,0.06226191 A 95.889392,95.889392 0 0 0 8.9999996,95.950936 95.889392,95.889392 0 0 0 104.88867,191.84156 95.889392,95.889392 0 0 0 147.59375,181.76343 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 200.7793,95.950966 95.889392,95.889392 0 0 0 104.88867,0.06229191 Z"
+ style="display:inline;opacity:0.4;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;filter:url(#filter5640)" />
+ </g>
+ <g style="display:inline" inkscape:label="bubble" id="layer4" inkscape:groupmode="layer"
+ transform="translate(-4,2.6816348)">
<path
- style="display:inline;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m 1120.8042,772.36056 -118.0025,103.66316 118.5792,46.01918 c 8.4859,3.29325 19.6524,7.94481 27.2622,0.71376 7.3868,-7.01907 5.6502,-14.13839 3.0935,-24.54095 z"
- id="path5319"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccsssc" />
- <circle
- style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
- id="circle5321"
- cx="883.16943"
- cy="677.19611"
- r="229.80969"
- clip-path="url(#clipPath4859)"
- transform="matrix(1.0878566,0,0,1.0878566,-57.401992,-79.686482)" />
- </g>
- </clipPath>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath6882">
- <path
- inkscape:connector-curvature="0"
- id="path6884"
- d="M 99.88867,-2.3837657e-4 A 95.889392,95.889392 0 0 0 4,95.888436 95.889392,95.889392 0 0 0 99.88867,191.77906 95.889392,95.889392 0 0 0 142.59375,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.7793,95.888466 95.889392,95.889392 0 0 0 99.88867,-2.0837657e-4 Z"
- style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
- </clipPath>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath6886">
- <path
- inkscape:connector-curvature="0"
- id="path6888"
- d="M 99.88867,-2.3837657e-4 A 95.889392,95.889392 0 0 0 4,95.888436 95.889392,95.889392 0 0 0 99.88867,191.77906 95.889392,95.889392 0 0 0 142.59375,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.7793,95.888466 95.889392,95.889392 0 0 0 99.88867,-2.0837657e-4 Z"
- style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
- </clipPath>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath6890">
- <path
- inkscape:connector-curvature="0"
- id="path6892"
- d="M 99.88867,-2.3837657e-4 A 95.889392,95.889392 0 0 0 4,95.888436 95.889392,95.889392 0 0 0 99.88867,191.77906 95.889392,95.889392 0 0 0 142.59375,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.7793,95.888466 95.889392,95.889392 0 0 0 99.88867,-2.0837657e-4 Z"
- style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
- </clipPath>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath6894">
- <path
- inkscape:connector-curvature="0"
- id="path6896"
- d="M 99.88867,-2.3837657e-4 A 95.889392,95.889392 0 0 0 4,95.888436 95.889392,95.889392 0 0 0 99.88867,191.77906 95.889392,95.889392 0 0 0 142.59375,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.7793,95.888466 95.889392,95.889392 0 0 0 99.88867,-2.0837657e-4 Z"
- style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
- </clipPath>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath6898">
- <path
- inkscape:connector-curvature="0"
- id="path6900"
- d="M 99.88867,-2.3837657e-4 A 95.889392,95.889392 0 0 0 4,95.888436 95.889392,95.889392 0 0 0 99.88867,191.77906 95.889392,95.889392 0 0 0 142.59375,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.7793,95.888466 95.889392,95.889392 0 0 0 99.88867,-2.0837657e-4 Z"
- style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
- </clipPath>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath6902">
- <path
- inkscape:connector-curvature="0"
- id="path6904"
- d="M 99.88867,-2.3837657e-4 A 95.889392,95.889392 0 0 0 4,95.888436 95.889392,95.889392 0 0 0 99.88867,191.77906 95.889392,95.889392 0 0 0 142.59375,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.7793,95.888466 95.889392,95.889392 0 0 0 99.88867,-2.0837657e-4 Z"
- style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
- </clipPath>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath6906">
- <path
- inkscape:connector-curvature="0"
- id="path6908"
- d="M 99.88867,-2.3837657e-4 A 95.889392,95.889392 0 0 0 4,95.888436 95.889392,95.889392 0 0 0 99.88867,191.77906 95.889392,95.889392 0 0 0 142.59375,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.7793,95.888466 95.889392,95.889392 0 0 0 99.88867,-2.0837657e-4 Z"
- style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
- </clipPath>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath6910">
- <path
- inkscape:connector-curvature="0"
- id="path6912"
- d="M 99.88867,-2.3837657e-4 A 95.889392,95.889392 0 0 0 4,95.888436 95.889392,95.889392 0 0 0 99.88867,191.77906 95.889392,95.889392 0 0 0 142.59375,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.7793,95.888466 95.889392,95.889392 0 0 0 99.88867,-2.0837657e-4 Z"
- style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
- </clipPath>
- <filter
- inkscape:collect="always"
- style="color-interpolation-filters:sRGB"
- id="filter5640"
- x="-0.012227737"
- width="1.0244555"
- y="-0.011780591"
- height="1.0235612">
- <feGaussianBlur
- inkscape:collect="always"
- stdDeviation="0.9782166"
- id="feGaussianBlur5642" />
- </filter>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath5745">
- <path
- inkscape:connector-curvature="0"
- id="path5747"
- d="M 99.908581,-2.3831968e-4 A 95.889392,95.889392 0 0 0 4.0199102,95.888436 95.889392,95.889392 0 0 0 99.908581,191.77906 95.889392,95.889392 0 0 0 142.61366,181.70093 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 L 183.8285,142.24002 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 195.79921,95.888466 95.889392,95.889392 0 0 0 99.908581,-2.0831968e-4 Z"
- style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
- </clipPath>
- </defs>
- <metadata
- id="metadata4216">
- <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:groupmode="layer"
- id="layer9"
- inkscape:label="shaddow"
- transform="translate(-4,2.6816164)"
- style="display:inline">
- <path
- inkscape:connector-curvature="0"
- id="path6914"
- d="M 104.88867,0.06226191 A 95.889392,95.889392 0 0 0 8.9999996,95.950936 95.889392,95.889392 0 0 0 104.88867,191.84156 95.889392,95.889392 0 0 0 147.59375,181.76343 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 l -11.34766,-46.16797 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 200.7793,95.950966 95.889392,95.889392 0 0 0 104.88867,0.06229191 Z"
- style="display:inline;opacity:0.4;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;filter:url(#filter5640)" />
- </g>
- <g
- style="display:inline"
- inkscape:label="bubble"
- id="layer4"
- inkscape:groupmode="layer"
- transform="translate(-4,2.6816348)">
- <path
- style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
- d="M 104.88867,-1.9377566 A 95.889392,95.889392 0 0 0 8.9999996,93.950918 95.889392,95.889392 0 0 0 104.88867,189.84154 95.889392,95.889392 0 0 0 147.59375,179.76341 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 L 188.80859,140.3025 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 200.7793,93.950948 95.889392,95.889392 0 0 0 104.88867,-1.9377266 Z"
- id="circle6661"
- inkscape:connector-curvature="0" />
- <text
+ style="display:inline;opacity:1;fill:#08183e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 104.88867,-1.9377566 A 95.889392,95.889392 0 0 0 8.9999996,93.950918 95.889392,95.889392 0 0 0 104.88867,189.84154 95.889392,95.889392 0 0 0 147.59375,179.76341 l 0.12695,0.0137 40.79297,15.83204 c 3.25479,1.26313 7.53628,3.04697 10.45508,0.27343 2.83326,-2.69222 2.16811,-5.42213 1.1875,-9.41211 L 188.80859,140.3025 a 95.889392,95.889392 0 0 1 -0.002,0.002 l 0,-0.008 0.002,0.006 A 95.889392,95.889392 0 0 0 200.7793,93.950948 95.889392,95.889392 0 0 0 104.88867,-1.9377266 Z"
+ id="circle6661" inkscape:connector-curvature="0" />
+ <text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:125px;line-height:1000%;font-family:Sans;letter-spacing:-10.89000034px;word-spacing:5px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="85.862968"
@@ -293,123 +159,79 @@
id="tspan6636"
x="85.862968"
y="-55.271603" /></text>
- </g>
- <g
- inkscape:groupmode="layer"
- id="layer8"
- inkscape:label="dotted line"
- style="display:inline"
- transform="translate(-4,2.6816164)">
- <path
- style="opacity:1;fill:#2e417e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- clip-path="url(#clipPath6910)"
- d="m 145.16406,11.183594 -5.13232,9.649402 c -0.77924,1.465076 -0.65974,2.41396 0.66876,3.18097 9.66686,5.488467 18.12303,12.874168 24.86104,21.711122 1.05534,1.616079 2.08054,1.713076 3.67763,0.571565 L 178.04883,40 C 169.45271,27.990203 158.19857,18.128379 145.16406,11.183594 Z"
- id="path7364"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="csccscc"
- transform="translate(4.9999996,-1.9374999)" />
- <path
- style="opacity:1;fill:#2e417e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- clip-path="url(#clipPath6906)"
- d="m 193.80469,75.615234 -9.62713,2.062751 c -2.66266,0.570512 -3.40763,1.172953 -2.90593,3.917433 0.85823,4.714633 1.30424,9.497137 1.33189,14.293254 -0.028,5.578758 -0.62194,11.137108 -1.77093,16.589918 -0.86591,3.23162 0.13682,3.77092 3.16149,4.58138 l 8.98639,2.30136 c 1.98177,-7.66828 3.00584,-15.55255 3.04883,-23.472658 -0.0187,-6.817681 -0.76446,-13.613926 -2.22461,-20.273438 z"
- id="path7366"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="csccccccc"
- transform="translate(4.9999996,-1.9374999)" />
- <path
- style="opacity:1;fill:#2e417e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- clip-path="url(#clipPath6902)"
- d="m 14.264281,102.76512 -10.2076406,0.87943 c 1.2093798,14.83154 5.8540346,29.17808 13.5664056,41.90429 l 8.544301,-5.23239 c 2.394983,-1.46665 1.895406,-3.37834 0.986202,-5.04513 -5.118253,-9.40257 -8.359018,-19.71635 -9.536202,-30.36553 0,-2.09418 -1.881577,-2.26744 -3.353066,-2.14067 z"
- id="path7372"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="sccsccs"
- transform="translate(4.9999996,-1.9374999)" />
- <path
- style="opacity:1;fill:#2e417e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- clip-path="url(#clipPath6898)"
- d="m 51.504371,166.60235 -5.82273,8.50898 c 12.710503,8.71282 27.333669,14.23394 42.630859,16.0957 l 1.220329,-9.90843 c 0.355066,-2.88295 -1.085712,-3.52946 -3.332252,-3.90256 -10.402329,-1.73697 -20.373956,-5.45322 -29.373754,-10.94516 -1.647505,-1.06744 -3.639993,-2.30718 -5.322452,0.15147 z"
- id="path7370"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="sccsccs"
- transform="translate(4.9999996,-1.9374999)" />
- <path
- style="opacity:1;fill:#2e417e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- clip-path="url(#clipPath6894)"
- d="M 32.208984,27.683594 C 21.779177,38.079001 13.883707,50.736882 9.1347656,64.675781 L 19.33617,68.090365 c 1.658147,0.55501 2.832564,-0.120955 3.374272,-1.591979 3.777598,-10.021698 9.470788,-19.210103 16.759132,-27.052307 1.561136,-1.561136 1.567283,-2.960058 0.447507,-4.076606 z"
- id="path7374"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccsccsc"
- transform="translate(4.9999996,-1.9374999)" />
- <path
- style="opacity:1;fill:#2e417e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- clip-path="url(#clipPath6890)"
- d="M 99.888672,-0.25 C 87.701045,-0.2239408 75.630114,2.1252837 64.322266,6.671875 l 3.530435,8.74898 c 1.063314,2.635062 1.616754,3.526314 4.973913,2.352259 8.692057,-3.031338 17.839027,-4.588849 27.062058,-4.599286 5.555828,0 6.486278,0.350026 6.780788,-3.4460223 l 0.74851,-9.64772758 C 104.9135,-0.12857239 102.40179,-0.23868346 99.888672,-0.25 Z"
- id="path7376"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccsccscc"
- transform="translate(4.9999996,-1.9374999)" />
- <path
- style="display:inline;fill:#2e417e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- clip-path="url(#clipPath6886)"
- d="m 138.72416,168.48439 c -4.17634,2.25458 -8.55959,4.09055 -13.0504,5.63418 -1.00363,0.34498 -1.20742,1.18222 -0.8682,2.27372 l 3.44056,11.0706 c 4.92985,-1.53124 9.72799,-3.45808 14.34766,-5.76172 l 0.12695,0.0137 14.0293,5.44532 4.12174,-10.20577 c 0.7548,-1.86894 -0.0184,-2.7016 -1.59462,-3.31324 l -14.72114,-5.71251 c -1.86679,-0.7244 -3.68834,-0.60144 -5.83185,0.55572 z"
- id="path5005"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cssccccsssc"
- transform="translate(4.9999996,-1.9374999)" />
- <path
- style="display:inline;fill:#2e417e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- clip-path="url(#clipPath6882)"
- d="m 186.53125,152.80469 -10.6386,2.70888 c -0.78879,0.20085 -1.67397,1.02386 -1.35494,2.33801 l 9.75918,40.15428 c 8.56713,5.97538 15.30408,3.06731 11.01563,-9.47266 z"
- id="path5071"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cssccc"
- transform="translate(4.9999996,-1.9374999)" />
- </g>
- <g
- style="display:inline"
- inkscape:label="dots"
- id="layer2"
- inkscape:groupmode="layer"
- transform="translate(-4,2.6816348)">
- <g
- inkscape:export-ydpi="100"
- inkscape:export-xdpi="100"
- style="fill:#f5f5f5;fill-opacity:1"
- transform="matrix(0.3835576,0,0,0.3835576,-248.17635,-138.86977)"
- id="g5126">
- <circle
- r="27.299093"
- style="opacity:1;fill:#f5f5f5;fill-opacity:1;fill-rule:evenodd;stroke:none"
- id="path3047-4"
- cx="799.11273"
- cy="609.86285" />
- <circle
- r="27.299093"
- style="opacity:1;fill:#f5f5f5;fill-opacity:1;fill-rule:evenodd;stroke:none"
- id="path3047-1-2"
- cx="918.91962"
- cy="609.86285" />
- <circle
- r="27.299093"
- style="opacity:1;fill:#f5f5f5;fill-opacity:1;fill-rule:evenodd;stroke:none"
- id="path3047-1-8-6"
- cx="1039.0352"
- cy="609.86285" />
</g>
- </g>
- <g
- inkscape:groupmode="layer"
- id="layer1"
- inkscape:label="light"
- style="display:inline"
- transform="translate(-4,2.6816164)">
- <path
- style="display:inline;opacity:0.19211821;fill:url(#radialGradient3883);fill-opacity:1;stroke:none"
- d="m 192.44891,47.715674 c -61.69765,0 -111.704333,49.103472 -111.704333,109.668976 0,12.77573 2.228815,25.0414 6.321575,36.4393 5.069139,0.70557 10.251828,1.06876 15.514978,1.06876 18.80489,0 30.91434,7.28449 47.46533,1.26909 l 54.00234,6.06606 c 5.24363,2.11897 11.63381,1.37954 10.27166,-4.11162 l -14.23663,-57.56735 c 9.15073,-16.06873 12.27539,-34.36633 12.27539,-53.240271 0,-13.72556 -2.63167,-26.842322 -7.42478,-38.909717 -4.09925,-0.447474 -8.2658,-0.683228 -12.48553,-0.683228 z"
- id="path3878"
- inkscape:connector-curvature="0"
- clip-path="url(#clipPath5745)"
- transform="translate(4.9800894,-1.9374999)"
- sodipodi:nodetypes="sscsccccscs" />
- </g>
+ <g inkscape:groupmode="layer" id="layer8" inkscape:label="dotted line" style="display:inline"
+ transform="translate(-4,2.6816164)">
+ <path
+ style="opacity:1;fill:#2e417e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ clip-path="url(#clipPath6910)"
+ d="m 145.16406,11.183594 -5.13232,9.649402 c -0.77924,1.465076 -0.65974,2.41396 0.66876,3.18097 9.66686,5.488467 18.12303,12.874168 24.86104,21.711122 1.05534,1.616079 2.08054,1.713076 3.67763,0.571565 L 178.04883,40 C 169.45271,27.990203 158.19857,18.128379 145.16406,11.183594 Z"
+ id="path7364" inkscape:connector-curvature="0" sodipodi:nodetypes="csccscc"
+ transform="translate(4.9999996,-1.9374999)" />
+ <path
+ style="opacity:1;fill:#2e417e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ clip-path="url(#clipPath6906)"
+ d="m 193.80469,75.615234 -9.62713,2.062751 c -2.66266,0.570512 -3.40763,1.172953 -2.90593,3.917433 0.85823,4.714633 1.30424,9.497137 1.33189,14.293254 -0.028,5.578758 -0.62194,11.137108 -1.77093,16.589918 -0.86591,3.23162 0.13682,3.77092 3.16149,4.58138 l 8.98639,2.30136 c 1.98177,-7.66828 3.00584,-15.55255 3.04883,-23.472658 -0.0187,-6.817681 -0.76446,-13.613926 -2.22461,-20.273438 z"
+ id="path7366" inkscape:connector-curvature="0" sodipodi:nodetypes="csccccccc"
+ transform="translate(4.9999996,-1.9374999)" />
+ <path
+ style="opacity:1;fill:#2e417e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ clip-path="url(#clipPath6902)"
+ d="m 14.264281,102.76512 -10.2076406,0.87943 c 1.2093798,14.83154 5.8540346,29.17808 13.5664056,41.90429 l 8.544301,-5.23239 c 2.394983,-1.46665 1.895406,-3.37834 0.986202,-5.04513 -5.118253,-9.40257 -8.359018,-19.71635 -9.536202,-30.36553 0,-2.09418 -1.881577,-2.26744 -3.353066,-2.14067 z"
+ id="path7372" inkscape:connector-curvature="0" sodipodi:nodetypes="sccsccs"
+ transform="translate(4.9999996,-1.9374999)" />
+ <path
+ style="opacity:1;fill:#2e417e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ clip-path="url(#clipPath6898)"
+ d="m 51.504371,166.60235 -5.82273,8.50898 c 12.710503,8.71282 27.333669,14.23394 42.630859,16.0957 l 1.220329,-9.90843 c 0.355066,-2.88295 -1.085712,-3.52946 -3.332252,-3.90256 -10.402329,-1.73697 -20.373956,-5.45322 -29.373754,-10.94516 -1.647505,-1.06744 -3.639993,-2.30718 -5.322452,0.15147 z"
+ id="path7370" inkscape:connector-curvature="0" sodipodi:nodetypes="sccsccs"
+ transform="translate(4.9999996,-1.9374999)" />
+ <path
+ style="opacity:1;fill:#2e417e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ clip-path="url(#clipPath6894)"
+ d="M 32.208984,27.683594 C 21.779177,38.079001 13.883707,50.736882 9.1347656,64.675781 L 19.33617,68.090365 c 1.658147,0.55501 2.832564,-0.120955 3.374272,-1.591979 3.777598,-10.021698 9.470788,-19.210103 16.759132,-27.052307 1.561136,-1.561136 1.567283,-2.960058 0.447507,-4.076606 z"
+ id="path7374" inkscape:connector-curvature="0" sodipodi:nodetypes="ccsccsc"
+ transform="translate(4.9999996,-1.9374999)" />
+ <path
+ style="opacity:1;fill:#2e417e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ clip-path="url(#clipPath6890)"
+ d="M 99.888672,-0.25 C 87.701045,-0.2239408 75.630114,2.1252837 64.322266,6.671875 l 3.530435,8.74898 c 1.063314,2.635062 1.616754,3.526314 4.973913,2.352259 8.692057,-3.031338 17.839027,-4.588849 27.062058,-4.599286 5.555828,0 6.486278,0.350026 6.780788,-3.4460223 l 0.74851,-9.64772758 C 104.9135,-0.12857239 102.40179,-0.23868346 99.888672,-0.25 Z"
+ id="path7376" inkscape:connector-curvature="0" sodipodi:nodetypes="ccsccscc"
+ transform="translate(4.9999996,-1.9374999)" />
+ <path
+ style="display:inline;fill:#2e417e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ clip-path="url(#clipPath6886)"
+ d="m 138.72416,168.48439 c -4.17634,2.25458 -8.55959,4.09055 -13.0504,5.63418 -1.00363,0.34498 -1.20742,1.18222 -0.8682,2.27372 l 3.44056,11.0706 c 4.92985,-1.53124 9.72799,-3.45808 14.34766,-5.76172 l 0.12695,0.0137 14.0293,5.44532 4.12174,-10.20577 c 0.7548,-1.86894 -0.0184,-2.7016 -1.59462,-3.31324 l -14.72114,-5.71251 c -1.86679,-0.7244 -3.68834,-0.60144 -5.83185,0.55572 z"
+ id="path5005" inkscape:connector-curvature="0" sodipodi:nodetypes="cssccccsssc"
+ transform="translate(4.9999996,-1.9374999)" />
+ <path
+ style="display:inline;fill:#2e417e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ clip-path="url(#clipPath6882)"
+ d="m 186.53125,152.80469 -10.6386,2.70888 c -0.78879,0.20085 -1.67397,1.02386 -1.35494,2.33801 l 9.75918,40.15428 c 8.56713,5.97538 15.30408,3.06731 11.01563,-9.47266 z"
+ id="path5071" inkscape:connector-curvature="0" sodipodi:nodetypes="cssccc"
+ transform="translate(4.9999996,-1.9374999)" />
+ </g>
+ <g style="display:inline" inkscape:label="dots" id="layer2" inkscape:groupmode="layer"
+ transform="translate(-4,2.6816348)">
+ <g inkscape:export-ydpi="100" inkscape:export-xdpi="100" style="fill:#f5f5f5;fill-opacity:1"
+ transform="matrix(0.3835576,0,0,0.3835576,-248.17635,-138.86977)" id="g5126">
+ <circle r="27.299093"
+ style="opacity:1;fill:#f5f5f5;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path3047-4" cx="799.11273" cy="609.86285" />
+ <circle r="27.299093"
+ style="opacity:1;fill:#f5f5f5;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path3047-1-2" cx="918.91962" cy="609.86285" />
+ <circle r="27.299093"
+ style="opacity:1;fill:#f5f5f5;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path3047-1-8-6" cx="1039.0352" cy="609.86285" />
+ </g>
+ </g>
+ <g inkscape:groupmode="layer" id="layer1" inkscape:label="light" style="display:inline"
+ transform="translate(-4,2.6816164)">
+ <path
+ style="display:inline;opacity:0.19211821;fill:url(#radialGradient3883);fill-opacity:1;stroke:none"
+ d="m 192.44891,47.715674 c -61.69765,0 -111.704333,49.103472 -111.704333,109.668976 0,12.77573 2.228815,25.0414 6.321575,36.4393 5.069139,0.70557 10.251828,1.06876 15.514978,1.06876 18.80489,0 30.91434,7.28449 47.46533,1.26909 l 54.00234,6.06606 c 5.24363,2.11897 11.63381,1.37954 10.27166,-4.11162 l -14.23663,-57.56735 c 9.15073,-16.06873 12.27539,-34.36633 12.27539,-53.240271 0,-13.72556 -2.63167,-26.842322 -7.42478,-38.909717 -4.09925,-0.447474 -8.2658,-0.683228 -12.48553,-0.683228 z"
+ id="path3878" inkscape:connector-curvature="0" clip-path="url(#clipPath5745)"
+ transform="translate(4.9800894,-1.9374999)" sodipodi:nodetypes="sscsccccscs" />
+ </g>
</svg>
diff --git a/art/conversations_mono.svg b/art/conversations_mono.svg
index 93ba8a1ba..c1428ea29 100644
--- a/art/conversations_mono.svg
+++ b/art/conversations_mono.svg
@@ -1,325 +1,129 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?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="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+<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="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.48.5 r10040" sodipodi:docname="conversations_mono.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.4430075,-0.63865195,0.50745433,1.1475866,-594.40824,44.803037)"
- 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" />
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="1.4142136"
- inkscape:cx="387.25645"
- inkscape:cy="294.41185"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="false"
- inkscape:window-width="2560"
- inkscape:window-height="1020"
- inkscape:window-x="0"
- inkscape:window-y="27"
- inkscape:window-maximized="1"
- showguides="false"
- 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
- style="fill:#ffffff;fill-opacity:1;stroke:none"
- d="M 253.21875 17.71875 C 126.14405 17.71875 22.46875 118.88367 22.46875 243.75 C 22.46875 368.61632 126.13807 469.84375 253.21875 469.84375 C 292.73931 469.84375 323.2163 461.73599 358 449.09375 L 468.46875 493.625 A 14.555609 14.562046 0 0 0 488.0625 476.625 L 458.125 355.65625 C 477.35631 321.88611 483.9375 283.41561 483.9375 243.75 C 483.9375 118.88673 380.29349 17.71875 253.21875 17.71875 z M 143.84375 222 C 157.65087 222 168.84375 233.19288 168.84375 247 C 168.84375 260.80712 157.65087 272 143.84375 272 C 130.03663 272 118.84375 260.80712 118.84375 247 C 118.84375 233.19288 130.03663 222 143.84375 222 z M 253.5625 222 C 267.36962 222 278.5625 233.19288 278.5625 247 C 278.5625 260.80712 267.36962 272 253.5625 272 C 239.75538 272 228.5625 260.80712 228.5625 247 C 228.5625 233.19288 239.75538 222 253.5625 222 z M 363.5625 222 C 377.36962 222 388.5625 233.19288 388.5625 247 C 388.5625 260.80712 377.36962 272 363.5625 272 C 349.75538 272 338.5625 260.80712 338.5625 247 C 338.5625 233.19288 349.75538 222 363.5625 222 z "
- transform="translate(0,540.36218)"
- id="path3868" />
- <path
- sodipodi:nodetypes="ccsssscc"
- inkscape:connector-curvature="0"
- id="path3845"
- d="M 478.64112,1025.218 447.36049,898.60749 c 19.89028,-31.99834 26.74288,-69.57172 26.74288,-109.76189 0,-116.81686 -96.79943,-211.48385 -216.18374,-211.48385 -119.38425,0 -216.183656,94.66699 -216.183656,211.48385 0,116.81685 96.799406,211.5536 216.183656,211.5536 39.63617,0 68.58847,-8.14219 105.19417,-21.76075 z"
- style="opacity:0;fill:none;stroke:#000000;stroke-width:23.55835724;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:94.23343197, 94.23343197;stroke-dashoffset:0" />
- </g>
- <g
- inkscape:groupmode="layer"
- id="layer4"
- inkscape:label="Dots" />
+ <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.4430075,-0.63865195,0.50745433,1.1475866,-594.40824,44.803037)"
+ 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" />
+ </defs>
+ <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0"
+ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="1.4142136"
+ inkscape:cx="387.25645" inkscape:cy="294.41185" inkscape:document-units="px"
+ inkscape:current-layer="layer1" showgrid="false" inkscape:window-width="2560"
+ inkscape:window-height="1020" inkscape:window-x="0" inkscape:window-y="27"
+ inkscape:window-maximized="1" showguides="false" 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 style="fill:#ffffff;fill-opacity:1;stroke:none"
+ d="M 253.21875 17.71875 C 126.14405 17.71875 22.46875 118.88367 22.46875 243.75 C 22.46875 368.61632 126.13807 469.84375 253.21875 469.84375 C 292.73931 469.84375 323.2163 461.73599 358 449.09375 L 468.46875 493.625 A 14.555609 14.562046 0 0 0 488.0625 476.625 L 458.125 355.65625 C 477.35631 321.88611 483.9375 283.41561 483.9375 243.75 C 483.9375 118.88673 380.29349 17.71875 253.21875 17.71875 z M 143.84375 222 C 157.65087 222 168.84375 233.19288 168.84375 247 C 168.84375 260.80712 157.65087 272 143.84375 272 C 130.03663 272 118.84375 260.80712 118.84375 247 C 118.84375 233.19288 130.03663 222 143.84375 222 z M 253.5625 222 C 267.36962 222 278.5625 233.19288 278.5625 247 C 278.5625 260.80712 267.36962 272 253.5625 272 C 239.75538 272 228.5625 260.80712 228.5625 247 C 228.5625 233.19288 239.75538 222 253.5625 222 z M 363.5625 222 C 377.36962 222 388.5625 233.19288 388.5625 247 C 388.5625 260.80712 377.36962 272 363.5625 272 C 349.75538 272 338.5625 260.80712 338.5625 247 C 338.5625 233.19288 349.75538 222 363.5625 222 z "
+ transform="translate(0,540.36218)" id="path3868" />
+ <path sodipodi:nodetypes="ccsssscc" inkscape:connector-curvature="0" id="path3845"
+ d="M 478.64112,1025.218 447.36049,898.60749 c 19.89028,-31.99834 26.74288,-69.57172 26.74288,-109.76189 0,-116.81686 -96.79943,-211.48385 -216.18374,-211.48385 -119.38425,0 -216.183656,94.66699 -216.183656,211.48385 0,116.81685 96.799406,211.5536 216.183656,211.5536 39.63617,0 68.58847,-8.14219 105.19417,-21.76075 z"
+ style="opacity:0;fill:none;stroke:#000000;stroke-width:23.55835724;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:94.23343197, 94.23343197;stroke-dashoffset:0" />
+ </g>
+ <g inkscape:groupmode="layer" id="layer4" inkscape:label="Dots" />
</svg>
diff --git a/art/conversations_mono_dashed.svg b/art/conversations_mono_dashed.svg
index cce980890..985964a3f 100644
--- a/art/conversations_mono_dashed.svg
+++ b/art/conversations_mono_dashed.svg
@@ -1,388 +1,176 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?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="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+<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="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.48.5 r10040" sodipodi:docname="conversations_mono.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.4430075,-0.63865195,0.50745433,1.1475866,-594.40824,44.803037)"
- 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" />
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath3991">
- <g
- transform="translate(-1.6219224,-0.468756)"
- id="g3993"
- style="fill:none;display:inline"
- mask="none">
- <g
- id="g3995">
- <g
- id="g3997">
- <path
- inkscape:connector-curvature="0"
- transform="matrix(0.94594594,0,0,1.25,201.38502,-54.607132)"
- d="m 200,241.28571 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -116.285715,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -115.986612,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z"
- style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
- id="path3999" />
- </g>
- <g
- id="use4001">
- <path
- inkscape:connector-curvature="0"
- transform="matrix(0.94594594,0,0,1.25,201.38502,-54.607132)"
- d="m 200,241.28571 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -116.285715,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -115.986612,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z"
- style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
- id="path4086" />
- </g>
- <g
- id="use4003">
- <path
- id="path4094"
- style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
- d="m 200,241.28571 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -116.285715,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -115.986612,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z"
- transform="matrix(0.94594594,0,0,1.25,201.38502,-54.607132)"
- inkscape:connector-curvature="0" />
- </g>
- <g
- id="use4005">
- <path
- inkscape:connector-curvature="0"
- transform="matrix(0.94594594,0,0,1.25,201.38502,-54.607132)"
- d="m 200,241.28571 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -116.285715,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -115.986612,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z"
- style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
- id="path4102" />
- </g>
- <g
- id="use4007">
- <path
- id="path4110"
- style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
- d="m 200,241.28571 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -116.285715,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -115.986612,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z"
- transform="matrix(0.94594594,0,0,1.25,201.38502,-54.607132)"
- inkscape:connector-curvature="0" />
- </g>
+ <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.4430075,-0.63865195,0.50745433,1.1475866,-594.40824,44.803037)"
+ 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" />
+ <clipPath clipPathUnits="userSpaceOnUse" id="clipPath3991">
+ <g transform="translate(-1.6219224,-0.468756)" id="g3993"
+ style="fill:none;display:inline" mask="none">
+ <g id="g3995">
+ <g id="g3997">
+ <path inkscape:connector-curvature="0"
+ transform="matrix(0.94594594,0,0,1.25,201.38502,-54.607132)"
+ d="m 200,241.28571 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -116.285715,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -115.986612,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path3999" />
+ </g>
+ <g id="use4001">
+ <path inkscape:connector-curvature="0"
+ transform="matrix(0.94594594,0,0,1.25,201.38502,-54.607132)"
+ d="m 200,241.28571 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -116.285715,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -115.986612,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path4086" />
+ </g>
+ <g id="use4003">
+ <path id="path4094"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 200,241.28571 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -116.285715,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -115.986612,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z"
+ transform="matrix(0.94594594,0,0,1.25,201.38502,-54.607132)"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g id="use4005">
+ <path inkscape:connector-curvature="0"
+ transform="matrix(0.94594594,0,0,1.25,201.38502,-54.607132)"
+ d="m 200,241.28571 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -116.285715,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -115.986612,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path4102" />
+ </g>
+ <g id="use4007">
+ <path id="path4110"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 200,241.28571 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -116.285715,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z m -115.986612,0 c 0,11.04569 -11.83248,20 -26.42857,20 -14.5961,0 -26.42858,-8.95431 -26.42858,-20 0,-11.0457 11.83248,-20 26.42858,-20 14.59609,0 26.42857,8.9543 26.42857,20 z"
+ transform="matrix(0.94594594,0,0,1.25,201.38502,-54.607132)"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ </g>
+ </clipPath>
+ </defs>
+ <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0"
+ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="1.28"
+ inkscape:cx="328.75169" inkscape:cy="221.91901" inkscape:document-units="px"
+ inkscape:current-layer="layer1" showgrid="false" inkscape:window-width="2560"
+ inkscape:window-height="1020" inkscape:window-x="0" inkscape:window-y="27"
+ inkscape:window-maximized="1" showguides="false" 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;opacity:1">
+ <path inkscape:connector-curvature="0" id="path3855"
+ d="m 253.18246,561.05889 c -5.38379,-0.002 -10.7413,0.0871 -12.77023,0.22089 -16.80965,1.10727 -29.68729,3.05317 -44.38296,6.77453 -5.64799,1.43026 -9.96811,2.69833 -15.19914,4.41816 -3.34052,1.09828 -8.41764,2.85364 -8.68521,3.01909 -0.082,0.0507 3.32705,9.32907 7.98597,21.79631 0.0466,0.12496 0.17057,0.13832 0.33123,0.0736 1.11322,-0.44815 6.45699,-2.29745 8.94283,-3.09273 21.39718,-6.84518 43.95735,-10.19531 66.31683,-9.86723 3.14874,0.0461 7.13319,0.15915 8.83245,0.25775 1.69921,0.0987 3.12161,0.15378 3.16493,0.11037 0.0685,-0.0684 1.53237,-23.21444 1.47209,-23.26905 -0.0122,-0.0117 -1.41064,-0.10943 -3.12817,-0.22089 -2.0869,-0.13546 -7.49683,-0.21852 -12.88062,-0.22088 z m 110.81021,28.16581 c -0.10125,0.10911 -11.15095,20.28455 -11.15095,20.36043 0,0.0184 0.50701,0.31641 1.14084,0.66267 8.38104,4.57856 17.56037,10.63803 25.90897,17.04889 3.23527,2.48434 6.34578,5.02146 9.23674,7.54561 4.2123,3.67784 8.41256,7.68117 12.42754,11.81905 6.38417,6.5796 12.29989,13.4994 17.05071,19.99177 0.65274,0.89212 0.79099,1.01157 1.03047,0.84681 1.13402,-0.7802 18.39736,-13.76959 18.40089,-13.84358 0.005,-0.1 -3.33561,-4.52525 -4.74744,-6.29593 -5.64395,-7.07831 -10.59769,-12.59166 -17.26005,-19.25582 -8.26499,-8.26722 -16.14264,-15.121 -25.02569,-21.71453 -2.5667,-1.90515 -5.21733,-3.78858 -7.9855,-5.6781 -6.60132,-4.50598 -18.71149,-11.82683 -19.02653,-11.48727 z m -274.762206,39.94759 -2.428914,2.57732 c -21.579098,22.69359 -38.068397,49.23025 -48.467963,78.05431 -0.50904,1.41091 -0.957247,2.67589 -0.993643,2.83498 -0.04781,0.20904 2.956962,1.31003 10.78292,3.93954 5.956638,2.00143 10.92488,3.63791 11.040538,3.64502 0.115645,0.007 0.916879,-1.94564 1.803285,-4.34458 9.098432,-24.62317 23.187184,-47.25662 41.659643,-66.93523 l 3.05453,-3.27681 -8.206806,-8.24726 z m 388.222146,115.167 -9.89972,1.91451 c -5.44839,1.06994 -10.56998,2.07187 -11.40857,2.20908 -1.04711,0.17137 -1.54564,0.35016 -1.54564,0.55228 0,0.16187 0.23325,1.63204 0.51522,3.2768 2.70275,15.76547 3.28356,34.63258 1.69287,55.26394 -0.7281,9.44363 -2.34823,21.04449 -3.90099,28.01857 -0.23345,1.0486 -0.37949,1.97667 -0.33118,2.02499 0.0483,0.0483 5.12585,1.1561 11.29809,2.46683 6.17232,1.31067 11.30751,2.37915 11.3718,2.39315 0.0641,0.014 0.45734,-1.73307 0.88322,-3.90272 3.35867,-17.11028 4.82653,-33.18977 4.85786,-53.27572 0.0219,-14.08945 -0.79161,-24.35571 -2.87056,-36.96537 z m -427.268845,64.06345 -0.772838,0.11039 c -0.421858,0.0612 -5.59823,0.67716 -11.482161,1.36226 -5.883927,0.68512 -10.759171,1.30169 -10.819725,1.36228 -0.141991,0.142 0.252313,2.91986 1.140854,8.32086 4.869392,29.59836 15.038358,56.25732 31.539139,82.61977 0.450701,0.72005 0.931445,1.27763 1.06725,1.25182 0.361709,-0.0685 19.423106,-12.2036 19.431349,-12.3709 0.0036,-0.0779 -0.796734,-1.40341 -1.766487,-2.94542 -4.266677,-6.78447 -9.935035,-17.45299 -13.064635,-24.5945 -7.52905,-17.18062 -12.488823,-34.71382 -15.051936,-53.27567 z M 462.40066,926.44149 c -0.46898,0.009 -22.08567,5.38002 -22.2283,5.52269 -0.098,0.0981 22.04129,90.06142 22.37549,91.01382 0.40286,1.1482 3.73284,10.5298 13.56323,8.9156 10.95786,-2.3434 9.8458,-14.6677 8.99628,-14.4751 -0.11284,0.025 -5.02627,-20.61508 -11.18774,-45.58033 -8.79763,-35.64656 -11.26829,-45.40142 -11.51896,-45.39668 z M 143.91794,953.1714 c -0.40943,0.0131 -1.21588,1.3276 -6.29312,9.64634 -3.31435,5.43031 -6.03549,9.92123 -6.03549,9.9777 0,0.13674 3.42858,2.19027 7.06593,4.27089 22.35182,12.78549 47.08561,21.82095 72.42596,26.43487 3.59043,0.654 5.67261,1.0064 11.04051,1.804 0.69401,0.1031 1.36954,0.2073 1.50889,0.2212 0.31484,0.031 0.24386,0.6279 1.87691,-11.45018 0.75094,-5.5542 1.40492,-10.43428 1.47207,-10.86126 0.11781,-0.74877 0.0863,-0.77677 -0.58887,-0.88362 -0.38487,-0.0607 -2.68651,-0.4127 -5.11543,-0.77322 -22.30454,-3.3101 -45.25895,-10.90321 -65.32317,-21.57538 -3.55401,-1.89038 -10.16752,-5.64292 -11.85018,-6.73769 -0.0531,-0.0345 -0.12551,-0.0755 -0.18401,-0.0737 z m 214.22322,9.09404 c -1.98095,0.13013 -4.60205,1.01767 -10.4517,3.12954 -11.29964,4.0795 -24.13159,8.26507 -29.91986,9.75681 -0.83741,0.21582 -1.5445,0.50692 -1.54566,0.62592 -0.002,0.21503 5.72469,22.22722 5.81468,22.34847 0.0552,0.075 6.34708,-1.70267 10.2677,-2.90858 5.09669,-1.5677 13.67295,-4.44246 19.79936,-6.62722 l 6.07232,-2.17225 22.30187,8.98356 c 18.14341,7.31671 22.30098,8.95081 22.41231,8.68881 0.9061,-2.1322 8.61707,-21.32757 8.57483,-21.35419 -0.38803,-0.24492 -49.13929,-19.77601 -49.90327,-19.99221 -1.25781,-0.35596 -2.23399,-0.55669 -3.42258,-0.47866 z"
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:5.88958930999999986;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ sodipodi:nodetypes="ccssccssscccccccssssscccsssscccscsssccccccssssssssssccccscssccsscccsscsscsssssccsccsscsscsccscccccssc" />
+ <g style="display:inline" id="g3958" transform="translate(-1.55942,537.04967)" />
+ <g transform="scale(1.0022296,0.99777536)"
+ style="font-size:127.71524811000000454px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Segoe UI;-inkscape-font-specification:Segoe UI;opacity:1"
+ id="text4120">
+ <path style="font-size:3192.88085937999903763px;fill:#ffffff"
+ d="M 251.6875 68.34375 C 203.77053 68.344069 162.62474 85.959793 128.25 121.21875 C 94.91648 155.44121 78.249825 196.39023 78.25 244.09375 C 78.249825 291.79755 94.91648 332.77783 128.25 367 C 162.62474 401.22221 203.77053 418.34372 251.6875 418.34375 C 300.64542 418.34372 342.31207 401.22221 376.6875 367 C 411.06199 332.77783 428.24948 291.79755 428.25 244.09375 C 428.24948 196.39023 411.06199 155.44121 376.6875 121.21875 C 342.31207 85.959793 300.64542 68.344069 251.6875 68.34375 z M 150 224.0625 C 156.11974 224.06254 161.32807 226.26045 165.625 230.6875 C 169.92181 234.9844 172.06243 240.13544 172.0625 246.125 C 172.06243 252.1146 169.92181 257.26563 165.625 261.5625 C 161.32807 265.85937 156.11974 268 150 268 C 144.01038 268 138.85934 265.85937 134.5625 261.5625 C 130.39581 257.26563 128.31248 252.1146 128.3125 246.125 C 128.31248 240.13544 130.39581 234.9844 134.5625 230.6875 C 138.85934 226.26045 144.01038 224.06254 150 224.0625 z M 254.5 224.0625 C 260.61974 224.06254 265.82807 226.26045 270.125 230.6875 C 274.42181 234.9844 276.56243 240.13544 276.5625 246.125 C 276.56243 252.1146 274.42181 257.26563 270.125 261.5625 C 265.82807 265.85937 260.61974 268 254.5 268 C 248.51038 268 243.35934 265.85937 239.0625 261.5625 C 234.89581 257.26563 232.81248 252.1146 232.8125 246.125 C 232.81248 240.13544 234.89581 234.9844 239.0625 230.6875 C 243.35934 226.26045 248.51038 224.06254 254.5 224.0625 z M 357.34375 224.0625 C 363.46349 224.06254 368.67182 226.26045 372.96875 230.6875 C 377.26556 234.9844 379.40618 240.13544 379.40625 246.125 C 379.40618 252.1146 377.26556 257.26563 372.96875 261.5625 C 368.67182 265.85937 363.46349 268 357.34375 268 C 351.35413 268 346.20309 265.85937 341.90625 261.5625 C 337.73956 257.26563 335.65623 252.1146 335.65625 246.125 C 335.65623 240.13544 337.73956 234.9844 341.90625 230.6875 C 346.20309 226.26045 351.35413 224.06254 357.34375 224.0625 z "
+ transform="matrix(0.99777536,0,0,1.0022296,0,541.56697)" id="path4171" />
</g>
- </g>
- </clipPath>
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="1.28"
- inkscape:cx="328.75169"
- inkscape:cy="221.91901"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="false"
- inkscape:window-width="2560"
- inkscape:window-height="1020"
- inkscape:window-x="0"
- inkscape:window-y="27"
- inkscape:window-maximized="1"
- showguides="false"
- 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;opacity:1">
- <path
- inkscape:connector-curvature="0"
- id="path3855"
- d="m 253.18246,561.05889 c -5.38379,-0.002 -10.7413,0.0871 -12.77023,0.22089 -16.80965,1.10727 -29.68729,3.05317 -44.38296,6.77453 -5.64799,1.43026 -9.96811,2.69833 -15.19914,4.41816 -3.34052,1.09828 -8.41764,2.85364 -8.68521,3.01909 -0.082,0.0507 3.32705,9.32907 7.98597,21.79631 0.0466,0.12496 0.17057,0.13832 0.33123,0.0736 1.11322,-0.44815 6.45699,-2.29745 8.94283,-3.09273 21.39718,-6.84518 43.95735,-10.19531 66.31683,-9.86723 3.14874,0.0461 7.13319,0.15915 8.83245,0.25775 1.69921,0.0987 3.12161,0.15378 3.16493,0.11037 0.0685,-0.0684 1.53237,-23.21444 1.47209,-23.26905 -0.0122,-0.0117 -1.41064,-0.10943 -3.12817,-0.22089 -2.0869,-0.13546 -7.49683,-0.21852 -12.88062,-0.22088 z m 110.81021,28.16581 c -0.10125,0.10911 -11.15095,20.28455 -11.15095,20.36043 0,0.0184 0.50701,0.31641 1.14084,0.66267 8.38104,4.57856 17.56037,10.63803 25.90897,17.04889 3.23527,2.48434 6.34578,5.02146 9.23674,7.54561 4.2123,3.67784 8.41256,7.68117 12.42754,11.81905 6.38417,6.5796 12.29989,13.4994 17.05071,19.99177 0.65274,0.89212 0.79099,1.01157 1.03047,0.84681 1.13402,-0.7802 18.39736,-13.76959 18.40089,-13.84358 0.005,-0.1 -3.33561,-4.52525 -4.74744,-6.29593 -5.64395,-7.07831 -10.59769,-12.59166 -17.26005,-19.25582 -8.26499,-8.26722 -16.14264,-15.121 -25.02569,-21.71453 -2.5667,-1.90515 -5.21733,-3.78858 -7.9855,-5.6781 -6.60132,-4.50598 -18.71149,-11.82683 -19.02653,-11.48727 z m -274.762206,39.94759 -2.428914,2.57732 c -21.579098,22.69359 -38.068397,49.23025 -48.467963,78.05431 -0.50904,1.41091 -0.957247,2.67589 -0.993643,2.83498 -0.04781,0.20904 2.956962,1.31003 10.78292,3.93954 5.956638,2.00143 10.92488,3.63791 11.040538,3.64502 0.115645,0.007 0.916879,-1.94564 1.803285,-4.34458 9.098432,-24.62317 23.187184,-47.25662 41.659643,-66.93523 l 3.05453,-3.27681 -8.206806,-8.24726 z m 388.222146,115.167 -9.89972,1.91451 c -5.44839,1.06994 -10.56998,2.07187 -11.40857,2.20908 -1.04711,0.17137 -1.54564,0.35016 -1.54564,0.55228 0,0.16187 0.23325,1.63204 0.51522,3.2768 2.70275,15.76547 3.28356,34.63258 1.69287,55.26394 -0.7281,9.44363 -2.34823,21.04449 -3.90099,28.01857 -0.23345,1.0486 -0.37949,1.97667 -0.33118,2.02499 0.0483,0.0483 5.12585,1.1561 11.29809,2.46683 6.17232,1.31067 11.30751,2.37915 11.3718,2.39315 0.0641,0.014 0.45734,-1.73307 0.88322,-3.90272 3.35867,-17.11028 4.82653,-33.18977 4.85786,-53.27572 0.0219,-14.08945 -0.79161,-24.35571 -2.87056,-36.96537 z m -427.268845,64.06345 -0.772838,0.11039 c -0.421858,0.0612 -5.59823,0.67716 -11.482161,1.36226 -5.883927,0.68512 -10.759171,1.30169 -10.819725,1.36228 -0.141991,0.142 0.252313,2.91986 1.140854,8.32086 4.869392,29.59836 15.038358,56.25732 31.539139,82.61977 0.450701,0.72005 0.931445,1.27763 1.06725,1.25182 0.361709,-0.0685 19.423106,-12.2036 19.431349,-12.3709 0.0036,-0.0779 -0.796734,-1.40341 -1.766487,-2.94542 -4.266677,-6.78447 -9.935035,-17.45299 -13.064635,-24.5945 -7.52905,-17.18062 -12.488823,-34.71382 -15.051936,-53.27567 z M 462.40066,926.44149 c -0.46898,0.009 -22.08567,5.38002 -22.2283,5.52269 -0.098,0.0981 22.04129,90.06142 22.37549,91.01382 0.40286,1.1482 3.73284,10.5298 13.56323,8.9156 10.95786,-2.3434 9.8458,-14.6677 8.99628,-14.4751 -0.11284,0.025 -5.02627,-20.61508 -11.18774,-45.58033 -8.79763,-35.64656 -11.26829,-45.40142 -11.51896,-45.39668 z M 143.91794,953.1714 c -0.40943,0.0131 -1.21588,1.3276 -6.29312,9.64634 -3.31435,5.43031 -6.03549,9.92123 -6.03549,9.9777 0,0.13674 3.42858,2.19027 7.06593,4.27089 22.35182,12.78549 47.08561,21.82095 72.42596,26.43487 3.59043,0.654 5.67261,1.0064 11.04051,1.804 0.69401,0.1031 1.36954,0.2073 1.50889,0.2212 0.31484,0.031 0.24386,0.6279 1.87691,-11.45018 0.75094,-5.5542 1.40492,-10.43428 1.47207,-10.86126 0.11781,-0.74877 0.0863,-0.77677 -0.58887,-0.88362 -0.38487,-0.0607 -2.68651,-0.4127 -5.11543,-0.77322 -22.30454,-3.3101 -45.25895,-10.90321 -65.32317,-21.57538 -3.55401,-1.89038 -10.16752,-5.64292 -11.85018,-6.73769 -0.0531,-0.0345 -0.12551,-0.0755 -0.18401,-0.0737 z m 214.22322,9.09404 c -1.98095,0.13013 -4.60205,1.01767 -10.4517,3.12954 -11.29964,4.0795 -24.13159,8.26507 -29.91986,9.75681 -0.83741,0.21582 -1.5445,0.50692 -1.54566,0.62592 -0.002,0.21503 5.72469,22.22722 5.81468,22.34847 0.0552,0.075 6.34708,-1.70267 10.2677,-2.90858 5.09669,-1.5677 13.67295,-4.44246 19.79936,-6.62722 l 6.07232,-2.17225 22.30187,8.98356 c 18.14341,7.31671 22.30098,8.95081 22.41231,8.68881 0.9061,-2.1322 8.61707,-21.32757 8.57483,-21.35419 -0.38803,-0.24492 -49.13929,-19.77601 -49.90327,-19.99221 -1.25781,-0.35596 -2.23399,-0.55669 -3.42258,-0.47866 z"
- style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:5.88958930999999986;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- sodipodi:nodetypes="ccssccssscccccccssssscccsssscccscsssccccccssssssssssccccscssccsscccsscsscsssssccsccsscsscsccscccccssc" />
- <g
- style="display:inline"
- id="g3958"
- transform="translate(-1.55942,537.04967)" />
- <g
- transform="scale(1.0022296,0.99777536)"
- style="font-size:127.71524811000000454px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Segoe UI;-inkscape-font-specification:Segoe UI;opacity:1"
- id="text4120">
- <path
- style="font-size:3192.88085937999903763px;fill:#ffffff"
- d="M 251.6875 68.34375 C 203.77053 68.344069 162.62474 85.959793 128.25 121.21875 C 94.91648 155.44121 78.249825 196.39023 78.25 244.09375 C 78.249825 291.79755 94.91648 332.77783 128.25 367 C 162.62474 401.22221 203.77053 418.34372 251.6875 418.34375 C 300.64542 418.34372 342.31207 401.22221 376.6875 367 C 411.06199 332.77783 428.24948 291.79755 428.25 244.09375 C 428.24948 196.39023 411.06199 155.44121 376.6875 121.21875 C 342.31207 85.959793 300.64542 68.344069 251.6875 68.34375 z M 150 224.0625 C 156.11974 224.06254 161.32807 226.26045 165.625 230.6875 C 169.92181 234.9844 172.06243 240.13544 172.0625 246.125 C 172.06243 252.1146 169.92181 257.26563 165.625 261.5625 C 161.32807 265.85937 156.11974 268 150 268 C 144.01038 268 138.85934 265.85937 134.5625 261.5625 C 130.39581 257.26563 128.31248 252.1146 128.3125 246.125 C 128.31248 240.13544 130.39581 234.9844 134.5625 230.6875 C 138.85934 226.26045 144.01038 224.06254 150 224.0625 z M 254.5 224.0625 C 260.61974 224.06254 265.82807 226.26045 270.125 230.6875 C 274.42181 234.9844 276.56243 240.13544 276.5625 246.125 C 276.56243 252.1146 274.42181 257.26563 270.125 261.5625 C 265.82807 265.85937 260.61974 268 254.5 268 C 248.51038 268 243.35934 265.85937 239.0625 261.5625 C 234.89581 257.26563 232.81248 252.1146 232.8125 246.125 C 232.81248 240.13544 234.89581 234.9844 239.0625 230.6875 C 243.35934 226.26045 248.51038 224.06254 254.5 224.0625 z M 357.34375 224.0625 C 363.46349 224.06254 368.67182 226.26045 372.96875 230.6875 C 377.26556 234.9844 379.40618 240.13544 379.40625 246.125 C 379.40618 252.1146 377.26556 257.26563 372.96875 261.5625 C 368.67182 265.85937 363.46349 268 357.34375 268 C 351.35413 268 346.20309 265.85937 341.90625 261.5625 C 337.73956 257.26563 335.65623 252.1146 335.65625 246.125 C 335.65623 240.13544 337.73956 234.9844 341.90625 230.6875 C 346.20309 226.26045 351.35413 224.06254 357.34375 224.0625 z "
- transform="matrix(0.99777536,0,0,1.0022296,0,541.56697)"
- id="path4171" />
</g>
- </g>
</svg>
diff --git a/art/ic_read_indicator.svg b/art/ic_read_indicator.svg
index 343232ca6..7580e2ae6 100644
--- a/art/ic_read_indicator.svg
+++ b/art/ic_read_indicator.svg
@@ -1,51 +1,30 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?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="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+<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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" width="586.62292"
- height="576.55585" id="svg2" inkscape:version="0.48.1 r9760" sodipodi:docname="Check-71-128-204-brightblue.svg">
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="640"
- inkscape:window-height="480"
- id="namedview6"
- showgrid="false"
- fit-margin-top="0"
- fit-margin-left="0"
- fit-margin-right="0"
- fit-margin-bottom="0"
- inkscape:zoom="0.26226712"
- inkscape:cx="365.3587"
- inkscape:cy="62.096819"
- inkscape:window-x="0"
- inkscape:window-y="0"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <path
- d="m 1,392.89211 c 0,0 115.16535,129.68765 138.19842,182.66371 l 99.0422,0 C 279.70015,448.87393 440.93164,146.43825 579.13006,40.486115 607.76328,3.6719748 535.83226,-11.524105 477.78456,12.846425 390.29814,49.576415 225.29631,330.01448 194.47779,397.49872 150.71496,409.01526 104.64881,323.7929 104.64881,323.7929 L 1,392.89211 z"
- id="path4"
- style="fill:#4780cc;fill-opacity:1;stroke:#000000;stroke-width:2"
- inkscape:connector-curvature="0" />
+ height="576.55585" id="svg2" inkscape:version="0.48.1 r9760"
+ sodipodi:docname="Check-71-128-204-brightblue.svg">
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480"
+ id="namedview6" showgrid="false" fit-margin-top="0" fit-margin-left="0" fit-margin-right="0"
+ fit-margin-bottom="0" inkscape:zoom="0.26226712" inkscape:cx="365.3587"
+ inkscape:cy="62.096819" inkscape:window-x="0" inkscape:window-y="0"
+ inkscape:window-maximized="0" inkscape:current-layer="svg2" />
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <path
+ d="m 1,392.89211 c 0,0 115.16535,129.68765 138.19842,182.66371 l 99.0422,0 C 279.70015,448.87393 440.93164,146.43825 579.13006,40.486115 607.76328,3.6719748 535.83226,-11.524105 477.78456,12.846425 390.29814,49.576415 225.29631,330.01448 194.47779,397.49872 150.71496,409.01526 104.64881,323.7929 104.64881,323.7929 L 1,392.89211 z"
+ id="path4" style="fill:#4780cc;fill-opacity:1;stroke:#000000;stroke-width:2"
+ inkscape:connector-curvature="0" />
</svg>
diff --git a/art/ic_received_indicator.svg b/art/ic_received_indicator.svg
index a12292f51..c7833a741 100644
--- a/art/ic_received_indicator.svg
+++ b/art/ic_received_indicator.svg
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600">
-<path d="m7.7,404.6c0,0 115.2,129.7 138.2,182.68l99,0c41.5-126.7 202.7-429.1 340.92-535.1c28.6-36.8-43.3-52-101.35-27.62-87.5,36.7-252.5,317.2-283.3,384.64-43.7,11.5-89.8-73.7-89.84-73.7z" fill="#181"/>
+ <path
+ d="m7.7,404.6c0,0 115.2,129.7 138.2,182.68l99,0c41.5-126.7 202.7-429.1 340.92-535.1c28.6-36.8-43.3-52-101.35-27.62-87.5,36.7-252.5,317.2-283.3,384.64-43.7,11.5-89.8-73.7-89.84-73.7z"
+ fill="#181" />
</svg> \ No newline at end of file
diff --git a/art/ic_send_cancel_away.svg b/art/ic_send_cancel_away.svg
index 76c30da15..fa803779c 100644
--- a/art/ic_send_cancel_away.svg
+++ b/art/ic_send_cancel_away.svg
@@ -1,43 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_cancel_away.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="507"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="-8.3389831"
- inkscape:cy="24"
- inkscape:window-x="0"
- inkscape:window-y="549"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm10 27.17L31.17 34 24 26.83 16.83 34 14 31.17 21.17 24 14 16.83 16.83 14 24 21.17 31.17 14 34 16.83 26.83 24 34 31.17z"
- id="path4"
- style="fill:#ff9800;fill-opacity:0.627451" />
+<svg 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_cancel_away.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="507"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="-8.3389831"
+ inkscape:cy="24" inkscape:window-x="0" inkscape:window-y="549" inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path
+ d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm10 27.17L31.17 34 24 26.83 16.83 34 14 31.17 21.17 24 14 16.83 16.83 14 24 21.17 31.17 14 34 16.83 26.83 24 34 31.17z"
+ id="path4" style="fill:#ff9800;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_cancel_dnd.svg b/art/ic_send_cancel_dnd.svg
index 0db9b04e7..9501f7441 100644
--- a/art/ic_send_cancel_dnd.svg
+++ b/art/ic_send_cancel_dnd.svg
@@ -1,43 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_cancel_dnd.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="507"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="-8.3389831"
- inkscape:cy="24"
- inkscape:window-x="0"
- inkscape:window-y="549"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm10 27.17L31.17 34 24 26.83 16.83 34 14 31.17 21.17 24 14 16.83 16.83 14 24 21.17 31.17 14 34 16.83 26.83 24 34 31.17z"
- id="path4"
- style="fill:#f44336;fill-opacity:0.627451" />
+<svg 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_cancel_dnd.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="507"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="-8.3389831"
+ inkscape:cy="24" inkscape:window-x="0" inkscape:window-y="549" inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path
+ d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm10 27.17L31.17 34 24 26.83 16.83 34 14 31.17 21.17 24 14 16.83 16.83 14 24 21.17 31.17 14 34 16.83 26.83 24 34 31.17z"
+ id="path4" style="fill:#f44336;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_cancel_offline.svg b/art/ic_send_cancel_offline.svg
index 73a3575a5..9d37a350f 100644
--- a/art/ic_send_cancel_offline.svg
+++ b/art/ic_send_cancel_offline.svg
@@ -1,43 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_cancel_offline.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1920"
- inkscape:window-height="1080"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="-7.9322034"
- inkscape:cy="24"
- inkscape:window-x="0"
- inkscape:window-y="0"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm10 27.17L31.17 34 24 26.83 16.83 34 14 31.17 21.17 24 14 16.83 16.83 14 24 21.17 31.17 14 34 16.83 26.83 24 34 31.17z"
- id="path4"
- style="fill:#000000;fill-opacity:0.627451" />
+<svg 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_cancel_offline.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="1920" inkscape:window-height="1080"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="-7.9322034"
+ inkscape:cy="24" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path
+ d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm10 27.17L31.17 34 24 26.83 16.83 34 14 31.17 21.17 24 14 16.83 16.83 14 24 21.17 31.17 14 34 16.83 26.83 24 34 31.17z"
+ id="path4" style="fill:#000000;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_cancel_online.svg b/art/ic_send_cancel_online.svg
index eb5f97506..53ac9c8b1 100644
--- a/art/ic_send_cancel_online.svg
+++ b/art/ic_send_cancel_online.svg
@@ -1,43 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_cancel_online.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="507"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="-8.3389831"
- inkscape:cy="24"
- inkscape:window-x="0"
- inkscape:window-y="549"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm10 27.17L31.17 34 24 26.83 16.83 34 14 31.17 21.17 24 14 16.83 16.83 14 24 21.17 31.17 14 34 16.83 26.83 24 34 31.17z"
- id="path4"
- style="fill:#259b24;fill-opacity:0.627451" />
+<svg 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_cancel_online.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="507"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="-8.3389831"
+ inkscape:cy="24" inkscape:window-x="0" inkscape:window-y="549" inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path
+ d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm10 27.17L31.17 34 24 26.83 16.83 34 14 31.17 21.17 24 14 16.83 16.83 14 24 21.17 31.17 14 34 16.83 26.83 24 34 31.17z"
+ id="path4" style="fill:#259b24;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_location_away.svg b/art/ic_send_location_away.svg
index 410597c34..8a171774e 100644
--- a/art/ic_send_location_away.svg
+++ b/art/ic_send_location_away.svg
@@ -1,43 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_location_away.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="1156"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="0.91525424"
- inkscape:cy="24"
- inkscape:window-x="2880"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M24 4c-7.73 0-14 6.27-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.73-6.27-14-14-14zm0 19c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
- id="path4"
- style="fill:#ff9800;fill-opacity:0.627451" />
+<svg 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_location_away.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="1156"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="0.91525424"
+ inkscape:cy="24" inkscape:window-x="2880" inkscape:window-y="20"
+ inkscape:window-maximized="0" inkscape:current-layer="svg2" />
+ <path
+ d="M24 4c-7.73 0-14 6.27-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.73-6.27-14-14-14zm0 19c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
+ id="path4" style="fill:#ff9800;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_location_dnd.svg b/art/ic_send_location_dnd.svg
index 97d15d8a5..6fc835bc7 100644
--- a/art/ic_send_location_dnd.svg
+++ b/art/ic_send_location_dnd.svg
@@ -1,43 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_location_dnd.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="1156"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="-7.9322034"
- inkscape:cy="24"
- inkscape:window-x="2880"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M24 4c-7.73 0-14 6.27-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.73-6.27-14-14-14zm0 19c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
- id="path4"
- style="fill:#f44336;fill-opacity:0.627451" />
+<svg 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_location_dnd.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="1156"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="-7.9322034"
+ inkscape:cy="24" inkscape:window-x="2880" inkscape:window-y="20"
+ inkscape:window-maximized="0" inkscape:current-layer="svg2" />
+ <path
+ d="M24 4c-7.73 0-14 6.27-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.73-6.27-14-14-14zm0 19c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
+ id="path4" style="fill:#f44336;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_location_offline.svg b/art/ic_send_location_offline.svg
index 219ec427f..e957a15b2 100644
--- a/art/ic_send_location_offline.svg
+++ b/art/ic_send_location_offline.svg
@@ -1,43 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_location_offline.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="1156"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="-7.9322034"
- inkscape:cy="24"
- inkscape:window-x="2880"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M24 4c-7.73 0-14 6.27-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.73-6.27-14-14-14zm0 19c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
- id="path4"
- style="fill:#000000;fill-opacity:0.627451" />
+<svg 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_location_offline.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="1156"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="-7.9322034"
+ inkscape:cy="24" inkscape:window-x="2880" inkscape:window-y="20"
+ inkscape:window-maximized="0" inkscape:current-layer="svg2" />
+ <path
+ d="M24 4c-7.73 0-14 6.27-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.73-6.27-14-14-14zm0 19c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
+ id="path4" style="fill:#000000;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_location_online.svg b/art/ic_send_location_online.svg
index dd50df43b..d82369d69 100644
--- a/art/ic_send_location_online.svg
+++ b/art/ic_send_location_online.svg
@@ -1,43 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_location_online.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="1156"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="-7.9322034"
- inkscape:cy="24"
- inkscape:window-x="2880"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M24 4c-7.73 0-14 6.27-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.73-6.27-14-14-14zm0 19c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
- id="path4"
- style="stroke:none;stroke-opacity:0;fill:#259b24;fill-opacity:0.627451" />
+<svg 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_location_online.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="1156"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="-7.9322034"
+ inkscape:cy="24" inkscape:window-x="2880" inkscape:window-y="20"
+ inkscape:window-maximized="0" inkscape:current-layer="svg2" />
+ <path
+ d="M24 4c-7.73 0-14 6.27-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.73-6.27-14-14-14zm0 19c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
+ id="path4" style="stroke:none;stroke-opacity:0;fill:#259b24;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_photo_away.svg b/art/ic_send_photo_away.svg
index f2f5e8f22..9c605e7cf 100644
--- a/art/ic_send_photo_away.svg
+++ b/art/ic_send_photo_away.svg
@@ -1,49 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_photo_away.svg">
- <metadata
- id="metadata12">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs10" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="567"
- id="namedview8"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="-8.3389831"
- inkscape:cy="24"
- inkscape:window-x="960"
- inkscape:window-y="609"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <circle
- cx="24"
- cy="24"
- r="6.4"
- id="circle4"
- style="fill-opacity:0.627451;fill:#ff9800" />
- <path
- d="M18 4l-3.66 4H8c-2.21 0-4 1.79-4 4v24c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V12c0-2.21-1.79-4-4-4h-6.34L30 4H18zm6 30c-5.52 0-10-4.48-10-10s4.48-10 10-10 10 4.48 10 10-4.48 10-10 10z"
- id="path6"
- style="fill:#ff9800;fill-opacity:0.627451" />
+<svg 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_photo_away.svg">
+ <metadata id="metadata12">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs10" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="567"
+ id="namedview8" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="-8.3389831"
+ inkscape:cy="24" inkscape:window-x="960" inkscape:window-y="609"
+ inkscape:window-maximized="0" inkscape:current-layer="svg2" />
+ <circle cx="24" cy="24" r="6.4" id="circle4" style="fill-opacity:0.627451;fill:#ff9800" />
+ <path
+ d="M18 4l-3.66 4H8c-2.21 0-4 1.79-4 4v24c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V12c0-2.21-1.79-4-4-4h-6.34L30 4H18zm6 30c-5.52 0-10-4.48-10-10s4.48-10 10-10 10 4.48 10 10-4.48 10-10 10z"
+ id="path6" style="fill:#ff9800;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_photo_dnd.svg b/art/ic_send_photo_dnd.svg
index 5c26150d9..6e5562cd9 100644
--- a/art/ic_send_photo_dnd.svg
+++ b/art/ic_send_photo_dnd.svg
@@ -1,49 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_photo_dnd.svg">
- <metadata
- id="metadata12">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs10" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="567"
- id="namedview8"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="-8.3389831"
- inkscape:cy="24"
- inkscape:window-x="960"
- inkscape:window-y="609"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <circle
- cx="24"
- cy="24"
- r="6.4"
- id="circle4"
- style="fill:#f44336;fill-opacity:0.627451" />
- <path
- d="M18 4l-3.66 4H8c-2.21 0-4 1.79-4 4v24c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V12c0-2.21-1.79-4-4-4h-6.34L30 4H18zm6 30c-5.52 0-10-4.48-10-10s4.48-10 10-10 10 4.48 10 10-4.48 10-10 10z"
- id="path6"
- style="fill:#f44336;fill-opacity:0.627451" />
+<svg 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_photo_dnd.svg">
+ <metadata id="metadata12">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs10" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="567"
+ id="namedview8" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="-8.3389831"
+ inkscape:cy="24" inkscape:window-x="960" inkscape:window-y="609"
+ inkscape:window-maximized="0" inkscape:current-layer="svg2" />
+ <circle cx="24" cy="24" r="6.4" id="circle4" style="fill:#f44336;fill-opacity:0.627451" />
+ <path
+ d="M18 4l-3.66 4H8c-2.21 0-4 1.79-4 4v24c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V12c0-2.21-1.79-4-4-4h-6.34L30 4H18zm6 30c-5.52 0-10-4.48-10-10s4.48-10 10-10 10 4.48 10 10-4.48 10-10 10z"
+ id="path6" style="fill:#f44336;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_photo_offline.svg b/art/ic_send_photo_offline.svg
index e652d34a9..fcbab71e4 100644
--- a/art/ic_send_photo_offline.svg
+++ b/art/ic_send_photo_offline.svg
@@ -1,49 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_photo_offline.svg">
- <metadata
- id="metadata12">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs10" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="567"
- id="namedview8"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="-8.3389831"
- inkscape:cy="24"
- inkscape:window-x="960"
- inkscape:window-y="609"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <circle
- cx="24"
- cy="24"
- r="6.4"
- id="circle4"
- style="fill:#000000;fill-opacity:0.627451" />
- <path
- d="M18 4l-3.66 4H8c-2.21 0-4 1.79-4 4v24c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V12c0-2.21-1.79-4-4-4h-6.34L30 4H18zm6 30c-5.52 0-10-4.48-10-10s4.48-10 10-10 10 4.48 10 10-4.48 10-10 10z"
- id="path6"
- style="fill:#000000;fill-opacity:0.627451" />
+<svg 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_photo_offline.svg">
+ <metadata id="metadata12">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs10" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="567"
+ id="namedview8" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="-8.3389831"
+ inkscape:cy="24" inkscape:window-x="960" inkscape:window-y="609"
+ inkscape:window-maximized="0" inkscape:current-layer="svg2" />
+ <circle cx="24" cy="24" r="6.4" id="circle4" style="fill:#000000;fill-opacity:0.627451" />
+ <path
+ d="M18 4l-3.66 4H8c-2.21 0-4 1.79-4 4v24c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V12c0-2.21-1.79-4-4-4h-6.34L30 4H18zm6 30c-5.52 0-10-4.48-10-10s4.48-10 10-10 10 4.48 10 10-4.48 10-10 10z"
+ id="path6" style="fill:#000000;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_photo_online.svg b/art/ic_send_photo_online.svg
index c7afb231a..3c4ccceae 100644
--- a/art/ic_send_photo_online.svg
+++ b/art/ic_send_photo_online.svg
@@ -1,49 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_photo_online.svg">
- <metadata
- id="metadata12">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs10" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="567"
- id="namedview8"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="-8.3389831"
- inkscape:cy="24"
- inkscape:window-x="960"
- inkscape:window-y="609"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <circle
- cx="24"
- cy="24"
- r="6.4"
- id="circle4"
- style="fill:#259b24;fill-opacity:0.627451" />
- <path
- d="M18 4l-3.66 4H8c-2.21 0-4 1.79-4 4v24c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V12c0-2.21-1.79-4-4-4h-6.34L30 4H18zm6 30c-5.52 0-10-4.48-10-10s4.48-10 10-10 10 4.48 10 10-4.48 10-10 10z"
- id="path6"
- style="fill:#259b24;fill-opacity:0.627451" />
+<svg 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_photo_online.svg">
+ <metadata id="metadata12">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs10" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="567"
+ id="namedview8" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="-8.3389831"
+ inkscape:cy="24" inkscape:window-x="960" inkscape:window-y="609"
+ inkscape:window-maximized="0" inkscape:current-layer="svg2" />
+ <circle cx="24" cy="24" r="6.4" id="circle4" style="fill:#259b24;fill-opacity:0.627451" />
+ <path
+ d="M18 4l-3.66 4H8c-2.21 0-4 1.79-4 4v24c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V12c0-2.21-1.79-4-4-4h-6.34L30 4H18zm6 30c-5.52 0-10-4.48-10-10s4.48-10 10-10 10 4.48 10 10-4.48 10-10 10z"
+ id="path6" style="fill:#259b24;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_picture_away.svg b/art/ic_send_picture_away.svg
index ba6dcc716..5a2bfaf28 100644
--- a/art/ic_send_picture_away.svg
+++ b/art/ic_send_picture_away.svg
@@ -1,44 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_picture_away.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1916"
- inkscape:window-height="1036"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="6.5084746"
- inkscape:cy="24"
- inkscape:window-x="0"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M42 38V10c0-2.21-1.79-4-4-4H10c-2.21 0-4 1.79-4 4v28c0 2.21 1.79 4 4 4h28c2.21 0 4-1.79 4-4zM17 27l5 6.01L29 24l9 12H10l7-9z"
- id="path4"
- style="fill:#ff9800;fill-opacity:0.627451" />
+<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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_picture_away.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="1916" inkscape:window-height="1036"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="6.5084746"
+ inkscape:cy="24" inkscape:window-x="0" inkscape:window-y="20" inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path
+ d="M42 38V10c0-2.21-1.79-4-4-4H10c-2.21 0-4 1.79-4 4v28c0 2.21 1.79 4 4 4h28c2.21 0 4-1.79 4-4zM17 27l5 6.01L29 24l9 12H10l7-9z"
+ id="path4" style="fill:#ff9800;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_picture_dnd.svg b/art/ic_send_picture_dnd.svg
index 1f13a1bba..2666b76ee 100644
--- a/art/ic_send_picture_dnd.svg
+++ b/art/ic_send_picture_dnd.svg
@@ -1,44 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_picture_dnd.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1916"
- inkscape:window-height="1036"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="6.5084746"
- inkscape:cy="24"
- inkscape:window-x="0"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M42 38V10c0-2.21-1.79-4-4-4H10c-2.21 0-4 1.79-4 4v28c0 2.21 1.79 4 4 4h28c2.21 0 4-1.79 4-4zM17 27l5 6.01L29 24l9 12H10l7-9z"
- id="path4"
- style="fill:#f44336;fill-opacity:0.627451" />
+<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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_picture_dnd.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="1916" inkscape:window-height="1036"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="6.5084746"
+ inkscape:cy="24" inkscape:window-x="0" inkscape:window-y="20" inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path
+ d="M42 38V10c0-2.21-1.79-4-4-4H10c-2.21 0-4 1.79-4 4v28c0 2.21 1.79 4 4 4h28c2.21 0 4-1.79 4-4zM17 27l5 6.01L29 24l9 12H10l7-9z"
+ id="path4" style="fill:#f44336;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_picture_offline.svg b/art/ic_send_picture_offline.svg
index 0d4221b1d..cb77ee998 100644
--- a/art/ic_send_picture_offline.svg
+++ b/art/ic_send_picture_offline.svg
@@ -1,44 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_picture_offline.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1916"
- inkscape:window-height="1036"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="6.5084746"
- inkscape:cy="24"
- inkscape:window-x="0"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M42 38V10c0-2.21-1.79-4-4-4H10c-2.21 0-4 1.79-4 4v28c0 2.21 1.79 4 4 4h28c2.21 0 4-1.79 4-4zM17 27l5 6.01L29 24l9 12H10l7-9z"
- id="path4"
- style="fill:#000000;fill-opacity:0.627451" />
+<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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_picture_offline.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="1916" inkscape:window-height="1036"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="6.5084746"
+ inkscape:cy="24" inkscape:window-x="0" inkscape:window-y="20" inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path
+ d="M42 38V10c0-2.21-1.79-4-4-4H10c-2.21 0-4 1.79-4 4v28c0 2.21 1.79 4 4 4h28c2.21 0 4-1.79 4-4zM17 27l5 6.01L29 24l9 12H10l7-9z"
+ id="path4" style="fill:#000000;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_picture_online.svg b/art/ic_send_picture_online.svg
index 271951fbf..ab8109c7f 100644
--- a/art/ic_send_picture_online.svg
+++ b/art/ic_send_picture_online.svg
@@ -1,44 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_picture_online.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1916"
- inkscape:window-height="1036"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="6.5084746"
- inkscape:cy="24"
- inkscape:window-x="0"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M42 38V10c0-2.21-1.79-4-4-4H10c-2.21 0-4 1.79-4 4v28c0 2.21 1.79 4 4 4h28c2.21 0 4-1.79 4-4zM17 27l5 6.01L29 24l9 12H10l7-9z"
- id="path4"
- style="fill:#259b24;fill-opacity:0.627451" />
+<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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_picture_online.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="1916" inkscape:window-height="1036"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="6.5084746"
+ inkscape:cy="24" inkscape:window-x="0" inkscape:window-y="20" inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path
+ d="M42 38V10c0-2.21-1.79-4-4-4H10c-2.21 0-4 1.79-4 4v28c0 2.21 1.79 4 4 4h28c2.21 0 4-1.79 4-4zM17 27l5 6.01L29 24l9 12H10l7-9z"
+ id="path4" style="fill:#259b24;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_text_away.svg b/art/ic_send_text_away.svg
index c4efbe568..f6b02b4f9 100644
--- a/art/ic_send_text_away.svg
+++ b/art/ic_send_text_away.svg
@@ -1,58 +1,33 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?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="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+<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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg3621" version="1.1"
inkscape:version="0.91 r13725" width="96" height="96" sodipodi:docname="ic_send_text_away.svg"
inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-xxhdpi/ic_action_send_now_online.png"
inkscape:export-xdpi="154.28572" inkscape:export-ydpi="154.28572">
- <metadata
- id="metadata3627">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs3625" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="744"
- inkscape:window-height="1156"
- id="namedview3623"
- showgrid="true"
- showguides="true"
- inkscape:zoom="8"
- inkscape:cx="55.595803"
- inkscape:cy="56.761328"
- inkscape:window-x="3092"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg3621">
- <inkscape:grid
- type="xygrid"
- id="grid3631" />
- </sodipodi:namedview>
- <path
- style="fill:#ff9800;fill-opacity:0.627451;stroke:none"
- d="M 3.887575,4.1549246 90.999747,47.676331 3.887575,91.286663 13.203552,52.344101 63.012683,47.720794 13.203552,43.008558 Z"
- id="path3633"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccccccc"
- inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-mdpi/ic_action_send_now_dnd.png"
- inkscape:export-xdpi="51.42857"
- inkscape:export-ydpi="51.42857" />
+ <metadata id="metadata3627">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs3625" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="744" inkscape:window-height="1156"
+ id="namedview3623" showgrid="true" showguides="true" inkscape:zoom="8"
+ inkscape:cx="55.595803" inkscape:cy="56.761328" inkscape:window-x="3092"
+ inkscape:window-y="20" inkscape:window-maximized="0" inkscape:current-layer="svg3621">
+ <inkscape:grid type="xygrid" id="grid3631" />
+ </sodipodi:namedview>
+ <path style="fill:#ff9800;fill-opacity:0.627451;stroke:none"
+ d="M 3.887575,4.1549246 90.999747,47.676331 3.887575,91.286663 13.203552,52.344101 63.012683,47.720794 13.203552,43.008558 Z"
+ id="path3633" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccc"
+ inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-mdpi/ic_action_send_now_dnd.png"
+ inkscape:export-xdpi="51.42857" inkscape:export-ydpi="51.42857" />
</svg>
diff --git a/art/ic_send_text_dnd.svg b/art/ic_send_text_dnd.svg
index c6f67133a..0ebb40f41 100644
--- a/art/ic_send_text_dnd.svg
+++ b/art/ic_send_text_dnd.svg
@@ -1,58 +1,33 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?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="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+<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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg3621" version="1.1"
inkscape:version="0.91 r13725" width="96" height="96" sodipodi:docname="ic_send_text_dnd.svg"
inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-xxhdpi/ic_action_send_now_online.png"
inkscape:export-xdpi="154.28572" inkscape:export-ydpi="154.28572">
- <metadata
- id="metadata3627">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs3625" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="1156"
- id="namedview3623"
- showgrid="true"
- showguides="true"
- inkscape:zoom="8"
- inkscape:cx="49.908303"
- inkscape:cy="56.761328"
- inkscape:window-x="2880"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg3621">
- <inkscape:grid
- type="xygrid"
- id="grid3631" />
- </sodipodi:namedview>
- <path
- style="fill:#f44336;fill-opacity:0.627451;stroke:none"
- d="M 3.887575,4.1549246 90.999747,47.676331 3.887575,91.286663 13.203552,52.344101 63.012683,47.720794 13.203552,43.008558 Z"
- id="path3633"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccccccc"
- inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-mdpi/ic_action_send_now_dnd.png"
- inkscape:export-xdpi="51.42857"
- inkscape:export-ydpi="51.42857" />
+ <metadata id="metadata3627">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs3625" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="1156"
+ id="namedview3623" showgrid="true" showguides="true" inkscape:zoom="8"
+ inkscape:cx="49.908303" inkscape:cy="56.761328" inkscape:window-x="2880"
+ inkscape:window-y="20" inkscape:window-maximized="0" inkscape:current-layer="svg3621">
+ <inkscape:grid type="xygrid" id="grid3631" />
+ </sodipodi:namedview>
+ <path style="fill:#f44336;fill-opacity:0.627451;stroke:none"
+ d="M 3.887575,4.1549246 90.999747,47.676331 3.887575,91.286663 13.203552,52.344101 63.012683,47.720794 13.203552,43.008558 Z"
+ id="path3633" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccc"
+ inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-mdpi/ic_action_send_now_dnd.png"
+ inkscape:export-xdpi="51.42857" inkscape:export-ydpi="51.42857" />
</svg>
diff --git a/art/ic_send_text_offline.svg b/art/ic_send_text_offline.svg
index 6d3faa93e..25ffc5f61 100644
--- a/art/ic_send_text_offline.svg
+++ b/art/ic_send_text_offline.svg
@@ -1,58 +1,34 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?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="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+<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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg3621" version="1.1"
- inkscape:version="0.91 r13725" width="96" height="96" sodipodi:docname="ic_send_text_offline.svg"
+ inkscape:version="0.91 r13725" width="96" height="96"
+ sodipodi:docname="ic_send_text_offline.svg"
inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-xxhdpi/ic_action_send_now_online.png"
inkscape:export-xdpi="154.28572" inkscape:export-ydpi="154.28572">
- <metadata
- id="metadata3627">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs3625" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="1156"
- id="namedview3623"
- showgrid="true"
- showguides="true"
- inkscape:zoom="8"
- inkscape:cx="50.158303"
- inkscape:cy="56.761328"
- inkscape:window-x="2880"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg3621">
- <inkscape:grid
- type="xygrid"
- id="grid3631" />
- </sodipodi:namedview>
- <path
- style="fill:#000000;fill-opacity:0.627451;stroke:none"
- d="M 3.887575,4.1549246 90.999747,47.676331 3.887575,91.286663 13.203552,52.344101 63.012683,47.720794 13.203552,43.008558 Z"
- id="path3633"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccccccc"
- inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-mdpi/ic_action_send_now_dnd.png"
- inkscape:export-xdpi="51.42857"
- inkscape:export-ydpi="51.42857" />
+ <metadata id="metadata3627">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs3625" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="1156"
+ id="namedview3623" showgrid="true" showguides="true" inkscape:zoom="8"
+ inkscape:cx="50.158303" inkscape:cy="56.761328" inkscape:window-x="2880"
+ inkscape:window-y="20" inkscape:window-maximized="0" inkscape:current-layer="svg3621">
+ <inkscape:grid type="xygrid" id="grid3631" />
+ </sodipodi:namedview>
+ <path style="fill:#000000;fill-opacity:0.627451;stroke:none"
+ d="M 3.887575,4.1549246 90.999747,47.676331 3.887575,91.286663 13.203552,52.344101 63.012683,47.720794 13.203552,43.008558 Z"
+ id="path3633" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccc"
+ inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-mdpi/ic_action_send_now_dnd.png"
+ inkscape:export-xdpi="51.42857" inkscape:export-ydpi="51.42857" />
</svg>
diff --git a/art/ic_send_text_online.svg b/art/ic_send_text_online.svg
index 747029f74..aec29e306 100644
--- a/art/ic_send_text_online.svg
+++ b/art/ic_send_text_online.svg
@@ -1,58 +1,33 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?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="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+<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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg3621" version="1.1"
inkscape:version="0.91 r13725" width="96" height="96" sodipodi:docname="ic_action_send_now.svg"
inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-xxhdpi/ic_action_send_now_online.png"
inkscape:export-xdpi="154.28572" inkscape:export-ydpi="154.28572">
- <metadata
- id="metadata3627">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs3625" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1920"
- inkscape:window-height="1200"
- id="namedview3623"
- showgrid="true"
- showguides="true"
- inkscape:zoom="8"
- inkscape:cx="69.783303"
- inkscape:cy="56.761328"
- inkscape:window-x="1920"
- inkscape:window-y="0"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg3621">
- <inkscape:grid
- type="xygrid"
- id="grid3631" />
- </sodipodi:namedview>
- <path
- style="fill:#259b24;fill-opacity:0.62745098;stroke:none"
- d="M 3.887575,4.1549246 90.999747,47.676331 3.887575,91.286663 13.203552,52.344101 63.012683,47.720794 13.203552,43.008558 Z"
- id="path3633"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccccccc"
- inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-mdpi/ic_action_send_now_dnd.png"
- inkscape:export-xdpi="51.42857"
- inkscape:export-ydpi="51.42857" />
+ <metadata id="metadata3627">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs3625" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="1920" inkscape:window-height="1200"
+ id="namedview3623" showgrid="true" showguides="true" inkscape:zoom="8"
+ inkscape:cx="69.783303" inkscape:cy="56.761328" inkscape:window-x="1920"
+ inkscape:window-y="0" inkscape:window-maximized="0" inkscape:current-layer="svg3621">
+ <inkscape:grid type="xygrid" id="grid3631" />
+ </sodipodi:namedview>
+ <path style="fill:#259b24;fill-opacity:0.62745098;stroke:none"
+ d="M 3.887575,4.1549246 90.999747,47.676331 3.887575,91.286663 13.203552,52.344101 63.012683,47.720794 13.203552,43.008558 Z"
+ id="path3633" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccc"
+ inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-mdpi/ic_action_send_now_dnd.png"
+ inkscape:export-xdpi="51.42857" inkscape:export-ydpi="51.42857" />
</svg>
diff --git a/art/ic_send_video_away.svg b/art/ic_send_video_away.svg
index cd1316950..c98417a27 100644
--- a/art/ic_send_video_away.svg
+++ b/art/ic_send_video_away.svg
@@ -1,48 +1,28 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" fill="#FF9800" fill-opacity="0.627451"
- height="48" viewBox="0 0 24 24" width="48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
- sodipodi:docname="ic_send_video_away.svg">
- <metadata
- id="metadata12">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs10" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="640"
- inkscape:window-height="480"
- id="namedview8"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="24"
- inkscape:cy="24"
- inkscape:window-x="0"
- inkscape:window-y="27"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M0 0h24v24H0z"
- fill="none"
- id="path4" />
- <path
- d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"
- id="path6" />
+<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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" fill="#FF9800"
+ fill-opacity="0.627451" height="48" viewBox="0 0 24 24" width="48" id="svg2" version="1.1"
+ inkscape:version="0.91 r13725" sodipodi:docname="ic_send_video_away.svg">
+ <metadata id="metadata12">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs10" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480"
+ id="namedview8" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="24" inkscape:cy="24"
+ inkscape:window-x="0" inkscape:window-y="27" inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path d="M0 0h24v24H0z" fill="none" id="path4" />
+ <path
+ d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"
+ id="path6" />
</svg>
diff --git a/art/ic_send_video_dnd.svg b/art/ic_send_video_dnd.svg
index 8c4e477ea..cbf7d5775 100644
--- a/art/ic_send_video_dnd.svg
+++ b/art/ic_send_video_dnd.svg
@@ -1,48 +1,28 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" fill="#F44336" fill-opacity="0.627451"
- height="48" viewBox="0 0 24 24" width="48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
- sodipodi:docname="ic_send_video_dnd.svg">
- <metadata
- id="metadata12">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs10" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="640"
- inkscape:window-height="480"
- id="namedview8"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="24"
- inkscape:cy="23.59322"
- inkscape:window-x="0"
- inkscape:window-y="27"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M0 0h24v24H0z"
- fill="none"
- id="path4" />
- <path
- d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"
- id="path6" />
+<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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" fill="#F44336"
+ fill-opacity="0.627451" height="48" viewBox="0 0 24 24" width="48" id="svg2" version="1.1"
+ inkscape:version="0.91 r13725" sodipodi:docname="ic_send_video_dnd.svg">
+ <metadata id="metadata12">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs10" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480"
+ id="namedview8" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="24"
+ inkscape:cy="23.59322" inkscape:window-x="0" inkscape:window-y="27"
+ inkscape:window-maximized="0" inkscape:current-layer="svg2" />
+ <path d="M0 0h24v24H0z" fill="none" id="path4" />
+ <path
+ d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"
+ id="path6" />
</svg>
diff --git a/art/ic_send_video_offline.svg b/art/ic_send_video_offline.svg
index c0e3ecc04..0426a7233 100644
--- a/art/ic_send_video_offline.svg
+++ b/art/ic_send_video_offline.svg
@@ -1,48 +1,28 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" fill="#000000" fill-opacity="0.627451"
- height="48" viewBox="0 0 24 24" width="48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
- sodipodi:docname="ic_send_video_offline.svg">
- <metadata
- id="metadata12">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs10" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="640"
- inkscape:window-height="480"
- id="namedview8"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="24"
- inkscape:cy="24"
- inkscape:window-x="0"
- inkscape:window-y="27"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M0 0h24v24H0z"
- fill="none"
- id="path4" />
- <path
- d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"
- id="path6" />
+<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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" fill="#000000"
+ fill-opacity="0.627451" height="48" viewBox="0 0 24 24" width="48" id="svg2" version="1.1"
+ inkscape:version="0.91 r13725" sodipodi:docname="ic_send_video_offline.svg">
+ <metadata id="metadata12">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs10" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480"
+ id="namedview8" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="24" inkscape:cy="24"
+ inkscape:window-x="0" inkscape:window-y="27" inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path d="M0 0h24v24H0z" fill="none" id="path4" />
+ <path
+ d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"
+ id="path6" />
</svg>
diff --git a/art/ic_send_video_online.svg b/art/ic_send_video_online.svg
index c8ba1818a..021da4eb5 100644
--- a/art/ic_send_video_online.svg
+++ b/art/ic_send_video_online.svg
@@ -1,48 +1,28 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" fill="#259B24" fill-opacity="0.627451"
- height="48" viewBox="0 0 24 24" width="48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
- sodipodi:docname="ic_send_video_online.svg">
- <metadata
- id="metadata12">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs10" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="640"
- inkscape:window-height="480"
- id="namedview8"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="24"
- inkscape:cy="23.59322"
- inkscape:window-x="0"
- inkscape:window-y="27"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M0 0h24v24H0z"
- fill="none"
- id="path4" />
- <path
- d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"
- id="path6" />
+<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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" fill="#259B24"
+ fill-opacity="0.627451" height="48" viewBox="0 0 24 24" width="48" id="svg2" version="1.1"
+ inkscape:version="0.91 r13725" sodipodi:docname="ic_send_video_online.svg">
+ <metadata id="metadata12">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs10" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480"
+ id="namedview8" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="24"
+ inkscape:cy="23.59322" inkscape:window-x="0" inkscape:window-y="27"
+ inkscape:window-maximized="0" inkscape:current-layer="svg2" />
+ <path d="M0 0h24v24H0z" fill="none" id="path4" />
+ <path
+ d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"
+ id="path6" />
</svg>
diff --git a/art/ic_send_voice_away.svg b/art/ic_send_voice_away.svg
index 78534750c..f1c303984 100644
--- a/art/ic_send_voice_away.svg
+++ b/art/ic_send_voice_away.svg
@@ -1,43 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_voice_away.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="1156"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="-7.9322034"
- inkscape:cy="24"
- inkscape:window-x="2880"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M24 30c3.31 0 5.98-2.69 5.98-6L30 12c0-3.32-2.68-6-6-6-3.31 0-6 2.68-6 6v12c0 3.31 2.69 6 6 6zm10.6-6c0 6-5.07 10.2-10.6 10.2-5.52 0-10.6-4.2-10.6-10.2H10c0 6.83 5.44 12.47 12 13.44V44h4v-6.56c6.56-.97 12-6.61 12-13.44h-3.4z"
- id="path4"
- style="fill:#ff9800;fill-opacity:0.627451" />
+<svg 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_voice_away.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="1156"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="-7.9322034"
+ inkscape:cy="24" inkscape:window-x="2880" inkscape:window-y="20"
+ inkscape:window-maximized="0" inkscape:current-layer="svg2" />
+ <path
+ d="M24 30c3.31 0 5.98-2.69 5.98-6L30 12c0-3.32-2.68-6-6-6-3.31 0-6 2.68-6 6v12c0 3.31 2.69 6 6 6zm10.6-6c0 6-5.07 10.2-10.6 10.2-5.52 0-10.6-4.2-10.6-10.2H10c0 6.83 5.44 12.47 12 13.44V44h4v-6.56c6.56-.97 12-6.61 12-13.44h-3.4z"
+ id="path4" style="fill:#ff9800;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_voice_dnd.svg b/art/ic_send_voice_dnd.svg
index 4ef81de7f..fdd21b126 100644
--- a/art/ic_send_voice_dnd.svg
+++ b/art/ic_send_voice_dnd.svg
@@ -1,43 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_voice_dnd.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="1156"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="-7.9322034"
- inkscape:cy="24"
- inkscape:window-x="2880"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M24 30c3.31 0 5.98-2.69 5.98-6L30 12c0-3.32-2.68-6-6-6-3.31 0-6 2.68-6 6v12c0 3.31 2.69 6 6 6zm10.6-6c0 6-5.07 10.2-10.6 10.2-5.52 0-10.6-4.2-10.6-10.2H10c0 6.83 5.44 12.47 12 13.44V44h4v-6.56c6.56-.97 12-6.61 12-13.44h-3.4z"
- id="path4"
- style="fill:#f44336;fill-opacity:0.627451" />
+<svg 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_voice_dnd.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="1156"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="-7.9322034"
+ inkscape:cy="24" inkscape:window-x="2880" inkscape:window-y="20"
+ inkscape:window-maximized="0" inkscape:current-layer="svg2" />
+ <path
+ d="M24 30c3.31 0 5.98-2.69 5.98-6L30 12c0-3.32-2.68-6-6-6-3.31 0-6 2.68-6 6v12c0 3.31 2.69 6 6 6zm10.6-6c0 6-5.07 10.2-10.6 10.2-5.52 0-10.6-4.2-10.6-10.2H10c0 6.83 5.44 12.47 12 13.44V44h4v-6.56c6.56-.97 12-6.61 12-13.44h-3.4z"
+ id="path4" style="fill:#f44336;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_voice_offline.svg b/art/ic_send_voice_offline.svg
index fbbf589f9..41edd5b65 100644
--- a/art/ic_send_voice_offline.svg
+++ b/art/ic_send_voice_offline.svg
@@ -1,43 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_voice_offline.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="1156"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="-7.9322034"
- inkscape:cy="24"
- inkscape:window-x="2880"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M24 30c3.31 0 5.98-2.69 5.98-6L30 12c0-3.32-2.68-6-6-6-3.31 0-6 2.68-6 6v12c0 3.31 2.69 6 6 6zm10.6-6c0 6-5.07 10.2-10.6 10.2-5.52 0-10.6-4.2-10.6-10.2H10c0 6.83 5.44 12.47 12 13.44V44h4v-6.56c6.56-.97 12-6.61 12-13.44h-3.4z"
- id="path4"
- style="fill:#000000;fill-opacity:0.627451" />
+<svg 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_voice_offline.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="1156"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="-7.9322034"
+ inkscape:cy="24" inkscape:window-x="2880" inkscape:window-y="20"
+ inkscape:window-maximized="0" inkscape:current-layer="svg2" />
+ <path
+ d="M24 30c3.31 0 5.98-2.69 5.98-6L30 12c0-3.32-2.68-6-6-6-3.31 0-6 2.68-6 6v12c0 3.31 2.69 6 6 6zm10.6-6c0 6-5.07 10.2-10.6 10.2-5.52 0-10.6-4.2-10.6-10.2H10c0 6.83 5.44 12.47 12 13.44V44h4v-6.56c6.56-.97 12-6.61 12-13.44h-3.4z"
+ id="path4" style="fill:#000000;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_send_voice_online.svg b/art/ic_send_voice_online.svg
index 77c71d881..e73c6697b 100644
--- a/art/ic_send_voice_online.svg
+++ b/art/ic_send_voice_online.svg
@@ -1,43 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_send_voice_online.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="956"
- inkscape:window-height="1156"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="-7.9322034"
- inkscape:cy="24"
- inkscape:window-x="2880"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M24 30c3.31 0 5.98-2.69 5.98-6L30 12c0-3.32-2.68-6-6-6-3.31 0-6 2.68-6 6v12c0 3.31 2.69 6 6 6zm10.6-6c0 6-5.07 10.2-10.6 10.2-5.52 0-10.6-4.2-10.6-10.2H10c0 6.83 5.44 12.47 12 13.44V44h4v-6.56c6.56-.97 12-6.61 12-13.44h-3.4z"
- id="path4"
- style="fill:#259b24;fill-opacity:0.627451" />
+<svg 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_send_voice_online.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="956" inkscape:window-height="1156"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="-7.9322034"
+ inkscape:cy="24" inkscape:window-x="2880" inkscape:window-y="20"
+ inkscape:window-maximized="0" inkscape:current-layer="svg2" />
+ <path
+ d="M24 30c3.31 0 5.98-2.69 5.98-6L30 12c0-3.32-2.68-6-6-6-3.31 0-6 2.68-6 6v12c0 3.31 2.69 6 6 6zm10.6-6c0 6-5.07 10.2-10.6 10.2-5.52 0-10.6-4.2-10.6-10.2H10c0 6.83 5.44 12.47 12 13.44V44h4v-6.56c6.56-.97 12-6.61 12-13.44h-3.4z"
+ id="path4" style="fill:#259b24;fill-opacity:0.627451" />
</svg>
diff --git a/art/ic_verified_fingerprint.svg b/art/ic_verified_fingerprint.svg
index 5adfad356..625b29759 100644
--- a/art/ic_verified_fingerprint.svg
+++ b/art/ic_verified_fingerprint.svg
@@ -1,43 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="ic_verified_fingerprint.svg">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1916"
- inkscape:window-height="1156"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="-3.3559322"
- inkscape:cy="24"
- inkscape:window-x="0"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M24 2L6 10v12c0 11.11 7.67 21.47 18 24 10.33-2.53 18-12.89 18-24V10L24 2zm-4 32l-8-8 2.83-2.83L20 28.34l13.17-13.17L36 18 20 34z"
- id="path4"
- style="fill:#259b24;fill-opacity:0.627451" />
+<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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="ic_verified_fingerprint.svg">
+ <metadata id="metadata10">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs8" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="1916" inkscape:window-height="1156"
+ id="namedview6" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="-3.3559322"
+ inkscape:cy="24" inkscape:window-x="0" inkscape:window-y="20" inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path
+ d="M24 2L6 10v12c0 11.11 7.67 21.47 18 24 10.33-2.53 18-12.89 18-24V10L24 2zm-4 32l-8-8 2.83-2.83L20 28.34l13.17-13.17L36 18 20 34z"
+ id="path4" style="fill:#259b24;fill-opacity:0.627451" />
</svg>
diff --git a/art/md_switch_thumb_disable.svg b/art/md_switch_thumb_disable.svg
index 63ba6bc1f..6639218fa 100644
--- a/art/md_switch_thumb_disable.svg
+++ b/art/md_switch_thumb_disable.svg
@@ -1,144 +1,67 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" 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="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" sodipodi:docname="md_switch_thumb_disable_centered_square.svg"
- viewBox="0 0 120 120" height="120" width="120" inkscape:version="0.91 r13725" version="1.1" id="svg2">
- <metadata
- id="metadata8">
- <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>
- <defs
- id="defs6">
- <linearGradient
- inkscape:collect="always"
- id="linearGradient4222">
- <stop
- style="stop-color:#000000;stop-opacity:1"
- offset="0"
- id="stop4224" />
- <stop
- style="stop-color:#ffffff;stop-opacity:1"
- offset="1"
- id="stop4226" />
- </linearGradient>
- <linearGradient
- id="linearGradient4179"
- osb:paint="gradient">
- <stop
- style="stop-color:#ffffff;stop-opacity:1;"
- offset="0"
- id="stop4181" />
- <stop
- style="stop-color:#ffffff;stop-opacity:0.25454545"
- offset="1"
- id="stop4183" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient4222"
- id="linearGradient4228"
- x1="159.38722"
- y1="19.802504"
- x2="212.27522"
- y2="19.802504"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(-260.32215,163.27594)" />
- <filter
- inkscape:collect="always"
- style="color-interpolation-filters:sRGB"
- id="filter4230"
- x="-0.012"
- width="1.024"
- y="-0.012"
- height="1.024">
- <feGaussianBlur
- inkscape:collect="always"
- stdDeviation="0.25916904"
- id="feGaussianBlur4232" />
- </filter>
- <filter
- inkscape:collect="always"
- style="color-interpolation-filters:sRGB"
- id="filter4371"
- x="-0.23999999"
- width="1.48"
- y="-0.23999999"
- height="1.48">
- <feGaussianBlur
- inkscape:collect="always"
- stdDeviation="5.2888"
- id="feGaussianBlur4373" />
- </filter>
- </defs>
- <sodipodi:namedview
- inkscape:current-layer="layer2"
- inkscape:window-maximized="1"
- inkscape:window-y="0"
- inkscape:window-x="1400"
- inkscape:cy="61.379767"
- inkscape:cx="10.572032"
- inkscape:zoom="3.8530612"
- showgrid="false"
- id="namedview4"
- inkscape:window-height="1024"
- inkscape:window-width="1680"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0"
- guidetolerance="10"
- gridtolerance="10"
- objecttolerance="10"
- borderopacity="1"
- bordercolor="#666666"
- pagecolor="#ffffff" />
- <g
- inkscape:groupmode="layer"
- id="layer1"
- inkscape:label="PNG"
- style="display:none"
- sodipodi:insensitive="true"
- transform="translate(0,-2.5)" />
- <g
- inkscape:groupmode="layer"
- id="layer2"
- inkscape:label="SVG"
- style="display:inline"
- transform="translate(0,-2.5)">
- <g
- id="g6404">
- <circle
- style="opacity:1;fill:#000404;fill-opacity:0.45531915;stroke:none;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4371)"
- id="circle4234"
- cx="59.999996"
- cy="66.499878"
- r="26.444" />
- <g
- transform="translate(3.3103058e-6,0.33229253)"
- id="g4148">
- <circle
- style="opacity:1;fill:#bdbdbd;fill-opacity:1;stroke:#bdbdbd;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="path4218"
- cx="59.999996"
- cy="62.167587"
- r="25.916904" />
- <circle
- r="25.916904"
- cy="183.07845"
- cx="-74.490921"
- id="circle4220"
- style="opacity:0.3;fill:none;fill-opacity:1;stroke:url(#linearGradient4228);stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4230)"
- transform="matrix(0,-1,1,0,-123.07845,-12.323334)" />
- </g>
+<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ 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="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"
+ sodipodi:docname="md_switch_thumb_disable_centered_square.svg" viewBox="0 0 120 120"
+ height="120" width="120" inkscape:version="0.91 r13725" version="1.1" id="svg2">
+ <metadata id="metadata8">
+ <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>
+ <defs id="defs6">
+ <linearGradient inkscape:collect="always" id="linearGradient4222">
+ <stop style="stop-color:#000000;stop-opacity:1" offset="0" id="stop4224" />
+ <stop style="stop-color:#ffffff;stop-opacity:1" offset="1" id="stop4226" />
+ </linearGradient>
+ <linearGradient id="linearGradient4179" osb:paint="gradient">
+ <stop style="stop-color:#ffffff;stop-opacity:1;" offset="0" id="stop4181" />
+ <stop style="stop-color:#ffffff;stop-opacity:0.25454545" offset="1" id="stop4183" />
+ </linearGradient>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient4222"
+ id="linearGradient4228" x1="159.38722" y1="19.802504" x2="212.27522" y2="19.802504"
+ gradientUnits="userSpaceOnUse" gradientTransform="translate(-260.32215,163.27594)" />
+ <filter inkscape:collect="always" style="color-interpolation-filters:sRGB" id="filter4230"
+ x="-0.012" width="1.024" y="-0.012" height="1.024">
+ <feGaussianBlur inkscape:collect="always" stdDeviation="0.25916904"
+ id="feGaussianBlur4232" />
+ </filter>
+ <filter inkscape:collect="always" style="color-interpolation-filters:sRGB" id="filter4371"
+ x="-0.23999999" width="1.48" y="-0.23999999" height="1.48">
+ <feGaussianBlur inkscape:collect="always" stdDeviation="5.2888"
+ id="feGaussianBlur4373" />
+ </filter>
+ </defs>
+ <sodipodi:namedview inkscape:current-layer="layer2" inkscape:window-maximized="1"
+ inkscape:window-y="0" inkscape:window-x="1400" inkscape:cy="61.379767"
+ inkscape:cx="10.572032" inkscape:zoom="3.8530612" showgrid="false" id="namedview4"
+ inkscape:window-height="1024" inkscape:window-width="1680" inkscape:pageshadow="2"
+ inkscape:pageopacity="0" guidetolerance="10" gridtolerance="10" objecttolerance="10"
+ borderopacity="1" bordercolor="#666666" pagecolor="#ffffff" />
+ <g inkscape:groupmode="layer" id="layer1" inkscape:label="PNG" style="display:none"
+ sodipodi:insensitive="true" transform="translate(0,-2.5)" />
+ <g inkscape:groupmode="layer" id="layer2" inkscape:label="SVG" style="display:inline"
+ transform="translate(0,-2.5)">
+ <g id="g6404">
+ <circle
+ style="opacity:1;fill:#000404;fill-opacity:0.45531915;stroke:none;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4371)"
+ id="circle4234" cx="59.999996" cy="66.499878" r="26.444" />
+ <g transform="translate(3.3103058e-6,0.33229253)" id="g4148">
+ <circle
+ style="opacity:1;fill:#bdbdbd;fill-opacity:1;stroke:#bdbdbd;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path4218" cx="59.999996" cy="62.167587" r="25.916904" />
+ <circle r="25.916904" cy="183.07845" cx="-74.490921" id="circle4220"
+ style="opacity:0.3;fill:none;fill-opacity:1;stroke:url(#linearGradient4228);stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4230)"
+ transform="matrix(0,-1,1,0,-123.07845,-12.323334)" />
+ </g>
+ </g>
</g>
- </g>
</svg>
diff --git a/art/md_switch_thumb_off_normal.svg b/art/md_switch_thumb_off_normal.svg
index a902b12f4..9c86e37d3 100644
--- a/art/md_switch_thumb_off_normal.svg
+++ b/art/md_switch_thumb_off_normal.svg
@@ -1,141 +1,63 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" 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="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" sodipodi:docname="md_switch_thumb_off_normal_centered.svg"
- viewBox="0 0 120 120" height="120" width="120" inkscape:version="0.91 r13725" version="1.1" id="svg2">
- <metadata
- id="metadata8">
- <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>
- <defs
- id="defs6">
- <linearGradient
- inkscape:collect="always"
- id="linearGradient4222">
- <stop
- style="stop-color:#000000;stop-opacity:1"
- offset="0"
- id="stop4224" />
- <stop
- style="stop-color:#ffffff;stop-opacity:1"
- offset="1"
- id="stop4226" />
- </linearGradient>
- <linearGradient
- id="linearGradient4179"
- osb:paint="gradient">
- <stop
- style="stop-color:#ffffff;stop-opacity:1;"
- offset="0"
- id="stop4181" />
- <stop
- style="stop-color:#ffffff;stop-opacity:0.25454545"
- offset="1"
- id="stop4183" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient4222"
- id="linearGradient4228"
- x1="159.38722"
- y1="19.802504"
- x2="212.27522"
- y2="19.802504"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(-260.32215,163.27594)" />
- <filter
- inkscape:collect="always"
- style="color-interpolation-filters:sRGB"
- id="filter4230"
- x="-0.012"
- width="1.024"
- y="-0.012"
- height="1.024">
- <feGaussianBlur
- inkscape:collect="always"
- stdDeviation="0.25916904"
- id="feGaussianBlur4232" />
- </filter>
- <filter
- inkscape:collect="always"
- style="color-interpolation-filters:sRGB"
- id="filter4371"
- x="-0.23999999"
- width="1.48"
- y="-0.23999999"
- height="1.48">
- <feGaussianBlur
- inkscape:collect="always"
- stdDeviation="5.2888"
- id="feGaussianBlur4373" />
- </filter>
- </defs>
- <sodipodi:namedview
- inkscape:current-layer="layer2"
- inkscape:window-maximized="1"
- inkscape:window-y="0"
- inkscape:window-x="1400"
- inkscape:cy="61.379767"
- inkscape:cx="10.052965"
- inkscape:zoom="3.8530612"
- showgrid="false"
- id="namedview4"
- inkscape:window-height="1024"
- inkscape:window-width="1680"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0"
- guidetolerance="10"
- gridtolerance="10"
- objecttolerance="10"
- borderopacity="1"
- bordercolor="#666666"
- pagecolor="#ffffff" />
- <g
- inkscape:groupmode="layer"
- id="layer1"
- inkscape:label="PNG"
- style="display:none"
- sodipodi:insensitive="true"
- transform="translate(0,-2.5)" />
- <g
- inkscape:groupmode="layer"
- id="layer2"
- inkscape:label="SVG"
- style="display:inline"
- transform="translate(0,-2.5)">
- <circle
- r="26.444"
- cy="66.5"
- cx="59.999996"
- id="circle4234"
- style="opacity:1;fill:#000404;fill-opacity:0.45531915;stroke:none;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4371)" />
- <g
- id="g6390"
- transform="translate(3.3103058e-6,-0.91758577)">
- <circle
- r="25.916904"
- cy="63.417587"
- cx="59.999996"
- id="path4218"
- style="opacity:1;fill:#fafafa;fill-opacity:1;stroke:#fafafa;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- <circle
- transform="matrix(0,-1,1,0,-123.07845,-11.073334)"
- style="opacity:0.3;fill:none;fill-opacity:1;stroke:url(#linearGradient4228);stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4230)"
- id="circle4220"
- cx="-74.490921"
- cy="183.07845"
- r="25.916904" />
+<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ 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="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"
+ sodipodi:docname="md_switch_thumb_off_normal_centered.svg" viewBox="0 0 120 120" height="120"
+ width="120" inkscape:version="0.91 r13725" version="1.1" id="svg2">
+ <metadata id="metadata8">
+ <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>
+ <defs id="defs6">
+ <linearGradient inkscape:collect="always" id="linearGradient4222">
+ <stop style="stop-color:#000000;stop-opacity:1" offset="0" id="stop4224" />
+ <stop style="stop-color:#ffffff;stop-opacity:1" offset="1" id="stop4226" />
+ </linearGradient>
+ <linearGradient id="linearGradient4179" osb:paint="gradient">
+ <stop style="stop-color:#ffffff;stop-opacity:1;" offset="0" id="stop4181" />
+ <stop style="stop-color:#ffffff;stop-opacity:0.25454545" offset="1" id="stop4183" />
+ </linearGradient>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient4222"
+ id="linearGradient4228" x1="159.38722" y1="19.802504" x2="212.27522" y2="19.802504"
+ gradientUnits="userSpaceOnUse" gradientTransform="translate(-260.32215,163.27594)" />
+ <filter inkscape:collect="always" style="color-interpolation-filters:sRGB" id="filter4230"
+ x="-0.012" width="1.024" y="-0.012" height="1.024">
+ <feGaussianBlur inkscape:collect="always" stdDeviation="0.25916904"
+ id="feGaussianBlur4232" />
+ </filter>
+ <filter inkscape:collect="always" style="color-interpolation-filters:sRGB" id="filter4371"
+ x="-0.23999999" width="1.48" y="-0.23999999" height="1.48">
+ <feGaussianBlur inkscape:collect="always" stdDeviation="5.2888"
+ id="feGaussianBlur4373" />
+ </filter>
+ </defs>
+ <sodipodi:namedview inkscape:current-layer="layer2" inkscape:window-maximized="1"
+ inkscape:window-y="0" inkscape:window-x="1400" inkscape:cy="61.379767"
+ inkscape:cx="10.052965" inkscape:zoom="3.8530612" showgrid="false" id="namedview4"
+ inkscape:window-height="1024" inkscape:window-width="1680" inkscape:pageshadow="2"
+ inkscape:pageopacity="0" guidetolerance="10" gridtolerance="10" objecttolerance="10"
+ borderopacity="1" bordercolor="#666666" pagecolor="#ffffff" />
+ <g inkscape:groupmode="layer" id="layer1" inkscape:label="PNG" style="display:none"
+ sodipodi:insensitive="true" transform="translate(0,-2.5)" />
+ <g inkscape:groupmode="layer" id="layer2" inkscape:label="SVG" style="display:inline"
+ transform="translate(0,-2.5)">
+ <circle r="26.444" cy="66.5" cx="59.999996" id="circle4234"
+ style="opacity:1;fill:#000404;fill-opacity:0.45531915;stroke:none;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4371)" />
+ <g id="g6390" transform="translate(3.3103058e-6,-0.91758577)">
+ <circle r="25.916904" cy="63.417587" cx="59.999996" id="path4218"
+ style="opacity:1;fill:#fafafa;fill-opacity:1;stroke:#fafafa;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <circle transform="matrix(0,-1,1,0,-123.07845,-11.073334)"
+ style="opacity:0.3;fill:none;fill-opacity:1;stroke:url(#linearGradient4228);stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4230)"
+ id="circle4220" cx="-74.490921" cy="183.07845" r="25.916904" />
+ </g>
</g>
- </g>
</svg>
diff --git a/art/md_switch_thumb_off_pressed.svg b/art/md_switch_thumb_off_pressed.svg
index 47a8d3bd4..fecb6bc2d 100644
--- a/art/md_switch_thumb_off_pressed.svg
+++ b/art/md_switch_thumb_off_pressed.svg
@@ -1,147 +1,66 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" 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="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" sodipodi:docname="md_switch_thumb_off_pressed_centered.svg"
- viewBox="0 0 120 120" height="120" width="120" inkscape:version="0.91 r13725" version="1.1" id="svg2">
- <metadata
- id="metadata8">
- <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>
- <defs
- id="defs6">
- <linearGradient
- inkscape:collect="always"
- id="linearGradient4222">
- <stop
- style="stop-color:#000000;stop-opacity:1"
- offset="0"
- id="stop4224" />
- <stop
- style="stop-color:#ffffff;stop-opacity:1"
- offset="1"
- id="stop4226" />
- </linearGradient>
- <linearGradient
- id="linearGradient4179"
- osb:paint="gradient">
- <stop
- style="stop-color:#ffffff;stop-opacity:1;"
- offset="0"
- id="stop4181" />
- <stop
- style="stop-color:#ffffff;stop-opacity:0.25454545"
- offset="1"
- id="stop4183" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient4222"
- id="linearGradient4228"
- x1="159.38722"
- y1="19.802504"
- x2="212.27522"
- y2="19.802504"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(-260.32215,163.27594)" />
- <filter
- inkscape:collect="always"
- style="color-interpolation-filters:sRGB"
- id="filter4230"
- x="-0.012"
- width="1.024"
- y="-0.012"
- height="1.024">
- <feGaussianBlur
- inkscape:collect="always"
- stdDeviation="0.25916904"
- id="feGaussianBlur4232" />
- </filter>
- <filter
- inkscape:collect="always"
- style="color-interpolation-filters:sRGB"
- id="filter4371"
- x="-0.23999999"
- width="1.48"
- y="-0.23999999"
- height="1.48">
- <feGaussianBlur
- inkscape:collect="always"
- stdDeviation="5.2888"
- id="feGaussianBlur4373" />
- </filter>
- </defs>
- <sodipodi:namedview
- inkscape:current-layer="layer2"
- inkscape:window-maximized="1"
- inkscape:window-y="0"
- inkscape:window-x="1400"
- inkscape:cy="61.379767"
- inkscape:cx="10.572032"
- inkscape:zoom="3.8530612"
- showgrid="false"
- id="namedview4"
- inkscape:window-height="1024"
- inkscape:window-width="1680"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0"
- guidetolerance="10"
- gridtolerance="10"
- objecttolerance="10"
- borderopacity="1"
- bordercolor="#666666"
- pagecolor="#ffffff" />
- <g
- inkscape:groupmode="layer"
- id="layer1"
- inkscape:label="PNG"
- style="display:none"
- sodipodi:insensitive="true"
- transform="translate(0,-2.5)" />
- <g
- inkscape:groupmode="layer"
- id="layer2"
- inkscape:label="SVG"
- style="display:inline"
- transform="translate(0,-2.5)">
- <circle
- style="opacity:1;fill:#313131;fill-opacity:0.10196078;fill-rule:nonzero;stroke:none;stroke-width:1.00100005;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.10196078"
- id="path4819"
- cx="60"
- cy="62.5"
- r="60" />
- <circle
- r="26.444"
- cy="66.5"
- cx="59.999996"
- id="circle4234"
- style="opacity:1;fill:#000404;fill-opacity:0.45531915;stroke:none;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4371)" />
- <g
- id="g6417"
- transform="translate(3.3103058e-6,-0.91758577)">
- <circle
- r="25.916904"
- cy="63.417587"
- cx="59.999996"
- id="path4218"
- style="opacity:1;fill:#fafafa;fill-opacity:1;stroke:#fafafa;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- <circle
- transform="matrix(0,-1,1,0,-123.07845,-11.073334)"
- style="opacity:0.3;fill:none;fill-opacity:1;stroke:url(#linearGradient4228);stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4230)"
- id="circle4220"
- cx="-74.490921"
- cy="183.07845"
- r="25.916904" />
+<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ 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="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"
+ sodipodi:docname="md_switch_thumb_off_pressed_centered.svg" viewBox="0 0 120 120" height="120"
+ width="120" inkscape:version="0.91 r13725" version="1.1" id="svg2">
+ <metadata id="metadata8">
+ <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>
+ <defs id="defs6">
+ <linearGradient inkscape:collect="always" id="linearGradient4222">
+ <stop style="stop-color:#000000;stop-opacity:1" offset="0" id="stop4224" />
+ <stop style="stop-color:#ffffff;stop-opacity:1" offset="1" id="stop4226" />
+ </linearGradient>
+ <linearGradient id="linearGradient4179" osb:paint="gradient">
+ <stop style="stop-color:#ffffff;stop-opacity:1;" offset="0" id="stop4181" />
+ <stop style="stop-color:#ffffff;stop-opacity:0.25454545" offset="1" id="stop4183" />
+ </linearGradient>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient4222"
+ id="linearGradient4228" x1="159.38722" y1="19.802504" x2="212.27522" y2="19.802504"
+ gradientUnits="userSpaceOnUse" gradientTransform="translate(-260.32215,163.27594)" />
+ <filter inkscape:collect="always" style="color-interpolation-filters:sRGB" id="filter4230"
+ x="-0.012" width="1.024" y="-0.012" height="1.024">
+ <feGaussianBlur inkscape:collect="always" stdDeviation="0.25916904"
+ id="feGaussianBlur4232" />
+ </filter>
+ <filter inkscape:collect="always" style="color-interpolation-filters:sRGB" id="filter4371"
+ x="-0.23999999" width="1.48" y="-0.23999999" height="1.48">
+ <feGaussianBlur inkscape:collect="always" stdDeviation="5.2888"
+ id="feGaussianBlur4373" />
+ </filter>
+ </defs>
+ <sodipodi:namedview inkscape:current-layer="layer2" inkscape:window-maximized="1"
+ inkscape:window-y="0" inkscape:window-x="1400" inkscape:cy="61.379767"
+ inkscape:cx="10.572032" inkscape:zoom="3.8530612" showgrid="false" id="namedview4"
+ inkscape:window-height="1024" inkscape:window-width="1680" inkscape:pageshadow="2"
+ inkscape:pageopacity="0" guidetolerance="10" gridtolerance="10" objecttolerance="10"
+ borderopacity="1" bordercolor="#666666" pagecolor="#ffffff" />
+ <g inkscape:groupmode="layer" id="layer1" inkscape:label="PNG" style="display:none"
+ sodipodi:insensitive="true" transform="translate(0,-2.5)" />
+ <g inkscape:groupmode="layer" id="layer2" inkscape:label="SVG" style="display:inline"
+ transform="translate(0,-2.5)">
+ <circle
+ style="opacity:1;fill:#313131;fill-opacity:0.10196078;fill-rule:nonzero;stroke:none;stroke-width:1.00100005;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.10196078"
+ id="path4819" cx="60" cy="62.5" r="60" />
+ <circle r="26.444" cy="66.5" cx="59.999996" id="circle4234"
+ style="opacity:1;fill:#000404;fill-opacity:0.45531915;stroke:none;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4371)" />
+ <g id="g6417" transform="translate(3.3103058e-6,-0.91758577)">
+ <circle r="25.916904" cy="63.417587" cx="59.999996" id="path4218"
+ style="opacity:1;fill:#fafafa;fill-opacity:1;stroke:#fafafa;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <circle transform="matrix(0,-1,1,0,-123.07845,-11.073334)"
+ style="opacity:0.3;fill:none;fill-opacity:1;stroke:url(#linearGradient4228);stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4230)"
+ id="circle4220" cx="-74.490921" cy="183.07845" r="25.916904" />
+ </g>
</g>
- </g>
</svg>
diff --git a/art/md_switch_thumb_on_normal.svg b/art/md_switch_thumb_on_normal.svg
index 0082a8bf1..8b66115e2 100644
--- a/art/md_switch_thumb_on_normal.svg
+++ b/art/md_switch_thumb_on_normal.svg
@@ -1,134 +1,61 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" 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="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" sodipodi:docname="md_switch_thumb_on_normal_centered_square.svg"
- viewBox="0 0 120 120" height="120" width="120" inkscape:version="0.91 r13725" version="1.1" id="svg2">
- <metadata
- id="metadata8">
- <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>
- <defs
- id="defs6">
- <linearGradient
- inkscape:collect="always"
- id="linearGradient4222">
- <stop
- style="stop-color:#000000;stop-opacity:1"
- offset="0"
- id="stop4224" />
- <stop
- style="stop-color:#ffffff;stop-opacity:1"
- offset="1"
- id="stop4226" />
- </linearGradient>
- <linearGradient
- id="linearGradient4179"
- osb:paint="gradient">
- <stop
- style="stop-color:#ffffff;stop-opacity:1;"
- offset="0"
- id="stop4181" />
- <stop
- style="stop-color:#ffffff;stop-opacity:0.25454545"
- offset="1"
- id="stop4183" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient4222"
- id="linearGradient4228"
- x1="159.38722"
- y1="19.802504"
- x2="212.27522"
- y2="19.802504"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(-260.32215,163.27594)" />
- <filter
- inkscape:collect="always"
- style="color-interpolation-filters:sRGB"
- id="filter4230"
- x="-0.012"
- width="1.024"
- y="-0.012"
- height="1.024">
- <feGaussianBlur
- inkscape:collect="always"
- stdDeviation="0.25916904"
- id="feGaussianBlur4232" />
- </filter>
- <filter
- inkscape:collect="always"
- style="color-interpolation-filters:sRGB"
- id="filter4371"
- x="-0.23999999"
- width="1.48"
- y="-0.23999999"
- height="1.48">
- <feGaussianBlur
- inkscape:collect="always"
- stdDeviation="5.2888"
- id="feGaussianBlur4373" />
- </filter>
- </defs>
- <sodipodi:namedview
- inkscape:current-layer="layer2"
- inkscape:window-maximized="1"
- inkscape:window-y="0"
- inkscape:window-x="1400"
- inkscape:cy="61.379767"
- inkscape:cx="-14.397519"
- inkscape:zoom="3.8530612"
- showgrid="false"
- id="namedview4"
- inkscape:window-height="1024"
- inkscape:window-width="1680"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0"
- guidetolerance="10"
- gridtolerance="10"
- objecttolerance="10"
- borderopacity="1"
- bordercolor="#666666"
- pagecolor="#ffffff" />
- <g
- inkscape:groupmode="layer"
- id="layer2"
- inkscape:label="SVG"
- style="display:inline"
- transform="translate(0,-2.5)">
- <circle
- r="26.444"
- cy="66.499878"
- cx="59.999996"
- id="circle4234"
- style="opacity:1;fill:#000404;fill-opacity:0.45531915;stroke:none;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4371)" />
- <g
- id="g6440"
- transform="translate(3.3103058e-6,0.33241423)">
- <circle
- r="25.916904"
- cy="62.167587"
- cx="59.999996"
- id="path4218"
- style="opacity:1;fill:#0091ea;fill-opacity:1;stroke:#0091ea;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- <circle
- transform="matrix(0,-1,1,0,-123.07845,-12.323334)"
- style="opacity:0.3;fill:none;fill-opacity:1;stroke:url(#linearGradient4228);stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4230)"
- id="circle4220"
- cx="-74.490921"
- cy="183.07845"
- r="25.916904" />
+<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ 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="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"
+ sodipodi:docname="md_switch_thumb_on_normal_centered_square.svg" viewBox="0 0 120 120"
+ height="120" width="120" inkscape:version="0.91 r13725" version="1.1" id="svg2">
+ <metadata id="metadata8">
+ <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>
+ <defs id="defs6">
+ <linearGradient inkscape:collect="always" id="linearGradient4222">
+ <stop style="stop-color:#000000;stop-opacity:1" offset="0" id="stop4224" />
+ <stop style="stop-color:#ffffff;stop-opacity:1" offset="1" id="stop4226" />
+ </linearGradient>
+ <linearGradient id="linearGradient4179" osb:paint="gradient">
+ <stop style="stop-color:#ffffff;stop-opacity:1;" offset="0" id="stop4181" />
+ <stop style="stop-color:#ffffff;stop-opacity:0.25454545" offset="1" id="stop4183" />
+ </linearGradient>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient4222"
+ id="linearGradient4228" x1="159.38722" y1="19.802504" x2="212.27522" y2="19.802504"
+ gradientUnits="userSpaceOnUse" gradientTransform="translate(-260.32215,163.27594)" />
+ <filter inkscape:collect="always" style="color-interpolation-filters:sRGB" id="filter4230"
+ x="-0.012" width="1.024" y="-0.012" height="1.024">
+ <feGaussianBlur inkscape:collect="always" stdDeviation="0.25916904"
+ id="feGaussianBlur4232" />
+ </filter>
+ <filter inkscape:collect="always" style="color-interpolation-filters:sRGB" id="filter4371"
+ x="-0.23999999" width="1.48" y="-0.23999999" height="1.48">
+ <feGaussianBlur inkscape:collect="always" stdDeviation="5.2888"
+ id="feGaussianBlur4373" />
+ </filter>
+ </defs>
+ <sodipodi:namedview inkscape:current-layer="layer2" inkscape:window-maximized="1"
+ inkscape:window-y="0" inkscape:window-x="1400" inkscape:cy="61.379767"
+ inkscape:cx="-14.397519" inkscape:zoom="3.8530612" showgrid="false" id="namedview4"
+ inkscape:window-height="1024" inkscape:window-width="1680" inkscape:pageshadow="2"
+ inkscape:pageopacity="0" guidetolerance="10" gridtolerance="10" objecttolerance="10"
+ borderopacity="1" bordercolor="#666666" pagecolor="#ffffff" />
+ <g inkscape:groupmode="layer" id="layer2" inkscape:label="SVG" style="display:inline"
+ transform="translate(0,-2.5)">
+ <circle r="26.444" cy="66.499878" cx="59.999996" id="circle4234"
+ style="opacity:1;fill:#000404;fill-opacity:0.45531915;stroke:none;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4371)" />
+ <g id="g6440" transform="translate(3.3103058e-6,0.33241423)">
+ <circle r="25.916904" cy="62.167587" cx="59.999996" id="path4218"
+ style="opacity:1;fill:#0091ea;fill-opacity:1;stroke:#0091ea;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <circle transform="matrix(0,-1,1,0,-123.07845,-12.323334)"
+ style="opacity:0.3;fill:none;fill-opacity:1;stroke:url(#linearGradient4228);stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4230)"
+ id="circle4220" cx="-74.490921" cy="183.07845" r="25.916904" />
+ </g>
</g>
- </g>
</svg>
diff --git a/art/md_switch_thumb_on_pressed.svg b/art/md_switch_thumb_on_pressed.svg
index cf828b3a4..90d14cbe4 100644
--- a/art/md_switch_thumb_on_pressed.svg
+++ b/art/md_switch_thumb_on_pressed.svg
@@ -1,150 +1,70 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" 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="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" sodipodi:docname="md_switch_thumb_on_pressed_centered_square.svg"
- viewBox="0 0 120 120" height="120" width="120" inkscape:version="0.91 r13725" version="1.1" id="svg2">
- <metadata
- id="metadata8">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs6">
- <linearGradient
- inkscape:collect="always"
- id="linearGradient4222">
- <stop
- style="stop-color:#000000;stop-opacity:1"
- offset="0"
- id="stop4224" />
- <stop
- style="stop-color:#ffffff;stop-opacity:1"
- offset="1"
- id="stop4226" />
- </linearGradient>
- <linearGradient
- id="linearGradient4179"
- osb:paint="gradient">
- <stop
- style="stop-color:#ffffff;stop-opacity:1;"
- offset="0"
- id="stop4181" />
- <stop
- style="stop-color:#ffffff;stop-opacity:0.25454545"
- offset="1"
- id="stop4183" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient4222"
- id="linearGradient4228"
- x1="159.38722"
- y1="19.802504"
- x2="212.27522"
- y2="19.802504"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(-260.32215,163.27594)" />
- <filter
- inkscape:collect="always"
- style="color-interpolation-filters:sRGB"
- id="filter4230"
- x="-0.012"
- width="1.024"
- y="-0.012"
- height="1.024">
- <feGaussianBlur
- inkscape:collect="always"
- stdDeviation="0.25916904"
- id="feGaussianBlur4232" />
- </filter>
- <filter
- inkscape:collect="always"
- style="color-interpolation-filters:sRGB"
- id="filter4371"
- x="-0.23999999"
- width="1.48"
- y="-0.23999999"
- height="1.48">
- <feGaussianBlur
- inkscape:collect="always"
- stdDeviation="5.2888"
- id="feGaussianBlur4373" />
- </filter>
- </defs>
- <sodipodi:namedview
- inkscape:current-layer="layer2"
- inkscape:window-maximized="1"
- inkscape:window-y="0"
- inkscape:window-x="1400"
- inkscape:cy="61.379767"
- inkscape:cx="-46.31369"
- inkscape:zoom="3.8530612"
- showgrid="false"
- id="namedview4"
- inkscape:window-height="1024"
- inkscape:window-width="1680"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0"
- guidetolerance="10"
- gridtolerance="10"
- objecttolerance="10"
- borderopacity="1"
- bordercolor="#666666"
- pagecolor="#ffffff" />
- <g
- inkscape:groupmode="layer"
- id="layer1"
- inkscape:label="PNG"
- style="display:none"
- sodipodi:insensitive="true"
- transform="translate(0,-2.5)" />
- <g
- inkscape:groupmode="layer"
- id="layer2"
- inkscape:label="SVG"
- style="display:inline"
- transform="translate(0,-2.5)">
- <circle
- style="opacity:1;fill:#0093e8;fill-opacity:0.10196078;fill-rule:nonzero;stroke:none;stroke-width:1.00100005;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.10196078"
- id="path4819"
- cx="60"
- cy="62.5"
- r="60" />
- <g
- id="g4156">
- <circle
- style="opacity:1;fill:#000404;fill-opacity:0.45531915;stroke:none;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4371)"
- id="circle4234"
- cx="59.999996"
- cy="66.5"
- r="26.444" />
- <g
- transform="translate(3.3103058e-6,0.33241423)"
- id="g4149">
+<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ 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="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"
+ sodipodi:docname="md_switch_thumb_on_pressed_centered_square.svg" viewBox="0 0 120 120"
+ height="120" width="120" inkscape:version="0.91 r13725" version="1.1" id="svg2">
+ <metadata id="metadata8">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs6">
+ <linearGradient inkscape:collect="always" id="linearGradient4222">
+ <stop style="stop-color:#000000;stop-opacity:1" offset="0" id="stop4224" />
+ <stop style="stop-color:#ffffff;stop-opacity:1" offset="1" id="stop4226" />
+ </linearGradient>
+ <linearGradient id="linearGradient4179" osb:paint="gradient">
+ <stop style="stop-color:#ffffff;stop-opacity:1;" offset="0" id="stop4181" />
+ <stop style="stop-color:#ffffff;stop-opacity:0.25454545" offset="1" id="stop4183" />
+ </linearGradient>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient4222"
+ id="linearGradient4228" x1="159.38722" y1="19.802504" x2="212.27522" y2="19.802504"
+ gradientUnits="userSpaceOnUse" gradientTransform="translate(-260.32215,163.27594)" />
+ <filter inkscape:collect="always" style="color-interpolation-filters:sRGB" id="filter4230"
+ x="-0.012" width="1.024" y="-0.012" height="1.024">
+ <feGaussianBlur inkscape:collect="always" stdDeviation="0.25916904"
+ id="feGaussianBlur4232" />
+ </filter>
+ <filter inkscape:collect="always" style="color-interpolation-filters:sRGB" id="filter4371"
+ x="-0.23999999" width="1.48" y="-0.23999999" height="1.48">
+ <feGaussianBlur inkscape:collect="always" stdDeviation="5.2888"
+ id="feGaussianBlur4373" />
+ </filter>
+ </defs>
+ <sodipodi:namedview inkscape:current-layer="layer2" inkscape:window-maximized="1"
+ inkscape:window-y="0" inkscape:window-x="1400" inkscape:cy="61.379767"
+ inkscape:cx="-46.31369" inkscape:zoom="3.8530612" showgrid="false" id="namedview4"
+ inkscape:window-height="1024" inkscape:window-width="1680" inkscape:pageshadow="2"
+ inkscape:pageopacity="0" guidetolerance="10" gridtolerance="10" objecttolerance="10"
+ borderopacity="1" bordercolor="#666666" pagecolor="#ffffff" />
+ <g inkscape:groupmode="layer" id="layer1" inkscape:label="PNG" style="display:none"
+ sodipodi:insensitive="true" transform="translate(0,-2.5)" />
+ <g inkscape:groupmode="layer" id="layer2" inkscape:label="SVG" style="display:inline"
+ transform="translate(0,-2.5)">
<circle
- style="opacity:1;fill:#0091ea;fill-opacity:1;stroke:#0091ea;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="path4218"
- cx="59.999996"
- cy="62.167587"
- r="25.916904" />
- <circle
- r="25.916904"
- cy="183.07845"
- cx="-74.490921"
- id="circle4220"
- style="opacity:0.3;fill:none;fill-opacity:1;stroke:url(#linearGradient4228);stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4230)"
- transform="matrix(0,-1,1,0,-123.07845,-12.323334)" />
- </g>
+ style="opacity:1;fill:#0093e8;fill-opacity:0.10196078;fill-rule:nonzero;stroke:none;stroke-width:1.00100005;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.10196078"
+ id="path4819" cx="60" cy="62.5" r="60" />
+ <g id="g4156">
+ <circle
+ style="opacity:1;fill:#000404;fill-opacity:0.45531915;stroke:none;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4371)"
+ id="circle4234" cx="59.999996" cy="66.5" r="26.444" />
+ <g transform="translate(3.3103058e-6,0.33241423)" id="g4149">
+ <circle
+ style="opacity:1;fill:#0091ea;fill-opacity:1;stroke:#0091ea;stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path4218" cx="59.999996" cy="62.167587" r="25.916904" />
+ <circle r="25.916904" cy="183.07845" cx="-74.490921" id="circle4220"
+ style="opacity:0.3;fill:none;fill-opacity:1;stroke:url(#linearGradient4228);stroke-width:1.05419147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4230)"
+ transform="matrix(0,-1,1,0,-123.07845,-12.323334)" />
+ </g>
+ </g>
</g>
- </g>
</svg>
diff --git a/art/message_bubble_received.svg b/art/message_bubble_received.svg
index ea242146f..5e3d9fe0e 100644
--- a/art/message_bubble_received.svg
+++ b/art/message_bubble_received.svg
@@ -1,155 +1,60 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?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="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+<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="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="36" height="26" id="svg2"
version="1.1" inkscape:version="0.48.5 r10040" sodipodi:docname="message_bubble_received.svg">
- <defs
- id="defs4">
- <filter
- x="-0.25"
- y="-0.25"
- width="1.5"
- height="1.5"
- inkscape:label="Drop Shadow"
- id="filter3811"
- color-interpolation-filters="sRGB">
- <feFlood
- flood-opacity="0.25"
- flood-color="rgb(0,0,0)"
- result="flood"
- id="feFlood3813" />
- <feComposite
- in="flood"
- in2="SourceGraphic"
- operator="in"
- result="composite1"
- id="feComposite3815" />
- <feGaussianBlur
- stdDeviation="0.5"
- result="blur"
- id="feGaussianBlur3817" />
- <feOffset
- dx="0"
- dy="1"
- result="offset"
- id="feOffset3819" />
- <feComposite
- in="SourceGraphic"
- in2="offset"
- operator="over"
- result="composite2"
- id="feComposite3821" />
- </filter>
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="16"
- inkscape:cx="25.745257"
- inkscape:cy="9.618802"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="true"
- inkscape:window-width="989"
- inkscape:window-height="755"
- inkscape:window-x="22"
- inkscape:window-y="16"
- inkscape:window-maximized="0"
- showguides="true"
- inkscape:guide-bbox="true"
- guidecolor="#000000"
- guideopacity="0.49803922">
- <inkscape:grid
- type="xygrid"
- id="grid2985"
- empspacing="4"
- visible="true"
- enabled="true"
- snapvisiblegridlinesonly="true"
- spacingx="1px"
- spacingy="1px"
- originx="0px"
- originy="0px"
- color="#0000ff"
- opacity="0.03137255" />
- <sodipodi:guide
- orientation="1,0"
- position="20,26"
- id="guide3060" />
- <sodipodi:guide
- orientation="1,0"
- position="24,26"
- id="guide3062" />
- <sodipodi:guide
- orientation="0,1"
- position="36,22"
- id="guide3064" />
- <sodipodi:guide
- orientation="0,1"
- position="36,6"
- id="guide3066" />
- <sodipodi:guide
- orientation="1,0"
- position="26,0"
- id="guide3068" />
- <sodipodi:guide
- orientation="1,0"
- position="18,0"
- id="guide3070" />
- <sodipodi:guide
- orientation="0,1"
- position="0,10"
- id="guide3074" />
- <sodipodi:guide
- orientation="0,1"
- position="0,8"
- id="guide3076" />
- </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"
- inkscape:groupmode="layer"
- id="layer"
- transform="translate(0,-2)">
- <g
- id="g3759"
- style="fill:#fafafa;fill-opacity:1;stroke:none;fill-rule:nonzero;filter:url(#filter3811)">
- <path
- style="display:none"
- d="m 8,6 c 2,2 4,6 4,10 L 16,6 z"
- id="path3805"
- inkscape:connector-curvature="0"
- transform="translate(0,2)"
- sodipodi:nodetypes="cccc" />
- <path
- inkscape:connector-curvature="0"
- id="path2989"
- d="M 4,4 16,16 16,4 z"
- sodipodi:nodetypes="cccc" />
- <rect
- ry="2"
- y="4"
- x="12"
- height="20"
- width="20"
- id="rect2987" />
+ <defs id="defs4">
+ <filter x="-0.25" y="-0.25" width="1.5" height="1.5" inkscape:label="Drop Shadow"
+ id="filter3811" color-interpolation-filters="sRGB">
+ <feFlood flood-opacity="0.25" flood-color="rgb(0,0,0)" result="flood"
+ id="feFlood3813" />
+ <feComposite in="flood" in2="SourceGraphic" operator="in" result="composite1"
+ id="feComposite3815" />
+ <feGaussianBlur stdDeviation="0.5" result="blur" id="feGaussianBlur3817" />
+ <feOffset dx="0" dy="1" result="offset" id="feOffset3819" />
+ <feComposite in="SourceGraphic" in2="offset" operator="over" result="composite2"
+ id="feComposite3821" />
+ </filter>
+ </defs>
+ <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0"
+ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="16"
+ inkscape:cx="25.745257" inkscape:cy="9.618802" inkscape:document-units="px"
+ inkscape:current-layer="layer1" showgrid="true" inkscape:window-width="989"
+ inkscape:window-height="755" inkscape:window-x="22" inkscape:window-y="16"
+ inkscape:window-maximized="0" showguides="true" inkscape:guide-bbox="true"
+ guidecolor="#000000" guideopacity="0.49803922">
+ <inkscape:grid type="xygrid" id="grid2985" empspacing="4" visible="true" enabled="true"
+ snapvisiblegridlinesonly="true" spacingx="1px" spacingy="1px" originx="0px"
+ originy="0px" color="#0000ff" opacity="0.03137255" />
+ <sodipodi:guide orientation="1,0" position="20,26" id="guide3060" />
+ <sodipodi:guide orientation="1,0" position="24,26" id="guide3062" />
+ <sodipodi:guide orientation="0,1" position="36,22" id="guide3064" />
+ <sodipodi:guide orientation="0,1" position="36,6" id="guide3066" />
+ <sodipodi:guide orientation="1,0" position="26,0" id="guide3068" />
+ <sodipodi:guide orientation="1,0" position="18,0" id="guide3070" />
+ <sodipodi:guide orientation="0,1" position="0,10" id="guide3074" />
+ <sodipodi:guide orientation="0,1" position="0,8" id="guide3076" />
+ </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" inkscape:groupmode="layer" id="layer" transform="translate(0,-2)">
+ <g id="g3759"
+ style="fill:#fafafa;fill-opacity:1;stroke:none;fill-rule:nonzero;filter:url(#filter3811)">
+ <path style="display:none" d="m 8,6 c 2,2 4,6 4,10 L 16,6 z" id="path3805"
+ inkscape:connector-curvature="0" transform="translate(0,2)"
+ sodipodi:nodetypes="cccc" />
+ <path inkscape:connector-curvature="0" id="path2989" d="M 4,4 16,16 16,4 z"
+ sodipodi:nodetypes="cccc" />
+ <rect ry="2" y="4" x="12" height="20" width="20" id="rect2987" />
+ </g>
</g>
- </g>
</svg>
diff --git a/art/message_bubble_received_warning.svg b/art/message_bubble_received_warning.svg
index d76197914..e24f27b94 100644
--- a/art/message_bubble_received_warning.svg
+++ b/art/message_bubble_received_warning.svg
@@ -1,155 +1,60 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?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="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+<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="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="36" height="26" id="svg2"
version="1.1" inkscape:version="0.48.5 r10040" sodipodi:docname="message_bubble_received.svg">
- <defs
- id="defs4">
- <filter
- x="-0.25"
- y="-0.25"
- width="1.5"
- height="1.5"
- inkscape:label="Drop Shadow"
- id="filter3811"
- color-interpolation-filters="sRGB">
- <feFlood
- flood-opacity="0.25"
- flood-color="rgb(0,0,0)"
- result="flood"
- id="feFlood3813" />
- <feComposite
- in="flood"
- in2="SourceGraphic"
- operator="in"
- result="composite1"
- id="feComposite3815" />
- <feGaussianBlur
- stdDeviation="0.5"
- result="blur"
- id="feGaussianBlur3817" />
- <feOffset
- dx="0"
- dy="1"
- result="offset"
- id="feOffset3819" />
- <feComposite
- in="SourceGraphic"
- in2="offset"
- operator="over"
- result="composite2"
- id="feComposite3821" />
- </filter>
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="16"
- inkscape:cx="25.745257"
- inkscape:cy="9.618802"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="true"
- inkscape:window-width="989"
- inkscape:window-height="755"
- inkscape:window-x="22"
- inkscape:window-y="16"
- inkscape:window-maximized="0"
- showguides="true"
- inkscape:guide-bbox="true"
- guidecolor="#000000"
- guideopacity="0.49803922">
- <inkscape:grid
- type="xygrid"
- id="grid2985"
- empspacing="4"
- visible="true"
- enabled="true"
- snapvisiblegridlinesonly="true"
- spacingx="1px"
- spacingy="1px"
- originx="0px"
- originy="0px"
- color="#0000ff"
- opacity="0.03137255" />
- <sodipodi:guide
- orientation="1,0"
- position="20,26"
- id="guide3060" />
- <sodipodi:guide
- orientation="1,0"
- position="24,26"
- id="guide3062" />
- <sodipodi:guide
- orientation="0,1"
- position="36,22"
- id="guide3064" />
- <sodipodi:guide
- orientation="0,1"
- position="36,6"
- id="guide3066" />
- <sodipodi:guide
- orientation="1,0"
- position="26,0"
- id="guide3068" />
- <sodipodi:guide
- orientation="1,0"
- position="18,0"
- id="guide3070" />
- <sodipodi:guide
- orientation="0,1"
- position="0,10"
- id="guide3074" />
- <sodipodi:guide
- orientation="0,1"
- position="0,8"
- id="guide3076" />
- </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"
- inkscape:groupmode="layer"
- id="layer"
- transform="translate(0,-2)">
- <g
- id="g3759"
- style="fill:#c64545;fill-opacity:1;stroke:none;fill-rule:nonzero;filter:url(#filter3811)">
- <path
- style="display:none"
- d="m 8,6 c 2,2 4,6 4,10 L 16,6 z"
- id="path3805"
- inkscape:connector-curvature="0"
- transform="translate(0,2)"
- sodipodi:nodetypes="cccc" />
- <path
- inkscape:connector-curvature="0"
- id="path2989"
- d="M 4,4 16,16 16,4 z"
- sodipodi:nodetypes="cccc" />
- <rect
- ry="2"
- y="4"
- x="12"
- height="20"
- width="20"
- id="rect2987" />
+ <defs id="defs4">
+ <filter x="-0.25" y="-0.25" width="1.5" height="1.5" inkscape:label="Drop Shadow"
+ id="filter3811" color-interpolation-filters="sRGB">
+ <feFlood flood-opacity="0.25" flood-color="rgb(0,0,0)" result="flood"
+ id="feFlood3813" />
+ <feComposite in="flood" in2="SourceGraphic" operator="in" result="composite1"
+ id="feComposite3815" />
+ <feGaussianBlur stdDeviation="0.5" result="blur" id="feGaussianBlur3817" />
+ <feOffset dx="0" dy="1" result="offset" id="feOffset3819" />
+ <feComposite in="SourceGraphic" in2="offset" operator="over" result="composite2"
+ id="feComposite3821" />
+ </filter>
+ </defs>
+ <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0"
+ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="16"
+ inkscape:cx="25.745257" inkscape:cy="9.618802" inkscape:document-units="px"
+ inkscape:current-layer="layer1" showgrid="true" inkscape:window-width="989"
+ inkscape:window-height="755" inkscape:window-x="22" inkscape:window-y="16"
+ inkscape:window-maximized="0" showguides="true" inkscape:guide-bbox="true"
+ guidecolor="#000000" guideopacity="0.49803922">
+ <inkscape:grid type="xygrid" id="grid2985" empspacing="4" visible="true" enabled="true"
+ snapvisiblegridlinesonly="true" spacingx="1px" spacingy="1px" originx="0px"
+ originy="0px" color="#0000ff" opacity="0.03137255" />
+ <sodipodi:guide orientation="1,0" position="20,26" id="guide3060" />
+ <sodipodi:guide orientation="1,0" position="24,26" id="guide3062" />
+ <sodipodi:guide orientation="0,1" position="36,22" id="guide3064" />
+ <sodipodi:guide orientation="0,1" position="36,6" id="guide3066" />
+ <sodipodi:guide orientation="1,0" position="26,0" id="guide3068" />
+ <sodipodi:guide orientation="1,0" position="18,0" id="guide3070" />
+ <sodipodi:guide orientation="0,1" position="0,10" id="guide3074" />
+ <sodipodi:guide orientation="0,1" position="0,8" id="guide3076" />
+ </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" inkscape:groupmode="layer" id="layer" transform="translate(0,-2)">
+ <g id="g3759"
+ style="fill:#c64545;fill-opacity:1;stroke:none;fill-rule:nonzero;filter:url(#filter3811)">
+ <path style="display:none" d="m 8,6 c 2,2 4,6 4,10 L 16,6 z" id="path3805"
+ inkscape:connector-curvature="0" transform="translate(0,2)"
+ sodipodi:nodetypes="cccc" />
+ <path inkscape:connector-curvature="0" id="path2989" d="M 4,4 16,16 16,4 z"
+ sodipodi:nodetypes="cccc" />
+ <rect ry="2" y="4" x="12" height="20" width="20" id="rect2987" />
+ </g>
</g>
- </g>
</svg>
diff --git a/art/message_bubble_received_white.svg b/art/message_bubble_received_white.svg
index ea242146f..5e3d9fe0e 100644
--- a/art/message_bubble_received_white.svg
+++ b/art/message_bubble_received_white.svg
@@ -1,155 +1,60 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?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="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+<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="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="36" height="26" id="svg2"
version="1.1" inkscape:version="0.48.5 r10040" sodipodi:docname="message_bubble_received.svg">
- <defs
- id="defs4">
- <filter
- x="-0.25"
- y="-0.25"
- width="1.5"
- height="1.5"
- inkscape:label="Drop Shadow"
- id="filter3811"
- color-interpolation-filters="sRGB">
- <feFlood
- flood-opacity="0.25"
- flood-color="rgb(0,0,0)"
- result="flood"
- id="feFlood3813" />
- <feComposite
- in="flood"
- in2="SourceGraphic"
- operator="in"
- result="composite1"
- id="feComposite3815" />
- <feGaussianBlur
- stdDeviation="0.5"
- result="blur"
- id="feGaussianBlur3817" />
- <feOffset
- dx="0"
- dy="1"
- result="offset"
- id="feOffset3819" />
- <feComposite
- in="SourceGraphic"
- in2="offset"
- operator="over"
- result="composite2"
- id="feComposite3821" />
- </filter>
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="16"
- inkscape:cx="25.745257"
- inkscape:cy="9.618802"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="true"
- inkscape:window-width="989"
- inkscape:window-height="755"
- inkscape:window-x="22"
- inkscape:window-y="16"
- inkscape:window-maximized="0"
- showguides="true"
- inkscape:guide-bbox="true"
- guidecolor="#000000"
- guideopacity="0.49803922">
- <inkscape:grid
- type="xygrid"
- id="grid2985"
- empspacing="4"
- visible="true"
- enabled="true"
- snapvisiblegridlinesonly="true"
- spacingx="1px"
- spacingy="1px"
- originx="0px"
- originy="0px"
- color="#0000ff"
- opacity="0.03137255" />
- <sodipodi:guide
- orientation="1,0"
- position="20,26"
- id="guide3060" />
- <sodipodi:guide
- orientation="1,0"
- position="24,26"
- id="guide3062" />
- <sodipodi:guide
- orientation="0,1"
- position="36,22"
- id="guide3064" />
- <sodipodi:guide
- orientation="0,1"
- position="36,6"
- id="guide3066" />
- <sodipodi:guide
- orientation="1,0"
- position="26,0"
- id="guide3068" />
- <sodipodi:guide
- orientation="1,0"
- position="18,0"
- id="guide3070" />
- <sodipodi:guide
- orientation="0,1"
- position="0,10"
- id="guide3074" />
- <sodipodi:guide
- orientation="0,1"
- position="0,8"
- id="guide3076" />
- </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"
- inkscape:groupmode="layer"
- id="layer"
- transform="translate(0,-2)">
- <g
- id="g3759"
- style="fill:#fafafa;fill-opacity:1;stroke:none;fill-rule:nonzero;filter:url(#filter3811)">
- <path
- style="display:none"
- d="m 8,6 c 2,2 4,6 4,10 L 16,6 z"
- id="path3805"
- inkscape:connector-curvature="0"
- transform="translate(0,2)"
- sodipodi:nodetypes="cccc" />
- <path
- inkscape:connector-curvature="0"
- id="path2989"
- d="M 4,4 16,16 16,4 z"
- sodipodi:nodetypes="cccc" />
- <rect
- ry="2"
- y="4"
- x="12"
- height="20"
- width="20"
- id="rect2987" />
+ <defs id="defs4">
+ <filter x="-0.25" y="-0.25" width="1.5" height="1.5" inkscape:label="Drop Shadow"
+ id="filter3811" color-interpolation-filters="sRGB">
+ <feFlood flood-opacity="0.25" flood-color="rgb(0,0,0)" result="flood"
+ id="feFlood3813" />
+ <feComposite in="flood" in2="SourceGraphic" operator="in" result="composite1"
+ id="feComposite3815" />
+ <feGaussianBlur stdDeviation="0.5" result="blur" id="feGaussianBlur3817" />
+ <feOffset dx="0" dy="1" result="offset" id="feOffset3819" />
+ <feComposite in="SourceGraphic" in2="offset" operator="over" result="composite2"
+ id="feComposite3821" />
+ </filter>
+ </defs>
+ <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0"
+ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="16"
+ inkscape:cx="25.745257" inkscape:cy="9.618802" inkscape:document-units="px"
+ inkscape:current-layer="layer1" showgrid="true" inkscape:window-width="989"
+ inkscape:window-height="755" inkscape:window-x="22" inkscape:window-y="16"
+ inkscape:window-maximized="0" showguides="true" inkscape:guide-bbox="true"
+ guidecolor="#000000" guideopacity="0.49803922">
+ <inkscape:grid type="xygrid" id="grid2985" empspacing="4" visible="true" enabled="true"
+ snapvisiblegridlinesonly="true" spacingx="1px" spacingy="1px" originx="0px"
+ originy="0px" color="#0000ff" opacity="0.03137255" />
+ <sodipodi:guide orientation="1,0" position="20,26" id="guide3060" />
+ <sodipodi:guide orientation="1,0" position="24,26" id="guide3062" />
+ <sodipodi:guide orientation="0,1" position="36,22" id="guide3064" />
+ <sodipodi:guide orientation="0,1" position="36,6" id="guide3066" />
+ <sodipodi:guide orientation="1,0" position="26,0" id="guide3068" />
+ <sodipodi:guide orientation="1,0" position="18,0" id="guide3070" />
+ <sodipodi:guide orientation="0,1" position="0,10" id="guide3074" />
+ <sodipodi:guide orientation="0,1" position="0,8" id="guide3076" />
+ </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" inkscape:groupmode="layer" id="layer" transform="translate(0,-2)">
+ <g id="g3759"
+ style="fill:#fafafa;fill-opacity:1;stroke:none;fill-rule:nonzero;filter:url(#filter3811)">
+ <path style="display:none" d="m 8,6 c 2,2 4,6 4,10 L 16,6 z" id="path3805"
+ inkscape:connector-curvature="0" transform="translate(0,2)"
+ sodipodi:nodetypes="cccc" />
+ <path inkscape:connector-curvature="0" id="path2989" d="M 4,4 16,16 16,4 z"
+ sodipodi:nodetypes="cccc" />
+ <rect ry="2" y="4" x="12" height="20" width="20" id="rect2987" />
+ </g>
</g>
- </g>
</svg>
diff --git a/art/message_bubble_sent.svg b/art/message_bubble_sent.svg
index 06040a9f0..025ea3a73 100644
--- a/art/message_bubble_sent.svg
+++ b/art/message_bubble_sent.svg
@@ -1,155 +1,60 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?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="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+<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="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="36" height="26" id="svg2"
version="1.1" inkscape:version="0.48.5 r10040" sodipodi:docname="message_bubble_sent.svg">
- <defs
- id="defs4">
- <filter
- x="-0.25"
- y="-0.25"
- width="1.5"
- height="1.5"
- inkscape:label="Drop Shadow"
- id="filter3811"
- color-interpolation-filters="sRGB">
- <feFlood
- flood-opacity="0.25"
- flood-color="rgb(0,0,0)"
- result="flood"
- id="feFlood3813" />
- <feComposite
- in="flood"
- in2="SourceGraphic"
- operator="in"
- result="composite1"
- id="feComposite3815" />
- <feGaussianBlur
- stdDeviation="0.5"
- result="blur"
- id="feGaussianBlur3817" />
- <feOffset
- dx="0"
- dy="1"
- result="offset"
- id="feOffset3819" />
- <feComposite
- in="SourceGraphic"
- in2="offset"
- operator="over"
- result="composite2"
- id="feComposite3821" />
- </filter>
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="16"
- inkscape:cx="14.269338"
- inkscape:cy="16.118802"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="true"
- inkscape:window-width="989"
- inkscape:window-height="755"
- inkscape:window-x="434"
- inkscape:window-y="16"
- inkscape:window-maximized="0"
- showguides="true"
- inkscape:guide-bbox="true"
- guidecolor="#404040"
- guideopacity="0.49803922">
- <inkscape:grid
- type="xygrid"
- id="grid2985"
- empspacing="4"
- visible="true"
- enabled="true"
- snapvisiblegridlinesonly="true"
- spacingx="1px"
- spacingy="1px"
- originx="0px"
- originy="0px"
- color="#0000ff"
- opacity="0.03137255" />
- <sodipodi:guide
- orientation="1,0"
- position="12,26"
- id="guide3146" />
- <sodipodi:guide
- orientation="1,0"
- position="16,26"
- id="guide3148" />
- <sodipodi:guide
- orientation="0,1"
- position="36,22"
- id="guide3150" />
- <sodipodi:guide
- orientation="0,1"
- position="36,6"
- id="guide3152" />
- <sodipodi:guide
- orientation="1,0"
- position="18,0"
- id="guide3154" />
- <sodipodi:guide
- orientation="1,0"
- position="10,0"
- id="guide3160" />
- <sodipodi:guide
- orientation="0,1"
- position="0,20"
- id="guide3162" />
- <sodipodi:guide
- orientation="0,1"
- position="0,18"
- id="guide3164" />
- </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"
- inkscape:groupmode="layer"
- id="layer"
- transform="translate(0,-2)">
- <g
- id="g3759"
- style="fill:#e2e7f1;fill-opacity:1;stroke:none;fill-rule:nonzero;filter:url(#filter3811)">
- <path
- style="display:none"
- d="M 28,18 C 26,16 24,12 24,8 l -4,10 z"
- id="path3809"
- inkscape:connector-curvature="0"
- transform="translate(0,2)"
- sodipodi:nodetypes="cccc" />
- <path
- inkscape:connector-curvature="0"
- id="path2989"
- d="m 20,12 0,12 12,0 z"
- sodipodi:nodetypes="cccc" />
- <rect
- ry="2"
- y="4"
- x="4"
- height="20"
- width="20"
- id="rect2987" />
+ <defs id="defs4">
+ <filter x="-0.25" y="-0.25" width="1.5" height="1.5" inkscape:label="Drop Shadow"
+ id="filter3811" color-interpolation-filters="sRGB">
+ <feFlood flood-opacity="0.25" flood-color="rgb(0,0,0)" result="flood"
+ id="feFlood3813" />
+ <feComposite in="flood" in2="SourceGraphic" operator="in" result="composite1"
+ id="feComposite3815" />
+ <feGaussianBlur stdDeviation="0.5" result="blur" id="feGaussianBlur3817" />
+ <feOffset dx="0" dy="1" result="offset" id="feOffset3819" />
+ <feComposite in="SourceGraphic" in2="offset" operator="over" result="composite2"
+ id="feComposite3821" />
+ </filter>
+ </defs>
+ <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0"
+ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="16"
+ inkscape:cx="14.269338" inkscape:cy="16.118802" inkscape:document-units="px"
+ inkscape:current-layer="layer1" showgrid="true" inkscape:window-width="989"
+ inkscape:window-height="755" inkscape:window-x="434" inkscape:window-y="16"
+ inkscape:window-maximized="0" showguides="true" inkscape:guide-bbox="true"
+ guidecolor="#404040" guideopacity="0.49803922">
+ <inkscape:grid type="xygrid" id="grid2985" empspacing="4" visible="true" enabled="true"
+ snapvisiblegridlinesonly="true" spacingx="1px" spacingy="1px" originx="0px"
+ originy="0px" color="#0000ff" opacity="0.03137255" />
+ <sodipodi:guide orientation="1,0" position="12,26" id="guide3146" />
+ <sodipodi:guide orientation="1,0" position="16,26" id="guide3148" />
+ <sodipodi:guide orientation="0,1" position="36,22" id="guide3150" />
+ <sodipodi:guide orientation="0,1" position="36,6" id="guide3152" />
+ <sodipodi:guide orientation="1,0" position="18,0" id="guide3154" />
+ <sodipodi:guide orientation="1,0" position="10,0" id="guide3160" />
+ <sodipodi:guide orientation="0,1" position="0,20" id="guide3162" />
+ <sodipodi:guide orientation="0,1" position="0,18" id="guide3164" />
+ </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" inkscape:groupmode="layer" id="layer" transform="translate(0,-2)">
+ <g id="g3759"
+ style="fill:#e2e7f1;fill-opacity:1;stroke:none;fill-rule:nonzero;filter:url(#filter3811)">
+ <path style="display:none" d="M 28,18 C 26,16 24,12 24,8 l -4,10 z" id="path3809"
+ inkscape:connector-curvature="0" transform="translate(0,2)"
+ sodipodi:nodetypes="cccc" />
+ <path inkscape:connector-curvature="0" id="path2989" d="m 20,12 0,12 12,0 z"
+ sodipodi:nodetypes="cccc" />
+ <rect ry="2" y="4" x="4" height="20" width="20" id="rect2987" />
+ </g>
</g>
- </g>
</svg>
diff --git a/art/message_bubble_sent_white.svg b/art/message_bubble_sent_white.svg
index c5cfa5829..53ea90940 100644
--- a/art/message_bubble_sent_white.svg
+++ b/art/message_bubble_sent_white.svg
@@ -1,155 +1,60 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?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="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+<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="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="36" height="26" id="svg2"
version="1.1" inkscape:version="0.48.5 r10040" sodipodi:docname="message_bubble_sent.svg">
- <defs
- id="defs4">
- <filter
- x="-0.25"
- y="-0.25"
- width="1.5"
- height="1.5"
- inkscape:label="Drop Shadow"
- id="filter3811"
- color-interpolation-filters="sRGB">
- <feFlood
- flood-opacity="0.25"
- flood-color="rgb(0,0,0)"
- result="flood"
- id="feFlood3813" />
- <feComposite
- in="flood"
- in2="SourceGraphic"
- operator="in"
- result="composite1"
- id="feComposite3815" />
- <feGaussianBlur
- stdDeviation="0.5"
- result="blur"
- id="feGaussianBlur3817" />
- <feOffset
- dx="0"
- dy="1"
- result="offset"
- id="feOffset3819" />
- <feComposite
- in="SourceGraphic"
- in2="offset"
- operator="over"
- result="composite2"
- id="feComposite3821" />
- </filter>
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="16"
- inkscape:cx="14.269338"
- inkscape:cy="16.118802"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="true"
- inkscape:window-width="989"
- inkscape:window-height="755"
- inkscape:window-x="434"
- inkscape:window-y="16"
- inkscape:window-maximized="0"
- showguides="true"
- inkscape:guide-bbox="true"
- guidecolor="#404040"
- guideopacity="0.49803922">
- <inkscape:grid
- type="xygrid"
- id="grid2985"
- empspacing="4"
- visible="true"
- enabled="true"
- snapvisiblegridlinesonly="true"
- spacingx="1px"
- spacingy="1px"
- originx="0px"
- originy="0px"
- color="#0000ff"
- opacity="0.03137255" />
- <sodipodi:guide
- orientation="1,0"
- position="12,26"
- id="guide3146" />
- <sodipodi:guide
- orientation="1,0"
- position="16,26"
- id="guide3148" />
- <sodipodi:guide
- orientation="0,1"
- position="36,22"
- id="guide3150" />
- <sodipodi:guide
- orientation="0,1"
- position="36,6"
- id="guide3152" />
- <sodipodi:guide
- orientation="1,0"
- position="18,0"
- id="guide3154" />
- <sodipodi:guide
- orientation="1,0"
- position="10,0"
- id="guide3160" />
- <sodipodi:guide
- orientation="0,1"
- position="0,20"
- id="guide3162" />
- <sodipodi:guide
- orientation="0,1"
- position="0,18"
- id="guide3164" />
- </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"
- inkscape:groupmode="layer"
- id="layer"
- transform="translate(0,-2)">
- <g
- id="g3759"
- style="fill:#ffffff;fill-opacity:1;stroke:none;fill-rule:nonzero;filter:url(#filter3811)">
- <path
- style="display:none"
- d="M 28,18 C 26,16 24,12 24,8 l -4,10 z"
- id="path3809"
- inkscape:connector-curvature="0"
- transform="translate(0,2)"
- sodipodi:nodetypes="cccc" />
- <path
- inkscape:connector-curvature="0"
- id="path2989"
- d="m 20,12 0,12 12,0 z"
- sodipodi:nodetypes="cccc" />
- <rect
- ry="2"
- y="4"
- x="4"
- height="20"
- width="20"
- id="rect2987" />
+ <defs id="defs4">
+ <filter x="-0.25" y="-0.25" width="1.5" height="1.5" inkscape:label="Drop Shadow"
+ id="filter3811" color-interpolation-filters="sRGB">
+ <feFlood flood-opacity="0.25" flood-color="rgb(0,0,0)" result="flood"
+ id="feFlood3813" />
+ <feComposite in="flood" in2="SourceGraphic" operator="in" result="composite1"
+ id="feComposite3815" />
+ <feGaussianBlur stdDeviation="0.5" result="blur" id="feGaussianBlur3817" />
+ <feOffset dx="0" dy="1" result="offset" id="feOffset3819" />
+ <feComposite in="SourceGraphic" in2="offset" operator="over" result="composite2"
+ id="feComposite3821" />
+ </filter>
+ </defs>
+ <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0"
+ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="16"
+ inkscape:cx="14.269338" inkscape:cy="16.118802" inkscape:document-units="px"
+ inkscape:current-layer="layer1" showgrid="true" inkscape:window-width="989"
+ inkscape:window-height="755" inkscape:window-x="434" inkscape:window-y="16"
+ inkscape:window-maximized="0" showguides="true" inkscape:guide-bbox="true"
+ guidecolor="#404040" guideopacity="0.49803922">
+ <inkscape:grid type="xygrid" id="grid2985" empspacing="4" visible="true" enabled="true"
+ snapvisiblegridlinesonly="true" spacingx="1px" spacingy="1px" originx="0px"
+ originy="0px" color="#0000ff" opacity="0.03137255" />
+ <sodipodi:guide orientation="1,0" position="12,26" id="guide3146" />
+ <sodipodi:guide orientation="1,0" position="16,26" id="guide3148" />
+ <sodipodi:guide orientation="0,1" position="36,22" id="guide3150" />
+ <sodipodi:guide orientation="0,1" position="36,6" id="guide3152" />
+ <sodipodi:guide orientation="1,0" position="18,0" id="guide3154" />
+ <sodipodi:guide orientation="1,0" position="10,0" id="guide3160" />
+ <sodipodi:guide orientation="0,1" position="0,20" id="guide3162" />
+ <sodipodi:guide orientation="0,1" position="0,18" id="guide3164" />
+ </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" inkscape:groupmode="layer" id="layer" transform="translate(0,-2)">
+ <g id="g3759"
+ style="fill:#ffffff;fill-opacity:1;stroke:none;fill-rule:nonzero;filter:url(#filter3811)">
+ <path style="display:none" d="M 28,18 C 26,16 24,12 24,8 l -4,10 z" id="path3809"
+ inkscape:connector-curvature="0" transform="translate(0,2)"
+ sodipodi:nodetypes="cccc" />
+ <path inkscape:connector-curvature="0" id="path2989" d="m 20,12 0,12 12,0 z"
+ sodipodi:nodetypes="cccc" />
+ <rect ry="2" y="4" x="4" height="20" width="20" id="rect2987" />
+ </g>
</g>
- </g>
</svg>
diff --git a/art/omemo_logo.svg b/art/omemo_logo.svg
index b2f821c69..d6980027c 100644
--- a/art/omemo_logo.svg
+++ b/art/omemo_logo.svg
@@ -1,263 +1,163 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" 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="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ 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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg4196" version="1.1"
- inkscape:version="0.91 r13725" width="2367.5596" height="1451.5084" viewBox="0 0 2367.5595 1451.5084"
- sodipodi:docname="omemo_logo.svg">
- <metadata
- id="metadata4202">
- <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>
- <defs
- id="defs4200">
- <linearGradient
- id="linearGradient4245"
- osb:paint="solid">
- <stop
- style="stop-color:#000000;stop-opacity:1;"
- offset="0"
- id="stop4247" />
- </linearGradient>
- </defs>
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1600"
- inkscape:window-height="836"
- id="namedview4198"
- showgrid="false"
- inkscape:zoom="0.32"
- inkscape:cx="1158.7782"
- inkscape:cy="667.71025"
- inkscape:window-x="0"
- inkscape:window-y="27"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg4196"
- fit-margin-top="0"
- fit-margin-left="0"
- fit-margin-right="0"
- fit-margin-bottom="0" />
- <path
- style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m 1160.235,302.29735 271.9745,-131.35135 186.9826,134.44197 24.7249,151.44038 -86.5373,135.98729 z"
- id="path4267"
- inkscape:connector-curvature="0"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#f57c00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m 598.8809,1125.9476 -43.05491,131.8557 -21.52745,94.8553 4.0364,47.0913 67.27328,6.7273 80.72795,-58.5277 43.72764,-78.7098 7.40006,-55.1641 -21.52745,-71.9824 z"
- id="path4259"
- inkscape:connector-curvature="0"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="M 709.52231,1171.4517 C 480.05218,1174.9052 321.72113,1008.3849 269.81593,895.97589 206.11648,758.02449 215.35674,596.92706 303.94612,450.17116 390.00741,320.24292 538.03872,188.34494 665.64434,170.1992 c 86.87989,-10.63238 215.40898,15.76659 250.11793,24.23821 35.046,8.55388 138.10213,41.16536 192.58973,67.91907 53.5186,26.27793 164.698,69.05834 309.1218,196.39025 100.3317,88.4579 183.2875,109.97875 279.7545,106.68109 52.9405,-1.80973 148.8273,-10.56706 171.5302,-24.72865 679.9746,-424.15329 639.4516,799.03733 13.1124,405.39142 -158.3183,-74.1014 -440.1478,10.5521 -637.0436,91.78671 -223.8429,92.3524 -350.01628,130.7858 -535.30499,133.5744 z"
- id="path4225"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ssccssssscss"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 2121.4484,451.36293 c -26.791,-0.0103 -69.7877,2.87028 -101.1871,10.73905 -68.1167,46.199 -138.5457,83.35128 -167.446,144.67176 -12.1866,25.8575 -15.1986,221.06115 -3.3883,250.53885 22.0574,55.0538 36.5353,68.5186 75.8437,113.5484 490.8133,255.43581 586.5854,-519.34849 196.1777,-519.49806 z"
- id="path4225-4"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093"
- sodipodi:nodetypes="scsscs" />
- <path
- style="fill:#f57c00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 1879.4205,872.05219 c -30.0884,-43.2017 -23.0447,-213.01732 -11.2518,-239.49258 19.553,-43.89704 110.0168,-119.19707 177.1545,-153.50421 62.2867,-31.14337 245.3285,107.06591 242.3844,259.61033 -2.4489,126.88796 -74.9751,256.91706 -216.1596,260.51446 -95.0727,-15.7629 -143.2721,-56.9801 -192.1275,-127.128 z"
- id="path4313"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="sscscs"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#f57c00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m 712.41248,50.975873 130.5787,23.17966 80.35619,97.354527 11.5898,38.63275 -335.33229,-24.72496 56.4038,-112.807627 z"
- id="path4317"
- inkscape:connector-curvature="0"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 1414.8968,95.744433 c -119.2326,0.1221 -252.577,46.677797 -357.9883,141.492197 59.2267,85.339 179.6057,681.13776 68.8789,839.94337 91.9688,196.1395 767.4955,273.501 557.166,-210.17391 -15.7049,-36.1151 -49.7142,-108.75426 -41.832,-193.48626 8.4493,-90.8299 56.4409,-192.1808 64.2324,-223.9238 57.3257,-233.5482 -98.225,-354.048497 -290.457,-353.851597 z m -37.9434,48.607397 c 179.9257,-1.202 313.9232,108.10167 295.8852,273.14927 -49.0308,223.244 -65.6093,352.99519 9.7574,506.70029 0.9067,322.06951 -372.1528,246.99471 -531.1856,150.28521 136.0694,-390.78747 -67.0566,-814.79857 -78.5644,-831.62107 107.9381,-67.831 213.0862,-97.9056 304.1074,-98.5137 z"
- id="path4227-8"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093"
- sodipodi:nodetypes="sccsssssccccs" />
- <path
- inkscape:connector-curvature="0"
- style="fill:#f57c00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 1371.9686,142.84932 c -91.0213,0.60808 -196.1693,30.68269 -304.1075,98.51367 11.5078,16.82249 214.6339,440.83547 78.5645,831.62311 159.0328,96.7094 532.0903,171.7842 531.1836,-150.28521 -75.3667,-153.7051 -47.9691,-295.82084 1.0617,-519.06483 19.5833,-183.59134 -126.7767,-261.98875 -306.7023,-260.78674 z m 42.0957,76.75039 c 158.8265,-0.80887 251.0755,161.9003 140.5517,325.36606 -113.709,-40.69316 -178.0341,-143.3305 -350.0787,-233.47358 73.9173,-58.593 149.3003,-91.58576 209.527,-91.89248 z"
- id="path4229-6"
- sodipodi:nodetypes="sccccssccs"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 1354.944,727.52278 -6.0809,177.36791 128.2608,-32.6939 7.361,-132.81901 c 65.526,-55.1437 -11.1658,-135.6742 -75.9144,-147.0284 -93.1144,-16.3282 -143.1451,90.3398 -53.6265,135.1734 z"
- id="path4233"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccccsc"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m 598.8809,1127.2931 c -14.1274,92.1644 -82.99521,244.9415 -51.12771,263.7113 36.46239,21.4761 172.66811,-90.819 192.40161,-197.7835 18.83652,133.4254 -129.0419,247.1826 -195.76526,219.9837 -38.73013,-15.7879 4.93336,-176.7045 54.49136,-285.9115 z"
- id="path4257"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cscsc"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#f57c00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m 713.64035,1156.0811 23.95231,78.8109 72.62957,139.0779 118.98884,69.5389 -1.5453,-78.0381 -40.9507,-101.9905 -65.67567,-100.4452 -27.04293,-32.4515 z"
- id="path4261"
- inkscape:connector-curvature="0"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 719.04894,1169.2163 c 9.27185,21.6343 50.66928,211.7208 189.30053,231.7965 30.3325,4.3925 -14.6805,-140.6232 -105.85379,-251.8855 102.24809,93.7488 161.32989,298.1418 122.07959,299.7901 C 810.58,1453.7045 732.44164,1267.601 719.04894,1169.2163 Z"
- id="path4255"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cscsc"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#f57c00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m 234.13659,657.90289 -48.22924,-33.49254 -68.9946,-29.47343 -68.324762,2.00956 -31.48299,135.97969 54.2579,195.59632 92.028162,42.154 87.08057,10.8767 79.91784,5.717 49.77454,-1.1406 z"
- id="path4265"
- inkscape:connector-curvature="0"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093"
- sodipodi:nodetypes="ccccccccccc" />
- <path
- style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.81825721px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m 362.2228,973.50299 c -87.01468,18.7244 -206.31388,2.7914 -260.29527,-66.2183 C 40.854878,829.20969 44.412488,641.34522 72.212698,611.40084 98.152348,583.46053 206.19233,642.42569 258.48372,672.39141 226.33414,633.9643 97.758248,551.92129 22.266478,615.19423 c -39.234376,32.88402 -22.2634293,269.25766 24.02476,303.47066 82.593032,61.047 269.567992,98.25131 315.931562,54.8381 z"
- id="path4263"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="csscssc"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- inkscape:connector-curvature="0"
- style="fill:#f57c00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 705.09299,169.2159 c -15.02488,0.0627 -29.55297,0.81546 -43.06769,2.46948 -127.0389,18.06512 -274.41191,149.37803 -360.09102,278.72918 -88.19593,146.10416 -97.39583,306.48603 -33.97922,443.82493 51.67469,111.90981 209.30324,277.68941 437.75427,274.25121 103.34093,-1.5552 188.21293,-14.2523 282.05624,-41.2806 l 53.34803,-135.65461 7.6922,-153.845 -32.3069,-87.691 -68.46233,-87.69283 -22.3067,-99.22916 30.769,-200.76658 -28.3598,-161.94745 c -6.6814,-1.88509 -12.5089,-3.44563 -17.1054,-4.56753 -29.15558,-7.11615 -124.80661,-26.93763 -205.94068,-26.60004 z"
- id="path4225-42-9"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m 447.07437,279.1177 -67.22098,67.22099 -37.8601,46.3593 180.02862,4.63593 96.58187,45.58664 70.31161,81.90143 47.13196,130.5787 -4.63593,166.8935 -88.85533,154.531 -78.03816,55.63111 16.99841,16.2258 81.12878,2.318 72.62957,-32.4515 L 807.90426,918.10339 837.26515,722.62168 810.99488,518.64075 734.50204,415.87764 630.96627,335.52152 535.9297,298.43408 Z"
- id="path4247"
- inkscape:connector-curvature="0"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="M 704.34544,1170.0033 C 474.87531,1173.4568 316.54426,1006.9366 264.63906,894.52759 200.93961,756.57613 210.17987,595.47873 298.76925,448.72283 384.83054,318.7946 532.86185,186.89663 660.46747,168.75089 c 86.87989,-10.63238 215.40898,15.76659 250.1179,24.23821 35.046,8.55388 138.10213,41.16536 192.58973,67.91907 53.5186,26.27792 164.698,69.05836 309.1218,196.39026 100.3317,88.4579 183.2875,109.9787 279.7545,106.6811 52.9405,-1.8098 148.8273,-10.5671 171.5302,-24.7287 679.9746,-424.15326 639.4516,799.03727 13.1124,405.39146 -158.3183,-74.1014 -440.1478,10.5521 -637.0436,91.78671 -223.8429,92.3524 -350.0163,130.7857 -535.30496,133.5743 z"
- id="path4225-42"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ssccssssscss"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 600.77485,188.39742 c 45.59931,-168.111187 93.61702,-207.521997 165.6011,-175.903587 28.11465,12.34913 88.59168,36.45928 110.93127,66.5789 46.81515,63.119057 81.36115,162.974077 99.35615,284.156557 -8.7416,75.03201 -41.5452,164.02089 -27.3175,238.20842 17.5559,91.54126 116.68213,142.15421 125.66043,234.93028 9.4985,98.1511 -22.9467,217.44721 -86.32323,293.93611 36.78763,-80.4955 64.77883,-202.86651 55.72773,-281.91641 -15.7564,-137.61237 -102.80503,-141.89728 -115.82623,-244.76458 -9.3046,-73.506 20.1158,-155.47823 24.0394,-229.46683 3.7424,-70.5705 -32.2949,-195.09979 -74.30353,-233.83762 C 781.36459,50.911903 652.78479,43.071673 600.77485,188.39742 Z"
- id="path4405"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="csscsscssssc"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#f57c00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m 978.84877,929.24739 143.14363,-41.5225 87.4159,-74.3036 5.4635,-186.85146 -73.2108,-30.5956 -65.562,22.9467 -77.58163,46.986 -87.416,87.416 z"
- id="path4289"
- inkscape:connector-curvature="0"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 913.12497,751.04553 c 45.1649,-86.3232 245.53153,-195.3877 312.51203,-177.0172 29.651,8.1322 84.3992,143.4773 -29.5028,270.98936 -50.127,56.1165 -219.63263,88.5086 -219.63263,88.5086 l 2.1854,-6.5562 c 0,0 154.41873,-31.7084 192.31513,-91.7868 38.4759,-60.9969 52.9259,-177.98746 0,-216.35436 -79.3518,-57.5234 -257.87713,132.2166 -257.87713,132.2166 z"
- id="path4287"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cssccssc"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m 330.40347,402.7425 c 114.35294,-10.55962 249.75787,-2.93669 319.87917,81.90143 79.96514,96.74795 96.08396,242.01494 62.58506,351.55806 -23.16188,75.7405 -98.38474,154.531 -163.80286,199.34501 60.19701,36.3696 76.03151,31.5859 158.39427,3.8632 C 761.64992,1021.17 829.49446,914.53909 837.26515,833.88399 848.77388,714.43039 855.97093,574.91586 790.90585,472.28145 719.85004,360.19719 579.71348,287.1018 454.02827,276.79974 l -13.13513,9.27185 c 166.63592,15.4531 280.2303,99.1189 342.28616,214.02545 65.19894,120.72647 36.96723,291.05045 30.9062,330.69635 -11.59484,75.8433 -39.28607,162.0595 -121.30683,197.02701 -32.03238,13.6562 -80.61368,27.043 -116.67091,8.4993 C 636.37485,988.41499 708.98856,931.86239 729.09345,855.51829 762.38235,729.11061 744.53737,534.45916 642.55609,446.01118 573.9429,386.50322 397.62445,372.60895 347.40188,381.10816 Z"
- id="path4245"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="csscsssccssscsscc"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
- id="path4249"
- sodipodi:type="arc"
- sodipodi:cx="592.71979"
- sodipodi:cy="560.36414"
- sodipodi:rx="41.337044"
- sodipodi:ry="48.677265"
- sodipodi:start="0"
- sodipodi:end="6.2714218"
- sodipodi:open="true"
- d="m 634.05683,560.36414 a 41.337044,48.677265 0 0 1 -41.21548,48.67705 41.337044,48.677265 0 0 1 -41.45789,-48.39075 41.337044,48.677265 0 0 1 40.97163,-48.96167 41.337044,48.677265 0 0 1 41.69888,48.10276"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 329.82495,822.87809 c 50.9573,98.8977 80.17049,31.9344 81.80769,19.0432 2.98204,-23.4803 -26.03926,-8.0283 -44.87764,-12.8177 -24.76611,-6.2965 -49.64587,-30.9043 -36.93005,-6.2255 z"
- id="path4253"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ssss"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 349.24366,1015.5476 c 24.85373,22.5357 29.23211,28.8458 48.29094,41.7233 13.9627,-4.7761 21.9738,-0.484 43.60813,-17.9975 -43.655,-2.9618 -58.6749,-15.7418 -91.89907,-23.7258 z"
- id="path4339"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cccc"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#000000;fill-opacity:0.11764706;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 1840.2193,917.45789 c -144.4722,-64.5024 -401.6544,9.1852 -581.3301,79.8965 C 1054.6229,1077.7432 939.48447,1111.1985 770.40085,1113.6259 560.99961,1116.632 416.51658,971.68219 369.15085,873.83489 311.02239,753.75418 319.45382,613.52678 400.29538,485.78208 478.83,372.68518 613.91429,257.87398 730.35984,242.07898 c 12.38775,-1.4461 25.70459,-2.1055 39.47656,-2.1601 74.36866,-0.2952 162.04327,17.0358 188.76757,23.2578 31.981,7.4458 126.02373,35.8332 175.74613,59.1211 48.8379,22.8738 150.2931,60.1123 282.0859,170.9492 91.5569,76.9988 167.2589,95.7317 255.2891,92.8613 48.3104,-1.5753 135.8099,-9.1984 156.5274,-21.5254 l 39.4824,-26.4785 c -22.7029,14.1616 -118.5888,22.9187 -171.5293,24.7285 -96.467,3.2976 -179.4222,-18.2237 -279.7539,-106.6816 -144.4238,-127.3319 -255.6045,-170.1108 -309.1231,-196.3887 -54.4876,-26.7537 -157.54383,-59.3661 -192.58983,-67.9199 -29.28571,-7.148 -125.36331,-27.0578 -206.8594,-26.7188 -15.09187,0.063 -29.68283,0.8192 -43.25782,2.4805 -127.60562,18.1458 -275.63793,150.0445 -361.69921,279.9727 -88.58938,146.7559 -97.82836,307.8532 -34.12891,445.80461 51.9052,112.40901 210.23495,278.92821 439.70508,275.47471 185.28865,-2.7886 311.46379,-41.2219 535.30669,-133.5743 196.8958,-81.23461 478.7247,-165.88851 637.043,-91.78711 z"
- id="path4225-42-3"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="csssccsssssccsssssccssscc"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
- <path
- style="fill:#000000;fill-opacity:0.11764706;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 707.63885,164.4148 c -15.0918,0.063 -29.6848,0.8211 -43.2597,2.4824 -127.6056,18.1458 -275.6361,150.0425 -361.6973,279.9707 -88.5894,146.7559 -97.8304,307.8533 -34.1309,445.80469 51.9052,112.40901 210.237,278.93011 439.7071,275.47661 185.2887,-2.7886 311.46185,-41.2218 535.30475,-133.5742 195.3026,-80.57741 474.1694,-164.51701 633.1757,-93.55281 -7.0564,-4.019 -14.1914,-8.2481 -21.4101,-12.7011 -151.9427,-69.8031 -422.4225,9.9405 -611.3887,86.46281 -214.8282,86.9953 -335.92133,123.1994 -513.74805,125.8262 -220.2288,3.2531 -372.1832,-153.60771 -421.9981,-259.49611 -61.1341,-129.94919 -52.2658,-281.70249 32.7559,-419.94529 82.5954,-122.3913 224.6641,-246.6373 347.1308,-263.7305 83.381,-10.0156 206.734,14.8519 240.04502,22.8321 33.6347,8.0576 132.54073,38.7767 184.83393,63.9785 51.3632,24.7535 158.0664,65.0525 296.6738,184.998 96.2911,83.3266 175.9061,103.5985 268.4883,100.4922 50.8085,-1.7048 142.8325,-9.9528 164.6211,-23.2929 0.3844,-0.2354 0.7646,-0.4611 1.1485,-0.6954 -38.3016,9.244 -106.3509,14.9537 -147.9278,16.375 -96.467,3.2976 -179.4222,-18.2237 -279.7539,-106.6816 C 1271.7855,328.1122 1160.6067,285.3314 1107.0881,259.0535 1052.6005,232.2998 949.54427,199.6894 914.49827,191.1355 885.21265,183.9876 789.13495,164.0757 707.63885,164.4148 Z"
- id="path4421"
- inkscape:connector-curvature="0"
- inkscape:export-xdpi="15.191093"
- inkscape:export-ydpi="15.191093" />
+ inkscape:version="0.91 r13725" width="2367.5596" height="1451.5084"
+ viewBox="0 0 2367.5595 1451.5084" sodipodi:docname="omemo_logo.svg">
+ <metadata id="metadata4202">
+ <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>
+ <defs id="defs4200">
+ <linearGradient id="linearGradient4245" osb:paint="solid">
+ <stop style="stop-color:#000000;stop-opacity:1;" offset="0" id="stop4247" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="1600" inkscape:window-height="836"
+ id="namedview4198" showgrid="false" inkscape:zoom="0.32" inkscape:cx="1158.7782"
+ inkscape:cy="667.71025" inkscape:window-x="0" inkscape:window-y="27"
+ inkscape:window-maximized="1" inkscape:current-layer="svg4196" fit-margin-top="0"
+ fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 1160.235,302.29735 271.9745,-131.35135 186.9826,134.44197 24.7249,151.44038 -86.5373,135.98729 z"
+ id="path4267" inkscape:connector-curvature="0" inkscape:export-xdpi="15.191093"
+ inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#f57c00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 598.8809,1125.9476 -43.05491,131.8557 -21.52745,94.8553 4.0364,47.0913 67.27328,6.7273 80.72795,-58.5277 43.72764,-78.7098 7.40006,-55.1641 -21.52745,-71.9824 z"
+ id="path4259" inkscape:connector-curvature="0" inkscape:export-xdpi="15.191093"
+ inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 709.52231,1171.4517 C 480.05218,1174.9052 321.72113,1008.3849 269.81593,895.97589 206.11648,758.02449 215.35674,596.92706 303.94612,450.17116 390.00741,320.24292 538.03872,188.34494 665.64434,170.1992 c 86.87989,-10.63238 215.40898,15.76659 250.11793,24.23821 35.046,8.55388 138.10213,41.16536 192.58973,67.91907 53.5186,26.27793 164.698,69.05834 309.1218,196.39025 100.3317,88.4579 183.2875,109.97875 279.7545,106.68109 52.9405,-1.80973 148.8273,-10.56706 171.5302,-24.72865 679.9746,-424.15329 639.4516,799.03733 13.1124,405.39142 -158.3183,-74.1014 -440.1478,10.5521 -637.0436,91.78671 -223.8429,92.3524 -350.01628,130.7858 -535.30499,133.5744 z"
+ id="path4225" inkscape:connector-curvature="0" sodipodi:nodetypes="ssccssssscss"
+ inkscape:export-xdpi="15.191093" inkscape:export-ydpi="15.191093" />
+ <path inkscape:connector-curvature="0"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 2121.4484,451.36293 c -26.791,-0.0103 -69.7877,2.87028 -101.1871,10.73905 -68.1167,46.199 -138.5457,83.35128 -167.446,144.67176 -12.1866,25.8575 -15.1986,221.06115 -3.3883,250.53885 22.0574,55.0538 36.5353,68.5186 75.8437,113.5484 490.8133,255.43581 586.5854,-519.34849 196.1777,-519.49806 z"
+ id="path4225-4" inkscape:export-xdpi="15.191093" inkscape:export-ydpi="15.191093"
+ sodipodi:nodetypes="scsscs" />
+ <path
+ style="fill:#f57c00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1879.4205,872.05219 c -30.0884,-43.2017 -23.0447,-213.01732 -11.2518,-239.49258 19.553,-43.89704 110.0168,-119.19707 177.1545,-153.50421 62.2867,-31.14337 245.3285,107.06591 242.3844,259.61033 -2.4489,126.88796 -74.9751,256.91706 -216.1596,260.51446 -95.0727,-15.7629 -143.2721,-56.9801 -192.1275,-127.128 z"
+ id="path4313" inkscape:connector-curvature="0" sodipodi:nodetypes="sscscs"
+ inkscape:export-xdpi="15.191093" inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#f57c00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 712.41248,50.975873 130.5787,23.17966 80.35619,97.354527 11.5898,38.63275 -335.33229,-24.72496 56.4038,-112.807627 z"
+ id="path4317" inkscape:connector-curvature="0" inkscape:export-xdpi="15.191093"
+ inkscape:export-ydpi="15.191093" />
+ <path inkscape:connector-curvature="0"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1414.8968,95.744433 c -119.2326,0.1221 -252.577,46.677797 -357.9883,141.492197 59.2267,85.339 179.6057,681.13776 68.8789,839.94337 91.9688,196.1395 767.4955,273.501 557.166,-210.17391 -15.7049,-36.1151 -49.7142,-108.75426 -41.832,-193.48626 8.4493,-90.8299 56.4409,-192.1808 64.2324,-223.9238 57.3257,-233.5482 -98.225,-354.048497 -290.457,-353.851597 z m -37.9434,48.607397 c 179.9257,-1.202 313.9232,108.10167 295.8852,273.14927 -49.0308,223.244 -65.6093,352.99519 9.7574,506.70029 0.9067,322.06951 -372.1528,246.99471 -531.1856,150.28521 136.0694,-390.78747 -67.0566,-814.79857 -78.5644,-831.62107 107.9381,-67.831 213.0862,-97.9056 304.1074,-98.5137 z"
+ id="path4227-8" inkscape:export-xdpi="15.191093" inkscape:export-ydpi="15.191093"
+ sodipodi:nodetypes="sccsssssccccs" />
+ <path inkscape:connector-curvature="0"
+ style="fill:#f57c00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1371.9686,142.84932 c -91.0213,0.60808 -196.1693,30.68269 -304.1075,98.51367 11.5078,16.82249 214.6339,440.83547 78.5645,831.62311 159.0328,96.7094 532.0903,171.7842 531.1836,-150.28521 -75.3667,-153.7051 -47.9691,-295.82084 1.0617,-519.06483 19.5833,-183.59134 -126.7767,-261.98875 -306.7023,-260.78674 z m 42.0957,76.75039 c 158.8265,-0.80887 251.0755,161.9003 140.5517,325.36606 -113.709,-40.69316 -178.0341,-143.3305 -350.0787,-233.47358 73.9173,-58.593 149.3003,-91.58576 209.527,-91.89248 z"
+ id="path4229-6" sodipodi:nodetypes="sccccssccs" inkscape:export-xdpi="15.191093"
+ inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1354.944,727.52278 -6.0809,177.36791 128.2608,-32.6939 7.361,-132.81901 c 65.526,-55.1437 -11.1658,-135.6742 -75.9144,-147.0284 -93.1144,-16.3282 -143.1451,90.3398 -53.6265,135.1734 z"
+ id="path4233" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccsc"
+ inkscape:export-xdpi="15.191093" inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 598.8809,1127.2931 c -14.1274,92.1644 -82.99521,244.9415 -51.12771,263.7113 36.46239,21.4761 172.66811,-90.819 192.40161,-197.7835 18.83652,133.4254 -129.0419,247.1826 -195.76526,219.9837 -38.73013,-15.7879 4.93336,-176.7045 54.49136,-285.9115 z"
+ id="path4257" inkscape:connector-curvature="0" sodipodi:nodetypes="cscsc"
+ inkscape:export-xdpi="15.191093" inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#f57c00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 713.64035,1156.0811 23.95231,78.8109 72.62957,139.0779 118.98884,69.5389 -1.5453,-78.0381 -40.9507,-101.9905 -65.67567,-100.4452 -27.04293,-32.4515 z"
+ id="path4261" inkscape:connector-curvature="0" inkscape:export-xdpi="15.191093"
+ inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 719.04894,1169.2163 c 9.27185,21.6343 50.66928,211.7208 189.30053,231.7965 30.3325,4.3925 -14.6805,-140.6232 -105.85379,-251.8855 102.24809,93.7488 161.32989,298.1418 122.07959,299.7901 C 810.58,1453.7045 732.44164,1267.601 719.04894,1169.2163 Z"
+ id="path4255" inkscape:connector-curvature="0" sodipodi:nodetypes="cscsc"
+ inkscape:export-xdpi="15.191093" inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#f57c00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 234.13659,657.90289 -48.22924,-33.49254 -68.9946,-29.47343 -68.324762,2.00956 -31.48299,135.97969 54.2579,195.59632 92.028162,42.154 87.08057,10.8767 79.91784,5.717 49.77454,-1.1406 z"
+ id="path4265" inkscape:connector-curvature="0" inkscape:export-xdpi="15.191093"
+ inkscape:export-ydpi="15.191093" sodipodi:nodetypes="ccccccccccc" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.81825721px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 362.2228,973.50299 c -87.01468,18.7244 -206.31388,2.7914 -260.29527,-66.2183 C 40.854878,829.20969 44.412488,641.34522 72.212698,611.40084 98.152348,583.46053 206.19233,642.42569 258.48372,672.39141 226.33414,633.9643 97.758248,551.92129 22.266478,615.19423 c -39.234376,32.88402 -22.2634293,269.25766 24.02476,303.47066 82.593032,61.047 269.567992,98.25131 315.931562,54.8381 z"
+ id="path4263" inkscape:connector-curvature="0" sodipodi:nodetypes="csscssc"
+ inkscape:export-xdpi="15.191093" inkscape:export-ydpi="15.191093" />
+ <path inkscape:connector-curvature="0"
+ style="fill:#f57c00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 705.09299,169.2159 c -15.02488,0.0627 -29.55297,0.81546 -43.06769,2.46948 -127.0389,18.06512 -274.41191,149.37803 -360.09102,278.72918 -88.19593,146.10416 -97.39583,306.48603 -33.97922,443.82493 51.67469,111.90981 209.30324,277.68941 437.75427,274.25121 103.34093,-1.5552 188.21293,-14.2523 282.05624,-41.2806 l 53.34803,-135.65461 7.6922,-153.845 -32.3069,-87.691 -68.46233,-87.69283 -22.3067,-99.22916 30.769,-200.76658 -28.3598,-161.94745 c -6.6814,-1.88509 -12.5089,-3.44563 -17.1054,-4.56753 -29.15558,-7.11615 -124.80661,-26.93763 -205.94068,-26.60004 z"
+ id="path4225-42-9" inkscape:export-xdpi="15.191093" inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 447.07437,279.1177 -67.22098,67.22099 -37.8601,46.3593 180.02862,4.63593 96.58187,45.58664 70.31161,81.90143 47.13196,130.5787 -4.63593,166.8935 -88.85533,154.531 -78.03816,55.63111 16.99841,16.2258 81.12878,2.318 72.62957,-32.4515 L 807.90426,918.10339 837.26515,722.62168 810.99488,518.64075 734.50204,415.87764 630.96627,335.52152 535.9297,298.43408 Z"
+ id="path4247" inkscape:connector-curvature="0" inkscape:export-xdpi="15.191093"
+ inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 704.34544,1170.0033 C 474.87531,1173.4568 316.54426,1006.9366 264.63906,894.52759 200.93961,756.57613 210.17987,595.47873 298.76925,448.72283 384.83054,318.7946 532.86185,186.89663 660.46747,168.75089 c 86.87989,-10.63238 215.40898,15.76659 250.1179,24.23821 35.046,8.55388 138.10213,41.16536 192.58973,67.91907 53.5186,26.27792 164.698,69.05836 309.1218,196.39026 100.3317,88.4579 183.2875,109.9787 279.7545,106.6811 52.9405,-1.8098 148.8273,-10.5671 171.5302,-24.7287 679.9746,-424.15326 639.4516,799.03727 13.1124,405.39146 -158.3183,-74.1014 -440.1478,10.5521 -637.0436,91.78671 -223.8429,92.3524 -350.0163,130.7857 -535.30496,133.5743 z"
+ id="path4225-42" inkscape:connector-curvature="0" sodipodi:nodetypes="ssccssssscss"
+ inkscape:export-xdpi="15.191093" inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 600.77485,188.39742 c 45.59931,-168.111187 93.61702,-207.521997 165.6011,-175.903587 28.11465,12.34913 88.59168,36.45928 110.93127,66.5789 46.81515,63.119057 81.36115,162.974077 99.35615,284.156557 -8.7416,75.03201 -41.5452,164.02089 -27.3175,238.20842 17.5559,91.54126 116.68213,142.15421 125.66043,234.93028 9.4985,98.1511 -22.9467,217.44721 -86.32323,293.93611 36.78763,-80.4955 64.77883,-202.86651 55.72773,-281.91641 -15.7564,-137.61237 -102.80503,-141.89728 -115.82623,-244.76458 -9.3046,-73.506 20.1158,-155.47823 24.0394,-229.46683 3.7424,-70.5705 -32.2949,-195.09979 -74.30353,-233.83762 C 781.36459,50.911903 652.78479,43.071673 600.77485,188.39742 Z"
+ id="path4405" inkscape:connector-curvature="0" sodipodi:nodetypes="csscsscssssc"
+ inkscape:export-xdpi="15.191093" inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#f57c00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 978.84877,929.24739 143.14363,-41.5225 87.4159,-74.3036 5.4635,-186.85146 -73.2108,-30.5956 -65.562,22.9467 -77.58163,46.986 -87.416,87.416 z"
+ id="path4289" inkscape:connector-curvature="0" inkscape:export-xdpi="15.191093"
+ inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 913.12497,751.04553 c 45.1649,-86.3232 245.53153,-195.3877 312.51203,-177.0172 29.651,8.1322 84.3992,143.4773 -29.5028,270.98936 -50.127,56.1165 -219.63263,88.5086 -219.63263,88.5086 l 2.1854,-6.5562 c 0,0 154.41873,-31.7084 192.31513,-91.7868 38.4759,-60.9969 52.9259,-177.98746 0,-216.35436 -79.3518,-57.5234 -257.87713,132.2166 -257.87713,132.2166 z"
+ id="path4287" inkscape:connector-curvature="0" sodipodi:nodetypes="cssccssc"
+ inkscape:export-xdpi="15.191093" inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 330.40347,402.7425 c 114.35294,-10.55962 249.75787,-2.93669 319.87917,81.90143 79.96514,96.74795 96.08396,242.01494 62.58506,351.55806 -23.16188,75.7405 -98.38474,154.531 -163.80286,199.34501 60.19701,36.3696 76.03151,31.5859 158.39427,3.8632 C 761.64992,1021.17 829.49446,914.53909 837.26515,833.88399 848.77388,714.43039 855.97093,574.91586 790.90585,472.28145 719.85004,360.19719 579.71348,287.1018 454.02827,276.79974 l -13.13513,9.27185 c 166.63592,15.4531 280.2303,99.1189 342.28616,214.02545 65.19894,120.72647 36.96723,291.05045 30.9062,330.69635 -11.59484,75.8433 -39.28607,162.0595 -121.30683,197.02701 -32.03238,13.6562 -80.61368,27.043 -116.67091,8.4993 C 636.37485,988.41499 708.98856,931.86239 729.09345,855.51829 762.38235,729.11061 744.53737,534.45916 642.55609,446.01118 573.9429,386.50322 397.62445,372.60895 347.40188,381.10816 Z"
+ id="path4245" inkscape:connector-curvature="0" sodipodi:nodetypes="csscsssccssscsscc"
+ inkscape:export-xdpi="15.191093" inkscape:export-ydpi="15.191093" />
+ <path
+ style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4249" sodipodi:type="arc" sodipodi:cx="592.71979" sodipodi:cy="560.36414"
+ sodipodi:rx="41.337044" sodipodi:ry="48.677265" sodipodi:start="0" sodipodi:end="6.2714218"
+ sodipodi:open="true"
+ d="m 634.05683,560.36414 a 41.337044,48.677265 0 0 1 -41.21548,48.67705 41.337044,48.677265 0 0 1 -41.45789,-48.39075 41.337044,48.677265 0 0 1 40.97163,-48.96167 41.337044,48.677265 0 0 1 41.69888,48.10276"
+ inkscape:export-xdpi="15.191093" inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 329.82495,822.87809 c 50.9573,98.8977 80.17049,31.9344 81.80769,19.0432 2.98204,-23.4803 -26.03926,-8.0283 -44.87764,-12.8177 -24.76611,-6.2965 -49.64587,-30.9043 -36.93005,-6.2255 z"
+ id="path4253" inkscape:connector-curvature="0" sodipodi:nodetypes="ssss"
+ inkscape:export-xdpi="15.191093" inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 349.24366,1015.5476 c 24.85373,22.5357 29.23211,28.8458 48.29094,41.7233 13.9627,-4.7761 21.9738,-0.484 43.60813,-17.9975 -43.655,-2.9618 -58.6749,-15.7418 -91.89907,-23.7258 z"
+ id="path4339" inkscape:connector-curvature="0" sodipodi:nodetypes="cccc"
+ inkscape:export-xdpi="15.191093" inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#000000;fill-opacity:0.11764706;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1840.2193,917.45789 c -144.4722,-64.5024 -401.6544,9.1852 -581.3301,79.8965 C 1054.6229,1077.7432 939.48447,1111.1985 770.40085,1113.6259 560.99961,1116.632 416.51658,971.68219 369.15085,873.83489 311.02239,753.75418 319.45382,613.52678 400.29538,485.78208 478.83,372.68518 613.91429,257.87398 730.35984,242.07898 c 12.38775,-1.4461 25.70459,-2.1055 39.47656,-2.1601 74.36866,-0.2952 162.04327,17.0358 188.76757,23.2578 31.981,7.4458 126.02373,35.8332 175.74613,59.1211 48.8379,22.8738 150.2931,60.1123 282.0859,170.9492 91.5569,76.9988 167.2589,95.7317 255.2891,92.8613 48.3104,-1.5753 135.8099,-9.1984 156.5274,-21.5254 l 39.4824,-26.4785 c -22.7029,14.1616 -118.5888,22.9187 -171.5293,24.7285 -96.467,3.2976 -179.4222,-18.2237 -279.7539,-106.6816 -144.4238,-127.3319 -255.6045,-170.1108 -309.1231,-196.3887 -54.4876,-26.7537 -157.54383,-59.3661 -192.58983,-67.9199 -29.28571,-7.148 -125.36331,-27.0578 -206.8594,-26.7188 -15.09187,0.063 -29.68283,0.8192 -43.25782,2.4805 -127.60562,18.1458 -275.63793,150.0445 -361.69921,279.9727 -88.58938,146.7559 -97.82836,307.8532 -34.12891,445.80461 51.9052,112.40901 210.23495,278.92821 439.70508,275.47471 185.28865,-2.7886 311.46379,-41.2219 535.30669,-133.5743 196.8958,-81.23461 478.7247,-165.88851 637.043,-91.78711 z"
+ id="path4225-42-3" inkscape:connector-curvature="0"
+ sodipodi:nodetypes="csssccsssssccsssssccssscc" inkscape:export-xdpi="15.191093"
+ inkscape:export-ydpi="15.191093" />
+ <path
+ style="fill:#000000;fill-opacity:0.11764706;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 707.63885,164.4148 c -15.0918,0.063 -29.6848,0.8211 -43.2597,2.4824 -127.6056,18.1458 -275.6361,150.0425 -361.6973,279.9707 -88.5894,146.7559 -97.8304,307.8533 -34.1309,445.80469 51.9052,112.40901 210.237,278.93011 439.7071,275.47661 185.2887,-2.7886 311.46185,-41.2218 535.30475,-133.5742 195.3026,-80.57741 474.1694,-164.51701 633.1757,-93.55281 -7.0564,-4.019 -14.1914,-8.2481 -21.4101,-12.7011 -151.9427,-69.8031 -422.4225,9.9405 -611.3887,86.46281 -214.8282,86.9953 -335.92133,123.1994 -513.74805,125.8262 -220.2288,3.2531 -372.1832,-153.60771 -421.9981,-259.49611 -61.1341,-129.94919 -52.2658,-281.70249 32.7559,-419.94529 82.5954,-122.3913 224.6641,-246.6373 347.1308,-263.7305 83.381,-10.0156 206.734,14.8519 240.04502,22.8321 33.6347,8.0576 132.54073,38.7767 184.83393,63.9785 51.3632,24.7535 158.0664,65.0525 296.6738,184.998 96.2911,83.3266 175.9061,103.5985 268.4883,100.4922 50.8085,-1.7048 142.8325,-9.9528 164.6211,-23.2929 0.3844,-0.2354 0.7646,-0.4611 1.1485,-0.6954 -38.3016,9.244 -106.3509,14.9537 -147.9278,16.375 -96.467,3.2976 -179.4222,-18.2237 -279.7539,-106.6816 C 1271.7855,328.1122 1160.6067,285.3314 1107.0881,259.0535 1052.6005,232.2998 949.54427,199.6894 914.49827,191.1355 885.21265,183.9876 789.13495,164.0757 707.63885,164.4148 Z"
+ id="path4421" inkscape:connector-curvature="0" inkscape:export-xdpi="15.191093"
+ inkscape:export-ydpi="15.191093" />
</svg>
diff --git a/art/play_video.svg b/art/play_video.svg
index 02df3fb36..72d07433a 100644
--- a/art/play_video.svg
+++ b/art/play_video.svg
@@ -1,48 +1,29 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48" viewBox="0 0 48 48"
- id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="play_video.svg">
- <metadata
- id="metadata12">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs10" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1916"
- inkscape:window-height="1156"
- id="namedview8"
- showgrid="false"
- inkscape:zoom="4.9166667"
- inkscape:cx="0.91525424"
- inkscape:cy="24"
- inkscape:window-x="0"
- inkscape:window-y="20"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg2" />
- <path
- d="M0 0h48v48H0z"
- fill="none"
- id="path4" />
- <path
- d="M20 33l12-9-12-9v18zm4-29C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm0 36c-8.82 0-16-7.18-16-16S15.18 8 24 8s16 7.18 16 16-7.18 16-16 16z"
- id="path6"
- style="fill:#ffffff;fill-opacity:0.7019608;opacity:1;stroke:none;stroke-opacity:0.38039216" />
+<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="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="48" height="48"
+ viewBox="0 0 48 48" id="svg2" version="1.1" inkscape:version="0.91 r13725"
+ sodipodi:docname="play_video.svg">
+ <metadata id="metadata12">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs10" />
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
+ objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
+ inkscape:pageshadow="2" inkscape:window-width="1916" inkscape:window-height="1156"
+ id="namedview8" showgrid="false" inkscape:zoom="4.9166667" inkscape:cx="0.91525424"
+ inkscape:cy="24" inkscape:window-x="0" inkscape:window-y="20" inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path d="M0 0h48v48H0z" fill="none" id="path4" />
+ <path
+ d="M20 33l12-9-12-9v18zm4-29C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm0 36c-8.82 0-16-7.18-16-16S15.18 8 24 8s16 7.18 16 16-7.18 16-16 16z"
+ id="path6"
+ style="fill:#ffffff;fill-opacity:0.7019608;opacity:1;stroke:none;stroke-opacity:0.38039216" />
</svg>
diff --git a/libs/MemorizingTrustManager/AndroidManifest.xml b/libs/MemorizingTrustManager/AndroidManifest.xml
index c125afe42..4d3a2b41c 100644
--- a/libs/MemorizingTrustManager/AndroidManifest.xml
+++ b/libs/MemorizingTrustManager/AndroidManifest.xml
@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="de.duenndns.ssl"
- android:versionCode="1"
- android:versionName="1.0">
+ package="de.duenndns.ssl"
+ android:versionCode="1"
+ android:versionName="1.0">
- <application android:label="MemorizingTrustManager">
- <activity android:name="de.duenndns.ssl.MemorizingActivity"
- android:theme="@android:style/Theme.Translucent.NoTitleBar" />
- </application>
+ <application android:label="MemorizingTrustManager">
+ <activity
+ android:name="de.duenndns.ssl.MemorizingActivity"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar" />
+ </application>
</manifest>
diff --git a/libs/MemorizingTrustManager/ant.properties b/libs/MemorizingTrustManager/ant.properties
index ee52d86d9..21ef4e321 100644
--- a/libs/MemorizingTrustManager/ant.properties
+++ b/libs/MemorizingTrustManager/ant.properties
@@ -2,13 +2,10 @@
#
# This file must be checked in Version Control Systems, as it is
# integral to the build system of your project.
-
# This file is only used by the Ant script.
-
# You can use this to override default values such as
# 'source.dir' for the location of your java source folder and
# 'out.dir' for the location of your output folder.
-
# You can also use it define how the release builds are signed by declaring
# the following properties:
# 'key.store' for the location of your keystore and
diff --git a/libs/MemorizingTrustManager/build.gradle b/libs/MemorizingTrustManager/build.gradle
index a3b4c5106..12072b81f 100644
--- a/libs/MemorizingTrustManager/build.gradle
+++ b/libs/MemorizingTrustManager/build.gradle
@@ -1,32 +1,32 @@
buildscript {
- repositories {
- mavenCentral()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:2.2.2'
- }
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.2.2'
+ }
}
apply plugin: 'android-library'
android {
- compileSdkVersion 19
- buildToolsVersion "19.1"
- defaultConfig {
- minSdkVersion 7
- targetSdkVersion 19
- }
+ compileSdkVersion 19
+ buildToolsVersion "19.1"
+ defaultConfig {
+ minSdkVersion 7
+ targetSdkVersion 19
+ }
- sourceSets {
- main {
- manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = ['src']
- resources.srcDirs = ['src']
- aidl.srcDirs = ['src']
- renderscript.srcDirs = ['src']
- res.srcDirs = ['res']
- assets.srcDirs = ['assets']
- }
- }
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ resources.srcDirs = ['src']
+ aidl.srcDirs = ['src']
+ renderscript.srcDirs = ['src']
+ res.srcDirs = ['res']
+ assets.srcDirs = ['assets']
+ }
+ }
}
diff --git a/libs/MemorizingTrustManager/build.xml b/libs/MemorizingTrustManager/build.xml
index 06cf485c1..15b0b0104 100644
--- a/libs/MemorizingTrustManager/build.xml
+++ b/libs/MemorizingTrustManager/build.xml
@@ -50,9 +50,8 @@
<!-- quick check on sdk.dir -->
<fail
- message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
- unless="sdk.dir"
- />
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
+ unless="sdk.dir" />
<!--
Import per project custom build rules if present at the root of the project.
diff --git a/libs/MemorizingTrustManager/example/AndroidManifest.xml b/libs/MemorizingTrustManager/example/AndroidManifest.xml
index cdc0450b3..ebc664d65 100644
--- a/libs/MemorizingTrustManager/example/AndroidManifest.xml
+++ b/libs/MemorizingTrustManager/example/AndroidManifest.xml
@@ -1,20 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="de.duenndns.mtmexample"
- android:versionCode="1"
- android:versionName="1.0">
-
+ package="de.duenndns.mtmexample"
+ android:versionCode="1"
+ android:versionName="1.0">
+
<uses-sdk
android:minSdkVersion="3"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.INTERNET" />
- <application android:label="@string/app_name" android:icon="@android:drawable/ic_lock_lock">
+ <application
+ android:label="@string/app_name"
+ android:icon="@android:drawable/ic_lock_lock">
<activity
android:name=".MTMExample"
android:configChanges="keyboardHidden|orientation|screenSize|screenLayout"
- android:label="@string/app_name" >
+ android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -22,8 +24,9 @@
</intent-filter>
</activity>
- <!-- ADD THE FOLLOWING TO YOUR MANIFEST: -->
- <activity android:name="de.duenndns.ssl.MemorizingActivity"
- android:theme="@android:style/Theme.Translucent.NoTitleBar" />
+ <!-- ADD THE FOLLOWING TO YOUR MANIFEST: -->
+ <activity
+ android:name="de.duenndns.ssl.MemorizingActivity"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar" />
</application>
</manifest>
diff --git a/libs/MemorizingTrustManager/example/ant.properties b/libs/MemorizingTrustManager/example/ant.properties
index 27fcaadd8..2f48b3655 100644
--- a/libs/MemorizingTrustManager/example/ant.properties
+++ b/libs/MemorizingTrustManager/example/ant.properties
@@ -2,17 +2,13 @@
#
# This file must be checked in Version Control Systems, as it is
# integral to the build system of your project.
-
# This file is only used by the Ant script.
-
# You can use this to override default values such as
# 'source.dir' for the location of your java source folder and
# 'out.dir' for the location of your output folder.
-
# You can also use it define how the release builds are signed by declaring
# the following properties:
# 'key.store' for the location of your keystore and
# 'key.alias' for the name of the key to use.
# The password will be asked during the build when you use the 'release' target.
-
application.package=de.duenndns.mtmexample
diff --git a/libs/MemorizingTrustManager/example/build.gradle b/libs/MemorizingTrustManager/example/build.gradle
index 00bfe99e2..a07fbe87b 100644
--- a/libs/MemorizingTrustManager/example/build.gradle
+++ b/libs/MemorizingTrustManager/example/build.gradle
@@ -1,23 +1,23 @@
apply plugin: 'android'
dependencies {
- compile rootProject
+ compile rootProject
}
android {
- compileSdkVersion 19
- buildToolsVersion "19.1"
- defaultConfig {
- minSdkVersion 7
- targetSdkVersion 19
- }
+ compileSdkVersion 19
+ buildToolsVersion "19.1"
+ defaultConfig {
+ minSdkVersion 7
+ targetSdkVersion 19
+ }
- sourceSets {
- main {
- manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = ['src']
- res.srcDirs = ['res']
- }
- }
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ res.srcDirs = ['res']
+ }
+ }
}
diff --git a/libs/MemorizingTrustManager/example/build.xml b/libs/MemorizingTrustManager/example/build.xml
index cdc74917d..0c61a2917 100644
--- a/libs/MemorizingTrustManager/example/build.xml
+++ b/libs/MemorizingTrustManager/example/build.xml
@@ -50,9 +50,8 @@
<!-- quick check on sdk.dir -->
<fail
- message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
- unless="sdk.dir"
- />
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
+ unless="sdk.dir" />
<!--
Import per project custom build rules if present at the root of the project.
diff --git a/libs/MemorizingTrustManager/example/project.properties b/libs/MemorizingTrustManager/example/project.properties
index 3692949fd..be830d977 100644
--- a/libs/MemorizingTrustManager/example/project.properties
+++ b/libs/MemorizingTrustManager/example/project.properties
@@ -6,7 +6,6 @@
# To customize properties used by the Ant build system use,
# "ant.properties", and override values to adapt the script to your
# project structure.
-
android.library.reference.1=../
# Project target.
target=android-19
diff --git a/libs/MemorizingTrustManager/example/res/layout/mtmexample.xml b/libs/MemorizingTrustManager/example/res/layout/mtmexample.xml
index dfef58b6c..4a08b6899 100644
--- a/libs/MemorizingTrustManager/example/res/layout/mtmexample.xml
+++ b/libs/MemorizingTrustManager/example/res/layout/mtmexample.xml
@@ -1,36 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" >
- <EditText
- android:id="@+id/url"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:hint="HTTPS address"
- android:text="https://op-co.de/mtm/"
- android:singleLine="true"
- />
- <Button
- android:id="@+id/connect"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="Connect"
- />
- <TextView
- android:id="@+id/content"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="Please enter a HTTPS URL and press 'Connect'!"
- android:textSize="11pt"
- />
- <Button
- android:id="@+id/manage"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="Clean up Certificates"
- android:onClick="onManage"
- />
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <EditText
+ android:id="@+id/url"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:hint="HTTPS address"
+ android:text="https://op-co.de/mtm/"
+ android:singleLine="true" />
+
+ <Button
+ android:id="@+id/connect"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Connect" />
+
+ <TextView
+ android:id="@+id/content"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Please enter a HTTPS URL and press 'Connect'!"
+ android:textSize="11pt" />
+
+ <Button
+ android:id="@+id/manage"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Clean up Certificates"
+ android:onClick="onManage" />
</LinearLayout>
diff --git a/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/JULHandler.java b/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/JULHandler.java
index 6d183eb60..2cd70d0c5 100644
--- a/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/JULHandler.java
+++ b/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/JULHandler.java
@@ -34,136 +34,146 @@ import java.util.logging.Logger;
* If there are no {@code DebugLogSettings} configured, then all messages sent
* to JUL will be logged.
* </p>
- *
+ *
* @author Florian Schmaus
- *
*/
@SuppressWarnings("deprecation")
public class JULHandler extends Handler {
- /** Implement this interface to toggle debug logging.
- */
- public interface DebugLogSettings {
- public boolean isDebugLogEnabled();
- }
-
- private static final String CLASS_NAME = JULHandler.class.getName();
-
- /**
- * The global LogManager configuration.
- * <p>
- * This configures:
- * <ul>
- * <li> JULHandler as the default handler for all log messages
- * <li> A default log level FINEST (300). Meaning that log messages of a level 300 or higher a
- * logged
- * </ul>
- * </p>
- */
- private static final InputStream LOG_MANAGER_CONFIG = new StringBufferInputStream(
+ /**
+ * Implement this interface to toggle debug logging.
+ */
+ public interface DebugLogSettings {
+ public boolean isDebugLogEnabled();
+ }
+
+ private static final String CLASS_NAME = JULHandler.class.getName();
+
+ /**
+ * The global LogManager configuration.
+ * <p>
+ * This configures:
+ * <ul>
+ * <li> JULHandler as the default handler for all log messages
+ * <li> A default log level FINEST (300). Meaning that log messages of a level 300 or higher a
+ * logged
+ * </ul>
+ * </p>
+ */
+ private static final InputStream LOG_MANAGER_CONFIG = new StringBufferInputStream(
// @formatter:off
-"handlers = " + CLASS_NAME + '\n' +
-".level = FINEST"
-);
+ "handlers = " + CLASS_NAME + '\n' +
+ ".level = FINEST"
+ );
// @formatter:on
- // Constants for Android vs. JUL debug level comparisons
- private static final int FINE_INT = Level.FINE.intValue();
- private static final int INFO_INT = Level.INFO.intValue();
- private static final int WARN_INT = Level.WARNING.intValue();
- private static final int SEVE_INT = Level.SEVERE.intValue();
-
- private static final Logger LOGGER = Logger.getLogger(CLASS_NAME);
-
- /** A formatter that creates output similar to Android's Log.x. */
- private static final Formatter FORMATTER = new Formatter() {
- @Override
- public String format(LogRecord logRecord) {
- Throwable thrown = logRecord.getThrown();
- if (thrown != null) {
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw, false);
- pw.write(logRecord.getMessage() + ' ');
- thrown.printStackTrace(pw);
- pw.flush();
- return sw.toString();
- } else {
- return logRecord.getMessage();
- }
- }
- };
-
- private static DebugLogSettings sDebugLogSettings;
- private static boolean initialized = false;
-
- public static void initialize() {
- try {
- LogManager.getLogManager().readConfiguration(LOG_MANAGER_CONFIG);
- initialized = true;
- } catch (IOException e) {
- Log.e("JULHandler", "Can not initialize configuration", e);
- }
- if (initialized) LOGGER.info("Initialzied java.util.logging logger");
- }
-
- public static void setDebugLogSettings(DebugLogSettings debugLogSettings) {
- if (!isInitialized()) initialize();
- sDebugLogSettings = debugLogSettings;
- }
-
- public static boolean isInitialized() {
- return initialized;
- }
-
- public JULHandler() {
- setFormatter(FORMATTER);
- }
-
- @Override
- public void close() {}
-
- @Override
- public void flush() {}
-
- @Override
- public boolean isLoggable(LogRecord record) {
- final boolean debugLog = sDebugLogSettings == null ? true : sDebugLogSettings
- .isDebugLogEnabled();
-
- if (record.getLevel().intValue() <= FINE_INT) {
- return debugLog;
- }
- return true;
- }
-
- /** JUL method that forwards log records to Android's LogCat. */
- @Override
- public void publish(LogRecord record) {
- if (!isLoggable(record)) return;
-
- final int priority = getAndroidPriority(record.getLevel());
- final String tag = substringAfterLastDot(record.getSourceClassName());
- final String msg = getFormatter().format(record);
-
- Log.println(priority, tag, msg);
- }
-
- /** Helper to convert JUL verbosity levels to Android's Log. */
- private static int getAndroidPriority(Level level) {
- int value = level.intValue();
- if (value >= SEVE_INT) {
- return Log.ERROR;
- } else if (value >= WARN_INT) {
- return Log.WARN;
- } else if (value >= INFO_INT) {
- return Log.INFO;
- } else {
- return Log.DEBUG;
- }
- }
-
- /** Helper to extract short class names. */
- private static String substringAfterLastDot(String s) {
- return s.substring(s.lastIndexOf('.') + 1).trim();
- }
+ // Constants for Android vs. JUL debug level comparisons
+ private static final int FINE_INT = Level.FINE.intValue();
+ private static final int INFO_INT = Level.INFO.intValue();
+ private static final int WARN_INT = Level.WARNING.intValue();
+ private static final int SEVE_INT = Level.SEVERE.intValue();
+
+ private static final Logger LOGGER = Logger.getLogger(CLASS_NAME);
+
+ /**
+ * A formatter that creates output similar to Android's Log.x.
+ */
+ private static final Formatter FORMATTER = new Formatter() {
+ @Override
+ public String format(LogRecord logRecord) {
+ Throwable thrown = logRecord.getThrown();
+ if (thrown != null) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw, false);
+ pw.write(logRecord.getMessage() + ' ');
+ thrown.printStackTrace(pw);
+ pw.flush();
+ return sw.toString();
+ } else {
+ return logRecord.getMessage();
+ }
+ }
+ };
+
+ private static DebugLogSettings sDebugLogSettings;
+ private static boolean initialized = false;
+
+ public static void initialize() {
+ try {
+ LogManager.getLogManager().readConfiguration(LOG_MANAGER_CONFIG);
+ initialized = true;
+ } catch (IOException e) {
+ Log.e("JULHandler", "Can not initialize configuration", e);
+ }
+ if (initialized) LOGGER.info("Initialzied java.util.logging logger");
+ }
+
+ public static void setDebugLogSettings(DebugLogSettings debugLogSettings) {
+ if (!isInitialized()) initialize();
+ sDebugLogSettings = debugLogSettings;
+ }
+
+ public static boolean isInitialized() {
+ return initialized;
+ }
+
+ public JULHandler() {
+ setFormatter(FORMATTER);
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public boolean isLoggable(LogRecord record) {
+ final boolean debugLog = sDebugLogSettings == null ? true : sDebugLogSettings
+ .isDebugLogEnabled();
+
+ if (record.getLevel().intValue() <= FINE_INT) {
+ return debugLog;
+ }
+ return true;
+ }
+
+ /**
+ * JUL method that forwards log records to Android's LogCat.
+ */
+ @Override
+ public void publish(LogRecord record) {
+ if (!isLoggable(record)) return;
+
+ final int priority = getAndroidPriority(record.getLevel());
+ final String tag = substringAfterLastDot(record.getSourceClassName());
+ final String msg = getFormatter().format(record);
+
+ Log.println(priority, tag, msg);
+ }
+
+ /**
+ * Helper to convert JUL verbosity levels to Android's Log.
+ */
+ private static int getAndroidPriority(Level level) {
+ int value = level.intValue();
+ if (value >= SEVE_INT) {
+ return Log.ERROR;
+ } else if (value >= WARN_INT) {
+ return Log.WARN;
+ } else if (value >= INFO_INT) {
+ return Log.INFO;
+ } else {
+ return Log.DEBUG;
+ }
+ }
+
+ /**
+ * Helper to extract short class names.
+ */
+ private static String substringAfterLastDot(String s) {
+ return s.substring(s.lastIndexOf('.') + 1).trim();
+ }
}
diff --git a/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/MTMExample.java b/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/MTMExample.java
index 52e20dff6..31e37bd00 100644
--- a/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/MTMExample.java
+++ b/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/MTMExample.java
@@ -28,116 +28,124 @@ import de.duenndns.ssl.MemorizingTrustManager;
* Example to demonstrate the use of MemorizingTrustManager on HTTPS
* sockets.
*/
-public class MTMExample extends Activity implements OnClickListener
-{
- MemorizingTrustManager mtm;
-
- TextView content;
- HostnameVerifier defaultverifier;
- EditText urlinput;
- String text;
- Handler hdlr;
-
- /** Creates the Activity and registers a MemorizingTrustManager. */
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- JULHandler.initialize();
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- setContentView(R.layout.mtmexample);
-
-
- // set up gui elements
- findViewById(R.id.connect).setOnClickListener(this);
- content = (TextView)findViewById(R.id.content);
- urlinput = (EditText)findViewById(R.id.url);
-
- // register handler for background thread
- hdlr = new Handler();
-
- // Here, the MemorizingTrustManager is activated for HTTPS
- try {
- // set location of the keystore
- MemorizingTrustManager.setKeyStoreFile("private", "sslkeys.bks");
-
- // register MemorizingTrustManager for HTTPS
- SSLContext sc = SSLContext.getInstance("TLS");
- mtm = new MemorizingTrustManager(this);
- sc.init(null, new X509TrustManager[] { mtm },
- new java.security.SecureRandom());
- HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
- HttpsURLConnection.setDefaultHostnameVerifier(
- mtm.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier()));
-
- // disable redirects to reduce possible confusion
- HttpsURLConnection.setFollowRedirects(false);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- /** Updates the screen content from a background thread. */
- void setText(final String s, final boolean progress) {
- text = s;
- hdlr.post(new Runnable() {
- public void run() {
- content.setText(s);
- setProgressBarIndeterminateVisibility(progress);
- }
- });
- }
-
- /** Spawns a new thread connecting to the specified URL.
- * The result of the request is displayed on the screen.
- * @param urlString a HTTPS URL to connect to.
- */
- void connect(final String urlString) {
- new Thread() {
- public void run() {
- try {
- URL u = new URL(urlString);
- HttpsURLConnection c = (HttpsURLConnection)u.openConnection();
- c.connect();
- setText("" + c.getResponseCode() + " "
- + c.getResponseMessage(), false);
- c.disconnect();
- } catch (Exception e) {
- setText(e.toString(), false);
- e.printStackTrace();
- }
- }
- }.start();
- }
-
- /** Reacts on the connect Button press. */
- @Override
- public void onClick(View view) {
- String url = urlinput.getText().toString();
- setText("Loading " + url, true);
- setProgressBarIndeterminateVisibility(true);
- connect(url);
- }
-
- /** React on the "Manage Certificates" button press. */
- public void onManage(View view) {
- final ArrayList<String> aliases = Collections.list(mtm.getCertificates());
- ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.select_dialog_item, aliases);
- new AlertDialog.Builder(this).setTitle("Tap Certificate to Delete")
- .setNegativeButton(android.R.string.cancel, null)
- .setAdapter(adapter, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- try {
- String alias = aliases.get(which);
- mtm.deleteCertificate(alias);
- setText("Deleted " + alias, false);
- } catch (KeyStoreException e) {
- e.printStackTrace();
- setText("Error: " + e.getLocalizedMessage(), false);
- }
- }
- })
- .create().show();
- }
+public class MTMExample extends Activity implements OnClickListener {
+ MemorizingTrustManager mtm;
+
+ TextView content;
+ HostnameVerifier defaultverifier;
+ EditText urlinput;
+ String text;
+ Handler hdlr;
+
+ /**
+ * Creates the Activity and registers a MemorizingTrustManager.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ JULHandler.initialize();
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ setContentView(R.layout.mtmexample);
+
+
+ // set up gui elements
+ findViewById(R.id.connect).setOnClickListener(this);
+ content = (TextView) findViewById(R.id.content);
+ urlinput = (EditText) findViewById(R.id.url);
+
+ // register handler for background thread
+ hdlr = new Handler();
+
+ // Here, the MemorizingTrustManager is activated for HTTPS
+ try {
+ // set location of the keystore
+ MemorizingTrustManager.setKeyStoreFile("private", "sslkeys.bks");
+
+ // register MemorizingTrustManager for HTTPS
+ SSLContext sc = SSLContext.getInstance("TLS");
+ mtm = new MemorizingTrustManager(this);
+ sc.init(null, new X509TrustManager[]{mtm},
+ new java.security.SecureRandom());
+ HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+ HttpsURLConnection.setDefaultHostnameVerifier(
+ mtm.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier()));
+
+ // disable redirects to reduce possible confusion
+ HttpsURLConnection.setFollowRedirects(false);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Updates the screen content from a background thread.
+ */
+ void setText(final String s, final boolean progress) {
+ text = s;
+ hdlr.post(new Runnable() {
+ public void run() {
+ content.setText(s);
+ setProgressBarIndeterminateVisibility(progress);
+ }
+ });
+ }
+
+ /**
+ * Spawns a new thread connecting to the specified URL.
+ * The result of the request is displayed on the screen.
+ *
+ * @param urlString a HTTPS URL to connect to.
+ */
+ void connect(final String urlString) {
+ new Thread() {
+ public void run() {
+ try {
+ URL u = new URL(urlString);
+ HttpsURLConnection c = (HttpsURLConnection) u.openConnection();
+ c.connect();
+ setText("" + c.getResponseCode() + " "
+ + c.getResponseMessage(), false);
+ c.disconnect();
+ } catch (Exception e) {
+ setText(e.toString(), false);
+ e.printStackTrace();
+ }
+ }
+ }.start();
+ }
+
+ /**
+ * Reacts on the connect Button press.
+ */
+ @Override
+ public void onClick(View view) {
+ String url = urlinput.getText().toString();
+ setText("Loading " + url, true);
+ setProgressBarIndeterminateVisibility(true);
+ connect(url);
+ }
+
+ /**
+ * React on the "Manage Certificates" button press.
+ */
+ public void onManage(View view) {
+ final ArrayList<String> aliases = Collections.list(mtm.getCertificates());
+ ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.select_dialog_item, aliases);
+ new AlertDialog.Builder(this).setTitle("Tap Certificate to Delete")
+ .setNegativeButton(android.R.string.cancel, null)
+ .setAdapter(adapter, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ try {
+ String alias = aliases.get(which);
+ mtm.deleteCertificate(alias);
+ setText("Deleted " + alias, false);
+ } catch (KeyStoreException e) {
+ e.printStackTrace();
+ setText("Error: " + e.getLocalizedMessage(), false);
+ }
+ }
+ })
+ .create().show();
+ }
}
diff --git a/libs/MemorizingTrustManager/project.properties b/libs/MemorizingTrustManager/project.properties
index c57400d00..7c444cd57 100644
--- a/libs/MemorizingTrustManager/project.properties
+++ b/libs/MemorizingTrustManager/project.properties
@@ -6,7 +6,6 @@
# To customize properties used by the Ant build system use,
# "ant.properties", and override values to adapt the script to your
# project structure.
-
android.library=true
# Project target.
target=android-19
diff --git a/libs/MemorizingTrustManager/src/de/duenndns/ssl/MTMDecision.java b/libs/MemorizingTrustManager/src/de/duenndns/ssl/MTMDecision.java
index 0efe6b515..2b84c3ee5 100644
--- a/libs/MemorizingTrustManager/src/de/duenndns/ssl/MTMDecision.java
+++ b/libs/MemorizingTrustManager/src/de/duenndns/ssl/MTMDecision.java
@@ -24,10 +24,10 @@
package de.duenndns.ssl;
class MTMDecision {
- public final static int DECISION_INVALID = 0;
- public final static int DECISION_ABORT = 1;
- public final static int DECISION_ONCE = 2;
- public final static int DECISION_ALWAYS = 3;
+ public final static int DECISION_INVALID = 0;
+ public final static int DECISION_ABORT = 1;
+ public final static int DECISION_ONCE = 2;
+ public final static int DECISION_ALWAYS = 3;
- int state = DECISION_INVALID;
+ int state = DECISION_INVALID;
}
diff --git a/libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingActivity.java b/libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingActivity.java
index 567c2d817..c917c5d08 100644
--- a/libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingActivity.java
+++ b/libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingActivity.java
@@ -36,69 +36,69 @@ import java.util.logging.Level;
import java.util.logging.Logger;
public class MemorizingActivity extends Activity
- implements OnClickListener,OnCancelListener {
+ implements OnClickListener, OnCancelListener {
- private final static Logger LOGGER = Logger.getLogger(MemorizingActivity.class.getName());
+ private final static Logger LOGGER = Logger.getLogger(MemorizingActivity.class.getName());
- int decisionId;
+ int decisionId;
- AlertDialog dialog;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- LOGGER.log(Level.FINE, "onCreate");
- super.onCreate(savedInstanceState);
- }
+ AlertDialog dialog;
- @Override
- public void onResume() {
- super.onResume();
- Intent i = getIntent();
- decisionId = i.getIntExtra(MemorizingTrustManager.DECISION_INTENT_ID, MTMDecision.DECISION_INVALID);
- int titleId = i.getIntExtra(MemorizingTrustManager.DECISION_TITLE_ID, R.string.mtm_accept_cert);
- String cert = i.getStringExtra(MemorizingTrustManager.DECISION_INTENT_CERT);
- LOGGER.log(Level.FINE, "onResume with " + i.getExtras() + " decId=" + decisionId + " data: " + i.getData());
- dialog = new AlertDialog.Builder(this).setTitle(titleId)
- .setMessage(cert)
- .setPositiveButton(R.string.mtm_decision_always, this)
- .setNeutralButton(R.string.mtm_decision_once, this)
- .setNegativeButton(R.string.mtm_decision_abort, this)
- .setOnCancelListener(this)
- .create();
- dialog.show();
- }
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ LOGGER.log(Level.FINE, "onCreate");
+ super.onCreate(savedInstanceState);
+ }
- @Override
- protected void onPause() {
- if (dialog.isShowing())
- dialog.dismiss();
- super.onPause();
- }
+ @Override
+ public void onResume() {
+ super.onResume();
+ Intent i = getIntent();
+ decisionId = i.getIntExtra(MemorizingTrustManager.DECISION_INTENT_ID, MTMDecision.DECISION_INVALID);
+ int titleId = i.getIntExtra(MemorizingTrustManager.DECISION_TITLE_ID, R.string.mtm_accept_cert);
+ String cert = i.getStringExtra(MemorizingTrustManager.DECISION_INTENT_CERT);
+ LOGGER.log(Level.FINE, "onResume with " + i.getExtras() + " decId=" + decisionId + " data: " + i.getData());
+ dialog = new AlertDialog.Builder(this).setTitle(titleId)
+ .setMessage(cert)
+ .setPositiveButton(R.string.mtm_decision_always, this)
+ .setNeutralButton(R.string.mtm_decision_once, this)
+ .setNegativeButton(R.string.mtm_decision_abort, this)
+ .setOnCancelListener(this)
+ .create();
+ dialog.show();
+ }
- void sendDecision(int decision) {
- LOGGER.log(Level.FINE, "Sending decision: " + decision);
- MemorizingTrustManager.interactResult(decisionId, decision);
- finish();
- }
+ @Override
+ protected void onPause() {
+ if (dialog.isShowing())
+ dialog.dismiss();
+ super.onPause();
+ }
- // react on AlertDialog button press
- public void onClick(DialogInterface dialog, int btnId) {
- int decision;
- dialog.dismiss();
- switch (btnId) {
- case DialogInterface.BUTTON_POSITIVE:
- decision = MTMDecision.DECISION_ALWAYS;
- break;
- case DialogInterface.BUTTON_NEUTRAL:
- decision = MTMDecision.DECISION_ONCE;
- break;
- default:
- decision = MTMDecision.DECISION_ABORT;
- }
- sendDecision(decision);
- }
+ void sendDecision(int decision) {
+ LOGGER.log(Level.FINE, "Sending decision: " + decision);
+ MemorizingTrustManager.interactResult(decisionId, decision);
+ finish();
+ }
- public void onCancel(DialogInterface dialog) {
- sendDecision(MTMDecision.DECISION_ABORT);
- }
+ // react on AlertDialog button press
+ public void onClick(DialogInterface dialog, int btnId) {
+ int decision;
+ dialog.dismiss();
+ switch (btnId) {
+ case DialogInterface.BUTTON_POSITIVE:
+ decision = MTMDecision.DECISION_ALWAYS;
+ break;
+ case DialogInterface.BUTTON_NEUTRAL:
+ decision = MTMDecision.DECISION_ONCE;
+ break;
+ default:
+ decision = MTMDecision.DECISION_ABORT;
+ }
+ sendDecision(decision);
+ }
+
+ public void onCancel(DialogInterface dialog) {
+ sendDecision(MTMDecision.DECISION_ABORT);
+ }
}
diff --git a/libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingTrustManager.java b/libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingTrustManager.java
index d1a7e341a..d2fc75db8 100644
--- a/libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingTrustManager.java
+++ b/libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingTrustManager.java
@@ -73,667 +73,666 @@ import javax.net.ssl.X509TrustManager;
* opening sockets!
*/
public class MemorizingTrustManager implements X509TrustManager {
- final static String DECISION_INTENT = "de.duenndns.ssl.DECISION";
- final static String DECISION_INTENT_ID = DECISION_INTENT + ".decisionId";
- final static String DECISION_INTENT_CERT = DECISION_INTENT + ".cert";
- final static String DECISION_INTENT_CHOICE = DECISION_INTENT + ".decisionChoice";
-
- private final static Logger LOGGER = Logger.getLogger(MemorizingTrustManager.class.getName());
- final static String DECISION_TITLE_ID = DECISION_INTENT + ".titleId";
- private final static int NOTIFICATION_ID = 100509;
-
- final static String NO_TRUST_ANCHOR = "Trust anchor for certification path not found.";
-
- static String KEYSTORE_DIR = "KeyStore";
- static String KEYSTORE_FILE = "KeyStore.bks";
-
- Context master;
- Activity foregroundAct;
- NotificationManager notificationManager;
- private static int decisionId = 0;
- private static SparseArray<MTMDecision> openDecisions = new SparseArray<MTMDecision>();
-
- Handler masterHandler;
- private File keyStoreFile;
- private KeyStore appKeyStore;
- private X509TrustManager defaultTrustManager;
- private X509TrustManager appTrustManager;
-
- /** Creates an instance of the MemorizingTrustManager class that falls back to a custom TrustManager.
- *
- * You need to supply the application context. This has to be one of:
- * - Application
- * - Activity
- * - Service
- *
- * The context is used for file management, to display the dialog /
- * notification and for obtaining translated strings.
- *
- * @param m Context for the application.
- * @param defaultTrustManager Delegate trust management to this TM. If null, the user must accept every certificate.
- */
- public MemorizingTrustManager(Context m, X509TrustManager defaultTrustManager) {
- init(m);
- this.appTrustManager = getTrustManager(appKeyStore);
- this.defaultTrustManager = defaultTrustManager;
- }
-
- /** Creates an instance of the MemorizingTrustManager class using the system X509TrustManager.
- *
- * You need to supply the application context. This has to be one of:
- * - Application
- * - Activity
- * - Service
- *
- * The context is used for file management, to display the dialog /
- * notification and for obtaining translated strings.
- *
- * @param m Context for the application.
- */
- public MemorizingTrustManager(Context m) {
- init(m);
- this.appTrustManager = getTrustManager(appKeyStore);
- this.defaultTrustManager = getTrustManager(null);
- }
-
- void init(Context m) {
- master = m;
- masterHandler = new Handler(m.getMainLooper());
- notificationManager = (NotificationManager)master.getSystemService(Context.NOTIFICATION_SERVICE);
-
- Application app;
- if (m instanceof Application) {
- app = (Application)m;
- } else if (m instanceof Service) {
- app = ((Service)m).getApplication();
- } else if (m instanceof Activity) {
- app = ((Activity)m).getApplication();
- } else throw new ClassCastException("MemorizingTrustManager context must be either Activity or Service!");
-
- File dir = app.getDir(KEYSTORE_DIR, Context.MODE_PRIVATE);
- keyStoreFile = new File(dir + File.separator + KEYSTORE_FILE);
-
- appKeyStore = loadAppKeyStore();
- }
-
-
- /**
- * Returns a X509TrustManager list containing a new instance of
- * TrustManagerFactory.
- *
- * This function is meant for convenience only. You can use it
- * as follows to integrate TrustManagerFactory for HTTPS sockets:
- *
- * <pre>
- * SSLContext sc = SSLContext.getInstance("TLS");
- * sc.init(null, MemorizingTrustManager.getInstanceList(this),
- * new java.security.SecureRandom());
- * HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
- * </pre>
- * @param c Activity or Service to show the Dialog / Notification
- */
- public static X509TrustManager[] getInstanceList(Context c) {
- return new X509TrustManager[] { new MemorizingTrustManager(c) };
- }
-
- /**
- * Binds an Activity to the MTM for displaying the query dialog.
- *
- * This is useful if your connection is run from a service that is
- * triggered by user interaction -- in such cases the activity is
- * visible and the user tends to ignore the service notification.
- *
- * You should never have a hidden activity bound to MTM! Use this
- * function in onResume() and @see unbindDisplayActivity in onPause().
- *
- * @param act Activity to be bound
- */
- public void bindDisplayActivity(Activity act) {
- foregroundAct = act;
- }
-
- /**
- * Removes an Activity from the MTM display stack.
- *
- * Always call this function when the Activity added with
- * {@link #bindDisplayActivity(Activity)} is hidden.
- *
- * @param act Activity to be unbound
- */
- public void unbindDisplayActivity(Activity act) {
- // do not remove if it was overridden by a different activity
- if (foregroundAct == act)
- foregroundAct = null;
- }
-
- /**
- * Changes the path for the KeyStore file.
- *
- * The actual filename relative to the app's directory will be
- * <code>app_<i>dirname</i>/<i>filename</i></code>.
- *
- * @param dirname directory to store the KeyStore.
- * @param filename file name for the KeyStore.
- */
- public static void setKeyStoreFile(String dirname, String filename) {
- KEYSTORE_DIR = dirname;
- KEYSTORE_FILE = filename;
- }
-
- /**
- * Get a list of all certificate aliases stored in MTM.
- *
- * @return an {@link Enumeration} of all certificates
- */
- public Enumeration<String> getCertificates() {
- try {
- return appKeyStore.aliases();
- } catch (KeyStoreException e) {
- // this should never happen, however...
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Get a certificate for a given alias.
- *
- * @param alias the certificate's alias as returned by {@link #getCertificates()}.
- *
- * @return the certificate associated with the alias or <tt>null</tt> if none found.
- */
- public Certificate getCertificate(String alias) {
- try {
- return appKeyStore.getCertificate(alias);
- } catch (KeyStoreException e) {
- // this should never happen, however...
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Removes the given certificate from MTMs key store.
- *
- * <p>
- * <b>WARNING</b>: this does not immediately invalidate the certificate. It is
- * well possible that (a) data is transmitted over still existing connections or
- * (b) new connections are created using TLS renegotiation, without a new cert
- * check.
- * </p>
- * @param alias the certificate's alias as returned by {@link #getCertificates()}.
- *
- * @throws KeyStoreException if the certificate could not be deleted.
- */
- public void deleteCertificate(String alias) throws KeyStoreException {
- appKeyStore.deleteEntry(alias);
- keyStoreUpdated();
- }
-
- /**
- * Creates a new hostname verifier supporting user interaction.
- *
- * <p>This method creates a new {@link HostnameVerifier} that is bound to
- * the given instance of {@link MemorizingTrustManager}, and leverages an
- * existing {@link HostnameVerifier}. The returned verifier performs the
- * following steps, returning as soon as one of them succeeds:
- * </p>
- * <ol>
- * <li>Success, if the wrapped defaultVerifier accepts the certificate.</li>
- * <li>Success, if the server certificate is stored in the keystore under the given hostname.</li>
- * <li>Ask the user and return accordingly.</li>
- * <li>Failure on exception.</li>
- * </ol>
- *
- * @param defaultVerifier the {@link HostnameVerifier} that should perform the actual check
- * @return a new hostname verifier using the MTM's key store
- *
- * @throws IllegalArgumentException if the defaultVerifier parameter is null
- */
- public HostnameVerifier wrapHostnameVerifier(final HostnameVerifier defaultVerifier) {
- if (defaultVerifier == null)
- throw new IllegalArgumentException("The default verifier may not be null");
-
- return new MemorizingHostnameVerifier(defaultVerifier);
- }
-
- public HostnameVerifier wrapHostnameVerifierNonInteractive(final HostnameVerifier defaultVerifier) {
- if (defaultVerifier == null)
- throw new IllegalArgumentException("The default verifier may not be null");
-
- return new NonInteractiveMemorizingHostnameVerifier(defaultVerifier);
- }
-
- X509TrustManager getTrustManager(KeyStore ks) {
- try {
- TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
- tmf.init(ks);
- for (TrustManager t : tmf.getTrustManagers()) {
- if (t instanceof X509TrustManager) {
- return (X509TrustManager)t;
- }
- }
- } catch (Exception e) {
- // Here, we are covering up errors. It might be more useful
- // however to throw them out of the constructor so the
- // embedding app knows something went wrong.
- LOGGER.log(Level.SEVERE, "getTrustManager(" + ks + ")", e);
- }
- return null;
- }
-
- KeyStore loadAppKeyStore() {
- KeyStore ks;
- try {
- ks = KeyStore.getInstance(KeyStore.getDefaultType());
- } catch (KeyStoreException e) {
- LOGGER.log(Level.SEVERE, "getAppKeyStore()", e);
- return null;
- }
- try {
- ks.load(null, null);
- ks.load(new java.io.FileInputStream(keyStoreFile), "MTM".toCharArray());
- } catch (java.io.FileNotFoundException e) {
- LOGGER.log(Level.INFO, "getAppKeyStore(" + keyStoreFile + ") - file does not exist");
- } catch (Exception e) {
- LOGGER.log(Level.SEVERE, "getAppKeyStore(" + keyStoreFile + ")", e);
- }
- return ks;
- }
-
- void storeCert(String alias, Certificate cert) {
- try {
- appKeyStore.setCertificateEntry(alias, cert);
- } catch (KeyStoreException e) {
- LOGGER.log(Level.SEVERE, "storeCert(" + cert + ")", e);
- return;
- }
- keyStoreUpdated();
- }
-
- void storeCert(X509Certificate cert) {
- storeCert(cert.getSubjectDN().toString(), cert);
- }
-
- void keyStoreUpdated() {
- // reload appTrustManager
- appTrustManager = getTrustManager(appKeyStore);
-
- // store KeyStore to file
- java.io.FileOutputStream fos = null;
- try {
- fos = new java.io.FileOutputStream(keyStoreFile);
- appKeyStore.store(fos, "MTM".toCharArray());
- } catch (Exception e) {
- LOGGER.log(Level.SEVERE, "storeCert(" + keyStoreFile + ")", e);
- } finally {
- if (fos != null) {
- try {
- fos.close();
- } catch (IOException e) {
- LOGGER.log(Level.SEVERE, "storeCert(" + keyStoreFile + ")", e);
- }
- }
- }
- }
-
- // if the certificate is stored in the app key store, it is considered "known"
- private boolean isCertKnown(X509Certificate cert) {
- try {
- return appKeyStore.getCertificateAlias(cert) != null;
- } catch (KeyStoreException e) {
- return false;
- }
- }
-
- private boolean isExpiredException(Throwable e) {
- do {
- if (e instanceof CertificateExpiredException)
- return true;
- e = e.getCause();
- } while (e != null);
- return false;
- }
-
- public void checkCertTrusted(X509Certificate[] chain, String authType, boolean isServer, boolean interactive)
- throws CertificateException
- {
- LOGGER.log(Level.FINE, "checkCertTrusted(" + chain + ", " + authType + ", " + isServer + ")");
- try {
- LOGGER.log(Level.FINE, "checkCertTrusted: trying appTrustManager");
- if (isServer)
- appTrustManager.checkServerTrusted(chain, authType);
- else
- appTrustManager.checkClientTrusted(chain, authType);
- } catch (CertificateException ae) {
- LOGGER.log(Level.FINER, "checkCertTrusted: appTrustManager failed", ae);
- // if the cert is stored in our appTrustManager, we ignore expiredness
- if (isExpiredException(ae)) {
- LOGGER.log(Level.INFO, "checkCertTrusted: accepting expired certificate from keystore");
- return;
- }
- if (isCertKnown(chain[0])) {
- LOGGER.log(Level.INFO, "checkCertTrusted: accepting cert already stored in keystore");
- return;
- }
- try {
- if (defaultTrustManager == null)
- throw ae;
- LOGGER.log(Level.FINE, "checkCertTrusted: trying defaultTrustManager");
- if (isServer)
- defaultTrustManager.checkServerTrusted(chain, authType);
- else
- defaultTrustManager.checkClientTrusted(chain, authType);
- } catch (CertificateException e) {
- e.printStackTrace();
- if (interactive) {
- interactCert(chain, authType, e);
- } else {
- throw e;
- }
- }
- }
- }
-
- public void checkClientTrusted(X509Certificate[] chain, String authType)
- throws CertificateException
- {
- checkCertTrusted(chain, authType, false,true);
- }
-
- public void checkServerTrusted(X509Certificate[] chain, String authType)
- throws CertificateException
- {
- checkCertTrusted(chain, authType, true,true);
- }
-
- public X509Certificate[] getAcceptedIssuers()
- {
- LOGGER.log(Level.FINE, "getAcceptedIssuers()");
- return defaultTrustManager.getAcceptedIssuers();
- }
-
- private int createDecisionId(MTMDecision d) {
- int myId;
- synchronized(openDecisions) {
- myId = decisionId;
- openDecisions.put(myId, d);
- decisionId += 1;
- }
- return myId;
- }
-
- private static String hexString(byte[] data) {
- StringBuffer si = new StringBuffer();
- for (int i = 0; i < data.length; i++) {
- si.append(String.format("%02x", data[i]));
- if (i < data.length - 1)
- si.append(":");
- }
- return si.toString();
- }
-
- private static String certHash(final X509Certificate cert, String digest) {
- try {
- MessageDigest md = MessageDigest.getInstance(digest);
- md.update(cert.getEncoded());
- return hexString(md.digest());
- } catch (java.security.cert.CertificateEncodingException e) {
- return e.getMessage();
- } catch (java.security.NoSuchAlgorithmException e) {
- return e.getMessage();
- }
- }
-
- private void certDetails(StringBuffer si, X509Certificate c) {
- SimpleDateFormat validityDateFormater = new SimpleDateFormat("yyyy-MM-dd");
- si.append("\n");
- si.append(c.getSubjectDN().toString());
- si.append("\n");
- si.append(validityDateFormater.format(c.getNotBefore()));
- si.append(" - ");
- si.append(validityDateFormater.format(c.getNotAfter()));
- si.append("\nSHA-256: ");
- si.append(certHash(c, "SHA-256"));
- si.append("\nSHA-1: ");
- si.append(certHash(c, "SHA-1"));
- si.append("\nSigned by: ");
- si.append(c.getIssuerDN().toString());
- si.append("\n");
- }
-
- private String certChainMessage(final X509Certificate[] chain, CertificateException cause) {
- Throwable e = cause;
- LOGGER.log(Level.FINE, "certChainMessage for " + e);
- StringBuffer si = new StringBuffer();
- if (e.getCause() != null) {
- e = e.getCause();
- // HACK: there is no sane way to check if the error is a "trust anchor
- // not found", so we use string comparison.
- if (NO_TRUST_ANCHOR.equals(e.getMessage())) {
- si.append(master.getString(R.string.mtm_trust_anchor));
- } else
- si.append(e.getLocalizedMessage());
- si.append("\n");
- }
- si.append("\n");
- si.append(master.getString(R.string.mtm_connect_anyway));
- si.append("\n\n");
- si.append(master.getString(R.string.mtm_cert_details));
- for (X509Certificate c : chain) {
- certDetails(si, c);
- }
- return si.toString();
- }
-
- private String hostNameMessage(X509Certificate cert, String hostname) {
- StringBuffer si = new StringBuffer();
-
- si.append(master.getString(R.string.mtm_hostname_mismatch, hostname));
- si.append("\n\n");
- try {
- Collection<List<?>> sans = cert.getSubjectAlternativeNames();
- if (sans == null) {
- si.append(cert.getSubjectDN());
- si.append("\n");
- } else for (List<?> altName : sans) {
- Object name = altName.get(1);
- if (name instanceof String) {
- si.append("[");
- si.append((Integer)altName.get(0));
- si.append("] ");
- si.append(name);
- si.append("\n");
- }
- }
- } catch (CertificateParsingException e) {
- e.printStackTrace();
- si.append("<Parsing error: ");
- si.append(e.getLocalizedMessage());
- si.append(">\n");
- }
- si.append("\n");
- si.append(master.getString(R.string.mtm_connect_anyway));
- si.append("\n\n");
- si.append(master.getString(R.string.mtm_cert_details));
- certDetails(si, cert);
- return si.toString();
- }
-
- // We can use Notification.Builder once MTM's minSDK is >= 11
- @SuppressWarnings("deprecation")
- void startActivityNotification(Intent intent, int decisionId, String certName) {
- Notification n = new Notification(android.R.drawable.ic_lock_lock,
- master.getString(R.string.mtm_notification),
- System.currentTimeMillis());
- PendingIntent call = PendingIntent.getActivity(master, 0, intent, 0);
- n.setLatestEventInfo(master.getApplicationContext(),
- master.getString(R.string.mtm_notification),
- certName, call);
- n.flags |= Notification.FLAG_AUTO_CANCEL;
-
- notificationManager.notify(NOTIFICATION_ID + decisionId, n);
- }
-
- /**
- * Returns the top-most entry of the activity stack.
- *
- * @return the Context of the currently bound UI or the master context if none is bound
- */
- Context getUI() {
- return (foregroundAct != null) ? foregroundAct : master;
- }
-
- int interact(final String message, final int titleId) {
- /* prepare the MTMDecision blocker object */
- MTMDecision choice = new MTMDecision();
- final int myId = createDecisionId(choice);
-
- masterHandler.post(new Runnable() {
- public void run() {
- Intent ni = new Intent(master, MemorizingActivity.class);
- ni.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- ni.setData(Uri.parse(MemorizingTrustManager.class.getName() + "/" + myId));
- ni.putExtra(DECISION_INTENT_ID, myId);
- ni.putExtra(DECISION_INTENT_CERT, message);
- ni.putExtra(DECISION_TITLE_ID, titleId);
-
- // we try to directly start the activity and fall back to
- // making a notification
- try {
- getUI().startActivity(ni);
- } catch (Exception e) {
- LOGGER.log(Level.FINE, "startActivity(MemorizingActivity)", e);
- startActivityNotification(ni, myId, message);
- }
- }
- });
-
- LOGGER.log(Level.FINE, "openDecisions: " + openDecisions + ", waiting on " + myId);
- try {
- synchronized(choice) { choice.wait(); }
- } catch (InterruptedException e) {
- LOGGER.log(Level.FINER, "InterruptedException", e);
- }
- LOGGER.log(Level.FINE, "finished wait on " + myId + ": " + choice.state);
- return choice.state;
- }
-
- void interactCert(final X509Certificate[] chain, String authType, CertificateException cause)
- throws CertificateException
- {
- switch (interact(certChainMessage(chain, cause), R.string.mtm_accept_cert)) {
- case MTMDecision.DECISION_ALWAYS:
- storeCert(chain[0]); // only store the server cert, not the whole chain
- case MTMDecision.DECISION_ONCE:
- break;
- default:
- throw (cause);
- }
- }
-
- boolean interactHostname(X509Certificate cert, String hostname)
- {
- switch (interact(hostNameMessage(cert, hostname), R.string.mtm_accept_servername)) {
- case MTMDecision.DECISION_ALWAYS:
- storeCert(hostname, cert);
- case MTMDecision.DECISION_ONCE:
- return true;
- default:
- return false;
- }
- }
-
- protected static void interactResult(int decisionId, int choice) {
- MTMDecision d;
- synchronized(openDecisions) {
- d = openDecisions.get(decisionId);
- openDecisions.remove(decisionId);
- }
- if (d == null) {
- LOGGER.log(Level.SEVERE, "interactResult: aborting due to stale decision reference!");
- return;
- }
- synchronized(d) {
- d.state = choice;
- d.notify();
- }
- }
-
- class MemorizingHostnameVerifier implements HostnameVerifier {
- private HostnameVerifier defaultVerifier;
-
- public MemorizingHostnameVerifier(HostnameVerifier wrapped) {
- defaultVerifier = wrapped;
- }
-
- protected boolean verify(String hostname, SSLSession session, boolean interactive) {
- LOGGER.log(Level.FINE, "hostname verifier for " + hostname + ", trying default verifier first");
- // if the default verifier accepts the hostname, we are done
- if (defaultVerifier.verify(hostname, session)) {
- LOGGER.log(Level.FINE, "default verifier accepted " + hostname);
- return true;
- }
- // otherwise, we check if the hostname is an alias for this cert in our keystore
- try {
- X509Certificate cert = (X509Certificate)session.getPeerCertificates()[0];
- //Log.d(TAG, "cert: " + cert);
- if (cert.equals(appKeyStore.getCertificate(hostname.toLowerCase(Locale.US)))) {
- LOGGER.log(Level.FINE, "certificate for " + hostname + " is in our keystore. accepting.");
- return true;
- } else {
- LOGGER.log(Level.FINE, "server " + hostname + " provided wrong certificate, asking user.");
- if (interactive) {
- return interactHostname(cert, hostname);
- } else {
- return false;
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
- @Override
- public boolean verify(String hostname, SSLSession session) {
- return verify(hostname, session, true);
- }
- }
-
- class NonInteractiveMemorizingHostnameVerifier extends MemorizingHostnameVerifier {
-
- public NonInteractiveMemorizingHostnameVerifier(HostnameVerifier wrapped) {
- super(wrapped);
- }
- @Override
- public boolean verify(String hostname, SSLSession session) {
- return verify(hostname, session, false);
- }
-
-
- }
-
- public X509TrustManager getNonInteractive() {
- return new NonInteractiveMemorizingTrustManager();
- }
-
- private class NonInteractiveMemorizingTrustManager implements X509TrustManager {
-
- @Override
- public void checkClientTrusted(X509Certificate[] chain, String authType)
- throws CertificateException {
- MemorizingTrustManager.this.checkCertTrusted(chain, authType, false, false);
- }
-
- @Override
- public void checkServerTrusted(X509Certificate[] chain, String authType)
- throws CertificateException {
- MemorizingTrustManager.this.checkCertTrusted(chain, authType, true, false);
- }
-
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return MemorizingTrustManager.this.getAcceptedIssuers();
- }
-
- }
+ final static String DECISION_INTENT = "de.duenndns.ssl.DECISION";
+ final static String DECISION_INTENT_ID = DECISION_INTENT + ".decisionId";
+ final static String DECISION_INTENT_CERT = DECISION_INTENT + ".cert";
+ final static String DECISION_INTENT_CHOICE = DECISION_INTENT + ".decisionChoice";
+
+ private final static Logger LOGGER = Logger.getLogger(MemorizingTrustManager.class.getName());
+ final static String DECISION_TITLE_ID = DECISION_INTENT + ".titleId";
+ private final static int NOTIFICATION_ID = 100509;
+
+ final static String NO_TRUST_ANCHOR = "Trust anchor for certification path not found.";
+
+ static String KEYSTORE_DIR = "KeyStore";
+ static String KEYSTORE_FILE = "KeyStore.bks";
+
+ Context master;
+ Activity foregroundAct;
+ NotificationManager notificationManager;
+ private static int decisionId = 0;
+ private static SparseArray<MTMDecision> openDecisions = new SparseArray<MTMDecision>();
+
+ Handler masterHandler;
+ private File keyStoreFile;
+ private KeyStore appKeyStore;
+ private X509TrustManager defaultTrustManager;
+ private X509TrustManager appTrustManager;
+
+ /**
+ * Creates an instance of the MemorizingTrustManager class that falls back to a custom TrustManager.
+ * <p>
+ * You need to supply the application context. This has to be one of:
+ * - Application
+ * - Activity
+ * - Service
+ * <p>
+ * The context is used for file management, to display the dialog /
+ * notification and for obtaining translated strings.
+ *
+ * @param m Context for the application.
+ * @param defaultTrustManager Delegate trust management to this TM. If null, the user must accept every certificate.
+ */
+ public MemorizingTrustManager(Context m, X509TrustManager defaultTrustManager) {
+ init(m);
+ this.appTrustManager = getTrustManager(appKeyStore);
+ this.defaultTrustManager = defaultTrustManager;
+ }
+
+ /**
+ * Creates an instance of the MemorizingTrustManager class using the system X509TrustManager.
+ * <p>
+ * You need to supply the application context. This has to be one of:
+ * - Application
+ * - Activity
+ * - Service
+ * <p>
+ * The context is used for file management, to display the dialog /
+ * notification and for obtaining translated strings.
+ *
+ * @param m Context for the application.
+ */
+ public MemorizingTrustManager(Context m) {
+ init(m);
+ this.appTrustManager = getTrustManager(appKeyStore);
+ this.defaultTrustManager = getTrustManager(null);
+ }
+
+ void init(Context m) {
+ master = m;
+ masterHandler = new Handler(m.getMainLooper());
+ notificationManager = (NotificationManager) master.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ Application app;
+ if (m instanceof Application) {
+ app = (Application) m;
+ } else if (m instanceof Service) {
+ app = ((Service) m).getApplication();
+ } else if (m instanceof Activity) {
+ app = ((Activity) m).getApplication();
+ } else
+ throw new ClassCastException("MemorizingTrustManager context must be either Activity or Service!");
+
+ File dir = app.getDir(KEYSTORE_DIR, Context.MODE_PRIVATE);
+ keyStoreFile = new File(dir + File.separator + KEYSTORE_FILE);
+
+ appKeyStore = loadAppKeyStore();
+ }
+
+
+ /**
+ * Returns a X509TrustManager list containing a new instance of
+ * TrustManagerFactory.
+ * <p>
+ * This function is meant for convenience only. You can use it
+ * as follows to integrate TrustManagerFactory for HTTPS sockets:
+ * <p>
+ * <pre>
+ * SSLContext sc = SSLContext.getInstance("TLS");
+ * sc.init(null, MemorizingTrustManager.getInstanceList(this),
+ * new java.security.SecureRandom());
+ * HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+ * </pre>
+ *
+ * @param c Activity or Service to show the Dialog / Notification
+ */
+ public static X509TrustManager[] getInstanceList(Context c) {
+ return new X509TrustManager[]{new MemorizingTrustManager(c)};
+ }
+
+ /**
+ * Binds an Activity to the MTM for displaying the query dialog.
+ * <p>
+ * This is useful if your connection is run from a service that is
+ * triggered by user interaction -- in such cases the activity is
+ * visible and the user tends to ignore the service notification.
+ * <p>
+ * You should never have a hidden activity bound to MTM! Use this
+ * function in onResume() and @see unbindDisplayActivity in onPause().
+ *
+ * @param act Activity to be bound
+ */
+ public void bindDisplayActivity(Activity act) {
+ foregroundAct = act;
+ }
+
+ /**
+ * Removes an Activity from the MTM display stack.
+ * <p>
+ * Always call this function when the Activity added with
+ * {@link #bindDisplayActivity(Activity)} is hidden.
+ *
+ * @param act Activity to be unbound
+ */
+ public void unbindDisplayActivity(Activity act) {
+ // do not remove if it was overridden by a different activity
+ if (foregroundAct == act)
+ foregroundAct = null;
+ }
+
+ /**
+ * Changes the path for the KeyStore file.
+ * <p>
+ * The actual filename relative to the app's directory will be
+ * <code>app_<i>dirname</i>/<i>filename</i></code>.
+ *
+ * @param dirname directory to store the KeyStore.
+ * @param filename file name for the KeyStore.
+ */
+ public static void setKeyStoreFile(String dirname, String filename) {
+ KEYSTORE_DIR = dirname;
+ KEYSTORE_FILE = filename;
+ }
+
+ /**
+ * Get a list of all certificate aliases stored in MTM.
+ *
+ * @return an {@link Enumeration} of all certificates
+ */
+ public Enumeration<String> getCertificates() {
+ try {
+ return appKeyStore.aliases();
+ } catch (KeyStoreException e) {
+ // this should never happen, however...
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Get a certificate for a given alias.
+ *
+ * @param alias the certificate's alias as returned by {@link #getCertificates()}.
+ * @return the certificate associated with the alias or <tt>null</tt> if none found.
+ */
+ public Certificate getCertificate(String alias) {
+ try {
+ return appKeyStore.getCertificate(alias);
+ } catch (KeyStoreException e) {
+ // this should never happen, however...
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Removes the given certificate from MTMs key store.
+ * <p>
+ * <p>
+ * <b>WARNING</b>: this does not immediately invalidate the certificate. It is
+ * well possible that (a) data is transmitted over still existing connections or
+ * (b) new connections are created using TLS renegotiation, without a new cert
+ * check.
+ * </p>
+ *
+ * @param alias the certificate's alias as returned by {@link #getCertificates()}.
+ * @throws KeyStoreException if the certificate could not be deleted.
+ */
+ public void deleteCertificate(String alias) throws KeyStoreException {
+ appKeyStore.deleteEntry(alias);
+ keyStoreUpdated();
+ }
+
+ /**
+ * Creates a new hostname verifier supporting user interaction.
+ * <p>
+ * <p>This method creates a new {@link HostnameVerifier} that is bound to
+ * the given instance of {@link MemorizingTrustManager}, and leverages an
+ * existing {@link HostnameVerifier}. The returned verifier performs the
+ * following steps, returning as soon as one of them succeeds:
+ * </p>
+ * <ol>
+ * <li>Success, if the wrapped defaultVerifier accepts the certificate.</li>
+ * <li>Success, if the server certificate is stored in the keystore under the given hostname.</li>
+ * <li>Ask the user and return accordingly.</li>
+ * <li>Failure on exception.</li>
+ * </ol>
+ *
+ * @param defaultVerifier the {@link HostnameVerifier} that should perform the actual check
+ * @return a new hostname verifier using the MTM's key store
+ * @throws IllegalArgumentException if the defaultVerifier parameter is null
+ */
+ public HostnameVerifier wrapHostnameVerifier(final HostnameVerifier defaultVerifier) {
+ if (defaultVerifier == null)
+ throw new IllegalArgumentException("The default verifier may not be null");
+
+ return new MemorizingHostnameVerifier(defaultVerifier);
+ }
+
+ public HostnameVerifier wrapHostnameVerifierNonInteractive(final HostnameVerifier defaultVerifier) {
+ if (defaultVerifier == null)
+ throw new IllegalArgumentException("The default verifier may not be null");
+
+ return new NonInteractiveMemorizingHostnameVerifier(defaultVerifier);
+ }
+
+ X509TrustManager getTrustManager(KeyStore ks) {
+ try {
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
+ tmf.init(ks);
+ for (TrustManager t : tmf.getTrustManagers()) {
+ if (t instanceof X509TrustManager) {
+ return (X509TrustManager) t;
+ }
+ }
+ } catch (Exception e) {
+ // Here, we are covering up errors. It might be more useful
+ // however to throw them out of the constructor so the
+ // embedding app knows something went wrong.
+ LOGGER.log(Level.SEVERE, "getTrustManager(" + ks + ")", e);
+ }
+ return null;
+ }
+
+ KeyStore loadAppKeyStore() {
+ KeyStore ks;
+ try {
+ ks = KeyStore.getInstance(KeyStore.getDefaultType());
+ } catch (KeyStoreException e) {
+ LOGGER.log(Level.SEVERE, "getAppKeyStore()", e);
+ return null;
+ }
+ try {
+ ks.load(null, null);
+ ks.load(new java.io.FileInputStream(keyStoreFile), "MTM".toCharArray());
+ } catch (java.io.FileNotFoundException e) {
+ LOGGER.log(Level.INFO, "getAppKeyStore(" + keyStoreFile + ") - file does not exist");
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, "getAppKeyStore(" + keyStoreFile + ")", e);
+ }
+ return ks;
+ }
+
+ void storeCert(String alias, Certificate cert) {
+ try {
+ appKeyStore.setCertificateEntry(alias, cert);
+ } catch (KeyStoreException e) {
+ LOGGER.log(Level.SEVERE, "storeCert(" + cert + ")", e);
+ return;
+ }
+ keyStoreUpdated();
+ }
+
+ void storeCert(X509Certificate cert) {
+ storeCert(cert.getSubjectDN().toString(), cert);
+ }
+
+ void keyStoreUpdated() {
+ // reload appTrustManager
+ appTrustManager = getTrustManager(appKeyStore);
+
+ // store KeyStore to file
+ java.io.FileOutputStream fos = null;
+ try {
+ fos = new java.io.FileOutputStream(keyStoreFile);
+ appKeyStore.store(fos, "MTM".toCharArray());
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, "storeCert(" + keyStoreFile + ")", e);
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE, "storeCert(" + keyStoreFile + ")", e);
+ }
+ }
+ }
+ }
+
+ // if the certificate is stored in the app key store, it is considered "known"
+ private boolean isCertKnown(X509Certificate cert) {
+ try {
+ return appKeyStore.getCertificateAlias(cert) != null;
+ } catch (KeyStoreException e) {
+ return false;
+ }
+ }
+
+ private boolean isExpiredException(Throwable e) {
+ do {
+ if (e instanceof CertificateExpiredException)
+ return true;
+ e = e.getCause();
+ } while (e != null);
+ return false;
+ }
+
+ public void checkCertTrusted(X509Certificate[] chain, String authType, boolean isServer, boolean interactive)
+ throws CertificateException {
+ LOGGER.log(Level.FINE, "checkCertTrusted(" + chain + ", " + authType + ", " + isServer + ")");
+ try {
+ LOGGER.log(Level.FINE, "checkCertTrusted: trying appTrustManager");
+ if (isServer)
+ appTrustManager.checkServerTrusted(chain, authType);
+ else
+ appTrustManager.checkClientTrusted(chain, authType);
+ } catch (CertificateException ae) {
+ LOGGER.log(Level.FINER, "checkCertTrusted: appTrustManager failed", ae);
+ // if the cert is stored in our appTrustManager, we ignore expiredness
+ if (isExpiredException(ae)) {
+ LOGGER.log(Level.INFO, "checkCertTrusted: accepting expired certificate from keystore");
+ return;
+ }
+ if (isCertKnown(chain[0])) {
+ LOGGER.log(Level.INFO, "checkCertTrusted: accepting cert already stored in keystore");
+ return;
+ }
+ try {
+ if (defaultTrustManager == null)
+ throw ae;
+ LOGGER.log(Level.FINE, "checkCertTrusted: trying defaultTrustManager");
+ if (isServer)
+ defaultTrustManager.checkServerTrusted(chain, authType);
+ else
+ defaultTrustManager.checkClientTrusted(chain, authType);
+ } catch (CertificateException e) {
+ e.printStackTrace();
+ if (interactive) {
+ interactCert(chain, authType, e);
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ checkCertTrusted(chain, authType, false, true);
+ }
+
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ checkCertTrusted(chain, authType, true, true);
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ LOGGER.log(Level.FINE, "getAcceptedIssuers()");
+ return defaultTrustManager.getAcceptedIssuers();
+ }
+
+ private int createDecisionId(MTMDecision d) {
+ int myId;
+ synchronized (openDecisions) {
+ myId = decisionId;
+ openDecisions.put(myId, d);
+ decisionId += 1;
+ }
+ return myId;
+ }
+
+ private static String hexString(byte[] data) {
+ StringBuffer si = new StringBuffer();
+ for (int i = 0; i < data.length; i++) {
+ si.append(String.format("%02x", data[i]));
+ if (i < data.length - 1)
+ si.append(":");
+ }
+ return si.toString();
+ }
+
+ private static String certHash(final X509Certificate cert, String digest) {
+ try {
+ MessageDigest md = MessageDigest.getInstance(digest);
+ md.update(cert.getEncoded());
+ return hexString(md.digest());
+ } catch (java.security.cert.CertificateEncodingException e) {
+ return e.getMessage();
+ } catch (java.security.NoSuchAlgorithmException e) {
+ return e.getMessage();
+ }
+ }
+
+ private void certDetails(StringBuffer si, X509Certificate c) {
+ SimpleDateFormat validityDateFormater = new SimpleDateFormat("yyyy-MM-dd");
+ si.append("\n");
+ si.append(c.getSubjectDN().toString());
+ si.append("\n");
+ si.append(validityDateFormater.format(c.getNotBefore()));
+ si.append(" - ");
+ si.append(validityDateFormater.format(c.getNotAfter()));
+ si.append("\nSHA-256: ");
+ si.append(certHash(c, "SHA-256"));
+ si.append("\nSHA-1: ");
+ si.append(certHash(c, "SHA-1"));
+ si.append("\nSigned by: ");
+ si.append(c.getIssuerDN().toString());
+ si.append("\n");
+ }
+
+ private String certChainMessage(final X509Certificate[] chain, CertificateException cause) {
+ Throwable e = cause;
+ LOGGER.log(Level.FINE, "certChainMessage for " + e);
+ StringBuffer si = new StringBuffer();
+ if (e.getCause() != null) {
+ e = e.getCause();
+ // HACK: there is no sane way to check if the error is a "trust anchor
+ // not found", so we use string comparison.
+ if (NO_TRUST_ANCHOR.equals(e.getMessage())) {
+ si.append(master.getString(R.string.mtm_trust_anchor));
+ } else
+ si.append(e.getLocalizedMessage());
+ si.append("\n");
+ }
+ si.append("\n");
+ si.append(master.getString(R.string.mtm_connect_anyway));
+ si.append("\n\n");
+ si.append(master.getString(R.string.mtm_cert_details));
+ for (X509Certificate c : chain) {
+ certDetails(si, c);
+ }
+ return si.toString();
+ }
+
+ private String hostNameMessage(X509Certificate cert, String hostname) {
+ StringBuffer si = new StringBuffer();
+
+ si.append(master.getString(R.string.mtm_hostname_mismatch, hostname));
+ si.append("\n\n");
+ try {
+ Collection<List<?>> sans = cert.getSubjectAlternativeNames();
+ if (sans == null) {
+ si.append(cert.getSubjectDN());
+ si.append("\n");
+ } else for (List<?> altName : sans) {
+ Object name = altName.get(1);
+ if (name instanceof String) {
+ si.append("[");
+ si.append((Integer) altName.get(0));
+ si.append("] ");
+ si.append(name);
+ si.append("\n");
+ }
+ }
+ } catch (CertificateParsingException e) {
+ e.printStackTrace();
+ si.append("<Parsing error: ");
+ si.append(e.getLocalizedMessage());
+ si.append(">\n");
+ }
+ si.append("\n");
+ si.append(master.getString(R.string.mtm_connect_anyway));
+ si.append("\n\n");
+ si.append(master.getString(R.string.mtm_cert_details));
+ certDetails(si, cert);
+ return si.toString();
+ }
+
+ // We can use Notification.Builder once MTM's minSDK is >= 11
+ @SuppressWarnings("deprecation")
+ void startActivityNotification(Intent intent, int decisionId, String certName) {
+ Notification n = new Notification(android.R.drawable.ic_lock_lock,
+ master.getString(R.string.mtm_notification),
+ System.currentTimeMillis());
+ PendingIntent call = PendingIntent.getActivity(master, 0, intent, 0);
+ n.setLatestEventInfo(master.getApplicationContext(),
+ master.getString(R.string.mtm_notification),
+ certName, call);
+ n.flags |= Notification.FLAG_AUTO_CANCEL;
+
+ notificationManager.notify(NOTIFICATION_ID + decisionId, n);
+ }
+
+ /**
+ * Returns the top-most entry of the activity stack.
+ *
+ * @return the Context of the currently bound UI or the master context if none is bound
+ */
+ Context getUI() {
+ return (foregroundAct != null) ? foregroundAct : master;
+ }
+
+ int interact(final String message, final int titleId) {
+ /* prepare the MTMDecision blocker object */
+ MTMDecision choice = new MTMDecision();
+ final int myId = createDecisionId(choice);
+
+ masterHandler.post(new Runnable() {
+ public void run() {
+ Intent ni = new Intent(master, MemorizingActivity.class);
+ ni.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ ni.setData(Uri.parse(MemorizingTrustManager.class.getName() + "/" + myId));
+ ni.putExtra(DECISION_INTENT_ID, myId);
+ ni.putExtra(DECISION_INTENT_CERT, message);
+ ni.putExtra(DECISION_TITLE_ID, titleId);
+
+ // we try to directly start the activity and fall back to
+ // making a notification
+ try {
+ getUI().startActivity(ni);
+ } catch (Exception e) {
+ LOGGER.log(Level.FINE, "startActivity(MemorizingActivity)", e);
+ startActivityNotification(ni, myId, message);
+ }
+ }
+ });
+
+ LOGGER.log(Level.FINE, "openDecisions: " + openDecisions + ", waiting on " + myId);
+ try {
+ synchronized (choice) {
+ choice.wait();
+ }
+ } catch (InterruptedException e) {
+ LOGGER.log(Level.FINER, "InterruptedException", e);
+ }
+ LOGGER.log(Level.FINE, "finished wait on " + myId + ": " + choice.state);
+ return choice.state;
+ }
+
+ void interactCert(final X509Certificate[] chain, String authType, CertificateException cause)
+ throws CertificateException {
+ switch (interact(certChainMessage(chain, cause), R.string.mtm_accept_cert)) {
+ case MTMDecision.DECISION_ALWAYS:
+ storeCert(chain[0]); // only store the server cert, not the whole chain
+ case MTMDecision.DECISION_ONCE:
+ break;
+ default:
+ throw (cause);
+ }
+ }
+
+ boolean interactHostname(X509Certificate cert, String hostname) {
+ switch (interact(hostNameMessage(cert, hostname), R.string.mtm_accept_servername)) {
+ case MTMDecision.DECISION_ALWAYS:
+ storeCert(hostname, cert);
+ case MTMDecision.DECISION_ONCE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected static void interactResult(int decisionId, int choice) {
+ MTMDecision d;
+ synchronized (openDecisions) {
+ d = openDecisions.get(decisionId);
+ openDecisions.remove(decisionId);
+ }
+ if (d == null) {
+ LOGGER.log(Level.SEVERE, "interactResult: aborting due to stale decision reference!");
+ return;
+ }
+ synchronized (d) {
+ d.state = choice;
+ d.notify();
+ }
+ }
+
+ class MemorizingHostnameVerifier implements HostnameVerifier {
+ private HostnameVerifier defaultVerifier;
+
+ public MemorizingHostnameVerifier(HostnameVerifier wrapped) {
+ defaultVerifier = wrapped;
+ }
+
+ protected boolean verify(String hostname, SSLSession session, boolean interactive) {
+ LOGGER.log(Level.FINE, "hostname verifier for " + hostname + ", trying default verifier first");
+ // if the default verifier accepts the hostname, we are done
+ if (defaultVerifier.verify(hostname, session)) {
+ LOGGER.log(Level.FINE, "default verifier accepted " + hostname);
+ return true;
+ }
+ // otherwise, we check if the hostname is an alias for this cert in our keystore
+ try {
+ X509Certificate cert = (X509Certificate) session.getPeerCertificates()[0];
+ //Log.d(TAG, "cert: " + cert);
+ if (cert.equals(appKeyStore.getCertificate(hostname.toLowerCase(Locale.US)))) {
+ LOGGER.log(Level.FINE, "certificate for " + hostname + " is in our keystore. accepting.");
+ return true;
+ } else {
+ LOGGER.log(Level.FINE, "server " + hostname + " provided wrong certificate, asking user.");
+ if (interactive) {
+ return interactHostname(cert, hostname);
+ } else {
+ return false;
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ return verify(hostname, session, true);
+ }
+ }
+
+ class NonInteractiveMemorizingHostnameVerifier extends MemorizingHostnameVerifier {
+
+ public NonInteractiveMemorizingHostnameVerifier(HostnameVerifier wrapped) {
+ super(wrapped);
+ }
+
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ return verify(hostname, session, false);
+ }
+
+
+ }
+
+ public X509TrustManager getNonInteractive() {
+ return new NonInteractiveMemorizingTrustManager();
+ }
+
+ private class NonInteractiveMemorizingTrustManager implements X509TrustManager {
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ MemorizingTrustManager.this.checkCertTrusted(chain, authType, false, false);
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ MemorizingTrustManager.this.checkCertTrusted(chain, authType, true, false);
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return MemorizingTrustManager.this.getAcceptedIssuers();
+ }
+
+ }
}
diff --git a/libs/audiowife/build.gradle b/libs/audiowife/build.gradle
index 63c2e8e78..15adabe9f 100644
--- a/libs/audiowife/build.gradle
+++ b/libs/audiowife/build.gradle
@@ -1,12 +1,12 @@
apply plugin: 'com.android.library'
android {
- compileSdkVersion 23
+ compileSdkVersion 23
buildToolsVersion "23.0.3"
- defaultConfig {
- minSdkVersion 14
- targetSdkVersion 21
+ defaultConfig {
+ minSdkVersion 14
+ targetSdkVersion 21
versionCode 1
versionName "1.0"
}
diff --git a/libs/audiowife/src/main/AndroidManifest.xml b/libs/audiowife/src/main/AndroidManifest.xml
index dad095276..5876c3afe 100644
--- a/libs/audiowife/src/main/AndroidManifest.xml
+++ b/libs/audiowife/src/main/AndroidManifest.xml
@@ -1,7 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="nl.changer.audiowife">
- <application android:allowBackup="true"
+ <application
+ android:allowBackup="true"
android:label="@string/app_name">
</application>
diff --git a/libs/audiowife/src/main/java/nl/changer/audiowife/AudioWife.java b/libs/audiowife/src/main/java/nl/changer/audiowife/AudioWife.java
index 26fb08e91..25bf7a003 100644
--- a/libs/audiowife/src/main/java/nl/changer/audiowife/AudioWife.java
+++ b/libs/audiowife/src/main/java/nl/changer/audiowife/AudioWife.java
@@ -685,6 +685,7 @@ public class AudioWife {
mHasDefaultUi = true;
return this;
}
+
public void release() {
if (mMediaPlayer != null) {
diff --git a/libs/audiowife/src/main/res/drawable/gray_border_wo_padding.xml b/libs/audiowife/src/main/res/drawable/gray_border_wo_padding.xml
index 409ed7ccd..890aadcbf 100644
--- a/libs/audiowife/src/main/res/drawable/gray_border_wo_padding.xml
+++ b/libs/audiowife/src/main/res/drawable/gray_border_wo_padding.xml
@@ -1,12 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android" >
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- view border color and width -->
<stroke
android:width="1dp"
- android:color="#F2F2F2" >
- </stroke>
+ android:color="#F2F2F2"></stroke>
<gradient
android:endColor="@android:color/white"
diff --git a/libs/audiowife/src/main/res/layout/aw_player.xml b/libs/audiowife/src/main/res/layout/aw_player.xml
index 6d2904e31..f3e061381 100644
--- a/libs/audiowife/src/main/res/layout/aw_player.xml
+++ b/libs/audiowife/src/main/res/layout/aw_player.xml
@@ -27,7 +27,7 @@
android:layout_weight="0.8"
android:paddingBottom="5dp"
android:paddingRight="10dp"
- android:paddingTop="5dp" >
+ android:paddingTop="5dp">
<SeekBar
android:id="@+id/media_seekbar"
diff --git a/libs/emojicon/AndroidManifest.xml b/libs/emojicon/AndroidManifest.xml
index a07b8e21b..135e6fec5 100644
--- a/libs/emojicon/AndroidManifest.xml
+++ b/libs/emojicon/AndroidManifest.xml
@@ -1,9 +1,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="github.ankushsachdeva.emojicon">
- <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19"/>
+ <uses-sdk
+ android:minSdkVersion="8"
+ android:targetSdkVersion="19" />
- <application android:allowBackup="true"
+ <application
+ android:allowBackup="true"
android:label="@string/app_name"
android:icon="@drawable/ic_launcher">
diff --git a/libs/emojicon/lint.xml b/libs/emojicon/lint.xml
index 8423c0ef9..c70207fba 100644
--- a/libs/emojicon/lint.xml
+++ b/libs/emojicon/lint.xml
@@ -1,3 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
-<lint>
-</lint> \ No newline at end of file
+<lint></lint> \ No newline at end of file
diff --git a/libs/emojicon/project.properties b/libs/emojicon/project.properties
index 91d2b0246..414284d51 100644
--- a/libs/emojicon/project.properties
+++ b/libs/emojicon/project.properties
@@ -9,7 +9,6 @@
#
# 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
android.library=true
diff --git a/libs/emojicon/res/drawable/ic_emoji_nature_light.xml b/libs/emojicon/res/drawable/ic_emoji_nature_light.xml
index 543409e03..87464fb1e 100644
--- a/libs/emojicon/res/drawable/ic_emoji_nature_light.xml
+++ b/libs/emojicon/res/drawable/ic_emoji_nature_light.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
/*
**
** Copyright 2013, The Android Open Source Project
@@ -19,15 +18,8 @@
-->
<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" />
+ <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/res/drawable/ic_emoji_objects_light.xml b/libs/emojicon/res/drawable/ic_emoji_objects_light.xml
index 4096e695b..74c80e3d0 100644
--- a/libs/emojicon/res/drawable/ic_emoji_objects_light.xml
+++ b/libs/emojicon/res/drawable/ic_emoji_objects_light.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
/*
**
** Copyright 2013, The Android Open Source Project
@@ -19,14 +18,8 @@
-->
<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: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/res/drawable/ic_emoji_people_light.xml b/libs/emojicon/res/drawable/ic_emoji_people_light.xml
index ea9e406a4..06f7aebbf 100644
--- a/libs/emojicon/res/drawable/ic_emoji_people_light.xml
+++ b/libs/emojicon/res/drawable/ic_emoji_people_light.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
/*
**
** Copyright 2013, The Android Open Source Project
@@ -19,14 +18,8 @@
-->
<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: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/res/drawable/ic_emoji_places_light.xml b/libs/emojicon/res/drawable/ic_emoji_places_light.xml
index 312cad9c3..36f258876 100644
--- a/libs/emojicon/res/drawable/ic_emoji_places_light.xml
+++ b/libs/emojicon/res/drawable/ic_emoji_places_light.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
/*
**
** Copyright 2013, The Android Open Source Project
@@ -19,14 +18,8 @@
-->
<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: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/res/drawable/ic_emoji_recent_light.xml b/libs/emojicon/res/drawable/ic_emoji_recent_light.xml
index 8c2123f83..90f2c8565 100644
--- a/libs/emojicon/res/drawable/ic_emoji_recent_light.xml
+++ b/libs/emojicon/res/drawable/ic_emoji_recent_light.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
/*
**
** Copyright 2013, The Android Open Source Project
@@ -19,14 +18,8 @@
-->
<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: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/res/drawable/ic_emoji_symbols_light.xml b/libs/emojicon/res/drawable/ic_emoji_symbols_light.xml
index 79aaf0fc5..9077c3289 100644
--- a/libs/emojicon/res/drawable/ic_emoji_symbols_light.xml
+++ b/libs/emojicon/res/drawable/ic_emoji_symbols_light.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
/*
**
** Copyright 2013, The Android Open Source Project
@@ -19,14 +18,8 @@
-->
<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: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/res/drawable/orca_composer_attach_camera_button.xml b/libs/emojicon/res/drawable/orca_composer_attach_camera_button.xml
index 0b255fb27..56e8d0484 100644
--- a/libs/emojicon/res/drawable/orca_composer_attach_camera_button.xml
+++ b/libs/emojicon/res/drawable/orca_composer_attach_camera_button.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<selector
- xmlns:android="http://schemas.android.com/apk/res/android">
+<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/res/drawable/orca_composer_attach_location_button.xml b/libs/emojicon/res/drawable/orca_composer_attach_location_button.xml
index 4971113aa..be898ebcf 100644
--- a/libs/emojicon/res/drawable/orca_composer_attach_location_button.xml
+++ b/libs/emojicon/res/drawable/orca_composer_attach_location_button.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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
@@ -14,8 +13,7 @@
~ 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 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/res/drawable/orca_composer_attach_photo_button.xml b/libs/emojicon/res/drawable/orca_composer_attach_photo_button.xml
index ca7508dd9..21f8e8e6c 100644
--- a/libs/emojicon/res/drawable/orca_composer_attach_photo_button.xml
+++ b/libs/emojicon/res/drawable/orca_composer_attach_photo_button.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<selector
- xmlns:android="http://schemas.android.com/apk/res/android">
+<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/res/drawable/orca_composer_popup_button.xml b/libs/emojicon/res/drawable/orca_composer_popup_button.xml
index d43dc26e4..dccfb4848 100644
--- a/libs/emojicon/res/drawable/orca_composer_popup_button.xml
+++ b/libs/emojicon/res/drawable/orca_composer_popup_button.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<selector
- xmlns:android="http://schemas.android.com/apk/res/android">
+<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/res/drawable/orca_composer_popup_button_active.xml b/libs/emojicon/res/drawable/orca_composer_popup_button_active.xml
index f5e40ef8c..556d0151b 100644
--- a/libs/emojicon/res/drawable/orca_composer_popup_button_active.xml
+++ b/libs/emojicon/res/drawable/orca_composer_popup_button_active.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<selector
- xmlns:android="http://schemas.android.com/apk/res/android">
+<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/res/drawable/orca_emoji_backspace_front_button.xml b/libs/emojicon/res/drawable/orca_emoji_backspace_front_button.xml
index a2d2f5b62..e922eb686 100644
--- a/libs/emojicon/res/drawable/orca_emoji_backspace_front_button.xml
+++ b/libs/emojicon/res/drawable/orca_emoji_backspace_front_button.xml
@@ -1,5 +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"/>
+ <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/res/drawable/orca_emoji_more_front_button.xml b/libs/emojicon/res/drawable/orca_emoji_more_front_button.xml
index a799d56d9..3e0e9f6f0 100644
--- a/libs/emojicon/res/drawable/orca_emoji_more_front_button.xml
+++ b/libs/emojicon/res/drawable/orca_emoji_more_front_button.xml
@@ -1,5 +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"/>
+ <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/res/drawable/orca_emoji_tab_background.xml b/libs/emojicon/res/drawable/orca_emoji_tab_background.xml
index f68e6212b..d597dc03a 100644
--- a/libs/emojicon/res/drawable/orca_emoji_tab_background.xml
+++ b/libs/emojicon/res/drawable/orca_emoji_tab_background.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<selector
- xmlns:android="http://schemas.android.com/apk/res/android">
+<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" />
diff --git a/libs/emojicon/res/drawable/orca_emoji_tab_dark_background.xml b/libs/emojicon/res/drawable/orca_emoji_tab_dark_background.xml
index 07ff608c9..f0b0d1a66 100644
--- a/libs/emojicon/res/drawable/orca_emoji_tab_dark_background.xml
+++ b/libs/emojicon/res/drawable/orca_emoji_tab_dark_background.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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
@@ -14,8 +13,7 @@
~ ITS DERIVATIVES.
-->
-<selector
- xmlns:android="http://schemas.android.com/apk/res/android">
+<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" />
diff --git a/libs/emojicon/res/layout/emojicon_grid.xml b/libs/emojicon/res/layout/emojicon_grid.xml
index 457f8756b..7e1579c3f 100644
--- a/libs/emojicon/res/layout/emojicon_grid.xml
+++ b/libs/emojicon/res/layout/emojicon_grid.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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
diff --git a/libs/emojicon/res/layout/emojicon_item.xml b/libs/emojicon/res/layout/emojicon_item.xml
index ed11041a7..a85b1b640 100644
--- a/libs/emojicon/res/layout/emojicon_item.xml
+++ b/libs/emojicon/res/layout/emojicon_item.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2014 Ankush Sachdeva
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,18 +14,18 @@
~ 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">
+<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"/>
+ 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/res/layout/emojicons.xml b/libs/emojicon/res/layout/emojicons.xml
index 287923d1b..e61c2fb34 100644
--- a/libs/emojicon/res/layout/emojicons.xml
+++ b/libs/emojicon/res/layout/emojicons.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2014 Ankush Sachdeva
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,104 +15,120 @@
-->
<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">
+ 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">
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ android:layout_alignParentBottom="true" />
+
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_below="@id/emojis_tab"
- android:background="#8f8f8f"/>
+ android:background="#8f8f8f" />
</RelativeLayout>
diff --git a/libs/emojicon/res/values/attrs.xml b/libs/emojicon/res/values/attrs.xml
index 9810158dc..f4f21f903 100644
--- a/libs/emojicon/res/values/attrs.xml
+++ b/libs/emojicon/res/values/attrs.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2014 Ankush Sachdeva
~
~ Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiAdapter.java b/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiAdapter.java
index 347870c9d..18842ea0d 100644
--- a/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiAdapter.java
+++ b/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiAdapter.java
@@ -33,7 +33,8 @@ import github.ankushsachdeva.emojicon.emoji.Emojicon;
* @author Ankush Sachdeva (sankush@yahoo.co.in)
*/
class EmojiAdapter extends ArrayAdapter<Emojicon> {
- OnEmojiconClickedListener emojiClickListener;
+ OnEmojiconClickedListener emojiClickListener;
+
public EmojiAdapter(Context context, List<Emojicon> data) {
super(context, R.layout.emojicon_item, data);
}
@@ -41,11 +42,11 @@ class EmojiAdapter extends ArrayAdapter<Emojicon> {
public EmojiAdapter(Context context, Emojicon[] data) {
super(context, R.layout.emojicon_item, data);
}
-
- public void setEmojiClickListener(OnEmojiconClickedListener listener){
- this.emojiClickListener = listener;
+
+ public void setEmojiClickListener(OnEmojiconClickedListener listener) {
+ this.emojiClickListener = listener;
}
-
+
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View v = convertView;
@@ -59,11 +60,11 @@ class EmojiAdapter extends ArrayAdapter<Emojicon> {
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));
- }
- });
+ @Override
+ public void onClick(View v) {
+ emojiClickListener.onEmojiconClicked(getItem(position));
+ }
+ });
return v;
}
diff --git a/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconGridView.java b/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconGridView.java
index 10379129c..72087e6e6 100644
--- a/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconGridView.java
+++ b/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconGridView.java
@@ -30,48 +30,48 @@ import github.ankushsachdeva.emojicon.emoji.People;
/**
* @author Hieu Rocker (rockerhieu@gmail.com)
- * @author Ankush Sachdeva (sankush@yahoo.co.in)
+ * @author Ankush Sachdeva (sankush@yahoo.co.in)
*/
-public class EmojiconGridView{
- public View rootView;
- EmojiconsPopup mEmojiconPopup;
+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) {
+ 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/github/ankushsachdeva/emojicon/EmojiconHandler.java b/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconHandler.java
index bc7a228c0..84f087d86 100644
--- a/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconHandler.java
+++ b/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconHandler.java
@@ -1396,7 +1396,7 @@ public final class EmojiconHandler {
public static void addEmojis(Context context, Spannable text, int emojiSize, int index, int length) {
int textLength = text.length();
int textLengthToProcessMax = textLength - index;
- int textLengthToProcess = length < 0 || length >= textLengthToProcessMax ? textLength : (length+index);
+ int textLengthToProcess = length < 0 || length >= textLengthToProcessMax ? textLength : (length + index);
// remove spans throughout all text
EmojiconSpan[] oldSpans = text.getSpans(0, textLength, EmojiconSpan.class);
diff --git a/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconRecents.java b/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconRecents.java
index c7a90c837..e3274ed77 100644
--- a/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconRecents.java
+++ b/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconRecents.java
@@ -21,8 +21,8 @@ import android.content.Context;
import github.ankushsachdeva.emojicon.emoji.Emojicon;
/**
-* @author Daniele Ricci
-*/
+ * @author Daniele Ricci
+ */
public interface EmojiconRecents {
public void addRecentEmoji(Context context, Emojicon emojicon);
}
diff --git a/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconRecentsGridView.java b/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconRecentsGridView.java
index ea98c809d..ff65ac06d 100644
--- a/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconRecentsGridView.java
+++ b/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconRecentsGridView.java
@@ -24,26 +24,26 @@ import github.ankushsachdeva.emojicon.emoji.Emojicon;
/**
* @author Daniele Ricci
- * @author Ankush Sachdeva (sankush@yahoo.co.in)
+ * @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);
- }
- }
- });
+ 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);
}
@@ -51,7 +51,7 @@ public class EmojiconRecentsGridView extends EmojiconGridView implements Emojico
@Override
public void addRecentEmoji(Context context, Emojicon emojicon) {
EmojiconRecentsManager recents = EmojiconRecentsManager
- .getInstance(context);
+ .getInstance(context);
recents.push(emojicon);
// notify dataset changed
diff --git a/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java b/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java
index 177f11591..f208b10c4 100644
--- a/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java
+++ b/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java
@@ -26,8 +26,8 @@ import github.ankushsachdeva.emojicon.emoji.Emojicon;
/**
-* @author Daniele Ricci
-*/
+ * @author Daniele Ricci
+ */
public class EmojiconRecentsManager extends ArrayList<Emojicon> {
private static final String PREFERENCE_NAME = "emojicon";
@@ -88,7 +88,7 @@ public class EmojiconRecentsManager extends ArrayList<Emojicon> {
boolean ret = super.remove(object);
return ret;
}
-
+
private SharedPreferences getPreferences() {
return mContext.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
}
@@ -99,14 +99,13 @@ public class EmojiconRecentsManager extends ArrayList<Emojicon> {
StringTokenizer tokenizer = new StringTokenizer(str, "~");
while (tokenizer.hasMoreTokens()) {
try {
- add(new Emojicon(tokenizer.nextToken()));
- }
- catch (NumberFormatException e) {
+ add(new Emojicon(tokenizer.nextToken()));
+ } catch (NumberFormatException e) {
// ignored
}
}
}
-
+
public void saveRecents() {
StringBuilder str = new StringBuilder();
int c = size();
diff --git a/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconsPopup.java b/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconsPopup.java
index 2a35aa9c0..f7d68ddc5 100644
--- a/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconsPopup.java
+++ b/libs/emojicon/src/github/ankushsachdeva/emojicon/EmojiconsPopup.java
@@ -53,372 +53,376 @@ import github.ankushsachdeva.emojicon.emoji.Symbols;
*/
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();
- }
-
- /**
- * 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 = getUsableScreenHeight();
- int heightDifference = screenHeight
- - (r.bottom - r.top);
- int resourceId = mContext.getResources()
- .getIdentifier("status_bar_height",
- "dimen", "android");
- if (resourceId > 0) {
- heightDifference -= mContext.getResources()
- .getDimensionPixelSize(resourceId);
- }
- 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();
- }
- }
- });
- }
-
- private int getUsableScreenHeight() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- DisplayMetrics metrics = new DisplayMetrics();
-
- WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- windowManager.getDefaultDisplay().getMetrics(metrics);
-
- return metrics.heightPixels;
-
- } else {
- return rootView.getRootView().getHeight();
- }
- }
-
- /**
- * 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();
- }
+ 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();
+ }
+
+ /**
+ * 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 = getUsableScreenHeight();
+ int heightDifference = screenHeight
+ - (r.bottom - r.top);
+ int resourceId = mContext.getResources()
+ .getIdentifier("status_bar_height",
+ "dimen", "android");
+ if (resourceId > 0) {
+ heightDifference -= mContext.getResources()
+ .getDimensionPixelSize(resourceId);
+ }
+ 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();
+ }
+ }
+ });
+ }
+
+ private int getUsableScreenHeight() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ DisplayMetrics metrics = new DisplayMetrics();
+
+ WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ windowManager.getDefaultDisplay().getMetrics(metrics);
+
+ return metrics.heightPixels;
+
+ } else {
+ return rootView.getRootView().getHeight();
+ }
+ }
+
+ /**
+ * 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/AndroidManifest.xml b/libs/emojicon/src/main/AndroidManifest.xml
index a07b8e21b..135e6fec5 100644
--- a/libs/emojicon/src/main/AndroidManifest.xml
+++ b/libs/emojicon/src/main/AndroidManifest.xml
@@ -1,9 +1,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="github.ankushsachdeva.emojicon">
- <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19"/>
+ <uses-sdk
+ android:minSdkVersion="8"
+ android:targetSdkVersion="19" />
- <application android:allowBackup="true"
+ <application
+ android:allowBackup="true"
android:label="@string/app_name"
android:icon="@drawable/ic_launcher">
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiAdapter.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiAdapter.java
index e33d8d6b9..c6efb45a3 100644
--- a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiAdapter.java
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiAdapter.java
@@ -32,7 +32,8 @@ import github.ankushsachdeva.emojicon.emoji.Emojicon;
* @author Ankush Sachdeva (sankush@yahoo.co.in)
*/
class EmojiAdapter extends ArrayAdapter<Emojicon> {
- OnEmojiconClickedListener emojiClickListener;
+ OnEmojiconClickedListener emojiClickListener;
+
public EmojiAdapter(Context context, List<Emojicon> data) {
super(context, R.layout.emojicon_item, data);
}
@@ -40,11 +41,11 @@ class EmojiAdapter extends ArrayAdapter<Emojicon> {
public EmojiAdapter(Context context, Emojicon[] data) {
super(context, R.layout.emojicon_item, data);
}
-
- public void setEmojiClickListener(OnEmojiconClickedListener listener){
- this.emojiClickListener = listener;
+
+ public void setEmojiClickListener(OnEmojiconClickedListener listener) {
+ this.emojiClickListener = listener;
}
-
+
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View v = convertView;
@@ -58,11 +59,11 @@ class EmojiAdapter extends ArrayAdapter<Emojicon> {
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));
- }
- });
+ @Override
+ public void onClick(View v) {
+ emojiClickListener.onEmojiconClicked(getItem(position));
+ }
+ });
return v;
}
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconGridView.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconGridView.java
index 1f2c6bb46..1feda8ac6 100644
--- a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconGridView.java
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconGridView.java
@@ -29,48 +29,48 @@ import github.ankushsachdeva.emojicon.emoji.People;
/**
* @author Hieu Rocker (rockerhieu@gmail.com)
- * @author Ankush Sachdeva (sankush@yahoo.co.in)
+ * @author Ankush Sachdeva (sankush@yahoo.co.in)
*/
-public class EmojiconGridView{
- public View rootView;
- EmojiconsPopup mEmojiconPopup;
+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) {
+ 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
index 8fac5385b..b9c214bad 100644
--- a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconHandler.java
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconHandler.java
@@ -1394,7 +1394,7 @@ public final class EmojiconHandler {
public static void addEmojis(Context context, Spannable text, int emojiSize, int index, int length) {
int textLength = text.length();
int textLengthToProcessMax = textLength - index;
- int textLengthToProcess = length < 0 || length >= textLengthToProcessMax ? textLength : (length+index);
+ int textLengthToProcess = length < 0 || length >= textLengthToProcessMax ? textLength : (length + index);
// remove spans throughout all text
EmojiconSpan[] oldSpans = text.getSpans(0, textLength, EmojiconSpan.class);
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecents.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecents.java
index c7a90c837..e3274ed77 100644
--- a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecents.java
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecents.java
@@ -21,8 +21,8 @@ import android.content.Context;
import github.ankushsachdeva.emojicon.emoji.Emojicon;
/**
-* @author Daniele Ricci
-*/
+ * @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
index d4f2211e7..c07ba082d 100644
--- a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsGridView.java
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsGridView.java
@@ -23,26 +23,26 @@ import github.ankushsachdeva.emojicon.emoji.Emojicon;
/**
* @author Daniele Ricci
- * @author Ankush Sachdeva (sankush@yahoo.co.in)
+ * @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);
- }
- }
- });
+ 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);
}
@@ -50,7 +50,7 @@ public class EmojiconRecentsGridView extends EmojiconGridView implements Emojico
@Override
public void addRecentEmoji(Context context, Emojicon emojicon) {
EmojiconRecentsManager recents = EmojiconRecentsManager
- .getInstance(context);
+ .getInstance(context);
recents.push(emojicon);
// notify dataset changed
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java
index 177f11591..f208b10c4 100644
--- a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java
@@ -26,8 +26,8 @@ import github.ankushsachdeva.emojicon.emoji.Emojicon;
/**
-* @author Daniele Ricci
-*/
+ * @author Daniele Ricci
+ */
public class EmojiconRecentsManager extends ArrayList<Emojicon> {
private static final String PREFERENCE_NAME = "emojicon";
@@ -88,7 +88,7 @@ public class EmojiconRecentsManager extends ArrayList<Emojicon> {
boolean ret = super.remove(object);
return ret;
}
-
+
private SharedPreferences getPreferences() {
return mContext.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
}
@@ -99,14 +99,13 @@ public class EmojiconRecentsManager extends ArrayList<Emojicon> {
StringTokenizer tokenizer = new StringTokenizer(str, "~");
while (tokenizer.hasMoreTokens()) {
try {
- add(new Emojicon(tokenizer.nextToken()));
- }
- catch (NumberFormatException e) {
+ add(new Emojicon(tokenizer.nextToken()));
+ } catch (NumberFormatException e) {
// ignored
}
}
}
-
+
public void saveRecents() {
StringBuilder str = new StringBuilder();
int c = size();
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconsPopup.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconsPopup.java
index 2a35aa9c0..f7d68ddc5 100644
--- a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconsPopup.java
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconsPopup.java
@@ -53,372 +53,376 @@ import github.ankushsachdeva.emojicon.emoji.Symbols;
*/
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();
- }
-
- /**
- * 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 = getUsableScreenHeight();
- int heightDifference = screenHeight
- - (r.bottom - r.top);
- int resourceId = mContext.getResources()
- .getIdentifier("status_bar_height",
- "dimen", "android");
- if (resourceId > 0) {
- heightDifference -= mContext.getResources()
- .getDimensionPixelSize(resourceId);
- }
- 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();
- }
- }
- });
- }
-
- private int getUsableScreenHeight() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- DisplayMetrics metrics = new DisplayMetrics();
-
- WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- windowManager.getDefaultDisplay().getMetrics(metrics);
-
- return metrics.heightPixels;
-
- } else {
- return rootView.getRootView().getHeight();
- }
- }
-
- /**
- * 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();
- }
+ 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();
+ }
+
+ /**
+ * 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 = getUsableScreenHeight();
+ int heightDifference = screenHeight
+ - (r.bottom - r.top);
+ int resourceId = mContext.getResources()
+ .getIdentifier("status_bar_height",
+ "dimen", "android");
+ if (resourceId > 0) {
+ heightDifference -= mContext.getResources()
+ .getDimensionPixelSize(resourceId);
+ }
+ 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();
+ }
+ }
+ });
+ }
+
+ private int getUsableScreenHeight() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ DisplayMetrics metrics = new DisplayMetrics();
+
+ WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ windowManager.getDefaultDisplay().getMetrics(metrics);
+
+ return metrics.heightPixels;
+
+ } else {
+ return rootView.getRootView().getHeight();
+ }
+ }
+
+ /**
+ * 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/res/drawable/ic_emoji_nature_light.xml b/libs/emojicon/src/main/res/drawable/ic_emoji_nature_light.xml
index 543409e03..87464fb1e 100644
--- a/libs/emojicon/src/main/res/drawable/ic_emoji_nature_light.xml
+++ b/libs/emojicon/src/main/res/drawable/ic_emoji_nature_light.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
/*
**
** Copyright 2013, The Android Open Source Project
@@ -19,15 +18,8 @@
-->
<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" />
+ <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
index 4096e695b..74c80e3d0 100644
--- a/libs/emojicon/src/main/res/drawable/ic_emoji_objects_light.xml
+++ b/libs/emojicon/src/main/res/drawable/ic_emoji_objects_light.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
/*
**
** Copyright 2013, The Android Open Source Project
@@ -19,14 +18,8 @@
-->
<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: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
index ea9e406a4..06f7aebbf 100644
--- a/libs/emojicon/src/main/res/drawable/ic_emoji_people_light.xml
+++ b/libs/emojicon/src/main/res/drawable/ic_emoji_people_light.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
/*
**
** Copyright 2013, The Android Open Source Project
@@ -19,14 +18,8 @@
-->
<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: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
index 312cad9c3..36f258876 100644
--- a/libs/emojicon/src/main/res/drawable/ic_emoji_places_light.xml
+++ b/libs/emojicon/src/main/res/drawable/ic_emoji_places_light.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
/*
**
** Copyright 2013, The Android Open Source Project
@@ -19,14 +18,8 @@
-->
<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: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
index 8c2123f83..90f2c8565 100644
--- a/libs/emojicon/src/main/res/drawable/ic_emoji_recent_light.xml
+++ b/libs/emojicon/src/main/res/drawable/ic_emoji_recent_light.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
/*
**
** Copyright 2013, The Android Open Source Project
@@ -19,14 +18,8 @@
-->
<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: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
index 79aaf0fc5..9077c3289 100644
--- a/libs/emojicon/src/main/res/drawable/ic_emoji_symbols_light.xml
+++ b/libs/emojicon/src/main/res/drawable/ic_emoji_symbols_light.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
/*
**
** Copyright 2013, The Android Open Source Project
@@ -19,14 +18,8 @@
-->
<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: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
index 0b255fb27..56e8d0484 100644
--- 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
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<selector
- xmlns:android="http://schemas.android.com/apk/res/android">
+<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
index 4971113aa..be898ebcf 100644
--- 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
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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
@@ -14,8 +13,7 @@
~ 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 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
index ca7508dd9..21f8e8e6c 100644
--- 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
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<selector
- xmlns:android="http://schemas.android.com/apk/res/android">
+<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
index d43dc26e4..dccfb4848 100644
--- a/libs/emojicon/src/main/res/drawable/orca_composer_popup_button.xml
+++ b/libs/emojicon/src/main/res/drawable/orca_composer_popup_button.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<selector
- xmlns:android="http://schemas.android.com/apk/res/android">
+<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
index f5e40ef8c..556d0151b 100644
--- 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
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<selector
- xmlns:android="http://schemas.android.com/apk/res/android">
+<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
index a2d2f5b62..e922eb686 100644
--- 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
@@ -1,5 +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"/>
+ <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
index a799d56d9..3e0e9f6f0 100644
--- 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
@@ -1,5 +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"/>
+ <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
index f68e6212b..d597dc03a 100644
--- a/libs/emojicon/src/main/res/drawable/orca_emoji_tab_background.xml
+++ b/libs/emojicon/src/main/res/drawable/orca_emoji_tab_background.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<selector
- xmlns:android="http://schemas.android.com/apk/res/android">
+<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" />
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
index 07ff608c9..f0b0d1a66 100644
--- 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
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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
@@ -14,8 +13,7 @@
~ ITS DERIVATIVES.
-->
-<selector
- xmlns:android="http://schemas.android.com/apk/res/android">
+<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" />
diff --git a/libs/emojicon/src/main/res/layout/emojicon_grid.xml b/libs/emojicon/src/main/res/layout/emojicon_grid.xml
index 457f8756b..7e1579c3f 100644
--- a/libs/emojicon/src/main/res/layout/emojicon_grid.xml
+++ b/libs/emojicon/src/main/res/layout/emojicon_grid.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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
diff --git a/libs/emojicon/src/main/res/layout/emojicon_item.xml b/libs/emojicon/src/main/res/layout/emojicon_item.xml
index ed11041a7..a85b1b640 100644
--- a/libs/emojicon/src/main/res/layout/emojicon_item.xml
+++ b/libs/emojicon/src/main/res/layout/emojicon_item.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2014 Ankush Sachdeva
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,18 +14,18 @@
~ 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">
+<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"/>
+ 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
index 287923d1b..e61c2fb34 100644
--- a/libs/emojicon/src/main/res/layout/emojicons.xml
+++ b/libs/emojicon/src/main/res/layout/emojicons.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2014 Ankush Sachdeva
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,104 +15,120 @@
-->
<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">
+ 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">
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ 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"/>
+ android:layout_alignParentBottom="true" />
+
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_below="@id/emojis_tab"
- android:background="#8f8f8f"/>
+ android:background="#8f8f8f" />
</RelativeLayout>
diff --git a/libs/emojicon/src/main/res/values/attrs.xml b/libs/emojicon/src/main/res/values/attrs.xml
index 9810158dc..f4f21f903 100644
--- a/libs/emojicon/src/main/res/values/attrs.xml
+++ b/libs/emojicon/src/main/res/values/attrs.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2014 Ankush Sachdeva
~
~ Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/src/free/java/de/pixart/messenger/services/PushManagementService.java b/src/free/java/de/pixart/messenger/services/PushManagementService.java
index 17e49cbbb..b76f5b4de 100644
--- a/src/free/java/de/pixart/messenger/services/PushManagementService.java
+++ b/src/free/java/de/pixart/messenger/services/PushManagementService.java
@@ -4,21 +4,21 @@ import de.pixart.messenger.entities.Account;
public class PushManagementService {
- protected final XmppConnectionService mXmppConnectionService;
+ protected final XmppConnectionService mXmppConnectionService;
- public PushManagementService(XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
+ public PushManagementService(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
- public void registerPushTokenOnServer(Account account) {
- //stub implementation. only affects playstore flavor
- }
+ public void registerPushTokenOnServer(Account account) {
+ //stub implementation. only affects playstore flavor
+ }
- public boolean available(Account account) {
- return false;
- }
+ public boolean available(Account account) {
+ return false;
+ }
- public boolean isStub() {
- return true;
- }
+ public boolean isStub() {
+ return true;
+ }
}
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 7e79e474a..d1f0b6e23 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -1,27 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="de.pixart.messenger">
+ xmlns:tools="http://schemas.android.com/tools"
+ package="de.pixart.messenger">
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.READ_CONTACTS"/>
- <uses-permission android:name="android.permission.READ_PROFILE"/>
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
- <uses-permission android:name="android.permission.WAKE_LOCK"/>
- <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
- <uses-permission android:name="android.permission.VIBRATE"/>
- <uses-permission android:name="android.permission.NFC"/>
- <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
- <uses-permission android:name="android.permission.RECORD_AUDIO"/>
- <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
- <uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+ <uses-permission android:name="android.permission.READ_PROFILE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <uses-permission android:name="android.permission.VIBRATE" />
+ <uses-permission android:name="android.permission.NFC" />
+ <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission
android:name="android.permission.READ_PHONE_STATE"
- tools:node="remove"/>
+ tools:node="remove" />
<application
android:name="android.support.multidex.MultiDexApplication"
@@ -31,26 +31,24 @@
android:largeHeap="false"
android:theme="@style/ConversationsTheme"
tools:replace="android:label">
- <service android:name="de.pixart.messenger.services.XmppConnectionService"/>
+ <service android:name="de.pixart.messenger.services.XmppConnectionService" />
<receiver android:name="de.pixart.messenger.services.EventReceiver">
<intent-filter>
- <action android:name="android.intent.action.BOOT_COMPLETED"/>
- <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
- <action android:name="android.intent.action.ACTION_SHUTDOWN"/>
- <action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
- <action android:name="android.media.RINGER_MODE_CHANGED"/>
+ <action android:name="android.intent.action.BOOT_COMPLETED" />
+ <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
+ <action android:name="android.intent.action.ACTION_SHUTDOWN" />
+ <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
+ <action android:name="android.media.RINGER_MODE_CHANGED" />
</intent-filter>
<intent-filter>
- <action android:name="android.intent.action.PACKAGE_REPLACED"/>
+ <action android:name="android.intent.action.PACKAGE_REPLACED" />
<data
android:path="de.pixart.messenger"
- android:scheme="package"/>
+ android:scheme="package" />
</intent-filter>
</receiver>
- <receiver
- android:name="de.pixart.messenger.services.AlarmReceiver">
- </receiver>
+ <receiver android:name="de.pixart.messenger.services.AlarmReceiver"></receiver>
<activity
android:name="de.pixart.messenger.ui.StartUI"
@@ -60,8 +58,8 @@
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
android:windowSoftInputMode="stateHidden">
<intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
@@ -71,117 +69,116 @@
android:hardwareAccelerated="true"
android:minWidth="300dp"
android:minHeight="300dp"
- android:windowSoftInputMode="stateHidden">
- </activity>
+ android:windowSoftInputMode="stateHidden"></activity>
<activity
android:name="de.pixart.messenger.ui.StartConversationActivity"
android:configChanges="orientation|screenSize"
android:label="@string/title_activity_start_conversation"
android:launchMode="singleTask">
<intent-filter>
- <action android:name="android.intent.action.SENDTO"/>
- <category android:name="android.intent.category.DEFAULT"/>
+ <action android:name="android.intent.action.SENDTO" />
+ <category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="imto"/>
- <data android:host="jabber"/>
+ <data android:scheme="imto" />
+ <data android:host="jabber" />
</intent-filter>
<intent-filter>
- <action android:name="android.intent.action.VIEW"/>
+ <action android:name="android.intent.action.VIEW" />
- <category android:name="android.intent.category.DEFAULT"/>
- <category android:name="android.intent.category.BROWSABLE"/>
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
- <data android:scheme="xmpp"/>
+ <data android:scheme="xmpp" />
</intent-filter>
<intent-filter>
- <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
- <category android:name="android.intent.category.DEFAULT"/>
- <data android:scheme="xmpp"/>
+ <action android:name="android.nfc.action.NDEF_DISCOVERED" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="xmpp" />
</intent-filter>
<intent-filter android:autoVerify="true">
- <action android:name="android.intent.action.VIEW"/>
+ <action android:name="android.intent.action.VIEW" />
- <category android:name="android.intent.category.DEFAULT"/>
- <category android:name="android.intent.category.BROWSABLE"/>
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
- <data android:scheme="https"/>
- <data android:host="jabber.pix-art.de"/>
- <data android:pathPrefix="/i/"/>
- <data android:pathPrefix="/j/"/>
+ <data android:scheme="https" />
+ <data android:host="jabber.pix-art.de" />
+ <data android:pathPrefix="/i/" />
+ <data android:pathPrefix="/j/" />
</intent-filter>
</activity>
<activity
android:name="de.pixart.messenger.ui.WelcomeActivity"
android:label="@string/app_name"
- android:launchMode="singleTask"/>
+ android:launchMode="singleTask" />
<activity
android:name="de.pixart.messenger.ui.MagicCreateActivity"
android:label="@string/create_account"
- android:launchMode="singleTask"/>
+ android:launchMode="singleTask" />
<activity
android:name="de.pixart.messenger.ui.SetPresenceActivity"
android:configChanges="orientation|screenSize"
android:label="@string/change_presence"
android:launchMode="singleTask"
- android:windowSoftInputMode="stateHidden|adjustResize"/>
+ android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name="de.pixart.messenger.ui.SettingsActivity"
- android:label="@string/title_activity_settings"/>
+ android:label="@string/title_activity_settings" />
<activity
android:name="de.pixart.messenger.ui.ChooseContactActivity"
- android:label="@string/title_activity_choose_contact"/>
+ android:label="@string/title_activity_choose_contact" />
<activity
android:name="de.pixart.messenger.ui.BlocklistActivity"
- android:label="@string/title_activity_block_list"/>
+ android:label="@string/title_activity_block_list" />
<activity
android:name="de.pixart.messenger.ui.ChangePasswordActivity"
- android:label="@string/change_password_on_server"/>
+ android:label="@string/change_password_on_server" />
<activity
android:name="de.pixart.messenger.ui.ManageAccountActivity"
android:label="@string/title_activity_manage_accounts"
- android:launchMode="singleTask"/>
+ android:launchMode="singleTask" />
<activity
android:name="de.pixart.messenger.ui.EditAccountActivity"
android:launchMode="singleTask"
- android:windowSoftInputMode="stateHidden|adjustResize"/>
+ android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name="de.pixart.messenger.ui.ConferenceDetailsActivity"
android:label="@string/title_activity_conference_details"
- android:windowSoftInputMode="stateHidden"/>
+ android:windowSoftInputMode="stateHidden" />
<activity
android:name="de.pixart.messenger.ui.ContactDetailsActivity"
android:label="@string/title_activity_contact_details"
- android:windowSoftInputMode="stateHidden"/>
+ android:windowSoftInputMode="stateHidden" />
<activity
android:name="de.pixart.messenger.ui.PublishProfilePictureActivity"
android:label="@string/mgmt_account_publish_avatar"
- android:windowSoftInputMode="stateHidden"/>
+ android:windowSoftInputMode="stateHidden" />
<activity
android:name="de.pixart.messenger.ui.VerifyOTRActivity"
android:label="@string/verify_otr"
- android:windowSoftInputMode="stateHidden"/>
+ android:windowSoftInputMode="stateHidden" />
<activity
android:name="de.pixart.messenger.ui.ShareWithActivity"
android:label="@string/app_name">
<intent-filter>
- <action android:name="android.intent.action.SEND"/>
- <category android:name="android.intent.category.DEFAULT"/>
- <data android:mimeType="text/plain"/>
+ <action android:name="android.intent.action.SEND" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
- <action android:name="android.intent.action.SEND"/>
- <category android:name="android.intent.category.DEFAULT"/>
- <data android:mimeType="*/*"/>
+ <action android:name="android.intent.action.SEND" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="*/*" />
</intent-filter>
<intent-filter>
- <action android:name="android.intent.action.SEND_MULTIPLE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- <data android:mimeType="image/*"/>
+ <action android:name="android.intent.action.SEND_MULTIPLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="image/*" />
</intent-filter>
<meta-data
android:name="android.service.chooser.chooser_target_service"
- android:value=".services.ContactChooserTargetService"/>
+ android:value=".services.ContactChooserTargetService" />
</activity>
<activity
android:name="de.pixart.messenger.ui.RecordingActivity"
@@ -189,17 +186,17 @@
android:label="@string/app_name"
android:theme="@style/ConversationsDialog">
<intent-filter>
- <action android:name="android.provider.MediaStore.RECORD_SOUND"/>
- <category android:name="android.intent.category.DEFAULT"/>
+ <action android:name="android.provider.MediaStore.RECORD_SOUND" />
+ <category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<meta-data
android:name="com.google.android.gms.version"
- android:value="@integer/google_play_services_version"/>
+ android:value="@integer/google_play_services_version" />
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
- android:value="@string/google_maps_api_key"/>
+ android:value="@string/google_maps_api_key" />
<activity
android:name="de.pixart.messenger.ui.ShareLocationActivity"
@@ -207,8 +204,8 @@
android:parentActivityName="de.pixart.messenger.ui.ConversationActivity"
android:hardwareAccelerated="true">
<intent-filter>
- <action android:name="de.pixart.messenger.location.request"/>
- <category android:name="android.intent.category.DEFAULT"/>
+ <action android:name="de.pixart.messenger.location.request" />
+ <category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
@@ -216,52 +213,49 @@
android:label="@string/show_location"
android:hardwareAccelerated="true">
<intent-filter>
- <action android:name="de.pixart.messenger.location.show"/>
- <category android:name="android.intent.category.DEFAULT"/>
+ <action android:name="de.pixart.messenger.location.show" />
+ <category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name="de.pixart.messenger.ui.ShowFullscreenMessageActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:configChanges="orientation|screenSize"
- android:hardwareAccelerated="true">
- </activity>
+ android:hardwareAccelerated="true"></activity>
<activity
android:name="de.pixart.messenger.ui.TrustKeysActivity"
android:label="@string/trust_omemo_fingerprints"
- android:windowSoftInputMode="stateAlwaysHidden"/>
+ android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name="de.duenndns.ssl.MemorizingActivity"
android:theme="@style/ConversationsTheme"
- tools:replace="android:theme"/>
+ tools:replace="android:theme" />
<activity
android:name="de.pixart.messenger.ui.AboutActivity"
android:label="@string/title_activity_about"
android:parentActivityName="de.pixart.messenger.ui.SettingsActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value="de.pixart.messenger.ui.SettingsActivity"/>
+ android:value="de.pixart.messenger.ui.SettingsActivity" />
</activity>
<activity
android:name="de.pixart.messenger.ui.UpdaterActivity"
android:configChanges="orientation|screenSize"
android:label="@string/title_activity_updater"
android:launchMode="singleTask"
- android:theme="@style/ConversationsTheme">
- </activity>
+ android:theme="@style/ConversationsTheme"></activity>
<activity
android:name="com.soundcloud.android.crop.CropImageActivity"
- android:hardwareAccelerated="true">
- </activity>
+ android:hardwareAccelerated="true"></activity>
- <service android:name="de.pixart.messenger.services.UpdaterWebService"/>
- <service android:name="de.pixart.messenger.services.CheckAppVersionService"/>
- <service android:name="de.pixart.messenger.services.ExportLogsService"/>
+ <service android:name="de.pixart.messenger.services.UpdaterWebService" />
+ <service android:name="de.pixart.messenger.services.CheckAppVersionService" />
+ <service android:name="de.pixart.messenger.services.ExportLogsService" />
<service
android:name="de.pixart.messenger.services.ContactChooserTargetService"
android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
<intent-filter>
- <action android:name="android.service.chooser.ChooserTargetService"/>
+ <action android:name="android.service.chooser.ChooserTargetService" />
</intent-filter>
</service>
@@ -272,7 +266,7 @@
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
- android:resource="@xml/file_paths"/>
+ android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>
diff --git a/src/main/java/de/pixart/messenger/Config.java b/src/main/java/de/pixart/messenger/Config.java
index 61422938c..f44ab661b 100644
--- a/src/main/java/de/pixart/messenger/Config.java
+++ b/src/main/java/de/pixart/messenger/Config.java
@@ -116,7 +116,7 @@ public final class Config {
public static final boolean PARSE_REAL_JID_FROM_MUC_MAM = false; //dangerous if server doesn’t filter
public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
- public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2;
+ public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2;
public static final int MAM_MAX_MESSAGES = 500;
public static final long FREQUENT_RESTARTS_DETECTION_WINDOW = 8 * 60 * 60 * 1000; // 8 hours
diff --git a/src/main/java/de/pixart/messenger/OmemoActivity.java b/src/main/java/de/pixart/messenger/OmemoActivity.java
index f60b064cf..dcf420442 100644
--- a/src/main/java/de/pixart/messenger/OmemoActivity.java
+++ b/src/main/java/de/pixart/messenger/OmemoActivity.java
@@ -28,7 +28,7 @@ public abstract class OmemoActivity extends XmppActivity {
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu,v,menuInfo);
+ super.onCreateContextMenu(menu, v, menuInfo);
Object account = v.getTag(R.id.TAG_ACCOUNT);
Object fingerprint = v.getTag(R.id.TAG_FINGERPRINT);
if (account != null && fingerprint != null && account instanceof Account && fingerprint instanceof String) {
@@ -42,7 +42,7 @@ public abstract class OmemoActivity extends XmppActivity {
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.purge_omemo_key:
- showPurgeKeyDialog(mSelectedAccount,mSelectedFingerprint);
+ showPurgeKeyDialog(mSelectedAccount, mSelectedFingerprint);
break;
case R.id.copy_omemo_key:
copyOmemoFingerprint(mSelectedFingerprint);
@@ -88,7 +88,7 @@ public abstract class OmemoActivity extends XmppActivity {
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
- showX509Certificate(account,fingerprint);
+ showX509Certificate(account, fingerprint);
}
};
key.setOnClickListener(listener);
@@ -98,13 +98,13 @@ public abstract class OmemoActivity extends XmppActivity {
ImageView verifiedFingerprintSymbol = (ImageView) view.findViewById(R.id.verified_fingerprint);
trustToggle.setVisibility(View.VISIBLE);
registerForContextMenu(view);
- view.setTag(R.id.TAG_ACCOUNT,account);
- view.setTag(R.id.TAG_FINGERPRINT,fingerprint);
+ view.setTag(R.id.TAG_ACCOUNT, account);
+ view.setTag(R.id.TAG_FINGERPRINT, fingerprint);
boolean x509 = Config.X509_VERIFICATION && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509;
final View.OnClickListener toast;
trustToggle.setChecked(status.isTrusted(), false);
- if (status.isActive()){
+ if (status.isActive()) {
key.setTextColor(getPrimaryTextColor());
keyType.setTextColor(getSecondaryTextColor());
if (status.isVerified()) {
@@ -126,7 +126,7 @@ public abstract class OmemoActivity extends XmppActivity {
trustToggle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- account.getAxolotlService().setFingerprintTrust(fingerprint,FingerprintStatus.createActive(false));
+ account.getAxolotlService().setFingerprintTrust(fingerprint, FingerprintStatus.createActive(false));
v.setEnabled(true);
v.setOnClickListener(null);
}
@@ -211,7 +211,7 @@ public abstract class OmemoActivity extends XmppActivity {
if (x509Certificate != null) {
showCertificateInformationDialog(CryptoHelper.extractCertificateInformation(x509Certificate));
} else {
- Toast.makeText(this,R.string.certificate_not_found, Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.certificate_not_found, Toast.LENGTH_SHORT).show();
}
}
diff --git a/src/main/java/de/pixart/messenger/crypto/OtrService.java b/src/main/java/de/pixart/messenger/crypto/OtrService.java
index 18f9670d8..459f49c38 100644
--- a/src/main/java/de/pixart/messenger/crypto/OtrService.java
+++ b/src/main/java/de/pixart/messenger/crypto/OtrService.java
@@ -38,25 +38,25 @@ import de.pixart.messenger.xmpp.stanzas.MessagePacket;
public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost {
- private Account account;
- private OtrPolicy otrPolicy;
- private KeyPair keyPair;
- private XmppConnectionService mXmppConnectionService;
-
- public OtrService(XmppConnectionService service, Account account) {
- this.account = account;
- this.otrPolicy = new OtrPolicyImpl();
- this.otrPolicy.setAllowV1(false);
- this.otrPolicy.setAllowV2(true);
- this.otrPolicy.setAllowV3(true);
- this.keyPair = loadKey(account.getKeys());
- this.mXmppConnectionService = service;
- }
-
- private KeyPair loadKey(final JSONObject keys) {
- if (keys == null) {
- return null;
- }
+ private Account account;
+ private OtrPolicy otrPolicy;
+ private KeyPair keyPair;
+ private XmppConnectionService mXmppConnectionService;
+
+ public OtrService(XmppConnectionService service, Account account) {
+ this.account = account;
+ this.otrPolicy = new OtrPolicyImpl();
+ this.otrPolicy.setAllowV1(false);
+ this.otrPolicy.setAllowV2(true);
+ this.otrPolicy.setAllowV3(true);
+ this.keyPair = loadKey(account.getKeys());
+ this.mXmppConnectionService = service;
+ }
+
+ private KeyPair loadKey(final JSONObject keys) {
+ if (keys == null) {
+ return null;
+ }
synchronized (keys) {
try {
BigInteger x = new BigInteger(keys.getString("otr_x"), 16);
@@ -78,232 +78,232 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost {
return null;
}
}
- }
-
- private void saveKey() {
- PublicKey publicKey = keyPair.getPublic();
- PrivateKey privateKey = keyPair.getPrivate();
- KeyFactory keyFactory;
- try {
- keyFactory = KeyFactory.getInstance("DSA");
- DSAPrivateKeySpec privateKeySpec = keyFactory.getKeySpec(
- privateKey, DSAPrivateKeySpec.class);
- DSAPublicKeySpec publicKeySpec = keyFactory.getKeySpec(publicKey,
- DSAPublicKeySpec.class);
- this.account.setKey("otr_x", privateKeySpec.getX().toString(16));
- this.account.setKey("otr_g", privateKeySpec.getG().toString(16));
- this.account.setKey("otr_p", privateKeySpec.getP().toString(16));
- this.account.setKey("otr_q", privateKeySpec.getQ().toString(16));
- this.account.setKey("otr_y", publicKeySpec.getY().toString(16));
- } catch (final NoSuchAlgorithmException | InvalidKeySpecException e) {
- e.printStackTrace();
- }
+ }
+
+ private void saveKey() {
+ PublicKey publicKey = keyPair.getPublic();
+ PrivateKey privateKey = keyPair.getPrivate();
+ KeyFactory keyFactory;
+ try {
+ keyFactory = KeyFactory.getInstance("DSA");
+ DSAPrivateKeySpec privateKeySpec = keyFactory.getKeySpec(
+ privateKey, DSAPrivateKeySpec.class);
+ DSAPublicKeySpec publicKeySpec = keyFactory.getKeySpec(publicKey,
+ DSAPublicKeySpec.class);
+ this.account.setKey("otr_x", privateKeySpec.getX().toString(16));
+ this.account.setKey("otr_g", privateKeySpec.getG().toString(16));
+ this.account.setKey("otr_p", privateKeySpec.getP().toString(16));
+ this.account.setKey("otr_q", privateKeySpec.getQ().toString(16));
+ this.account.setKey("otr_y", publicKeySpec.getY().toString(16));
+ } catch (final NoSuchAlgorithmException | InvalidKeySpecException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ @Override
+ public void askForSecret(SessionID id, InstanceTag instanceTag, String question) {
+ try {
+ final Jid jid = Jid.fromSessionID(id);
+ Conversation conversation = this.mXmppConnectionService.find(this.account, jid);
+ if (conversation != null) {
+ conversation.smp().hint = question;
+ conversation.smp().status = Conversation.Smp.STATUS_CONTACT_REQUESTED;
+ mXmppConnectionService.updateConversationUi();
+ }
+ } catch (InvalidJidException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": smp in invalid session " + id.toString());
+ }
+ }
+
+ @Override
+ public void finishedSessionMessage(SessionID arg0, String arg1)
+ throws OtrException {
+
+ }
+
+ @Override
+ public String getFallbackMessage(SessionID arg0) {
+ return MessageGenerator.OTR_FALLBACK_MESSAGE;
+ }
+
+ @Override
+ public byte[] getLocalFingerprintRaw(SessionID arg0) {
+ try {
+ return getFingerprintRaw(getPublicKey());
+ } catch (OtrCryptoException e) {
+ return null;
+ }
+ }
+
+ public PublicKey getPublicKey() {
+ if (this.keyPair == null) {
+ return null;
+ }
+ return this.keyPair.getPublic();
+ }
+
+ @Override
+ public KeyPair getLocalKeyPair(SessionID arg0) throws OtrException {
+ if (this.keyPair == null) {
+ KeyPairGenerator kg;
+ try {
+ kg = KeyPairGenerator.getInstance("DSA");
+ this.keyPair = kg.genKeyPair();
+ this.saveKey();
+ mXmppConnectionService.databaseBackend.updateAccount(account);
+ } catch (NoSuchAlgorithmException e) {
+ Log.d(Config.LOGTAG,
+ "error generating key pair " + e.getMessage());
+ }
+ }
+ return this.keyPair;
+ }
+
+ @Override
+ public String getReplyForUnreadableMessage(SessionID arg0) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public OtrPolicy getSessionPolicy(SessionID arg0) {
+ return otrPolicy;
+ }
+
+ @Override
+ public void injectMessage(SessionID session, String body)
+ throws OtrException {
+ MessagePacket packet = new MessagePacket();
+ packet.setFrom(account.getJid());
+ if (session.getUserID().isEmpty()) {
+ packet.setAttribute("to", session.getAccountID());
+ } else {
+ packet.setAttribute("to", session.getAccountID() + "/" + session.getUserID());
+ }
+ packet.setBody(body);
+ MessageGenerator.addMessageHints(packet);
+ try {
+ Jid jid = Jid.fromSessionID(session);
+ Conversation conversation = mXmppConnectionService.find(account, jid);
+ if (conversation != null && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
+ if (mXmppConnectionService.sendChatStates()) {
+ packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
+ }
+ }
+ } catch (final InvalidJidException ignored) {
+
+ }
+
+ packet.setType(MessagePacket.TYPE_CHAT);
+ account.getXmppConnection().sendMessagePacket(packet);
+ }
+
+ @Override
+ public void messageFromAnotherInstanceReceived(SessionID session) {
+ sendOtrErrorMessage(session, "Message from another OTR-instance received");
+ }
+
+ @Override
+ public void multipleInstancesDetected(SessionID arg0) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void requireEncryptedMessage(SessionID arg0, String arg1)
+ throws OtrException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void showError(SessionID arg0, String arg1) throws OtrException {
+ Log.d(Config.LOGTAG, "show error");
+ }
+
+ @Override
+ public void smpAborted(SessionID id) throws OtrException {
+ setSmpStatus(id, Conversation.Smp.STATUS_NONE);
+ }
+
+ private void setSmpStatus(SessionID id, int status) {
+ try {
+ final Jid jid = Jid.fromSessionID(id);
+ Conversation conversation = this.mXmppConnectionService.find(this.account, jid);
+ if (conversation != null) {
+ conversation.smp().status = status;
+ mXmppConnectionService.updateConversationUi();
+ }
+ } catch (final InvalidJidException ignored) {
+
+ }
+ }
+
+ @Override
+ public void smpError(SessionID id, int arg1, boolean arg2)
+ throws OtrException {
+ setSmpStatus(id, Conversation.Smp.STATUS_NONE);
+ }
+
+ @Override
+ public void unencryptedMessageReceived(SessionID arg0, String arg1)
+ throws OtrException {
+ throw new OtrException(new Exception("unencrypted message received"));
+ }
+
+ @Override
+ public void unreadableMessageReceived(SessionID session) throws OtrException {
+ Log.d(Config.LOGTAG, "unreadable message received");
+ sendOtrErrorMessage(session, "You sent me an unreadable OTR-encrypted message");
+ }
+
+ public void sendOtrErrorMessage(SessionID session, String errorText) {
+ try {
+ Jid jid = Jid.fromSessionID(session);
+ Conversation conversation = mXmppConnectionService.find(account, jid);
+ String id = conversation == null ? null : conversation.getLastReceivedOtrMessageId();
+ if (id != null) {
+ MessagePacket packet = mXmppConnectionService.getMessageGenerator()
+ .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()
+ + ": unreadable OTR message in " + conversation.getName());
+ }
+ } catch (InvalidJidException e) {
+ return;
+ }
+ }
+ @Override
+ public void unverify(SessionID id, String arg1) {
+ setSmpStatus(id, Conversation.Smp.STATUS_FAILED);
}
- @Override
- public void askForSecret(SessionID id, InstanceTag instanceTag, String question) {
- try {
- final Jid jid = Jid.fromSessionID(id);
- Conversation conversation = this.mXmppConnectionService.find(this.account,jid);
- if (conversation!=null) {
- conversation.smp().hint = question;
- conversation.smp().status = Conversation.Smp.STATUS_CONTACT_REQUESTED;
- mXmppConnectionService.updateConversationUi();
- }
- } catch (InvalidJidException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": smp in invalid session "+id.toString());
- }
- }
-
- @Override
- public void finishedSessionMessage(SessionID arg0, String arg1)
- throws OtrException {
-
- }
-
- @Override
- public String getFallbackMessage(SessionID arg0) {
- return MessageGenerator.OTR_FALLBACK_MESSAGE;
- }
-
- @Override
- public byte[] getLocalFingerprintRaw(SessionID arg0) {
- try {
- return getFingerprintRaw(getPublicKey());
- } catch (OtrCryptoException e) {
- return null;
- }
- }
-
- public PublicKey getPublicKey() {
- if (this.keyPair == null) {
- return null;
- }
- return this.keyPair.getPublic();
- }
-
- @Override
- public KeyPair getLocalKeyPair(SessionID arg0) throws OtrException {
- if (this.keyPair == null) {
- KeyPairGenerator kg;
- try {
- kg = KeyPairGenerator.getInstance("DSA");
- this.keyPair = kg.genKeyPair();
- this.saveKey();
- mXmppConnectionService.databaseBackend.updateAccount(account);
- } catch (NoSuchAlgorithmException e) {
- Log.d(Config.LOGTAG,
- "error generating key pair " + e.getMessage());
- }
- }
- return this.keyPair;
- }
-
- @Override
- public String getReplyForUnreadableMessage(SessionID arg0) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public OtrPolicy getSessionPolicy(SessionID arg0) {
- return otrPolicy;
- }
-
- @Override
- public void injectMessage(SessionID session, String body)
- throws OtrException {
- MessagePacket packet = new MessagePacket();
- packet.setFrom(account.getJid());
- if (session.getUserID().isEmpty()) {
- packet.setAttribute("to", session.getAccountID());
- } else {
- packet.setAttribute("to", session.getAccountID() + "/" + session.getUserID());
- }
- packet.setBody(body);
- MessageGenerator.addMessageHints(packet);
- try {
- Jid jid = Jid.fromSessionID(session);
- Conversation conversation = mXmppConnectionService.find(account,jid);
- if (conversation != null && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
- if (mXmppConnectionService.sendChatStates()) {
- packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
- }
- }
- } catch (final InvalidJidException ignored) {
-
- }
-
- packet.setType(MessagePacket.TYPE_CHAT);
- account.getXmppConnection().sendMessagePacket(packet);
- }
-
- @Override
- public void messageFromAnotherInstanceReceived(SessionID session) {
- sendOtrErrorMessage(session, "Message from another OTR-instance received");
- }
-
- @Override
- public void multipleInstancesDetected(SessionID arg0) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void requireEncryptedMessage(SessionID arg0, String arg1)
- throws OtrException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void showError(SessionID arg0, String arg1) throws OtrException {
- Log.d(Config.LOGTAG,"show error");
- }
-
- @Override
- public void smpAborted(SessionID id) throws OtrException {
- setSmpStatus(id, Conversation.Smp.STATUS_NONE);
- }
-
- private void setSmpStatus(SessionID id, int status) {
- try {
- final Jid jid = Jid.fromSessionID(id);
- Conversation conversation = this.mXmppConnectionService.find(this.account,jid);
- if (conversation!=null) {
- conversation.smp().status = status;
- mXmppConnectionService.updateConversationUi();
- }
- } catch (final InvalidJidException ignored) {
-
- }
- }
-
- @Override
- public void smpError(SessionID id, int arg1, boolean arg2)
- throws OtrException {
- setSmpStatus(id, Conversation.Smp.STATUS_NONE);
- }
-
- @Override
- public void unencryptedMessageReceived(SessionID arg0, String arg1)
- throws OtrException {
- throw new OtrException(new Exception("unencrypted message received"));
- }
-
- @Override
- public void unreadableMessageReceived(SessionID session) throws OtrException {
- Log.d(Config.LOGTAG,"unreadable message received");
- sendOtrErrorMessage(session, "You sent me an unreadable OTR-encrypted message");
- }
-
- public void sendOtrErrorMessage(SessionID session, String errorText) {
- try {
- Jid jid = Jid.fromSessionID(session);
- Conversation conversation = mXmppConnectionService.find(account, jid);
- String id = conversation == null ? null : conversation.getLastReceivedOtrMessageId();
- if (id != null) {
- MessagePacket packet = mXmppConnectionService.getMessageGenerator()
- .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()
- +": unreadable OTR message in "+conversation.getName());
- }
- } catch (InvalidJidException e) {
- return;
- }
- }
-
- @Override
- public void unverify(SessionID id, String arg1) {
- setSmpStatus(id, Conversation.Smp.STATUS_FAILED);
- }
-
- @Override
- public void verify(SessionID id, String fingerprint, boolean approved) {
- Log.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);
- if (conversation!=null) {
- if (approved) {
- conversation.getContact().addOtrFingerprint(fingerprint);
- }
- conversation.smp().hint = null;
- conversation.smp().status = Conversation.Smp.STATUS_VERIFIED;
- mXmppConnectionService.updateConversationUi();
- mXmppConnectionService.syncRosterToDisk(conversation.getAccount());
- }
- } catch (final InvalidJidException ignored) {
- }
- }
-
- @Override
- public FragmenterInstructions getFragmenterInstructions(SessionID sessionID) {
- return null;
- }
+ @Override
+ public void verify(SessionID id, String fingerprint, boolean approved) {
+ Log.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);
+ if (conversation != null) {
+ if (approved) {
+ conversation.getContact().addOtrFingerprint(fingerprint);
+ }
+ conversation.smp().hint = null;
+ conversation.smp().status = Conversation.Smp.STATUS_VERIFIED;
+ mXmppConnectionService.updateConversationUi();
+ mXmppConnectionService.syncRosterToDisk(conversation.getAccount());
+ }
+ } catch (final InvalidJidException ignored) {
+ }
+ }
+
+ @Override
+ public FragmenterInstructions getFragmenterInstructions(SessionID sessionID) {
+ return null;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/PgpDecryptionService.java b/src/main/java/de/pixart/messenger/crypto/PgpDecryptionService.java
index ffa853fda..4b0e78764 100644
--- a/src/main/java/de/pixart/messenger/crypto/PgpDecryptionService.java
+++ b/src/main/java/de/pixart/messenger/crypto/PgpDecryptionService.java
@@ -28,9 +28,9 @@ public class PgpDecryptionService {
private final XmppConnectionService mXmppConnectionService;
private OpenPgpApi openPgpApi = null;
- protected final ArrayDeque<Message> messages = new ArrayDeque();
+ protected final ArrayDeque<Message> messages = new ArrayDeque();
protected final HashSet<Message> pendingNotifications = new HashSet<>();
- Message currentMessage;
+ Message currentMessage;
private PendingIntent pendingIntent;
@@ -38,7 +38,7 @@ public class PgpDecryptionService {
this.mXmppConnectionService = service;
}
- public synchronized boolean decrypt(final Message message, boolean notify) {
+ public synchronized boolean decrypt(final Message message, boolean notify) {
messages.add(message);
if (notify && pendingIntent == null) {
pendingNotifications.add(message);
@@ -48,10 +48,10 @@ public class PgpDecryptionService {
continueDecryption();
return notify;
}
- }
+ }
public synchronized void decrypt(final List<Message> list) {
- for(Message message : list) {
+ for (Message message : list) {
if (message.getEncryption() == Message.ENCRYPTION_PGP) {
messages.add(message);
}
@@ -69,19 +69,19 @@ public class PgpDecryptionService {
this.pendingNotifications.remove(message);
}
- protected synchronized void decryptNext() {
- if (pendingIntent == null
+ protected synchronized void decryptNext() {
+ if (pendingIntent == null
&& getOpenPgpApi() != null
- && (currentMessage = messages.poll()) != null) {
- new Thread(new Runnable() {
+ && (currentMessage = messages.poll()) != null) {
+ new Thread(new Runnable() {
@Override
public void run() {
executeApi(currentMessage);
decryptNext();
}
}).start();
- }
- }
+ }
+ }
public synchronized void continueDecryption(boolean resetPending) {
if (resetPending) {
@@ -200,7 +200,7 @@ public class PgpDecryptionService {
if (pendingIntent == null) {
return false;
} else {
- for(Message message : messages) {
+ for (Message message : messages) {
if (message.getConversation() == conversation) {
return true;
}
diff --git a/src/main/java/de/pixart/messenger/crypto/PgpEngine.java b/src/main/java/de/pixart/messenger/crypto/PgpEngine.java
index 38d097ec3..c906c7514 100644
--- a/src/main/java/de/pixart/messenger/crypto/PgpEngine.java
+++ b/src/main/java/de/pixart/messenger/crypto/PgpEngine.java
@@ -29,290 +29,290 @@ import de.pixart.messenger.services.XmppConnectionService;
import de.pixart.messenger.ui.UiCallback;
public class PgpEngine {
- private OpenPgpApi api;
- private XmppConnectionService mXmppConnectionService;
+ private OpenPgpApi api;
+ private XmppConnectionService mXmppConnectionService;
- public PgpEngine(OpenPgpApi api, XmppConnectionService service) {
- this.api = api;
- this.mXmppConnectionService = service;
- }
+ public PgpEngine(OpenPgpApi api, XmppConnectionService service) {
+ this.api = api;
+ this.mXmppConnectionService = service;
+ }
- public void encrypt(final Message message, final UiCallback<Message> callback) {
- Intent params = new Intent();
- params.setAction(OpenPgpApi.ACTION_ENCRYPT);
- final Conversation conversation = message.getConversation();
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- long[] keys = {
- conversation.getContact().getPgpKeyId(),
- conversation.getAccount().getPgpId()
- };
- params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keys);
- } else {
- params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, conversation.getMucOptions().getPgpKeyIds());
- }
+ public void encrypt(final Message message, final UiCallback<Message> callback) {
+ Intent params = new Intent();
+ params.setAction(OpenPgpApi.ACTION_ENCRYPT);
+ final Conversation conversation = message.getConversation();
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ long[] keys = {
+ conversation.getContact().getPgpKeyId(),
+ conversation.getAccount().getPgpId()
+ };
+ params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keys);
+ } else {
+ params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, conversation.getMucOptions().getPgpKeyIds());
+ }
- if (!message.needsUploading()) {
- params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
- String body;
- if (message.hasFileOnRemoteHost()) {
- body = message.getFileParams().url.toString();
- } else {
- body = message.getBody();
- }
- InputStream is = new ByteArrayInputStream(body.getBytes());
- final OutputStream os = new ByteArrayOutputStream();
- api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
+ if (!message.needsUploading()) {
+ params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
+ String body;
+ if (message.hasFileOnRemoteHost()) {
+ body = message.getFileParams().url.toString();
+ } else {
+ body = message.getBody();
+ }
+ InputStream is = new ByteArrayInputStream(body.getBytes());
+ final OutputStream os = new ByteArrayOutputStream();
+ api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
- @Override
- public void onReturn(Intent result) {
- switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
- OpenPgpApi.RESULT_CODE_ERROR)) {
- case OpenPgpApi.RESULT_CODE_SUCCESS:
- try {
- os.flush();
- StringBuilder encryptedMessageBody = new StringBuilder();
- 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());
- }
- }
- message.setEncryptedBody(encryptedMessageBody
- .toString());
- callback.success(message);
- } catch (IOException e) {
- callback.error(R.string.openpgp_error, message);
- }
+ @Override
+ public void onReturn(Intent result) {
+ switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
+ OpenPgpApi.RESULT_CODE_ERROR)) {
+ case OpenPgpApi.RESULT_CODE_SUCCESS:
+ try {
+ os.flush();
+ StringBuilder encryptedMessageBody = new StringBuilder();
+ 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());
+ }
+ }
+ message.setEncryptedBody(encryptedMessageBody
+ .toString());
+ callback.success(message);
+ } catch (IOException e) {
+ callback.error(R.string.openpgp_error, message);
+ }
- break;
- case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
- callback.userInputRequried((PendingIntent) result
- .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
- message);
- break;
- case OpenPgpApi.RESULT_CODE_ERROR:
- logError(conversation.getAccount(), (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
- callback.error(R.string.openpgp_error, message);
- break;
- }
- }
- });
- } else {
- try {
- DownloadableFile inputFile = this.mXmppConnectionService
- .getFileBackend().getFile(message, true);
- DownloadableFile outputFile = this.mXmppConnectionService
- .getFileBackend().getFile(message, false);
- outputFile.getParentFile().mkdirs();
- outputFile.createNewFile();
- final InputStream is = new FileInputStream(inputFile);
- final OutputStream os = new FileOutputStream(outputFile);
- api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
+ break;
+ case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+ callback.userInputRequried((PendingIntent) result
+ .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
+ message);
+ break;
+ case OpenPgpApi.RESULT_CODE_ERROR:
+ logError(conversation.getAccount(), (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
+ callback.error(R.string.openpgp_error, message);
+ break;
+ }
+ }
+ });
+ } else {
+ try {
+ DownloadableFile inputFile = this.mXmppConnectionService
+ .getFileBackend().getFile(message, true);
+ DownloadableFile outputFile = this.mXmppConnectionService
+ .getFileBackend().getFile(message, false);
+ outputFile.getParentFile().mkdirs();
+ outputFile.createNewFile();
+ final InputStream is = new FileInputStream(inputFile);
+ final OutputStream os = new FileOutputStream(outputFile);
+ api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
- @Override
- public void onReturn(Intent result) {
- switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
- OpenPgpApi.RESULT_CODE_ERROR)) {
- case OpenPgpApi.RESULT_CODE_SUCCESS:
- try {
- os.flush();
- } catch (IOException ignored) {
- //ignored
- }
- FileBackend.close(os);
- callback.success(message);
- break;
- case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
- callback.userInputRequried(
- (PendingIntent) result
- .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
- message);
- break;
- case OpenPgpApi.RESULT_CODE_ERROR:
- logError(conversation.getAccount(), (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
- callback.error(R.string.openpgp_error, message);
- break;
- }
- }
- });
- } catch (final IOException e) {
- callback.error(R.string.openpgp_error, message);
- }
- }
- }
+ @Override
+ public void onReturn(Intent result) {
+ switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
+ OpenPgpApi.RESULT_CODE_ERROR)) {
+ case OpenPgpApi.RESULT_CODE_SUCCESS:
+ try {
+ os.flush();
+ } catch (IOException ignored) {
+ //ignored
+ }
+ FileBackend.close(os);
+ callback.success(message);
+ break;
+ case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+ callback.userInputRequried(
+ (PendingIntent) result
+ .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
+ message);
+ break;
+ case OpenPgpApi.RESULT_CODE_ERROR:
+ logError(conversation.getAccount(), (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
+ callback.error(R.string.openpgp_error, message);
+ break;
+ }
+ }
+ });
+ } catch (final IOException e) {
+ callback.error(R.string.openpgp_error, message);
+ }
+ }
+ }
- public long fetchKeyId(Account account, String status, String signature) {
- if ((signature == null) || (api == null)) {
- return 0;
- }
- if (status == null) {
- status = "";
- }
- final StringBuilder pgpSig = new StringBuilder();
- pgpSig.append("-----BEGIN PGP SIGNED MESSAGE-----");
- pgpSig.append('\n');
- pgpSig.append('\n');
- pgpSig.append(status);
- pgpSig.append('\n');
- pgpSig.append("-----BEGIN PGP SIGNATURE-----");
- pgpSig.append('\n');
- pgpSig.append('\n');
- pgpSig.append(signature.replace("\n", "").trim());
- pgpSig.append('\n');
- pgpSig.append("-----END PGP SIGNATURE-----");
- Intent params = new Intent();
- params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
- params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
- InputStream is = new ByteArrayInputStream(pgpSig.toString().getBytes());
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- Intent result = api.executeApi(params, is, os);
- switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
- OpenPgpApi.RESULT_CODE_ERROR)) {
- case OpenPgpApi.RESULT_CODE_SUCCESS:
- OpenPgpSignatureResult sigResult = result
- .getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
- if (sigResult != null) {
- return sigResult.getKeyId();
- } else {
- return 0;
- }
- case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
- return 0;
- case OpenPgpApi.RESULT_CODE_ERROR:
- logError(account, (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
- return 0;
- }
- return 0;
- }
+ public long fetchKeyId(Account account, String status, String signature) {
+ if ((signature == null) || (api == null)) {
+ return 0;
+ }
+ if (status == null) {
+ status = "";
+ }
+ final StringBuilder pgpSig = new StringBuilder();
+ pgpSig.append("-----BEGIN PGP SIGNED MESSAGE-----");
+ pgpSig.append('\n');
+ pgpSig.append('\n');
+ pgpSig.append(status);
+ pgpSig.append('\n');
+ pgpSig.append("-----BEGIN PGP SIGNATURE-----");
+ pgpSig.append('\n');
+ pgpSig.append('\n');
+ pgpSig.append(signature.replace("\n", "").trim());
+ pgpSig.append('\n');
+ pgpSig.append("-----END PGP SIGNATURE-----");
+ Intent params = new Intent();
+ params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
+ params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
+ InputStream is = new ByteArrayInputStream(pgpSig.toString().getBytes());
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ Intent result = api.executeApi(params, is, os);
+ switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
+ OpenPgpApi.RESULT_CODE_ERROR)) {
+ case OpenPgpApi.RESULT_CODE_SUCCESS:
+ OpenPgpSignatureResult sigResult = result
+ .getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
+ if (sigResult != null) {
+ return sigResult.getKeyId();
+ } else {
+ return 0;
+ }
+ case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+ return 0;
+ case OpenPgpApi.RESULT_CODE_ERROR:
+ logError(account, (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
+ return 0;
+ }
+ return 0;
+ }
- public void chooseKey(final Account account, final UiCallback<Account> callback) {
- Intent p = new Intent();
- p.setAction(OpenPgpApi.ACTION_GET_SIGN_KEY_ID);
- api.executeApiAsync(p, null, null, new IOpenPgpCallback() {
+ public void chooseKey(final Account account, final UiCallback<Account> callback) {
+ Intent p = new Intent();
+ p.setAction(OpenPgpApi.ACTION_GET_SIGN_KEY_ID);
+ api.executeApiAsync(p, null, null, new IOpenPgpCallback() {
- @Override
- public void onReturn(Intent result) {
- switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
- case OpenPgpApi.RESULT_CODE_SUCCESS:
- callback.success(account);
- return;
- case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
- callback.userInputRequried((PendingIntent) result
- .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
- account);
- return;
- case OpenPgpApi.RESULT_CODE_ERROR:
- logError(account, (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
- callback.error(R.string.openpgp_error, account);
- }
- }
- });
- }
+ @Override
+ public void onReturn(Intent result) {
+ switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
+ case OpenPgpApi.RESULT_CODE_SUCCESS:
+ callback.success(account);
+ return;
+ case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+ callback.userInputRequried((PendingIntent) result
+ .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
+ account);
+ return;
+ case OpenPgpApi.RESULT_CODE_ERROR:
+ logError(account, (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
+ callback.error(R.string.openpgp_error, account);
+ }
+ }
+ });
+ }
- public void generateSignature(final Account account, String status,
- final UiCallback<Account> callback) {
- if (account.getPgpId() == 0) {
- return;
- }
- Intent params = new Intent();
- params.setAction(OpenPgpApi.ACTION_CLEARTEXT_SIGN);
- params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
- params.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, account.getPgpId());
- InputStream is = new ByteArrayInputStream(status.getBytes());
- final OutputStream os = new ByteArrayOutputStream();
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": signing status message \""+status+"\"");
- api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
+ public void generateSignature(final Account account, String status,
+ final UiCallback<Account> callback) {
+ if (account.getPgpId() == 0) {
+ return;
+ }
+ Intent params = new Intent();
+ params.setAction(OpenPgpApi.ACTION_CLEARTEXT_SIGN);
+ params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
+ params.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, account.getPgpId());
+ InputStream is = new ByteArrayInputStream(status.getBytes());
+ final OutputStream os = new ByteArrayOutputStream();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": signing status message \"" + status + "\"");
+ api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
- @Override
- public void onReturn(Intent result) {
- switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
- case OpenPgpApi.RESULT_CODE_SUCCESS:
- StringBuilder signatureBuilder = new StringBuilder();
- try {
- os.flush();
- String[] lines = os.toString().split("\n");
- boolean sig = false;
- for (String line : lines) {
- if (sig) {
- if (line.contains("END PGP SIGNATURE")) {
- sig = false;
- } else {
- if (!line.contains("Version")) {
- signatureBuilder.append(line.trim());
- }
- }
- }
- if (line.contains("BEGIN PGP SIGNATURE")) {
- sig = true;
- }
- }
- } catch (IOException e) {
- callback.error(R.string.openpgp_error, account);
- return;
- }
- account.setPgpSignature(signatureBuilder.toString());
- callback.success(account);
- return;
- case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
- callback.userInputRequried((PendingIntent) result
- .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
- account);
- return;
- case OpenPgpApi.RESULT_CODE_ERROR:
- OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
- if (error != null && "signing subkey not found!".equals(error.getMessage())) {
- callback.error(0,account);
- } else {
- logError(account, error);
- callback.error(R.string.unable_to_connect_to_keychain, null);
- }
+ @Override
+ public void onReturn(Intent result) {
+ switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
+ case OpenPgpApi.RESULT_CODE_SUCCESS:
+ StringBuilder signatureBuilder = new StringBuilder();
+ try {
+ os.flush();
+ String[] lines = os.toString().split("\n");
+ boolean sig = false;
+ for (String line : lines) {
+ if (sig) {
+ if (line.contains("END PGP SIGNATURE")) {
+ sig = false;
+ } else {
+ if (!line.contains("Version")) {
+ signatureBuilder.append(line.trim());
+ }
+ }
+ }
+ if (line.contains("BEGIN PGP SIGNATURE")) {
+ sig = true;
+ }
+ }
+ } catch (IOException e) {
+ callback.error(R.string.openpgp_error, account);
+ return;
+ }
+ account.setPgpSignature(signatureBuilder.toString());
+ callback.success(account);
+ return;
+ case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+ callback.userInputRequried((PendingIntent) result
+ .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
+ account);
+ return;
+ case OpenPgpApi.RESULT_CODE_ERROR:
+ OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
+ if (error != null && "signing subkey not found!".equals(error.getMessage())) {
+ callback.error(0, account);
+ } else {
+ logError(account, error);
+ callback.error(R.string.unable_to_connect_to_keychain, null);
+ }
}
- }
- });
- }
+ }
+ });
+ }
- public void hasKey(final Contact contact, final UiCallback<Contact> callback) {
- Intent params = new Intent();
- params.setAction(OpenPgpApi.ACTION_GET_KEY);
- params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
- api.executeApiAsync(params, null, null, new IOpenPgpCallback() {
+ public void hasKey(final Contact contact, final UiCallback<Contact> callback) {
+ Intent params = new Intent();
+ params.setAction(OpenPgpApi.ACTION_GET_KEY);
+ params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
+ api.executeApiAsync(params, null, null, new IOpenPgpCallback() {
- @Override
- public void onReturn(Intent result) {
- switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
- case OpenPgpApi.RESULT_CODE_SUCCESS:
- callback.success(contact);
- return;
- case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
- callback.userInputRequried((PendingIntent) result
- .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
- contact);
- return;
- case OpenPgpApi.RESULT_CODE_ERROR:
- logError(contact.getAccount(), (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
- callback.error(R.string.openpgp_error, contact);
+ @Override
+ public void onReturn(Intent result) {
+ switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
+ case OpenPgpApi.RESULT_CODE_SUCCESS:
+ callback.success(contact);
+ return;
+ case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+ callback.userInputRequried((PendingIntent) result
+ .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
+ contact);
+ return;
+ case OpenPgpApi.RESULT_CODE_ERROR:
+ logError(contact.getAccount(), (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
+ callback.error(R.string.openpgp_error, contact);
}
- }
- });
- }
+ }
+ });
+ }
- private static void logError(Account account, OpenPgpError error) {
- if (error != null) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": OpenKeychain error '"+error.getMessage()+"' code="+error.getErrorId());
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": OpenKeychain error with no message");
- }
- }
+ private static void logError(Account account, OpenPgpError error) {
+ if (error != null) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": OpenKeychain error '" + error.getMessage() + "' code=" + error.getErrorId());
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": OpenKeychain error with no message");
+ }
+ }
- public PendingIntent getIntentForKey(Contact contact) {
- return getIntentForKey(contact.getPgpKeyId());
- }
+ public PendingIntent getIntentForKey(Contact contact) {
+ return getIntentForKey(contact.getPgpKeyId());
+ }
- public PendingIntent getIntentForKey(long pgpKeyId) {
- Intent params = new Intent();
- params.setAction(OpenPgpApi.ACTION_GET_KEY);
- params.putExtra(OpenPgpApi.EXTRA_KEY_ID, pgpKeyId);
- Intent result = api.executeApi(params, null, null);
- return (PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
- }
+ public PendingIntent getIntentForKey(long pgpKeyId) {
+ Intent params = new Intent();
+ params.setAction(OpenPgpApi.ACTION_GET_KEY);
+ params.putExtra(OpenPgpApi.EXTRA_KEY_ID, pgpKeyId);
+ Intent result = api.executeApi(params, null, null);
+ return (PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java b/src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java
index 250516daa..d305a33d5 100644
--- a/src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java
+++ b/src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java
@@ -26,102 +26,102 @@ import javax.net.ssl.SSLSession;
public class XmppDomainVerifier implements HostnameVerifier {
- private static final String LOGTAG = "XmppDomainVerifier";
+ private static final String LOGTAG = "XmppDomainVerifier";
- private final String SRVName = "1.3.6.1.5.5.7.8.7";
- private final String xmppAddr = "1.3.6.1.5.5.7.8.5";
+ private final String SRVName = "1.3.6.1.5.5.7.8.7";
+ private final String xmppAddr = "1.3.6.1.5.5.7.8.5";
- @Override
- public boolean verify(String domain, SSLSession sslSession) {
- try {
- Certificate[] chain = sslSession.getPeerCertificates();
- if (chain.length == 0 || !(chain[0] instanceof X509Certificate)) {
- return false;
- }
- X509Certificate certificate = (X509Certificate) chain[0];
- Collection<List<?>> alternativeNames = certificate.getSubjectAlternativeNames();
- List<String> xmppAddrs = new ArrayList<>();
- List<String> srvNames = new ArrayList<>();
- List<String> domains = new ArrayList<>();
- if (alternativeNames != null) {
- for (List<?> san : alternativeNames) {
- Integer type = (Integer) san.get(0);
- if (type == 0) {
- Pair<String, String> otherName = parseOtherName((byte[]) san.get(1));
- if (otherName != null) {
- switch (otherName.first) {
- case SRVName:
- srvNames.add(otherName.second);
- break;
- case xmppAddr:
- xmppAddrs.add(otherName.second);
- break;
- default:
- Log.d(LOGTAG, "oid: " + otherName.first + " value: " + otherName.second);
- }
- }
- } else if (type == 2) {
- Object value = san.get(1);
- if (value instanceof String) {
- domains.add((String) value);
- }
- }
- }
- }
- if (srvNames.size() == 0 && xmppAddrs.size() == 0 && domains.size() == 0) {
- X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
- RDN[] rdns = x500name.getRDNs(BCStyle.CN);
- for (int i = 0; i < rdns.length; ++i) {
- domains.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[i].getFirst().getValue()));
- }
- }
- Log.d(LOGTAG, "searching for " + domain + " in srvNames: " + srvNames + " xmppAddrs: " + xmppAddrs + " domains:" + domains);
- return xmppAddrs.contains(domain) || srvNames.contains("_xmpp-client." + domain) || matchDomain(domain, domains);
- } catch (Exception e) {
- return false;
- }
- }
+ @Override
+ public boolean verify(String domain, SSLSession sslSession) {
+ try {
+ Certificate[] chain = sslSession.getPeerCertificates();
+ if (chain.length == 0 || !(chain[0] instanceof X509Certificate)) {
+ return false;
+ }
+ X509Certificate certificate = (X509Certificate) chain[0];
+ Collection<List<?>> alternativeNames = certificate.getSubjectAlternativeNames();
+ List<String> xmppAddrs = new ArrayList<>();
+ List<String> srvNames = new ArrayList<>();
+ List<String> domains = new ArrayList<>();
+ if (alternativeNames != null) {
+ for (List<?> san : alternativeNames) {
+ Integer type = (Integer) san.get(0);
+ if (type == 0) {
+ Pair<String, String> otherName = parseOtherName((byte[]) san.get(1));
+ if (otherName != null) {
+ switch (otherName.first) {
+ case SRVName:
+ srvNames.add(otherName.second);
+ break;
+ case xmppAddr:
+ xmppAddrs.add(otherName.second);
+ break;
+ default:
+ Log.d(LOGTAG, "oid: " + otherName.first + " value: " + otherName.second);
+ }
+ }
+ } else if (type == 2) {
+ Object value = san.get(1);
+ if (value instanceof String) {
+ domains.add((String) value);
+ }
+ }
+ }
+ }
+ if (srvNames.size() == 0 && xmppAddrs.size() == 0 && domains.size() == 0) {
+ X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
+ RDN[] rdns = x500name.getRDNs(BCStyle.CN);
+ for (int i = 0; i < rdns.length; ++i) {
+ domains.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[i].getFirst().getValue()));
+ }
+ }
+ Log.d(LOGTAG, "searching for " + domain + " in srvNames: " + srvNames + " xmppAddrs: " + xmppAddrs + " domains:" + domains);
+ return xmppAddrs.contains(domain) || srvNames.contains("_xmpp-client." + domain) || matchDomain(domain, domains);
+ } catch (Exception e) {
+ return false;
+ }
+ }
- private static Pair<String, String> parseOtherName(byte[] otherName) {
- try {
- ASN1Primitive asn1Primitive = ASN1Primitive.fromByteArray(otherName);
- if (asn1Primitive instanceof DERTaggedObject) {
- ASN1Primitive inner = ((DERTaggedObject) asn1Primitive).getObject();
- if (inner instanceof DLSequence) {
- DLSequence sequence = (DLSequence) inner;
- if (sequence.size() >= 2 && sequence.getObjectAt(1) instanceof DERTaggedObject) {
- String oid = sequence.getObjectAt(0).toString();
- ASN1Primitive value = ((DERTaggedObject) sequence.getObjectAt(1)).getObject();
- if (value instanceof DERUTF8String) {
- return new Pair<>(oid, ((DERUTF8String) value).getString());
- } else if (value instanceof DERIA5String) {
- return new Pair<>(oid, ((DERIA5String) value).getString());
- }
- }
- }
- }
- return null;
- } catch (IOException e) {
- return null;
- }
- }
+ private static Pair<String, String> parseOtherName(byte[] otherName) {
+ try {
+ ASN1Primitive asn1Primitive = ASN1Primitive.fromByteArray(otherName);
+ if (asn1Primitive instanceof DERTaggedObject) {
+ ASN1Primitive inner = ((DERTaggedObject) asn1Primitive).getObject();
+ if (inner instanceof DLSequence) {
+ DLSequence sequence = (DLSequence) inner;
+ if (sequence.size() >= 2 && sequence.getObjectAt(1) instanceof DERTaggedObject) {
+ String oid = sequence.getObjectAt(0).toString();
+ ASN1Primitive value = ((DERTaggedObject) sequence.getObjectAt(1)).getObject();
+ if (value instanceof DERUTF8String) {
+ return new Pair<>(oid, ((DERUTF8String) value).getString());
+ } else if (value instanceof DERIA5String) {
+ return new Pair<>(oid, ((DERIA5String) value).getString());
+ }
+ }
+ }
+ }
+ return null;
+ } catch (IOException e) {
+ return null;
+ }
+ }
- private static boolean matchDomain(String needle, List<String> haystack) {
- for (String entry : haystack) {
- if (entry.startsWith("*.")) {
- int i = needle.indexOf('.');
- Log.d(LOGTAG, "comparing " + needle.substring(i) + " and " + entry.substring(1));
- if (i != -1 && needle.substring(i).equals(entry.substring(1))) {
- Log.d(LOGTAG, "domain " + needle + " matched " + entry);
- return true;
- }
- } else {
- if (entry.equals(needle)) {
- Log.d(LOGTAG, "domain " + needle + " matched " + entry);
- return true;
- }
- }
- }
- return false;
- }
+ private static boolean matchDomain(String needle, List<String> haystack) {
+ for (String entry : haystack) {
+ if (entry.startsWith("*.")) {
+ int i = needle.indexOf('.');
+ Log.d(LOGTAG, "comparing " + needle.substring(i) + " and " + entry.substring(1));
+ if (i != -1 && needle.substring(i).equals(entry.substring(1))) {
+ Log.d(LOGTAG, "domain " + needle + " matched " + entry);
+ return true;
+ }
+ } else {
+ if (entry.equals(needle)) {
+ Log.d(LOGTAG, "domain " + needle + " matched " + entry);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java b/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
index 532fbe470..1ed410fdb 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
@@ -51,1049 +51,1049 @@ import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class AxolotlService implements 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";
-
- public static final String LOGPREFIX = "AxolotlService";
-
- public static final int NUM_KEYS_TO_PUBLISH = 100;
- 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 (Config.supportOmemo()
- && 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;
- }
-
- public void preVerifyFingerprint(Contact contact, String fingerprint) {
- axolotlStore.preVerifyFingerprint(contact.getAccount(), contact.getJid().toBareJid().toPreppedString(), fingerprint);
- }
-
- public void preVerifyFingerprint(Account account, String fingerprint) {
- axolotlStore.preVerifyFingerprint(account, account.getJid().toBareJid().toPreppedString(), fingerprint);
- }
-
- 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().toPreppedString());
- putDevicesForJid(account.getJid().toBareJid().toPreppedString(), 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); //TODO why?
- }
-
- public void put(XmppAxolotlSession session) {
- this.put(session.getRemoteAddress(), session);
- }
- }
-
- public enum FetchStatus {
- PENDING,
- SUCCESS,
- SUCCESS_VERIFIED,
- TIMEOUT,
- 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().toPreppedString());
- 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(FingerprintStatus status) {
- return axolotlStore.getContactKeysWithTrust(account.getJid().toBareJid().toPreppedString(), status);
- }
-
- public Set<IdentityKey> getKeysWithTrust(FingerprintStatus status, Jid jid) {
- return axolotlStore.getContactKeysWithTrust(jid.toBareJid().toPreppedString(), status);
- }
-
- public Set<IdentityKey> getKeysWithTrust(FingerprintStatus status, List<Jid> jids) {
- Set<IdentityKey> keys = new HashSet<>();
- for(Jid jid : jids) {
- keys.addAll(axolotlStore.getContactKeysWithTrust(jid.toPreppedString(), status));
- }
- return keys;
- }
-
- public long getNumTrustedKeys(Jid jid) {
- return axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toPreppedString());
- }
-
- public boolean anyTargetHasNoTrustedKeys(List<Jid> jids) {
- for(Jid jid : jids) {
- if (axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toPreppedString()) == 0) {
- return true;
- }
- }
- return false;
- }
-
- private AxolotlAddress getAddressForJid(Jid jid) {
- return new AxolotlAddress(jid.toPreppedString(), 0);
- }
-
- public 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());
- }
-
- 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().toPreppedString(), deviceId);
- if (sessions.get(ownDeviceAddress) == null) {
- buildSessionFromPEP(ownDeviceAddress);
- }
- }
- }
- Set<Integer> expiredDevices = new HashSet<>(axolotlStore.getSubDeviceSessions(jid.toBareJid().toPreppedString()));
- expiredDevices.removeAll(deviceIds);
- for (Integer deviceId : expiredDevices) {
- AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toPreppedString(), deviceId);
- XmppAxolotlSession session = sessions.get(address);
- if (session != null && session.getFingerprint() != null) {
- if (session.getTrust().isActive()) {
- session.setTrust(session.getTrust().toInactive());
- }
- }
- }
- Set<Integer> newDevices = new HashSet<>(deviceIds);
- for (Integer deviceId : newDevices) {
- AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toPreppedString(), deviceId);
- XmppAxolotlSession session = sessions.get(address);
- if (session != null && session.getFingerprint() != null) {
- if (!session.getTrust().isActive()) {
- session.setTrust(session.getTrust().toActive());
- }
- }
- }
- 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.setFingerprintStatus(fingerprint.replaceAll("\\s", ""), FingerprintStatus.createCompromised());
- }
-
- 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"));
- }
- }
- });
- }
-
- public enum AxolotlCapability {
- FULL,
- MISSING_PRESENCE,
- MISSING_KEYS,
- WRONG_CONFIGURATION,
- NO_MEMBERS
- }
-
- public boolean isConversationAxolotlCapable(Conversation conversation) {
- return isConversationAxolotlCapableDetailed(conversation).first == AxolotlCapability.FULL;
- }
-
- public Pair<AxolotlCapability,Jid> isConversationAxolotlCapableDetailed(Conversation conversation) {
- if (conversation.getMode() == Conversation.MODE_SINGLE || (conversation.getMucOptions().membersOnly() && conversation.getMucOptions().nonanonymous())) {
- final List<Jid> jids = getCryptoTargets(conversation);
- for(Jid jid : jids) {
- if (!hasAny(jid) && (!deviceIds.containsKey(jid) || deviceIds.get(jid).isEmpty())) {
- if (conversation.getAccount().getRoster().getContact(jid).mutualPresenceSubscription()) {
- return new Pair<>(AxolotlCapability.MISSING_KEYS,jid);
- } else {
- return new Pair<>(AxolotlCapability.MISSING_PRESENCE,jid);
- }
- }
- }
- if (jids.size() > 0) {
- return new Pair<>(AxolotlCapability.FULL, null);
- } else {
- return new Pair<>(AxolotlCapability.NO_MEMBERS, null);
- }
- } else {
- return new Pair<>(AxolotlCapability.WRONG_CONFIGURATION, null);
- }
- }
-
- 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 FingerprintStatus getFingerprintTrust(String fingerprint) {
- return axolotlStore.getFingerprintStatus(fingerprint);
- }
-
- public X509Certificate getFingerprintCertificate(String fingerprint) {
- return axolotlStore.getFingerprintCertificate(fingerprint);
- }
-
- public void setFingerprintTrust(String fingerprint, FingerprintStatus status) {
- axolotlStore.setFingerprintStatus(fingerprint, status);
- }
-
- 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, FingerprintStatus.createActiveVerified(true));
- 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().toPreppedString(), 0);
- Map<Integer, FetchStatus> own = fetchStatusMap.getAll(ownAddress);
- Map<Integer, FetchStatus> remote = fetchStatusMap.getAll(address);
- if (!own.containsValue(FetchStatus.PENDING) && !remote.containsValue(FetchStatus.PENDING)) {
- FetchStatus report = null;
- if (own.containsValue(FetchStatus.SUCCESS) || remote.containsValue(FetchStatus.SUCCESS)) {
- report = FetchStatus.SUCCESS;
- } else if (own.containsValue(FetchStatus.SUCCESS_VERIFIED) || remote.containsValue(FetchStatus.SUCCESS_VERIFIED)) {
- report = FetchStatus.SUCCESS_VERIFIED;
- } else if (own.containsValue(FetchStatus.ERROR) || remote.containsValue(FetchStatus.ERROR)) {
- report = FetchStatus.ERROR;
- }
- mXmppConnectionService.keyStatusUpdated(report);
- }
- }
-
- 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?!");
- }
-
- 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() {
-
- @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;
- }
-
- 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 {
- FingerprintStatus status = getFingerprintTrust(bundle.getIdentityKey().getFingerprint().replaceAll("\\s",""));
- boolean verified = status != null && status.isVerified();
- fetchStatusMap.put(address, verified ? FetchStatus.SUCCESS_VERIFIED : 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());
- }
- }
-
- 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().toPreppedString(), 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");
- }
- }
- }
- }
- }
-
- return addresses;
- }
-
- 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());
- }
- }
-
- 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().isTrustedAndActive()) {
- if (session.getTrust().getTrust() == FingerprintStatus.Trust.VERIFIED_X509) {
- verified = true;
- } else {
- return false;
- }
- }
- }
- return verified;
- }
-
- public boolean hasPendingKeyFetches(Account account, List<Jid> jids) {
- AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), 0);
- if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)) {
- return true;
- }
- for(Jid jid : jids) {
- AxolotlAddress foreignAddress = new AxolotlAddress(jid.toBareJid().toPreppedString(), 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, 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);
- }
-
- 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) {
- 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);
- }
- }
- });
- }
-
- 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, 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;
- }
-
- 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, 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;
- }
-
- 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");
- }
- }
- }
+ 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";
+
+ public static final String LOGPREFIX = "AxolotlService";
+
+ public static final int NUM_KEYS_TO_PUBLISH = 100;
+ 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 (Config.supportOmemo()
+ && 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;
+ }
+
+ public void preVerifyFingerprint(Contact contact, String fingerprint) {
+ axolotlStore.preVerifyFingerprint(contact.getAccount(), contact.getJid().toBareJid().toPreppedString(), fingerprint);
+ }
+
+ public void preVerifyFingerprint(Account account, String fingerprint) {
+ axolotlStore.preVerifyFingerprint(account, account.getJid().toBareJid().toPreppedString(), fingerprint);
+ }
+
+ 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().toPreppedString());
+ putDevicesForJid(account.getJid().toBareJid().toPreppedString(), 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); //TODO why?
+ }
+
+ public void put(XmppAxolotlSession session) {
+ this.put(session.getRemoteAddress(), session);
+ }
+ }
+
+ public enum FetchStatus {
+ PENDING,
+ SUCCESS,
+ SUCCESS_VERIFIED,
+ TIMEOUT,
+ 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().toPreppedString());
+ 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(FingerprintStatus status) {
+ return axolotlStore.getContactKeysWithTrust(account.getJid().toBareJid().toPreppedString(), status);
+ }
+
+ public Set<IdentityKey> getKeysWithTrust(FingerprintStatus status, Jid jid) {
+ return axolotlStore.getContactKeysWithTrust(jid.toBareJid().toPreppedString(), status);
+ }
+
+ public Set<IdentityKey> getKeysWithTrust(FingerprintStatus status, List<Jid> jids) {
+ Set<IdentityKey> keys = new HashSet<>();
+ for (Jid jid : jids) {
+ keys.addAll(axolotlStore.getContactKeysWithTrust(jid.toPreppedString(), status));
+ }
+ return keys;
+ }
+
+ public long getNumTrustedKeys(Jid jid) {
+ return axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toPreppedString());
+ }
+
+ public boolean anyTargetHasNoTrustedKeys(List<Jid> jids) {
+ for (Jid jid : jids) {
+ if (axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toPreppedString()) == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private AxolotlAddress getAddressForJid(Jid jid) {
+ return new AxolotlAddress(jid.toPreppedString(), 0);
+ }
+
+ public 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());
+ }
+
+ 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().toPreppedString(), deviceId);
+ if (sessions.get(ownDeviceAddress) == null) {
+ buildSessionFromPEP(ownDeviceAddress);
+ }
+ }
+ }
+ Set<Integer> expiredDevices = new HashSet<>(axolotlStore.getSubDeviceSessions(jid.toBareJid().toPreppedString()));
+ expiredDevices.removeAll(deviceIds);
+ for (Integer deviceId : expiredDevices) {
+ AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toPreppedString(), deviceId);
+ XmppAxolotlSession session = sessions.get(address);
+ if (session != null && session.getFingerprint() != null) {
+ if (session.getTrust().isActive()) {
+ session.setTrust(session.getTrust().toInactive());
+ }
+ }
+ }
+ Set<Integer> newDevices = new HashSet<>(deviceIds);
+ for (Integer deviceId : newDevices) {
+ AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toPreppedString(), deviceId);
+ XmppAxolotlSession session = sessions.get(address);
+ if (session != null && session.getFingerprint() != null) {
+ if (!session.getTrust().isActive()) {
+ session.setTrust(session.getTrust().toActive());
+ }
+ }
+ }
+ 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.setFingerprintStatus(fingerprint.replaceAll("\\s", ""), FingerprintStatus.createCompromised());
+ }
+
+ 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"));
+ }
+ }
+ });
+ }
+
+ public enum AxolotlCapability {
+ FULL,
+ MISSING_PRESENCE,
+ MISSING_KEYS,
+ WRONG_CONFIGURATION,
+ NO_MEMBERS
+ }
+
+ public boolean isConversationAxolotlCapable(Conversation conversation) {
+ return isConversationAxolotlCapableDetailed(conversation).first == AxolotlCapability.FULL;
+ }
+
+ public Pair<AxolotlCapability, Jid> isConversationAxolotlCapableDetailed(Conversation conversation) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE || (conversation.getMucOptions().membersOnly() && conversation.getMucOptions().nonanonymous())) {
+ final List<Jid> jids = getCryptoTargets(conversation);
+ for (Jid jid : jids) {
+ if (!hasAny(jid) && (!deviceIds.containsKey(jid) || deviceIds.get(jid).isEmpty())) {
+ if (conversation.getAccount().getRoster().getContact(jid).mutualPresenceSubscription()) {
+ return new Pair<>(AxolotlCapability.MISSING_KEYS, jid);
+ } else {
+ return new Pair<>(AxolotlCapability.MISSING_PRESENCE, jid);
+ }
+ }
+ }
+ if (jids.size() > 0) {
+ return new Pair<>(AxolotlCapability.FULL, null);
+ } else {
+ return new Pair<>(AxolotlCapability.NO_MEMBERS, null);
+ }
+ } else {
+ return new Pair<>(AxolotlCapability.WRONG_CONFIGURATION, null);
+ }
+ }
+
+ 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 FingerprintStatus getFingerprintTrust(String fingerprint) {
+ return axolotlStore.getFingerprintStatus(fingerprint);
+ }
+
+ public X509Certificate getFingerprintCertificate(String fingerprint) {
+ return axolotlStore.getFingerprintCertificate(fingerprint);
+ }
+
+ public void setFingerprintTrust(String fingerprint, FingerprintStatus status) {
+ axolotlStore.setFingerprintStatus(fingerprint, status);
+ }
+
+ 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, FingerprintStatus.createActiveVerified(true));
+ 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().toPreppedString(), 0);
+ Map<Integer, FetchStatus> own = fetchStatusMap.getAll(ownAddress);
+ Map<Integer, FetchStatus> remote = fetchStatusMap.getAll(address);
+ if (!own.containsValue(FetchStatus.PENDING) && !remote.containsValue(FetchStatus.PENDING)) {
+ FetchStatus report = null;
+ if (own.containsValue(FetchStatus.SUCCESS) || remote.containsValue(FetchStatus.SUCCESS)) {
+ report = FetchStatus.SUCCESS;
+ } else if (own.containsValue(FetchStatus.SUCCESS_VERIFIED) || remote.containsValue(FetchStatus.SUCCESS_VERIFIED)) {
+ report = FetchStatus.SUCCESS_VERIFIED;
+ } else if (own.containsValue(FetchStatus.ERROR) || remote.containsValue(FetchStatus.ERROR)) {
+ report = FetchStatus.ERROR;
+ }
+ mXmppConnectionService.keyStatusUpdated(report);
+ }
+ }
+
+ 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?!");
+ }
+
+ 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() {
+
+ @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;
+ }
+
+ 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 {
+ FingerprintStatus status = getFingerprintTrust(bundle.getIdentityKey().getFingerprint().replaceAll("\\s", ""));
+ boolean verified = status != null && status.isVerified();
+ fetchStatusMap.put(address, verified ? FetchStatus.SUCCESS_VERIFIED : 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());
+ }
+ }
+
+ 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().toPreppedString(), 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");
+ }
+ }
+ }
+ }
+ }
+
+ return addresses;
+ }
+
+ 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());
+ }
+ }
+
+ 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().isTrustedAndActive()) {
+ if (session.getTrust().getTrust() == FingerprintStatus.Trust.VERIFIED_X509) {
+ verified = true;
+ } else {
+ return false;
+ }
+ }
+ }
+ return verified;
+ }
+
+ public boolean hasPendingKeyFetches(Account account, List<Jid> jids) {
+ AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), 0);
+ if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)) {
+ return true;
+ }
+ for (Jid jid : jids) {
+ AxolotlAddress foreignAddress = new AxolotlAddress(jid.toBareJid().toPreppedString(), 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, 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);
+ }
+
+ 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) {
+ 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);
+ }
+ }
+ });
+ }
+
+ 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, 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;
+ }
+
+ 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, 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;
+ }
+
+ 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/de/pixart/messenger/crypto/axolotl/CryptoFailedException.java b/src/main/java/de/pixart/messenger/crypto/axolotl/CryptoFailedException.java
index b29d7cb30..da847393b 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/CryptoFailedException.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/CryptoFailedException.java
@@ -1,7 +1,7 @@
package de.pixart.messenger.crypto.axolotl;
public class CryptoFailedException extends Exception {
- public CryptoFailedException(Exception e){
- super(e);
- }
+ public CryptoFailedException(Exception e) {
+ super(e);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java b/src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java
index db67617b9..3be469b5a 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java
@@ -32,8 +32,8 @@ public class FingerprintStatus {
public ContentValues toContentValues() {
final ContentValues contentValues = new ContentValues();
- contentValues.put(SQLiteAxolotlStore.TRUST,trust.toString());
- contentValues.put(SQLiteAxolotlStore.ACTIVE,active ? 1 : 0);
+ contentValues.put(SQLiteAxolotlStore.TRUST, trust.toString());
+ contentValues.put(SQLiteAxolotlStore.ACTIVE, active ? 1 : 0);
return contentValues;
}
@@ -41,7 +41,7 @@ public class FingerprintStatus {
final FingerprintStatus status = new FingerprintStatus();
try {
status.trust = Trust.valueOf(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.TRUST)));
- } catch(IllegalArgumentException e) {
+ } catch (IllegalArgumentException e) {
status.trust = Trust.UNTRUSTED;
}
status.active = cursor.getInt(cursor.getColumnIndex(SQLiteAxolotlStore.ACTIVE)) > 0;
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/NoSessionsCreatedException.java b/src/main/java/de/pixart/messenger/crypto/axolotl/NoSessionsCreatedException.java
index 5d0a7547a..92635a364 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/NoSessionsCreatedException.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/NoSessionsCreatedException.java
@@ -1,4 +1,4 @@
package de.pixart.messenger.crypto.axolotl;
-public class NoSessionsCreatedException extends Throwable{
+public class NoSessionsCreatedException extends Throwable {
}
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/OnMessageCreatedCallback.java b/src/main/java/de/pixart/messenger/crypto/axolotl/OnMessageCreatedCallback.java
index a6daeb196..088831661 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/OnMessageCreatedCallback.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/OnMessageCreatedCallback.java
@@ -1,5 +1,5 @@
package de.pixart.messenger.crypto.axolotl;
public interface OnMessageCreatedCallback {
- void run(XmppAxolotlMessage message);
+ void run(XmppAxolotlMessage message);
}
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/SQLiteAxolotlStore.java b/src/main/java/de/pixart/messenger/crypto/axolotl/SQLiteAxolotlStore.java
index 2980bf7cc..c45e5045e 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/SQLiteAxolotlStore.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/SQLiteAxolotlStore.java
@@ -25,421 +25,421 @@ import de.pixart.messenger.services.XmppConnectionService;
public class SQLiteAxolotlStore implements AxolotlStore {
- public static final String PREKEY_TABLENAME = "prekeys";
- public static final String SIGNED_PREKEY_TABLENAME = "signed_prekeys";
- public static final String SESSION_TABLENAME = "sessions";
- public static final String IDENTITIES_TABLENAME = "identities";
- public static final String ACCOUNT = "account";
- public static final String DEVICE_ID = "device_id";
- public static final String ID = "id";
- public static final String KEY = "key";
- public static final String FINGERPRINT = "fingerprint";
- public static final String NAME = "name";
- public static final String TRUSTED = "trusted"; //no longer used
- public static final String TRUST = "trust";
- public static final String ACTIVE = "active";
- public static final String OWN = "ownkey";
- public static final String CERTIFICATE = "certificate";
-
- public static final String JSONKEY_REGISTRATION_ID = "axolotl_reg_id";
- public static final String JSONKEY_CURRENT_PREKEY_ID = "axolotl_cur_prekey_id";
-
- private static final int NUM_TRUSTS_TO_CACHE = 100;
-
- private final Account account;
- private final XmppConnectionService mXmppConnectionService;
-
- private IdentityKeyPair identityKeyPair;
- private int localRegistrationId;
- private int currentPreKeyId = 0;
-
- private final LruCache<String, FingerprintStatus> trustCache =
- new LruCache<String, FingerprintStatus>(NUM_TRUSTS_TO_CACHE) {
- @Override
- protected FingerprintStatus create(String fingerprint) {
- return mXmppConnectionService.databaseBackend.getFingerprintStatus(account, fingerprint);
- }
- };
-
- private static IdentityKeyPair generateIdentityKeyPair() {
- Log.i(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Generating axolotl IdentityKeyPair...");
- ECKeyPair identityKeyPairKeys = Curve.generateKeyPair();
- return new IdentityKeyPair(new IdentityKey(identityKeyPairKeys.getPublicKey()),
- identityKeyPairKeys.getPrivateKey());
- }
-
- private static int generateRegistrationId() {
- Log.i(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Generating axolotl registration ID...");
- return KeyHelper.generateRegistrationId(true);
- }
-
- public SQLiteAxolotlStore(Account account, XmppConnectionService service) {
- this.account = account;
- this.mXmppConnectionService = service;
- this.localRegistrationId = loadRegistrationId();
- this.currentPreKeyId = loadCurrentPreKeyId();
- for (SignedPreKeyRecord record : loadSignedPreKeys()) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Got Axolotl signed prekey record:" + record.getId());
- }
- }
-
- public int getCurrentPreKeyId() {
- return currentPreKeyId;
- }
-
- // --------------------------------------
- // IdentityKeyStore
- // --------------------------------------
-
- private IdentityKeyPair loadIdentityKeyPair() {
- synchronized (mXmppConnectionService) {
- IdentityKeyPair ownKey = mXmppConnectionService.databaseBackend.loadOwnIdentityKeyPair(account);
-
- if (ownKey != null) {
- return ownKey;
- } else {
- Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Could not retrieve own IdentityKeyPair");
- ownKey = generateIdentityKeyPair();
- mXmppConnectionService.databaseBackend.storeOwnIdentityKeyPair(account, ownKey);
- }
- return ownKey;
- }
- }
-
- private int loadRegistrationId() {
- return loadRegistrationId(false);
- }
-
- private int loadRegistrationId(boolean regenerate) {
- String regIdString = this.account.getKey(JSONKEY_REGISTRATION_ID);
- int reg_id;
- 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());
- 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!");
- }
- }
- return reg_id;
- }
-
- private int loadCurrentPreKeyId() {
- String prekeyIdString = this.account.getKey(JSONKEY_CURRENT_PREKEY_ID);
- int prekey_id;
- 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());
- prekey_id = 0;
- }
- return prekey_id;
- }
-
- public void regenerate() {
- mXmppConnectionService.databaseBackend.wipeAxolotlDb(account);
- trustCache.evictAll();
- account.setKey(JSONKEY_CURRENT_PREKEY_ID, Integer.toString(0));
- identityKeyPair = loadIdentityKeyPair();
- localRegistrationId = loadRegistrationId(true);
- currentPreKeyId = 0;
- mXmppConnectionService.updateAccountUi();
- }
-
- /**
- * Get the local client's identity key pair.
- *
- * @return The local client's persistent identity key pair.
- */
- @Override
- public IdentityKeyPair getIdentityKeyPair() {
- if (identityKeyPair == null) {
- identityKeyPair = loadIdentityKeyPair();
- }
- return identityKeyPair;
- }
-
- /**
- * Return the local client's registration ID.
- * <p/>
- * Clients should maintain a registration ID, a random number
- * between 1 and 16380 that's generated once at install time.
- *
- * @return the local client's registration ID.
- */
- @Override
- public int getLocalRegistrationId() {
- return localRegistrationId;
- }
-
- /**
- * Save a remote client's identity key
- * <p/>
- * Store a remote client's identity key as trusted.
- *
- * @param name The name of the remote client.
- * @param identityKey The remote client's identity key.
- */
- @Override
- public void saveIdentity(String name, IdentityKey identityKey) {
- if (!mXmppConnectionService.databaseBackend.loadIdentityKeys(account, name).contains(identityKey)) {
- String fingerprint = identityKey.getFingerprint().replaceAll("\\s", "");
- FingerprintStatus status = getFingerprintStatus(fingerprint);
- if (status == null) {
- status = FingerprintStatus.createActiveUndecided(); //default for new keys
- } else {
- status = status.toActive();
- }
- mXmppConnectionService.databaseBackend.storeIdentityKey(account, name, identityKey, status);
- trustCache.remove(fingerprint);
- }
- }
-
- /**
- * Verify a remote client's identity key.
- * <p/>
- * Determine whether a remote client's identity is trusted. Convention is
- * that the TextSecure protocol is 'trust on first use.' This means that
- * an identity key is considered 'trusted' if there is no entry for the recipient
- * in the local store, or if it matches the saved key for a recipient in the local
- * store. Only if it mismatches an entry in the local store is it considered
- * 'untrusted.'
- *
- * @param name The name of the remote client.
- * @param identityKey The identity key to verify.
- * @return true if trusted, false if untrusted.
- */
- @Override
- public boolean isTrustedIdentity(String name, IdentityKey identityKey) {
- return true;
- }
-
- public FingerprintStatus getFingerprintStatus(String fingerprint) {
- return (fingerprint == null)? null : trustCache.get(fingerprint);
- }
-
- public void setFingerprintStatus(String fingerprint, FingerprintStatus status) {
- mXmppConnectionService.databaseBackend.setIdentityKeyTrust(account, fingerprint, status);
- trustCache.remove(fingerprint);
- }
-
- public void setFingerprintCertificate(String fingerprint, X509Certificate x509Certificate) {
- mXmppConnectionService.databaseBackend.setIdentityKeyCertificate(account, fingerprint, x509Certificate);
- }
-
- public X509Certificate getFingerprintCertificate(String fingerprint) {
- return mXmppConnectionService.databaseBackend.getIdentityKeyCertifcate(account, fingerprint);
- }
-
- public Set<IdentityKey> getContactKeysWithTrust(String bareJid, FingerprintStatus status) {
- return mXmppConnectionService.databaseBackend.loadIdentityKeys(account, bareJid, status);
- }
-
- public long getContactNumTrustedKeys(String bareJid) {
- return mXmppConnectionService.databaseBackend.numTrustedKeys(account, bareJid);
- }
-
- // --------------------------------------
- // SessionStore
- // --------------------------------------
-
- /**
- * Returns a copy of the {@link SessionRecord} corresponding to the recipientId + deviceId tuple,
- * or a new SessionRecord if one does not currently exist.
- * <p/>
- * It is important that implementations return a copy of the current durable information. The
- * returned SessionRecord may be modified, but those changes should not have an effect on the
- * durable session state (what is returned by subsequent calls to this method) without the
- * store method being called here first.
- *
- * @param address The name and device ID of the remote client.
- * @return a copy of the SessionRecord corresponding to the recipientId + deviceId tuple, or
- * a new SessionRecord if one does not currently exist.
- */
- @Override
- public SessionRecord loadSession(AxolotlAddress address) {
- SessionRecord session = mXmppConnectionService.databaseBackend.loadSession(this.account, address);
- return (session != null) ? session : new SessionRecord();
- }
-
- /**
- * Returns all known devices with active sessions for a recipient
- *
- * @param name the name of the client.
- * @return all known sub-devices with active sessions.
- */
- @Override
- public List<Integer> getSubDeviceSessions(String name) {
- return mXmppConnectionService.databaseBackend.getSubDeviceSessions(account,
- new AxolotlAddress(name, 0));
- }
-
- /**
- * Commit to storage the {@link SessionRecord} for a given recipientId + deviceId tuple.
- *
- * @param address the address of the remote client.
- * @param record the current SessionRecord for the remote client.
- */
- @Override
- public void storeSession(AxolotlAddress address, SessionRecord record) {
- mXmppConnectionService.databaseBackend.storeSession(account, address, record);
- }
-
- /**
- * Determine whether there is a committed {@link SessionRecord} for a recipientId + deviceId tuple.
- *
- * @param address the address of the remote client.
- * @return true if a {@link SessionRecord} exists, false otherwise.
- */
- @Override
- public boolean containsSession(AxolotlAddress address) {
- return mXmppConnectionService.databaseBackend.containsSession(account, address);
- }
-
- /**
- * Remove a {@link SessionRecord} for a recipientId + deviceId tuple.
- *
- * @param address the address of the remote client.
- */
- @Override
- public void deleteSession(AxolotlAddress address) {
- mXmppConnectionService.databaseBackend.deleteSession(account, address);
- }
-
- /**
- * Remove the {@link SessionRecord}s corresponding to all devices of a recipientId.
- *
- * @param name the name of the remote client.
- */
- @Override
- public void deleteAllSessions(String name) {
- AxolotlAddress address = new AxolotlAddress(name, 0);
- mXmppConnectionService.databaseBackend.deleteAllSessions(account,
- address);
- }
-
- // --------------------------------------
- // PreKeyStore
- // --------------------------------------
-
- /**
- * Load a local PreKeyRecord.
- *
- * @param preKeyId the ID of the local PreKeyRecord.
- * @return the corresponding PreKeyRecord.
- * @throws InvalidKeyIdException when there is no corresponding PreKeyRecord.
- */
- @Override
- public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
- PreKeyRecord record = mXmppConnectionService.databaseBackend.loadPreKey(account, preKeyId);
- if (record == null) {
- throw new InvalidKeyIdException("No such PreKeyRecord: " + preKeyId);
- }
- return record;
- }
-
- /**
- * Store a local PreKeyRecord.
- *
- * @param preKeyId the ID of the PreKeyRecord to store.
- * @param record the PreKeyRecord.
- */
- @Override
- public void storePreKey(int preKeyId, PreKeyRecord record) {
- mXmppConnectionService.databaseBackend.storePreKey(account, record);
- currentPreKeyId = preKeyId;
- boolean success = this.account.setKey(JSONKEY_CURRENT_PREKEY_ID, Integer.toString(preKeyId));
- if (success) {
- mXmppConnectionService.databaseBackend.updateAccount(account);
- } else {
- Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to write new prekey id to the database!");
- }
- }
-
- /**
- * @param preKeyId A PreKeyRecord ID.
- * @return true if the store has a record for the preKeyId, otherwise false.
- */
- @Override
- public boolean containsPreKey(int preKeyId) {
- return mXmppConnectionService.databaseBackend.containsPreKey(account, preKeyId);
- }
-
- /**
- * Delete a PreKeyRecord from local storage.
- *
- * @param preKeyId The ID of the PreKeyRecord to remove.
- */
- @Override
- public void removePreKey(int preKeyId) {
- mXmppConnectionService.databaseBackend.deletePreKey(account, preKeyId);
- }
-
- // --------------------------------------
- // SignedPreKeyStore
- // --------------------------------------
-
- /**
- * Load a local SignedPreKeyRecord.
- *
- * @param signedPreKeyId the ID of the local SignedPreKeyRecord.
- * @return the corresponding SignedPreKeyRecord.
- * @throws InvalidKeyIdException when there is no corresponding SignedPreKeyRecord.
- */
- @Override
- public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
- SignedPreKeyRecord record = mXmppConnectionService.databaseBackend.loadSignedPreKey(account, signedPreKeyId);
- if (record == null) {
- throw new InvalidKeyIdException("No such SignedPreKeyRecord: " + signedPreKeyId);
- }
- return record;
- }
-
- /**
- * Load all local SignedPreKeyRecords.
- *
- * @return All stored SignedPreKeyRecords.
- */
- @Override
- public List<SignedPreKeyRecord> loadSignedPreKeys() {
- return mXmppConnectionService.databaseBackend.loadSignedPreKeys(account);
- }
-
- /**
- * Store a local SignedPreKeyRecord.
- *
- * @param signedPreKeyId the ID of the SignedPreKeyRecord to store.
- * @param record the SignedPreKeyRecord.
- */
- @Override
- public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) {
- mXmppConnectionService.databaseBackend.storeSignedPreKey(account, record);
- }
-
- /**
- * @param signedPreKeyId A SignedPreKeyRecord ID.
- * @return true if the store has a record for the signedPreKeyId, otherwise false.
- */
- @Override
- public boolean containsSignedPreKey(int signedPreKeyId) {
- return mXmppConnectionService.databaseBackend.containsSignedPreKey(account, signedPreKeyId);
- }
-
- /**
- * Delete a SignedPreKeyRecord from local storage.
- *
- * @param signedPreKeyId The ID of the SignedPreKeyRecord to remove.
- */
- @Override
- public void removeSignedPreKey(int signedPreKeyId) {
- mXmppConnectionService.databaseBackend.deleteSignedPreKey(account, signedPreKeyId);
- }
-
- public void preVerifyFingerprint(Account account, String name, String fingerprint) {
- mXmppConnectionService.databaseBackend.storePreVerification(account,name,fingerprint,FingerprintStatus.createInactiveVerified());
- }
+ public static final String PREKEY_TABLENAME = "prekeys";
+ public static final String SIGNED_PREKEY_TABLENAME = "signed_prekeys";
+ public static final String SESSION_TABLENAME = "sessions";
+ public static final String IDENTITIES_TABLENAME = "identities";
+ public static final String ACCOUNT = "account";
+ public static final String DEVICE_ID = "device_id";
+ public static final String ID = "id";
+ public static final String KEY = "key";
+ public static final String FINGERPRINT = "fingerprint";
+ public static final String NAME = "name";
+ public static final String TRUSTED = "trusted"; //no longer used
+ public static final String TRUST = "trust";
+ public static final String ACTIVE = "active";
+ public static final String OWN = "ownkey";
+ public static final String CERTIFICATE = "certificate";
+
+ public static final String JSONKEY_REGISTRATION_ID = "axolotl_reg_id";
+ public static final String JSONKEY_CURRENT_PREKEY_ID = "axolotl_cur_prekey_id";
+
+ private static final int NUM_TRUSTS_TO_CACHE = 100;
+
+ private final Account account;
+ private final XmppConnectionService mXmppConnectionService;
+
+ private IdentityKeyPair identityKeyPair;
+ private int localRegistrationId;
+ private int currentPreKeyId = 0;
+
+ private final LruCache<String, FingerprintStatus> trustCache =
+ new LruCache<String, FingerprintStatus>(NUM_TRUSTS_TO_CACHE) {
+ @Override
+ protected FingerprintStatus create(String fingerprint) {
+ return mXmppConnectionService.databaseBackend.getFingerprintStatus(account, fingerprint);
+ }
+ };
+
+ private static IdentityKeyPair generateIdentityKeyPair() {
+ Log.i(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Generating axolotl IdentityKeyPair...");
+ ECKeyPair identityKeyPairKeys = Curve.generateKeyPair();
+ return new IdentityKeyPair(new IdentityKey(identityKeyPairKeys.getPublicKey()),
+ identityKeyPairKeys.getPrivateKey());
+ }
+
+ private static int generateRegistrationId() {
+ Log.i(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Generating axolotl registration ID...");
+ return KeyHelper.generateRegistrationId(true);
+ }
+
+ public SQLiteAxolotlStore(Account account, XmppConnectionService service) {
+ this.account = account;
+ this.mXmppConnectionService = service;
+ this.localRegistrationId = loadRegistrationId();
+ this.currentPreKeyId = loadCurrentPreKeyId();
+ for (SignedPreKeyRecord record : loadSignedPreKeys()) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Got Axolotl signed prekey record:" + record.getId());
+ }
+ }
+
+ public int getCurrentPreKeyId() {
+ return currentPreKeyId;
+ }
+
+ // --------------------------------------
+ // IdentityKeyStore
+ // --------------------------------------
+
+ private IdentityKeyPair loadIdentityKeyPair() {
+ synchronized (mXmppConnectionService) {
+ IdentityKeyPair ownKey = mXmppConnectionService.databaseBackend.loadOwnIdentityKeyPair(account);
+
+ if (ownKey != null) {
+ return ownKey;
+ } else {
+ Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Could not retrieve own IdentityKeyPair");
+ ownKey = generateIdentityKeyPair();
+ mXmppConnectionService.databaseBackend.storeOwnIdentityKeyPair(account, ownKey);
+ }
+ return ownKey;
+ }
+ }
+
+ private int loadRegistrationId() {
+ return loadRegistrationId(false);
+ }
+
+ private int loadRegistrationId(boolean regenerate) {
+ String regIdString = this.account.getKey(JSONKEY_REGISTRATION_ID);
+ int reg_id;
+ 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());
+ 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!");
+ }
+ }
+ return reg_id;
+ }
+
+ private int loadCurrentPreKeyId() {
+ String prekeyIdString = this.account.getKey(JSONKEY_CURRENT_PREKEY_ID);
+ int prekey_id;
+ 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());
+ prekey_id = 0;
+ }
+ return prekey_id;
+ }
+
+ public void regenerate() {
+ mXmppConnectionService.databaseBackend.wipeAxolotlDb(account);
+ trustCache.evictAll();
+ account.setKey(JSONKEY_CURRENT_PREKEY_ID, Integer.toString(0));
+ identityKeyPair = loadIdentityKeyPair();
+ localRegistrationId = loadRegistrationId(true);
+ currentPreKeyId = 0;
+ mXmppConnectionService.updateAccountUi();
+ }
+
+ /**
+ * Get the local client's identity key pair.
+ *
+ * @return The local client's persistent identity key pair.
+ */
+ @Override
+ public IdentityKeyPair getIdentityKeyPair() {
+ if (identityKeyPair == null) {
+ identityKeyPair = loadIdentityKeyPair();
+ }
+ return identityKeyPair;
+ }
+
+ /**
+ * Return the local client's registration ID.
+ * <p/>
+ * Clients should maintain a registration ID, a random number
+ * between 1 and 16380 that's generated once at install time.
+ *
+ * @return the local client's registration ID.
+ */
+ @Override
+ public int getLocalRegistrationId() {
+ return localRegistrationId;
+ }
+
+ /**
+ * Save a remote client's identity key
+ * <p/>
+ * Store a remote client's identity key as trusted.
+ *
+ * @param name The name of the remote client.
+ * @param identityKey The remote client's identity key.
+ */
+ @Override
+ public void saveIdentity(String name, IdentityKey identityKey) {
+ if (!mXmppConnectionService.databaseBackend.loadIdentityKeys(account, name).contains(identityKey)) {
+ String fingerprint = identityKey.getFingerprint().replaceAll("\\s", "");
+ FingerprintStatus status = getFingerprintStatus(fingerprint);
+ if (status == null) {
+ status = FingerprintStatus.createActiveUndecided(); //default for new keys
+ } else {
+ status = status.toActive();
+ }
+ mXmppConnectionService.databaseBackend.storeIdentityKey(account, name, identityKey, status);
+ trustCache.remove(fingerprint);
+ }
+ }
+
+ /**
+ * Verify a remote client's identity key.
+ * <p/>
+ * Determine whether a remote client's identity is trusted. Convention is
+ * that the TextSecure protocol is 'trust on first use.' This means that
+ * an identity key is considered 'trusted' if there is no entry for the recipient
+ * in the local store, or if it matches the saved key for a recipient in the local
+ * store. Only if it mismatches an entry in the local store is it considered
+ * 'untrusted.'
+ *
+ * @param name The name of the remote client.
+ * @param identityKey The identity key to verify.
+ * @return true if trusted, false if untrusted.
+ */
+ @Override
+ public boolean isTrustedIdentity(String name, IdentityKey identityKey) {
+ return true;
+ }
+
+ public FingerprintStatus getFingerprintStatus(String fingerprint) {
+ return (fingerprint == null) ? null : trustCache.get(fingerprint);
+ }
+
+ public void setFingerprintStatus(String fingerprint, FingerprintStatus status) {
+ mXmppConnectionService.databaseBackend.setIdentityKeyTrust(account, fingerprint, status);
+ trustCache.remove(fingerprint);
+ }
+
+ public void setFingerprintCertificate(String fingerprint, X509Certificate x509Certificate) {
+ mXmppConnectionService.databaseBackend.setIdentityKeyCertificate(account, fingerprint, x509Certificate);
+ }
+
+ public X509Certificate getFingerprintCertificate(String fingerprint) {
+ return mXmppConnectionService.databaseBackend.getIdentityKeyCertifcate(account, fingerprint);
+ }
+
+ public Set<IdentityKey> getContactKeysWithTrust(String bareJid, FingerprintStatus status) {
+ return mXmppConnectionService.databaseBackend.loadIdentityKeys(account, bareJid, status);
+ }
+
+ public long getContactNumTrustedKeys(String bareJid) {
+ return mXmppConnectionService.databaseBackend.numTrustedKeys(account, bareJid);
+ }
+
+ // --------------------------------------
+ // SessionStore
+ // --------------------------------------
+
+ /**
+ * Returns a copy of the {@link SessionRecord} corresponding to the recipientId + deviceId tuple,
+ * or a new SessionRecord if one does not currently exist.
+ * <p/>
+ * It is important that implementations return a copy of the current durable information. The
+ * returned SessionRecord may be modified, but those changes should not have an effect on the
+ * durable session state (what is returned by subsequent calls to this method) without the
+ * store method being called here first.
+ *
+ * @param address The name and device ID of the remote client.
+ * @return a copy of the SessionRecord corresponding to the recipientId + deviceId tuple, or
+ * a new SessionRecord if one does not currently exist.
+ */
+ @Override
+ public SessionRecord loadSession(AxolotlAddress address) {
+ SessionRecord session = mXmppConnectionService.databaseBackend.loadSession(this.account, address);
+ return (session != null) ? session : new SessionRecord();
+ }
+
+ /**
+ * Returns all known devices with active sessions for a recipient
+ *
+ * @param name the name of the client.
+ * @return all known sub-devices with active sessions.
+ */
+ @Override
+ public List<Integer> getSubDeviceSessions(String name) {
+ return mXmppConnectionService.databaseBackend.getSubDeviceSessions(account,
+ new AxolotlAddress(name, 0));
+ }
+
+ /**
+ * Commit to storage the {@link SessionRecord} for a given recipientId + deviceId tuple.
+ *
+ * @param address the address of the remote client.
+ * @param record the current SessionRecord for the remote client.
+ */
+ @Override
+ public void storeSession(AxolotlAddress address, SessionRecord record) {
+ mXmppConnectionService.databaseBackend.storeSession(account, address, record);
+ }
+
+ /**
+ * Determine whether there is a committed {@link SessionRecord} for a recipientId + deviceId tuple.
+ *
+ * @param address the address of the remote client.
+ * @return true if a {@link SessionRecord} exists, false otherwise.
+ */
+ @Override
+ public boolean containsSession(AxolotlAddress address) {
+ return mXmppConnectionService.databaseBackend.containsSession(account, address);
+ }
+
+ /**
+ * Remove a {@link SessionRecord} for a recipientId + deviceId tuple.
+ *
+ * @param address the address of the remote client.
+ */
+ @Override
+ public void deleteSession(AxolotlAddress address) {
+ mXmppConnectionService.databaseBackend.deleteSession(account, address);
+ }
+
+ /**
+ * Remove the {@link SessionRecord}s corresponding to all devices of a recipientId.
+ *
+ * @param name the name of the remote client.
+ */
+ @Override
+ public void deleteAllSessions(String name) {
+ AxolotlAddress address = new AxolotlAddress(name, 0);
+ mXmppConnectionService.databaseBackend.deleteAllSessions(account,
+ address);
+ }
+
+ // --------------------------------------
+ // PreKeyStore
+ // --------------------------------------
+
+ /**
+ * Load a local PreKeyRecord.
+ *
+ * @param preKeyId the ID of the local PreKeyRecord.
+ * @return the corresponding PreKeyRecord.
+ * @throws InvalidKeyIdException when there is no corresponding PreKeyRecord.
+ */
+ @Override
+ public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
+ PreKeyRecord record = mXmppConnectionService.databaseBackend.loadPreKey(account, preKeyId);
+ if (record == null) {
+ throw new InvalidKeyIdException("No such PreKeyRecord: " + preKeyId);
+ }
+ return record;
+ }
+
+ /**
+ * Store a local PreKeyRecord.
+ *
+ * @param preKeyId the ID of the PreKeyRecord to store.
+ * @param record the PreKeyRecord.
+ */
+ @Override
+ public void storePreKey(int preKeyId, PreKeyRecord record) {
+ mXmppConnectionService.databaseBackend.storePreKey(account, record);
+ currentPreKeyId = preKeyId;
+ boolean success = this.account.setKey(JSONKEY_CURRENT_PREKEY_ID, Integer.toString(preKeyId));
+ if (success) {
+ mXmppConnectionService.databaseBackend.updateAccount(account);
+ } else {
+ Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to write new prekey id to the database!");
+ }
+ }
+
+ /**
+ * @param preKeyId A PreKeyRecord ID.
+ * @return true if the store has a record for the preKeyId, otherwise false.
+ */
+ @Override
+ public boolean containsPreKey(int preKeyId) {
+ return mXmppConnectionService.databaseBackend.containsPreKey(account, preKeyId);
+ }
+
+ /**
+ * Delete a PreKeyRecord from local storage.
+ *
+ * @param preKeyId The ID of the PreKeyRecord to remove.
+ */
+ @Override
+ public void removePreKey(int preKeyId) {
+ mXmppConnectionService.databaseBackend.deletePreKey(account, preKeyId);
+ }
+
+ // --------------------------------------
+ // SignedPreKeyStore
+ // --------------------------------------
+
+ /**
+ * Load a local SignedPreKeyRecord.
+ *
+ * @param signedPreKeyId the ID of the local SignedPreKeyRecord.
+ * @return the corresponding SignedPreKeyRecord.
+ * @throws InvalidKeyIdException when there is no corresponding SignedPreKeyRecord.
+ */
+ @Override
+ public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
+ SignedPreKeyRecord record = mXmppConnectionService.databaseBackend.loadSignedPreKey(account, signedPreKeyId);
+ if (record == null) {
+ throw new InvalidKeyIdException("No such SignedPreKeyRecord: " + signedPreKeyId);
+ }
+ return record;
+ }
+
+ /**
+ * Load all local SignedPreKeyRecords.
+ *
+ * @return All stored SignedPreKeyRecords.
+ */
+ @Override
+ public List<SignedPreKeyRecord> loadSignedPreKeys() {
+ return mXmppConnectionService.databaseBackend.loadSignedPreKeys(account);
+ }
+
+ /**
+ * Store a local SignedPreKeyRecord.
+ *
+ * @param signedPreKeyId the ID of the SignedPreKeyRecord to store.
+ * @param record the SignedPreKeyRecord.
+ */
+ @Override
+ public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) {
+ mXmppConnectionService.databaseBackend.storeSignedPreKey(account, record);
+ }
+
+ /**
+ * @param signedPreKeyId A SignedPreKeyRecord ID.
+ * @return true if the store has a record for the signedPreKeyId, otherwise false.
+ */
+ @Override
+ public boolean containsSignedPreKey(int signedPreKeyId) {
+ return mXmppConnectionService.databaseBackend.containsSignedPreKey(account, signedPreKeyId);
+ }
+
+ /**
+ * Delete a SignedPreKeyRecord from local storage.
+ *
+ * @param signedPreKeyId The ID of the SignedPreKeyRecord to remove.
+ */
+ @Override
+ public void removeSignedPreKey(int signedPreKeyId) {
+ mXmppConnectionService.databaseBackend.deleteSignedPreKey(account, signedPreKeyId);
+ }
+
+ public void preVerifyFingerprint(Account account, String name, String fingerprint) {
+ mXmppConnectionService.databaseBackend.storePreVerification(account, name, fingerprint, FingerprintStatus.createInactiveVerified());
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
index 431f81d9b..f49eefb5a 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
@@ -26,227 +26,227 @@ import de.pixart.messenger.xml.Element;
import de.pixart.messenger.xmpp.jid.Jid;
public class XmppAxolotlMessage {
- public static final String CONTAINERTAG = "encrypted";
- public static final String HEADER = "header";
- public static final String SOURCEID = "sid";
- public static final String KEYTAG = "key";
- public static final String REMOTEID = "rid";
- public static final String IVTAG = "iv";
- public static final String PAYLOAD = "payload";
-
- private static final String KEYTYPE = "AES";
- private static final String CIPHERMODE = "AES/GCM/NoPadding";
- private static final String PROVIDER = "BC";
-
- private byte[] innerKey;
- private byte[] ciphertext = null;
- private byte[] iv = null;
- private final Map<Integer, byte[]> keys;
- private final Jid from;
- private final int sourceDeviceId;
-
- public static class XmppAxolotlPlaintextMessage {
- private final String plaintext;
- private final String fingerprint;
-
- public XmppAxolotlPlaintextMessage(String plaintext, String fingerprint) {
- this.plaintext = plaintext;
- this.fingerprint = fingerprint;
- }
-
- public String getPlaintext() {
- return plaintext;
- }
-
-
- public String getFingerprint() {
- return fingerprint;
- }
- }
-
- public static class XmppAxolotlKeyTransportMessage {
- private final String fingerprint;
- private final byte[] key;
- private final byte[] iv;
-
- public XmppAxolotlKeyTransportMessage(String fingerprint, byte[] key, byte[] iv) {
- this.fingerprint = fingerprint;
- this.key = key;
- this.iv = iv;
- }
-
- public String getFingerprint() {
- return fingerprint;
- }
-
- public byte[] getKey() {
- return key;
- }
-
- public byte[] getIv() {
- return iv;
- }
- }
-
- private XmppAxolotlMessage(final Element axolotlMessage, final Jid from) throws IllegalArgumentException {
- this.from = from;
- Element header = axolotlMessage.findChild(HEADER);
- try {
- this.sourceDeviceId = Integer.parseInt(header.getAttribute(SOURCEID));
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("invalid source id");
- }
- List<Element> keyElements = header.getChildren();
- this.keys = new HashMap<>(keyElements.size());
- for (Element keyElement : keyElements) {
- switch (keyElement.getName()) {
- case KEYTAG:
- try {
- Integer recipientId = Integer.parseInt(keyElement.getAttribute(REMOTEID));
- byte[] key = Base64.decode(keyElement.getContent().trim(), Base64.DEFAULT);
- this.keys.put(recipientId, key);
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("invalid remote id");
- }
- break;
- case IVTAG:
- if (this.iv != null) {
- throw new IllegalArgumentException("Duplicate iv entry");
- }
- iv = Base64.decode(keyElement.getContent().trim(), Base64.DEFAULT);
- break;
- default:
- Log.w(Config.LOGTAG, "Unexpected element in header: " + keyElement.toString());
- break;
- }
- }
- Element payloadElement = axolotlMessage.findChild(PAYLOAD);
- if (payloadElement != null) {
- ciphertext = Base64.decode(payloadElement.getContent().trim(), Base64.DEFAULT);
- }
- }
-
- public XmppAxolotlMessage(Jid from, int sourceDeviceId) {
- this.from = from;
- this.sourceDeviceId = sourceDeviceId;
- this.keys = new HashMap<>();
- this.iv = generateIv();
- this.innerKey = generateKey();
- }
-
- public static XmppAxolotlMessage fromElement(Element element, Jid from) {
- return new XmppAxolotlMessage(element, from);
- }
-
- private static byte[] generateKey() {
- try {
- KeyGenerator generator = KeyGenerator.getInstance(KEYTYPE);
- generator.init(128);
- return generator.generateKey().getEncoded();
- } catch (NoSuchAlgorithmException e) {
- Log.e(Config.LOGTAG, e.getMessage());
- return null;
- }
- }
-
- private static byte[] generateIv() {
- SecureRandom random = new SecureRandom();
- byte[] iv = new byte[16];
- random.nextBytes(iv);
- return iv;
- }
-
- public void encrypt(String plaintext) throws CryptoFailedException {
- try {
- SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE);
- IvParameterSpec ivSpec = new IvParameterSpec(iv);
- Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
- cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
- this.ciphertext = cipher.doFinal(plaintext.getBytes());
- } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
- | IllegalBlockSizeException | BadPaddingException | NoSuchProviderException
- | InvalidAlgorithmParameterException e) {
- throw new CryptoFailedException(e);
- }
- }
-
- public Jid getFrom() {
- return this.from;
- }
-
- public int getSenderDeviceId() {
- return sourceDeviceId;
- }
-
- public byte[] getCiphertext() {
- return ciphertext;
- }
-
- public void addDevice(XmppAxolotlSession session) {
- byte[] key = session.processSending(innerKey);
- if (key != null) {
- keys.put(session.getRemoteAddress().getDeviceId(), key);
- }
- }
-
- public byte[] getInnerKey() {
- return innerKey;
- }
-
- public byte[] getIV() {
- return this.iv;
- }
-
- public Element toElement() {
- Element encryptionElement = new Element(CONTAINERTAG, AxolotlService.PEP_PREFIX);
- Element headerElement = encryptionElement.addChild(HEADER);
- headerElement.setAttribute(SOURCEID, sourceDeviceId);
- for (Map.Entry<Integer, byte[]> keyEntry : keys.entrySet()) {
- Element keyElement = new Element(KEYTAG);
- keyElement.setAttribute(REMOTEID, keyEntry.getKey());
- keyElement.setContent(Base64.encodeToString(keyEntry.getValue(), Base64.NO_WRAP));
- headerElement.addChild(keyElement);
- }
- headerElement.addChild(IVTAG).setContent(Base64.encodeToString(iv, Base64.NO_WRAP));
- if (ciphertext != null) {
- Element payload = encryptionElement.addChild(PAYLOAD);
- payload.setContent(Base64.encodeToString(ciphertext, Base64.NO_WRAP));
- }
- return encryptionElement;
- }
-
- private byte[] unpackKey(XmppAxolotlSession session, Integer sourceDeviceId) {
- byte[] encryptedKey = keys.get(sourceDeviceId);
- return (encryptedKey != null) ? session.processReceiving(encryptedKey) : null;
- }
-
- public XmppAxolotlKeyTransportMessage getParameters(XmppAxolotlSession session, Integer sourceDeviceId) {
- byte[] key = unpackKey(session, sourceDeviceId);
- return (key != null)
- ? new XmppAxolotlKeyTransportMessage(session.getFingerprint(), key, getIV())
- : null;
- }
-
- public XmppAxolotlPlaintextMessage decrypt(XmppAxolotlSession session, Integer sourceDeviceId) throws CryptoFailedException {
- XmppAxolotlPlaintextMessage plaintextMessage = null;
- byte[] key = unpackKey(session, sourceDeviceId);
- if (key != null) {
- try {
- Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
- SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
- IvParameterSpec ivSpec = new IvParameterSpec(iv);
-
- cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
-
- String plaintext = new String(cipher.doFinal(ciphertext));
- plaintextMessage = new XmppAxolotlPlaintextMessage(plaintext, session.getFingerprint());
-
- } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
- | InvalidAlgorithmParameterException | IllegalBlockSizeException
- | BadPaddingException | NoSuchProviderException e) {
- throw new CryptoFailedException(e);
- }
- }
- return plaintextMessage;
- }
+ public static final String CONTAINERTAG = "encrypted";
+ public static final String HEADER = "header";
+ public static final String SOURCEID = "sid";
+ public static final String KEYTAG = "key";
+ public static final String REMOTEID = "rid";
+ public static final String IVTAG = "iv";
+ public static final String PAYLOAD = "payload";
+
+ private static final String KEYTYPE = "AES";
+ private static final String CIPHERMODE = "AES/GCM/NoPadding";
+ private static final String PROVIDER = "BC";
+
+ private byte[] innerKey;
+ private byte[] ciphertext = null;
+ private byte[] iv = null;
+ private final Map<Integer, byte[]> keys;
+ private final Jid from;
+ private final int sourceDeviceId;
+
+ public static class XmppAxolotlPlaintextMessage {
+ private final String plaintext;
+ private final String fingerprint;
+
+ public XmppAxolotlPlaintextMessage(String plaintext, String fingerprint) {
+ this.plaintext = plaintext;
+ this.fingerprint = fingerprint;
+ }
+
+ public String getPlaintext() {
+ return plaintext;
+ }
+
+
+ public String getFingerprint() {
+ return fingerprint;
+ }
+ }
+
+ public static class XmppAxolotlKeyTransportMessage {
+ private final String fingerprint;
+ private final byte[] key;
+ private final byte[] iv;
+
+ public XmppAxolotlKeyTransportMessage(String fingerprint, byte[] key, byte[] iv) {
+ this.fingerprint = fingerprint;
+ this.key = key;
+ this.iv = iv;
+ }
+
+ public String getFingerprint() {
+ return fingerprint;
+ }
+
+ public byte[] getKey() {
+ return key;
+ }
+
+ public byte[] getIv() {
+ return iv;
+ }
+ }
+
+ private XmppAxolotlMessage(final Element axolotlMessage, final Jid from) throws IllegalArgumentException {
+ this.from = from;
+ Element header = axolotlMessage.findChild(HEADER);
+ try {
+ this.sourceDeviceId = Integer.parseInt(header.getAttribute(SOURCEID));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("invalid source id");
+ }
+ List<Element> keyElements = header.getChildren();
+ this.keys = new HashMap<>(keyElements.size());
+ for (Element keyElement : keyElements) {
+ switch (keyElement.getName()) {
+ case KEYTAG:
+ try {
+ Integer recipientId = Integer.parseInt(keyElement.getAttribute(REMOTEID));
+ byte[] key = Base64.decode(keyElement.getContent().trim(), Base64.DEFAULT);
+ this.keys.put(recipientId, key);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("invalid remote id");
+ }
+ break;
+ case IVTAG:
+ if (this.iv != null) {
+ throw new IllegalArgumentException("Duplicate iv entry");
+ }
+ iv = Base64.decode(keyElement.getContent().trim(), Base64.DEFAULT);
+ break;
+ default:
+ Log.w(Config.LOGTAG, "Unexpected element in header: " + keyElement.toString());
+ break;
+ }
+ }
+ Element payloadElement = axolotlMessage.findChild(PAYLOAD);
+ if (payloadElement != null) {
+ ciphertext = Base64.decode(payloadElement.getContent().trim(), Base64.DEFAULT);
+ }
+ }
+
+ public XmppAxolotlMessage(Jid from, int sourceDeviceId) {
+ this.from = from;
+ this.sourceDeviceId = sourceDeviceId;
+ this.keys = new HashMap<>();
+ this.iv = generateIv();
+ this.innerKey = generateKey();
+ }
+
+ public static XmppAxolotlMessage fromElement(Element element, Jid from) {
+ return new XmppAxolotlMessage(element, from);
+ }
+
+ private static byte[] generateKey() {
+ try {
+ KeyGenerator generator = KeyGenerator.getInstance(KEYTYPE);
+ generator.init(128);
+ return generator.generateKey().getEncoded();
+ } catch (NoSuchAlgorithmException e) {
+ Log.e(Config.LOGTAG, e.getMessage());
+ return null;
+ }
+ }
+
+ private static byte[] generateIv() {
+ SecureRandom random = new SecureRandom();
+ byte[] iv = new byte[16];
+ random.nextBytes(iv);
+ return iv;
+ }
+
+ public void encrypt(String plaintext) throws CryptoFailedException {
+ try {
+ SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE);
+ IvParameterSpec ivSpec = new IvParameterSpec(iv);
+ Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
+ this.ciphertext = cipher.doFinal(plaintext.getBytes());
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
+ | IllegalBlockSizeException | BadPaddingException | NoSuchProviderException
+ | InvalidAlgorithmParameterException e) {
+ throw new CryptoFailedException(e);
+ }
+ }
+
+ public Jid getFrom() {
+ return this.from;
+ }
+
+ public int getSenderDeviceId() {
+ return sourceDeviceId;
+ }
+
+ public byte[] getCiphertext() {
+ return ciphertext;
+ }
+
+ public void addDevice(XmppAxolotlSession session) {
+ byte[] key = session.processSending(innerKey);
+ if (key != null) {
+ keys.put(session.getRemoteAddress().getDeviceId(), key);
+ }
+ }
+
+ public byte[] getInnerKey() {
+ return innerKey;
+ }
+
+ public byte[] getIV() {
+ return this.iv;
+ }
+
+ public Element toElement() {
+ Element encryptionElement = new Element(CONTAINERTAG, AxolotlService.PEP_PREFIX);
+ Element headerElement = encryptionElement.addChild(HEADER);
+ headerElement.setAttribute(SOURCEID, sourceDeviceId);
+ for (Map.Entry<Integer, byte[]> keyEntry : keys.entrySet()) {
+ Element keyElement = new Element(KEYTAG);
+ keyElement.setAttribute(REMOTEID, keyEntry.getKey());
+ keyElement.setContent(Base64.encodeToString(keyEntry.getValue(), Base64.NO_WRAP));
+ headerElement.addChild(keyElement);
+ }
+ headerElement.addChild(IVTAG).setContent(Base64.encodeToString(iv, Base64.NO_WRAP));
+ if (ciphertext != null) {
+ Element payload = encryptionElement.addChild(PAYLOAD);
+ payload.setContent(Base64.encodeToString(ciphertext, Base64.NO_WRAP));
+ }
+ return encryptionElement;
+ }
+
+ private byte[] unpackKey(XmppAxolotlSession session, Integer sourceDeviceId) {
+ byte[] encryptedKey = keys.get(sourceDeviceId);
+ return (encryptedKey != null) ? session.processReceiving(encryptedKey) : null;
+ }
+
+ public XmppAxolotlKeyTransportMessage getParameters(XmppAxolotlSession session, Integer sourceDeviceId) {
+ byte[] key = unpackKey(session, sourceDeviceId);
+ return (key != null)
+ ? new XmppAxolotlKeyTransportMessage(session.getFingerprint(), key, getIV())
+ : null;
+ }
+
+ public XmppAxolotlPlaintextMessage decrypt(XmppAxolotlSession session, Integer sourceDeviceId) throws CryptoFailedException {
+ XmppAxolotlPlaintextMessage plaintextMessage = null;
+ byte[] key = unpackKey(session, sourceDeviceId);
+ if (key != null) {
+ try {
+ Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
+ SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
+ IvParameterSpec ivSpec = new IvParameterSpec(iv);
+
+ cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
+
+ String plaintext = new String(cipher.doFinal(ciphertext));
+ plaintextMessage = new XmppAxolotlPlaintextMessage(plaintext, session.getFingerprint());
+
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
+ | InvalidAlgorithmParameterException | IllegalBlockSizeException
+ | BadPaddingException | NoSuchProviderException e) {
+ throw new CryptoFailedException(e);
+ }
+ }
+ return plaintextMessage;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java
index 566afef9a..27647fb66 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java
@@ -23,113 +23,113 @@ import de.pixart.messenger.Config;
import de.pixart.messenger.entities.Account;
public class XmppAxolotlSession {
- private final SessionCipher cipher;
- private final SQLiteAxolotlStore sqLiteAxolotlStore;
- private final AxolotlAddress remoteAddress;
- private final Account account;
- private IdentityKey identityKey;
- private Integer preKeyId = null;
- private boolean fresh = true;
-
- public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress, IdentityKey identityKey) {
- this(account, store, remoteAddress);
- this.identityKey = identityKey;
- }
-
- public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress) {
- this.cipher = new SessionCipher(store, remoteAddress);
- this.remoteAddress = remoteAddress;
- this.sqLiteAxolotlStore = store;
- this.account = account;
- }
-
- public Integer getPreKeyId() {
- return preKeyId;
- }
-
- public void resetPreKeyId() {
-
- preKeyId = null;
- }
-
- public String getFingerprint() {
- return identityKey == null ? null : identityKey.getFingerprint().replaceAll("\\s", "");
- }
-
- public IdentityKey getIdentityKey() {
- return identityKey;
- }
-
- public AxolotlAddress getRemoteAddress() {
- return remoteAddress;
- }
-
- public boolean isFresh() {
- return fresh;
- }
-
- public void setNotFresh() {
- this.fresh = false;
- }
-
- protected void setTrust(FingerprintStatus status) {
- sqLiteAxolotlStore.setFingerprintStatus(getFingerprint(), status);
- }
-
- public FingerprintStatus getTrust() {
- FingerprintStatus status = sqLiteAxolotlStore.getFingerprintStatus(getFingerprint());
- return (status == null) ? FingerprintStatus.createActiveUndecided() : status;
- }
-
- @Nullable
- public byte[] processReceiving(byte[] encryptedKey) {
- byte[] plaintext = null;
- FingerprintStatus status = getTrust();
- if (!status.isCompromised()) {
- try {
- try {
- PreKeyWhisperMessage message = new PreKeyWhisperMessage(encryptedKey);
- if (!message.getPreKeyId().isPresent()) {
- Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage did not contain a PreKeyId");
- return null;
- }
- Log.i(Config.LOGTAG, AxolotlService.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());
- } 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");
- 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());
- }
- } catch (LegacyMessageException | InvalidMessageException | DuplicateMessageException | NoSessionException e) {
- Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
- }
-
- if (plaintext != null) {
- if (!status.isActive()) {
- setTrust(status.toActive());
- }
- }
- }
- return plaintext;
- }
-
- @Nullable
- public byte[] processSending(@NonNull byte[] outgoingMessage) {
- FingerprintStatus status = getTrust();
- if (status.isTrustedAndActive()) {
- CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
- return ciphertextMessage.serialize();
- } else {
- return null;
- }
- }
+ private final SessionCipher cipher;
+ private final SQLiteAxolotlStore sqLiteAxolotlStore;
+ private final AxolotlAddress remoteAddress;
+ private final Account account;
+ private IdentityKey identityKey;
+ private Integer preKeyId = null;
+ private boolean fresh = true;
+
+ public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress, IdentityKey identityKey) {
+ this(account, store, remoteAddress);
+ this.identityKey = identityKey;
+ }
+
+ public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress) {
+ this.cipher = new SessionCipher(store, remoteAddress);
+ this.remoteAddress = remoteAddress;
+ this.sqLiteAxolotlStore = store;
+ this.account = account;
+ }
+
+ public Integer getPreKeyId() {
+ return preKeyId;
+ }
+
+ public void resetPreKeyId() {
+
+ preKeyId = null;
+ }
+
+ public String getFingerprint() {
+ return identityKey == null ? null : identityKey.getFingerprint().replaceAll("\\s", "");
+ }
+
+ public IdentityKey getIdentityKey() {
+ return identityKey;
+ }
+
+ public AxolotlAddress getRemoteAddress() {
+ return remoteAddress;
+ }
+
+ public boolean isFresh() {
+ return fresh;
+ }
+
+ public void setNotFresh() {
+ this.fresh = false;
+ }
+
+ protected void setTrust(FingerprintStatus status) {
+ sqLiteAxolotlStore.setFingerprintStatus(getFingerprint(), status);
+ }
+
+ public FingerprintStatus getTrust() {
+ FingerprintStatus status = sqLiteAxolotlStore.getFingerprintStatus(getFingerprint());
+ return (status == null) ? FingerprintStatus.createActiveUndecided() : status;
+ }
+
+ @Nullable
+ public byte[] processReceiving(byte[] encryptedKey) {
+ byte[] plaintext = null;
+ FingerprintStatus status = getTrust();
+ if (!status.isCompromised()) {
+ try {
+ try {
+ PreKeyWhisperMessage message = new PreKeyWhisperMessage(encryptedKey);
+ if (!message.getPreKeyId().isPresent()) {
+ Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage did not contain a PreKeyId");
+ return null;
+ }
+ Log.i(Config.LOGTAG, AxolotlService.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());
+ } 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");
+ 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());
+ }
+ } catch (LegacyMessageException | InvalidMessageException | DuplicateMessageException | NoSessionException e) {
+ Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
+ }
+
+ if (plaintext != null) {
+ if (!status.isActive()) {
+ setTrust(status.toActive());
+ }
+ }
+ }
+ return plaintext;
+ }
+
+ @Nullable
+ public byte[] processSending(@NonNull byte[] outgoingMessage) {
+ FingerprintStatus status = getTrust();
+ if (status.isTrustedAndActive()) {
+ CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
+ return ciphertextMessage.serialize();
+ } else {
+ return null;
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/sasl/Anonymous.java b/src/main/java/de/pixart/messenger/crypto/sasl/Anonymous.java
index 7e4e98bc6..93c3de64b 100644
--- a/src/main/java/de/pixart/messenger/crypto/sasl/Anonymous.java
+++ b/src/main/java/de/pixart/messenger/crypto/sasl/Anonymous.java
@@ -7,22 +7,22 @@ import de.pixart.messenger.xml.TagWriter;
public class Anonymous extends SaslMechanism {
- public Anonymous(TagWriter tagWriter, Account account, SecureRandom rng) {
- super(tagWriter, account, rng);
- }
+ public Anonymous(TagWriter tagWriter, Account account, SecureRandom rng) {
+ super(tagWriter, account, rng);
+ }
- @Override
- public int getPriority() {
- return 0;
- }
+ @Override
+ public int getPriority() {
+ return 0;
+ }
- @Override
- public String getMechanism() {
- return "ANONYMOUS";
- }
+ @Override
+ public String getMechanism() {
+ return "ANONYMOUS";
+ }
- @Override
- public String getClientFirstMessage() {
- return "";
- }
+ @Override
+ public String getClientFirstMessage() {
+ return "";
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/sasl/DigestMd5.java b/src/main/java/de/pixart/messenger/crypto/sasl/DigestMd5.java
index 09ac4865a..e974d8cd4 100644
--- a/src/main/java/de/pixart/messenger/crypto/sasl/DigestMd5.java
+++ b/src/main/java/de/pixart/messenger/crypto/sasl/DigestMd5.java
@@ -13,79 +13,79 @@ import de.pixart.messenger.utils.CryptoHelper;
import de.pixart.messenger.xml.TagWriter;
public class DigestMd5 extends SaslMechanism {
- public DigestMd5(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
- super(tagWriter, account, rng);
- }
+ public DigestMd5(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
+ super(tagWriter, account, rng);
+ }
- @Override
- public int getPriority() {
- return 10;
- }
+ @Override
+ public int getPriority() {
+ return 10;
+ }
- @Override
- public String getMechanism() {
- return "DIGEST-MD5";
- }
+ @Override
+ public String getMechanism() {
+ return "DIGEST-MD5";
+ }
- private State state = State.INITIAL;
+ private State state = State.INITIAL;
- @Override
- public String getResponse(final String challenge) throws AuthenticationException {
- switch (state) {
- case INITIAL:
- state = State.RESPONSE_SENT;
- final String encodedResponse;
- try {
- final Tokenizer tokenizer = new Tokenizer(Base64.decode(challenge, Base64.DEFAULT));
- String nonce = "";
- for (final String token : tokenizer) {
- final String[] parts = token.split("=", 2);
- if (parts[0].equals("nonce")) {
- nonce = parts[1].replace("\"", "");
- } else if (parts[0].equals("rspauth")) {
- return "";
- }
- }
- final String digestUri = "xmpp/" + account.getServer();
- final String nonceCount = "00000001";
- final String x = account.getUsername() + ":" + account.getServer() + ":"
- + account.getPassword();
- final MessageDigest md = MessageDigest.getInstance("MD5");
- final byte[] y = md.digest(x.getBytes(Charset.defaultCharset()));
- final String cNonce = new BigInteger(100, rng).toString(32);
- final byte[] a1 = CryptoHelper.concatenateByteArrays(y,
- (":" + nonce + ":" + cNonce).getBytes(Charset.defaultCharset()));
- final String a2 = "AUTHENTICATE:" + digestUri;
- final String ha1 = CryptoHelper.bytesToHex(md.digest(a1));
- final String ha2 = CryptoHelper.bytesToHex(md.digest(a2.getBytes(Charset
- .defaultCharset())));
- final String kd = ha1 + ":" + nonce + ":" + nonceCount + ":" + cNonce
- + ":auth:" + ha2;
- final String response = CryptoHelper.bytesToHex(md.digest(kd.getBytes(Charset
- .defaultCharset())));
- final String saslString = "username=\"" + account.getUsername()
- + "\",realm=\"" + account.getServer() + "\",nonce=\""
- + nonce + "\",cnonce=\"" + cNonce + "\",nc=" + nonceCount
- + ",qop=auth,digest-uri=\"" + digestUri + "\",response="
- + response + ",charset=utf-8";
- encodedResponse = Base64.encodeToString(
- saslString.getBytes(Charset.defaultCharset()),
- Base64.NO_WRAP);
- } catch (final NoSuchAlgorithmException e) {
- throw new AuthenticationException(e);
- }
+ @Override
+ public String getResponse(final String challenge) throws AuthenticationException {
+ switch (state) {
+ case INITIAL:
+ state = State.RESPONSE_SENT;
+ final String encodedResponse;
+ try {
+ final Tokenizer tokenizer = new Tokenizer(Base64.decode(challenge, Base64.DEFAULT));
+ String nonce = "";
+ for (final String token : tokenizer) {
+ final String[] parts = token.split("=", 2);
+ if (parts[0].equals("nonce")) {
+ nonce = parts[1].replace("\"", "");
+ } else if (parts[0].equals("rspauth")) {
+ return "";
+ }
+ }
+ final String digestUri = "xmpp/" + account.getServer();
+ final String nonceCount = "00000001";
+ final String x = account.getUsername() + ":" + account.getServer() + ":"
+ + account.getPassword();
+ final MessageDigest md = MessageDigest.getInstance("MD5");
+ final byte[] y = md.digest(x.getBytes(Charset.defaultCharset()));
+ final String cNonce = new BigInteger(100, rng).toString(32);
+ final byte[] a1 = CryptoHelper.concatenateByteArrays(y,
+ (":" + nonce + ":" + cNonce).getBytes(Charset.defaultCharset()));
+ final String a2 = "AUTHENTICATE:" + digestUri;
+ final String ha1 = CryptoHelper.bytesToHex(md.digest(a1));
+ final String ha2 = CryptoHelper.bytesToHex(md.digest(a2.getBytes(Charset
+ .defaultCharset())));
+ final String kd = ha1 + ":" + nonce + ":" + nonceCount + ":" + cNonce
+ + ":auth:" + ha2;
+ final String response = CryptoHelper.bytesToHex(md.digest(kd.getBytes(Charset
+ .defaultCharset())));
+ final String saslString = "username=\"" + account.getUsername()
+ + "\",realm=\"" + account.getServer() + "\",nonce=\""
+ + nonce + "\",cnonce=\"" + cNonce + "\",nc=" + nonceCount
+ + ",qop=auth,digest-uri=\"" + digestUri + "\",response="
+ + response + ",charset=utf-8";
+ encodedResponse = Base64.encodeToString(
+ saslString.getBytes(Charset.defaultCharset()),
+ Base64.NO_WRAP);
+ } catch (final NoSuchAlgorithmException e) {
+ throw new AuthenticationException(e);
+ }
- return encodedResponse;
- case RESPONSE_SENT:
- state = State.VALID_SERVER_RESPONSE;
- break;
- case VALID_SERVER_RESPONSE:
- if (challenge==null) {
- return null; //everything is fine
- }
- default:
- throw new InvalidStateException(state);
- }
- return null;
- }
+ return encodedResponse;
+ case RESPONSE_SENT:
+ state = State.VALID_SERVER_RESPONSE;
+ break;
+ case VALID_SERVER_RESPONSE:
+ if (challenge == null) {
+ return null; //everything is fine
+ }
+ default:
+ throw new InvalidStateException(state);
+ }
+ return null;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/sasl/External.java b/src/main/java/de/pixart/messenger/crypto/sasl/External.java
index a1a79a0a8..b80d174f0 100644
--- a/src/main/java/de/pixart/messenger/crypto/sasl/External.java
+++ b/src/main/java/de/pixart/messenger/crypto/sasl/External.java
@@ -9,22 +9,22 @@ import de.pixart.messenger.xml.TagWriter;
public class External extends SaslMechanism {
- public External(TagWriter tagWriter, Account account, SecureRandom rng) {
- super(tagWriter, account, rng);
- }
-
- @Override
- public int getPriority() {
- return 25;
- }
-
- @Override
- public String getMechanism() {
- return "EXTERNAL";
- }
-
- @Override
- public String getClientFirstMessage() {
- return Base64.encodeToString(account.getJid().toBareJid().toString().getBytes(),Base64.NO_WRAP);
- }
+ public External(TagWriter tagWriter, Account account, SecureRandom rng) {
+ super(tagWriter, account, rng);
+ }
+
+ @Override
+ public int getPriority() {
+ return 25;
+ }
+
+ @Override
+ public String getMechanism() {
+ return "EXTERNAL";
+ }
+
+ @Override
+ public String getClientFirstMessage() {
+ return Base64.encodeToString(account.getJid().toBareJid().toString().getBytes(), Base64.NO_WRAP);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/sasl/Plain.java b/src/main/java/de/pixart/messenger/crypto/sasl/Plain.java
index d0aa90b49..27de45247 100644
--- a/src/main/java/de/pixart/messenger/crypto/sasl/Plain.java
+++ b/src/main/java/de/pixart/messenger/crypto/sasl/Plain.java
@@ -8,23 +8,23 @@ import de.pixart.messenger.entities.Account;
import de.pixart.messenger.xml.TagWriter;
public class Plain extends SaslMechanism {
- public Plain(final TagWriter tagWriter, final Account account) {
- super(tagWriter, account, null);
- }
+ public Plain(final TagWriter tagWriter, final Account account) {
+ super(tagWriter, account, null);
+ }
- @Override
- public int getPriority() {
- return 10;
- }
+ @Override
+ public int getPriority() {
+ return 10;
+ }
- @Override
- public String getMechanism() {
- return "PLAIN";
- }
+ @Override
+ public String getMechanism() {
+ return "PLAIN";
+ }
- @Override
- public String getClientFirstMessage() {
- final String sasl = '\u0000' + account.getUsername() + '\u0000' + account.getPassword();
- return Base64.encodeToString(sasl.getBytes(Charset.defaultCharset()), Base64.NO_WRAP);
- }
+ @Override
+ public String getClientFirstMessage() {
+ final String sasl = '\u0000' + account.getUsername() + '\u0000' + account.getPassword();
+ return Base64.encodeToString(sasl.getBytes(Charset.defaultCharset()), Base64.NO_WRAP);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/sasl/SaslMechanism.java b/src/main/java/de/pixart/messenger/crypto/sasl/SaslMechanism.java
index 19e8f3591..8b8883b9f 100644
--- a/src/main/java/de/pixart/messenger/crypto/sasl/SaslMechanism.java
+++ b/src/main/java/de/pixart/messenger/crypto/sasl/SaslMechanism.java
@@ -7,56 +7,59 @@ import de.pixart.messenger.xml.TagWriter;
public abstract class SaslMechanism {
- final protected TagWriter tagWriter;
- final protected Account account;
- final protected SecureRandom rng;
-
- protected enum State {
- INITIAL,
- AUTH_TEXT_SENT,
- RESPONSE_SENT,
- VALID_SERVER_RESPONSE,
- }
-
- public static class AuthenticationException extends Exception {
- public AuthenticationException(final String message) {
- super(message);
- }
-
- public AuthenticationException(final Exception inner) {
- super(inner);
- }
- }
-
- public static class InvalidStateException extends AuthenticationException {
- public InvalidStateException(final String message) {
- super(message);
- }
-
- public InvalidStateException(final State state) {
- this("Invalid state: " + state.toString());
- }
- }
-
- public SaslMechanism(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
- this.tagWriter = tagWriter;
- this.account = account;
- this.rng = rng;
- }
-
- /**
- * The priority is used to pin the authentication mechanism. If authentication fails, it MAY be retried with another
- * mechanism of the same priority, but MUST NOT be tried with a mechanism of lower priority (to prevent downgrade
- * attacks).
- * @return An arbitrary int representing the priority
- */
- public abstract int getPriority();
-
- public abstract String getMechanism();
- public String getClientFirstMessage() {
- return "";
- }
- public String getResponse(final String challenge) throws AuthenticationException {
- return "";
- }
+ final protected TagWriter tagWriter;
+ final protected Account account;
+ final protected SecureRandom rng;
+
+ protected enum State {
+ INITIAL,
+ AUTH_TEXT_SENT,
+ RESPONSE_SENT,
+ VALID_SERVER_RESPONSE,
+ }
+
+ public static class AuthenticationException extends Exception {
+ public AuthenticationException(final String message) {
+ super(message);
+ }
+
+ public AuthenticationException(final Exception inner) {
+ super(inner);
+ }
+ }
+
+ public static class InvalidStateException extends AuthenticationException {
+ public InvalidStateException(final String message) {
+ super(message);
+ }
+
+ public InvalidStateException(final State state) {
+ this("Invalid state: " + state.toString());
+ }
+ }
+
+ public SaslMechanism(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
+ this.tagWriter = tagWriter;
+ this.account = account;
+ this.rng = rng;
+ }
+
+ /**
+ * The priority is used to pin the authentication mechanism. If authentication fails, it MAY be retried with another
+ * mechanism of the same priority, but MUST NOT be tried with a mechanism of lower priority (to prevent downgrade
+ * attacks).
+ *
+ * @return An arbitrary int representing the priority
+ */
+ public abstract int getPriority();
+
+ public abstract String getMechanism();
+
+ public String getClientFirstMessage() {
+ return "";
+ }
+
+ public String getResponse(final String challenge) throws AuthenticationException {
+ return "";
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/sasl/ScramSha1.java b/src/main/java/de/pixart/messenger/crypto/sasl/ScramSha1.java
index abff542d4..d21cad37d 100644
--- a/src/main/java/de/pixart/messenger/crypto/sasl/ScramSha1.java
+++ b/src/main/java/de/pixart/messenger/crypto/sasl/ScramSha1.java
@@ -18,221 +18,222 @@ import de.pixart.messenger.utils.CryptoHelper;
import de.pixart.messenger.xml.TagWriter;
public class ScramSha1 extends SaslMechanism {
- // TODO: When channel binding (SCRAM-SHA1-PLUS) is supported in future, generalize this to indicate support and/or usage.
- final private static String GS2_HEADER = "n,,";
- private String clientFirstMessageBare;
- final private String clientNonce;
- private byte[] serverSignature = null;
- private static HMac HMAC;
- private static Digest DIGEST;
- private static final byte[] CLIENT_KEY_BYTES = "Client Key".getBytes();
- private static final byte[] SERVER_KEY_BYTES = "Server Key".getBytes();
-
- public static class KeyPair {
- final public byte[] clientKey;
- final public byte[] serverKey;
-
- public KeyPair(final byte[] clientKey, final byte[] serverKey) {
- this.clientKey = clientKey;
- this.serverKey = serverKey;
- }
- }
-
- private static final LruCache<String, KeyPair> CACHE;
-
- static {
- DIGEST = new SHA1Digest();
- HMAC = new HMac(new SHA1Digest());
- CACHE = new LruCache<String, KeyPair>(10) {
- protected KeyPair create(final String k) {
- // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations".
- // Changing any of these values forces a cache miss. `CryptoHelper.bytesToHex()'
- // is applied to prevent commas in the strings breaking things.
- final String[] kparts = k.split(",", 4);
- try {
- final byte[] saltedPassword, serverKey, clientKey;
- saltedPassword = hi(CryptoHelper.hexToString(kparts[1]).getBytes(),
- Base64.decode(CryptoHelper.hexToString(kparts[2]), Base64.DEFAULT), Integer.valueOf(kparts[3]));
- serverKey = hmac(saltedPassword, SERVER_KEY_BYTES);
- clientKey = hmac(saltedPassword, CLIENT_KEY_BYTES);
-
- return new KeyPair(clientKey, serverKey);
- } catch (final InvalidKeyException | NumberFormatException e) {
- return null;
- }
- }
- };
- }
-
- private State state = State.INITIAL;
-
- public ScramSha1(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
- super(tagWriter, account, rng);
-
- // This nonce should be different for each authentication attempt.
- clientNonce = new BigInteger(100, this.rng).toString(32);
- clientFirstMessageBare = "";
- }
-
- @Override
- public int getPriority() {
- return 20;
- }
-
- @Override
- public String getMechanism() {
- return "SCRAM-SHA-1";
- }
-
- @Override
- public String getClientFirstMessage() {
- if (clientFirstMessageBare.isEmpty() && state == State.INITIAL) {
- clientFirstMessageBare = "n=" + CryptoHelper.saslEscape(CryptoHelper.saslPrep(account.getUsername())) +
- ",r=" + this.clientNonce;
- state = State.AUTH_TEXT_SENT;
- }
- return Base64.encodeToString(
- (GS2_HEADER + clientFirstMessageBare).getBytes(Charset.defaultCharset()),
- Base64.NO_WRAP);
- }
-
- @Override
- public String getResponse(final String challenge) throws AuthenticationException {
- switch (state) {
- case AUTH_TEXT_SENT:
- if (challenge == null) {
- throw new AuthenticationException("challenge can not be null");
- }
- byte[] serverFirstMessage = Base64.decode(challenge, Base64.DEFAULT);
- final Tokenizer tokenizer = new Tokenizer(serverFirstMessage);
- String nonce = "";
- int iterationCount = -1;
- String salt = "";
- for (final String token : tokenizer) {
- if (token.charAt(1) == '=') {
- switch (token.charAt(0)) {
- case 'i':
- try {
- iterationCount = Integer.parseInt(token.substring(2));
- } catch (final NumberFormatException e) {
- throw new AuthenticationException(e);
- }
- break;
- case 's':
- salt = token.substring(2);
- break;
- case 'r':
- nonce = token.substring(2);
- break;
- case 'm':
- /*
+ // TODO: When channel binding (SCRAM-SHA1-PLUS) is supported in future, generalize this to indicate support and/or usage.
+ final private static String GS2_HEADER = "n,,";
+ private String clientFirstMessageBare;
+ final private String clientNonce;
+ private byte[] serverSignature = null;
+ private static HMac HMAC;
+ private static Digest DIGEST;
+ private static final byte[] CLIENT_KEY_BYTES = "Client Key".getBytes();
+ private static final byte[] SERVER_KEY_BYTES = "Server Key".getBytes();
+
+ public static class KeyPair {
+ final public byte[] clientKey;
+ final public byte[] serverKey;
+
+ public KeyPair(final byte[] clientKey, final byte[] serverKey) {
+ this.clientKey = clientKey;
+ this.serverKey = serverKey;
+ }
+ }
+
+ private static final LruCache<String, KeyPair> CACHE;
+
+ static {
+ DIGEST = new SHA1Digest();
+ HMAC = new HMac(new SHA1Digest());
+ CACHE = new LruCache<String, KeyPair>(10) {
+ protected KeyPair create(final String k) {
+ // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations".
+ // Changing any of these values forces a cache miss. `CryptoHelper.bytesToHex()'
+ // is applied to prevent commas in the strings breaking things.
+ final String[] kparts = k.split(",", 4);
+ try {
+ final byte[] saltedPassword, serverKey, clientKey;
+ saltedPassword = hi(CryptoHelper.hexToString(kparts[1]).getBytes(),
+ Base64.decode(CryptoHelper.hexToString(kparts[2]), Base64.DEFAULT), Integer.valueOf(kparts[3]));
+ serverKey = hmac(saltedPassword, SERVER_KEY_BYTES);
+ clientKey = hmac(saltedPassword, CLIENT_KEY_BYTES);
+
+ return new KeyPair(clientKey, serverKey);
+ } catch (final InvalidKeyException | NumberFormatException e) {
+ return null;
+ }
+ }
+ };
+ }
+
+ private State state = State.INITIAL;
+
+ public ScramSha1(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
+ super(tagWriter, account, rng);
+
+ // This nonce should be different for each authentication attempt.
+ clientNonce = new BigInteger(100, this.rng).toString(32);
+ clientFirstMessageBare = "";
+ }
+
+ @Override
+ public int getPriority() {
+ return 20;
+ }
+
+ @Override
+ public String getMechanism() {
+ return "SCRAM-SHA-1";
+ }
+
+ @Override
+ public String getClientFirstMessage() {
+ if (clientFirstMessageBare.isEmpty() && state == State.INITIAL) {
+ clientFirstMessageBare = "n=" + CryptoHelper.saslEscape(CryptoHelper.saslPrep(account.getUsername())) +
+ ",r=" + this.clientNonce;
+ state = State.AUTH_TEXT_SENT;
+ }
+ return Base64.encodeToString(
+ (GS2_HEADER + clientFirstMessageBare).getBytes(Charset.defaultCharset()),
+ Base64.NO_WRAP);
+ }
+
+ @Override
+ public String getResponse(final String challenge) throws AuthenticationException {
+ switch (state) {
+ case AUTH_TEXT_SENT:
+ if (challenge == null) {
+ throw new AuthenticationException("challenge can not be null");
+ }
+ byte[] serverFirstMessage = Base64.decode(challenge, Base64.DEFAULT);
+ final Tokenizer tokenizer = new Tokenizer(serverFirstMessage);
+ String nonce = "";
+ int iterationCount = -1;
+ String salt = "";
+ for (final String token : tokenizer) {
+ if (token.charAt(1) == '=') {
+ switch (token.charAt(0)) {
+ case 'i':
+ try {
+ iterationCount = Integer.parseInt(token.substring(2));
+ } catch (final NumberFormatException e) {
+ throw new AuthenticationException(e);
+ }
+ break;
+ case 's':
+ salt = token.substring(2);
+ break;
+ case 'r':
+ nonce = token.substring(2);
+ break;
+ case 'm':
+ /*
* RFC 5802:
* m: This attribute is reserved for future extensibility. In this
* version of SCRAM, its presence in a client or a server message
* MUST cause authentication failure when the attribute is parsed by
* the other end.
*/
- throw new AuthenticationException("Server sent reserved token: `m'");
- }
- }
- }
-
- if (iterationCount < 0) {
- throw new AuthenticationException("Server did not send iteration count");
- }
- if (nonce.isEmpty() || !nonce.startsWith(clientNonce)) {
- throw new AuthenticationException("Server nonce does not contain client nonce: " + nonce);
- }
- if (salt.isEmpty()) {
- throw new AuthenticationException("Server sent empty salt");
- }
-
- final String clientFinalMessageWithoutProof = "c=" + Base64.encodeToString(
- GS2_HEADER.getBytes(), Base64.NO_WRAP) + ",r=" + nonce;
- final byte[] authMessage = (clientFirstMessageBare + ',' + new String(serverFirstMessage) + ','
- + clientFinalMessageWithoutProof).getBytes();
-
- // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations".
- final KeyPair keys = CACHE.get(
- CryptoHelper.bytesToHex(account.getJid().toBareJid().toString().getBytes()) + ","
- + CryptoHelper.bytesToHex(account.getPassword().getBytes()) + ","
- + CryptoHelper.bytesToHex(salt.getBytes()) + ","
- + String.valueOf(iterationCount)
- );
- if (keys == null) {
- throw new AuthenticationException("Invalid keys generated");
- }
- final byte[] clientSignature;
- try {
- serverSignature = hmac(keys.serverKey, authMessage);
- final byte[] storedKey = digest(keys.clientKey);
-
- clientSignature = hmac(storedKey, authMessage);
-
- } catch (final InvalidKeyException e) {
- throw new AuthenticationException(e);
- }
-
- final byte[] clientProof = new byte[keys.clientKey.length];
-
- for (int i = 0; i < clientProof.length; i++) {
- clientProof[i] = (byte) (keys.clientKey[i] ^ clientSignature[i]);
- }
-
-
- final String clientFinalMessage = clientFinalMessageWithoutProof + ",p=" +
- Base64.encodeToString(clientProof, Base64.NO_WRAP);
- state = State.RESPONSE_SENT;
- return Base64.encodeToString(clientFinalMessage.getBytes(), Base64.NO_WRAP);
- case RESPONSE_SENT:
- try {
- final String clientCalculatedServerFinalMessage = "v=" +
- Base64.encodeToString(serverSignature, Base64.NO_WRAP);
- if (!clientCalculatedServerFinalMessage.equals(new String(Base64.decode(challenge, Base64.DEFAULT)))) {
- throw new Exception();
- };
- state = State.VALID_SERVER_RESPONSE;
- return "";
- } catch(Exception e) {
- throw new AuthenticationException("Server final message does not match calculated final message");
- }
- default:
- throw new InvalidStateException(state);
- }
- }
-
- public static synchronized byte[] hmac(final byte[] key, final byte[] input)
- throws InvalidKeyException {
- HMAC.init(new KeyParameter(key));
- HMAC.update(input, 0, input.length);
- final byte[] out = new byte[HMAC.getMacSize()];
- HMAC.doFinal(out, 0);
- return out;
- }
-
- public static synchronized byte[] digest(byte[] bytes) {
- DIGEST.reset();
- DIGEST.update(bytes, 0, bytes.length);
- final byte[] out = new byte[DIGEST.getDigestSize()];
- DIGEST.doFinal(out, 0);
- return out;
- }
-
- /*
- * Hi() is, essentially, PBKDF2 [RFC2898] with HMAC() as the
- * pseudorandom function (PRF) and with dkLen == output length of
- * HMAC() == output length of H().
- */
- private static synchronized byte[] hi(final byte[] key, final byte[] salt, final int iterations)
- throws InvalidKeyException {
- byte[] u = hmac(key, CryptoHelper.concatenateByteArrays(salt, CryptoHelper.ONE));
- byte[] out = u.clone();
- for (int i = 1; i < iterations; i++) {
- u = hmac(key, u);
- for (int j = 0; j < u.length; j++) {
- out[j] ^= u[j];
- }
- }
- return out;
- }
+ throw new AuthenticationException("Server sent reserved token: `m'");
+ }
+ }
+ }
+
+ if (iterationCount < 0) {
+ throw new AuthenticationException("Server did not send iteration count");
+ }
+ if (nonce.isEmpty() || !nonce.startsWith(clientNonce)) {
+ throw new AuthenticationException("Server nonce does not contain client nonce: " + nonce);
+ }
+ if (salt.isEmpty()) {
+ throw new AuthenticationException("Server sent empty salt");
+ }
+
+ final String clientFinalMessageWithoutProof = "c=" + Base64.encodeToString(
+ GS2_HEADER.getBytes(), Base64.NO_WRAP) + ",r=" + nonce;
+ final byte[] authMessage = (clientFirstMessageBare + ',' + new String(serverFirstMessage) + ','
+ + clientFinalMessageWithoutProof).getBytes();
+
+ // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations".
+ final KeyPair keys = CACHE.get(
+ CryptoHelper.bytesToHex(account.getJid().toBareJid().toString().getBytes()) + ","
+ + CryptoHelper.bytesToHex(account.getPassword().getBytes()) + ","
+ + CryptoHelper.bytesToHex(salt.getBytes()) + ","
+ + String.valueOf(iterationCount)
+ );
+ if (keys == null) {
+ throw new AuthenticationException("Invalid keys generated");
+ }
+ final byte[] clientSignature;
+ try {
+ serverSignature = hmac(keys.serverKey, authMessage);
+ final byte[] storedKey = digest(keys.clientKey);
+
+ clientSignature = hmac(storedKey, authMessage);
+
+ } catch (final InvalidKeyException e) {
+ throw new AuthenticationException(e);
+ }
+
+ final byte[] clientProof = new byte[keys.clientKey.length];
+
+ for (int i = 0; i < clientProof.length; i++) {
+ clientProof[i] = (byte) (keys.clientKey[i] ^ clientSignature[i]);
+ }
+
+
+ final String clientFinalMessage = clientFinalMessageWithoutProof + ",p=" +
+ Base64.encodeToString(clientProof, Base64.NO_WRAP);
+ state = State.RESPONSE_SENT;
+ return Base64.encodeToString(clientFinalMessage.getBytes(), Base64.NO_WRAP);
+ case RESPONSE_SENT:
+ try {
+ final String clientCalculatedServerFinalMessage = "v=" +
+ Base64.encodeToString(serverSignature, Base64.NO_WRAP);
+ if (!clientCalculatedServerFinalMessage.equals(new String(Base64.decode(challenge, Base64.DEFAULT)))) {
+ throw new Exception();
+ }
+ ;
+ state = State.VALID_SERVER_RESPONSE;
+ return "";
+ } catch (Exception e) {
+ throw new AuthenticationException("Server final message does not match calculated final message");
+ }
+ default:
+ throw new InvalidStateException(state);
+ }
+ }
+
+ public static synchronized byte[] hmac(final byte[] key, final byte[] input)
+ throws InvalidKeyException {
+ HMAC.init(new KeyParameter(key));
+ HMAC.update(input, 0, input.length);
+ final byte[] out = new byte[HMAC.getMacSize()];
+ HMAC.doFinal(out, 0);
+ return out;
+ }
+
+ public static synchronized byte[] digest(byte[] bytes) {
+ DIGEST.reset();
+ DIGEST.update(bytes, 0, bytes.length);
+ final byte[] out = new byte[DIGEST.getDigestSize()];
+ DIGEST.doFinal(out, 0);
+ return out;
+ }
+
+ /*
+ * Hi() is, essentially, PBKDF2 [RFC2898] with HMAC() as the
+ * pseudorandom function (PRF) and with dkLen == output length of
+ * HMAC() == output length of H().
+ */
+ private static synchronized byte[] hi(final byte[] key, final byte[] salt, final int iterations)
+ throws InvalidKeyException {
+ byte[] u = hmac(key, CryptoHelper.concatenateByteArrays(salt, CryptoHelper.ONE));
+ byte[] out = u.clone();
+ for (int i = 1; i < iterations; i++) {
+ u = hmac(key, u);
+ for (int j = 0; j < u.length; j++) {
+ out[j] ^= u[j];
+ }
+ }
+ return out;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/sasl/Tokenizer.java b/src/main/java/de/pixart/messenger/crypto/sasl/Tokenizer.java
index 01cd07929..67e4418dd 100644
--- a/src/main/java/de/pixart/messenger/crypto/sasl/Tokenizer.java
+++ b/src/main/java/de/pixart/messenger/crypto/sasl/Tokenizer.java
@@ -10,69 +10,69 @@ import java.util.NoSuchElementException;
* A tokenizer for GS2 header strings
*/
public final class Tokenizer implements Iterator<String>, Iterable<String> {
- private final List<String> parts;
- private int index;
+ private final List<String> parts;
+ private int index;
- public Tokenizer(final byte[] challenge) {
- final String challengeString = new String(challenge);
- parts = new ArrayList<>(Arrays.asList(challengeString.split(",")));
- // Trim parts.
- for (int i = 0; i < parts.size(); i++) {
- parts.set(i, parts.get(i).trim());
- }
- index = 0;
- }
+ public Tokenizer(final byte[] challenge) {
+ final String challengeString = new String(challenge);
+ parts = new ArrayList<>(Arrays.asList(challengeString.split(",")));
+ // Trim parts.
+ for (int i = 0; i < parts.size(); i++) {
+ parts.set(i, parts.get(i).trim());
+ }
+ index = 0;
+ }
- /**
- * Returns true if there is at least one more element, false otherwise.
- *
- * @see #next
- */
- @Override
- public boolean hasNext() {
- return parts.size() != index + 1;
- }
+ /**
+ * Returns true if there is at least one more element, false otherwise.
+ *
+ * @see #next
+ */
+ @Override
+ public boolean hasNext() {
+ return parts.size() != index + 1;
+ }
- /**
- * Returns the next object and advances the iterator.
- *
- * @return the next object.
- * @throws java.util.NoSuchElementException if there are no more elements.
- * @see #hasNext
- */
- @Override
- public String next() {
- if (hasNext()) {
- return parts.get(index++);
- } else {
- throw new NoSuchElementException("No such element. Size is: " + parts.size());
- }
- }
+ /**
+ * Returns the next object and advances the iterator.
+ *
+ * @return the next object.
+ * @throws java.util.NoSuchElementException if there are no more elements.
+ * @see #hasNext
+ */
+ @Override
+ public String next() {
+ if (hasNext()) {
+ return parts.get(index++);
+ } else {
+ throw new NoSuchElementException("No such element. Size is: " + parts.size());
+ }
+ }
- /**
- * Removes the last object returned by {@code next} from the collection.
- * This method can only be called once between each call to {@code next}.
- *
- * @throws UnsupportedOperationException if removing is not supported by the collection being
- * iterated.
- * @throws IllegalStateException if {@code next} has not been called, or {@code remove} has
- * already been called after the last call to {@code next}.
- */
- @Override
- public void remove() {
- if(index <= 0) {
- throw new IllegalStateException("You can't delete an element before first next() method call");
- }
- parts.remove(--index);
- }
+ /**
+ * Removes the last object returned by {@code next} from the collection.
+ * This method can only be called once between each call to {@code next}.
+ *
+ * @throws UnsupportedOperationException if removing is not supported by the collection being
+ * iterated.
+ * @throws IllegalStateException if {@code next} has not been called, or {@code remove} has
+ * already been called after the last call to {@code next}.
+ */
+ @Override
+ public void remove() {
+ if (index <= 0) {
+ throw new IllegalStateException("You can't delete an element before first next() method call");
+ }
+ parts.remove(--index);
+ }
- /**
- * Returns an {@link java.util.Iterator} for the elements in this object.
- *
- * @return An {@code Iterator} instance.
- */
- @Override
- public Iterator<String> iterator() {
- return parts.iterator();
- }
+ /**
+ * Returns an {@link java.util.Iterator} for the elements in this object.
+ *
+ * @return An {@code Iterator} instance.
+ */
+ @Override
+ public Iterator<String> iterator() {
+ return parts.iterator();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/AbstractEntity.java b/src/main/java/de/pixart/messenger/entities/AbstractEntity.java
index 1194cb819..2cac3405e 100644
--- a/src/main/java/de/pixart/messenger/entities/AbstractEntity.java
+++ b/src/main/java/de/pixart/messenger/entities/AbstractEntity.java
@@ -4,17 +4,17 @@ import android.content.ContentValues;
public abstract class AbstractEntity {
- public static final String UUID = "uuid";
+ public static final String UUID = "uuid";
- protected String uuid;
+ protected String uuid;
- public String getUuid() {
- return this.uuid;
- }
+ public String getUuid() {
+ return this.uuid;
+ }
- public abstract ContentValues getContentValues();
+ public abstract ContentValues getContentValues();
- public boolean equals(AbstractEntity entity) {
- return this.getUuid().equals(entity.getUuid());
- }
+ public boolean equals(AbstractEntity entity) {
+ return this.getUuid().equals(entity.getUuid());
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/Account.java b/src/main/java/de/pixart/messenger/entities/Account.java
index 7a94750ab..1b349e526 100644
--- a/src/main/java/de/pixart/messenger/entities/Account.java
+++ b/src/main/java/de/pixart/messenger/entities/Account.java
@@ -33,633 +33,633 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class Account extends AbstractEntity {
- public static final String TABLENAME = "accounts";
-
- public static final String USERNAME = "username";
- public static final String SERVER = "server";
- public static final String PASSWORD = "password";
- public static final String OPTIONS = "options";
- public static final String ROSTERVERSION = "rosterversion";
- public static final String KEYS = "keys";
- public static final String AVATAR = "avatar";
- public static final String DISPLAY_NAME = "display_name";
- public static final String HOSTNAME = "hostname";
- public static final String PORT = "port";
- public static final String STATUS = "status";
- public static final String STATUS_MESSAGE = "status_message";
-
- public static final String PINNED_MECHANISM_KEY = "pinned_mechanism";
-
- public static final int OPTION_USETLS = 0;
- public static final int OPTION_DISABLED = 1;
- public static final int OPTION_REGISTER = 2;
- public static final int OPTION_USECOMPRESSION = 3;
- public static final int OPTION_MAGIC_CREATE = 4;
- public final HashSet<Pair<String, String>> inProgressDiscoFetches = new HashSet<>();
-
- public boolean httpUploadAvailable(long filesize) {
- return xmppConnection != null && xmppConnection.getFeatures().httpUpload(filesize);
- }
-
- public boolean httpUploadAvailable() {
- return httpUploadAvailable(0);
- }
-
- public void setDisplayName(String displayName) {
- this.displayName = displayName;
- }
-
- public String getDisplayName() {
- return displayName;
- }
-
- public XmppConnection.Identity getServerIdentity() {
- if (xmppConnection == null) {
- return XmppConnection.Identity.UNKNOWN;
- } else {
- return xmppConnection.getServerIdentity();
- }
- }
-
- public Contact getSelfContact() {
- return getRoster().getContact(jid);
- }
-
- public boolean hasPendingPgpIntent(Conversation conversation) {
- return pgpDecryptionService != null && pgpDecryptionService.hasPendingIntent(conversation);
- }
-
- public boolean isPgpDecryptionServiceConnected() {
- return pgpDecryptionService != null && pgpDecryptionService.isConnected();
- }
-
- public boolean setShowErrorNotification(boolean newValue) {
- boolean oldValue = showErrorNotification();
- setKey("show_error",Boolean.toString(newValue));
- return newValue != oldValue;
- }
-
- public boolean showErrorNotification() {
- String key = getKey("show_error");
- return key == null || Boolean.parseBoolean(key);
- }
-
- public enum State {
- DISABLED,
- OFFLINE,
- CONNECTING,
- ONLINE,
- NO_INTERNET,
- UNAUTHORIZED(true),
- SERVER_NOT_FOUND(true),
- REGISTRATION_FAILED(true),
- REGISTRATION_CONFLICT(true),
- REGISTRATION_SUCCESSFUL,
- REGISTRATION_NOT_SUPPORTED(true),
- SECURITY_ERROR(true),
- INCOMPATIBLE_SERVER(true),
- TOR_NOT_AVAILABLE(true),
- BIND_FAILURE(true),
- HOST_UNKNOWN(true),
- REGISTRATION_PLEASE_WAIT(true),
- STREAM_ERROR(true),
- POLICY_VIOLATION(true),
- REGISTRATION_PASSWORD_TOO_WEAK(true),
- PAYMENT_REQUIRED(true),
- MISSING_INTERNET_PERMISSION(true);
-
- private final boolean isError;
-
- public boolean isError() {
- return this.isError;
- }
-
- State(final boolean isError) {
- this.isError = isError;
- }
-
- State() {
- this(false);
- }
-
- public int getReadableId() {
- switch (this) {
- case DISABLED:
- return R.string.account_status_disabled;
- case ONLINE:
- return R.string.account_status_online;
- case CONNECTING:
- return R.string.account_status_connecting;
- case OFFLINE:
- return R.string.account_status_offline;
- case UNAUTHORIZED:
- return R.string.account_status_unauthorized;
- case SERVER_NOT_FOUND:
- return R.string.account_status_not_found;
- case NO_INTERNET:
- return R.string.account_status_no_internet;
- case REGISTRATION_FAILED:
- return R.string.account_status_regis_fail;
- case REGISTRATION_CONFLICT:
- return R.string.account_status_regis_conflict;
- case REGISTRATION_SUCCESSFUL:
- return R.string.account_status_regis_success;
- case REGISTRATION_NOT_SUPPORTED:
- return R.string.account_status_regis_not_sup;
- case SECURITY_ERROR:
- return R.string.account_status_security_error;
- case INCOMPATIBLE_SERVER:
- return R.string.account_status_incompatible_server;
- case TOR_NOT_AVAILABLE:
- return R.string.account_status_tor_unavailable;
- case BIND_FAILURE:
- return R.string.account_status_bind_failure;
- case HOST_UNKNOWN:
- return R.string.account_status_host_unknown;
- case POLICY_VIOLATION:
- return R.string.account_status_policy_violation;
- case REGISTRATION_PLEASE_WAIT:
- return R.string.registration_please_wait;
- case REGISTRATION_PASSWORD_TOO_WEAK:
- return R.string.registration_password_too_weak;
- case STREAM_ERROR:
- return R.string.account_status_stream_error;
- case PAYMENT_REQUIRED:
- return R.string.payment_required;
- case MISSING_INTERNET_PERMISSION:
- return R.string.missing_internet_permission;
- default:
- return R.string.account_status_unknown;
- }
- }
- }
-
- public List<Conversation> pendingConferenceJoins = new CopyOnWriteArrayList<>();
- public List<Conversation> pendingConferenceLeaves = new CopyOnWriteArrayList<>();
-
- private static final String KEY_PGP_SIGNATURE = "pgp_signature";
- private static final String KEY_PGP_ID = "pgp_id";
-
- protected Jid jid;
- protected String password;
- protected int options = 0;
- protected String rosterVersion;
- protected State status = State.OFFLINE;
- protected final JSONObject keys;
- protected String avatar;
- protected String displayName = null;
- protected String hostname = null;
- protected int port = 5222;
- protected boolean online = false;
- private OtrService mOtrService = null;
- private AxolotlService axolotlService = null;
- private PgpDecryptionService pgpDecryptionService = null;
- private XmppConnection xmppConnection = null;
- private long mEndGracePeriod = 0L;
- private String otrFingerprint;
- private final Roster roster = new Roster(this);
- private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
- private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
- private Presence.Status presenceStatus = Presence.Status.ONLINE;
- private String presenceStatusMessage = null;
-
- 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);
- }
-
- private Account(final String uuid, final Jid jid,
- final String password, final int options, final String rosterVersion, final String keys,
- final String avatar, String displayName, String hostname, int port,
- final Presence.Status status, String statusMessage) {
- this.uuid = uuid;
- this.jid = jid;
- if (jid.isBareJid()) {
- this.setResource("mobile");
- }
- this.password = password;
- this.options = options;
- this.rosterVersion = rosterVersion;
- JSONObject tmp;
- try {
- tmp = new JSONObject(keys);
- } catch (JSONException e) {
- tmp = new JSONObject();
- }
- this.keys = tmp;
- this.avatar = avatar;
- this.displayName = displayName;
- this.hostname = hostname;
- this.port = port;
- this.presenceStatus = status;
- this.presenceStatusMessage = statusMessage;
- }
-
- public static Account fromCursor(final Cursor cursor) {
- Jid jid = null;
- try {
- jid = Jid.fromParts(cursor.getString(cursor.getColumnIndex(USERNAME)),
- cursor.getString(cursor.getColumnIndex(SERVER)), "mobile");
- } catch (final InvalidJidException ignored) {
- }
- return new Account(cursor.getString(cursor.getColumnIndex(UUID)),
- jid,
- cursor.getString(cursor.getColumnIndex(PASSWORD)),
- cursor.getInt(cursor.getColumnIndex(OPTIONS)),
- cursor.getString(cursor.getColumnIndex(ROSTERVERSION)),
- cursor.getString(cursor.getColumnIndex(KEYS)),
- cursor.getString(cursor.getColumnIndex(AVATAR)),
- cursor.getString(cursor.getColumnIndex(DISPLAY_NAME)),
- cursor.getString(cursor.getColumnIndex(HOSTNAME)),
- cursor.getInt(cursor.getColumnIndex(PORT)),
- Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS))),
- cursor.getString(cursor.getColumnIndex(STATUS_MESSAGE)));
- }
-
- public boolean isOptionSet(final int option) {
- return ((options & (1 << option)) != 0);
- }
-
- public void setOption(final int option, final boolean value) {
- if (value) {
- this.options |= 1 << option;
- } else {
- this.options &= ~(1 << option);
- }
- }
-
- public String getUsername() {
- return jid.getLocalpart();
- }
-
- public boolean setJid(final Jid next) {
- final Jid prev = this.jid != null ? this.jid.toBareJid() : null;
- this.jid = next;
- return prev == null || (next != null && !prev.equals(next.toBareJid())
- );
- }
-
- public Jid getServer() {
- return jid.toDomainJid();
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setPassword(final String password) {
- this.password = password;
- }
-
- public void setHostname(String hostname) {
- this.hostname = hostname;
- }
-
- public String getHostname() {
- return this.hostname == null ? "" : this.hostname;
- }
-
- public boolean isOnion() {
- final Jid server = getServer();
- return server != null && server.toString().toLowerCase().endsWith(".onion");
- }
-
- public void setPort(int port) {
- this.port = port;
- }
-
- public int getPort() {
- return this.port;
- }
-
- public State getStatus() {
- if (isOptionSet(OPTION_DISABLED)) {
- return State.DISABLED;
- } else {
- return this.status;
- }
- }
-
- public void setStatus(final State status) {
- this.status = status;
- }
-
- public boolean errorStatus() {
- return getStatus().isError();
- }
-
- public boolean hasErrorStatus() {
- return getXmppConnection() != null
- && (getStatus().isError() || getStatus() == State.CONNECTING)
- && getXmppConnection().getAttempt() >= 3;
- }
-
- public void setPresenceStatus(Presence.Status status) {
- this.presenceStatus = status;
- }
-
- public Presence.Status getPresenceStatus() {
- return this.presenceStatus;
- }
-
- public void setPresenceStatusMessage(String message) {
- this.presenceStatusMessage = message;
- }
-
- public String getPresenceStatusMessage() {
- return this.presenceStatusMessage;
- }
-
- public String getResource() {
- return jid.getResourcepart();
- }
-
- public boolean setResource(final String resource) {
- final String oldResource = jid.getResourcepart();
- if (oldResource == null || !oldResource.equals(resource)) {
- try {
- jid = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), resource);
- return true;
- } catch (final InvalidJidException ignored) {
- return true;
- }
- }
- return false;
- }
-
- public Jid getJid() {
- return jid;
- }
-
- public JSONObject getKeys() {
- return keys;
- }
-
- public String getKey(final String name) {
- synchronized (this.keys) {
- return this.keys.optString(name, null);
- }
- }
-
- public int getKeyAsInt(final String name, int defaultValue) {
- String key = getKey(name);
- try {
- return key == null ? defaultValue : Integer.parseInt(key);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- }
-
- public boolean setKey(final String keyName, final String keyValue) {
- synchronized (this.keys) {
- try {
- this.keys.put(keyName, keyValue);
- return true;
- } catch (final JSONException e) {
- return false;
- }
- }
- }
-
- public boolean setPrivateKeyAlias(String alias) {
- return setKey("private_key_alias", alias);
- }
-
- public String getPrivateKeyAlias() {
- return getKey("private_key_alias");
- }
-
- @Override
- public ContentValues getContentValues() {
- final ContentValues values = new ContentValues();
- values.put(UUID, uuid);
- values.put(USERNAME, jid.getLocalpart());
- values.put(SERVER, jid.getDomainpart());
- values.put(PASSWORD, password);
- values.put(OPTIONS, options);
- synchronized (this.keys) {
- values.put(KEYS, this.keys.toString());
- }
- values.put(ROSTERVERSION, rosterVersion);
- values.put(AVATAR, avatar);
- values.put(DISPLAY_NAME, displayName);
- values.put(HOSTNAME, hostname);
- values.put(PORT, port);
- values.put(STATUS, presenceStatus.toShowString());
- values.put(STATUS_MESSAGE, presenceStatusMessage);
- return values;
- }
-
- public AxolotlService getAxolotlService() {
- return axolotlService;
- }
-
- public void initAccountServices(final XmppConnectionService context) {
- this.mOtrService = new OtrService(context, this);
- this.axolotlService = new AxolotlService(this, context);
- this.pgpDecryptionService = new PgpDecryptionService(context);
- if (xmppConnection != null) {
- xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
- }
- }
-
- public OtrService getOtrService() {
- return this.mOtrService;
- }
-
- public PgpDecryptionService getPgpDecryptionService() {
- return this.pgpDecryptionService;
- }
-
- public XmppConnection getXmppConnection() {
- return this.xmppConnection;
- }
-
- public void setXmppConnection(final XmppConnection connection) {
- this.xmppConnection = connection;
- }
-
- public String getOtrFingerprint() {
- if (this.otrFingerprint == null) {
- try {
- if (this.mOtrService == null) {
- return null;
- }
- final PublicKey publicKey = this.mOtrService.getPublicKey();
- if (publicKey == null || !(publicKey instanceof DSAPublicKey)) {
- return null;
- }
- this.otrFingerprint = new OtrCryptoEngineImpl().getFingerprint(publicKey);
- return this.otrFingerprint;
- } catch (final OtrCryptoException ignored) {
- return null;
- }
- } else {
- return this.otrFingerprint;
- }
- }
-
- public String getRosterVersion() {
- if (this.rosterVersion == null) {
- return "";
- } else {
- return this.rosterVersion;
- }
- }
-
- public void setRosterVersion(final String version) {
- this.rosterVersion = version;
- }
-
- public int countPresences() {
- return this.getSelfContact().getPresences().size();
- }
-
- public String getPgpSignature() {
- return getKey(KEY_PGP_SIGNATURE);
- }
-
- public boolean setPgpSignature(String signature) {
- return setKey(KEY_PGP_SIGNATURE, signature);
- }
-
- public boolean unsetPgpSignature() {
- synchronized (this.keys) {
- return keys.remove(KEY_PGP_SIGNATURE) != null;
- }
- }
-
- public long getPgpId() {
- synchronized (this.keys) {
- if (keys.has(KEY_PGP_ID)) {
- try {
- return keys.getLong(KEY_PGP_ID);
- } catch (JSONException e) {
- return 0;
- }
- } else {
- return 0;
- }
- }
- }
-
- public boolean setPgpSignId(long pgpID) {
- synchronized (this.keys) {
- try {
- keys.put(KEY_PGP_ID, pgpID);
- } catch (JSONException e) {
- return false;
- }
- return true;
- }
- }
-
- public Roster getRoster() {
- return this.roster;
- }
-
- public List<Bookmark> getBookmarks() {
- return this.bookmarks;
- }
-
- public void setBookmarks(final List<Bookmark> bookmarks) {
- this.bookmarks = bookmarks;
- }
-
- public boolean hasBookmarkFor(final Jid conferenceJid) {
- for (final Bookmark bookmark : this.bookmarks) {
- final Jid jid = bookmark.getJid();
- if (jid != null && jid.equals(conferenceJid.toBareJid())) {
- return true;
- }
- }
- return false;
- }
-
- public boolean setAvatar(final String filename) {
- if (this.avatar != null && this.avatar.equals(filename)) {
- return false;
- } else {
- this.avatar = filename;
- return true;
- }
- }
-
- public String getAvatar() {
- return this.avatar;
- }
-
- public void activateGracePeriod(long duration) {
- this.mEndGracePeriod = SystemClock.elapsedRealtime() + duration;
- }
-
- public void deactivateGracePeriod() {
- this.mEndGracePeriod = 0L;
- }
-
- public boolean inGracePeriod() {
- return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
- }
-
- public String getShareableUri() {
- List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
- String uri = "xmpp:"+this.getJid().toBareJid().toString();
- if (fingerprints.size() > 0) {
- StringBuilder builder = new StringBuilder(uri);
- builder.append('?');
- for(int i = 0; i < fingerprints.size(); ++i) {
- XmppUri.FingerprintType type = fingerprints.get(i).type;
- if (type == XmppUri.FingerprintType.OMEMO) {
- builder.append(XmppUri.OMEMO_URI_PARAM);
- builder.append(fingerprints.get(i).deviceId);
- } else if (type == XmppUri.FingerprintType.OTR) {
- builder.append(XmppUri.OTR_URI_PARAM);
- }
- builder.append('=');
- builder.append(fingerprints.get(i).fingerprint);
- if (i != fingerprints.size() -1) {
- builder.append(';');
- }
- }
- return builder.toString();
- } else {
- return uri;
- }
- }
-
- private List<XmppUri.Fingerprint> getFingerprints() {
- ArrayList<XmppUri.Fingerprint> fingerprints = new ArrayList<>();
- final String otr = this.getOtrFingerprint();
- if (otr != null) {
- fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OTR,otr));
- }
- fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OMEMO,axolotlService.getOwnFingerprint().substring(2),axolotlService.getOwnDeviceId()));
- for(XmppAxolotlSession session : axolotlService.findOwnSessions()) {
- if (session.getTrust().isVerified() && session.getTrust().isActive()) {
- fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OMEMO,session.getFingerprint().substring(2).replaceAll("\\s",""),session.getRemoteAddress().getDeviceId()));
- }
- }
- return fingerprints;
- }
-
- public boolean isBlocked(final ListItem contact) {
- final Jid jid = contact.getJid();
- return jid != null && (blocklist.contains(jid.toBareJid()) || blocklist.contains(jid.toDomainJid()));
- }
-
- public boolean isBlocked(final Jid jid) {
- return jid != null && blocklist.contains(jid.toBareJid());
- }
-
- public Collection<Jid> getBlocklist() {
- return this.blocklist;
- }
-
- public void clearBlocklist() {
- getBlocklist().clear();
- }
-
- public boolean isOnlineAndConnected() {
- return this.getStatus() == State.ONLINE && this.getXmppConnection() != null;
- }
+ public static final String TABLENAME = "accounts";
+
+ public static final String USERNAME = "username";
+ public static final String SERVER = "server";
+ public static final String PASSWORD = "password";
+ public static final String OPTIONS = "options";
+ public static final String ROSTERVERSION = "rosterversion";
+ public static final String KEYS = "keys";
+ public static final String AVATAR = "avatar";
+ public static final String DISPLAY_NAME = "display_name";
+ public static final String HOSTNAME = "hostname";
+ public static final String PORT = "port";
+ public static final String STATUS = "status";
+ public static final String STATUS_MESSAGE = "status_message";
+
+ public static final String PINNED_MECHANISM_KEY = "pinned_mechanism";
+
+ public static final int OPTION_USETLS = 0;
+ public static final int OPTION_DISABLED = 1;
+ public static final int OPTION_REGISTER = 2;
+ public static final int OPTION_USECOMPRESSION = 3;
+ public static final int OPTION_MAGIC_CREATE = 4;
+ public final HashSet<Pair<String, String>> inProgressDiscoFetches = new HashSet<>();
+
+ public boolean httpUploadAvailable(long filesize) {
+ return xmppConnection != null && xmppConnection.getFeatures().httpUpload(filesize);
+ }
+
+ public boolean httpUploadAvailable() {
+ return httpUploadAvailable(0);
+ }
+
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public XmppConnection.Identity getServerIdentity() {
+ if (xmppConnection == null) {
+ return XmppConnection.Identity.UNKNOWN;
+ } else {
+ return xmppConnection.getServerIdentity();
+ }
+ }
+
+ public Contact getSelfContact() {
+ return getRoster().getContact(jid);
+ }
+
+ public boolean hasPendingPgpIntent(Conversation conversation) {
+ return pgpDecryptionService != null && pgpDecryptionService.hasPendingIntent(conversation);
+ }
+
+ public boolean isPgpDecryptionServiceConnected() {
+ return pgpDecryptionService != null && pgpDecryptionService.isConnected();
+ }
+
+ public boolean setShowErrorNotification(boolean newValue) {
+ boolean oldValue = showErrorNotification();
+ setKey("show_error", Boolean.toString(newValue));
+ return newValue != oldValue;
+ }
+
+ public boolean showErrorNotification() {
+ String key = getKey("show_error");
+ return key == null || Boolean.parseBoolean(key);
+ }
+
+ public enum State {
+ DISABLED,
+ OFFLINE,
+ CONNECTING,
+ ONLINE,
+ NO_INTERNET,
+ UNAUTHORIZED(true),
+ SERVER_NOT_FOUND(true),
+ REGISTRATION_FAILED(true),
+ REGISTRATION_CONFLICT(true),
+ REGISTRATION_SUCCESSFUL,
+ REGISTRATION_NOT_SUPPORTED(true),
+ SECURITY_ERROR(true),
+ INCOMPATIBLE_SERVER(true),
+ TOR_NOT_AVAILABLE(true),
+ BIND_FAILURE(true),
+ HOST_UNKNOWN(true),
+ REGISTRATION_PLEASE_WAIT(true),
+ STREAM_ERROR(true),
+ POLICY_VIOLATION(true),
+ REGISTRATION_PASSWORD_TOO_WEAK(true),
+ PAYMENT_REQUIRED(true),
+ MISSING_INTERNET_PERMISSION(true);
+
+ private final boolean isError;
+
+ public boolean isError() {
+ return this.isError;
+ }
+
+ State(final boolean isError) {
+ this.isError = isError;
+ }
+
+ State() {
+ this(false);
+ }
+
+ public int getReadableId() {
+ switch (this) {
+ case DISABLED:
+ return R.string.account_status_disabled;
+ case ONLINE:
+ return R.string.account_status_online;
+ case CONNECTING:
+ return R.string.account_status_connecting;
+ case OFFLINE:
+ return R.string.account_status_offline;
+ case UNAUTHORIZED:
+ return R.string.account_status_unauthorized;
+ case SERVER_NOT_FOUND:
+ return R.string.account_status_not_found;
+ case NO_INTERNET:
+ return R.string.account_status_no_internet;
+ case REGISTRATION_FAILED:
+ return R.string.account_status_regis_fail;
+ case REGISTRATION_CONFLICT:
+ return R.string.account_status_regis_conflict;
+ case REGISTRATION_SUCCESSFUL:
+ return R.string.account_status_regis_success;
+ case REGISTRATION_NOT_SUPPORTED:
+ return R.string.account_status_regis_not_sup;
+ case SECURITY_ERROR:
+ return R.string.account_status_security_error;
+ case INCOMPATIBLE_SERVER:
+ return R.string.account_status_incompatible_server;
+ case TOR_NOT_AVAILABLE:
+ return R.string.account_status_tor_unavailable;
+ case BIND_FAILURE:
+ return R.string.account_status_bind_failure;
+ case HOST_UNKNOWN:
+ return R.string.account_status_host_unknown;
+ case POLICY_VIOLATION:
+ return R.string.account_status_policy_violation;
+ case REGISTRATION_PLEASE_WAIT:
+ return R.string.registration_please_wait;
+ case REGISTRATION_PASSWORD_TOO_WEAK:
+ return R.string.registration_password_too_weak;
+ case STREAM_ERROR:
+ return R.string.account_status_stream_error;
+ case PAYMENT_REQUIRED:
+ return R.string.payment_required;
+ case MISSING_INTERNET_PERMISSION:
+ return R.string.missing_internet_permission;
+ default:
+ return R.string.account_status_unknown;
+ }
+ }
+ }
+
+ public List<Conversation> pendingConferenceJoins = new CopyOnWriteArrayList<>();
+ public List<Conversation> pendingConferenceLeaves = new CopyOnWriteArrayList<>();
+
+ private static final String KEY_PGP_SIGNATURE = "pgp_signature";
+ private static final String KEY_PGP_ID = "pgp_id";
+
+ protected Jid jid;
+ protected String password;
+ protected int options = 0;
+ protected String rosterVersion;
+ protected State status = State.OFFLINE;
+ protected final JSONObject keys;
+ protected String avatar;
+ protected String displayName = null;
+ protected String hostname = null;
+ protected int port = 5222;
+ protected boolean online = false;
+ private OtrService mOtrService = null;
+ private AxolotlService axolotlService = null;
+ private PgpDecryptionService pgpDecryptionService = null;
+ private XmppConnection xmppConnection = null;
+ private long mEndGracePeriod = 0L;
+ private String otrFingerprint;
+ private final Roster roster = new Roster(this);
+ private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
+ private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
+ private Presence.Status presenceStatus = Presence.Status.ONLINE;
+ private String presenceStatusMessage = null;
+
+ 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);
+ }
+
+ private Account(final String uuid, final Jid jid,
+ final String password, final int options, final String rosterVersion, final String keys,
+ final String avatar, String displayName, String hostname, int port,
+ final Presence.Status status, String statusMessage) {
+ this.uuid = uuid;
+ this.jid = jid;
+ if (jid.isBareJid()) {
+ this.setResource("mobile");
+ }
+ this.password = password;
+ this.options = options;
+ this.rosterVersion = rosterVersion;
+ JSONObject tmp;
+ try {
+ tmp = new JSONObject(keys);
+ } catch (JSONException e) {
+ tmp = new JSONObject();
+ }
+ this.keys = tmp;
+ this.avatar = avatar;
+ this.displayName = displayName;
+ this.hostname = hostname;
+ this.port = port;
+ this.presenceStatus = status;
+ this.presenceStatusMessage = statusMessage;
+ }
+
+ public static Account fromCursor(final Cursor cursor) {
+ Jid jid = null;
+ try {
+ jid = Jid.fromParts(cursor.getString(cursor.getColumnIndex(USERNAME)),
+ cursor.getString(cursor.getColumnIndex(SERVER)), "mobile");
+ } catch (final InvalidJidException ignored) {
+ }
+ return new Account(cursor.getString(cursor.getColumnIndex(UUID)),
+ jid,
+ cursor.getString(cursor.getColumnIndex(PASSWORD)),
+ cursor.getInt(cursor.getColumnIndex(OPTIONS)),
+ cursor.getString(cursor.getColumnIndex(ROSTERVERSION)),
+ cursor.getString(cursor.getColumnIndex(KEYS)),
+ cursor.getString(cursor.getColumnIndex(AVATAR)),
+ cursor.getString(cursor.getColumnIndex(DISPLAY_NAME)),
+ cursor.getString(cursor.getColumnIndex(HOSTNAME)),
+ cursor.getInt(cursor.getColumnIndex(PORT)),
+ Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS))),
+ cursor.getString(cursor.getColumnIndex(STATUS_MESSAGE)));
+ }
+
+ public boolean isOptionSet(final int option) {
+ return ((options & (1 << option)) != 0);
+ }
+
+ public void setOption(final int option, final boolean value) {
+ if (value) {
+ this.options |= 1 << option;
+ } else {
+ this.options &= ~(1 << option);
+ }
+ }
+
+ public String getUsername() {
+ return jid.getLocalpart();
+ }
+
+ public boolean setJid(final Jid next) {
+ final Jid prev = this.jid != null ? this.jid.toBareJid() : null;
+ this.jid = next;
+ return prev == null || (next != null && !prev.equals(next.toBareJid())
+ );
+ }
+
+ public Jid getServer() {
+ return jid.toDomainJid();
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(final String password) {
+ this.password = password;
+ }
+
+ public void setHostname(String hostname) {
+ this.hostname = hostname;
+ }
+
+ public String getHostname() {
+ return this.hostname == null ? "" : this.hostname;
+ }
+
+ public boolean isOnion() {
+ final Jid server = getServer();
+ return server != null && server.toString().toLowerCase().endsWith(".onion");
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public int getPort() {
+ return this.port;
+ }
+
+ public State getStatus() {
+ if (isOptionSet(OPTION_DISABLED)) {
+ return State.DISABLED;
+ } else {
+ return this.status;
+ }
+ }
+
+ public void setStatus(final State status) {
+ this.status = status;
+ }
+
+ public boolean errorStatus() {
+ return getStatus().isError();
+ }
+
+ public boolean hasErrorStatus() {
+ return getXmppConnection() != null
+ && (getStatus().isError() || getStatus() == State.CONNECTING)
+ && getXmppConnection().getAttempt() >= 3;
+ }
+
+ public void setPresenceStatus(Presence.Status status) {
+ this.presenceStatus = status;
+ }
+
+ public Presence.Status getPresenceStatus() {
+ return this.presenceStatus;
+ }
+
+ public void setPresenceStatusMessage(String message) {
+ this.presenceStatusMessage = message;
+ }
+
+ public String getPresenceStatusMessage() {
+ return this.presenceStatusMessage;
+ }
+
+ public String getResource() {
+ return jid.getResourcepart();
+ }
+
+ public boolean setResource(final String resource) {
+ final String oldResource = jid.getResourcepart();
+ if (oldResource == null || !oldResource.equals(resource)) {
+ try {
+ jid = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), resource);
+ return true;
+ } catch (final InvalidJidException ignored) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Jid getJid() {
+ return jid;
+ }
+
+ public JSONObject getKeys() {
+ return keys;
+ }
+
+ public String getKey(final String name) {
+ synchronized (this.keys) {
+ return this.keys.optString(name, null);
+ }
+ }
+
+ public int getKeyAsInt(final String name, int defaultValue) {
+ String key = getKey(name);
+ try {
+ return key == null ? defaultValue : Integer.parseInt(key);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+
+ public boolean setKey(final String keyName, final String keyValue) {
+ synchronized (this.keys) {
+ try {
+ this.keys.put(keyName, keyValue);
+ return true;
+ } catch (final JSONException e) {
+ return false;
+ }
+ }
+ }
+
+ public boolean setPrivateKeyAlias(String alias) {
+ return setKey("private_key_alias", alias);
+ }
+
+ public String getPrivateKeyAlias() {
+ return getKey("private_key_alias");
+ }
+
+ @Override
+ public ContentValues getContentValues() {
+ final ContentValues values = new ContentValues();
+ values.put(UUID, uuid);
+ values.put(USERNAME, jid.getLocalpart());
+ values.put(SERVER, jid.getDomainpart());
+ values.put(PASSWORD, password);
+ values.put(OPTIONS, options);
+ synchronized (this.keys) {
+ values.put(KEYS, this.keys.toString());
+ }
+ values.put(ROSTERVERSION, rosterVersion);
+ values.put(AVATAR, avatar);
+ values.put(DISPLAY_NAME, displayName);
+ values.put(HOSTNAME, hostname);
+ values.put(PORT, port);
+ values.put(STATUS, presenceStatus.toShowString());
+ values.put(STATUS_MESSAGE, presenceStatusMessage);
+ return values;
+ }
+
+ public AxolotlService getAxolotlService() {
+ return axolotlService;
+ }
+
+ public void initAccountServices(final XmppConnectionService context) {
+ this.mOtrService = new OtrService(context, this);
+ this.axolotlService = new AxolotlService(this, context);
+ this.pgpDecryptionService = new PgpDecryptionService(context);
+ if (xmppConnection != null) {
+ xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
+ }
+ }
+
+ public OtrService getOtrService() {
+ return this.mOtrService;
+ }
+
+ public PgpDecryptionService getPgpDecryptionService() {
+ return this.pgpDecryptionService;
+ }
+
+ public XmppConnection getXmppConnection() {
+ return this.xmppConnection;
+ }
+
+ public void setXmppConnection(final XmppConnection connection) {
+ this.xmppConnection = connection;
+ }
+
+ public String getOtrFingerprint() {
+ if (this.otrFingerprint == null) {
+ try {
+ if (this.mOtrService == null) {
+ return null;
+ }
+ final PublicKey publicKey = this.mOtrService.getPublicKey();
+ if (publicKey == null || !(publicKey instanceof DSAPublicKey)) {
+ return null;
+ }
+ this.otrFingerprint = new OtrCryptoEngineImpl().getFingerprint(publicKey);
+ return this.otrFingerprint;
+ } catch (final OtrCryptoException ignored) {
+ return null;
+ }
+ } else {
+ return this.otrFingerprint;
+ }
+ }
+
+ public String getRosterVersion() {
+ if (this.rosterVersion == null) {
+ return "";
+ } else {
+ return this.rosterVersion;
+ }
+ }
+
+ public void setRosterVersion(final String version) {
+ this.rosterVersion = version;
+ }
+
+ public int countPresences() {
+ return this.getSelfContact().getPresences().size();
+ }
+
+ public String getPgpSignature() {
+ return getKey(KEY_PGP_SIGNATURE);
+ }
+
+ public boolean setPgpSignature(String signature) {
+ return setKey(KEY_PGP_SIGNATURE, signature);
+ }
+
+ public boolean unsetPgpSignature() {
+ synchronized (this.keys) {
+ return keys.remove(KEY_PGP_SIGNATURE) != null;
+ }
+ }
+
+ public long getPgpId() {
+ synchronized (this.keys) {
+ if (keys.has(KEY_PGP_ID)) {
+ try {
+ return keys.getLong(KEY_PGP_ID);
+ } catch (JSONException e) {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ public boolean setPgpSignId(long pgpID) {
+ synchronized (this.keys) {
+ try {
+ keys.put(KEY_PGP_ID, pgpID);
+ } catch (JSONException e) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public Roster getRoster() {
+ return this.roster;
+ }
+
+ public List<Bookmark> getBookmarks() {
+ return this.bookmarks;
+ }
+
+ public void setBookmarks(final List<Bookmark> bookmarks) {
+ this.bookmarks = bookmarks;
+ }
+
+ public boolean hasBookmarkFor(final Jid conferenceJid) {
+ for (final Bookmark bookmark : this.bookmarks) {
+ final Jid jid = bookmark.getJid();
+ if (jid != null && jid.equals(conferenceJid.toBareJid())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean setAvatar(final String filename) {
+ if (this.avatar != null && this.avatar.equals(filename)) {
+ return false;
+ } else {
+ this.avatar = filename;
+ return true;
+ }
+ }
+
+ public String getAvatar() {
+ return this.avatar;
+ }
+
+ public void activateGracePeriod(long duration) {
+ this.mEndGracePeriod = SystemClock.elapsedRealtime() + duration;
+ }
+
+ public void deactivateGracePeriod() {
+ this.mEndGracePeriod = 0L;
+ }
+
+ public boolean inGracePeriod() {
+ return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
+ }
+
+ public String getShareableUri() {
+ List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
+ String uri = "xmpp:" + this.getJid().toBareJid().toString();
+ if (fingerprints.size() > 0) {
+ StringBuilder builder = new StringBuilder(uri);
+ builder.append('?');
+ for (int i = 0; i < fingerprints.size(); ++i) {
+ XmppUri.FingerprintType type = fingerprints.get(i).type;
+ if (type == XmppUri.FingerprintType.OMEMO) {
+ builder.append(XmppUri.OMEMO_URI_PARAM);
+ builder.append(fingerprints.get(i).deviceId);
+ } else if (type == XmppUri.FingerprintType.OTR) {
+ builder.append(XmppUri.OTR_URI_PARAM);
+ }
+ builder.append('=');
+ builder.append(fingerprints.get(i).fingerprint);
+ if (i != fingerprints.size() - 1) {
+ builder.append(';');
+ }
+ }
+ return builder.toString();
+ } else {
+ return uri;
+ }
+ }
+
+ private List<XmppUri.Fingerprint> getFingerprints() {
+ ArrayList<XmppUri.Fingerprint> fingerprints = new ArrayList<>();
+ final String otr = this.getOtrFingerprint();
+ if (otr != null) {
+ fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OTR, otr));
+ }
+ fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OMEMO, axolotlService.getOwnFingerprint().substring(2), axolotlService.getOwnDeviceId()));
+ for (XmppAxolotlSession session : axolotlService.findOwnSessions()) {
+ if (session.getTrust().isVerified() && session.getTrust().isActive()) {
+ fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OMEMO, session.getFingerprint().substring(2).replaceAll("\\s", ""), session.getRemoteAddress().getDeviceId()));
+ }
+ }
+ return fingerprints;
+ }
+
+ public boolean isBlocked(final ListItem contact) {
+ final Jid jid = contact.getJid();
+ return jid != null && (blocklist.contains(jid.toBareJid()) || blocklist.contains(jid.toDomainJid()));
+ }
+
+ public boolean isBlocked(final Jid jid) {
+ return jid != null && blocklist.contains(jid.toBareJid());
+ }
+
+ public Collection<Jid> getBlocklist() {
+ return this.blocklist;
+ }
+
+ public void clearBlocklist() {
+ getBlocklist().clear();
+ }
+
+ public boolean isOnlineAndConnected() {
+ return this.getStatus() == State.ONLINE && this.getXmppConnection() != null;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/Blockable.java b/src/main/java/de/pixart/messenger/entities/Blockable.java
index 07860bcfe..9357f1cdb 100644
--- a/src/main/java/de/pixart/messenger/entities/Blockable.java
+++ b/src/main/java/de/pixart/messenger/entities/Blockable.java
@@ -3,9 +3,13 @@ package de.pixart.messenger.entities;
import de.pixart.messenger.xmpp.jid.Jid;
public interface Blockable {
- public boolean isBlocked();
- public boolean isDomainBlocked();
- public Jid getBlockedJid();
- public Jid getJid();
- public Account getAccount();
+ public boolean isBlocked();
+
+ public boolean isDomainBlocked();
+
+ public Jid getBlockedJid();
+
+ public Jid getJid();
+
+ public Account getAccount();
}
diff --git a/src/main/java/de/pixart/messenger/entities/Bookmark.java b/src/main/java/de/pixart/messenger/entities/Bookmark.java
index 9c99a2baf..c2e31039b 100644
--- a/src/main/java/de/pixart/messenger/entities/Bookmark.java
+++ b/src/main/java/de/pixart/messenger/entities/Bookmark.java
@@ -12,160 +12,160 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class Bookmark extends Element implements ListItem {
- private Account account;
- private Conversation mJoinedConversation;
-
- public Bookmark(final Account account, final Jid jid) {
- super("conference");
- this.setAttribute("jid", jid.toString());
- this.account = account;
- }
-
- private Bookmark(Account account) {
- super("conference");
- this.account = account;
- }
-
- public static Bookmark parse(Element element, Account account) {
- Bookmark bookmark = new Bookmark(account);
- bookmark.setAttributes(element.getAttributes());
- bookmark.setChildren(element.getChildren());
- return bookmark;
- }
-
- public void setAutojoin(boolean autojoin) {
- if (autojoin) {
- this.setAttribute("autojoin", "true");
- } else {
- this.setAttribute("autojoin", "false");
- }
- }
-
- @Override
- public int compareTo(final ListItem another) {
- return this.getDisplayName().compareToIgnoreCase(
- another.getDisplayName());
- }
-
- @Override
- public String getDisplayName() {
- if (this.mJoinedConversation != null) {
- return this.mJoinedConversation.getName();
- } else if (getBookmarkName() != null
- && !getBookmarkName().trim().isEmpty()) {
- return getBookmarkName().trim();
- } else {
- Jid jid = this.getJid();
- String name = jid != null ? jid.getLocalpart() : getAttribute("jid");
- return name != null ? name : "";
- }
- }
-
- @Override
- public String getDisplayJid() {
- Jid jid = getJid();
- if (jid != null) {
- return jid.toString();
- } else {
- return getAttribute("jid"); //fallback if jid wasn't parsable
- }
- }
-
- @Override
- public Jid getJid() {
- return this.getAttributeAsJid("jid");
- }
-
- @Override
- public List<Tag> getTags(Context context) {
- ArrayList<Tag> tags = new ArrayList<>();
- for (Element element : getChildren()) {
- if (element.getName().equals("group") && element.getContent() != null) {
- String group = element.getContent();
- tags.add(new Tag(group, UIHelper.getColorForName(group)));
- }
- }
- return tags;
- }
-
- public String getNick() {
- return this.findChildContent("nick");
- }
-
- public void setNick(String nick) {
- Element element = this.findChild("nick");
- if (element == null) {
- element = this.addChild("nick");
- }
- element.setContent(nick);
- }
-
- public boolean autojoin() {
- return this.getAttributeAsBoolean("autojoin");
- }
-
- public String getPassword() {
- return this.findChildContent("password");
- }
-
- public void setPassword(String password) {
- Element element = this.findChild("password");
- if (element != null) {
- element.setContent(password);
- }
- }
-
- @Override
- public boolean match(Context context, String needle) {
- if (needle == null) {
- return true;
- }
- needle = needle.toLowerCase(Locale.US);
- final Jid jid = getJid();
- return (jid != null && jid.toString().contains(needle)) ||
- getDisplayName().toLowerCase(Locale.US).contains(needle) ||
- matchInTag(context, needle);
- }
-
- private boolean matchInTag(Context context, String needle) {
- needle = needle.toLowerCase(Locale.US);
- for (Tag tag : getTags(context)) {
- if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
- return true;
- }
- }
- return false;
- }
-
- public Account getAccount() {
- return this.account;
- }
-
- public Conversation getConversation() {
- return this.mJoinedConversation;
- }
-
- public void setConversation(Conversation conversation) {
- this.mJoinedConversation = conversation;
- }
-
- public String getBookmarkName() {
- return this.getAttribute("name");
- }
-
- public boolean setBookmarkName(String name) {
- String before = getBookmarkName();
- if (name != null && !name.equals(before)) {
- this.setAttribute("name", name);
- return true;
- } else {
- return false;
- }
- }
-
- public void unregisterConversation() {
- if (this.mJoinedConversation != null) {
- this.mJoinedConversation.deregisterWithBookmark();
- }
- }
+ private Account account;
+ private Conversation mJoinedConversation;
+
+ public Bookmark(final Account account, final Jid jid) {
+ super("conference");
+ this.setAttribute("jid", jid.toString());
+ this.account = account;
+ }
+
+ private Bookmark(Account account) {
+ super("conference");
+ this.account = account;
+ }
+
+ public static Bookmark parse(Element element, Account account) {
+ Bookmark bookmark = new Bookmark(account);
+ bookmark.setAttributes(element.getAttributes());
+ bookmark.setChildren(element.getChildren());
+ return bookmark;
+ }
+
+ public void setAutojoin(boolean autojoin) {
+ if (autojoin) {
+ this.setAttribute("autojoin", "true");
+ } else {
+ this.setAttribute("autojoin", "false");
+ }
+ }
+
+ @Override
+ public int compareTo(final ListItem another) {
+ return this.getDisplayName().compareToIgnoreCase(
+ another.getDisplayName());
+ }
+
+ @Override
+ public String getDisplayName() {
+ if (this.mJoinedConversation != null) {
+ return this.mJoinedConversation.getName();
+ } else if (getBookmarkName() != null
+ && !getBookmarkName().trim().isEmpty()) {
+ return getBookmarkName().trim();
+ } else {
+ Jid jid = this.getJid();
+ String name = jid != null ? jid.getLocalpart() : getAttribute("jid");
+ return name != null ? name : "";
+ }
+ }
+
+ @Override
+ public String getDisplayJid() {
+ Jid jid = getJid();
+ if (jid != null) {
+ return jid.toString();
+ } else {
+ return getAttribute("jid"); //fallback if jid wasn't parsable
+ }
+ }
+
+ @Override
+ public Jid getJid() {
+ return this.getAttributeAsJid("jid");
+ }
+
+ @Override
+ public List<Tag> getTags(Context context) {
+ ArrayList<Tag> tags = new ArrayList<>();
+ for (Element element : getChildren()) {
+ if (element.getName().equals("group") && element.getContent() != null) {
+ String group = element.getContent();
+ tags.add(new Tag(group, UIHelper.getColorForName(group)));
+ }
+ }
+ return tags;
+ }
+
+ public String getNick() {
+ return this.findChildContent("nick");
+ }
+
+ public void setNick(String nick) {
+ Element element = this.findChild("nick");
+ if (element == null) {
+ element = this.addChild("nick");
+ }
+ element.setContent(nick);
+ }
+
+ public boolean autojoin() {
+ return this.getAttributeAsBoolean("autojoin");
+ }
+
+ public String getPassword() {
+ return this.findChildContent("password");
+ }
+
+ public void setPassword(String password) {
+ Element element = this.findChild("password");
+ if (element != null) {
+ element.setContent(password);
+ }
+ }
+
+ @Override
+ public boolean match(Context context, String needle) {
+ if (needle == null) {
+ return true;
+ }
+ needle = needle.toLowerCase(Locale.US);
+ final Jid jid = getJid();
+ return (jid != null && jid.toString().contains(needle)) ||
+ getDisplayName().toLowerCase(Locale.US).contains(needle) ||
+ matchInTag(context, needle);
+ }
+
+ private boolean matchInTag(Context context, String needle) {
+ needle = needle.toLowerCase(Locale.US);
+ for (Tag tag : getTags(context)) {
+ if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Account getAccount() {
+ return this.account;
+ }
+
+ public Conversation getConversation() {
+ return this.mJoinedConversation;
+ }
+
+ public void setConversation(Conversation conversation) {
+ this.mJoinedConversation = conversation;
+ }
+
+ public String getBookmarkName() {
+ return this.getAttribute("name");
+ }
+
+ public boolean setBookmarkName(String name) {
+ String before = getBookmarkName();
+ if (name != null && !name.equals(before)) {
+ this.setAttribute("name", name);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void unregisterConversation() {
+ if (this.mJoinedConversation != null) {
+ this.mJoinedConversation.deregisterWithBookmark();
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/Contact.java b/src/main/java/de/pixart/messenger/entities/Contact.java
index 32af78de0..06f7ea4d0 100644
--- a/src/main/java/de/pixart/messenger/entities/Contact.java
+++ b/src/main/java/de/pixart/messenger/entities/Contact.java
@@ -22,534 +22,535 @@ import de.pixart.messenger.xmpp.jid.Jid;
import de.pixart.messenger.xmpp.pep.Avatar;
public class Contact implements ListItem, Blockable {
- public static final String TABLENAME = "contacts";
-
- public static final String SYSTEMNAME = "systemname";
- public static final String SERVERNAME = "servername";
- public static final String JID = "jid";
- public static final String OPTIONS = "options";
- public static final String SYSTEMACCOUNT = "systemaccount";
- public static final String PHOTOURI = "photouri";
- public static final String KEYS = "pgpkey";
- public static final String ACCOUNT = "accountUuid";
- public static final String AVATAR = "avatar";
- public static final String LAST_PRESENCE = "last_presence";
- public static final String LAST_TIME = "last_time";
- public static final String GROUPS = "groups";
- protected String accountUuid;
- protected String systemName;
- protected String serverName;
- protected String presenceName;
- protected String commonName;
- protected Jid jid;
- protected int subscription = 0;
- protected String systemAccount;
- protected String photoUri;
- protected JSONObject keys = new JSONObject();
- protected JSONArray groups = new JSONArray();
- protected final Presences presences = new Presences();
- protected Account account;
- protected Avatar avatar;
-
- private boolean mActive = false;
- private long mLastseen = 0;
- private String mLastPresence = null;
-
- public Contact(final String account, final String systemName, final String serverName,
- final Jid jid, final int subscription, final String photoUri,
- final String systemAccount, final String keys, final String avatar, final long lastseen,
- final String presence, final String groups) {
- this.accountUuid = account;
- this.systemName = systemName;
- this.serverName = serverName;
- this.jid = jid;
- this.subscription = subscription;
- this.photoUri = photoUri;
- this.systemAccount = systemAccount;
- try {
- this.keys = (keys == null ? new JSONObject("") : new JSONObject(keys));
- } catch (JSONException e) {
- this.keys = new JSONObject();
- }
- if (avatar != null) {
- this.avatar = new Avatar();
- this.avatar.sha1sum = avatar;
- this.avatar.origin = Avatar.Origin.VCARD; //always assume worst
- }
- try {
- this.groups = (groups == null ? new JSONArray() : new JSONArray(groups));
- } catch (JSONException e) {
- this.groups = new JSONArray();
- }
- this.mLastseen = lastseen;
- this.mLastPresence = presence;
- }
-
- public Contact(final Jid jid) {
- this.jid = jid;
- }
-
- public static Contact fromCursor(final Cursor cursor) {
- final Jid jid;
- try {
- jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(JID)), true);
- } catch (final InvalidJidException e) {
- // TODO: Borked DB... handle this somehow?
- return null;
- }
- return new Contact(cursor.getString(cursor.getColumnIndex(ACCOUNT)),
- cursor.getString(cursor.getColumnIndex(SYSTEMNAME)),
- cursor.getString(cursor.getColumnIndex(SERVERNAME)),
- jid,
- cursor.getInt(cursor.getColumnIndex(OPTIONS)),
- cursor.getString(cursor.getColumnIndex(PHOTOURI)),
- cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)),
- cursor.getString(cursor.getColumnIndex(KEYS)),
- cursor.getString(cursor.getColumnIndex(AVATAR)),
- cursor.getLong(cursor.getColumnIndex(LAST_TIME)),
- cursor.getString(cursor.getColumnIndex(LAST_PRESENCE)),
- cursor.getString(cursor.getColumnIndex(GROUPS)));
- }
-
- public String getDisplayName() {
- if (this.commonName != null && Config.X509_VERIFICATION) {
- return this.commonName;
- } else if (this.systemName != null) {
- return this.systemName;
- } else if (this.serverName != null) {
- return this.serverName;
+ public static final String TABLENAME = "contacts";
+
+ public static final String SYSTEMNAME = "systemname";
+ public static final String SERVERNAME = "servername";
+ public static final String JID = "jid";
+ public static final String OPTIONS = "options";
+ public static final String SYSTEMACCOUNT = "systemaccount";
+ public static final String PHOTOURI = "photouri";
+ public static final String KEYS = "pgpkey";
+ public static final String ACCOUNT = "accountUuid";
+ public static final String AVATAR = "avatar";
+ public static final String LAST_PRESENCE = "last_presence";
+ public static final String LAST_TIME = "last_time";
+ public static final String GROUPS = "groups";
+ protected String accountUuid;
+ protected String systemName;
+ protected String serverName;
+ protected String presenceName;
+ protected String commonName;
+ protected Jid jid;
+ protected int subscription = 0;
+ protected String systemAccount;
+ protected String photoUri;
+ protected JSONObject keys = new JSONObject();
+ protected JSONArray groups = new JSONArray();
+ protected final Presences presences = new Presences();
+ protected Account account;
+ protected Avatar avatar;
+
+ private boolean mActive = false;
+ private long mLastseen = 0;
+ private String mLastPresence = null;
+
+ public Contact(final String account, final String systemName, final String serverName,
+ final Jid jid, final int subscription, final String photoUri,
+ final String systemAccount, final String keys, final String avatar, final long lastseen,
+ final String presence, final String groups) {
+ this.accountUuid = account;
+ this.systemName = systemName;
+ this.serverName = serverName;
+ this.jid = jid;
+ this.subscription = subscription;
+ this.photoUri = photoUri;
+ this.systemAccount = systemAccount;
+ try {
+ this.keys = (keys == null ? new JSONObject("") : new JSONObject(keys));
+ } catch (JSONException e) {
+ this.keys = new JSONObject();
+ }
+ if (avatar != null) {
+ this.avatar = new Avatar();
+ this.avatar.sha1sum = avatar;
+ this.avatar.origin = Avatar.Origin.VCARD; //always assume worst
+ }
+ try {
+ this.groups = (groups == null ? new JSONArray() : new JSONArray(groups));
+ } catch (JSONException e) {
+ this.groups = new JSONArray();
+ }
+ this.mLastseen = lastseen;
+ this.mLastPresence = presence;
+ }
+
+ public Contact(final Jid jid) {
+ this.jid = jid;
+ }
+
+ public static Contact fromCursor(final Cursor cursor) {
+ final Jid jid;
+ try {
+ jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(JID)), true);
+ } catch (final InvalidJidException e) {
+ // TODO: Borked DB... handle this somehow?
+ return null;
+ }
+ return new Contact(cursor.getString(cursor.getColumnIndex(ACCOUNT)),
+ cursor.getString(cursor.getColumnIndex(SYSTEMNAME)),
+ cursor.getString(cursor.getColumnIndex(SERVERNAME)),
+ jid,
+ cursor.getInt(cursor.getColumnIndex(OPTIONS)),
+ cursor.getString(cursor.getColumnIndex(PHOTOURI)),
+ cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)),
+ cursor.getString(cursor.getColumnIndex(KEYS)),
+ cursor.getString(cursor.getColumnIndex(AVATAR)),
+ cursor.getLong(cursor.getColumnIndex(LAST_TIME)),
+ cursor.getString(cursor.getColumnIndex(LAST_PRESENCE)),
+ cursor.getString(cursor.getColumnIndex(GROUPS)));
+ }
+
+ public String getDisplayName() {
+ if (this.commonName != null && Config.X509_VERIFICATION) {
+ return this.commonName;
+ } else if (this.systemName != null) {
+ return this.systemName;
+ } else if (this.serverName != null) {
+ return this.serverName;
} else if (this.presenceName != null && mutualPresenceSubscription()) {
- return this.presenceName;
- } else if (jid.hasLocalpart()) {
- return jid.getLocalpart();
- } else {
- return jid.getDomainpart();
- }
- }
-
- @Override
- public String getDisplayJid() {
- if (jid != null) {
- return jid.toString();
- } else {
- return null;
- }
- }
-
- public String getProfilePhoto() {
- return this.photoUri;
- }
-
- public Jid getJid() {
- return jid;
- }
-
- @Override
- public List<Tag> getTags(Context context) {
- final ArrayList<Tag> tags = new ArrayList<>();
- for (final String group : getGroups()) {
- tags.add(new Tag(group, UIHelper.getColorForName(group)));
- }
- Presence.Status status = getShownStatus();
- if (status != Presence.Status.OFFLINE) {
- tags.add(UIHelper.getTagForStatus(context, status));
- }
- if (isBlocked()) {
- tags.add(new Tag("blocked", 0xff2e2f3b));
- }
- return tags;
- }
-
- public boolean match(Context context, String needle) {
- if (needle == null || needle.isEmpty()) {
- return true;
- }
- needle = needle.toLowerCase(Locale.US).trim();
- String[] parts = needle.split("\\s+");
- if (parts.length > 1) {
- for(int i = 0; i < parts.length; ++i) {
- if (!match(context, parts[i])) {
- return false;
- }
- }
- return true;
- } else {
- return jid.toString().contains(needle) ||
- getDisplayName().toLowerCase(Locale.US).contains(needle) ||
- matchInTag(context, needle);
- }
- }
-
- private boolean matchInTag(Context context, String needle) {
- needle = needle.toLowerCase(Locale.US);
- for (Tag tag : getTags(context)) {
- if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
- return true;
- }
- }
- return false;
- }
-
- public ContentValues getContentValues() {
- synchronized (this.keys) {
- final ContentValues values = new ContentValues();
- values.put(ACCOUNT, accountUuid);
- values.put(SYSTEMNAME, systemName);
- values.put(SERVERNAME, serverName);
- values.put(JID, jid.toPreppedString());
- values.put(OPTIONS, subscription);
- values.put(SYSTEMACCOUNT, systemAccount);
- values.put(PHOTOURI, photoUri);
- values.put(KEYS, keys.toString());
- values.put(AVATAR, avatar == null ? null : avatar.getFilename());
- values.put(LAST_PRESENCE, mLastPresence);
- values.put(LAST_TIME, mLastseen);
- values.put(GROUPS, groups.toString());
- return values;
- }
- }
-
- public Account getAccount() {
- return this.account;
- }
-
- public void setAccount(Account account) {
- this.account = account;
- this.accountUuid = account.getUuid();
- }
-
- public Presences getPresences() {
- return this.presences;
- }
-
- public void updatePresence(final String resource, final Presence presence) {
- this.presences.updatePresence(resource, presence);
- }
-
- public void removePresence(final String resource) {
- this.presences.removePresence(resource);
- }
-
- public void clearPresences() {
- this.presences.clearPresences();
- this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
- }
-
- public Presence.Status getShownStatus() {
- return this.presences.getShownStatus();
- }
-
- public boolean setPhotoUri(String uri) {
- if (uri != null && !uri.equals(this.photoUri)) {
- this.photoUri = uri;
- return true;
- } else if (this.photoUri != null && uri == null) {
- this.photoUri = null;
- return true;
- } else {
- return false;
- }
- }
-
- public void setServerName(String serverName) {
- this.serverName = serverName;
- }
-
- public void setSystemName(String systemName) {
- this.systemName = systemName;
- }
-
- public void setPresenceName(String presenceName) {
- this.presenceName = presenceName;
- }
-
- public Uri getSystemAccount() {
- if (systemAccount == null) {
- return null;
- } else {
- String[] parts = systemAccount.split("#");
- if (parts.length != 2) {
- return null;
- } else {
- long id = Long.parseLong(parts[0]);
- return ContactsContract.Contacts.getLookupUri(id, parts[1]);
- }
- }
- }
-
- public void setSystemAccount(String account) {
- this.systemAccount = account;
- }
-
- public List<String> getGroups() {
- ArrayList<String> groups = new ArrayList<String>();
- for (int i = 0; i < this.groups.length(); ++i) {
- try {
- groups.add(this.groups.getString(i));
- } catch (final JSONException ignored) {
- }
- }
- return groups;
- }
-
- public ArrayList<String> getOtrFingerprints() {
- synchronized (this.keys) {
- final ArrayList<String> fingerprints = new ArrayList<String>();
- try {
- if (this.keys.has("otr_fingerprints")) {
- final JSONArray prints = this.keys.getJSONArray("otr_fingerprints");
- for (int i = 0; i < prints.length(); ++i) {
- final String print = prints.isNull(i) ? null : prints.getString(i);
- if (print != null && !print.isEmpty()) {
- fingerprints.add(prints.getString(i));
- }
- }
- }
- } catch (final JSONException ignored) {
-
- }
- return fingerprints;
- }
- }
- public boolean addOtrFingerprint(String print) {
- synchronized (this.keys) {
- if (getOtrFingerprints().contains(print)) {
- return false;
- }
- try {
- JSONArray fingerprints;
- if (!this.keys.has("otr_fingerprints")) {
- fingerprints = new JSONArray();
- } else {
- fingerprints = this.keys.getJSONArray("otr_fingerprints");
- }
- fingerprints.put(print);
- this.keys.put("otr_fingerprints", fingerprints);
- return true;
- } catch (final JSONException ignored) {
- return false;
- }
- }
- }
-
- public long getPgpKeyId() {
- synchronized (this.keys) {
- if (this.keys.has("pgp_keyid")) {
- try {
- return this.keys.getLong("pgp_keyid");
- } catch (JSONException e) {
- return 0;
- }
- } else {
- return 0;
- }
- }
- }
-
- public void setPgpKeyId(long keyId) {
- synchronized (this.keys) {
- try {
- this.keys.put("pgp_keyid", keyId);
- } catch (final JSONException ignored) {
- }
- }
- }
-
- public void setOption(int option) {
- this.subscription |= 1 << option;
- }
-
- public void resetOption(int option) {
- this.subscription &= ~(1 << option);
- }
-
- public boolean getOption(int option) {
- return ((this.subscription & (1 << option)) != 0);
- }
-
- public boolean showInRoster() {
- return (this.getOption(Contact.Options.IN_ROSTER) && (!this
- .getOption(Contact.Options.DIRTY_DELETE)))
- || (this.getOption(Contact.Options.DIRTY_PUSH));
- }
-
- public void parseSubscriptionFromElement(Element item) {
- String ask = item.getAttribute("ask");
- String subscription = item.getAttribute("subscription");
-
- if (subscription != null) {
- switch (subscription) {
- case "to":
- this.resetOption(Options.FROM);
- this.setOption(Options.TO);
- break;
- case "from":
- this.resetOption(Options.TO);
- this.setOption(Options.FROM);
- this.resetOption(Options.PREEMPTIVE_GRANT);
- this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
- break;
- case "both":
- this.setOption(Options.TO);
- this.setOption(Options.FROM);
- this.resetOption(Options.PREEMPTIVE_GRANT);
- this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
- break;
- case "none":
- this.resetOption(Options.FROM);
- this.resetOption(Options.TO);
- break;
- }
- }
-
- // do NOT override asking if pending push request
- if (!this.getOption(Contact.Options.DIRTY_PUSH)) {
- if ((ask != null) && (ask.equals("subscribe"))) {
- this.setOption(Contact.Options.ASKING);
- } else {
- this.resetOption(Contact.Options.ASKING);
- }
- }
- }
-
- public void parseGroupsFromElement(Element item) {
- this.groups = new JSONArray();
- for (Element element : item.getChildren()) {
- if (element.getName().equals("group") && element.getContent() != null) {
- this.groups.put(element.getContent());
- }
- }
- }
-
- public Element asElement() {
- final Element item = new Element("item");
- item.setAttribute("jid", this.jid.toString());
- if (this.serverName != null) {
- item.setAttribute("name", this.serverName);
- }
- for (String group : getGroups()) {
- item.addChild("group").setContent(group);
- }
- return item;
- }
-
- @Override
- public int compareTo(final ListItem another) {
- return this.getDisplayName().compareToIgnoreCase(
- another.getDisplayName());
- }
-
- public Jid getServer() {
- return getJid().toDomainJid();
- }
-
- public boolean setAvatar(Avatar avatar) {
- if (this.avatar != null && this.avatar.equals(avatar)) {
- return false;
- } else {
- if (this.avatar != null && this.avatar.origin == Avatar.Origin.PEP && avatar.origin == Avatar.Origin.VCARD) {
- return false;
- }
- this.avatar = avatar;
- return true;
- }
- }
-
- public String getAvatar() {
- return avatar == null ? null : avatar.getFilename();
- }
-
- public boolean deleteOtrFingerprint(String fingerprint) {
- synchronized (this.keys) {
- boolean success = false;
- try {
- if (this.keys.has("otr_fingerprints")) {
- JSONArray newPrints = new JSONArray();
- JSONArray oldPrints = this.keys
- .getJSONArray("otr_fingerprints");
- for (int i = 0; i < oldPrints.length(); ++i) {
- if (!oldPrints.getString(i).equals(fingerprint)) {
- newPrints.put(oldPrints.getString(i));
- } else {
- success = true;
- }
- }
- this.keys.put("otr_fingerprints", newPrints);
- }
- return success;
- } catch (JSONException e) {
- return false;
- }
- }
- }
-
- public boolean mutualPresenceSubscription() {
- return getOption(Options.FROM) && getOption(Options.TO);
- }
-
- @Override
- public boolean isBlocked() {
- return getAccount().isBlocked(this);
- }
-
- @Override
- public boolean isDomainBlocked() {
- return getAccount().isBlocked(this.getJid().toDomainJid());
- }
-
- @Override
- public Jid getBlockedJid() {
- if (isDomainBlocked()) {
- return getJid().toDomainJid();
- } else {
- return getJid();
- }
- }
-
- public boolean isSelf() {
- return account.getJid().toBareJid().equals(getJid().toBareJid());
- }
-
- public void setCommonName(String cn) {
- this.commonName = cn;
- }
-
- public void flagActive() {
- this.mActive = true;
- }
-
- public void flagInactive() {
- this.mActive = false;
- }
-
- public boolean isActive() {
- return this.mActive;
- }
-
- public void setLastseen(long timestamp) {
- this.mLastseen = Math.max(timestamp, mLastseen);
- }
-
- public long getLastseen() {
- return this.mLastseen;
- }
-
- public void setLastResource(String resource) {
- this.mLastPresence = resource;
- }
-
- public String getLastResource() {
- return this.mLastPresence;
- }
-
- public final class Options {
- public static final int TO = 0;
- public static final int FROM = 1;
- public static final int ASKING = 2;
- public static final int PREEMPTIVE_GRANT = 3;
- public static final int IN_ROSTER = 4;
- public static final int PENDING_SUBSCRIPTION_REQUEST = 5;
- public static final int DIRTY_PUSH = 6;
- public static final int DIRTY_DELETE = 7;
- }
+ return this.presenceName;
+ } else if (jid.hasLocalpart()) {
+ return jid.getLocalpart();
+ } else {
+ return jid.getDomainpart();
+ }
+ }
+
+ @Override
+ public String getDisplayJid() {
+ if (jid != null) {
+ return jid.toString();
+ } else {
+ return null;
+ }
+ }
+
+ public String getProfilePhoto() {
+ return this.photoUri;
+ }
+
+ public Jid getJid() {
+ return jid;
+ }
+
+ @Override
+ public List<Tag> getTags(Context context) {
+ final ArrayList<Tag> tags = new ArrayList<>();
+ for (final String group : getGroups()) {
+ tags.add(new Tag(group, UIHelper.getColorForName(group)));
+ }
+ Presence.Status status = getShownStatus();
+ if (status != Presence.Status.OFFLINE) {
+ tags.add(UIHelper.getTagForStatus(context, status));
+ }
+ if (isBlocked()) {
+ tags.add(new Tag("blocked", 0xff2e2f3b));
+ }
+ return tags;
+ }
+
+ public boolean match(Context context, String needle) {
+ if (needle == null || needle.isEmpty()) {
+ return true;
+ }
+ needle = needle.toLowerCase(Locale.US).trim();
+ String[] parts = needle.split("\\s+");
+ if (parts.length > 1) {
+ for (int i = 0; i < parts.length; ++i) {
+ if (!match(context, parts[i])) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return jid.toString().contains(needle) ||
+ getDisplayName().toLowerCase(Locale.US).contains(needle) ||
+ matchInTag(context, needle);
+ }
+ }
+
+ private boolean matchInTag(Context context, String needle) {
+ needle = needle.toLowerCase(Locale.US);
+ for (Tag tag : getTags(context)) {
+ if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public ContentValues getContentValues() {
+ synchronized (this.keys) {
+ final ContentValues values = new ContentValues();
+ values.put(ACCOUNT, accountUuid);
+ values.put(SYSTEMNAME, systemName);
+ values.put(SERVERNAME, serverName);
+ values.put(JID, jid.toPreppedString());
+ values.put(OPTIONS, subscription);
+ values.put(SYSTEMACCOUNT, systemAccount);
+ values.put(PHOTOURI, photoUri);
+ values.put(KEYS, keys.toString());
+ values.put(AVATAR, avatar == null ? null : avatar.getFilename());
+ values.put(LAST_PRESENCE, mLastPresence);
+ values.put(LAST_TIME, mLastseen);
+ values.put(GROUPS, groups.toString());
+ return values;
+ }
+ }
+
+ public Account getAccount() {
+ return this.account;
+ }
+
+ public void setAccount(Account account) {
+ this.account = account;
+ this.accountUuid = account.getUuid();
+ }
+
+ public Presences getPresences() {
+ return this.presences;
+ }
+
+ public void updatePresence(final String resource, final Presence presence) {
+ this.presences.updatePresence(resource, presence);
+ }
+
+ public void removePresence(final String resource) {
+ this.presences.removePresence(resource);
+ }
+
+ public void clearPresences() {
+ this.presences.clearPresences();
+ this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
+ }
+
+ public Presence.Status getShownStatus() {
+ return this.presences.getShownStatus();
+ }
+
+ public boolean setPhotoUri(String uri) {
+ if (uri != null && !uri.equals(this.photoUri)) {
+ this.photoUri = uri;
+ return true;
+ } else if (this.photoUri != null && uri == null) {
+ this.photoUri = null;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void setServerName(String serverName) {
+ this.serverName = serverName;
+ }
+
+ public void setSystemName(String systemName) {
+ this.systemName = systemName;
+ }
+
+ public void setPresenceName(String presenceName) {
+ this.presenceName = presenceName;
+ }
+
+ public Uri getSystemAccount() {
+ if (systemAccount == null) {
+ return null;
+ } else {
+ String[] parts = systemAccount.split("#");
+ if (parts.length != 2) {
+ return null;
+ } else {
+ long id = Long.parseLong(parts[0]);
+ return ContactsContract.Contacts.getLookupUri(id, parts[1]);
+ }
+ }
+ }
+
+ public void setSystemAccount(String account) {
+ this.systemAccount = account;
+ }
+
+ public List<String> getGroups() {
+ ArrayList<String> groups = new ArrayList<String>();
+ for (int i = 0; i < this.groups.length(); ++i) {
+ try {
+ groups.add(this.groups.getString(i));
+ } catch (final JSONException ignored) {
+ }
+ }
+ return groups;
+ }
+
+ public ArrayList<String> getOtrFingerprints() {
+ synchronized (this.keys) {
+ final ArrayList<String> fingerprints = new ArrayList<String>();
+ try {
+ if (this.keys.has("otr_fingerprints")) {
+ final JSONArray prints = this.keys.getJSONArray("otr_fingerprints");
+ for (int i = 0; i < prints.length(); ++i) {
+ final String print = prints.isNull(i) ? null : prints.getString(i);
+ if (print != null && !print.isEmpty()) {
+ fingerprints.add(prints.getString(i));
+ }
+ }
+ }
+ } catch (final JSONException ignored) {
+
+ }
+ return fingerprints;
+ }
+ }
+
+ public boolean addOtrFingerprint(String print) {
+ synchronized (this.keys) {
+ if (getOtrFingerprints().contains(print)) {
+ return false;
+ }
+ try {
+ JSONArray fingerprints;
+ if (!this.keys.has("otr_fingerprints")) {
+ fingerprints = new JSONArray();
+ } else {
+ fingerprints = this.keys.getJSONArray("otr_fingerprints");
+ }
+ fingerprints.put(print);
+ this.keys.put("otr_fingerprints", fingerprints);
+ return true;
+ } catch (final JSONException ignored) {
+ return false;
+ }
+ }
+ }
+
+ public long getPgpKeyId() {
+ synchronized (this.keys) {
+ if (this.keys.has("pgp_keyid")) {
+ try {
+ return this.keys.getLong("pgp_keyid");
+ } catch (JSONException e) {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ public void setPgpKeyId(long keyId) {
+ synchronized (this.keys) {
+ try {
+ this.keys.put("pgp_keyid", keyId);
+ } catch (final JSONException ignored) {
+ }
+ }
+ }
+
+ public void setOption(int option) {
+ this.subscription |= 1 << option;
+ }
+
+ public void resetOption(int option) {
+ this.subscription &= ~(1 << option);
+ }
+
+ public boolean getOption(int option) {
+ return ((this.subscription & (1 << option)) != 0);
+ }
+
+ public boolean showInRoster() {
+ return (this.getOption(Contact.Options.IN_ROSTER) && (!this
+ .getOption(Contact.Options.DIRTY_DELETE)))
+ || (this.getOption(Contact.Options.DIRTY_PUSH));
+ }
+
+ public void parseSubscriptionFromElement(Element item) {
+ String ask = item.getAttribute("ask");
+ String subscription = item.getAttribute("subscription");
+
+ if (subscription != null) {
+ switch (subscription) {
+ case "to":
+ this.resetOption(Options.FROM);
+ this.setOption(Options.TO);
+ break;
+ case "from":
+ this.resetOption(Options.TO);
+ this.setOption(Options.FROM);
+ this.resetOption(Options.PREEMPTIVE_GRANT);
+ this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
+ break;
+ case "both":
+ this.setOption(Options.TO);
+ this.setOption(Options.FROM);
+ this.resetOption(Options.PREEMPTIVE_GRANT);
+ this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
+ break;
+ case "none":
+ this.resetOption(Options.FROM);
+ this.resetOption(Options.TO);
+ break;
+ }
+ }
+
+ // do NOT override asking if pending push request
+ if (!this.getOption(Contact.Options.DIRTY_PUSH)) {
+ if ((ask != null) && (ask.equals("subscribe"))) {
+ this.setOption(Contact.Options.ASKING);
+ } else {
+ this.resetOption(Contact.Options.ASKING);
+ }
+ }
+ }
+
+ public void parseGroupsFromElement(Element item) {
+ this.groups = new JSONArray();
+ for (Element element : item.getChildren()) {
+ if (element.getName().equals("group") && element.getContent() != null) {
+ this.groups.put(element.getContent());
+ }
+ }
+ }
+
+ public Element asElement() {
+ final Element item = new Element("item");
+ item.setAttribute("jid", this.jid.toString());
+ if (this.serverName != null) {
+ item.setAttribute("name", this.serverName);
+ }
+ for (String group : getGroups()) {
+ item.addChild("group").setContent(group);
+ }
+ return item;
+ }
+
+ @Override
+ public int compareTo(final ListItem another) {
+ return this.getDisplayName().compareToIgnoreCase(
+ another.getDisplayName());
+ }
+
+ public Jid getServer() {
+ return getJid().toDomainJid();
+ }
+
+ public boolean setAvatar(Avatar avatar) {
+ if (this.avatar != null && this.avatar.equals(avatar)) {
+ return false;
+ } else {
+ if (this.avatar != null && this.avatar.origin == Avatar.Origin.PEP && avatar.origin == Avatar.Origin.VCARD) {
+ return false;
+ }
+ this.avatar = avatar;
+ return true;
+ }
+ }
+
+ public String getAvatar() {
+ return avatar == null ? null : avatar.getFilename();
+ }
+
+ public boolean deleteOtrFingerprint(String fingerprint) {
+ synchronized (this.keys) {
+ boolean success = false;
+ try {
+ if (this.keys.has("otr_fingerprints")) {
+ JSONArray newPrints = new JSONArray();
+ JSONArray oldPrints = this.keys
+ .getJSONArray("otr_fingerprints");
+ for (int i = 0; i < oldPrints.length(); ++i) {
+ if (!oldPrints.getString(i).equals(fingerprint)) {
+ newPrints.put(oldPrints.getString(i));
+ } else {
+ success = true;
+ }
+ }
+ this.keys.put("otr_fingerprints", newPrints);
+ }
+ return success;
+ } catch (JSONException e) {
+ return false;
+ }
+ }
+ }
+
+ public boolean mutualPresenceSubscription() {
+ return getOption(Options.FROM) && getOption(Options.TO);
+ }
+
+ @Override
+ public boolean isBlocked() {
+ return getAccount().isBlocked(this);
+ }
+
+ @Override
+ public boolean isDomainBlocked() {
+ return getAccount().isBlocked(this.getJid().toDomainJid());
+ }
+
+ @Override
+ public Jid getBlockedJid() {
+ if (isDomainBlocked()) {
+ return getJid().toDomainJid();
+ } else {
+ return getJid();
+ }
+ }
+
+ public boolean isSelf() {
+ return account.getJid().toBareJid().equals(getJid().toBareJid());
+ }
+
+ public void setCommonName(String cn) {
+ this.commonName = cn;
+ }
+
+ public void flagActive() {
+ this.mActive = true;
+ }
+
+ public void flagInactive() {
+ this.mActive = false;
+ }
+
+ public boolean isActive() {
+ return this.mActive;
+ }
+
+ public void setLastseen(long timestamp) {
+ this.mLastseen = Math.max(timestamp, mLastseen);
+ }
+
+ public long getLastseen() {
+ return this.mLastseen;
+ }
+
+ public void setLastResource(String resource) {
+ this.mLastPresence = resource;
+ }
+
+ public String getLastResource() {
+ return this.mLastPresence;
+ }
+
+ public final class Options {
+ public static final int TO = 0;
+ public static final int FROM = 1;
+ public static final int ASKING = 2;
+ public static final int PREEMPTIVE_GRANT = 3;
+ public static final int IN_ROSTER = 4;
+ public static final int PENDING_SUBSCRIPTION_REQUEST = 5;
+ public static final int DIRTY_PUSH = 6;
+ public static final int DIRTY_DELETE = 7;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/Conversation.java b/src/main/java/de/pixart/messenger/entities/Conversation.java
index 5a3dd4cf4..f9932f67d 100644
--- a/src/main/java/de/pixart/messenger/entities/Conversation.java
+++ b/src/main/java/de/pixart/messenger/entities/Conversation.java
@@ -29,948 +29,948 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class Conversation extends AbstractEntity implements Blockable, Comparable<Conversation> {
- public static final String TABLENAME = "conversations";
-
- public static final int STATUS_AVAILABLE = 0;
- public static final int STATUS_ARCHIVED = 1;
- public static final int STATUS_DELETED = 2;
-
- public static final int MODE_MULTI = 1;
- public static final int MODE_SINGLE = 0;
-
- public static final String NAME = "name";
- public static final String ACCOUNT = "accountUuid";
- public static final String CONTACT = "contactUuid";
- public static final String CONTACTJID = "contactJid";
- public static final String STATUS = "status";
- public static final String CREATED = "created";
- public static final String MODE = "mode";
- public static final String ATTRIBUTES = "attributes";
-
- public static final String ATTRIBUTE_NEXT_ENCRYPTION = "next_encryption";
- public static final String ATTRIBUTE_MUC_PASSWORD = "muc_password";
- public static final String ATTRIBUTE_MUTED_TILL = "muted_till";
- public static final String ATTRIBUTE_ALWAYS_NOTIFY = "always_notify";
- public static final String ATTRIBUTE_CRYPTO_TARGETS = "crypto_targets";
- public static final String ATTRIBUTE_LAST_CLEAR_HISTORY = "last_clear_history";
-
- private String name;
- private String contactUuid;
- private String accountUuid;
- private Jid contactJid;
- private int status;
- private long created;
- private int mode;
-
- private JSONObject attributes = new JSONObject();
-
- private Jid nextCounterpart;
-
- protected final ArrayList<Message> messages = new ArrayList<>();
- protected Account account = null;
-
- private transient SessionImpl otrSession;
-
- private transient String otrFingerprint = null;
- private Smp mSmp = new Smp();
-
- private String nextMessage;
-
- private transient MucOptions mucOptions = null;
-
- private byte[] symmetricKey;
-
- private Bookmark bookmark;
-
- private boolean messagesLeftOnServer = true;
- private ChatState mOutgoingChatState = Config.DEFAULT_CHATSTATE;
- private ChatState mIncomingChatState = Config.DEFAULT_CHATSTATE;
- private String mLastReceivedOtrMessageId = null;
- private String mFirstMamReference = null;
- private Message correctingMessage;
-
- public boolean hasMessagesLeftOnServer() {
- return messagesLeftOnServer;
- }
-
- public void setHasMessagesLeftOnServer(boolean value) {
- this.messagesLeftOnServer = value;
- }
-
-
- public Message getFirstUnreadMessage() {
- Message first = null;
- synchronized (this.messages) {
- for (int i = messages.size() - 1; i >= 0; --i) {
- if (messages.get(i).isRead()) {
- return first;
- } else {
- first = messages.get(i);
- }
- }
- }
- return first;
- }
-
- public Message findUnsentMessageWithUuid(String uuid) {
- synchronized(this.messages) {
- for (final Message message : this.messages) {
- final int s = message.getStatus();
- if ((s == Message.STATUS_UNSEND || s == Message.STATUS_WAITING) && message.getUuid().equals(uuid)) {
- return message;
- }
- }
- }
- return null;
- }
-
- public void findWaitingMessages(OnMessageFound onMessageFound) {
- synchronized (this.messages) {
- for(Message message : this.messages) {
- if (message.getStatus() == Message.STATUS_WAITING) {
- onMessageFound.onMessageFound(message);
- }
- }
- }
- }
-
- public void findUnreadMessages(OnMessageFound onMessageFound) {
- synchronized (this.messages) {
- for(Message message : this.messages) {
- if (!message.isRead()) {
- onMessageFound.onMessageFound(message);
- }
- }
- }
- }
-
- public void findMessagesWithFiles(final OnMessageFound onMessageFound) {
- synchronized (this.messages) {
- for (final Message message : this.messages) {
- if ((message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE)
- && message.getEncryption() != Message.ENCRYPTION_PGP) {
- onMessageFound.onMessageFound(message);
- }
- }
- }
- }
-
- public Message findMessageWithFileAndUuid(final String uuid) {
- synchronized (this.messages) {
- for (final Message message : this.messages) {
- if (message.getUuid().equals(uuid)
- && message.getEncryption() != Message.ENCRYPTION_PGP
- && (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.treatAsDownloadable() != Message.Decision.NEVER)) {
- return message;
- }
- }
- }
- return null;
- }
-
- public void clearMessages() {
- synchronized (this.messages) {
- this.messages.clear();
- }
- }
-
- public boolean setIncomingChatState(ChatState state) {
- if (this.mIncomingChatState == state) {
- return false;
- }
- this.mIncomingChatState = state;
- return true;
- }
-
- public ChatState getIncomingChatState() {
- return this.mIncomingChatState;
- }
-
- public boolean setOutgoingChatState(ChatState state) {
- if (mode == MODE_MULTI) {
- return false;
- }
- if (this.mOutgoingChatState != state) {
- this.mOutgoingChatState = state;
- return true;
- } else {
- return false;
- }
- }
-
- public ChatState getOutgoingChatState() {
- return this.mOutgoingChatState;
- }
-
- public void trim() {
- synchronized (this.messages) {
- final int size = messages.size();
- final int maxsize = Config.PAGE_SIZE * Config.MAX_NUM_PAGES;
- if (size > maxsize) {
- List<Message> discards = this.messages.subList(0, size - maxsize);
- final PgpDecryptionService pgpDecryptionService = account.getPgpDecryptionService();
- if (pgpDecryptionService != null) {
- pgpDecryptionService.discard(discards);
- }
- discards.clear();
- untieMessages();
- }
- }
- }
-
- public void findUnsentMessagesWithEncryption(int encryptionType, OnMessageFound onMessageFound) {
- synchronized (this.messages) {
- for (Message message : this.messages) {
- if ((message.getStatus() == Message.STATUS_UNSEND || message.getStatus() == Message.STATUS_WAITING)
- && (message.getEncryption() == encryptionType)) {
- onMessageFound.onMessageFound(message);
- }
- }
- }
- }
-
- public void findUnsentTextMessages(OnMessageFound onMessageFound) {
- synchronized (this.messages) {
- for (Message message : this.messages) {
- if (message.getType() != Message.TYPE_IMAGE
- && message.getStatus() == Message.STATUS_UNSEND) {
- onMessageFound.onMessageFound(message);
- }
- }
- }
- }
-
- public Message findSentMessageWithUuidOrRemoteId(String id) {
- synchronized (this.messages) {
- for (Message message : this.messages) {
- if (id.equals(message.getUuid())
- || (message.getStatus() >= Message.STATUS_SEND
- && id.equals(message.getRemoteMsgId()))) {
- return message;
- }
- }
- }
- return null;
- }
-
- public Message findMessageWithRemoteIdAndCounterpart(String id, Jid counterpart, boolean received, boolean carbon) {
- synchronized (this.messages) {
- for(int i = this.messages.size() - 1; i >= 0; --i) {
- Message message = messages.get(i);
- if (counterpart.equals(message.getCounterpart())
- && ((message.getStatus() == Message.STATUS_RECEIVED) == received)
- && (carbon == message.isCarbon() || received) ) {
- if (id.equals(message.getRemoteMsgId())) {
- return message;
- } else {
- return null;
- }
- }
- }
- }
- return null;
- }
-
- public Message findSentMessageWithUuid(String id) {
- synchronized (this.messages) {
- for (Message message : this.messages) {
- if (id.equals(message.getUuid())) {
- return message;
- }
- }
- }
- return null;
- }
-
- 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
- public boolean isBlocked() {
- return getContact().isBlocked();
- }
-
- @Override
- public boolean isDomainBlocked() {
- return getContact().isDomainBlocked();
- }
-
- @Override
- public Jid getBlockedJid() {
- return getContact().getBlockedJid();
- }
-
- public String getLastReceivedOtrMessageId() {
- return this.mLastReceivedOtrMessageId;
- }
-
- public void setLastReceivedOtrMessageId(String id) {
- this.mLastReceivedOtrMessageId = id;
- }
-
- public int countMessages() {
- synchronized (this.messages) {
- return this.messages.size();
- }
- }
-
- public void setFirstMamReference(String reference) {
- this.mFirstMamReference = reference;
- }
-
- public String getFirstMamReference() {
- return this.mFirstMamReference;
- }
-
- public void setLastClearHistory(long time) {
- setAttribute("ATTRIBUTE_LAST_CLEAR_HISTORY",String.valueOf(time));
- }
-
- public long getLastClearHistory() {
- return getLongAttribute("ATTRIBUTE_LAST_CLEAR_HISTORY", 0);
- }
-
- public List<Jid> getAcceptedCryptoTargets() {
- if (mode == MODE_SINGLE) {
- return Arrays.asList(getJid().toBareJid());
- } else {
- return getJidListAttribute(ATTRIBUTE_CRYPTO_TARGETS);
- }
- }
-
- public void setAcceptedCryptoTargets(List<Jid> acceptedTargets) {
- 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();
- }
-
- @Override
- public int compareTo(Conversation another) {
- final Message left = getLatestMessage();
- final Message right = another.getLatestMessage();
- if (left.getTimeSent() > right.getTimeSent()) {
- return -1;
- } else if (left.getTimeSent() < right.getTimeSent()) {
- return 1;
- } else {
- return 0;
- }
- }
-
- public interface OnMessageFound {
- void onMessageFound(final Message message);
- }
-
- public Conversation(final String name, final Account account, final Jid contactJid,
- final int mode) {
- this(java.util.UUID.randomUUID().toString(), name, null, account
- .getUuid(), contactJid, System.currentTimeMillis(),
- STATUS_AVAILABLE, mode, "");
- this.account = account;
- }
-
- public Conversation(final String uuid, final String name, final String contactUuid,
- final String accountUuid, final Jid contactJid, final long created, final int status,
- final int mode, final String attributes) {
- this.uuid = uuid;
- this.name = name;
- this.contactUuid = contactUuid;
- this.accountUuid = accountUuid;
- this.contactJid = contactJid;
- this.created = created;
- this.status = status;
- this.mode = mode;
- try {
- this.attributes = new JSONObject(attributes == null ? "" : attributes);
- } catch (JSONException e) {
- this.attributes = new JSONObject();
- }
- }
-
- public boolean isRead() {
- return (this.messages.size() == 0) || this.messages.get(this.messages.size() - 1).isRead();
- }
-
- public List<Message> markRead() {
- final List<Message> unread = new ArrayList<>();
- synchronized (this.messages) {
- for(Message message : this.messages) {
- if (!message.isRead()) {
- message.markRead();
- unread.add(message);
- }
- }
- }
- return unread;
- }
-
- public Message getLatestMarkableMessage() {
- for (int i = this.messages.size() - 1; i >= 0; --i) {
- if (this.messages.get(i).getStatus() <= Message.STATUS_RECEIVED
- && this.messages.get(i).markable) {
- if (this.messages.get(i).isRead()) {
- return null;
- } else {
- return this.messages.get(i);
- }
- }
- }
- return null;
- }
-
- public Message getLatestMessage() {
- if (this.messages.size() == 0) {
- Message message = new Message(this, "", Message.ENCRYPTION_NONE);
- message.setTime(getCreated());
- return message;
- } else {
- Message message = this.messages.get(this.messages.size() - 1);
- message.setConversation(this);
- return message;
- }
- }
-
- public String getName() {
- if (getMode() == MODE_MULTI) {
- if (getMucOptions().getSubject() != null) {
- return getMucOptions().getSubject();
- } else if (bookmark != null
- && bookmark.getBookmarkName() != null
- && !bookmark.getBookmarkName().trim().isEmpty()) {
- return bookmark.getBookmarkName().trim();
- } else {
- String generatedName = getMucOptions().createNameFromParticipants();
- if (generatedName != null) {
- return generatedName;
- } else {
- return getJid().getLocalpart();
- }
- }
- } else {
- return this.getContact().getDisplayName();
- }
- }
-
- public String getParticipants() {
- if (getMode() == MODE_MULTI) {
- String generatedName = getMucOptions().createNameFromParticipants();
- if (generatedName != null) {
- return generatedName;
- } else {
- return null;
- }
- } else {
- return null;
- }
- }
-
- public String getAccountUuid() {
- return this.accountUuid;
- }
-
- public Account getAccount() {
- return this.account;
- }
-
- public Contact getContact() {
- return this.account.getRoster().getContact(this.contactJid);
- }
-
- public void setAccount(final Account account) {
- this.account = account;
- }
-
- @Override
- public Jid getJid() {
- return this.contactJid;
- }
-
- public int getStatus() {
- return this.status;
- }
-
- public long getCreated() {
- return this.created;
- }
-
- public ContentValues getContentValues() {
- ContentValues values = new ContentValues();
- values.put(UUID, uuid);
- values.put(NAME, name);
- values.put(CONTACT, contactUuid);
- values.put(ACCOUNT, accountUuid);
- values.put(CONTACTJID, contactJid.toPreppedString());
- values.put(CREATED, created);
- values.put(STATUS, status);
- values.put(MODE, mode);
- values.put(ATTRIBUTES, attributes.toString());
- return values;
- }
-
- public static Conversation fromCursor(Cursor cursor) {
- Jid jid;
- try {
- jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(CONTACTJID)), true);
- } catch (final InvalidJidException e) {
- // Borked DB..
- jid = null;
- }
- return new Conversation(cursor.getString(cursor.getColumnIndex(UUID)),
- cursor.getString(cursor.getColumnIndex(NAME)),
- cursor.getString(cursor.getColumnIndex(CONTACT)),
- cursor.getString(cursor.getColumnIndex(ACCOUNT)),
- jid,
- cursor.getLong(cursor.getColumnIndex(CREATED)),
- cursor.getInt(cursor.getColumnIndex(STATUS)),
- cursor.getInt(cursor.getColumnIndex(MODE)),
- cursor.getString(cursor.getColumnIndex(ATTRIBUTES)));
- }
-
- public void setStatus(int status) {
- this.status = status;
- }
-
- public int getMode() {
- return this.mode;
- }
-
- public void setMode(int mode) {
- this.mode = mode;
- }
-
- public SessionImpl startOtrSession(String presence, boolean sendStart) {
- if (this.otrSession != null) {
- return this.otrSession;
- } else {
- final SessionID sessionId = new SessionID(this.getJid().toBareJid().toString(),
- presence,
- "xmpp");
- this.otrSession = new SessionImpl(sessionId, getAccount().getOtrService());
- try {
- if (sendStart) {
- this.otrSession.startSession();
- return this.otrSession;
- }
- return this.otrSession;
- } catch (OtrException e) {
- return null;
- }
- }
-
- }
-
- public SessionImpl getOtrSession() {
- return this.otrSession;
- }
-
- public void resetOtrSession() {
- this.otrFingerprint = null;
- this.otrSession = null;
- this.mSmp.hint = null;
- this.mSmp.secret = null;
- this.mSmp.status = Smp.STATUS_NONE;
- }
-
- public Smp smp() {
- return mSmp;
- }
-
- public boolean startOtrIfNeeded() {
- if (this.otrSession != null && this.otrSession.getSessionStatus() != SessionStatus.ENCRYPTED) {
- try {
- this.otrSession.startSession();
- return true;
- } catch (OtrException e) {
- this.resetOtrSession();
- return false;
- }
- } else {
- return true;
- }
- }
-
- public boolean endOtrIfNeeded() {
- if (this.otrSession != null) {
- if (this.otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) {
- try {
- this.otrSession.endSession();
- this.resetOtrSession();
- return true;
- } catch (OtrException e) {
- this.resetOtrSession();
- return false;
- }
- } else {
- this.resetOtrSession();
- return false;
- }
- } else {
- return false;
- }
- }
-
- public boolean hasValidOtrSession() {
- return this.otrSession != null;
- }
-
- public synchronized String getOtrFingerprint() {
- if (this.otrFingerprint == null) {
- try {
- if (getOtrSession() == null || getOtrSession().getSessionStatus() != SessionStatus.ENCRYPTED) {
- return null;
- }
- DSAPublicKey remotePubKey = (DSAPublicKey) getOtrSession().getRemotePublicKey();
- this.otrFingerprint = getAccount().getOtrService().getFingerprint(remotePubKey);
- } catch (final OtrCryptoException | UnsupportedOperationException ignored) {
- return null;
- }
- }
- return this.otrFingerprint;
- }
-
- public boolean verifyOtrFingerprint() {
- final String fingerprint = getOtrFingerprint();
- if (fingerprint != null) {
- getContact().addOtrFingerprint(fingerprint);
- return true;
- } else {
- return false;
- }
- }
-
- public boolean isOtrFingerprintVerified() {
- return getContact().getOtrFingerprints().contains(getOtrFingerprint());
- }
-
- /**
- * short for is Private and Non-anonymous
- */
- private boolean isPnNA() {
- return mode == MODE_SINGLE || (getMucOptions().membersOnly() && getMucOptions().nonanonymous());
- }
-
- public synchronized MucOptions getMucOptions() {
- if (this.mucOptions == null) {
- this.mucOptions = new MucOptions(this);
- }
- return this.mucOptions;
- }
-
- public void resetMucOptions() {
- this.mucOptions = null;
- }
-
- public void setContactJid(final Jid jid) {
- this.contactJid = jid;
- }
-
- public void setNextCounterpart(Jid jid) {
- this.nextCounterpart = jid;
- }
-
- public Jid getNextCounterpart() {
- return this.nextCounterpart;
- }
-
- private int getMostRecentlyUsedIncomingEncryption() {
- synchronized (this.messages) {
- for(int i = this.messages.size() -1; i >= 0; --i) {
- final Message m = this.messages.get(i);
- if (m.getStatus() == Message.STATUS_RECEIVED) {
- final int e = m.getEncryption();
- if (e == Message.ENCRYPTION_DECRYPTED || e == Message.ENCRYPTION_DECRYPTION_FAILED) {
- return Message.ENCRYPTION_PGP;
- } else {
- return e;
- }
- }
- }
- }
- return Message.ENCRYPTION_NONE;
- }
-
- public int getNextEncryption() {
- return Math.max(this.getIntAttribute(ATTRIBUTE_NEXT_ENCRYPTION, Message.ENCRYPTION_NONE), Message.ENCRYPTION_NONE);
- }
-
- public void setNextEncryption(int encryption) {
- this.setAttribute(ATTRIBUTE_NEXT_ENCRYPTION, String.valueOf(encryption));
- }
-
- public String getNextMessage() {
- if (this.nextMessage == null) {
- return "";
- } else {
- return this.nextMessage;
- }
- }
-
- public boolean smpRequested() {
- return smp().status == Smp.STATUS_CONTACT_REQUESTED;
- }
-
- public void setNextMessage(String message) {
- this.nextMessage = message;
- }
-
- public void setSymmetricKey(byte[] key) {
- this.symmetricKey = key;
- }
-
- public byte[] getSymmetricKey() {
- return this.symmetricKey;
- }
-
- public void setBookmark(Bookmark bookmark) {
- this.bookmark = bookmark;
- this.bookmark.setConversation(this);
- }
-
- public void deregisterWithBookmark() {
- if (this.bookmark != null) {
- this.bookmark.setConversation(null);
- }
- }
-
- public Bookmark getBookmark() {
- return this.bookmark;
- }
-
- public boolean hasDuplicateMessage(Message message) {
- synchronized (this.messages) {
- for (int i = this.messages.size() - 1; i >= 0; --i) {
- if (this.messages.get(i).similar(message)) {
- return true;
- }
- }
- }
- return false;
- }
-
- public Message findSentMessageWithBody(String body) {
- synchronized (this.messages) {
- for (int i = this.messages.size() - 1; i >= 0; --i) {
- Message message = this.messages.get(i);
- if (message.getStatus() == Message.STATUS_UNSEND || message.getStatus() == Message.STATUS_SEND) {
- String otherBody;
- if (message.hasFileOnRemoteHost()) {
- otherBody = message.getFileParams().url.toString();
- } else {
- otherBody = message.body;
- }
- if (otherBody != null && otherBody.equals(body)) {
- return message;
- }
- }
- }
- return null;
- }
- }
-
- public long getLastMessageTransmitted() {
- final long last_clear = getLastClearHistory();
- long last_received = 0;
- synchronized (this.messages) {
- for(int i = this.messages.size() - 1; i >= 0; --i) {
- Message message = this.messages.get(i);
- if (message.getStatus() == Message.STATUS_RECEIVED || message.isCarbon()) {
- last_received = message.getTimeSent();
- break;
- }
- }
- }
- return Math.max(last_clear,last_received);
- }
-
- public void setMutedTill(long value) {
- this.setAttribute(ATTRIBUTE_MUTED_TILL, String.valueOf(value));
- }
-
- public boolean isMuted() {
- return System.currentTimeMillis() < this.getLongAttribute(ATTRIBUTE_MUTED_TILL, 0);
- }
-
- public boolean alwaysNotify() {
- return mode == MODE_SINGLE || getBooleanAttribute(ATTRIBUTE_ALWAYS_NOTIFY, Config.ALWAYS_NOTIFY_BY_DEFAULT || isPnNA());
- }
-
- public boolean setAttribute(String key, String value) {
- synchronized (this.attributes) {
- try {
- this.attributes.put(key, value);
- return true;
- } catch (JSONException e) {
- return false;
- }
- }
- }
-
- public boolean setAttribute(String key, List<Jid> jids) {
- JSONArray array = new JSONArray();
- for(Jid jid : jids) {
- array.put(jid.toBareJid().toString());
- }
- synchronized (this.attributes) {
- try {
- this.attributes.put(key, array);
- return true;
- } catch (JSONException e) {
- e.printStackTrace();
- return false;
- }
- }
- }
-
- public String getAttribute(String key) {
- synchronized (this.attributes) {
- try {
- return this.attributes.getString(key);
- } catch (JSONException e) {
- return null;
- }
- }
- }
-
- public List<Jid> getJidListAttribute(String key) {
- ArrayList<Jid> list = new ArrayList<>();
- synchronized (this.attributes) {
- try {
- JSONArray array = this.attributes.getJSONArray(key);
- for (int i = 0; i < array.length(); ++i) {
- try {
- list.add(Jid.fromString(array.getString(i)));
- } catch (InvalidJidException e) {
- //ignored
- }
- }
- } catch (JSONException e) {
- //ignored
- }
- }
- return list;
- }
-
- public int getIntAttribute(String key, int defaultValue) {
- String value = this.getAttribute(key);
- if (value == null) {
- return defaultValue;
- } else {
- try {
- return Integer.parseInt(value);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- }
- }
-
- public long getLongAttribute(String key, long defaultValue) {
- String value = this.getAttribute(key);
- if (value == null) {
- return defaultValue;
- } else {
- try {
- return Long.parseLong(value);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- }
- }
-
- public boolean getBooleanAttribute(String key, boolean defaultValue) {
- String value = this.getAttribute(key);
- if (value == null) {
- return defaultValue;
- } else {
- return Boolean.parseBoolean(value);
- }
- }
-
- public void add(Message message) {
- message.setConversation(this);
- synchronized (this.messages) {
- this.messages.add(message);
- }
- }
-
- public void prepend(Message message) {
- message.setConversation(this);
- synchronized (this.messages) {
- this.messages.add(0,message);
- }
- }
-
- public void addAll(int index, List<Message> messages) {
- synchronized (this.messages) {
- this.messages.addAll(index, messages);
- }
- account.getPgpDecryptionService().decrypt(messages);
- }
-
- public void sort() {
- synchronized (this.messages) {
- Collections.sort(this.messages, new Comparator<Message>() {
- @Override
- public int compare(Message left, Message right) {
- if (left.getTimeSent() < right.getTimeSent()) {
- return -1;
- } else if (left.getTimeSent() > right.getTimeSent()) {
- return 1;
- } else {
- return 0;
- }
- }
- });
- untieMessages();
- }
- }
-
- private void untieMessages() {
- for(Message message : this.messages) {
- message.untie();
- }
- }
-
- public int unreadCount() {
- synchronized (this.messages) {
- int count = 0;
- for(int i = this.messages.size() - 1; i >= 0; --i) {
- if (this.messages.get(i).isRead()) {
- return count;
- }
- ++count;
- }
- return count;
- }
- }
-
- public class Smp {
- public static final int STATUS_NONE = 0;
- public static final int STATUS_CONTACT_REQUESTED = 1;
- public static final int STATUS_WE_REQUESTED = 2;
- public static final int STATUS_FAILED = 3;
- public static final int STATUS_VERIFIED = 4;
-
- public String secret = null;
- public String hint = null;
- public int status = 0;
- }
+ public static final String TABLENAME = "conversations";
+
+ public static final int STATUS_AVAILABLE = 0;
+ public static final int STATUS_ARCHIVED = 1;
+ public static final int STATUS_DELETED = 2;
+
+ public static final int MODE_MULTI = 1;
+ public static final int MODE_SINGLE = 0;
+
+ public static final String NAME = "name";
+ public static final String ACCOUNT = "accountUuid";
+ public static final String CONTACT = "contactUuid";
+ public static final String CONTACTJID = "contactJid";
+ public static final String STATUS = "status";
+ public static final String CREATED = "created";
+ public static final String MODE = "mode";
+ public static final String ATTRIBUTES = "attributes";
+
+ public static final String ATTRIBUTE_NEXT_ENCRYPTION = "next_encryption";
+ public static final String ATTRIBUTE_MUC_PASSWORD = "muc_password";
+ public static final String ATTRIBUTE_MUTED_TILL = "muted_till";
+ public static final String ATTRIBUTE_ALWAYS_NOTIFY = "always_notify";
+ public static final String ATTRIBUTE_CRYPTO_TARGETS = "crypto_targets";
+ public static final String ATTRIBUTE_LAST_CLEAR_HISTORY = "last_clear_history";
+
+ private String name;
+ private String contactUuid;
+ private String accountUuid;
+ private Jid contactJid;
+ private int status;
+ private long created;
+ private int mode;
+
+ private JSONObject attributes = new JSONObject();
+
+ private Jid nextCounterpart;
+
+ protected final ArrayList<Message> messages = new ArrayList<>();
+ protected Account account = null;
+
+ private transient SessionImpl otrSession;
+
+ private transient String otrFingerprint = null;
+ private Smp mSmp = new Smp();
+
+ private String nextMessage;
+
+ private transient MucOptions mucOptions = null;
+
+ private byte[] symmetricKey;
+
+ private Bookmark bookmark;
+
+ private boolean messagesLeftOnServer = true;
+ private ChatState mOutgoingChatState = Config.DEFAULT_CHATSTATE;
+ private ChatState mIncomingChatState = Config.DEFAULT_CHATSTATE;
+ private String mLastReceivedOtrMessageId = null;
+ private String mFirstMamReference = null;
+ private Message correctingMessage;
+
+ public boolean hasMessagesLeftOnServer() {
+ return messagesLeftOnServer;
+ }
+
+ public void setHasMessagesLeftOnServer(boolean value) {
+ this.messagesLeftOnServer = value;
+ }
+
+
+ public Message getFirstUnreadMessage() {
+ Message first = null;
+ synchronized (this.messages) {
+ for (int i = messages.size() - 1; i >= 0; --i) {
+ if (messages.get(i).isRead()) {
+ return first;
+ } else {
+ first = messages.get(i);
+ }
+ }
+ }
+ return first;
+ }
+
+ public Message findUnsentMessageWithUuid(String uuid) {
+ synchronized (this.messages) {
+ for (final Message message : this.messages) {
+ final int s = message.getStatus();
+ if ((s == Message.STATUS_UNSEND || s == Message.STATUS_WAITING) && message.getUuid().equals(uuid)) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void findWaitingMessages(OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (message.getStatus() == Message.STATUS_WAITING) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public void findUnreadMessages(OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (!message.isRead()) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public void findMessagesWithFiles(final OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for (final Message message : this.messages) {
+ if ((message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE)
+ && message.getEncryption() != Message.ENCRYPTION_PGP) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public Message findMessageWithFileAndUuid(final String uuid) {
+ synchronized (this.messages) {
+ for (final Message message : this.messages) {
+ if (message.getUuid().equals(uuid)
+ && message.getEncryption() != Message.ENCRYPTION_PGP
+ && (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.treatAsDownloadable() != Message.Decision.NEVER)) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void clearMessages() {
+ synchronized (this.messages) {
+ this.messages.clear();
+ }
+ }
+
+ public boolean setIncomingChatState(ChatState state) {
+ if (this.mIncomingChatState == state) {
+ return false;
+ }
+ this.mIncomingChatState = state;
+ return true;
+ }
+
+ public ChatState getIncomingChatState() {
+ return this.mIncomingChatState;
+ }
+
+ public boolean setOutgoingChatState(ChatState state) {
+ if (mode == MODE_MULTI) {
+ return false;
+ }
+ if (this.mOutgoingChatState != state) {
+ this.mOutgoingChatState = state;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public ChatState getOutgoingChatState() {
+ return this.mOutgoingChatState;
+ }
+
+ public void trim() {
+ synchronized (this.messages) {
+ final int size = messages.size();
+ final int maxsize = Config.PAGE_SIZE * Config.MAX_NUM_PAGES;
+ if (size > maxsize) {
+ List<Message> discards = this.messages.subList(0, size - maxsize);
+ final PgpDecryptionService pgpDecryptionService = account.getPgpDecryptionService();
+ if (pgpDecryptionService != null) {
+ pgpDecryptionService.discard(discards);
+ }
+ discards.clear();
+ untieMessages();
+ }
+ }
+ }
+
+ public void findUnsentMessagesWithEncryption(int encryptionType, OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if ((message.getStatus() == Message.STATUS_UNSEND || message.getStatus() == Message.STATUS_WAITING)
+ && (message.getEncryption() == encryptionType)) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public void findUnsentTextMessages(OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (message.getType() != Message.TYPE_IMAGE
+ && message.getStatus() == Message.STATUS_UNSEND) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public Message findSentMessageWithUuidOrRemoteId(String id) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (id.equals(message.getUuid())
+ || (message.getStatus() >= Message.STATUS_SEND
+ && id.equals(message.getRemoteMsgId()))) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+
+ public Message findMessageWithRemoteIdAndCounterpart(String id, Jid counterpart, boolean received, boolean carbon) {
+ synchronized (this.messages) {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ Message message = messages.get(i);
+ if (counterpart.equals(message.getCounterpart())
+ && ((message.getStatus() == Message.STATUS_RECEIVED) == received)
+ && (carbon == message.isCarbon() || received)) {
+ if (id.equals(message.getRemoteMsgId())) {
+ return message;
+ } else {
+ return null;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public Message findSentMessageWithUuid(String id) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (id.equals(message.getUuid())) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+
+ 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
+ public boolean isBlocked() {
+ return getContact().isBlocked();
+ }
+
+ @Override
+ public boolean isDomainBlocked() {
+ return getContact().isDomainBlocked();
+ }
+
+ @Override
+ public Jid getBlockedJid() {
+ return getContact().getBlockedJid();
+ }
+
+ public String getLastReceivedOtrMessageId() {
+ return this.mLastReceivedOtrMessageId;
+ }
+
+ public void setLastReceivedOtrMessageId(String id) {
+ this.mLastReceivedOtrMessageId = id;
+ }
+
+ public int countMessages() {
+ synchronized (this.messages) {
+ return this.messages.size();
+ }
+ }
+
+ public void setFirstMamReference(String reference) {
+ this.mFirstMamReference = reference;
+ }
+
+ public String getFirstMamReference() {
+ return this.mFirstMamReference;
+ }
+
+ public void setLastClearHistory(long time) {
+ setAttribute("ATTRIBUTE_LAST_CLEAR_HISTORY", String.valueOf(time));
+ }
+
+ public long getLastClearHistory() {
+ return getLongAttribute("ATTRIBUTE_LAST_CLEAR_HISTORY", 0);
+ }
+
+ public List<Jid> getAcceptedCryptoTargets() {
+ if (mode == MODE_SINGLE) {
+ return Arrays.asList(getJid().toBareJid());
+ } else {
+ return getJidListAttribute(ATTRIBUTE_CRYPTO_TARGETS);
+ }
+ }
+
+ public void setAcceptedCryptoTargets(List<Jid> acceptedTargets) {
+ 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();
+ }
+
+ @Override
+ public int compareTo(Conversation another) {
+ final Message left = getLatestMessage();
+ final Message right = another.getLatestMessage();
+ if (left.getTimeSent() > right.getTimeSent()) {
+ return -1;
+ } else if (left.getTimeSent() < right.getTimeSent()) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ public interface OnMessageFound {
+ void onMessageFound(final Message message);
+ }
+
+ public Conversation(final String name, final Account account, final Jid contactJid,
+ final int mode) {
+ this(java.util.UUID.randomUUID().toString(), name, null, account
+ .getUuid(), contactJid, System.currentTimeMillis(),
+ STATUS_AVAILABLE, mode, "");
+ this.account = account;
+ }
+
+ public Conversation(final String uuid, final String name, final String contactUuid,
+ final String accountUuid, final Jid contactJid, final long created, final int status,
+ final int mode, final String attributes) {
+ this.uuid = uuid;
+ this.name = name;
+ this.contactUuid = contactUuid;
+ this.accountUuid = accountUuid;
+ this.contactJid = contactJid;
+ this.created = created;
+ this.status = status;
+ this.mode = mode;
+ try {
+ this.attributes = new JSONObject(attributes == null ? "" : attributes);
+ } catch (JSONException e) {
+ this.attributes = new JSONObject();
+ }
+ }
+
+ public boolean isRead() {
+ return (this.messages.size() == 0) || this.messages.get(this.messages.size() - 1).isRead();
+ }
+
+ public List<Message> markRead() {
+ final List<Message> unread = new ArrayList<>();
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (!message.isRead()) {
+ message.markRead();
+ unread.add(message);
+ }
+ }
+ }
+ return unread;
+ }
+
+ public Message getLatestMarkableMessage() {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ if (this.messages.get(i).getStatus() <= Message.STATUS_RECEIVED
+ && this.messages.get(i).markable) {
+ if (this.messages.get(i).isRead()) {
+ return null;
+ } else {
+ return this.messages.get(i);
+ }
+ }
+ }
+ return null;
+ }
+
+ public Message getLatestMessage() {
+ if (this.messages.size() == 0) {
+ Message message = new Message(this, "", Message.ENCRYPTION_NONE);
+ message.setTime(getCreated());
+ return message;
+ } else {
+ Message message = this.messages.get(this.messages.size() - 1);
+ message.setConversation(this);
+ return message;
+ }
+ }
+
+ public String getName() {
+ if (getMode() == MODE_MULTI) {
+ if (getMucOptions().getSubject() != null) {
+ return getMucOptions().getSubject();
+ } else if (bookmark != null
+ && bookmark.getBookmarkName() != null
+ && !bookmark.getBookmarkName().trim().isEmpty()) {
+ return bookmark.getBookmarkName().trim();
+ } else {
+ String generatedName = getMucOptions().createNameFromParticipants();
+ if (generatedName != null) {
+ return generatedName;
+ } else {
+ return getJid().getLocalpart();
+ }
+ }
+ } else {
+ return this.getContact().getDisplayName();
+ }
+ }
+
+ public String getParticipants() {
+ if (getMode() == MODE_MULTI) {
+ String generatedName = getMucOptions().createNameFromParticipants();
+ if (generatedName != null) {
+ return generatedName;
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ public String getAccountUuid() {
+ return this.accountUuid;
+ }
+
+ public Account getAccount() {
+ return this.account;
+ }
+
+ public Contact getContact() {
+ return this.account.getRoster().getContact(this.contactJid);
+ }
+
+ public void setAccount(final Account account) {
+ this.account = account;
+ }
+
+ @Override
+ public Jid getJid() {
+ return this.contactJid;
+ }
+
+ public int getStatus() {
+ return this.status;
+ }
+
+ public long getCreated() {
+ return this.created;
+ }
+
+ public ContentValues getContentValues() {
+ ContentValues values = new ContentValues();
+ values.put(UUID, uuid);
+ values.put(NAME, name);
+ values.put(CONTACT, contactUuid);
+ values.put(ACCOUNT, accountUuid);
+ values.put(CONTACTJID, contactJid.toPreppedString());
+ values.put(CREATED, created);
+ values.put(STATUS, status);
+ values.put(MODE, mode);
+ values.put(ATTRIBUTES, attributes.toString());
+ return values;
+ }
+
+ public static Conversation fromCursor(Cursor cursor) {
+ Jid jid;
+ try {
+ jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(CONTACTJID)), true);
+ } catch (final InvalidJidException e) {
+ // Borked DB..
+ jid = null;
+ }
+ return new Conversation(cursor.getString(cursor.getColumnIndex(UUID)),
+ cursor.getString(cursor.getColumnIndex(NAME)),
+ cursor.getString(cursor.getColumnIndex(CONTACT)),
+ cursor.getString(cursor.getColumnIndex(ACCOUNT)),
+ jid,
+ cursor.getLong(cursor.getColumnIndex(CREATED)),
+ cursor.getInt(cursor.getColumnIndex(STATUS)),
+ cursor.getInt(cursor.getColumnIndex(MODE)),
+ cursor.getString(cursor.getColumnIndex(ATTRIBUTES)));
+ }
+
+ public void setStatus(int status) {
+ this.status = status;
+ }
+
+ public int getMode() {
+ return this.mode;
+ }
+
+ public void setMode(int mode) {
+ this.mode = mode;
+ }
+
+ public SessionImpl startOtrSession(String presence, boolean sendStart) {
+ if (this.otrSession != null) {
+ return this.otrSession;
+ } else {
+ final SessionID sessionId = new SessionID(this.getJid().toBareJid().toString(),
+ presence,
+ "xmpp");
+ this.otrSession = new SessionImpl(sessionId, getAccount().getOtrService());
+ try {
+ if (sendStart) {
+ this.otrSession.startSession();
+ return this.otrSession;
+ }
+ return this.otrSession;
+ } catch (OtrException e) {
+ return null;
+ }
+ }
+
+ }
+
+ public SessionImpl getOtrSession() {
+ return this.otrSession;
+ }
+
+ public void resetOtrSession() {
+ this.otrFingerprint = null;
+ this.otrSession = null;
+ this.mSmp.hint = null;
+ this.mSmp.secret = null;
+ this.mSmp.status = Smp.STATUS_NONE;
+ }
+
+ public Smp smp() {
+ return mSmp;
+ }
+
+ public boolean startOtrIfNeeded() {
+ if (this.otrSession != null && this.otrSession.getSessionStatus() != SessionStatus.ENCRYPTED) {
+ try {
+ this.otrSession.startSession();
+ return true;
+ } catch (OtrException e) {
+ this.resetOtrSession();
+ return false;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ public boolean endOtrIfNeeded() {
+ if (this.otrSession != null) {
+ if (this.otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) {
+ try {
+ this.otrSession.endSession();
+ this.resetOtrSession();
+ return true;
+ } catch (OtrException e) {
+ this.resetOtrSession();
+ return false;
+ }
+ } else {
+ this.resetOtrSession();
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ public boolean hasValidOtrSession() {
+ return this.otrSession != null;
+ }
+
+ public synchronized String getOtrFingerprint() {
+ if (this.otrFingerprint == null) {
+ try {
+ if (getOtrSession() == null || getOtrSession().getSessionStatus() != SessionStatus.ENCRYPTED) {
+ return null;
+ }
+ DSAPublicKey remotePubKey = (DSAPublicKey) getOtrSession().getRemotePublicKey();
+ this.otrFingerprint = getAccount().getOtrService().getFingerprint(remotePubKey);
+ } catch (final OtrCryptoException | UnsupportedOperationException ignored) {
+ return null;
+ }
+ }
+ return this.otrFingerprint;
+ }
+
+ public boolean verifyOtrFingerprint() {
+ final String fingerprint = getOtrFingerprint();
+ if (fingerprint != null) {
+ getContact().addOtrFingerprint(fingerprint);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isOtrFingerprintVerified() {
+ return getContact().getOtrFingerprints().contains(getOtrFingerprint());
+ }
+
+ /**
+ * short for is Private and Non-anonymous
+ */
+ private boolean isPnNA() {
+ return mode == MODE_SINGLE || (getMucOptions().membersOnly() && getMucOptions().nonanonymous());
+ }
+
+ public synchronized MucOptions getMucOptions() {
+ if (this.mucOptions == null) {
+ this.mucOptions = new MucOptions(this);
+ }
+ return this.mucOptions;
+ }
+
+ public void resetMucOptions() {
+ this.mucOptions = null;
+ }
+
+ public void setContactJid(final Jid jid) {
+ this.contactJid = jid;
+ }
+
+ public void setNextCounterpart(Jid jid) {
+ this.nextCounterpart = jid;
+ }
+
+ public Jid getNextCounterpart() {
+ return this.nextCounterpart;
+ }
+
+ private int getMostRecentlyUsedIncomingEncryption() {
+ synchronized (this.messages) {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ final Message m = this.messages.get(i);
+ if (m.getStatus() == Message.STATUS_RECEIVED) {
+ final int e = m.getEncryption();
+ if (e == Message.ENCRYPTION_DECRYPTED || e == Message.ENCRYPTION_DECRYPTION_FAILED) {
+ return Message.ENCRYPTION_PGP;
+ } else {
+ return e;
+ }
+ }
+ }
+ }
+ return Message.ENCRYPTION_NONE;
+ }
+
+ public int getNextEncryption() {
+ return Math.max(this.getIntAttribute(ATTRIBUTE_NEXT_ENCRYPTION, Message.ENCRYPTION_NONE), Message.ENCRYPTION_NONE);
+ }
+
+ public void setNextEncryption(int encryption) {
+ this.setAttribute(ATTRIBUTE_NEXT_ENCRYPTION, String.valueOf(encryption));
+ }
+
+ public String getNextMessage() {
+ if (this.nextMessage == null) {
+ return "";
+ } else {
+ return this.nextMessage;
+ }
+ }
+
+ public boolean smpRequested() {
+ return smp().status == Smp.STATUS_CONTACT_REQUESTED;
+ }
+
+ public void setNextMessage(String message) {
+ this.nextMessage = message;
+ }
+
+ public void setSymmetricKey(byte[] key) {
+ this.symmetricKey = key;
+ }
+
+ public byte[] getSymmetricKey() {
+ return this.symmetricKey;
+ }
+
+ public void setBookmark(Bookmark bookmark) {
+ this.bookmark = bookmark;
+ this.bookmark.setConversation(this);
+ }
+
+ public void deregisterWithBookmark() {
+ if (this.bookmark != null) {
+ this.bookmark.setConversation(null);
+ }
+ }
+
+ public Bookmark getBookmark() {
+ return this.bookmark;
+ }
+
+ public boolean hasDuplicateMessage(Message message) {
+ synchronized (this.messages) {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ if (this.messages.get(i).similar(message)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public Message findSentMessageWithBody(String body) {
+ synchronized (this.messages) {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ Message message = this.messages.get(i);
+ if (message.getStatus() == Message.STATUS_UNSEND || message.getStatus() == Message.STATUS_SEND) {
+ String otherBody;
+ if (message.hasFileOnRemoteHost()) {
+ otherBody = message.getFileParams().url.toString();
+ } else {
+ otherBody = message.body;
+ }
+ if (otherBody != null && otherBody.equals(body)) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+ }
+
+ public long getLastMessageTransmitted() {
+ final long last_clear = getLastClearHistory();
+ long last_received = 0;
+ synchronized (this.messages) {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ Message message = this.messages.get(i);
+ if (message.getStatus() == Message.STATUS_RECEIVED || message.isCarbon()) {
+ last_received = message.getTimeSent();
+ break;
+ }
+ }
+ }
+ return Math.max(last_clear, last_received);
+ }
+
+ public void setMutedTill(long value) {
+ this.setAttribute(ATTRIBUTE_MUTED_TILL, String.valueOf(value));
+ }
+
+ public boolean isMuted() {
+ return System.currentTimeMillis() < this.getLongAttribute(ATTRIBUTE_MUTED_TILL, 0);
+ }
+
+ public boolean alwaysNotify() {
+ return mode == MODE_SINGLE || getBooleanAttribute(ATTRIBUTE_ALWAYS_NOTIFY, Config.ALWAYS_NOTIFY_BY_DEFAULT || isPnNA());
+ }
+
+ public boolean setAttribute(String key, String value) {
+ synchronized (this.attributes) {
+ try {
+ this.attributes.put(key, value);
+ return true;
+ } catch (JSONException e) {
+ return false;
+ }
+ }
+ }
+
+ public boolean setAttribute(String key, List<Jid> jids) {
+ JSONArray array = new JSONArray();
+ for (Jid jid : jids) {
+ array.put(jid.toBareJid().toString());
+ }
+ synchronized (this.attributes) {
+ try {
+ this.attributes.put(key, array);
+ return true;
+ } catch (JSONException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+ }
+
+ public String getAttribute(String key) {
+ synchronized (this.attributes) {
+ try {
+ return this.attributes.getString(key);
+ } catch (JSONException e) {
+ return null;
+ }
+ }
+ }
+
+ public List<Jid> getJidListAttribute(String key) {
+ ArrayList<Jid> list = new ArrayList<>();
+ synchronized (this.attributes) {
+ try {
+ JSONArray array = this.attributes.getJSONArray(key);
+ for (int i = 0; i < array.length(); ++i) {
+ try {
+ list.add(Jid.fromString(array.getString(i)));
+ } catch (InvalidJidException e) {
+ //ignored
+ }
+ }
+ } catch (JSONException e) {
+ //ignored
+ }
+ }
+ return list;
+ }
+
+ public int getIntAttribute(String key, int defaultValue) {
+ String value = this.getAttribute(key);
+ if (value == null) {
+ return defaultValue;
+ } else {
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+ }
+
+ public long getLongAttribute(String key, long defaultValue) {
+ String value = this.getAttribute(key);
+ if (value == null) {
+ return defaultValue;
+ } else {
+ try {
+ return Long.parseLong(value);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+ }
+
+ public boolean getBooleanAttribute(String key, boolean defaultValue) {
+ String value = this.getAttribute(key);
+ if (value == null) {
+ return defaultValue;
+ } else {
+ return Boolean.parseBoolean(value);
+ }
+ }
+
+ public void add(Message message) {
+ message.setConversation(this);
+ synchronized (this.messages) {
+ this.messages.add(message);
+ }
+ }
+
+ public void prepend(Message message) {
+ message.setConversation(this);
+ synchronized (this.messages) {
+ this.messages.add(0, message);
+ }
+ }
+
+ public void addAll(int index, List<Message> messages) {
+ synchronized (this.messages) {
+ this.messages.addAll(index, messages);
+ }
+ account.getPgpDecryptionService().decrypt(messages);
+ }
+
+ public void sort() {
+ synchronized (this.messages) {
+ Collections.sort(this.messages, new Comparator<Message>() {
+ @Override
+ public int compare(Message left, Message right) {
+ if (left.getTimeSent() < right.getTimeSent()) {
+ return -1;
+ } else if (left.getTimeSent() > right.getTimeSent()) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ });
+ untieMessages();
+ }
+ }
+
+ private void untieMessages() {
+ for (Message message : this.messages) {
+ message.untie();
+ }
+ }
+
+ public int unreadCount() {
+ synchronized (this.messages) {
+ int count = 0;
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ if (this.messages.get(i).isRead()) {
+ return count;
+ }
+ ++count;
+ }
+ return count;
+ }
+ }
+
+ public class Smp {
+ public static final int STATUS_NONE = 0;
+ public static final int STATUS_CONTACT_REQUESTED = 1;
+ public static final int STATUS_WE_REQUESTED = 2;
+ public static final int STATUS_FAILED = 3;
+ public static final int STATUS_VERIFIED = 4;
+
+ public String secret = null;
+ public String hint = null;
+ public int status = 0;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/DownloadableFile.java b/src/main/java/de/pixart/messenger/entities/DownloadableFile.java
index 1a6e88881..ee0694196 100644
--- a/src/main/java/de/pixart/messenger/entities/DownloadableFile.java
+++ b/src/main/java/de/pixart/messenger/entities/DownloadableFile.java
@@ -6,78 +6,78 @@ import de.pixart.messenger.utils.MimeUtils;
public class DownloadableFile extends File {
- private static final long serialVersionUID = 2247012619505115863L;
-
- private long expectedSize = 0;
- private String sha1sum;
- private byte[] aeskey;
-
- private byte[] iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
- 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf };
-
- public DownloadableFile(String path) {
- super(path);
- }
-
- public long getSize() {
- return super.length();
- }
-
- public long getExpectedSize() {
- return this.expectedSize;
- }
-
- public String getMimeType() {
- String path = this.getAbsolutePath();
- int start = path.lastIndexOf('.') + 1;
- if (start < path.length()) {
- String mime = MimeUtils.guessMimeTypeFromExtension(path.substring(start));
- return mime == null ? "" : mime;
- } else {
- return "";
- }
- }
-
- public void setExpectedSize(long size) {
- this.expectedSize = size;
- }
-
- public String getSha1Sum() {
- return this.sha1sum;
- }
-
- public void setSha1Sum(String sum) {
- this.sha1sum = sum;
- }
-
- public void setKeyAndIv(byte[] keyIvCombo) {
- if (keyIvCombo.length == 48) {
- this.aeskey = new byte[32];
- this.iv = new byte[16];
- System.arraycopy(keyIvCombo, 0, this.iv, 0, 16);
- System.arraycopy(keyIvCombo, 16, this.aeskey, 0, 32);
- } else if (keyIvCombo.length >= 32) {
- this.aeskey = new byte[32];
- System.arraycopy(keyIvCombo, 0, aeskey, 0, 32);
- } else if (keyIvCombo.length >= 16) {
- this.aeskey = new byte[16];
- System.arraycopy(keyIvCombo, 0, this.aeskey, 0, 16);
- }
- }
-
- public void setKey(byte[] key) {
- this.aeskey = key;
- }
-
- public void setIv(byte[] iv) {
- this.iv = iv;
- }
-
- public byte[] getKey() {
- return this.aeskey;
- }
-
- public byte[] getIv() {
- return this.iv;
- }
+ private static final long serialVersionUID = 2247012619505115863L;
+
+ private long expectedSize = 0;
+ private String sha1sum;
+ private byte[] aeskey;
+
+ private byte[] iv = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf};
+
+ public DownloadableFile(String path) {
+ super(path);
+ }
+
+ public long getSize() {
+ return super.length();
+ }
+
+ public long getExpectedSize() {
+ return this.expectedSize;
+ }
+
+ public String getMimeType() {
+ String path = this.getAbsolutePath();
+ int start = path.lastIndexOf('.') + 1;
+ if (start < path.length()) {
+ String mime = MimeUtils.guessMimeTypeFromExtension(path.substring(start));
+ return mime == null ? "" : mime;
+ } else {
+ return "";
+ }
+ }
+
+ public void setExpectedSize(long size) {
+ this.expectedSize = size;
+ }
+
+ public String getSha1Sum() {
+ return this.sha1sum;
+ }
+
+ public void setSha1Sum(String sum) {
+ this.sha1sum = sum;
+ }
+
+ public void setKeyAndIv(byte[] keyIvCombo) {
+ if (keyIvCombo.length == 48) {
+ this.aeskey = new byte[32];
+ this.iv = new byte[16];
+ System.arraycopy(keyIvCombo, 0, this.iv, 0, 16);
+ System.arraycopy(keyIvCombo, 16, this.aeskey, 0, 32);
+ } else if (keyIvCombo.length >= 32) {
+ this.aeskey = new byte[32];
+ System.arraycopy(keyIvCombo, 0, aeskey, 0, 32);
+ } else if (keyIvCombo.length >= 16) {
+ this.aeskey = new byte[16];
+ System.arraycopy(keyIvCombo, 0, this.aeskey, 0, 16);
+ }
+ }
+
+ public void setKey(byte[] key) {
+ this.aeskey = key;
+ }
+
+ public void setIv(byte[] iv) {
+ this.iv = iv;
+ }
+
+ public byte[] getKey() {
+ return this.aeskey;
+ }
+
+ public byte[] getIv() {
+ return this.iv;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/ListItem.java b/src/main/java/de/pixart/messenger/entities/ListItem.java
index cb0644499..7fbd37200 100644
--- a/src/main/java/de/pixart/messenger/entities/ListItem.java
+++ b/src/main/java/de/pixart/messenger/entities/ListItem.java
@@ -7,31 +7,31 @@ import java.util.List;
import de.pixart.messenger.xmpp.jid.Jid;
public interface ListItem extends Comparable<ListItem> {
- String getDisplayName();
+ String getDisplayName();
- String getDisplayJid();
+ String getDisplayJid();
- Jid getJid();
+ Jid getJid();
- List<Tag> getTags(Context context);
+ List<Tag> getTags(Context context);
- final class Tag {
- private final String name;
- private final int color;
+ final class Tag {
+ private final String name;
+ private final int color;
- public Tag(final String name, final int color) {
- this.name = name;
- this.color = color;
- }
+ public Tag(final String name, final int color) {
+ this.name = name;
+ this.color = color;
+ }
- public int getColor() {
- return this.color;
- }
+ public int getColor() {
+ return this.color;
+ }
- public String getName() {
- return this.name;
- }
- }
+ public String getName() {
+ return this.name;
+ }
+ }
- boolean match(Context context, final String needle);
+ boolean match(Context context, final String needle);
}
diff --git a/src/main/java/de/pixart/messenger/entities/Message.java b/src/main/java/de/pixart/messenger/entities/Message.java
index 9cdf57922..4224f6d6f 100644
--- a/src/main/java/de/pixart/messenger/entities/Message.java
+++ b/src/main/java/de/pixart/messenger/entities/Message.java
@@ -230,7 +230,7 @@ public class Message extends AbstractEntity {
values.put(READ, read ? 1 : 0);
values.put(EDITED, edited);
values.put(OOB, oob ? 1 : 0);
- values.put(ERROR_MESSAGE,errorMessage);
+ values.put(ERROR_MESSAGE, errorMessage);
return values;
}
@@ -510,7 +510,8 @@ public class Message extends AbstractEntity {
);
}
- public static class MergeSeparator {}
+ public static class MergeSeparator {
+ }
public SpannableStringBuilder getMergedBody() {
SpannableStringBuilder body = new SpannableStringBuilder(this.body.trim());
diff --git a/src/main/java/de/pixart/messenger/entities/MucOptions.java b/src/main/java/de/pixart/messenger/entities/MucOptions.java
index 4817f4bae..a95debd89 100644
--- a/src/main/java/de/pixart/messenger/entities/MucOptions.java
+++ b/src/main/java/de/pixart/messenger/entities/MucOptions.java
@@ -17,667 +17,667 @@ import de.pixart.messenger.xmpp.pep.Avatar;
@SuppressLint("DefaultLocale")
public class MucOptions {
- private boolean mAutoPushConfiguration = true;
-
- public Account getAccount() {
- return this.conversation.getAccount();
- }
-
- public void setSelf(User user) {
- this.self = user;
- }
-
- public void changeAffiliation(Jid jid, Affiliation affiliation) {
- User user = findUserByRealJid(jid);
- synchronized (users) {
- if (user != null && user.getRole() == Role.NONE) {
- users.remove(user);
- if (affiliation.ranks(Affiliation.MEMBER)) {
- user.affiliation = affiliation;
- users.add(user);
- }
- }
- }
- }
-
- public void flagNoAutoPushConfiguration() {
- mAutoPushConfiguration = false;
- }
-
- public boolean autoPushConfiguration() {
- return mAutoPushConfiguration;
- }
-
- public enum Affiliation {
- OWNER("owner", 4, R.string.owner),
- ADMIN("admin", 3, R.string.admin),
- MEMBER("member", 2, R.string.member),
- OUTCAST("outcast", 0, R.string.outcast),
- NONE("none", 1, R.string.no_affiliation);
-
- Affiliation(String string, int rank, int resId) {
- this.string = string;
- this.resId = resId;
- this.rank = rank;
- }
-
- private String string;
- private int resId;
- private int rank;
-
- public int getResId() {
- return resId;
- }
-
- @Override
- public String toString() {
- return this.string;
- }
-
- public boolean outranks(Affiliation affiliation) {
- return rank > affiliation.rank;
- }
-
- public boolean ranks(Affiliation affiliation) {
- return rank >= affiliation.rank;
- }
- }
-
- public enum Role {
- MODERATOR("moderator", R.string.moderator,3),
- VISITOR("visitor", R.string.visitor,1),
- PARTICIPANT("participant", R.string.participant,2),
- NONE("none", R.string.no_role,0);
-
- Role(String string, int resId, int rank) {
- this.string = string;
- this.resId = resId;
- this.rank = rank;
- }
-
- private String string;
- private int resId;
- private int rank;
-
- public int getResId() {
- return resId;
- }
-
- @Override
- public String toString() {
- return this.string;
- }
-
- public boolean ranks(Role role) {
- return rank >= role.rank;
- }
- }
-
- public enum Error {
- NO_RESPONSE,
- SERVER_NOT_FOUND,
- NONE,
- NICK_IN_USE,
- PASSWORD_REQUIRED,
- BANNED,
- MEMBERS_ONLY,
- KICKED,
- SHUTDOWN,
- UNKNOWN
- }
-
- public static final String STATUS_CODE_SELF_PRESENCE = "110";
- public static final String STATUS_CODE_ROOM_CREATED = "201";
- public static final String STATUS_CODE_BANNED = "301";
- public static final String STATUS_CODE_CHANGED_NICK = "303";
- public static final String STATUS_CODE_KICKED = "307";
- public static final String STATUS_CODE_AFFILIATION_CHANGE = "321";
- public static final String STATUS_CODE_LOST_MEMBERSHIP = "322";
- public static final String STATUS_CODE_SHUTDOWN = "332";
-
- private interface OnEventListener {
- void onSuccess();
-
- void onFailure();
- }
-
- public interface OnRenameListener extends OnEventListener {
-
- }
-
- public static class User implements Comparable<User> {
- private Role role = Role.NONE;
- private Affiliation affiliation = Affiliation.NONE;
- private Jid realJid;
- private Jid fullJid;
- private long pgpKeyId = 0;
- private Avatar avatar;
- private MucOptions options;
-
- public User(MucOptions options, Jid from) {
- this.options = options;
- this.fullJid = from;
- }
-
- public String getName() {
- return fullJid == null ? null : fullJid.getResourcepart();
- }
-
- public void setRealJid(Jid jid) {
- this.realJid = jid != null ? jid.toBareJid() : null;
- }
-
- public Role getRole() {
- return this.role;
- }
-
- public void setRole(String role) {
- if (role == null) {
- this.role = Role.NONE;
- return;
- }
- role = role.toLowerCase();
- switch (role) {
- case "moderator":
- this.role = Role.MODERATOR;
- break;
- case "participant":
- this.role = Role.PARTICIPANT;
- break;
- case "visitor":
- this.role = Role.VISITOR;
- break;
- default:
- this.role = Role.NONE;
- break;
- }
- }
-
- public Affiliation getAffiliation() {
- return this.affiliation;
- }
-
- public void setAffiliation(String affiliation) {
- if (affiliation == null) {
- this.affiliation = Affiliation.NONE;
- return;
- }
- affiliation = affiliation.toLowerCase();
- switch (affiliation) {
- case "admin":
- this.affiliation = Affiliation.ADMIN;
- break;
- case "owner":
- this.affiliation = Affiliation.OWNER;
- break;
- case "member":
- this.affiliation = Affiliation.MEMBER;
- break;
- case "outcast":
- this.affiliation = Affiliation.OUTCAST;
- break;
- default:
- this.affiliation = Affiliation.NONE;
- }
- }
-
- public void setPgpKeyId(long id) {
- this.pgpKeyId = id;
- }
-
- public long getPgpKeyId() {
- return this.pgpKeyId;
- }
-
- public Contact getContact() {
- if (fullJid != null) {
- return getAccount().getRoster().getContactFromRoster(realJid);
- } else if (realJid != null){
- return getAccount().getRoster().getContact(realJid);
- } else {
- return null;
- }
- }
-
- public boolean setAvatar(Avatar avatar) {
- if (this.avatar != null && this.avatar.equals(avatar)) {
- return false;
- } else {
- this.avatar = avatar;
- return true;
- }
- }
-
- public String getAvatar() {
- return avatar == null ? null : avatar.getFilename();
- }
-
- public Account getAccount() {
- return options.getAccount();
- }
-
- public Jid getFullJid() {
- return fullJid;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- User user = (User) o;
-
- if (role != user.role) return false;
- if (affiliation != user.affiliation) return false;
- if (realJid != null ? !realJid.equals(user.realJid) : user.realJid != null)
- return false;
- return fullJid != null ? fullJid.equals(user.fullJid) : user.fullJid == null;
-
- }
-
- @Override
- public int hashCode() {
- int result = role != null ? role.hashCode() : 0;
- result = 31 * result + (affiliation != null ? affiliation.hashCode() : 0);
- result = 31 * result + (realJid != null ? realJid.hashCode() : 0);
- result = 31 * result + (fullJid != null ? fullJid.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return "[fulljid:"+String.valueOf(fullJid)+",realjid:"+String.valueOf(realJid)+",affiliation"+affiliation.toString()+"]";
- }
-
- public boolean realJidMatchesAccount() {
- return realJid != null && realJid.equals(options.account.getJid().toBareJid());
- }
-
- @Override
- public int compareTo(User another) {
- if (another.getAffiliation().outranks(getAffiliation())) {
- return 1;
- } else if (getAffiliation().outranks(another.getAffiliation())) {
- return -1;
- } else {
- return getComparableName().compareToIgnoreCase(another.getComparableName());
- }
- }
-
-
- private String getComparableName() {
- Contact contact = getContact();
- if (contact != null) {
- return contact.getDisplayName();
- } else {
- String name = getName();
- return name == null ? "" : name;
- }
- }
-
- public Jid getRealJid() {
- return realJid;
- }
- }
-
- private Account account;
- private final Set<User> users = new HashSet<>();
- private final List<String> features = new ArrayList<>();
- private Data form = new Data();
- private Conversation conversation;
- private boolean isOnline = false;
- private Error error = Error.NONE;
- public OnRenameListener onRenameListener = null;
- private User self;
- private String subject = null;
- private String password = null;
- public boolean mNickChangingInProgress = false;
-
- public MucOptions(Conversation conversation) {
- this.account = conversation.getAccount();
- this.conversation = conversation;
- this.self = new User(this,createJoinJid(getProposedNick()));
- }
-
- public void updateFeatures(ArrayList<String> features) {
- this.features.clear();
- this.features.addAll(features);
- }
-
- public void updateFormData(Data form) {
- this.form = form;
- }
-
- public boolean hasFeature(String feature) {
- return this.features.contains(feature);
- }
-
- public boolean canInvite() {
- Field field = this.form.getFieldByName("muc#roomconfig_allowinvites");
- return !membersOnly() || self.getRole().ranks(Role.MODERATOR) || (field != null && "1".equals(field.getValue()));
- }
-
- public boolean canChangeSubject() {
- Field field = this.form.getFieldByName("muc#roomconfig_changesubject");
- return self.getRole().ranks(Role.MODERATOR) || (field != null && "1".equals(field.getValue()));
- }
-
- public boolean participating() {
- return !online()
- || self.getRole().ranks(Role.PARTICIPANT)
- || hasFeature("muc_unmoderated");
- }
-
- public boolean membersOnly() {
- return hasFeature("muc_membersonly");
- }
-
- public boolean mamSupport() {
- // Update with "urn:xmpp:mam:1" once we support it
- return hasFeature("urn:xmpp:mam:0");
- }
-
- public boolean nonanonymous() {
- return hasFeature("muc_nonanonymous");
- }
-
- public boolean persistent() {
- return hasFeature("muc_persistent");
- }
-
- public boolean moderated() {
- return hasFeature("muc_moderated");
- }
-
- public User deleteUser(Jid jid) {
- User user = findUserByFullJid(jid);
- if (user != null) {
- synchronized (users) {
- users.remove(user);
- if (membersOnly() &&
- nonanonymous() &&
- user.affiliation.ranks(Affiliation.MEMBER) &&
- user.realJid != null) {
- user.role = Role.NONE;
- user.avatar = null;
- user.fullJid = null;
- users.add(user);
- }
- }
- }
- return user;
- }
-
- public void updateUser(User user) {
- User old;
- if (user.fullJid == null && user.realJid != null) {
- old = findUserByRealJid(user.realJid);
- if (old != null) {
- if (old.fullJid != null) {
- return; //don't add. user already exists
- } else {
- synchronized (users) {
- users.remove(old);
- }
- }
- }
- } else if (user.realJid != null) {
- old = findUserByRealJid(user.realJid);
- synchronized (users) {
- if (old != null && old.fullJid == null) {
- users.remove(old);
- }
- }
- }
- old = findUserByFullJid(user.getFullJid());
- synchronized (this.users) {
- if (old != null) {
- users.remove(old);
- }
+ private boolean mAutoPushConfiguration = true;
+
+ public Account getAccount() {
+ return this.conversation.getAccount();
+ }
+
+ public void setSelf(User user) {
+ this.self = user;
+ }
+
+ public void changeAffiliation(Jid jid, Affiliation affiliation) {
+ User user = findUserByRealJid(jid);
+ synchronized (users) {
+ if (user != null && user.getRole() == Role.NONE) {
+ users.remove(user);
+ if (affiliation.ranks(Affiliation.MEMBER)) {
+ user.affiliation = affiliation;
+ users.add(user);
+ }
+ }
+ }
+ }
+
+ public void flagNoAutoPushConfiguration() {
+ mAutoPushConfiguration = false;
+ }
+
+ public boolean autoPushConfiguration() {
+ return mAutoPushConfiguration;
+ }
+
+ public enum Affiliation {
+ OWNER("owner", 4, R.string.owner),
+ ADMIN("admin", 3, R.string.admin),
+ MEMBER("member", 2, R.string.member),
+ OUTCAST("outcast", 0, R.string.outcast),
+ NONE("none", 1, R.string.no_affiliation);
+
+ Affiliation(String string, int rank, int resId) {
+ this.string = string;
+ this.resId = resId;
+ this.rank = rank;
+ }
+
+ private String string;
+ private int resId;
+ private int rank;
+
+ public int getResId() {
+ return resId;
+ }
+
+ @Override
+ public String toString() {
+ return this.string;
+ }
+
+ public boolean outranks(Affiliation affiliation) {
+ return rank > affiliation.rank;
+ }
+
+ public boolean ranks(Affiliation affiliation) {
+ return rank >= affiliation.rank;
+ }
+ }
+
+ public enum Role {
+ MODERATOR("moderator", R.string.moderator, 3),
+ VISITOR("visitor", R.string.visitor, 1),
+ PARTICIPANT("participant", R.string.participant, 2),
+ NONE("none", R.string.no_role, 0);
+
+ Role(String string, int resId, int rank) {
+ this.string = string;
+ this.resId = resId;
+ this.rank = rank;
+ }
+
+ private String string;
+ private int resId;
+ private int rank;
+
+ public int getResId() {
+ return resId;
+ }
+
+ @Override
+ public String toString() {
+ return this.string;
+ }
+
+ public boolean ranks(Role role) {
+ return rank >= role.rank;
+ }
+ }
+
+ public enum Error {
+ NO_RESPONSE,
+ SERVER_NOT_FOUND,
+ NONE,
+ NICK_IN_USE,
+ PASSWORD_REQUIRED,
+ BANNED,
+ MEMBERS_ONLY,
+ KICKED,
+ SHUTDOWN,
+ UNKNOWN
+ }
+
+ public static final String STATUS_CODE_SELF_PRESENCE = "110";
+ public static final String STATUS_CODE_ROOM_CREATED = "201";
+ public static final String STATUS_CODE_BANNED = "301";
+ public static final String STATUS_CODE_CHANGED_NICK = "303";
+ public static final String STATUS_CODE_KICKED = "307";
+ public static final String STATUS_CODE_AFFILIATION_CHANGE = "321";
+ public static final String STATUS_CODE_LOST_MEMBERSHIP = "322";
+ public static final String STATUS_CODE_SHUTDOWN = "332";
+
+ private interface OnEventListener {
+ void onSuccess();
+
+ void onFailure();
+ }
+
+ public interface OnRenameListener extends OnEventListener {
+
+ }
+
+ public static class User implements Comparable<User> {
+ private Role role = Role.NONE;
+ private Affiliation affiliation = Affiliation.NONE;
+ private Jid realJid;
+ private Jid fullJid;
+ private long pgpKeyId = 0;
+ private Avatar avatar;
+ private MucOptions options;
+
+ public User(MucOptions options, Jid from) {
+ this.options = options;
+ this.fullJid = from;
+ }
+
+ public String getName() {
+ return fullJid == null ? null : fullJid.getResourcepart();
+ }
+
+ public void setRealJid(Jid jid) {
+ this.realJid = jid != null ? jid.toBareJid() : null;
+ }
+
+ public Role getRole() {
+ return this.role;
+ }
+
+ public void setRole(String role) {
+ if (role == null) {
+ this.role = Role.NONE;
+ return;
+ }
+ role = role.toLowerCase();
+ switch (role) {
+ case "moderator":
+ this.role = Role.MODERATOR;
+ break;
+ case "participant":
+ this.role = Role.PARTICIPANT;
+ break;
+ case "visitor":
+ this.role = Role.VISITOR;
+ break;
+ default:
+ this.role = Role.NONE;
+ break;
+ }
+ }
+
+ public Affiliation getAffiliation() {
+ return this.affiliation;
+ }
+
+ public void setAffiliation(String affiliation) {
+ if (affiliation == null) {
+ this.affiliation = Affiliation.NONE;
+ return;
+ }
+ affiliation = affiliation.toLowerCase();
+ switch (affiliation) {
+ case "admin":
+ this.affiliation = Affiliation.ADMIN;
+ break;
+ case "owner":
+ this.affiliation = Affiliation.OWNER;
+ break;
+ case "member":
+ this.affiliation = Affiliation.MEMBER;
+ break;
+ case "outcast":
+ this.affiliation = Affiliation.OUTCAST;
+ break;
+ default:
+ this.affiliation = Affiliation.NONE;
+ }
+ }
+
+ public void setPgpKeyId(long id) {
+ this.pgpKeyId = id;
+ }
+
+ public long getPgpKeyId() {
+ return this.pgpKeyId;
+ }
+
+ public Contact getContact() {
+ if (fullJid != null) {
+ return getAccount().getRoster().getContactFromRoster(realJid);
+ } else if (realJid != null) {
+ return getAccount().getRoster().getContact(realJid);
+ } else {
+ return null;
+ }
+ }
+
+ public boolean setAvatar(Avatar avatar) {
+ if (this.avatar != null && this.avatar.equals(avatar)) {
+ return false;
+ } else {
+ this.avatar = avatar;
+ return true;
+ }
+ }
+
+ public String getAvatar() {
+ return avatar == null ? null : avatar.getFilename();
+ }
+
+ public Account getAccount() {
+ return options.getAccount();
+ }
+
+ public Jid getFullJid() {
+ return fullJid;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ User user = (User) o;
+
+ if (role != user.role) return false;
+ if (affiliation != user.affiliation) return false;
+ if (realJid != null ? !realJid.equals(user.realJid) : user.realJid != null)
+ return false;
+ return fullJid != null ? fullJid.equals(user.fullJid) : user.fullJid == null;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = role != null ? role.hashCode() : 0;
+ result = 31 * result + (affiliation != null ? affiliation.hashCode() : 0);
+ result = 31 * result + (realJid != null ? realJid.hashCode() : 0);
+ result = 31 * result + (fullJid != null ? fullJid.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "[fulljid:" + String.valueOf(fullJid) + ",realjid:" + String.valueOf(realJid) + ",affiliation" + affiliation.toString() + "]";
+ }
+
+ public boolean realJidMatchesAccount() {
+ return realJid != null && realJid.equals(options.account.getJid().toBareJid());
+ }
+
+ @Override
+ public int compareTo(User another) {
+ if (another.getAffiliation().outranks(getAffiliation())) {
+ return 1;
+ } else if (getAffiliation().outranks(another.getAffiliation())) {
+ return -1;
+ } else {
+ return getComparableName().compareToIgnoreCase(another.getComparableName());
+ }
+ }
+
+
+ private String getComparableName() {
+ Contact contact = getContact();
+ if (contact != null) {
+ return contact.getDisplayName();
+ } else {
+ String name = getName();
+ return name == null ? "" : name;
+ }
+ }
+
+ public Jid getRealJid() {
+ return realJid;
+ }
+ }
+
+ private Account account;
+ private final Set<User> users = new HashSet<>();
+ private final List<String> features = new ArrayList<>();
+ private Data form = new Data();
+ private Conversation conversation;
+ private boolean isOnline = false;
+ private Error error = Error.NONE;
+ public OnRenameListener onRenameListener = null;
+ private User self;
+ private String subject = null;
+ private String password = null;
+ public boolean mNickChangingInProgress = false;
+
+ public MucOptions(Conversation conversation) {
+ this.account = conversation.getAccount();
+ this.conversation = conversation;
+ this.self = new User(this, createJoinJid(getProposedNick()));
+ }
+
+ public void updateFeatures(ArrayList<String> features) {
+ this.features.clear();
+ this.features.addAll(features);
+ }
+
+ public void updateFormData(Data form) {
+ this.form = form;
+ }
+
+ public boolean hasFeature(String feature) {
+ return this.features.contains(feature);
+ }
+
+ public boolean canInvite() {
+ Field field = this.form.getFieldByName("muc#roomconfig_allowinvites");
+ return !membersOnly() || self.getRole().ranks(Role.MODERATOR) || (field != null && "1".equals(field.getValue()));
+ }
+
+ public boolean canChangeSubject() {
+ Field field = this.form.getFieldByName("muc#roomconfig_changesubject");
+ return self.getRole().ranks(Role.MODERATOR) || (field != null && "1".equals(field.getValue()));
+ }
+
+ public boolean participating() {
+ return !online()
+ || self.getRole().ranks(Role.PARTICIPANT)
+ || hasFeature("muc_unmoderated");
+ }
+
+ public boolean membersOnly() {
+ return hasFeature("muc_membersonly");
+ }
+
+ public boolean mamSupport() {
+ // Update with "urn:xmpp:mam:1" once we support it
+ return hasFeature("urn:xmpp:mam:0");
+ }
+
+ public boolean nonanonymous() {
+ return hasFeature("muc_nonanonymous");
+ }
+
+ public boolean persistent() {
+ return hasFeature("muc_persistent");
+ }
+
+ public boolean moderated() {
+ return hasFeature("muc_moderated");
+ }
+
+ public User deleteUser(Jid jid) {
+ User user = findUserByFullJid(jid);
+ if (user != null) {
+ synchronized (users) {
+ users.remove(user);
+ if (membersOnly() &&
+ nonanonymous() &&
+ user.affiliation.ranks(Affiliation.MEMBER) &&
+ user.realJid != null) {
+ user.role = Role.NONE;
+ user.avatar = null;
+ user.fullJid = null;
+ users.add(user);
+ }
+ }
+ }
+ return user;
+ }
+
+ public void updateUser(User user) {
+ User old;
+ if (user.fullJid == null && user.realJid != null) {
+ old = findUserByRealJid(user.realJid);
+ if (old != null) {
+ if (old.fullJid != null) {
+ return; //don't add. user already exists
+ } else {
+ synchronized (users) {
+ users.remove(old);
+ }
+ }
+ }
+ } else if (user.realJid != null) {
+ old = findUserByRealJid(user.realJid);
+ synchronized (users) {
+ if (old != null && old.fullJid == null) {
+ users.remove(old);
+ }
+ }
+ }
+ old = findUserByFullJid(user.getFullJid());
+ synchronized (this.users) {
+ if (old != null) {
+ users.remove(old);
+ }
if ((!membersOnly() || user.getAffiliation().ranks(Affiliation.MEMBER))
- && user.getAffiliation().outranks(Affiliation.OUTCAST)){
+ && user.getAffiliation().outranks(Affiliation.OUTCAST)) {
this.users.add(user);
}
- }
- }
-
- public User findUserByFullJid(Jid jid) {
- if (jid == null) {
- return null;
- }
- synchronized (users) {
- for (User user : users) {
- if (jid.equals(user.getFullJid())) {
- return user;
- }
- }
- }
- return null;
- }
-
- private User findUserByRealJid(Jid jid) {
- if (jid == null) {
- return null;
- }
- synchronized (users) {
- for (User user : users) {
- if (jid.equals(user.realJid)) {
- return user;
- }
- }
- }
- return null;
- }
-
- public boolean isContactInRoom(Contact contact) {
- return findUserByRealJid(contact.getJid().toBareJid()) != null;
- }
-
- public boolean isUserInRoom(Jid jid) {
- return findUserByFullJid(jid) != null;
- }
-
- public void setError(Error error) {
- this.isOnline = isOnline && error == Error.NONE;
- this.error = error;
- }
-
- public void setOnline() {
- this.isOnline = true;
- }
-
- public ArrayList<User> getUsers() {
- return getUsers(true);
- }
-
- public ArrayList<User> getUsers(boolean includeOffline) {
- synchronized (users) {
- if (includeOffline) {
- return new ArrayList<>(users);
- } else {
- ArrayList<User> onlineUsers = new ArrayList<>();
- for (User user : users) {
- if (user.getRole().ranks(Role.PARTICIPANT)) {
- onlineUsers.add(user);
- }
- }
- return onlineUsers;
- }
- }
- }
-
- public List<User> getUsers(int max) {
- ArrayList<User> users = getUsers();
- return users.subList(0, Math.min(max, users.size()));
- }
-
- public int getUserCount() {
- synchronized (users) {
- return users.size();
- }
- }
-
- public String getProposedNick() {
- if (conversation.getBookmark() != null
- && conversation.getBookmark().getNick() != null
- && !conversation.getBookmark().getNick().trim().isEmpty()) {
- return conversation.getBookmark().getNick().trim();
- } else if (!conversation.getJid().isBareJid()) {
- return conversation.getJid().getResourcepart();
- } else {
- return account.getUsername();
- }
- }
-
- public String getActualNick() {
- if (this.self.getName() != null) {
- return this.self.getName();
- } else {
- return this.getProposedNick();
- }
- }
-
- public boolean online() {
- return this.isOnline;
- }
-
- public Error getError() {
- return this.error;
- }
-
- public void setOnRenameListener(OnRenameListener listener) {
- this.onRenameListener = listener;
- }
-
- public void setOffline() {
- synchronized (users) {
- this.users.clear();
- }
- this.error = Error.NO_RESPONSE;
- this.isOnline = false;
- }
-
- public User getSelf() {
- return self;
- }
-
- public void setSubject(String content) {
- this.subject = content;
- }
-
- public String getSubject() {
- return this.subject;
- }
-
- public String createNameFromParticipants() {
- if (users.size() >= 1) {
- List<String> names = new ArrayList<>();
- for (User user : getUsers(5)) {
- Contact contact = user.getContact();
- if (contact != null && !contact.getDisplayName().isEmpty()) {
- names.add(contact.getDisplayName().split("\\s+")[0]);
- } else if (user.getName() != null){
- names.add(user.getName());
- }
- }
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < names.size(); ++i) {
- builder.append(names.get(i));
- if (i != names.size() - 1) {
- builder.append(", ");
- }
- }
- return builder.toString();
- } else {
- return null;
- }
- }
-
- public long[] getPgpKeyIds() {
- List<Long> ids = new ArrayList<>();
- for (User user : this.users) {
- if (user.getPgpKeyId() != 0) {
- ids.add(user.getPgpKeyId());
- }
- }
- ids.add(account.getPgpId());
- long[] primitiveLongArray = new long[ids.size()];
- for (int i = 0; i < ids.size(); ++i) {
- primitiveLongArray[i] = ids.get(i);
- }
- return primitiveLongArray;
- }
-
- public boolean pgpKeysInUse() {
- synchronized (users) {
- for (User user : users) {
- if (user.getPgpKeyId() != 0) {
- return true;
- }
- }
- }
- return false;
- }
-
- public boolean everybodyHasKeys() {
- synchronized (users) {
- for (User user : users) {
- if (user.getPgpKeyId() == 0) {
- return false;
- }
- }
- }
- return true;
- }
-
- public Jid createJoinJid(String nick) {
- try {
- return Jid.fromString(this.conversation.getJid().toBareJid().toString() + "/" + nick);
- } catch (final InvalidJidException e) {
- return null;
- }
- }
-
- public Jid getTrueCounterpart(Jid jid) {
- if (jid.equals(getSelf().getFullJid())) {
- return account.getJid().toBareJid();
- }
- User user = findUserByFullJid(jid);
- return user == null ? null : user.realJid;
- }
-
- public String getPassword() {
- this.password = conversation.getAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD);
- if (this.password == null && conversation.getBookmark() != null
- && conversation.getBookmark().getPassword() != null) {
- return conversation.getBookmark().getPassword();
- } else {
- return this.password;
- }
- }
-
- public void setPassword(String password) {
- if (conversation.getBookmark() != null) {
- conversation.getBookmark().setPassword(password);
- } else {
- this.password = password;
- }
- conversation.setAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD, password);
- }
-
- public Conversation getConversation() {
- return this.conversation;
- }
-
- public List<Jid> getMembers() {
- ArrayList<Jid> members = new ArrayList<>();
- synchronized (users) {
- for (User user : users) {
- if (user.affiliation.ranks(Affiliation.MEMBER) && user.realJid != null) {
- members.add(user.realJid);
- }
- }
- }
- return members;
- }
+ }
+ }
+
+ public User findUserByFullJid(Jid jid) {
+ if (jid == null) {
+ return null;
+ }
+ synchronized (users) {
+ for (User user : users) {
+ if (jid.equals(user.getFullJid())) {
+ return user;
+ }
+ }
+ }
+ return null;
+ }
+
+ private User findUserByRealJid(Jid jid) {
+ if (jid == null) {
+ return null;
+ }
+ synchronized (users) {
+ for (User user : users) {
+ if (jid.equals(user.realJid)) {
+ return user;
+ }
+ }
+ }
+ return null;
+ }
+
+ public boolean isContactInRoom(Contact contact) {
+ return findUserByRealJid(contact.getJid().toBareJid()) != null;
+ }
+
+ public boolean isUserInRoom(Jid jid) {
+ return findUserByFullJid(jid) != null;
+ }
+
+ public void setError(Error error) {
+ this.isOnline = isOnline && error == Error.NONE;
+ this.error = error;
+ }
+
+ public void setOnline() {
+ this.isOnline = true;
+ }
+
+ public ArrayList<User> getUsers() {
+ return getUsers(true);
+ }
+
+ public ArrayList<User> getUsers(boolean includeOffline) {
+ synchronized (users) {
+ if (includeOffline) {
+ return new ArrayList<>(users);
+ } else {
+ ArrayList<User> onlineUsers = new ArrayList<>();
+ for (User user : users) {
+ if (user.getRole().ranks(Role.PARTICIPANT)) {
+ onlineUsers.add(user);
+ }
+ }
+ return onlineUsers;
+ }
+ }
+ }
+
+ public List<User> getUsers(int max) {
+ ArrayList<User> users = getUsers();
+ return users.subList(0, Math.min(max, users.size()));
+ }
+
+ public int getUserCount() {
+ synchronized (users) {
+ return users.size();
+ }
+ }
+
+ public String getProposedNick() {
+ if (conversation.getBookmark() != null
+ && conversation.getBookmark().getNick() != null
+ && !conversation.getBookmark().getNick().trim().isEmpty()) {
+ return conversation.getBookmark().getNick().trim();
+ } else if (!conversation.getJid().isBareJid()) {
+ return conversation.getJid().getResourcepart();
+ } else {
+ return account.getUsername();
+ }
+ }
+
+ public String getActualNick() {
+ if (this.self.getName() != null) {
+ return this.self.getName();
+ } else {
+ return this.getProposedNick();
+ }
+ }
+
+ public boolean online() {
+ return this.isOnline;
+ }
+
+ public Error getError() {
+ return this.error;
+ }
+
+ public void setOnRenameListener(OnRenameListener listener) {
+ this.onRenameListener = listener;
+ }
+
+ public void setOffline() {
+ synchronized (users) {
+ this.users.clear();
+ }
+ this.error = Error.NO_RESPONSE;
+ this.isOnline = false;
+ }
+
+ public User getSelf() {
+ return self;
+ }
+
+ public void setSubject(String content) {
+ this.subject = content;
+ }
+
+ public String getSubject() {
+ return this.subject;
+ }
+
+ public String createNameFromParticipants() {
+ if (users.size() >= 1) {
+ List<String> names = new ArrayList<>();
+ for (User user : getUsers(5)) {
+ Contact contact = user.getContact();
+ if (contact != null && !contact.getDisplayName().isEmpty()) {
+ names.add(contact.getDisplayName().split("\\s+")[0]);
+ } else if (user.getName() != null) {
+ names.add(user.getName());
+ }
+ }
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < names.size(); ++i) {
+ builder.append(names.get(i));
+ if (i != names.size() - 1) {
+ builder.append(", ");
+ }
+ }
+ return builder.toString();
+ } else {
+ return null;
+ }
+ }
+
+ public long[] getPgpKeyIds() {
+ List<Long> ids = new ArrayList<>();
+ for (User user : this.users) {
+ if (user.getPgpKeyId() != 0) {
+ ids.add(user.getPgpKeyId());
+ }
+ }
+ ids.add(account.getPgpId());
+ long[] primitiveLongArray = new long[ids.size()];
+ for (int i = 0; i < ids.size(); ++i) {
+ primitiveLongArray[i] = ids.get(i);
+ }
+ return primitiveLongArray;
+ }
+
+ public boolean pgpKeysInUse() {
+ synchronized (users) {
+ for (User user : users) {
+ if (user.getPgpKeyId() != 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean everybodyHasKeys() {
+ synchronized (users) {
+ for (User user : users) {
+ if (user.getPgpKeyId() == 0) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public Jid createJoinJid(String nick) {
+ try {
+ return Jid.fromString(this.conversation.getJid().toBareJid().toString() + "/" + nick);
+ } catch (final InvalidJidException e) {
+ return null;
+ }
+ }
+
+ public Jid getTrueCounterpart(Jid jid) {
+ if (jid.equals(getSelf().getFullJid())) {
+ return account.getJid().toBareJid();
+ }
+ User user = findUserByFullJid(jid);
+ return user == null ? null : user.realJid;
+ }
+
+ public String getPassword() {
+ this.password = conversation.getAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD);
+ if (this.password == null && conversation.getBookmark() != null
+ && conversation.getBookmark().getPassword() != null) {
+ return conversation.getBookmark().getPassword();
+ } else {
+ return this.password;
+ }
+ }
+
+ public void setPassword(String password) {
+ if (conversation.getBookmark() != null) {
+ conversation.getBookmark().setPassword(password);
+ } else {
+ this.password = password;
+ }
+ conversation.setAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD, password);
+ }
+
+ public Conversation getConversation() {
+ return this.conversation;
+ }
+
+ public List<Jid> getMembers() {
+ ArrayList<Jid> members = new ArrayList<>();
+ synchronized (users) {
+ for (User user : users) {
+ if (user.affiliation.ranks(Affiliation.MEMBER) && user.realJid != null) {
+ members.add(user.realJid);
+ }
+ }
+ }
+ return members;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/Presence.java b/src/main/java/de/pixart/messenger/entities/Presence.java
index fe7bac6ee..af467f1f6 100644
--- a/src/main/java/de/pixart/messenger/entities/Presence.java
+++ b/src/main/java/de/pixart/messenger/entities/Presence.java
@@ -6,87 +6,91 @@ import de.pixart.messenger.xml.Element;
public class Presence implements Comparable {
- public enum Status {
- CHAT, ONLINE, AWAY, XA, DND, OFFLINE;
-
- public String toShowString() {
- switch(this) {
- case CHAT: return "chat";
- case AWAY: return "away";
- case XA: return "xa";
- case DND: return "dnd";
- }
- return null;
- }
-
- public static Status fromShowString(String show) {
- if (show == null) {
- return ONLINE;
- } else {
- switch (show.toLowerCase(Locale.US)) {
- case "away":
- return AWAY;
- case "xa":
- return XA;
- case "dnd":
- return DND;
- case "chat":
- return CHAT;
- default:
- return ONLINE;
- }
- }
- }
- }
-
- private final Status status;
- private ServiceDiscoveryResult disco;
- private final String ver;
- private final String hash;
- private final String message;
-
- private Presence(Status status, String ver, String hash, String message) {
- this.status = status;
- this.ver = ver;
- this.hash = hash;
- this.message = message;
- }
-
- public static Presence parse(String show, Element caps, String message) {
- final String hash = caps == null ? null : caps.getAttribute("hash");
- final String ver = caps == null ? null : caps.getAttribute("ver");
- return new Presence(Status.fromShowString(show), ver, hash, message);
- }
-
- public int compareTo(Object other) {
- return this.status.compareTo(((Presence)other).status);
- }
-
- public Status getStatus() {
- return this.status;
- }
-
- public boolean hasCaps() {
- return ver != null && hash != null;
- }
-
- public String getVer() {
- return this.ver;
- }
-
- public String getHash() {
- return this.hash;
- }
-
- public String getMessage() {
- return this.message;
- }
-
- public void setServiceDiscoveryResult(ServiceDiscoveryResult disco) {
- this.disco = disco;
- }
-
- public ServiceDiscoveryResult getServiceDiscoveryResult() {
- return disco;
- }
+ public enum Status {
+ CHAT, ONLINE, AWAY, XA, DND, OFFLINE;
+
+ public String toShowString() {
+ switch (this) {
+ case CHAT:
+ return "chat";
+ case AWAY:
+ return "away";
+ case XA:
+ return "xa";
+ case DND:
+ return "dnd";
+ }
+ return null;
+ }
+
+ public static Status fromShowString(String show) {
+ if (show == null) {
+ return ONLINE;
+ } else {
+ switch (show.toLowerCase(Locale.US)) {
+ case "away":
+ return AWAY;
+ case "xa":
+ return XA;
+ case "dnd":
+ return DND;
+ case "chat":
+ return CHAT;
+ default:
+ return ONLINE;
+ }
+ }
+ }
+ }
+
+ private final Status status;
+ private ServiceDiscoveryResult disco;
+ private final String ver;
+ private final String hash;
+ private final String message;
+
+ private Presence(Status status, String ver, String hash, String message) {
+ this.status = status;
+ this.ver = ver;
+ this.hash = hash;
+ this.message = message;
+ }
+
+ public static Presence parse(String show, Element caps, String message) {
+ final String hash = caps == null ? null : caps.getAttribute("hash");
+ final String ver = caps == null ? null : caps.getAttribute("ver");
+ return new Presence(Status.fromShowString(show), ver, hash, message);
+ }
+
+ public int compareTo(Object other) {
+ return this.status.compareTo(((Presence) other).status);
+ }
+
+ public Status getStatus() {
+ return this.status;
+ }
+
+ public boolean hasCaps() {
+ return ver != null && hash != null;
+ }
+
+ public String getVer() {
+ return this.ver;
+ }
+
+ public String getHash() {
+ return this.hash;
+ }
+
+ public String getMessage() {
+ return this.message;
+ }
+
+ public void setServiceDiscoveryResult(ServiceDiscoveryResult disco) {
+ this.disco = disco;
+ }
+
+ public ServiceDiscoveryResult getServiceDiscoveryResult() {
+ return disco;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/PresenceTemplate.java b/src/main/java/de/pixart/messenger/entities/PresenceTemplate.java
index b8bd167db..ab71a41fd 100644
--- a/src/main/java/de/pixart/messenger/entities/PresenceTemplate.java
+++ b/src/main/java/de/pixart/messenger/entities/PresenceTemplate.java
@@ -6,71 +6,71 @@ import android.database.Cursor;
public class PresenceTemplate extends AbstractEntity {
- public static final String TABELNAME = "presence_templates";
- public static final String LAST_USED = "last_used";
- public static final String MESSAGE = "message";
- public static final String STATUS = "status";
-
- private long lastUsed = 0;
- private String statusMessage;
- private Presence.Status status = Presence.Status.ONLINE;
-
- public PresenceTemplate(Presence.Status status, String statusMessage) {
- this.status = status;
- this.statusMessage = statusMessage;
- this.lastUsed = System.currentTimeMillis();
- this.uuid = java.util.UUID.randomUUID().toString();
- }
-
- private PresenceTemplate() {
-
- }
-
- @Override
- public ContentValues getContentValues() {
- final String show = status.toShowString();
- ContentValues values = new ContentValues();
- values.put(LAST_USED, lastUsed);
- values.put(MESSAGE, statusMessage);
- values.put(STATUS, show == null ? "" : show);
- values.put(UUID, uuid);
- return values;
- }
-
- public static PresenceTemplate fromCursor(Cursor cursor) {
- PresenceTemplate template = new PresenceTemplate();
- template.uuid = cursor.getString(cursor.getColumnIndex(UUID));
- template.lastUsed = cursor.getLong(cursor.getColumnIndex(LAST_USED));
- template.statusMessage = cursor.getString(cursor.getColumnIndex(MESSAGE));
- template.status = Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS)));
- return template;
- }
-
- public Presence.Status getStatus() {
- return status;
- }
-
- public String getStatusMessage() {
- return statusMessage;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- PresenceTemplate template = (PresenceTemplate) o;
-
- if (statusMessage != null ? !statusMessage.equals(template.statusMessage) : template.statusMessage != null)
- return false;
- return status == template.status;
-
- }
-
- @Override
- public int hashCode() {
- int result = statusMessage != null ? statusMessage.hashCode() : 0;
- result = 31 * result + status.hashCode();
- return result;
- }
+ public static final String TABELNAME = "presence_templates";
+ public static final String LAST_USED = "last_used";
+ public static final String MESSAGE = "message";
+ public static final String STATUS = "status";
+
+ private long lastUsed = 0;
+ private String statusMessage;
+ private Presence.Status status = Presence.Status.ONLINE;
+
+ public PresenceTemplate(Presence.Status status, String statusMessage) {
+ this.status = status;
+ this.statusMessage = statusMessage;
+ this.lastUsed = System.currentTimeMillis();
+ this.uuid = java.util.UUID.randomUUID().toString();
+ }
+
+ private PresenceTemplate() {
+
+ }
+
+ @Override
+ public ContentValues getContentValues() {
+ final String show = status.toShowString();
+ ContentValues values = new ContentValues();
+ values.put(LAST_USED, lastUsed);
+ values.put(MESSAGE, statusMessage);
+ values.put(STATUS, show == null ? "" : show);
+ values.put(UUID, uuid);
+ return values;
+ }
+
+ public static PresenceTemplate fromCursor(Cursor cursor) {
+ PresenceTemplate template = new PresenceTemplate();
+ template.uuid = cursor.getString(cursor.getColumnIndex(UUID));
+ template.lastUsed = cursor.getLong(cursor.getColumnIndex(LAST_USED));
+ template.statusMessage = cursor.getString(cursor.getColumnIndex(MESSAGE));
+ template.status = Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS)));
+ return template;
+ }
+
+ public Presence.Status getStatus() {
+ return status;
+ }
+
+ public String getStatusMessage() {
+ return statusMessage;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ PresenceTemplate template = (PresenceTemplate) o;
+
+ if (statusMessage != null ? !statusMessage.equals(template.statusMessage) : template.statusMessage != null)
+ return false;
+ return status == template.status;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = statusMessage != null ? statusMessage.hashCode() : 0;
+ result = 31 * result + status.hashCode();
+ return result;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/Presences.java b/src/main/java/de/pixart/messenger/entities/Presences.java
index 98ea58fcf..6afd973fc 100644
--- a/src/main/java/de/pixart/messenger/entities/Presences.java
+++ b/src/main/java/de/pixart/messenger/entities/Presences.java
@@ -9,122 +9,122 @@ import java.util.List;
import java.util.Map;
public class Presences {
- private final Hashtable<String, Presence> presences = new Hashtable<>();
+ private final Hashtable<String, Presence> presences = new Hashtable<>();
- public Hashtable<String, Presence> getPresences() {
- return this.presences;
- }
+ public Hashtable<String, Presence> getPresences() {
+ return this.presences;
+ }
- public void updatePresence(String resource, Presence presence) {
- synchronized (this.presences) {
- this.presences.put(resource, presence);
- }
- }
+ public void updatePresence(String resource, Presence presence) {
+ synchronized (this.presences) {
+ this.presences.put(resource, presence);
+ }
+ }
- public void removePresence(String resource) {
- synchronized (this.presences) {
- this.presences.remove(resource);
- }
- }
+ public void removePresence(String resource) {
+ synchronized (this.presences) {
+ this.presences.remove(resource);
+ }
+ }
- public void clearPresences() {
- synchronized (this.presences) {
- this.presences.clear();
- }
- }
+ public void clearPresences() {
+ synchronized (this.presences) {
+ this.presences.clear();
+ }
+ }
- public Presence.Status getShownStatus() {
- Presence.Status status = Presence.Status.OFFLINE;
- synchronized (this.presences) {
- for(Presence p : presences.values()) {
- if (p.getStatus() == Presence.Status.DND) {
- return p.getStatus();
- } else if (p.getStatus().compareTo(status) < 0){
- status = p.getStatus();
- }
- }
- }
- return status;
- }
+ public Presence.Status getShownStatus() {
+ Presence.Status status = Presence.Status.OFFLINE;
+ synchronized (this.presences) {
+ for (Presence p : presences.values()) {
+ if (p.getStatus() == Presence.Status.DND) {
+ return p.getStatus();
+ } else if (p.getStatus().compareTo(status) < 0) {
+ status = p.getStatus();
+ }
+ }
+ }
+ return status;
+ }
- public int size() {
- synchronized (this.presences) {
- return presences.size();
- }
- }
+ public int size() {
+ synchronized (this.presences) {
+ return presences.size();
+ }
+ }
- public String[] toResourceArray() {
- synchronized (this.presences) {
- final String[] presencesArray = new String[presences.size()];
- presences.keySet().toArray(presencesArray);
- return presencesArray;
- }
- }
+ public String[] toResourceArray() {
+ synchronized (this.presences) {
+ final String[] presencesArray = new String[presences.size()];
+ presences.keySet().toArray(presencesArray);
+ return presencesArray;
+ }
+ }
- public List<PresenceTemplate> asTemplates() {
- synchronized (this.presences) {
- ArrayList<PresenceTemplate> templates = new ArrayList<>(presences.size());
- for(Presence p : presences.values()) {
- if (p.getMessage() != null && !p.getMessage().trim().isEmpty()) {
- templates.add(new PresenceTemplate(p.getStatus(), p.getMessage()));
- }
- }
- return templates;
- }
- }
+ public List<PresenceTemplate> asTemplates() {
+ synchronized (this.presences) {
+ ArrayList<PresenceTemplate> templates = new ArrayList<>(presences.size());
+ for (Presence p : presences.values()) {
+ if (p.getMessage() != null && !p.getMessage().trim().isEmpty()) {
+ templates.add(new PresenceTemplate(p.getStatus(), p.getMessage()));
+ }
+ }
+ return templates;
+ }
+ }
- public boolean has(String presence) {
- synchronized (this.presences) {
- return presences.containsKey(presence);
- }
- }
+ public boolean has(String presence) {
+ synchronized (this.presences) {
+ return presences.containsKey(presence);
+ }
+ }
- public List<String> getStatusMessages() {
- ArrayList<String> messages = new ArrayList<>();
- synchronized (this.presences) {
- for(Presence presence : this.presences.values()) {
- String message = presence.getMessage() == null ? null : presence.getMessage().trim();
- if (message != null && !message.isEmpty() && !messages.contains(message)) {
- messages.add(message);
- }
- }
- }
- return messages;
- }
+ public List<String> getStatusMessages() {
+ ArrayList<String> messages = new ArrayList<>();
+ synchronized (this.presences) {
+ for (Presence presence : this.presences.values()) {
+ String message = presence.getMessage() == null ? null : presence.getMessage().trim();
+ if (message != null && !message.isEmpty() && !messages.contains(message)) {
+ messages.add(message);
+ }
+ }
+ }
+ return messages;
+ }
- public boolean allOrNonSupport(String namespace) {
- synchronized (this.presences) {
- for(Presence presence : this.presences.values()) {
- ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
- if (disco == null || !disco.getFeatures().contains(namespace)) {
- return false;
- }
- }
- }
- return true;
- }
+ public boolean allOrNonSupport(String namespace) {
+ synchronized (this.presences) {
+ for (Presence presence : this.presences.values()) {
+ ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
+ if (disco == null || !disco.getFeatures().contains(namespace)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
- public Pair<Map<String, String>,Map<String,String>> toTypeAndNameMap() {
- Map<String,String> typeMap = new HashMap<>();
- Map<String,String> nameMap = new HashMap<>();
- synchronized (this.presences) {
- for(Map.Entry<String,Presence> presenceEntry : this.presences.entrySet()) {
- String resource = presenceEntry.getKey();
- Presence presence = presenceEntry.getValue();
- ServiceDiscoveryResult serviceDiscoveryResult = presence == null ? null : presence.getServiceDiscoveryResult();
- if (serviceDiscoveryResult != null && serviceDiscoveryResult.getIdentities().size() > 0) {
- ServiceDiscoveryResult.Identity identity = serviceDiscoveryResult.getIdentities().get(0);
- String type = identity.getType();
- String name = identity.getName();
- if (type != null) {
- typeMap.put(resource,type);
- }
- if (name != null) {
- nameMap.put(resource, name);
- }
- }
- }
- }
- return new Pair(typeMap,nameMap);
- }
+ public Pair<Map<String, String>, Map<String, String>> toTypeAndNameMap() {
+ Map<String, String> typeMap = new HashMap<>();
+ Map<String, String> nameMap = new HashMap<>();
+ synchronized (this.presences) {
+ for (Map.Entry<String, Presence> presenceEntry : this.presences.entrySet()) {
+ String resource = presenceEntry.getKey();
+ Presence presence = presenceEntry.getValue();
+ ServiceDiscoveryResult serviceDiscoveryResult = presence == null ? null : presence.getServiceDiscoveryResult();
+ if (serviceDiscoveryResult != null && serviceDiscoveryResult.getIdentities().size() > 0) {
+ ServiceDiscoveryResult.Identity identity = serviceDiscoveryResult.getIdentities().get(0);
+ String type = identity.getType();
+ String name = identity.getName();
+ if (type != null) {
+ typeMap.put(resource, type);
+ }
+ if (name != null) {
+ nameMap.put(resource, name);
+ }
+ }
+ }
+ }
+ return new Pair(typeMap, nameMap);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/Roster.java b/src/main/java/de/pixart/messenger/entities/Roster.java
index e267142bc..bdb7265f3 100644
--- a/src/main/java/de/pixart/messenger/entities/Roster.java
+++ b/src/main/java/de/pixart/messenger/entities/Roster.java
@@ -8,89 +8,89 @@ import java.util.List;
import de.pixart.messenger.xmpp.jid.Jid;
public class Roster {
- final Account account;
- final HashMap<Jid, Contact> contacts = new HashMap<>();
- private String version = null;
+ final Account account;
+ final HashMap<Jid, Contact> contacts = new HashMap<>();
+ private String version = null;
- public Roster(Account account) {
- this.account = account;
- }
+ public Roster(Account account) {
+ this.account = account;
+ }
- public Contact getContactFromRoster(Jid jid) {
- if (jid == null) {
- return null;
- }
- synchronized (this.contacts) {
- Contact contact = contacts.get(jid.toBareJid());
- if (contact != null && contact.showInRoster()) {
- return contact;
- } else {
- return null;
- }
- }
- }
+ public Contact getContactFromRoster(Jid jid) {
+ if (jid == null) {
+ return null;
+ }
+ synchronized (this.contacts) {
+ Contact contact = contacts.get(jid.toBareJid());
+ if (contact != null && contact.showInRoster()) {
+ return contact;
+ } else {
+ return null;
+ }
+ }
+ }
- public Contact getContact(final Jid jid) {
- synchronized (this.contacts) {
- if (!contacts.containsKey(jid.toBareJid())) {
- Contact contact = new Contact(jid.toBareJid());
- contact.setAccount(account);
- contacts.put(contact.getJid().toBareJid(), contact);
- return contact;
- }
- return contacts.get(jid.toBareJid());
- }
- }
+ public Contact getContact(final Jid jid) {
+ synchronized (this.contacts) {
+ if (!contacts.containsKey(jid.toBareJid())) {
+ Contact contact = new Contact(jid.toBareJid());
+ contact.setAccount(account);
+ contacts.put(contact.getJid().toBareJid(), contact);
+ return contact;
+ }
+ return contacts.get(jid.toBareJid());
+ }
+ }
- public void clearPresences() {
- for (Contact contact : getContacts()) {
- contact.clearPresences();
- }
- }
+ public void clearPresences() {
+ for (Contact contact : getContacts()) {
+ contact.clearPresences();
+ }
+ }
- public void markAllAsNotInRoster() {
- for (Contact contact : getContacts()) {
- contact.resetOption(Contact.Options.IN_ROSTER);
- }
- }
+ public void markAllAsNotInRoster() {
+ for (Contact contact : getContacts()) {
+ contact.resetOption(Contact.Options.IN_ROSTER);
+ }
+ }
- public List<Contact> getWithSystemAccounts() {
- List<Contact> with = getContacts();
- for(Iterator<Contact> iterator = with.iterator(); iterator.hasNext();) {
- Contact contact = iterator.next();
- if (contact.getSystemAccount() == null) {
- iterator.remove();
- }
- }
- return with;
- }
+ public List<Contact> getWithSystemAccounts() {
+ List<Contact> with = getContacts();
+ for (Iterator<Contact> iterator = with.iterator(); iterator.hasNext(); ) {
+ Contact contact = iterator.next();
+ if (contact.getSystemAccount() == null) {
+ iterator.remove();
+ }
+ }
+ return with;
+ }
- public List<Contact> getContacts() {
- synchronized (this.contacts) {
- return new ArrayList<>(this.contacts.values());
- }
- }
+ public List<Contact> getContacts() {
+ synchronized (this.contacts) {
+ return new ArrayList<>(this.contacts.values());
+ }
+ }
- public void initContact(final Contact contact) {
- if (contact == null) {
- return;
- }
- contact.setAccount(account);
- contact.setOption(Contact.Options.IN_ROSTER);
- synchronized (this.contacts) {
- contacts.put(contact.getJid().toBareJid(), contact);
- }
- }
+ public void initContact(final Contact contact) {
+ if (contact == null) {
+ return;
+ }
+ contact.setAccount(account);
+ contact.setOption(Contact.Options.IN_ROSTER);
+ synchronized (this.contacts) {
+ contacts.put(contact.getJid().toBareJid(), contact);
+ }
+ }
- public void setVersion(String version) {
- this.version = version;
- }
+ public void setVersion(String version) {
+ this.version = version;
+ }
- public String getVersion() {
- return this.version;
- }
+ public String getVersion() {
+ return this.version;
+ }
- public Account getAccount() {
- return this.account;
- }
+ public Account getAccount() {
+ return this.account;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/ServiceDiscoveryResult.java b/src/main/java/de/pixart/messenger/entities/ServiceDiscoveryResult.java
index cac20ba90..b0c3ebb77 100644
--- a/src/main/java/de/pixart/messenger/entities/ServiceDiscoveryResult.java
+++ b/src/main/java/de/pixart/messenger/entities/ServiceDiscoveryResult.java
@@ -22,328 +22,328 @@ import de.pixart.messenger.xmpp.forms.Field;
import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class ServiceDiscoveryResult {
- public static final String TABLENAME = "discovery_results";
- public static final String HASH = "hash";
- public static final String VER = "ver";
- public static final String RESULT = "result";
-
- protected static String blankNull(String s) {
- return s == null ? "" : s;
- }
-
- public static class Identity implements Comparable {
- protected final String category;
- protected final String type;
- protected final String lang;
- protected final String name;
-
- public Identity(final String category, final String type, final String lang, final String name) {
- this.category = category;
- this.type = type;
- this.lang = lang;
- this.name = name;
- }
-
- public Identity(final Element el) {
- this(
- el.getAttribute("category"),
- el.getAttribute("type"),
- el.getAttribute("xml:lang"),
- el.getAttribute("name")
- );
- }
-
- public Identity(final JSONObject o) {
-
- this(
- o.optString("category", null),
- o.optString("type", null),
- o.optString("lang", null),
- o.optString("name", null)
- );
- }
-
- public String getCategory() {
- return this.category;
- }
-
- public String getType() {
- return this.type;
- }
-
- public String getLang() {
- return this.lang;
- }
-
- public String getName() {
- return this.name;
- }
-
- public int compareTo(Object other) {
- Identity o = (Identity)other;
- int r = blankNull(this.getCategory()).compareTo(blankNull(o.getCategory()));
- if(r == 0) {
- r = blankNull(this.getType()).compareTo(blankNull(o.getType()));
- }
- if(r == 0) {
- r = blankNull(this.getLang()).compareTo(blankNull(o.getLang()));
- }
- if(r == 0) {
- r = blankNull(this.getName()).compareTo(blankNull(o.getName()));
- }
-
- return r;
- }
-
- public JSONObject toJSON() {
- try {
- JSONObject o = new JSONObject();
- o.put("category", this.getCategory());
- o.put("type", this.getType());
- o.put("lang", this.getLang());
- o.put("name", this.getName());
- return o;
- } catch(JSONException e) {
- return null;
- }
- }
- }
-
- protected final String hash;
- protected final byte[] ver;
- protected final List<Identity> identities;
- protected final List<String> features;
- protected final List<Data> forms;
-
- public ServiceDiscoveryResult(final IqPacket packet) {
- this.identities = new ArrayList<>();
- this.features = new ArrayList<>();
- this.forms = new ArrayList<>();
- this.hash = "sha-1"; // We only support sha-1 for now
-
- final List<Element> elements = packet.query().getChildren();
-
- for (final Element element : elements) {
- if (element.getName().equals("identity")) {
- Identity id = new Identity(element);
- if (id.getType() != null && id.getCategory() != null) {
- identities.add(id);
- }
- } else if (element.getName().equals("feature")) {
- if (element.getAttribute("var") != null) {
- features.add(element.getAttribute("var"));
- }
- } else if (element.getName().equals("x") && "jabber:x:data".equals(element.getAttribute("xmlns"))) {
- forms.add(Data.parse(element));
- }
- }
- this.ver = this.mkCapHash();
- }
-
- public ServiceDiscoveryResult(String hash, byte[] ver, JSONObject o) throws JSONException {
- this.identities = new ArrayList<>();
- this.features = new ArrayList<>();
- this.forms = new ArrayList<>();
- this.hash = hash;
- this.ver = ver;
-
- JSONArray identities = o.optJSONArray("identities");
- if (identities != null) {
- for (int i = 0; i < identities.length(); i++) {
- this.identities.add(new Identity(identities.getJSONObject(i)));
- }
- }
- JSONArray features = o.optJSONArray("features");
- if (features != null) {
- for (int i = 0; i < features.length(); i++) {
- this.features.add(features.getString(i));
- }
- }
- JSONArray forms = o.optJSONArray("forms");
- if (forms != null) {
- for(int i = 0; i < forms.length(); i++) {
- this.forms.add(createFormFromJSONObject(forms.getJSONObject(i)));
- }
- }
- }
-
- private static Data createFormFromJSONObject(JSONObject o) {
- Data data = new Data();
- JSONArray names = o.names();
- for(int i = 0; i < names.length(); ++i) {
- try {
- String name = names.getString(i);
- JSONArray jsonValues = o.getJSONArray(name);
- ArrayList<String> values = new ArrayList<>(jsonValues.length());
- for(int j = 0; j < jsonValues.length(); ++j) {
- values.add(jsonValues.getString(j));
- }
- data.put(name, values);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- return data;
- }
-
- private static JSONObject createJSONFromForm(Data data) {
- JSONObject object = new JSONObject();
- for(Field field : data.getFields()) {
- try {
- JSONArray jsonValues = new JSONArray();
- for(String value : field.getValues()) {
- jsonValues.put(value);
- }
- object.put(field.getFieldName(), jsonValues);
- } catch(Exception e) {
- e.printStackTrace();
- }
- }
- try {
- JSONArray jsonValues = new JSONArray();
- jsonValues.put(data.getFormType());
- object.put(Data.FORM_TYPE, jsonValues);
- } catch(Exception e) {
- e.printStackTrace();
- }
- return object;
- }
-
- public String getVer() {
- return new String(Base64.encode(this.ver, Base64.DEFAULT)).trim();
- }
-
- public ServiceDiscoveryResult(Cursor cursor) throws JSONException {
- this(
- cursor.getString(cursor.getColumnIndex(HASH)),
- Base64.decode(cursor.getString(cursor.getColumnIndex(VER)), Base64.DEFAULT),
- new JSONObject(cursor.getString(cursor.getColumnIndex(RESULT)))
- );
- }
-
- public List<Identity> getIdentities() {
- return this.identities;
- }
-
- public List<String> getFeatures() {
- return this.features;
- }
-
- public boolean hasIdentity(String category, String type) {
- for(Identity id : this.getIdentities()) {
- if((category == null || id.getCategory().equals(category)) &&
- (type == null || id.getType().equals(type))) {
- return true;
- }
- }
-
- return false;
- }
-
- public String getExtendedDiscoInformation(String formType, String name) {
- for(Data form : this.forms) {
- if (formType.equals(form.getFormType())) {
- for(Field field: form.getFields()) {
- if (name.equals(field.getFieldName())) {
- return field.getValue();
- }
- }
- }
- }
- return null;
- }
-
- protected byte[] mkCapHash() {
- StringBuilder s = new StringBuilder();
-
- List<Identity> identities = this.getIdentities();
- Collections.sort(identities);
-
- for(Identity id : identities) {
- s.append(
- blankNull(id.getCategory()) + "/" +
- blankNull(id.getType()) + "/" +
- blankNull(id.getLang()) + "/" +
- blankNull(id.getName()) + "<"
- );
- }
-
- List<String> features = this.getFeatures();
- Collections.sort(features);
-
- for (String feature : features) {
- s.append(feature + "<");
- }
-
- Collections.sort(forms, new Comparator<Data>() {
- @Override
- public int compare(Data lhs, Data rhs) {
- return lhs.getFormType().compareTo(rhs.getFormType());
- }
- });
-
- for(Data form : forms) {
- s.append(form.getFormType() + "<");
- List<Field> fields = form.getFields();
- Collections.sort(fields, new Comparator<Field>() {
- @Override
- public int compare(Field lhs, Field rhs) {
- return lhs.getFieldName().compareTo(rhs.getFieldName());
- }
- });
- for(Field field : fields) {
- s.append(field.getFieldName()+"<");
- List<String> values = field.getValues();
- Collections.sort(values);
- for(String value : values) {
- s.append(value+"<");
- }
- }
- }
-
- MessageDigest md;
- try {
- md = MessageDigest.getInstance("SHA-1");
- } catch (NoSuchAlgorithmException e) {
- return null;
- }
-
- try {
- return md.digest(s.toString().getBytes("UTF-8"));
- } catch(UnsupportedEncodingException e) {
- return null;
- }
- }
-
- public JSONObject toJSON() {
- try {
- JSONObject o = new JSONObject();
-
- JSONArray ids = new JSONArray();
- for(Identity id : this.getIdentities()) {
- ids.put(id.toJSON());
- }
- o.put("identities", ids);
-
- o.put("features", new JSONArray(this.getFeatures()));
-
- JSONArray forms = new JSONArray();
- for(Data data : this.forms) {
- forms.put(createJSONFromForm(data));
- }
- o.put("forms", forms);
-
- return o;
- } catch(JSONException e) {
- return null;
- }
- }
-
- public ContentValues getContentValues() {
- final ContentValues values = new ContentValues();
- values.put(HASH, this.hash);
- values.put(VER, getVer());
- values.put(RESULT, this.toJSON().toString());
- return values;
- }
+ public static final String TABLENAME = "discovery_results";
+ public static final String HASH = "hash";
+ public static final String VER = "ver";
+ public static final String RESULT = "result";
+
+ protected static String blankNull(String s) {
+ return s == null ? "" : s;
+ }
+
+ public static class Identity implements Comparable {
+ protected final String category;
+ protected final String type;
+ protected final String lang;
+ protected final String name;
+
+ public Identity(final String category, final String type, final String lang, final String name) {
+ this.category = category;
+ this.type = type;
+ this.lang = lang;
+ this.name = name;
+ }
+
+ public Identity(final Element el) {
+ this(
+ el.getAttribute("category"),
+ el.getAttribute("type"),
+ el.getAttribute("xml:lang"),
+ el.getAttribute("name")
+ );
+ }
+
+ public Identity(final JSONObject o) {
+
+ this(
+ o.optString("category", null),
+ o.optString("type", null),
+ o.optString("lang", null),
+ o.optString("name", null)
+ );
+ }
+
+ public String getCategory() {
+ return this.category;
+ }
+
+ public String getType() {
+ return this.type;
+ }
+
+ public String getLang() {
+ return this.lang;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public int compareTo(Object other) {
+ Identity o = (Identity) other;
+ int r = blankNull(this.getCategory()).compareTo(blankNull(o.getCategory()));
+ if (r == 0) {
+ r = blankNull(this.getType()).compareTo(blankNull(o.getType()));
+ }
+ if (r == 0) {
+ r = blankNull(this.getLang()).compareTo(blankNull(o.getLang()));
+ }
+ if (r == 0) {
+ r = blankNull(this.getName()).compareTo(blankNull(o.getName()));
+ }
+
+ return r;
+ }
+
+ public JSONObject toJSON() {
+ try {
+ JSONObject o = new JSONObject();
+ o.put("category", this.getCategory());
+ o.put("type", this.getType());
+ o.put("lang", this.getLang());
+ o.put("name", this.getName());
+ return o;
+ } catch (JSONException e) {
+ return null;
+ }
+ }
+ }
+
+ protected final String hash;
+ protected final byte[] ver;
+ protected final List<Identity> identities;
+ protected final List<String> features;
+ protected final List<Data> forms;
+
+ public ServiceDiscoveryResult(final IqPacket packet) {
+ this.identities = new ArrayList<>();
+ this.features = new ArrayList<>();
+ this.forms = new ArrayList<>();
+ this.hash = "sha-1"; // We only support sha-1 for now
+
+ final List<Element> elements = packet.query().getChildren();
+
+ for (final Element element : elements) {
+ if (element.getName().equals("identity")) {
+ Identity id = new Identity(element);
+ if (id.getType() != null && id.getCategory() != null) {
+ identities.add(id);
+ }
+ } else if (element.getName().equals("feature")) {
+ if (element.getAttribute("var") != null) {
+ features.add(element.getAttribute("var"));
+ }
+ } else if (element.getName().equals("x") && "jabber:x:data".equals(element.getAttribute("xmlns"))) {
+ forms.add(Data.parse(element));
+ }
+ }
+ this.ver = this.mkCapHash();
+ }
+
+ public ServiceDiscoveryResult(String hash, byte[] ver, JSONObject o) throws JSONException {
+ this.identities = new ArrayList<>();
+ this.features = new ArrayList<>();
+ this.forms = new ArrayList<>();
+ this.hash = hash;
+ this.ver = ver;
+
+ JSONArray identities = o.optJSONArray("identities");
+ if (identities != null) {
+ for (int i = 0; i < identities.length(); i++) {
+ this.identities.add(new Identity(identities.getJSONObject(i)));
+ }
+ }
+ JSONArray features = o.optJSONArray("features");
+ if (features != null) {
+ for (int i = 0; i < features.length(); i++) {
+ this.features.add(features.getString(i));
+ }
+ }
+ JSONArray forms = o.optJSONArray("forms");
+ if (forms != null) {
+ for (int i = 0; i < forms.length(); i++) {
+ this.forms.add(createFormFromJSONObject(forms.getJSONObject(i)));
+ }
+ }
+ }
+
+ private static Data createFormFromJSONObject(JSONObject o) {
+ Data data = new Data();
+ JSONArray names = o.names();
+ for (int i = 0; i < names.length(); ++i) {
+ try {
+ String name = names.getString(i);
+ JSONArray jsonValues = o.getJSONArray(name);
+ ArrayList<String> values = new ArrayList<>(jsonValues.length());
+ for (int j = 0; j < jsonValues.length(); ++j) {
+ values.add(jsonValues.getString(j));
+ }
+ data.put(name, values);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return data;
+ }
+
+ private static JSONObject createJSONFromForm(Data data) {
+ JSONObject object = new JSONObject();
+ for (Field field : data.getFields()) {
+ try {
+ JSONArray jsonValues = new JSONArray();
+ for (String value : field.getValues()) {
+ jsonValues.put(value);
+ }
+ object.put(field.getFieldName(), jsonValues);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ try {
+ JSONArray jsonValues = new JSONArray();
+ jsonValues.put(data.getFormType());
+ object.put(Data.FORM_TYPE, jsonValues);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return object;
+ }
+
+ public String getVer() {
+ return new String(Base64.encode(this.ver, Base64.DEFAULT)).trim();
+ }
+
+ public ServiceDiscoveryResult(Cursor cursor) throws JSONException {
+ this(
+ cursor.getString(cursor.getColumnIndex(HASH)),
+ Base64.decode(cursor.getString(cursor.getColumnIndex(VER)), Base64.DEFAULT),
+ new JSONObject(cursor.getString(cursor.getColumnIndex(RESULT)))
+ );
+ }
+
+ public List<Identity> getIdentities() {
+ return this.identities;
+ }
+
+ public List<String> getFeatures() {
+ return this.features;
+ }
+
+ public boolean hasIdentity(String category, String type) {
+ for (Identity id : this.getIdentities()) {
+ if ((category == null || id.getCategory().equals(category)) &&
+ (type == null || id.getType().equals(type))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public String getExtendedDiscoInformation(String formType, String name) {
+ for (Data form : this.forms) {
+ if (formType.equals(form.getFormType())) {
+ for (Field field : form.getFields()) {
+ if (name.equals(field.getFieldName())) {
+ return field.getValue();
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ protected byte[] mkCapHash() {
+ StringBuilder s = new StringBuilder();
+
+ List<Identity> identities = this.getIdentities();
+ Collections.sort(identities);
+
+ for (Identity id : identities) {
+ s.append(
+ blankNull(id.getCategory()) + "/" +
+ blankNull(id.getType()) + "/" +
+ blankNull(id.getLang()) + "/" +
+ blankNull(id.getName()) + "<"
+ );
+ }
+
+ List<String> features = this.getFeatures();
+ Collections.sort(features);
+
+ for (String feature : features) {
+ s.append(feature + "<");
+ }
+
+ Collections.sort(forms, new Comparator<Data>() {
+ @Override
+ public int compare(Data lhs, Data rhs) {
+ return lhs.getFormType().compareTo(rhs.getFormType());
+ }
+ });
+
+ for (Data form : forms) {
+ s.append(form.getFormType() + "<");
+ List<Field> fields = form.getFields();
+ Collections.sort(fields, new Comparator<Field>() {
+ @Override
+ public int compare(Field lhs, Field rhs) {
+ return lhs.getFieldName().compareTo(rhs.getFieldName());
+ }
+ });
+ for (Field field : fields) {
+ s.append(field.getFieldName() + "<");
+ List<String> values = field.getValues();
+ Collections.sort(values);
+ for (String value : values) {
+ s.append(value + "<");
+ }
+ }
+ }
+
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ }
+
+ try {
+ return md.digest(s.toString().getBytes("UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ return null;
+ }
+ }
+
+ public JSONObject toJSON() {
+ try {
+ JSONObject o = new JSONObject();
+
+ JSONArray ids = new JSONArray();
+ for (Identity id : this.getIdentities()) {
+ ids.put(id.toJSON());
+ }
+ o.put("identities", ids);
+
+ o.put("features", new JSONArray(this.getFeatures()));
+
+ JSONArray forms = new JSONArray();
+ for (Data data : this.forms) {
+ forms.put(createJSONFromForm(data));
+ }
+ o.put("forms", forms);
+
+ return o;
+ } catch (JSONException e) {
+ return null;
+ }
+ }
+
+ public ContentValues getContentValues() {
+ final ContentValues values = new ContentValues();
+ values.put(HASH, this.hash);
+ values.put(VER, getVer());
+ values.put(RESULT, this.toJSON().toString());
+ return values;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/Transferable.java b/src/main/java/de/pixart/messenger/entities/Transferable.java
index 14132ac38..c07ec750d 100644
--- a/src/main/java/de/pixart/messenger/entities/Transferable.java
+++ b/src/main/java/de/pixart/messenger/entities/Transferable.java
@@ -54,7 +54,7 @@ public interface Transferable {
//compressed
"zip",
"rar"
- );
+ );
int STATUS_UNKNOWN = 0x200;
int STATUS_CHECKING = 0x201;
diff --git a/src/main/java/de/pixart/messenger/entities/TransferablePlaceholder.java b/src/main/java/de/pixart/messenger/entities/TransferablePlaceholder.java
index 59f1dc846..e22984f5a 100644
--- a/src/main/java/de/pixart/messenger/entities/TransferablePlaceholder.java
+++ b/src/main/java/de/pixart/messenger/entities/TransferablePlaceholder.java
@@ -2,33 +2,34 @@ package de.pixart.messenger.entities;
public class TransferablePlaceholder implements Transferable {
- private int status;
-
- public TransferablePlaceholder(int status) {
- this.status = status;
- }
- @Override
- public boolean start() {
- return false;
- }
-
- @Override
- public int getStatus() {
- return status;
- }
-
- @Override
- public long getFileSize() {
- return 0;
- }
-
- @Override
- public int getProgress() {
- return 0;
- }
-
- @Override
- public void cancel() {
-
- }
+ private int status;
+
+ public TransferablePlaceholder(int status) {
+ this.status = status;
+ }
+
+ @Override
+ public boolean start() {
+ return false;
+ }
+
+ @Override
+ public int getStatus() {
+ return status;
+ }
+
+ @Override
+ public long getFileSize() {
+ return 0;
+ }
+
+ @Override
+ public int getProgress() {
+ return 0;
+ }
+
+ @Override
+ public void cancel() {
+
+ }
}
diff --git a/src/main/java/de/pixart/messenger/generator/AbstractGenerator.java b/src/main/java/de/pixart/messenger/generator/AbstractGenerator.java
index 829b033fe..33ce81859 100644
--- a/src/main/java/de/pixart/messenger/generator/AbstractGenerator.java
+++ b/src/main/java/de/pixart/messenger/generator/AbstractGenerator.java
@@ -20,105 +20,105 @@ import de.pixart.messenger.utils.PhoneHelper;
import de.pixart.messenger.xmpp.jingle.stanzas.Content;
public abstract class AbstractGenerator {
- private final String[] FEATURES = {
- "urn:xmpp:jingle:1",
- Content.Version.FT_3.getNamespace(),
- Content.Version.FT_4.getNamespace(),
- "urn:xmpp:jingle:transports:s5b:1",
- "urn:xmpp:jingle:transports:ibb:1",
- "http://jabber.org/protocol/muc",
- "jabber:x:conference",
- "http://jabber.org/protocol/caps",
- "http://jabber.org/protocol/disco#info",
- "urn:xmpp:avatar:metadata+notify",
- "http://jabber.org/protocol/nick+notify",
- "urn:xmpp:ping",
- "jabber:iq:version",
- "http://jabber.org/protocol/chatstates"
- };
- private final String[] MESSAGE_CONFIRMATION_FEATURES = {
- "urn:xmpp:chat-markers:0",
- "urn:xmpp:receipts"
- };
- private final String[] MESSAGE_CORRECTION_FEATURES = {
- "urn:xmpp:message-correct:0"
- };
- private final String[] PRIVACY_SENSITIVE = {
- "urn:xmpp:time" //XEP-0202: Entity Time leaks time zone
- };
- private final String[] OTR = {
- "urn:xmpp:otr:0"
- };
- private String mVersion = null;
+ private final String[] FEATURES = {
+ "urn:xmpp:jingle:1",
+ Content.Version.FT_3.getNamespace(),
+ Content.Version.FT_4.getNamespace(),
+ "urn:xmpp:jingle:transports:s5b:1",
+ "urn:xmpp:jingle:transports:ibb:1",
+ "http://jabber.org/protocol/muc",
+ "jabber:x:conference",
+ "http://jabber.org/protocol/caps",
+ "http://jabber.org/protocol/disco#info",
+ "urn:xmpp:avatar:metadata+notify",
+ "http://jabber.org/protocol/nick+notify",
+ "urn:xmpp:ping",
+ "jabber:iq:version",
+ "http://jabber.org/protocol/chatstates"
+ };
+ private final String[] MESSAGE_CONFIRMATION_FEATURES = {
+ "urn:xmpp:chat-markers:0",
+ "urn:xmpp:receipts"
+ };
+ private final String[] MESSAGE_CORRECTION_FEATURES = {
+ "urn:xmpp:message-correct:0"
+ };
+ private final String[] PRIVACY_SENSITIVE = {
+ "urn:xmpp:time" //XEP-0202: Entity Time leaks time zone
+ };
+ private final String[] OTR = {
+ "urn:xmpp:otr:0"
+ };
+ private String mVersion = null;
- private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
+ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
- protected XmppConnectionService mXmppConnectionService;
+ protected XmppConnectionService mXmppConnectionService;
- protected AbstractGenerator(XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
+ protected AbstractGenerator(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
- protected String getIdentityVersion() {
- if (mVersion == null) {
- this.mVersion = PhoneHelper.getVersionName(mXmppConnectionService);
- }
- return this.mVersion;
- }
+ protected String getIdentityVersion() {
+ if (mVersion == null) {
+ this.mVersion = PhoneHelper.getVersionName(mXmppConnectionService);
+ }
+ return this.mVersion;
+ }
- public String getIdentityName() {
- return mXmppConnectionService.getString(R.string.app_name) + " " + getIdentityVersion();
- }
+ public String getIdentityName() {
+ return mXmppConnectionService.getString(R.string.app_name) + " " + getIdentityVersion();
+ }
- public String getIdentityType() {
- if ("chromium".equals(android.os.Build.BRAND)) {
- return "pc";
- } else {
- return mXmppConnectionService.getString(R.string.default_resource).toLowerCase();
- }
- }
+ public String getIdentityType() {
+ if ("chromium".equals(android.os.Build.BRAND)) {
+ return "pc";
+ } else {
+ return mXmppConnectionService.getString(R.string.default_resource).toLowerCase();
+ }
+ }
- public String getCapHash() {
- StringBuilder s = new StringBuilder();
- s.append("client/" + getIdentityType() + "//" + getIdentityName() + "<");
- MessageDigest md;
- try {
- md = MessageDigest.getInstance("SHA-1");
- } catch (NoSuchAlgorithmException e) {
- return null;
- }
+ public String getCapHash() {
+ StringBuilder s = new StringBuilder();
+ s.append("client/" + getIdentityType() + "//" + getIdentityName() + "<");
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ }
- for (String feature : getFeatures()) {
- s.append(feature + "<");
- }
- byte[] sha1 = md.digest(s.toString().getBytes());
- return new String(Base64.encode(sha1, Base64.DEFAULT)).trim();
- }
+ for (String feature : getFeatures()) {
+ s.append(feature + "<");
+ }
+ byte[] sha1 = md.digest(s.toString().getBytes());
+ return new String(Base64.encode(sha1, Base64.DEFAULT)).trim();
+ }
- public static String getTimestamp(long time) {
- DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
- return DATE_FORMAT.format(time);
- }
+ public static String getTimestamp(long time) {
+ DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return DATE_FORMAT.format(time);
+ }
- public List<String> getFeatures() {
- ArrayList<String> features = new ArrayList<>();
- features.addAll(Arrays.asList(FEATURES));
- if (mXmppConnectionService.confirmMessages()) {
- features.addAll(Arrays.asList(MESSAGE_CONFIRMATION_FEATURES));
- }
- if (mXmppConnectionService.allowMessageCorrection()) {
- features.addAll(Arrays.asList(MESSAGE_CORRECTION_FEATURES));
- }
- if (Config.supportOmemo()) {
- features.add(AxolotlService.PEP_DEVICE_LIST_NOTIFY);
- }
- if (!mXmppConnectionService.useTorToConnect()) {
- features.addAll(Arrays.asList(PRIVACY_SENSITIVE));
- }
- if (Config.supportOtr()) {
- features.addAll(Arrays.asList(OTR));
- }
- Collections.sort(features);
- return features;
- }
+ public List<String> getFeatures() {
+ ArrayList<String> features = new ArrayList<>();
+ features.addAll(Arrays.asList(FEATURES));
+ if (mXmppConnectionService.confirmMessages()) {
+ features.addAll(Arrays.asList(MESSAGE_CONFIRMATION_FEATURES));
+ }
+ if (mXmppConnectionService.allowMessageCorrection()) {
+ features.addAll(Arrays.asList(MESSAGE_CORRECTION_FEATURES));
+ }
+ if (Config.supportOmemo()) {
+ features.add(AxolotlService.PEP_DEVICE_LIST_NOTIFY);
+ }
+ if (!mXmppConnectionService.useTorToConnect()) {
+ features.addAll(Arrays.asList(PRIVACY_SENSITIVE));
+ }
+ if (Config.supportOtr()) {
+ features.addAll(Arrays.asList(OTR));
+ }
+ Collections.sort(features);
+ return features;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/generator/IqGenerator.java b/src/main/java/de/pixart/messenger/generator/IqGenerator.java
index fd7177a8c..e2b462f76 100644
--- a/src/main/java/de/pixart/messenger/generator/IqGenerator.java
+++ b/src/main/java/de/pixart/messenger/generator/IqGenerator.java
@@ -34,347 +34,348 @@ import de.pixart.messenger.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());
- packet.setTo(request.getFrom());
- final Element query = packet.addChild("query",
- "http://jabber.org/protocol/disco#info");
- query.setAttribute("node", request.query().getAttribute("node"));
- final Element identity = query.addChild("identity");
- identity.setAttribute("category", "client");
- identity.setAttribute("type", getIdentityType());
- identity.setAttribute("name", getIdentityName());
- for (final String feature : getFeatures()) {
- query.addChild("feature").setAttribute("var", feature);
- }
- return packet;
- }
-
- 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(mXmppConnectionService.getString(R.string.app_name));
- query.addChild("version").setContent(getIdentityVersion());
- if ("chromium".equals(android.os.Build.BRAND)) {
- query.addChild("os").setContent("Chrome OS");
- } else{
- query.addChild("os").setContent("Android");
- }
- return packet;
- }
-
- public IqPacket entityTimeResponse(IqPacket request) {
- final IqPacket packet = request.generateResponse(IqPacket.TYPE.RESULT);
- Element time = packet.addChild("time","urn:xmpp:time");
- final long now = System.currentTimeMillis();
- time.addChild("utc").setContent(getTimestamp(now));
- TimeZone ourTimezone = TimeZone.getDefault();
- long offsetSeconds = ourTimezone.getOffset(now) / 1000;
- long offsetMinutes = offsetSeconds % (60 * 60);
- long offsetHours = offsetSeconds / (60 * 60);
- time.addChild("tzo").setContent(String.format("%02d",offsetHours)+":"+String.format("%02d",offsetMinutes));
- return packet;
- }
-
- protected IqPacket publish(final String node, final Element item) {
- final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
- final Element pubsub = packet.addChild("pubsub",
- "http://jabber.org/protocol/pubsub");
- final Element publish = pubsub.addChild("publish");
- publish.setAttribute("node", node);
- publish.addChild(item);
- return packet;
- }
-
- protected IqPacket retrieve(String node, Element item) {
- final IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
- final Element pubsub = packet.addChild("pubsub",
- "http://jabber.org/protocol/pubsub");
- final Element items = pubsub.addChild("items");
- items.setAttribute("node", node);
- if (item != null) {
- items.addChild(item);
- }
- return packet;
- }
-
- public IqPacket publishNick(String nick) {
- final Element item = new Element("item");
- item.addChild("nick","http://jabber.org/protocol/nick").setContent(nick);
- 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) {
- 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) {
- packet.setTo(to);
- }
- return packet;
- }
-
- public IqPacket retrieveBundlesForDevice(final Jid to, final int deviceid) {
- final IqPacket packet = retrieve(AxolotlService.PEP_BUNDLES+":"+deviceid, null);
- packet.setTo(to);
- return packet;
- }
-
- public IqPacket retrieveVerificationForDevice(final Jid to, final int deviceid) {
- final IqPacket packet = retrieve(AxolotlService.PEP_VERIFICATION+":"+deviceid, null);
- packet.setTo(to);
- return packet;
- }
-
- public IqPacket publishDeviceIds(final Set<Integer> ids) {
- final Element item = new Element("item");
- final Element list = item.addChild("list", AxolotlService.PEP_PREFIX);
- for(Integer id:ids) {
- final Element device = new Element("device");
- device.setAttribute("id", id);
- list.addChild(device);
- }
- return publish(AxolotlService.PEP_DEVICE_LIST, item);
- }
-
- public IqPacket publishBundles(final SignedPreKeyRecord signedPreKeyRecord, final IdentityKey identityKey,
- final Set<PreKeyRecord> preKeyRecords, final int deviceId) {
- final Element item = new Element("item");
- final Element bundle = item.addChild("bundle", AxolotlService.PEP_PREFIX);
- final Element signedPreKeyPublic = bundle.addChild("signedPreKeyPublic");
- signedPreKeyPublic.setAttribute("signedPreKeyId", signedPreKeyRecord.getId());
- ECPublicKey publicKey = signedPreKeyRecord.getKeyPair().getPublicKey();
- signedPreKeyPublic.setContent(Base64.encodeToString(publicKey.serialize(),Base64.DEFAULT));
- final Element signedPreKeySignature = bundle.addChild("signedPreKeySignature");
- signedPreKeySignature.setContent(Base64.encodeToString(signedPreKeyRecord.getSignature(),Base64.DEFAULT));
- final Element identityKeyElement = bundle.addChild("identityKey");
- identityKeyElement.setContent(Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT));
-
- final Element prekeys = bundle.addChild("prekeys", AxolotlService.PEP_PREFIX);
- for(PreKeyRecord preKeyRecord:preKeyRecords) {
- final Element prekey = prekeys.addChild("preKeyPublic");
- prekey.setAttribute("preKeyId", preKeyRecord.getId());
- prekey.setContent(Base64.encodeToString(preKeyRecord.getKeyPair().getPublicKey().serialize(), Base64.DEFAULT));
- }
-
- return publish(AxolotlService.PEP_BUNDLES+":"+deviceId, item);
- }
-
- public IqPacket publishVerification(byte[] signature, X509Certificate[] certificates, final int deviceId) {
- final Element item = new Element("item");
- final Element verification = item.addChild("verification", AxolotlService.PEP_PREFIX);
- final Element chain = verification.addChild("chain");
- for(int i = 0; i < certificates.length; ++i) {
- try {
- Element certificate = chain.addChild("certificate");
- certificate.setContent(Base64.encodeToString(certificates[i].getEncoded(), Base64.DEFAULT));
- certificate.setAttribute("index",i);
- } catch (CertificateEncodingException e) {
- Log.d(Config.LOGTAG, "could not encode certificate");
- }
- }
- verification.addChild("signature").setContent(Base64.encodeToString(signature, Base64.DEFAULT));
- return publish(AxolotlService.PEP_VERIFICATION+":"+deviceId, item);
- }
-
- public IqPacket queryMessageArchiveManagement(final MessageArchiveService.Query mam) {
- final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
- final Element query = packet.query("urn:xmpp:mam:0");
- query.setAttribute("queryid", mam.getQueryId());
- final Data data = new Data();
- data.setFormType("urn:xmpp:mam:0");
- if (mam.muc()) {
- packet.setTo(mam.getWith());
- } else if (mam.getWith()!=null) {
- data.put("with", mam.getWith().toString());
- }
- data.put("start", getTimestamp(mam.getStart()));
- data.put("end", getTimestamp(mam.getEnd()));
- data.submit();
- query.addChild(data);
- if (mam.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) {
- query.addChild("set", "http://jabber.org/protocol/rsm").addChild("before").setContent(mam.getReference());
- } else if (mam.getReference() != null) {
- query.addChild("set", "http://jabber.org/protocol/rsm").addChild("after").setContent(mam.getReference());
- }
- return packet;
- }
- public IqPacket generateGetBlockList() {
- final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
- iq.addChild("blocklist", Xmlns.BLOCKING);
-
- return iq;
- }
-
- public IqPacket generateSetBlockRequest(final Jid jid, boolean reportSpam) {
- final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- final Element block = iq.addChild("block", Xmlns.BLOCKING);
- final Element item = block.addChild("item").setAttribute("jid", jid.toBareJid().toString());
- if (reportSpam) {
- item.addChild("report", "urn:xmpp:reporting:0").addChild("spam");
- }
- Log.d(Config.LOGTAG,iq.toString());
- return iq;
- }
-
- public IqPacket generateSetUnblockRequest(final Jid jid) {
- final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- final Element block = iq.addChild("unblock", Xmlns.BLOCKING);
- block.addChild("item").setAttribute("jid", jid.toBareJid().toString());
- return iq;
- }
-
- public IqPacket generateSetPassword(final Account account, final String newPassword) {
- final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
- packet.setTo(account.getServer());
- final Element query = packet.addChild("query", Xmlns.REGISTER);
- final Jid jid = account.getJid();
- query.addChild("username").setContent(jid.getLocalpart());
- query.addChild("password").setContent(newPassword);
- return packet;
- }
-
- public IqPacket changeAffiliation(Conversation conference, Jid jid, String affiliation) {
- List<Jid> jids = new ArrayList<>();
- jids.add(jid);
- return changeAffiliation(conference,jids,affiliation);
- }
-
- public IqPacket changeAffiliation(Conversation conference, List<Jid> jids, String affiliation) {
- IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
- packet.setTo(conference.getJid().toBareJid());
- packet.setFrom(conference.getAccount().getJid());
- Element query = packet.query("http://jabber.org/protocol/muc#admin");
- for(Jid jid : jids) {
- Element item = query.addChild("item");
- item.setAttribute("jid", jid.toString());
- item.setAttribute("affiliation", affiliation);
- }
- return packet;
- }
-
- public IqPacket changeRole(Conversation conference, String nick, String role) {
- IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
- packet.setTo(conference.getJid().toBareJid());
- packet.setFrom(conference.getAccount().getJid());
- Element item = packet.query("http://jabber.org/protocol/muc#admin").addChild("item");
- item.setAttribute("nick", nick);
- item.setAttribute("role", role);
- return packet;
- }
-
- public IqPacket requestHttpUploadSlot(Jid host, DownloadableFile file, String mime) {
- IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
- packet.setTo(host);
- Element request = packet.addChild("request", Xmlns.HTTP_UPLOAD);
- request.addChild("filename").setContent(file.getName());
- request.addChild("size").setContent(String.valueOf(file.getExpectedSize()));
- if (mime != null) {
- request.addChild("content-type").setContent(mime);
- }
- return packet;
- }
-
- public IqPacket generateCreateAccountWithCaptcha(Account account, String id, Data data) {
- final IqPacket register = new IqPacket(IqPacket.TYPE.SET);
- register.setFrom(account.getJid().toBareJid());
- register.setTo(account.getServer());
- register.setId(id);
- Element query = register.query("jabber:iq:register");
- if (data != null) {
- query.addChild(data);
- }
- return register;
- }
-
- public IqPacket pushTokenToAppServer(Jid appServer, String token, String deviceId) {
- IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
- packet.setTo(appServer);
- Element command = packet.addChild("command", "http://jabber.org/protocol/commands");
- command.setAttribute("node","register-push-gcm");
- command.setAttribute("action","execute");
- Data data = new Data();
- data.put("token", token);
- data.put("device-id", deviceId);
- data.submit();
- command.addChild(data);
- return packet;
- }
-
- public IqPacket enablePush(Jid jid, String node, String secret) {
- IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
- Element enable = packet.addChild("enable","urn:xmpp:push:0");
- enable.setAttribute("jid",jid.toString());
- enable.setAttribute("node", node);
- Data data = new Data();
- data.setFormType("http://jabber.org/protocol/pubsub#publish-options");
- data.put("secret",secret);
- data.submit();
- enable.addChild(data);
- return packet;
- }
-
- public IqPacket queryAffiliation(Conversation conversation, String affiliation) {
- IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
- packet.setTo(conversation.getJid().toBareJid());
- packet.query("http://jabber.org/protocol/muc#admin").addChild("item").setAttribute("affiliation",affiliation);
- return packet;
- }
-
- public static Bundle defaultRoomConfiguration() {
- Bundle options = new Bundle();
- options.putString("muc#roomconfig_persistentroom", "1");
- options.putString("muc#roomconfig_membersonly", "1");
- options.putString("muc#roomconfig_publicroom", "0");
- options.putString("muc#roomconfig_whois", "anyone");
- return options;
- }
+ 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());
+ packet.setTo(request.getFrom());
+ final Element query = packet.addChild("query",
+ "http://jabber.org/protocol/disco#info");
+ query.setAttribute("node", request.query().getAttribute("node"));
+ final Element identity = query.addChild("identity");
+ identity.setAttribute("category", "client");
+ identity.setAttribute("type", getIdentityType());
+ identity.setAttribute("name", getIdentityName());
+ for (final String feature : getFeatures()) {
+ query.addChild("feature").setAttribute("var", feature);
+ }
+ return packet;
+ }
+
+ 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(mXmppConnectionService.getString(R.string.app_name));
+ query.addChild("version").setContent(getIdentityVersion());
+ if ("chromium".equals(android.os.Build.BRAND)) {
+ query.addChild("os").setContent("Chrome OS");
+ } else {
+ query.addChild("os").setContent("Android");
+ }
+ return packet;
+ }
+
+ public IqPacket entityTimeResponse(IqPacket request) {
+ final IqPacket packet = request.generateResponse(IqPacket.TYPE.RESULT);
+ Element time = packet.addChild("time", "urn:xmpp:time");
+ final long now = System.currentTimeMillis();
+ time.addChild("utc").setContent(getTimestamp(now));
+ TimeZone ourTimezone = TimeZone.getDefault();
+ long offsetSeconds = ourTimezone.getOffset(now) / 1000;
+ long offsetMinutes = offsetSeconds % (60 * 60);
+ long offsetHours = offsetSeconds / (60 * 60);
+ time.addChild("tzo").setContent(String.format("%02d", offsetHours) + ":" + String.format("%02d", offsetMinutes));
+ return packet;
+ }
+
+ protected IqPacket publish(final String node, final Element item) {
+ final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ final Element pubsub = packet.addChild("pubsub",
+ "http://jabber.org/protocol/pubsub");
+ final Element publish = pubsub.addChild("publish");
+ publish.setAttribute("node", node);
+ publish.addChild(item);
+ return packet;
+ }
+
+ protected IqPacket retrieve(String node, Element item) {
+ final IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
+ final Element pubsub = packet.addChild("pubsub",
+ "http://jabber.org/protocol/pubsub");
+ final Element items = pubsub.addChild("items");
+ items.setAttribute("node", node);
+ if (item != null) {
+ items.addChild(item);
+ }
+ return packet;
+ }
+
+ public IqPacket publishNick(String nick) {
+ final Element item = new Element("item");
+ item.addChild("nick", "http://jabber.org/protocol/nick").setContent(nick);
+ 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) {
+ 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) {
+ packet.setTo(to);
+ }
+ return packet;
+ }
+
+ public IqPacket retrieveBundlesForDevice(final Jid to, final int deviceid) {
+ final IqPacket packet = retrieve(AxolotlService.PEP_BUNDLES + ":" + deviceid, null);
+ packet.setTo(to);
+ return packet;
+ }
+
+ public IqPacket retrieveVerificationForDevice(final Jid to, final int deviceid) {
+ final IqPacket packet = retrieve(AxolotlService.PEP_VERIFICATION + ":" + deviceid, null);
+ packet.setTo(to);
+ return packet;
+ }
+
+ public IqPacket publishDeviceIds(final Set<Integer> ids) {
+ final Element item = new Element("item");
+ final Element list = item.addChild("list", AxolotlService.PEP_PREFIX);
+ for (Integer id : ids) {
+ final Element device = new Element("device");
+ device.setAttribute("id", id);
+ list.addChild(device);
+ }
+ return publish(AxolotlService.PEP_DEVICE_LIST, item);
+ }
+
+ public IqPacket publishBundles(final SignedPreKeyRecord signedPreKeyRecord, final IdentityKey identityKey,
+ final Set<PreKeyRecord> preKeyRecords, final int deviceId) {
+ final Element item = new Element("item");
+ final Element bundle = item.addChild("bundle", AxolotlService.PEP_PREFIX);
+ final Element signedPreKeyPublic = bundle.addChild("signedPreKeyPublic");
+ signedPreKeyPublic.setAttribute("signedPreKeyId", signedPreKeyRecord.getId());
+ ECPublicKey publicKey = signedPreKeyRecord.getKeyPair().getPublicKey();
+ signedPreKeyPublic.setContent(Base64.encodeToString(publicKey.serialize(), Base64.DEFAULT));
+ final Element signedPreKeySignature = bundle.addChild("signedPreKeySignature");
+ signedPreKeySignature.setContent(Base64.encodeToString(signedPreKeyRecord.getSignature(), Base64.DEFAULT));
+ final Element identityKeyElement = bundle.addChild("identityKey");
+ identityKeyElement.setContent(Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT));
+
+ final Element prekeys = bundle.addChild("prekeys", AxolotlService.PEP_PREFIX);
+ for (PreKeyRecord preKeyRecord : preKeyRecords) {
+ final Element prekey = prekeys.addChild("preKeyPublic");
+ prekey.setAttribute("preKeyId", preKeyRecord.getId());
+ prekey.setContent(Base64.encodeToString(preKeyRecord.getKeyPair().getPublicKey().serialize(), Base64.DEFAULT));
+ }
+
+ return publish(AxolotlService.PEP_BUNDLES + ":" + deviceId, item);
+ }
+
+ public IqPacket publishVerification(byte[] signature, X509Certificate[] certificates, final int deviceId) {
+ final Element item = new Element("item");
+ final Element verification = item.addChild("verification", AxolotlService.PEP_PREFIX);
+ final Element chain = verification.addChild("chain");
+ for (int i = 0; i < certificates.length; ++i) {
+ try {
+ Element certificate = chain.addChild("certificate");
+ certificate.setContent(Base64.encodeToString(certificates[i].getEncoded(), Base64.DEFAULT));
+ certificate.setAttribute("index", i);
+ } catch (CertificateEncodingException e) {
+ Log.d(Config.LOGTAG, "could not encode certificate");
+ }
+ }
+ verification.addChild("signature").setContent(Base64.encodeToString(signature, Base64.DEFAULT));
+ return publish(AxolotlService.PEP_VERIFICATION + ":" + deviceId, item);
+ }
+
+ public IqPacket queryMessageArchiveManagement(final MessageArchiveService.Query mam) {
+ final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ final Element query = packet.query("urn:xmpp:mam:0");
+ query.setAttribute("queryid", mam.getQueryId());
+ final Data data = new Data();
+ data.setFormType("urn:xmpp:mam:0");
+ if (mam.muc()) {
+ packet.setTo(mam.getWith());
+ } else if (mam.getWith() != null) {
+ data.put("with", mam.getWith().toString());
+ }
+ data.put("start", getTimestamp(mam.getStart()));
+ data.put("end", getTimestamp(mam.getEnd()));
+ data.submit();
+ query.addChild(data);
+ if (mam.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) {
+ query.addChild("set", "http://jabber.org/protocol/rsm").addChild("before").setContent(mam.getReference());
+ } else if (mam.getReference() != null) {
+ query.addChild("set", "http://jabber.org/protocol/rsm").addChild("after").setContent(mam.getReference());
+ }
+ return packet;
+ }
+
+ public IqPacket generateGetBlockList() {
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
+ iq.addChild("blocklist", Xmlns.BLOCKING);
+
+ return iq;
+ }
+
+ public IqPacket generateSetBlockRequest(final Jid jid, boolean reportSpam) {
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ final Element block = iq.addChild("block", Xmlns.BLOCKING);
+ final Element item = block.addChild("item").setAttribute("jid", jid.toBareJid().toString());
+ if (reportSpam) {
+ item.addChild("report", "urn:xmpp:reporting:0").addChild("spam");
+ }
+ Log.d(Config.LOGTAG, iq.toString());
+ return iq;
+ }
+
+ public IqPacket generateSetUnblockRequest(final Jid jid) {
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ final Element block = iq.addChild("unblock", Xmlns.BLOCKING);
+ block.addChild("item").setAttribute("jid", jid.toBareJid().toString());
+ return iq;
+ }
+
+ public IqPacket generateSetPassword(final Account account, final String newPassword) {
+ final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ packet.setTo(account.getServer());
+ final Element query = packet.addChild("query", Xmlns.REGISTER);
+ final Jid jid = account.getJid();
+ query.addChild("username").setContent(jid.getLocalpart());
+ query.addChild("password").setContent(newPassword);
+ return packet;
+ }
+
+ public IqPacket changeAffiliation(Conversation conference, Jid jid, String affiliation) {
+ List<Jid> jids = new ArrayList<>();
+ jids.add(jid);
+ return changeAffiliation(conference, jids, affiliation);
+ }
+
+ public IqPacket changeAffiliation(Conversation conference, List<Jid> jids, String affiliation) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ packet.setTo(conference.getJid().toBareJid());
+ packet.setFrom(conference.getAccount().getJid());
+ Element query = packet.query("http://jabber.org/protocol/muc#admin");
+ for (Jid jid : jids) {
+ Element item = query.addChild("item");
+ item.setAttribute("jid", jid.toString());
+ item.setAttribute("affiliation", affiliation);
+ }
+ return packet;
+ }
+
+ public IqPacket changeRole(Conversation conference, String nick, String role) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ packet.setTo(conference.getJid().toBareJid());
+ packet.setFrom(conference.getAccount().getJid());
+ Element item = packet.query("http://jabber.org/protocol/muc#admin").addChild("item");
+ item.setAttribute("nick", nick);
+ item.setAttribute("role", role);
+ return packet;
+ }
+
+ public IqPacket requestHttpUploadSlot(Jid host, DownloadableFile file, String mime) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
+ packet.setTo(host);
+ Element request = packet.addChild("request", Xmlns.HTTP_UPLOAD);
+ request.addChild("filename").setContent(file.getName());
+ request.addChild("size").setContent(String.valueOf(file.getExpectedSize()));
+ if (mime != null) {
+ request.addChild("content-type").setContent(mime);
+ }
+ return packet;
+ }
+
+ public IqPacket generateCreateAccountWithCaptcha(Account account, String id, Data data) {
+ final IqPacket register = new IqPacket(IqPacket.TYPE.SET);
+ register.setFrom(account.getJid().toBareJid());
+ register.setTo(account.getServer());
+ register.setId(id);
+ Element query = register.query("jabber:iq:register");
+ if (data != null) {
+ query.addChild(data);
+ }
+ return register;
+ }
+
+ public IqPacket pushTokenToAppServer(Jid appServer, String token, String deviceId) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ packet.setTo(appServer);
+ Element command = packet.addChild("command", "http://jabber.org/protocol/commands");
+ command.setAttribute("node", "register-push-gcm");
+ command.setAttribute("action", "execute");
+ Data data = new Data();
+ data.put("token", token);
+ data.put("device-id", deviceId);
+ data.submit();
+ command.addChild(data);
+ return packet;
+ }
+
+ public IqPacket enablePush(Jid jid, String node, String secret) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ Element enable = packet.addChild("enable", "urn:xmpp:push:0");
+ enable.setAttribute("jid", jid.toString());
+ enable.setAttribute("node", node);
+ Data data = new Data();
+ data.setFormType("http://jabber.org/protocol/pubsub#publish-options");
+ data.put("secret", secret);
+ data.submit();
+ enable.addChild(data);
+ return packet;
+ }
+
+ public IqPacket queryAffiliation(Conversation conversation, String affiliation) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
+ packet.setTo(conversation.getJid().toBareJid());
+ packet.query("http://jabber.org/protocol/muc#admin").addChild("item").setAttribute("affiliation", affiliation);
+ return packet;
+ }
+
+ public static Bundle defaultRoomConfiguration() {
+ Bundle options = new Bundle();
+ options.putString("muc#roomconfig_persistentroom", "1");
+ options.putString("muc#roomconfig_membersonly", "1");
+ options.putString("muc#roomconfig_publicroom", "0");
+ options.putString("muc#roomconfig_whois", "anyone");
+ return options;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/generator/MessageGenerator.java b/src/main/java/de/pixart/messenger/generator/MessageGenerator.java
index ce0a81a72..bdb4b7837 100644
--- a/src/main/java/de/pixart/messenger/generator/MessageGenerator.java
+++ b/src/main/java/de/pixart/messenger/generator/MessageGenerator.java
@@ -23,207 +23,207 @@ import de.pixart.messenger.xmpp.jid.Jid;
import de.pixart.messenger.xmpp.stanzas.MessagePacket;
public class MessageGenerator extends AbstractGenerator {
- public static final String OTR_FALLBACK_MESSAGE = "I would like to start a private (OTR encrypted) conversation but your client doesn’t seem to support that";
- private static final String OMEMO_FALLBACK_MESSAGE = "I sent you an OMEMO encrypted message but your client doesn’t seem to support that. Find more information on https://conversations.im/omemo";
- private static final String PGP_FALLBACK_MESSAGE = "I sent you a PGP encrypted message but your client doesn’t seem to support that.";
-
- public MessageGenerator(XmppConnectionService service) {
- super(service);
- }
-
- private MessagePacket preparePacket(Message message) {
- Conversation conversation = message.getConversation();
- Account account = conversation.getAccount();
- MessagePacket packet = new MessagePacket();
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- packet.setTo(message.getCounterpart());
- packet.setType(MessagePacket.TYPE_CHAT);
- packet.addChild("markable", "urn:xmpp:chat-markers:0");
- if (this.mXmppConnectionService.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()) {
- packet.addChild("request", "urn:xmpp:receipts");
- }
- } else {
- packet.setTo(message.getCounterpart().toBareJid());
- packet.setType(MessagePacket.TYPE_GROUPCHAT);
- }
- packet.setFrom(account.getJid());
- packet.setId(message.getUuid());
- if (message.edited()) {
- packet.addChild("replace","urn:xmpp:message-correct:0").setAttribute("id",message.getEditedId());
- }
- return packet;
- }
-
- public void addDelay(MessagePacket packet, long timestamp) {
- final SimpleDateFormat mDateFormat = new SimpleDateFormat(
- "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
- mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- Element delay = packet.addChild("delay", "urn:xmpp:delay");
- Date date = new Date(timestamp);
- delay.setAttribute("stamp", mDateFormat.format(date));
- }
-
- public MessagePacket generateAxolotlChat(Message message, XmppAxolotlMessage axolotlMessage) {
- MessagePacket packet = preparePacket(message);
- if (axolotlMessage == null) {
- return null;
- }
- packet.setAxolotlMessage(axolotlMessage.toElement());
- if (Config.supportUnencrypted() && !recipientSupportsOmemo(message)) {
- packet.setBody(OMEMO_FALLBACK_MESSAGE);
- }
- packet.addChild("store", "urn:xmpp:hints");
- return packet;
- }
-
- private static boolean recipientSupportsOmemo(Message message) {
- Contact c = message.getContact();
- return c != null && c.getPresences().allOrNonSupport(AxolotlService.PEP_DEVICE_LIST_NOTIFY);
- }
-
- public static void addMessageHints(MessagePacket packet) {
- packet.addChild("private", "urn:xmpp:carbons:2");
- packet.addChild("no-copy", "urn:xmpp:hints");
- packet.addChild("no-permanent-store", "urn:xmpp:hints");
- packet.addChild("no-permanent-storage", "urn:xmpp:hints"); //do not copy this. this is wrong. it is *store*
- }
-
- public MessagePacket generateOtrChat(Message message) {
- Session otrSession = message.getConversation().getOtrSession();
- if (otrSession == null) {
- return null;
- }
- MessagePacket packet = preparePacket(message);
- addMessageHints(packet);
- try {
- String content;
- if (message.hasFileOnRemoteHost()) {
- content = message.getFileParams().url.toString();
- } else {
- content = message.getBody();
- }
- packet.setBody(otrSession.transformSending(content)[0]);
- return packet;
- } catch (OtrException e) {
- return null;
- }
- }
-
- public MessagePacket generateChat(Message message) {
- MessagePacket packet = preparePacket(message);
- String content;
- if (message.hasFileOnRemoteHost()) {
- Message.FileParams fileParams = message.getFileParams();
- content = fileParams.url.toString();
- packet.addChild("x","jabber:x:oob").addChild("url").setContent(content);
- } else {
- content = message.getBody();
- }
- packet.setBody(content);
- return packet;
- }
-
- public MessagePacket generatePgpChat(Message message) {
- MessagePacket packet = preparePacket(message);
- if (Config.supportUnencrypted()) {
- packet.setBody(PGP_FALLBACK_MESSAGE);
- }
- if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- packet.addChild("x", "jabber:x:encrypted").setContent(message.getEncryptedBody());
- } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- packet.addChild("x", "jabber:x:encrypted").setContent(message.getBody());
- }
- return packet;
- }
-
- public MessagePacket generateChatState(Conversation conversation) {
- final Account account = conversation.getAccount();
- MessagePacket packet = new MessagePacket();
- packet.setType(MessagePacket.TYPE_CHAT);
- packet.setTo(conversation.getJid().toBareJid());
- packet.setFrom(account.getJid());
- packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
- packet.addChild("no-store", "urn:xmpp:hints");
- packet.addChild("no-storage", "urn:xmpp:hints"); //wrong! don't copy this. Its *store*
- return packet;
- }
-
- public MessagePacket confirm(final Account account, final Jid to, final String id) {
- MessagePacket packet = new MessagePacket();
- packet.setType(MessagePacket.TYPE_CHAT);
- packet.setTo(to);
- packet.setFrom(account.getJid());
- Element received = packet.addChild("displayed","urn:xmpp:chat-markers:0");
- received.setAttribute("id", id);
- packet.addChild("store", "urn:xmpp:hints");
- return packet;
- }
-
- public MessagePacket conferenceSubject(Conversation conversation,String subject) {
- MessagePacket packet = new MessagePacket();
- packet.setType(MessagePacket.TYPE_GROUPCHAT);
- packet.setTo(conversation.getJid().toBareJid());
- Element subjectChild = new Element("subject");
- subjectChild.setContent(subject);
- packet.addChild(subjectChild);
- packet.setFrom(conversation.getAccount().getJid().toBareJid());
- return packet;
- }
-
- public MessagePacket directInvite(final Conversation conversation, final Jid contact) {
- MessagePacket packet = new MessagePacket();
- packet.setType(MessagePacket.TYPE_NORMAL);
- packet.setTo(contact);
- packet.setFrom(conversation.getAccount().getJid());
- Element x = packet.addChild("x", "jabber:x:conference");
- x.setAttribute("jid", conversation.getJid().toBareJid().toString());
+ public static final String OTR_FALLBACK_MESSAGE = "I would like to start a private (OTR encrypted) conversation but your client doesn’t seem to support that";
+ private static final String OMEMO_FALLBACK_MESSAGE = "I sent you an OMEMO encrypted message but your client doesn’t seem to support that. Find more information on https://conversations.im/omemo";
+ private static final String PGP_FALLBACK_MESSAGE = "I sent you a PGP encrypted message but your client doesn’t seem to support that.";
+
+ public MessageGenerator(XmppConnectionService service) {
+ super(service);
+ }
+
+ private MessagePacket preparePacket(Message message) {
+ Conversation conversation = message.getConversation();
+ Account account = conversation.getAccount();
+ MessagePacket packet = new MessagePacket();
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ packet.setTo(message.getCounterpart());
+ packet.setType(MessagePacket.TYPE_CHAT);
+ packet.addChild("markable", "urn:xmpp:chat-markers:0");
+ if (this.mXmppConnectionService.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()) {
+ packet.addChild("request", "urn:xmpp:receipts");
+ }
+ } else {
+ packet.setTo(message.getCounterpart().toBareJid());
+ packet.setType(MessagePacket.TYPE_GROUPCHAT);
+ }
+ packet.setFrom(account.getJid());
+ packet.setId(message.getUuid());
+ if (message.edited()) {
+ packet.addChild("replace", "urn:xmpp:message-correct:0").setAttribute("id", message.getEditedId());
+ }
+ return packet;
+ }
+
+ public void addDelay(MessagePacket packet, long timestamp) {
+ final SimpleDateFormat mDateFormat = new SimpleDateFormat(
+ "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
+ mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ Element delay = packet.addChild("delay", "urn:xmpp:delay");
+ Date date = new Date(timestamp);
+ delay.setAttribute("stamp", mDateFormat.format(date));
+ }
+
+ public MessagePacket generateAxolotlChat(Message message, XmppAxolotlMessage axolotlMessage) {
+ MessagePacket packet = preparePacket(message);
+ if (axolotlMessage == null) {
+ return null;
+ }
+ packet.setAxolotlMessage(axolotlMessage.toElement());
+ if (Config.supportUnencrypted() && !recipientSupportsOmemo(message)) {
+ packet.setBody(OMEMO_FALLBACK_MESSAGE);
+ }
+ packet.addChild("store", "urn:xmpp:hints");
+ return packet;
+ }
+
+ private static boolean recipientSupportsOmemo(Message message) {
+ Contact c = message.getContact();
+ return c != null && c.getPresences().allOrNonSupport(AxolotlService.PEP_DEVICE_LIST_NOTIFY);
+ }
+
+ public static void addMessageHints(MessagePacket packet) {
+ packet.addChild("private", "urn:xmpp:carbons:2");
+ packet.addChild("no-copy", "urn:xmpp:hints");
+ packet.addChild("no-permanent-store", "urn:xmpp:hints");
+ packet.addChild("no-permanent-storage", "urn:xmpp:hints"); //do not copy this. this is wrong. it is *store*
+ }
+
+ public MessagePacket generateOtrChat(Message message) {
+ Session otrSession = message.getConversation().getOtrSession();
+ if (otrSession == null) {
+ return null;
+ }
+ MessagePacket packet = preparePacket(message);
+ addMessageHints(packet);
+ try {
+ String content;
+ if (message.hasFileOnRemoteHost()) {
+ content = message.getFileParams().url.toString();
+ } else {
+ content = message.getBody();
+ }
+ packet.setBody(otrSession.transformSending(content)[0]);
+ return packet;
+ } catch (OtrException e) {
+ return null;
+ }
+ }
+
+ public MessagePacket generateChat(Message message) {
+ MessagePacket packet = preparePacket(message);
+ String content;
+ if (message.hasFileOnRemoteHost()) {
+ Message.FileParams fileParams = message.getFileParams();
+ content = fileParams.url.toString();
+ packet.addChild("x", "jabber:x:oob").addChild("url").setContent(content);
+ } else {
+ content = message.getBody();
+ }
+ packet.setBody(content);
+ return packet;
+ }
+
+ public MessagePacket generatePgpChat(Message message) {
+ MessagePacket packet = preparePacket(message);
+ if (Config.supportUnencrypted()) {
+ packet.setBody(PGP_FALLBACK_MESSAGE);
+ }
+ if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ packet.addChild("x", "jabber:x:encrypted").setContent(message.getEncryptedBody());
+ } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ packet.addChild("x", "jabber:x:encrypted").setContent(message.getBody());
+ }
+ return packet;
+ }
+
+ public MessagePacket generateChatState(Conversation conversation) {
+ final Account account = conversation.getAccount();
+ MessagePacket packet = new MessagePacket();
+ packet.setType(MessagePacket.TYPE_CHAT);
+ packet.setTo(conversation.getJid().toBareJid());
+ packet.setFrom(account.getJid());
+ packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
+ packet.addChild("no-store", "urn:xmpp:hints");
+ packet.addChild("no-storage", "urn:xmpp:hints"); //wrong! don't copy this. Its *store*
+ return packet;
+ }
+
+ public MessagePacket confirm(final Account account, final Jid to, final String id) {
+ MessagePacket packet = new MessagePacket();
+ packet.setType(MessagePacket.TYPE_CHAT);
+ packet.setTo(to);
+ packet.setFrom(account.getJid());
+ Element received = packet.addChild("displayed", "urn:xmpp:chat-markers:0");
+ received.setAttribute("id", id);
+ packet.addChild("store", "urn:xmpp:hints");
+ return packet;
+ }
+
+ public MessagePacket conferenceSubject(Conversation conversation, String subject) {
+ MessagePacket packet = new MessagePacket();
+ packet.setType(MessagePacket.TYPE_GROUPCHAT);
+ packet.setTo(conversation.getJid().toBareJid());
+ Element subjectChild = new Element("subject");
+ subjectChild.setContent(subject);
+ packet.addChild(subjectChild);
+ packet.setFrom(conversation.getAccount().getJid().toBareJid());
+ return packet;
+ }
+
+ public MessagePacket directInvite(final Conversation conversation, final Jid contact) {
+ MessagePacket packet = new MessagePacket();
+ packet.setType(MessagePacket.TYPE_NORMAL);
+ packet.setTo(contact);
+ packet.setFrom(conversation.getAccount().getJid());
+ Element x = packet.addChild("x", "jabber:x:conference");
+ x.setAttribute("jid", conversation.getJid().toBareJid().toString());
String password = conversation.getMucOptions().getPassword();
if (password != null) {
- x.setAttribute("password",password);
+ x.setAttribute("password", password);
+ }
+ return packet;
+ }
+
+ public MessagePacket invite(Conversation conversation, Jid contact) {
+ MessagePacket packet = new MessagePacket();
+ packet.setTo(conversation.getJid().toBareJid());
+ packet.setFrom(conversation.getAccount().getJid());
+ Element x = new Element("x");
+ x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user");
+ Element invite = new Element("invite");
+ invite.setAttribute("to", contact.toBareJid().toString());
+ x.addChild(invite);
+ packet.addChild(x);
+ return packet;
+ }
+
+ public MessagePacket received(Account account, MessagePacket originalMessage, ArrayList<String> namespaces, int type) {
+ MessagePacket receivedPacket = new MessagePacket();
+ receivedPacket.setType(type);
+ receivedPacket.setTo(originalMessage.getFrom());
+ receivedPacket.setFrom(account.getJid());
+ for (String namespace : namespaces) {
+ receivedPacket.addChild("received", namespace).setAttribute("id", originalMessage.getId());
}
- return packet;
- }
-
- public MessagePacket invite(Conversation conversation, Jid contact) {
- MessagePacket packet = new MessagePacket();
- packet.setTo(conversation.getJid().toBareJid());
- packet.setFrom(conversation.getAccount().getJid());
- Element x = new Element("x");
- x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user");
- Element invite = new Element("invite");
- invite.setAttribute("to", contact.toBareJid().toString());
- x.addChild(invite);
- packet.addChild(x);
- return packet;
- }
-
- public MessagePacket received(Account account, MessagePacket originalMessage, ArrayList<String> namespaces, int type) {
- MessagePacket receivedPacket = new MessagePacket();
- receivedPacket.setType(type);
- receivedPacket.setTo(originalMessage.getFrom());
- receivedPacket.setFrom(account.getJid());
- for(String namespace : namespaces) {
- receivedPacket.addChild("received", namespace).setAttribute("id", originalMessage.getId());
- }
- return receivedPacket;
- }
-
- public MessagePacket generateOtrError(Jid to, String id, String errorText) {
- MessagePacket packet = new MessagePacket();
- packet.setType(MessagePacket.TYPE_ERROR);
- packet.setAttribute("id",id);
- packet.setTo(to);
- Element error = packet.addChild("error");
- error.setAttribute("code","406");
- error.setAttribute("type","modify");
- error.addChild("not-acceptable","urn:ietf:params:xml:ns:xmpp-stanzas");
- error.addChild("text").setContent("?OTR Error:" + errorText);
- return packet;
- }
+ return receivedPacket;
+ }
+
+ public MessagePacket generateOtrError(Jid to, String id, String errorText) {
+ MessagePacket packet = new MessagePacket();
+ packet.setType(MessagePacket.TYPE_ERROR);
+ packet.setAttribute("id", id);
+ packet.setTo(to);
+ Element error = packet.addChild("error");
+ error.setAttribute("code", "406");
+ error.setAttribute("type", "modify");
+ error.addChild("not-acceptable", "urn:ietf:params:xml:ns:xmpp-stanzas");
+ error.addChild("text").setContent("?OTR Error:" + errorText);
+ return packet;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/generator/PresenceGenerator.java b/src/main/java/de/pixart/messenger/generator/PresenceGenerator.java
index bf84444a5..2234d54e6 100644
--- a/src/main/java/de/pixart/messenger/generator/PresenceGenerator.java
+++ b/src/main/java/de/pixart/messenger/generator/PresenceGenerator.java
@@ -9,63 +9,63 @@ import de.pixart.messenger.xmpp.stanzas.PresencePacket;
public class PresenceGenerator extends AbstractGenerator {
- public PresenceGenerator(XmppConnectionService service) {
- super(service);
- }
+ public PresenceGenerator(XmppConnectionService service) {
+ super(service);
+ }
- private PresencePacket subscription(String type, Contact contact) {
- PresencePacket packet = new PresencePacket();
- packet.setAttribute("type", type);
- packet.setTo(contact.getJid());
- packet.setFrom(contact.getAccount().getJid().toBareJid());
- return packet;
- }
+ private PresencePacket subscription(String type, Contact contact) {
+ PresencePacket packet = new PresencePacket();
+ packet.setAttribute("type", type);
+ packet.setTo(contact.getJid());
+ packet.setFrom(contact.getAccount().getJid().toBareJid());
+ return packet;
+ }
- public PresencePacket requestPresenceUpdatesFrom(Contact contact) {
- return subscription("subscribe", contact);
- }
+ public PresencePacket requestPresenceUpdatesFrom(Contact contact) {
+ return subscription("subscribe", contact);
+ }
- public PresencePacket stopPresenceUpdatesFrom(Contact contact) {
- return subscription("unsubscribe", contact);
- }
+ public PresencePacket stopPresenceUpdatesFrom(Contact contact) {
+ return subscription("unsubscribe", contact);
+ }
- public PresencePacket stopPresenceUpdatesTo(Contact contact) {
- return subscription("unsubscribed", contact);
- }
+ public PresencePacket stopPresenceUpdatesTo(Contact contact) {
+ return subscription("unsubscribed", contact);
+ }
- public PresencePacket sendPresenceUpdatesTo(Contact contact) {
- return subscription("subscribed", contact);
- }
+ public PresencePacket sendPresenceUpdatesTo(Contact contact) {
+ return subscription("subscribed", contact);
+ }
- public PresencePacket selfPresence(Account account, Presence.Status status) {
+ public PresencePacket selfPresence(Account account, Presence.Status status) {
return selfPresence(account, status, true);
}
public PresencePacket selfPresence(Account account, Presence.Status status, boolean includePgpAnnouncement) {
- PresencePacket packet = new PresencePacket();
- if(status.toShowString() != null) {
- packet.addChild("show").setContent(status.toShowString());
- }
- packet.setFrom(account.getJid());
- String sig = account.getPgpSignature();
+ PresencePacket packet = new PresencePacket();
+ if (status.toShowString() != null) {
+ packet.addChild("show").setContent(status.toShowString());
+ }
+ packet.setFrom(account.getJid());
+ String sig = account.getPgpSignature();
if (includePgpAnnouncement && sig != null && mXmppConnectionService.getPgpEngine() != null) {
- packet.addChild("x", "jabber:x:signed").setContent(sig);
- }
- String capHash = getCapHash();
- if (capHash != null) {
- Element cap = packet.addChild("c",
- "http://jabber.org/protocol/caps");
- cap.setAttribute("hash", "sha-1");
- cap.setAttribute("node", "http://conversations.im");
- cap.setAttribute("ver", capHash);
- }
- return packet;
- }
+ packet.addChild("x", "jabber:x:signed").setContent(sig);
+ }
+ String capHash = getCapHash();
+ if (capHash != null) {
+ Element cap = packet.addChild("c",
+ "http://jabber.org/protocol/caps");
+ cap.setAttribute("hash", "sha-1");
+ cap.setAttribute("node", "http://conversations.im");
+ cap.setAttribute("ver", capHash);
+ }
+ return packet;
+ }
- public PresencePacket sendOfflinePresence(Account account) {
- PresencePacket packet = new PresencePacket();
- packet.setFrom(account.getJid());
- packet.setAttribute("type","unavailable");
- return packet;
- }
+ public PresencePacket sendOfflinePresence(Account account) {
+ PresencePacket packet = new PresencePacket();
+ packet.setFrom(account.getJid());
+ packet.setAttribute("type", "unavailable");
+ return packet;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/http/HttpConnectionManager.java b/src/main/java/de/pixart/messenger/http/HttpConnectionManager.java
index 921b20571..a03e36991 100644
--- a/src/main/java/de/pixart/messenger/http/HttpConnectionManager.java
+++ b/src/main/java/de/pixart/messenger/http/HttpConnectionManager.java
@@ -25,75 +25,75 @@ import de.pixart.messenger.utils.SSLSocketHelper;
public class HttpConnectionManager extends AbstractConnectionManager {
- public HttpConnectionManager(XmppConnectionService service) {
- super(service);
- }
-
- private List<HttpDownloadConnection> downloadConnections = new CopyOnWriteArrayList<>();
- private List<HttpUploadConnection> uploadConnections = new CopyOnWriteArrayList<>();
-
- public HttpDownloadConnection createNewDownloadConnection(Message message) {
- return this.createNewDownloadConnection(message, false);
- }
-
- public HttpDownloadConnection createNewDownloadConnection(Message message, boolean interactive) {
- HttpDownloadConnection connection = new HttpDownloadConnection(this);
- connection.init(message,interactive);
- this.downloadConnections.add(connection);
- return connection;
- }
-
- public HttpUploadConnection createNewUploadConnection(Message message, boolean delay) {
- HttpUploadConnection connection = new HttpUploadConnection(this);
- connection.init(message,delay);
- this.uploadConnections.add(connection);
- return connection;
- }
-
- public void finishConnection(HttpDownloadConnection connection) {
- this.downloadConnections.remove(connection);
- }
-
- public void finishUploadConnection(HttpUploadConnection httpUploadConnection) {
- this.uploadConnections.remove(httpUploadConnection);
- }
-
- public void setupTrustManager(final HttpsURLConnection connection, final boolean interactive) {
- final X509TrustManager trustManager;
- final HostnameVerifier hostnameVerifier;
- if (interactive) {
- trustManager = mXmppConnectionService.getMemorizingTrustManager();
- hostnameVerifier = mXmppConnectionService
- .getMemorizingTrustManager().wrapHostnameVerifier(
- new StrictHostnameVerifier());
- } else {
- trustManager = mXmppConnectionService.getMemorizingTrustManager()
- .getNonInteractive();
- hostnameVerifier = mXmppConnectionService
- .getMemorizingTrustManager()
- .wrapHostnameVerifierNonInteractive(
- new StrictHostnameVerifier());
- }
- try {
- final SSLContext sc = SSLSocketHelper.getSSLContext();
- sc.init(null, new X509TrustManager[]{trustManager},
- mXmppConnectionService.getRNG());
-
- final SSLSocketFactory sf = sc.getSocketFactory();
- final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites(
- sf.getSupportedCipherSuites());
- if (cipherSuites.length > 0) {
- sc.getDefaultSSLParameters().setCipherSuites(cipherSuites);
-
- }
-
- connection.setSSLSocketFactory(sf);
- connection.setHostnameVerifier(hostnameVerifier);
- } catch (final KeyManagementException | NoSuchAlgorithmException ignored) {
- }
- }
-
- public Proxy getProxy() throws IOException {
- return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(InetAddress.getByAddress(new byte[]{127,0,0,1}), 8118));
- }
+ public HttpConnectionManager(XmppConnectionService service) {
+ super(service);
+ }
+
+ private List<HttpDownloadConnection> downloadConnections = new CopyOnWriteArrayList<>();
+ private List<HttpUploadConnection> uploadConnections = new CopyOnWriteArrayList<>();
+
+ public HttpDownloadConnection createNewDownloadConnection(Message message) {
+ return this.createNewDownloadConnection(message, false);
+ }
+
+ public HttpDownloadConnection createNewDownloadConnection(Message message, boolean interactive) {
+ HttpDownloadConnection connection = new HttpDownloadConnection(this);
+ connection.init(message, interactive);
+ this.downloadConnections.add(connection);
+ return connection;
+ }
+
+ public HttpUploadConnection createNewUploadConnection(Message message, boolean delay) {
+ HttpUploadConnection connection = new HttpUploadConnection(this);
+ connection.init(message, delay);
+ this.uploadConnections.add(connection);
+ return connection;
+ }
+
+ public void finishConnection(HttpDownloadConnection connection) {
+ this.downloadConnections.remove(connection);
+ }
+
+ public void finishUploadConnection(HttpUploadConnection httpUploadConnection) {
+ this.uploadConnections.remove(httpUploadConnection);
+ }
+
+ public void setupTrustManager(final HttpsURLConnection connection, final boolean interactive) {
+ final X509TrustManager trustManager;
+ final HostnameVerifier hostnameVerifier;
+ if (interactive) {
+ trustManager = mXmppConnectionService.getMemorizingTrustManager();
+ hostnameVerifier = mXmppConnectionService
+ .getMemorizingTrustManager().wrapHostnameVerifier(
+ new StrictHostnameVerifier());
+ } else {
+ trustManager = mXmppConnectionService.getMemorizingTrustManager()
+ .getNonInteractive();
+ hostnameVerifier = mXmppConnectionService
+ .getMemorizingTrustManager()
+ .wrapHostnameVerifierNonInteractive(
+ new StrictHostnameVerifier());
+ }
+ try {
+ final SSLContext sc = SSLSocketHelper.getSSLContext();
+ sc.init(null, new X509TrustManager[]{trustManager},
+ mXmppConnectionService.getRNG());
+
+ final SSLSocketFactory sf = sc.getSocketFactory();
+ final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites(
+ sf.getSupportedCipherSuites());
+ if (cipherSuites.length > 0) {
+ sc.getDefaultSSLParameters().setCipherSuites(cipherSuites);
+
+ }
+
+ connection.setSSLSocketFactory(sf);
+ connection.setHostnameVerifier(hostnameVerifier);
+ } catch (final KeyManagementException | NoSuchAlgorithmException ignored) {
+ }
+ }
+
+ public Proxy getProxy() throws IOException {
+ return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(InetAddress.getByAddress(new byte[]{127, 0, 0, 1}), 8118));
+ }
}
diff --git a/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java b/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java
index f091c9dad..8c342e6f2 100644
--- a/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java
+++ b/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java
@@ -32,335 +32,335 @@ import de.pixart.messenger.utils.FileWriterException;
public class HttpDownloadConnection implements Transferable {
- private HttpConnectionManager mHttpConnectionManager;
- private XmppConnectionService mXmppConnectionService;
-
- private URL mUrl;
- private Message message;
- private DownloadableFile file;
- private int mStatus = Transferable.STATUS_UNKNOWN;
- private boolean acceptedAutomatically = false;
- private int mProgress = 0;
- private boolean mUseTor = false;
- private boolean canceled = false;
-
- private final SimpleDateFormat fileDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
-
- public HttpDownloadConnection(HttpConnectionManager manager) {
- this.mHttpConnectionManager = manager;
- this.mXmppConnectionService = manager.getXmppConnectionService();
- this.mUseTor = mXmppConnectionService.useTorToConnect();
- }
-
- @Override
- public boolean start() {
- if (mXmppConnectionService.hasInternetConnection()) {
- if (this.mStatus == STATUS_OFFER_CHECK_FILESIZE) {
- checkFileSize(true);
- } else {
- new Thread(new FileDownloader(true)).start();
- }
- return true;
- } else {
- return false;
- }
- }
-
- public void init(Message message) {
- init(message, false);
- }
-
- public void init(Message message, boolean interactive) {
- this.message = message;
- this.message.setTransferable(this);
- try {
- if (message.hasFileOnRemoteHost()) {
- mUrl = message.getFileParams().url;
- } 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)) {
- 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;
- } else {
- extension = lastPart;
- }
- String filename = fileDateFormat.format(new Date(message.getTimeSent()))+"_"+message.getUuid().substring(0,4);
- message.setRelativeFilePath(filename + "." + extension);
- this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
- String reference = mUrl.getRef();
- if (reference != null && reference.length() == 96) {
- this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference));
- }
-
- if ((this.message.getEncryption() == Message.ENCRYPTION_OTR
- || this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL)
- && this.file.getKey() == null) {
- this.message.setEncryption(Message.ENCRYPTION_NONE);
- }
- checkFileSize(interactive);
- } catch (MalformedURLException e) {
- this.cancel();
- }
- }
-
- private void checkFileSize(boolean interactive) {
- new Thread(new FileSizeChecker(interactive)).start();
- }
-
- @Override
- public void cancel() {
- this.canceled = true;
- mHttpConnectionManager.finishConnection(this);
- if (message.isFileOrImage()) {
- message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
- } else {
- message.setTransferable(null);
- }
- mXmppConnectionService.updateConversationUi();
- }
-
- private void finish() {
- mXmppConnectionService.getFileBackend().updateMediaScanner(file);
- message.setTransferable(null);
- mHttpConnectionManager.finishConnection(this);
- boolean notify = acceptedAutomatically && !message.isRead();
- if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- notify = message.getConversation().getAccount().getPgpDecryptionService().decrypt(message, notify);
- }
- mXmppConnectionService.updateConversationUi();
- if (notify) {
- mXmppConnectionService.getNotificationService().push(message);
- }
- }
-
- private void changeStatus(int status) {
- this.mStatus = status;
- mXmppConnectionService.updateConversationUi();
- }
-
- private void showToastForException(Exception e) {
- if (e instanceof java.net.UnknownHostException) {
- mXmppConnectionService.showErrorToastInUi(R.string.download_failed_server_not_found);
- } else if (e instanceof java.net.ConnectException) {
- mXmppConnectionService.showErrorToastInUi(R.string.download_failed_could_not_connect);
- } else if (e instanceof FileWriterException) {
- mXmppConnectionService.showErrorToastInUi(R.string.download_failed_could_not_write_file);
- } else if (!(e instanceof CancellationException)) {
- mXmppConnectionService.showErrorToastInUi(R.string.download_failed_file_not_found);
- }
- }
-
- private class FileSizeChecker implements Runnable {
-
- private boolean interactive = false;
-
- public FileSizeChecker(boolean interactive) {
- this.interactive = interactive;
- }
-
- @Override
- public void run() {
- long size;
- try {
- size = retrieveFileSize();
- } catch (Exception e) {
- changeStatus(STATUS_OFFER_CHECK_FILESIZE);
- Log.d(Config.LOGTAG, "io exception in http file size checker: " + e.getMessage());
- if (interactive) {
- showToastForException(e);
- } else {
- HttpDownloadConnection.this.acceptedAutomatically = false;
- HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
- }
- cancel();
- return;
- }
- file.setExpectedSize(size);
- if (mHttpConnectionManager.hasStoragePermission()
- && size <= mHttpConnectionManager.getAutoAcceptFileSize()
- && mXmppConnectionService.isDataSaverDisabled()) {
- HttpDownloadConnection.this.acceptedAutomatically = true;
- new Thread(new FileDownloader(interactive)).start();
- } else {
- changeStatus(STATUS_OFFER);
- HttpDownloadConnection.this.acceptedAutomatically = false;
- HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
- }
- }
-
- private long retrieveFileSize() throws IOException {
- PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_download_"+message.getUuid());
- try {
- wakeLock.acquire();
- Log.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();
- }
- 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());
- if (connection instanceof HttpsURLConnection) {
- mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
- }
- connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
- connection.setReadTimeout(Config.CONNECT_TIMEOUT * 1000);
- connection.connect();
- String contentLength = connection.getHeaderField("Content-Length");
- connection.disconnect();
- if (contentLength == null) {
- throw new IOException("no content-length found in HEAD response");
- }
- wakeLock.release();
- return Long.parseLong(contentLength, 10);
- } catch (IOException e) {
- throw e;
- } catch (NumberFormatException e) {
- throw new IOException();
- }
- }
-
- }
-
- private class FileDownloader implements Runnable {
-
- private boolean interactive = false;
-
- private OutputStream os;
-
- public FileDownloader(boolean interactive) {
- this.interactive = interactive;
- }
-
- @Override
- public void run() {
- try {
- changeStatus(STATUS_DOWNLOADING);
- download();
- updateImageBounds();
- finish();
- } catch (SSLHandshakeException e) {
- changeStatus(STATUS_OFFER);
- } catch (Exception e) {
- if (interactive) {
- showToastForException(e);
- } else {
- HttpDownloadConnection.this.acceptedAutomatically = false;
- HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
- }
- cancel();
- }
- }
-
- 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.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
- connection.setReadTimeout(Config.CONNECT_TIMEOUT * 1000);
- 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;
- byte[] buffer = new byte[1024];
- while ((count = is.read(buffer)) != -1) {
- transmitted += count;
- try {
- os.write(buffer, 0, count);
- } catch (IOException e) {
- throw new FileWriterException();
- }
- updateProgress((int) ((((double) transmitted) / expected) * 100));
- if (canceled) {
- throw new CancellationException();
- }
- }
- try {
- os.flush();
- } catch (IOException e) {
- throw new FileWriterException();
- }
- } catch (CancellationException | IOException e) {
- throw e;
- } finally {
- FileBackend.close(os);
- FileBackend.close(is);
- wakeLock.release();
- }
- }
-
- private void updateImageBounds() {
- message.setType(Message.TYPE_FILE);
- mXmppConnectionService.getFileBackend().updateFileParams(message, mUrl);
- mXmppConnectionService.updateMessage(message);
- }
-
- }
-
- public void updateProgress(int i) {
- this.mProgress = i;
- mXmppConnectionService.updateConversationUi();
- }
-
- @Override
- public int getStatus() {
- return this.mStatus;
- }
-
- @Override
- public long getFileSize() {
- if (this.file != null) {
- return this.file.getExpectedSize();
- } else {
- return 0;
- }
- }
-
- @Override
- public int getProgress() {
- return this.mProgress;
- }
+ private HttpConnectionManager mHttpConnectionManager;
+ private XmppConnectionService mXmppConnectionService;
+
+ private URL mUrl;
+ private Message message;
+ private DownloadableFile file;
+ private int mStatus = Transferable.STATUS_UNKNOWN;
+ private boolean acceptedAutomatically = false;
+ private int mProgress = 0;
+ private boolean mUseTor = false;
+ private boolean canceled = false;
+
+ private final SimpleDateFormat fileDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
+
+ public HttpDownloadConnection(HttpConnectionManager manager) {
+ this.mHttpConnectionManager = manager;
+ this.mXmppConnectionService = manager.getXmppConnectionService();
+ this.mUseTor = mXmppConnectionService.useTorToConnect();
+ }
+
+ @Override
+ public boolean start() {
+ if (mXmppConnectionService.hasInternetConnection()) {
+ if (this.mStatus == STATUS_OFFER_CHECK_FILESIZE) {
+ checkFileSize(true);
+ } else {
+ new Thread(new FileDownloader(true)).start();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void init(Message message) {
+ init(message, false);
+ }
+
+ public void init(Message message, boolean interactive) {
+ this.message = message;
+ this.message.setTransferable(this);
+ try {
+ if (message.hasFileOnRemoteHost()) {
+ mUrl = message.getFileParams().url;
+ } 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)) {
+ 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;
+ } else {
+ extension = lastPart;
+ }
+ String filename = fileDateFormat.format(new Date(message.getTimeSent())) + "_" + message.getUuid().substring(0, 4);
+ message.setRelativeFilePath(filename + "." + extension);
+ this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
+ String reference = mUrl.getRef();
+ if (reference != null && reference.length() == 96) {
+ this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference));
+ }
+
+ if ((this.message.getEncryption() == Message.ENCRYPTION_OTR
+ || this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL)
+ && this.file.getKey() == null) {
+ this.message.setEncryption(Message.ENCRYPTION_NONE);
+ }
+ checkFileSize(interactive);
+ } catch (MalformedURLException e) {
+ this.cancel();
+ }
+ }
+
+ private void checkFileSize(boolean interactive) {
+ new Thread(new FileSizeChecker(interactive)).start();
+ }
+
+ @Override
+ public void cancel() {
+ this.canceled = true;
+ mHttpConnectionManager.finishConnection(this);
+ if (message.isFileOrImage()) {
+ message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
+ } else {
+ message.setTransferable(null);
+ }
+ mXmppConnectionService.updateConversationUi();
+ }
+
+ private void finish() {
+ mXmppConnectionService.getFileBackend().updateMediaScanner(file);
+ message.setTransferable(null);
+ mHttpConnectionManager.finishConnection(this);
+ boolean notify = acceptedAutomatically && !message.isRead();
+ if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ notify = message.getConversation().getAccount().getPgpDecryptionService().decrypt(message, notify);
+ }
+ mXmppConnectionService.updateConversationUi();
+ if (notify) {
+ mXmppConnectionService.getNotificationService().push(message);
+ }
+ }
+
+ private void changeStatus(int status) {
+ this.mStatus = status;
+ mXmppConnectionService.updateConversationUi();
+ }
+
+ private void showToastForException(Exception e) {
+ if (e instanceof java.net.UnknownHostException) {
+ mXmppConnectionService.showErrorToastInUi(R.string.download_failed_server_not_found);
+ } else if (e instanceof java.net.ConnectException) {
+ mXmppConnectionService.showErrorToastInUi(R.string.download_failed_could_not_connect);
+ } else if (e instanceof FileWriterException) {
+ mXmppConnectionService.showErrorToastInUi(R.string.download_failed_could_not_write_file);
+ } else if (!(e instanceof CancellationException)) {
+ mXmppConnectionService.showErrorToastInUi(R.string.download_failed_file_not_found);
+ }
+ }
+
+ private class FileSizeChecker implements Runnable {
+
+ private boolean interactive = false;
+
+ public FileSizeChecker(boolean interactive) {
+ this.interactive = interactive;
+ }
+
+ @Override
+ public void run() {
+ long size;
+ try {
+ size = retrieveFileSize();
+ } catch (Exception e) {
+ changeStatus(STATUS_OFFER_CHECK_FILESIZE);
+ Log.d(Config.LOGTAG, "io exception in http file size checker: " + e.getMessage());
+ if (interactive) {
+ showToastForException(e);
+ } else {
+ HttpDownloadConnection.this.acceptedAutomatically = false;
+ HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
+ }
+ cancel();
+ return;
+ }
+ file.setExpectedSize(size);
+ if (mHttpConnectionManager.hasStoragePermission()
+ && size <= mHttpConnectionManager.getAutoAcceptFileSize()
+ && mXmppConnectionService.isDataSaverDisabled()) {
+ HttpDownloadConnection.this.acceptedAutomatically = true;
+ new Thread(new FileDownloader(interactive)).start();
+ } else {
+ changeStatus(STATUS_OFFER);
+ HttpDownloadConnection.this.acceptedAutomatically = false;
+ HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
+ }
+ }
+
+ private long retrieveFileSize() throws IOException {
+ PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_download_" + message.getUuid());
+ try {
+ wakeLock.acquire();
+ Log.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();
+ }
+ 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());
+ if (connection instanceof HttpsURLConnection) {
+ mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
+ }
+ connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
+ connection.setReadTimeout(Config.CONNECT_TIMEOUT * 1000);
+ connection.connect();
+ String contentLength = connection.getHeaderField("Content-Length");
+ connection.disconnect();
+ if (contentLength == null) {
+ throw new IOException("no content-length found in HEAD response");
+ }
+ wakeLock.release();
+ return Long.parseLong(contentLength, 10);
+ } catch (IOException e) {
+ throw e;
+ } catch (NumberFormatException e) {
+ throw new IOException();
+ }
+ }
+
+ }
+
+ private class FileDownloader implements Runnable {
+
+ private boolean interactive = false;
+
+ private OutputStream os;
+
+ public FileDownloader(boolean interactive) {
+ this.interactive = interactive;
+ }
+
+ @Override
+ public void run() {
+ try {
+ changeStatus(STATUS_DOWNLOADING);
+ download();
+ updateImageBounds();
+ finish();
+ } catch (SSLHandshakeException e) {
+ changeStatus(STATUS_OFFER);
+ } catch (Exception e) {
+ if (interactive) {
+ showToastForException(e);
+ } else {
+ HttpDownloadConnection.this.acceptedAutomatically = false;
+ HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
+ }
+ cancel();
+ }
+ }
+
+ 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.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
+ connection.setReadTimeout(Config.CONNECT_TIMEOUT * 1000);
+ 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;
+ byte[] buffer = new byte[1024];
+ while ((count = is.read(buffer)) != -1) {
+ transmitted += count;
+ try {
+ os.write(buffer, 0, count);
+ } catch (IOException e) {
+ throw new FileWriterException();
+ }
+ updateProgress((int) ((((double) transmitted) / expected) * 100));
+ if (canceled) {
+ throw new CancellationException();
+ }
+ }
+ try {
+ os.flush();
+ } catch (IOException e) {
+ throw new FileWriterException();
+ }
+ } catch (CancellationException | IOException e) {
+ throw e;
+ } finally {
+ FileBackend.close(os);
+ FileBackend.close(is);
+ wakeLock.release();
+ }
+ }
+
+ private void updateImageBounds() {
+ message.setType(Message.TYPE_FILE);
+ mXmppConnectionService.getFileBackend().updateFileParams(message, mUrl);
+ mXmppConnectionService.updateMessage(message);
+ }
+
+ }
+
+ public void updateProgress(int i) {
+ this.mProgress = i;
+ mXmppConnectionService.updateConversationUi();
+ }
+
+ @Override
+ public int getStatus() {
+ return this.mStatus;
+ }
+
+ @Override
+ public long getFileSize() {
+ if (this.file != null) {
+ return this.file.getExpectedSize();
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public int getProgress() {
+ return this.mProgress;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java b/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java
index 95f840d67..afb0b04df 100644
--- a/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java
+++ b/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java
@@ -34,208 +34,208 @@ import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class HttpUploadConnection implements Transferable {
- private HttpConnectionManager mHttpConnectionManager;
- private XmppConnectionService mXmppConnectionService;
-
- private boolean canceled = false;
- private boolean delayed = false;
- private Account account;
- private DownloadableFile file;
- private Message message;
- private String mime;
- private URL mGetUrl;
- private URL mPutUrl;
- private boolean mUseTor = false;
-
- private byte[] key = null;
-
- private long transmitted = 0;
-
- private InputStream mFileInputStream;
-
- public HttpUploadConnection(HttpConnectionManager httpConnectionManager) {
- this.mHttpConnectionManager = httpConnectionManager;
- this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService();
- this.mUseTor = mXmppConnectionService.useTorToConnect();
- }
-
- @Override
- public boolean start() {
- return false;
- }
-
- @Override
- public int getStatus() {
- return STATUS_UPLOADING;
- }
-
- @Override
- public long getFileSize() {
- return file == null ? 0 : file.getExpectedSize();
- }
-
- @Override
- public int getProgress() {
- if (file == null) {
- return 0;
- }
- return (int) ((((double) transmitted) / file.getExpectedSize()) * 100);
- }
-
- @Override
- public void cancel() {
- this.canceled = true;
- }
+ private HttpConnectionManager mHttpConnectionManager;
+ private XmppConnectionService mXmppConnectionService;
+
+ private boolean canceled = false;
+ private boolean delayed = false;
+ private Account account;
+ private DownloadableFile file;
+ private Message message;
+ private String mime;
+ private URL mGetUrl;
+ private URL mPutUrl;
+ private boolean mUseTor = false;
+
+ private byte[] key = null;
+
+ private long transmitted = 0;
+
+ private InputStream mFileInputStream;
+
+ public HttpUploadConnection(HttpConnectionManager httpConnectionManager) {
+ this.mHttpConnectionManager = httpConnectionManager;
+ this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService();
+ this.mUseTor = mXmppConnectionService.useTorToConnect();
+ }
+
+ @Override
+ public boolean start() {
+ return false;
+ }
+
+ @Override
+ public int getStatus() {
+ return STATUS_UPLOADING;
+ }
+
+ @Override
+ public long getFileSize() {
+ return file == null ? 0 : file.getExpectedSize();
+ }
+
+ @Override
+ public int getProgress() {
+ if (file == null) {
+ return 0;
+ }
+ return (int) ((((double) transmitted) / file.getExpectedSize()) * 100);
+ }
+
+ @Override
+ public void cancel() {
+ this.canceled = true;
+ }
private void fail(String errorMessage) {
- mHttpConnectionManager.finishUploadConnection(this);
- message.setTransferable(null);
+ mHttpConnectionManager.finishUploadConnection(this);
+ message.setTransferable(null);
mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED, errorMessage);
FileBackend.close(mFileInputStream);
- }
-
- public void init(Message message, boolean delay) {
- this.message = message;
- this.account = message.getConversation().getAccount();
- this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
- this.mime = this.file.getMimeType();
- this.delayed = delay;
- if (Config.ENCRYPT_ON_HTTP_UPLOADED
- || message.getEncryption() == Message.ENCRYPTION_AXOLOTL
- || message.getEncryption() == Message.ENCRYPTION_OTR) {
- this.key = new byte[48];
- mXmppConnectionService.getRNG().nextBytes(this.key);
- this.file.setKeyAndIv(this.key);
- }
- Pair<InputStream,Integer> pair;
- try {
- pair = AbstractConnectionManager.createInputStream(file, true);
- } catch (FileNotFoundException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not find file to upload - "+e.getMessage());
- fail(e.getMessage());
- return;
- }
- if (pair != null) {
- this.file.setExpectedSize(pair.second);
- this.mFileInputStream = pair.first;
- }
- Jid host = account.getXmppConnection().findDiscoItemByFeature(Xmlns.HTTP_UPLOAD);
- IqPacket request = mXmppConnectionService.getIqGenerator().requestHttpUploadSlot(host,file,mime);
- mXmppConnectionService.sendIqPacket(account, request, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- Element slot = packet.findChild("slot",Xmlns.HTTP_UPLOAD);
- if (slot != null) {
- try {
- mGetUrl = new URL(slot.findChildContent("get"));
- mPutUrl = new URL(slot.findChildContent("put"));
- if (!canceled) {
- new Thread(new FileUploader()).start();
- }
- return;
- } catch (MalformedURLException e) {
- //fall through
- }
- }
- }
- Log.d(Config.LOGTAG,account.getJid().toString()+": invalid response to slot request "+packet);
+ }
+
+ public void init(Message message, boolean delay) {
+ this.message = message;
+ this.account = message.getConversation().getAccount();
+ this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
+ this.mime = this.file.getMimeType();
+ this.delayed = delay;
+ if (Config.ENCRYPT_ON_HTTP_UPLOADED
+ || message.getEncryption() == Message.ENCRYPTION_AXOLOTL
+ || message.getEncryption() == Message.ENCRYPTION_OTR) {
+ this.key = new byte[48];
+ mXmppConnectionService.getRNG().nextBytes(this.key);
+ this.file.setKeyAndIv(this.key);
+ }
+ Pair<InputStream, Integer> pair;
+ try {
+ pair = AbstractConnectionManager.createInputStream(file, true);
+ } catch (FileNotFoundException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not find file to upload - " + e.getMessage());
+ fail(e.getMessage());
+ return;
+ }
+ if (pair != null) {
+ this.file.setExpectedSize(pair.second);
+ this.mFileInputStream = pair.first;
+ }
+ Jid host = account.getXmppConnection().findDiscoItemByFeature(Xmlns.HTTP_UPLOAD);
+ IqPacket request = mXmppConnectionService.getIqGenerator().requestHttpUploadSlot(host, file, mime);
+ mXmppConnectionService.sendIqPacket(account, request, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ Element slot = packet.findChild("slot", Xmlns.HTTP_UPLOAD);
+ if (slot != null) {
+ try {
+ mGetUrl = new URL(slot.findChildContent("get"));
+ mPutUrl = new URL(slot.findChildContent("put"));
+ if (!canceled) {
+ new Thread(new FileUploader()).start();
+ }
+ return;
+ } catch (MalformedURLException e) {
+ //fall through
+ }
+ }
+ }
+ Log.d(Config.LOGTAG, account.getJid().toString() + ": invalid response to slot request " + packet);
fail(IqParser.extractErrorMessage(packet));
- }
- });
- message.setTransferable(this);
- mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
- }
-
- private class FileUploader implements Runnable {
-
- @Override
- public void run() {
- this.upload();
- }
-
- private void upload() {
- OutputStream os = 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) {
- 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.setDoOutput(true);
+ }
+ });
+ message.setTransferable(this);
+ mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
+ }
+
+ private class FileUploader implements Runnable {
+
+ @Override
+ public void run() {
+ this.upload();
+ }
+
+ private void upload() {
+ OutputStream os = 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) {
+ 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.setDoOutput(true);
connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
connection.setReadTimeout(Config.CONNECT_TIMEOUT * 1000);
- connection.connect();
- os = connection.getOutputStream();
- transmitted = 0;
- int count;
- byte[] buffer = new byte[4096];
- while (((count = mFileInputStream.read(buffer)) != -1) && !canceled) {
- transmitted += count;
- os.write(buffer, 0, count);
- mXmppConnectionService.updateConversationUi();
- }
- os.flush();
- os.close();
- mFileInputStream.close();
- int code = connection.getResponseCode();
- if (code == 200 || code == 201) {
- Log.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);
- message.setTransferable(null);
- message.setCounterpart(message.getConversation().getJid().toBareJid());
- if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- mXmppConnectionService.getPgpEngine().encrypt(message, new UiCallback<Message>() {
- @Override
- public void success(Message message) {
- mXmppConnectionService.resendMessage(message,delayed);
- }
-
- @Override
- public void error(int errorCode, Message object) {
- Log.d(Config.LOGTAG,"pgp encryption failed");
+ connection.connect();
+ os = connection.getOutputStream();
+ transmitted = 0;
+ int count;
+ byte[] buffer = new byte[4096];
+ while (((count = mFileInputStream.read(buffer)) != -1) && !canceled) {
+ transmitted += count;
+ os.write(buffer, 0, count);
+ mXmppConnectionService.updateConversationUi();
+ }
+ os.flush();
+ os.close();
+ mFileInputStream.close();
+ int code = connection.getResponseCode();
+ if (code == 200 || code == 201) {
+ Log.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);
+ message.setTransferable(null);
+ message.setCounterpart(message.getConversation().getJid().toBareJid());
+ if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ mXmppConnectionService.getPgpEngine().encrypt(message, new UiCallback<Message>() {
+ @Override
+ public void success(Message message) {
+ mXmppConnectionService.resendMessage(message, delayed);
+ }
+
+ @Override
+ public void error(int errorCode, Message object) {
+ Log.d(Config.LOGTAG, "pgp encryption failed");
fail("pgp encryption failed");
- }
+ }
- @Override
- public void userInputRequried(PendingIntent pi, Message object) {
+ @Override
+ public void userInputRequried(PendingIntent pi, Message object) {
fail("pgp encryption failed");
- }
- });
- } else {
- mXmppConnectionService.resendMessage(message, delayed);
- }
- } else {
- Log.d(Config.LOGTAG,"http upload failed because response code was "+code);
- fail("http upload failed because response code was "+code);
- }
- } catch (IOException e) {
- e.printStackTrace();
- Log.d(Config.LOGTAG,"http upload failed "+e.getMessage());
- fail(e.getMessage());
- } finally {
- FileBackend.close(mFileInputStream);
- FileBackend.close(os);
- if (connection != null) {
- connection.disconnect();
- }
- wakeLock.release();
- }
- }
- }
+ }
+ });
+ } else {
+ mXmppConnectionService.resendMessage(message, delayed);
+ }
+ } else {
+ Log.d(Config.LOGTAG, "http upload failed because response code was " + code);
+ fail("http upload failed because response code was " + code);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ Log.d(Config.LOGTAG, "http upload failed " + e.getMessage());
+ fail(e.getMessage());
+ } finally {
+ FileBackend.close(mFileInputStream);
+ FileBackend.close(os);
+ if (connection != null) {
+ connection.disconnect();
+ }
+ wakeLock.release();
+ }
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/parser/AbstractParser.java b/src/main/java/de/pixart/messenger/parser/AbstractParser.java
index 3ad20783b..21da7fe67 100644
--- a/src/main/java/de/pixart/messenger/parser/AbstractParser.java
+++ b/src/main/java/de/pixart/messenger/parser/AbstractParser.java
@@ -15,88 +15,88 @@ import de.pixart.messenger.xmpp.jid.Jid;
public abstract class AbstractParser {
- protected XmppConnectionService mXmppConnectionService;
+ protected XmppConnectionService mXmppConnectionService;
- protected AbstractParser(XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
+ protected AbstractParser(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
- public static Long parseTimestamp(Element element, Long d) {
- Element delay = element.findChild("delay","urn:xmpp:delay");
- if (delay != null) {
- String stamp = delay.getAttribute("stamp");
- if (stamp != null) {
- try {
- return AbstractParser.parseTimestamp(delay.getAttribute("stamp"));
- } catch (ParseException e) {
- return d;
- }
- }
- }
- return d;
- }
+ public static Long parseTimestamp(Element element, Long d) {
+ Element delay = element.findChild("delay", "urn:xmpp:delay");
+ if (delay != null) {
+ String stamp = delay.getAttribute("stamp");
+ if (stamp != null) {
+ try {
+ return AbstractParser.parseTimestamp(delay.getAttribute("stamp"));
+ } catch (ParseException e) {
+ return d;
+ }
+ }
+ }
+ return d;
+ }
- public static long parseTimestamp(Element element) {
- return parseTimestamp(element, System.currentTimeMillis());
- }
+ public static long parseTimestamp(Element element) {
+ return parseTimestamp(element, System.currentTimeMillis());
+ }
- public static long parseTimestamp(String timestamp) throws ParseException {
- timestamp = timestamp.replace("Z", "+0000");
- SimpleDateFormat dateFormat;
- long ms;
- if (timestamp.charAt(19) == '.' && timestamp.length() >= 25) {
- String millis = timestamp.substring(19,timestamp.length() - 5);
- try {
- double fractions = Double.parseDouble("0" + millis);
- ms = Math.round(1000 * fractions);
- } catch (NumberFormatException e) {
- ms = 0;
- }
- } else {
- ms = 0;
- }
- 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 Math.min(dateFormat.parse(timestamp).getTime()+ms, System.currentTimeMillis());
- }
+ public static long parseTimestamp(String timestamp) throws ParseException {
+ timestamp = timestamp.replace("Z", "+0000");
+ SimpleDateFormat dateFormat;
+ long ms;
+ if (timestamp.charAt(19) == '.' && timestamp.length() >= 25) {
+ String millis = timestamp.substring(19, timestamp.length() - 5);
+ try {
+ double fractions = Double.parseDouble("0" + millis);
+ ms = Math.round(1000 * fractions);
+ } catch (NumberFormatException e) {
+ ms = 0;
+ }
+ } else {
+ ms = 0;
+ }
+ 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 Math.min(dateFormat.parse(timestamp).getTime() + ms, System.currentTimeMillis());
+ }
- protected void updateLastseen(final Account account, final Jid from) {
- final Contact contact = account.getRoster().getContact(from);
- contact.setLastResource(from.isBareJid() ? "" : from.getResourcepart());
- }
+ protected void updateLastseen(final Account account, final Jid from) {
+ final Contact contact = account.getRoster().getContact(from);
+ contact.setLastResource(from.isBareJid() ? "" : from.getResourcepart());
+ }
- protected String avatarData(Element items) {
- Element item = items.findChild("item");
- if (item == null) {
- return null;
- }
- return item.findChildContent("data", "urn:xmpp:avatar:data");
- }
+ protected String avatarData(Element items) {
+ Element item = items.findChild("item");
+ if (item == null) {
+ return null;
+ }
+ return item.findChildContent("data", "urn:xmpp:avatar:data");
+ }
- public static MucOptions.User parseItem(Conversation conference, Element item) {
+ public static MucOptions.User parseItem(Conversation conference, Element item) {
return parseItem(conference, item, null);
}
public static MucOptions.User parseItem(Conversation conference, Element item, Jid fullJid) {
- final String local = conference.getJid().getLocalpart();
- final String domain = conference.getJid().getDomainpart();
- String affiliation = item.getAttribute("affiliation");
- String role = item.getAttribute("role");
- String nick = item.getAttribute("nick");
+ final String local = conference.getJid().getLocalpart();
+ final String domain = conference.getJid().getDomainpart();
+ String affiliation = item.getAttribute("affiliation");
+ String role = item.getAttribute("role");
+ String nick = item.getAttribute("nick");
if (nick != null && fullJid == null) {
try {
fullJid = Jid.fromParts(local, domain, nick);
} catch (InvalidJidException e) {
fullJid = null;
}
- }
- Jid realJid = item.getAttributeAsJid("jid");
+ }
+ Jid realJid = item.getAttributeAsJid("jid");
MucOptions.User user = new MucOptions.User(conference.getMucOptions(), fullJid);
- user.setRealJid(realJid);
- user.setAffiliation(affiliation);
- user.setRole(role);
- return user;
- }
+ user.setRealJid(realJid);
+ user.setAffiliation(affiliation);
+ user.setRole(role);
+ return user;
+ }
public static String extractErrorMessage(Element packet) {
final Element error = packet.findChild("error");
@@ -105,7 +105,7 @@ public abstract class AbstractParser {
if (text != null && !text.trim().isEmpty()) {
return text;
} else {
- return error.getChildren().get(0).getName().replace("-"," ");
+ return error.getChildren().get(0).getName().replace("-", " ");
}
} else {
return null;
diff --git a/src/main/java/de/pixart/messenger/parser/IqParser.java b/src/main/java/de/pixart/messenger/parser/IqParser.java
index 5617973d6..25d946c1d 100644
--- a/src/main/java/de/pixart/messenger/parser/IqParser.java
+++ b/src/main/java/de/pixart/messenger/parser/IqParser.java
@@ -36,351 +36,351 @@ import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class IqParser extends AbstractParser implements OnIqPacketReceived {
- public IqParser(final XmppConnectionService service) {
- super(service);
- }
+ public IqParser(final XmppConnectionService service) {
+ super(service);
+ }
- private void rosterItems(final Account account, final Element query) {
- final String version = query.getAttribute("ver");
- if (version != null) {
- account.getRoster().setVersion(version);
- }
- for (final Element item : query.getChildren()) {
- if (item.getName().equals("item")) {
- final Jid jid = item.getAttributeAsJid("jid");
- if (jid == null) {
- continue;
- }
- final String name = item.getAttribute("name");
- final String subscription = item.getAttribute("subscription");
- final Contact contact = account.getRoster().getContact(jid);
- boolean bothPre = contact.getOption(Contact.Options.TO) && contact.getOption(Contact.Options.FROM);
- if (!contact.getOption(Contact.Options.DIRTY_PUSH)) {
- contact.setServerName(name);
- contact.parseGroupsFromElement(item);
- }
- if (subscription != null) {
- if (subscription.equals("remove")) {
- contact.resetOption(Contact.Options.IN_ROSTER);
- contact.resetOption(Contact.Options.DIRTY_DELETE);
- contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
- } else {
- contact.setOption(Contact.Options.IN_ROSTER);
- contact.resetOption(Contact.Options.DIRTY_PUSH);
- contact.parseSubscriptionFromElement(item);
- }
- }
- boolean both = contact.getOption(Contact.Options.TO) && contact.getOption(Contact.Options.FROM);
- if ((both != bothPre) && both) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": gained mutual presence subscription with "+contact.getJid());
- AxolotlService axolotlService = account.getAxolotlService();
- if (axolotlService != null) {
- axolotlService.clearErrorsInFetchStatusMap(contact.getJid());
- }
- }
- mXmppConnectionService.getAvatarService().clear(contact);
- }
- }
- mXmppConnectionService.updateConversationUi();
- mXmppConnectionService.updateRosterUi();
- }
+ private void rosterItems(final Account account, final Element query) {
+ final String version = query.getAttribute("ver");
+ if (version != null) {
+ account.getRoster().setVersion(version);
+ }
+ for (final Element item : query.getChildren()) {
+ if (item.getName().equals("item")) {
+ final Jid jid = item.getAttributeAsJid("jid");
+ if (jid == null) {
+ continue;
+ }
+ final String name = item.getAttribute("name");
+ final String subscription = item.getAttribute("subscription");
+ final Contact contact = account.getRoster().getContact(jid);
+ boolean bothPre = contact.getOption(Contact.Options.TO) && contact.getOption(Contact.Options.FROM);
+ if (!contact.getOption(Contact.Options.DIRTY_PUSH)) {
+ contact.setServerName(name);
+ contact.parseGroupsFromElement(item);
+ }
+ if (subscription != null) {
+ if (subscription.equals("remove")) {
+ contact.resetOption(Contact.Options.IN_ROSTER);
+ contact.resetOption(Contact.Options.DIRTY_DELETE);
+ contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
+ } else {
+ contact.setOption(Contact.Options.IN_ROSTER);
+ contact.resetOption(Contact.Options.DIRTY_PUSH);
+ contact.parseSubscriptionFromElement(item);
+ }
+ }
+ boolean both = contact.getOption(Contact.Options.TO) && contact.getOption(Contact.Options.FROM);
+ if ((both != bothPre) && both) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": gained mutual presence subscription with " + contact.getJid());
+ AxolotlService axolotlService = account.getAxolotlService();
+ if (axolotlService != null) {
+ axolotlService.clearErrorsInFetchStatusMap(contact.getJid());
+ }
+ }
+ mXmppConnectionService.getAvatarService().clear(contact);
+ }
+ }
+ mXmppConnectionService.updateConversationUi();
+ mXmppConnectionService.updateRosterUi();
+ }
- public String avatarData(final IqPacket packet) {
- final Element pubsub = packet.findChild("pubsub",
- "http://jabber.org/protocol/pubsub");
- if (pubsub == null) {
- return null;
- }
- final Element items = pubsub.findChild("items");
- if (items == null) {
- return null;
- }
- return super.avatarData(items);
- }
+ public String avatarData(final IqPacket packet) {
+ final Element pubsub = packet.findChild("pubsub",
+ "http://jabber.org/protocol/pubsub");
+ if (pubsub == null) {
+ return null;
+ }
+ final Element items = pubsub.findChild("items");
+ if (items == null) {
+ return null;
+ }
+ return super.avatarData(items);
+ }
- public Element getItem(final IqPacket packet) {
- final Element pubsub = packet.findChild("pubsub",
- "http://jabber.org/protocol/pubsub");
- if (pubsub == null) {
- return null;
- }
- final Element items = pubsub.findChild("items");
- if (items == null) {
- return null;
- }
- return items.findChild("item");
- }
+ public Element getItem(final IqPacket packet) {
+ final Element pubsub = packet.findChild("pubsub",
+ "http://jabber.org/protocol/pubsub");
+ if (pubsub == null) {
+ return null;
+ }
+ final Element items = pubsub.findChild("items");
+ if (items == null) {
+ return null;
+ }
+ return items.findChild("item");
+ }
- @NonNull
- public Set<Integer> deviceIds(final Element item) {
- Set<Integer> deviceIds = new HashSet<>();
- if (item != null) {
- final Element list = item.findChild("list");
- if (list != null) {
- for (Element device : list.getChildren()) {
- if (!device.getName().equals("device")) {
- continue;
- }
- try {
- Integer id = Integer.valueOf(device.getAttribute("id"));
- deviceIds.add(id);
- } catch (NumberFormatException e) {
- Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"Encountered invalid <device> node in PEP ("+e.getMessage()+"):" + device.toString()+ ", skipping...");
- continue;
- }
- }
- }
- }
- return deviceIds;
- }
+ @NonNull
+ public Set<Integer> deviceIds(final Element item) {
+ Set<Integer> deviceIds = new HashSet<>();
+ if (item != null) {
+ final Element list = item.findChild("list");
+ if (list != null) {
+ for (Element device : list.getChildren()) {
+ if (!device.getName().equals("device")) {
+ continue;
+ }
+ try {
+ Integer id = Integer.valueOf(device.getAttribute("id"));
+ deviceIds.add(id);
+ } catch (NumberFormatException e) {
+ Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Encountered invalid <device> node in PEP (" + e.getMessage() + "):" + device.toString() + ", skipping...");
+ continue;
+ }
+ }
+ }
+ }
+ return deviceIds;
+ }
- public Integer signedPreKeyId(final Element bundle) {
- final Element signedPreKeyPublic = bundle.findChild("signedPreKeyPublic");
- if(signedPreKeyPublic == null) {
- return null;
- }
+ public Integer signedPreKeyId(final Element bundle) {
+ final Element signedPreKeyPublic = bundle.findChild("signedPreKeyPublic");
+ if (signedPreKeyPublic == null) {
+ return null;
+ }
try {
return Integer.valueOf(signedPreKeyPublic.getAttribute("signedPreKeyId"));
} catch (NumberFormatException e) {
return null;
}
- }
+ }
- public ECPublicKey signedPreKeyPublic(final Element bundle) {
- ECPublicKey publicKey = null;
- final Element signedPreKeyPublic = bundle.findChild("signedPreKeyPublic");
- if(signedPreKeyPublic == null) {
- return null;
- }
- try {
- publicKey = Curve.decodePoint(Base64.decode(signedPreKeyPublic.getContent(),Base64.DEFAULT), 0);
- } catch (Throwable e) {
- Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"Invalid signedPreKeyPublic in PEP: " + e.getMessage());
- }
- return publicKey;
- }
+ public ECPublicKey signedPreKeyPublic(final Element bundle) {
+ ECPublicKey publicKey = null;
+ final Element signedPreKeyPublic = bundle.findChild("signedPreKeyPublic");
+ if (signedPreKeyPublic == null) {
+ return null;
+ }
+ try {
+ publicKey = Curve.decodePoint(Base64.decode(signedPreKeyPublic.getContent(), Base64.DEFAULT), 0);
+ } catch (Throwable e) {
+ Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Invalid signedPreKeyPublic in PEP: " + e.getMessage());
+ }
+ return publicKey;
+ }
- public byte[] signedPreKeySignature(final Element bundle) {
- final Element signedPreKeySignature = bundle.findChild("signedPreKeySignature");
- if(signedPreKeySignature == null) {
- return null;
- }
- try {
- return Base64.decode(signedPreKeySignature.getContent(), Base64.DEFAULT);
- } catch (Throwable e) {
- Log.e(Config.LOGTAG,AxolotlService.LOGPREFIX+" : Invalid base64 in signedPreKeySignature");
- return null;
- }
- }
+ public byte[] signedPreKeySignature(final Element bundle) {
+ final Element signedPreKeySignature = bundle.findChild("signedPreKeySignature");
+ if (signedPreKeySignature == null) {
+ return null;
+ }
+ try {
+ return Base64.decode(signedPreKeySignature.getContent(), Base64.DEFAULT);
+ } catch (Throwable e) {
+ Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : Invalid base64 in signedPreKeySignature");
+ return null;
+ }
+ }
- public IdentityKey identityKey(final Element bundle) {
- IdentityKey identityKey = null;
- final Element identityKeyElement = bundle.findChild("identityKey");
- if(identityKeyElement == null) {
- return null;
- }
- 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());
- }
- return identityKey;
- }
+ public IdentityKey identityKey(final Element bundle) {
+ IdentityKey identityKey = null;
+ final Element identityKeyElement = bundle.findChild("identityKey");
+ if (identityKeyElement == null) {
+ return null;
+ }
+ 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());
+ }
+ return identityKey;
+ }
- public Map<Integer, ECPublicKey> preKeyPublics(final IqPacket packet) {
- Map<Integer, ECPublicKey> preKeyRecords = new HashMap<>();
- Element item = getItem(packet);
- if (item == null) {
- Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"Couldn't find <item> in bundle IQ packet: " + packet);
- return null;
- }
- final Element bundleElement = item.findChild("bundle");
- if(bundleElement == null) {
- return null;
- }
- final Element prekeysElement = bundleElement.findChild("prekeys");
- if(prekeysElement == null) {
- Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"Couldn't find <prekeys> in bundle IQ packet: " + packet);
- return null;
- }
- for(Element preKeyPublicElement : prekeysElement.getChildren()) {
- if(!preKeyPublicElement.getName().equals("preKeyPublic")){
- Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"Encountered unexpected tag in prekeys list: " + preKeyPublicElement);
- continue;
- }
- Integer preKeyId = null;
- try {
- preKeyId = Integer.valueOf(preKeyPublicElement.getAttribute("preKeyId"));
- final ECPublicKey preKeyPublic = Curve.decodePoint(Base64.decode(preKeyPublicElement.getContent(), Base64.DEFAULT), 0);
- preKeyRecords.put(preKeyId, preKeyPublic);
- } catch (NumberFormatException e) {
- Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"could not parse preKeyId from preKey "+preKeyPublicElement.toString());
- } catch (Throwable e) {
- Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"Invalid preKeyPublic (ID="+preKeyId+") in PEP: "+ e.getMessage()+", skipping...");
- }
- }
- return preKeyRecords;
- }
+ public Map<Integer, ECPublicKey> preKeyPublics(final IqPacket packet) {
+ Map<Integer, ECPublicKey> preKeyRecords = new HashMap<>();
+ Element item = getItem(packet);
+ if (item == null) {
+ Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Couldn't find <item> in bundle IQ packet: " + packet);
+ return null;
+ }
+ final Element bundleElement = item.findChild("bundle");
+ if (bundleElement == null) {
+ return null;
+ }
+ final Element prekeysElement = bundleElement.findChild("prekeys");
+ if (prekeysElement == null) {
+ Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Couldn't find <prekeys> in bundle IQ packet: " + packet);
+ return null;
+ }
+ for (Element preKeyPublicElement : prekeysElement.getChildren()) {
+ if (!preKeyPublicElement.getName().equals("preKeyPublic")) {
+ Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Encountered unexpected tag in prekeys list: " + preKeyPublicElement);
+ continue;
+ }
+ Integer preKeyId = null;
+ try {
+ preKeyId = Integer.valueOf(preKeyPublicElement.getAttribute("preKeyId"));
+ final ECPublicKey preKeyPublic = Curve.decodePoint(Base64.decode(preKeyPublicElement.getContent(), Base64.DEFAULT), 0);
+ preKeyRecords.put(preKeyId, preKeyPublic);
+ } catch (NumberFormatException e) {
+ Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "could not parse preKeyId from preKey " + preKeyPublicElement.toString());
+ } catch (Throwable e) {
+ Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Invalid preKeyPublic (ID=" + preKeyId + ") in PEP: " + e.getMessage() + ", skipping...");
+ }
+ }
+ return preKeyRecords;
+ }
- public Pair<X509Certificate[],byte[]> verification(final IqPacket packet) {
- Element item = getItem(packet);
- 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) {
- List<Element> certElements = chain.getChildren();
- X509Certificate[] certificates = new X509Certificate[certElements.size()];
- try {
- CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
- int i = 0;
- for(Element cert : certElements) {
- certificates[i] = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(Base64.decode(cert.getContent(),Base64.DEFAULT)));
- ++i;
- }
- return new Pair<>(certificates,Base64.decode(signature.getContent(),Base64.DEFAULT));
- } catch (CertificateException e) {
- return null;
- }
- } else {
- return null;
- }
- }
+ public Pair<X509Certificate[], byte[]> verification(final IqPacket packet) {
+ Element item = getItem(packet);
+ 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) {
+ List<Element> certElements = chain.getChildren();
+ X509Certificate[] certificates = new X509Certificate[certElements.size()];
+ try {
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+ int i = 0;
+ for (Element cert : certElements) {
+ certificates[i] = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(Base64.decode(cert.getContent(), Base64.DEFAULT)));
+ ++i;
+ }
+ return new Pair<>(certificates, Base64.decode(signature.getContent(), Base64.DEFAULT));
+ } catch (CertificateException e) {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
- public PreKeyBundle bundle(final IqPacket bundle) {
- Element bundleItem = getItem(bundle);
- if(bundleItem == null) {
- return null;
- }
- final Element bundleElement = bundleItem.findChild("bundle");
- if(bundleElement == null) {
- return null;
- }
- ECPublicKey signedPreKeyPublic = signedPreKeyPublic(bundleElement);
- Integer signedPreKeyId = signedPreKeyId(bundleElement);
- byte[] signedPreKeySignature = signedPreKeySignature(bundleElement);
- IdentityKey identityKey = identityKey(bundleElement);
- if(signedPreKeyId == null || signedPreKeyPublic == null || identityKey == null) {
- return null;
- }
+ public PreKeyBundle bundle(final IqPacket bundle) {
+ Element bundleItem = getItem(bundle);
+ if (bundleItem == null) {
+ return null;
+ }
+ final Element bundleElement = bundleItem.findChild("bundle");
+ if (bundleElement == null) {
+ return null;
+ }
+ ECPublicKey signedPreKeyPublic = signedPreKeyPublic(bundleElement);
+ Integer signedPreKeyId = signedPreKeyId(bundleElement);
+ byte[] signedPreKeySignature = signedPreKeySignature(bundleElement);
+ IdentityKey identityKey = identityKey(bundleElement);
+ if (signedPreKeyId == null || signedPreKeyPublic == null || identityKey == null) {
+ return null;
+ }
- return new PreKeyBundle(0, 0, 0, null,
- signedPreKeyId, signedPreKeyPublic, signedPreKeySignature, identityKey);
- }
+ return new PreKeyBundle(0, 0, 0, null,
+ signedPreKeyId, signedPreKeyPublic, signedPreKeySignature, identityKey);
+ }
- public List<PreKeyBundle> preKeys(final IqPacket preKeys) {
- List<PreKeyBundle> bundles = new ArrayList<>();
- Map<Integer, ECPublicKey> preKeyPublics = preKeyPublics(preKeys);
- if ( preKeyPublics != null) {
- for (Integer preKeyId : preKeyPublics.keySet()) {
- ECPublicKey preKeyPublic = preKeyPublics.get(preKeyId);
- bundles.add(new PreKeyBundle(0, 0, preKeyId, preKeyPublic,
- 0, null, null, null));
- }
- }
+ public List<PreKeyBundle> preKeys(final IqPacket preKeys) {
+ List<PreKeyBundle> bundles = new ArrayList<>();
+ Map<Integer, ECPublicKey> preKeyPublics = preKeyPublics(preKeys);
+ if (preKeyPublics != null) {
+ for (Integer preKeyId : preKeyPublics.keySet()) {
+ ECPublicKey preKeyPublic = preKeyPublics.get(preKeyId);
+ bundles.add(new PreKeyBundle(0, 0, preKeyId, preKeyPublic,
+ 0, null, null, null));
+ }
+ }
- return bundles;
- }
+ return bundles;
+ }
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- final boolean isGet = packet.getType() == IqPacket.TYPE.GET;
- if (packet.getType() == IqPacket.TYPE.ERROR || packet.getType() == IqPacket.TYPE.TIMEOUT) {
- return;
- } else if (packet.hasChild("query", Xmlns.ROSTER) && packet.fromServer(account)) {
- final Element query = packet.findChild("query");
- // If this is in response to a query for the whole roster:
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- account.getRoster().markAllAsNotInRoster();
- }
- this.rosterItems(account, query);
- } 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");
- final Element blocklist = packet.findChild("blocklist", Xmlns.BLOCKING);
- final Element block = packet.findChild("block", Xmlns.BLOCKING);
- final Collection<Element> items = blocklist != null ? blocklist.getChildren() :
- (block != null ? block.getChildren() : null);
- // If this is a response to a blocklist query, clear the block list and replace with the new one.
- // Otherwise, just update the existing blocklist.
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- account.clearBlocklist();
- account.getXmppConnection().getFeatures().setBlockListRequested(true);
- }
- if (items != null) {
- final Collection<Jid> jids = new ArrayList<>(items.size());
- // Create a collection of Jids from the packet
- for (final Element item : items) {
- if (item.getName().equals("item")) {
- final Jid jid = item.getAttributeAsJid("jid");
- if (jid != null) {
- jids.add(jid);
- }
- }
- }
- account.getBlocklist().addAll(jids);
- }
- // Update the UI
- mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
- if (packet.getType() == IqPacket.TYPE.SET) {
- final IqPacket response = packet.generateResponse(IqPacket.TYPE.RESULT);
- mXmppConnectionService.sendIqPacket(account, response, null);
- }
- } else if (packet.hasChild("unblock", Xmlns.BLOCKING) &&
- packet.fromServer(account) && packet.getType() == IqPacket.TYPE.SET) {
- Log.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
- account.getBlocklist().clear();
- } else {
- final Collection<Jid> jids = new ArrayList<>(items.size());
- for (final Element item : items) {
- if (item.getName().equals("item")) {
- final Jid jid = item.getAttributeAsJid("jid");
- if (jid != null) {
- jids.add(jid);
- }
- }
- }
- account.getBlocklist().removeAll(jids);
- }
- mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
- final IqPacket response = packet.generateResponse(IqPacket.TYPE.RESULT);
- mXmppConnectionService.sendIqPacket(account, response, null);
- } else if (packet.hasChild("open", "http://jabber.org/protocol/ibb")
- || packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
- mXmppConnectionService.getJingleConnectionManager()
- .deliverIbbPacket(account, packet);
- } else if (packet.hasChild("query", "http://jabber.org/protocol/disco#info")) {
- final IqPacket response = mXmppConnectionService.getIqGenerator().discoResponse(packet);
- mXmppConnectionService.sendIqPacket(account, response, null);
- } else if (packet.hasChild("query","jabber:iq:version") && isGet) {
- final IqPacket response = mXmppConnectionService.getIqGenerator().versionResponse(packet);
- mXmppConnectionService.sendIqPacket(account,response,null);
- } else if (packet.hasChild("ping", "urn:xmpp:ping") && isGet) {
- final IqPacket response = packet.generateResponse(IqPacket.TYPE.RESULT);
- mXmppConnectionService.sendIqPacket(account, response, null);
- } else if (packet.hasChild("time","urn:xmpp:time") && isGet) {
- final IqPacket response;
- if (mXmppConnectionService.useTorToConnect()) {
- response = packet.generateResponse(IqPacket.TYPE.ERROR);
- final Element error = response.addChild("error");
- error.setAttribute("type","cancel");
- error.addChild("not-allowed","urn:ietf:params:xml:ns:xmpp-stanzas");
- } else {
- response = mXmppConnectionService.getIqGenerator().entityTimeResponse(packet);
- }
- mXmppConnectionService.sendIqPacket(account,response, null);
- } else {
- if (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET) {
- final IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR);
- final Element error = response.addChild("error");
- error.setAttribute("type", "cancel");
- error.addChild("feature-not-implemented","urn:ietf:params:xml:ns:xmpp-stanzas");
- account.getXmppConnection().sendIqPacket(response, null);
- }
- }
- }
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ final boolean isGet = packet.getType() == IqPacket.TYPE.GET;
+ if (packet.getType() == IqPacket.TYPE.ERROR || packet.getType() == IqPacket.TYPE.TIMEOUT) {
+ return;
+ } else if (packet.hasChild("query", Xmlns.ROSTER) && packet.fromServer(account)) {
+ final Element query = packet.findChild("query");
+ // If this is in response to a query for the whole roster:
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ account.getRoster().markAllAsNotInRoster();
+ }
+ this.rosterItems(account, query);
+ } 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");
+ final Element blocklist = packet.findChild("blocklist", Xmlns.BLOCKING);
+ final Element block = packet.findChild("block", Xmlns.BLOCKING);
+ final Collection<Element> items = blocklist != null ? blocklist.getChildren() :
+ (block != null ? block.getChildren() : null);
+ // If this is a response to a blocklist query, clear the block list and replace with the new one.
+ // Otherwise, just update the existing blocklist.
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ account.clearBlocklist();
+ account.getXmppConnection().getFeatures().setBlockListRequested(true);
+ }
+ if (items != null) {
+ final Collection<Jid> jids = new ArrayList<>(items.size());
+ // Create a collection of Jids from the packet
+ for (final Element item : items) {
+ if (item.getName().equals("item")) {
+ final Jid jid = item.getAttributeAsJid("jid");
+ if (jid != null) {
+ jids.add(jid);
+ }
+ }
+ }
+ account.getBlocklist().addAll(jids);
+ }
+ // Update the UI
+ mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
+ if (packet.getType() == IqPacket.TYPE.SET) {
+ final IqPacket response = packet.generateResponse(IqPacket.TYPE.RESULT);
+ mXmppConnectionService.sendIqPacket(account, response, null);
+ }
+ } else if (packet.hasChild("unblock", Xmlns.BLOCKING) &&
+ packet.fromServer(account) && packet.getType() == IqPacket.TYPE.SET) {
+ Log.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
+ account.getBlocklist().clear();
+ } else {
+ final Collection<Jid> jids = new ArrayList<>(items.size());
+ for (final Element item : items) {
+ if (item.getName().equals("item")) {
+ final Jid jid = item.getAttributeAsJid("jid");
+ if (jid != null) {
+ jids.add(jid);
+ }
+ }
+ }
+ account.getBlocklist().removeAll(jids);
+ }
+ mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
+ final IqPacket response = packet.generateResponse(IqPacket.TYPE.RESULT);
+ mXmppConnectionService.sendIqPacket(account, response, null);
+ } else if (packet.hasChild("open", "http://jabber.org/protocol/ibb")
+ || packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
+ mXmppConnectionService.getJingleConnectionManager()
+ .deliverIbbPacket(account, packet);
+ } else if (packet.hasChild("query", "http://jabber.org/protocol/disco#info")) {
+ final IqPacket response = mXmppConnectionService.getIqGenerator().discoResponse(packet);
+ mXmppConnectionService.sendIqPacket(account, response, null);
+ } else if (packet.hasChild("query", "jabber:iq:version") && isGet) {
+ final IqPacket response = mXmppConnectionService.getIqGenerator().versionResponse(packet);
+ mXmppConnectionService.sendIqPacket(account, response, null);
+ } else if (packet.hasChild("ping", "urn:xmpp:ping") && isGet) {
+ final IqPacket response = packet.generateResponse(IqPacket.TYPE.RESULT);
+ mXmppConnectionService.sendIqPacket(account, response, null);
+ } else if (packet.hasChild("time", "urn:xmpp:time") && isGet) {
+ final IqPacket response;
+ if (mXmppConnectionService.useTorToConnect()) {
+ response = packet.generateResponse(IqPacket.TYPE.ERROR);
+ final Element error = response.addChild("error");
+ error.setAttribute("type", "cancel");
+ error.addChild("not-allowed", "urn:ietf:params:xml:ns:xmpp-stanzas");
+ } else {
+ response = mXmppConnectionService.getIqGenerator().entityTimeResponse(packet);
+ }
+ mXmppConnectionService.sendIqPacket(account, response, null);
+ } else {
+ if (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET) {
+ final IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR);
+ final Element error = response.addChild("error");
+ error.setAttribute("type", "cancel");
+ error.addChild("feature-not-implemented", "urn:ietf:params:xml:ns:xmpp-stanzas");
+ account.getXmppConnection().sendIqPacket(response, null);
+ }
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/parser/MessageParser.java b/src/main/java/de/pixart/messenger/parser/MessageParser.java
index d506c3fa2..531e29629 100644
--- a/src/main/java/de/pixart/messenger/parser/MessageParser.java
+++ b/src/main/java/de/pixart/messenger/parser/MessageParser.java
@@ -41,396 +41,397 @@ import de.pixart.messenger.xmpp.stanzas.MessagePacket;
public class MessageParser extends AbstractParser implements OnMessagePacketReceived {
- private static final List<String> CLIENTS_SENDING_HTML_IN_OTR = Arrays.asList("Pidgin","Adium","Trillian");
-
- public MessageParser(XmppConnectionService service) {
- super(service);
- }
-
- private boolean extractChatState(Conversation conversation, final MessagePacket packet) {
- ChatState state = ChatState.parse(packet);
- if (state != null && conversation != null) {
- final Account account = conversation.getAccount();
- Jid from = packet.getFrom();
- if (from.toBareJid().equals(account.getJid().toBareJid())) {
- conversation.setOutgoingChatState(state);
- if (state == ChatState.ACTIVE || state == ChatState.COMPOSING) {
- mXmppConnectionService.markRead(conversation);
- activateGracePeriod(account);
- }
- return false;
- } else {
- return conversation.setIncomingChatState(state);
- }
- }
- return false;
- }
-
- private Message parseOtrChat(String body, Jid from, String id, Conversation conversation) {
- String presence;
- if (from.isBareJid()) {
- presence = "";
- } else {
- presence = from.getResourcepart();
- }
- if (body.matches("^\\?OTRv\\d{1,2}\\?.*")) {
- conversation.endOtrIfNeeded();
- }
- if (!conversation.hasValidOtrSession()) {
- conversation.startOtrSession(presence,false);
- } else {
- String foreignPresence = conversation.getOtrSession().getSessionID().getUserID();
- if (!foreignPresence.equals(presence)) {
- conversation.endOtrIfNeeded();
- conversation.startOtrSession(presence, false);
- }
- }
- try {
- conversation.setLastReceivedOtrMessageId(id);
- Session otrSession = conversation.getOtrSession();
- body = otrSession.transformReceiving(body);
- SessionStatus status = otrSession.getSessionStatus();
- if (body == null && status == SessionStatus.ENCRYPTED) {
- mXmppConnectionService.onOtrSessionEstablished(conversation);
- return null;
- } else if (body == null && status == SessionStatus.FINISHED) {
- conversation.resetOtrSession();
- mXmppConnectionService.updateConversationUi();
- return null;
- } else if (body == null || (body.isEmpty())) {
- return null;
- }
- if (body.startsWith(CryptoHelper.FILETRANSFER)) {
- String key = body.substring(CryptoHelper.FILETRANSFER.length());
- conversation.setSymmetricKey(CryptoHelper.hexToBytes(key));
- return null;
- }
- if (clientMightSendHtml(conversation.getAccount(), from)) {
- Log.d(Config.LOGTAG,conversation.getAccount().getJid().toBareJid()+": received OTR message from bad behaving client. escaping HTML…");
- body = Html.fromHtml(body).toString();
- }
-
- final OtrService otrService = conversation.getAccount().getOtrService();
- Message finishedMessage = new Message(conversation, body, Message.ENCRYPTION_OTR, Message.STATUS_RECEIVED);
- finishedMessage.setFingerprint(otrService.getFingerprint(otrSession.getRemotePublicKey()));
- conversation.setLastReceivedOtrMessageId(null);
-
- return finishedMessage;
- } catch (Exception e) {
- conversation.resetOtrSession();
- return null;
- }
- }
-
- private static boolean clientMightSendHtml(Account account, Jid from) {
- String resource = from.getResourcepart();
- if (resource == null) {
- return false;
- }
- Presence presence = account.getRoster().getContact(from).getPresences().getPresences().get(resource);
- ServiceDiscoveryResult disco = presence == null ? null : presence.getServiceDiscoveryResult();
- if (disco == null) {
- return false;
- }
- return hasIdentityKnowForSendingHtml(disco.getIdentities());
- }
-
- private static boolean hasIdentityKnowForSendingHtml(List<ServiceDiscoveryResult.Identity> identities) {
- for(ServiceDiscoveryResult.Identity identity : identities) {
- if (identity.getName() != null) {
- if (CLIENTS_SENDING_HTML_IN_OTR.contains(identity.getName())) {
- return true;
- }
- }
- }
- return false;
- }
-
- private Message parseAxolotlChat(Element axolotlMessage, Jid from, Conversation conversation, int status) {
- AxolotlService service = conversation.getAccount().getAxolotlService();
- XmppAxolotlMessage xmppAxolotlMessage;
- try {
- xmppAxolotlMessage = XmppAxolotlMessage.fromElement(axolotlMessage, from.toBareJid());
- } catch (Exception e) {
- Log.d(Config.LOGTAG,conversation.getAccount().getJid().toBareJid()+": invalid omemo message received "+e.getMessage());
- return null;
- }
- XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = service.processReceivingPayloadMessage(xmppAxolotlMessage);
- if(plaintextMessage != null) {
- Message 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());
- return finishedMessage;
- } else {
- return null;
- }
- }
-
- private class Invite {
- Jid jid;
- String password;
- Invite(Jid jid, String password) {
- this.jid = jid;
- this.password = password;
- }
-
- public boolean execute(Account account) {
- if (jid != null) {
- Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, jid, true);
- if (!conversation.getMucOptions().online()) {
- conversation.getMucOptions().setPassword(password);
- mXmppConnectionService.databaseBackend.updateConversation(conversation);
- mXmppConnectionService.joinMuc(conversation);
- mXmppConnectionService.updateConversationUi();
- }
- return true;
- }
- return false;
- }
- }
-
- private Invite extractInvite(Element message) {
- Element x = message.findChild("x", "http://jabber.org/protocol/muc#user");
- if (x != null) {
- Element invite = x.findChild("invite");
- if (invite != null) {
- Element pw = x.findChild("password");
- return new Invite(message.getAttributeAsJid("from"), pw != null ? pw.getContent(): null);
- }
- } else {
- x = message.findChild("x","jabber:x:conference");
- if (x != null) {
- return new Invite(x.getAttributeAsJid("jid"),x.getAttribute("password"));
- }
- }
- return null;
- }
-
- private static String extractStanzaId(Element packet, Jid by) {
- for(Element child : packet.getChildren()) {
- if (child.getName().equals("stanza-id")
- && Xmlns.STANZA_IDS.equals(child.getNamespace())
- && by.equals(child.getAttributeAsJid("by"))) {
- return child.getAttribute("id");
- }
- }
- return null;
- }
-
- private void parseEvent(final Element event, final Jid from, final Account account) {
- Element items = event.findChild("items");
- String node = items == null ? null : items.getAttribute("node");
- if ("urn:xmpp:avatar:metadata".equals(node)) {
- Avatar avatar = Avatar.parseMetadata(items);
- if (avatar != null) {
- avatar.owner = from.toBareJid();
- if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
- if (account.getJid().toBareJid().equals(from)) {
- if (account.setAvatar(avatar.getFilename())) {
- mXmppConnectionService.databaseBackend.updateAccount(account);
- }
- mXmppConnectionService.getAvatarService().clear(account);
- mXmppConnectionService.updateConversationUi();
- mXmppConnectionService.updateAccountUi();
- } else {
- Contact contact = account.getRoster().getContact(from);
- contact.setAvatar(avatar);
- mXmppConnectionService.getAvatarService().clear(contact);
- mXmppConnectionService.updateConversationUi();
- mXmppConnectionService.updateRosterUi();
- }
- } else if (mXmppConnectionService.isDataSaverDisabled()) {
- mXmppConnectionService.fetchAvatar(account, avatar);
- }
- }
- } else if ("http://jabber.org/protocol/nick".equals(node)) {
- Element i = items.findChild("item");
- Element nick = i == null ? null : i.findChild("nick", "http://jabber.org/protocol/nick");
- if (nick != null && nick.getContent() != null) {
- Contact contact = account.getRoster().getContact(from);
- contact.setPresenceName(nick.getContent());
- mXmppConnectionService.getAvatarService().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...");
- Element item = items.findChild("item");
- Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
- AxolotlService axolotlService = account.getAxolotlService();
- axolotlService.registerDevices(from, deviceIds);
- mXmppConnectionService.updateAccountUi();
- }
- }
-
- private boolean handleErrorMessage(Account account, MessagePacket packet) {
- if (packet.getType() == MessagePacket.TYPE_ERROR) {
- Jid from = packet.getFrom();
- if (from != null) {
- Message message = mXmppConnectionService.markMessage(account,
- from.toBareJid(),
- packet.getId(),
+ private static final List<String> CLIENTS_SENDING_HTML_IN_OTR = Arrays.asList("Pidgin", "Adium", "Trillian");
+
+ public MessageParser(XmppConnectionService service) {
+ super(service);
+ }
+
+ private boolean extractChatState(Conversation conversation, final MessagePacket packet) {
+ ChatState state = ChatState.parse(packet);
+ if (state != null && conversation != null) {
+ final Account account = conversation.getAccount();
+ Jid from = packet.getFrom();
+ if (from.toBareJid().equals(account.getJid().toBareJid())) {
+ conversation.setOutgoingChatState(state);
+ if (state == ChatState.ACTIVE || state == ChatState.COMPOSING) {
+ mXmppConnectionService.markRead(conversation);
+ activateGracePeriod(account);
+ }
+ return false;
+ } else {
+ return conversation.setIncomingChatState(state);
+ }
+ }
+ return false;
+ }
+
+ private Message parseOtrChat(String body, Jid from, String id, Conversation conversation) {
+ String presence;
+ if (from.isBareJid()) {
+ presence = "";
+ } else {
+ presence = from.getResourcepart();
+ }
+ if (body.matches("^\\?OTRv\\d{1,2}\\?.*")) {
+ conversation.endOtrIfNeeded();
+ }
+ if (!conversation.hasValidOtrSession()) {
+ conversation.startOtrSession(presence, false);
+ } else {
+ String foreignPresence = conversation.getOtrSession().getSessionID().getUserID();
+ if (!foreignPresence.equals(presence)) {
+ conversation.endOtrIfNeeded();
+ conversation.startOtrSession(presence, false);
+ }
+ }
+ try {
+ conversation.setLastReceivedOtrMessageId(id);
+ Session otrSession = conversation.getOtrSession();
+ body = otrSession.transformReceiving(body);
+ SessionStatus status = otrSession.getSessionStatus();
+ if (body == null && status == SessionStatus.ENCRYPTED) {
+ mXmppConnectionService.onOtrSessionEstablished(conversation);
+ return null;
+ } else if (body == null && status == SessionStatus.FINISHED) {
+ conversation.resetOtrSession();
+ mXmppConnectionService.updateConversationUi();
+ return null;
+ } else if (body == null || (body.isEmpty())) {
+ return null;
+ }
+ if (body.startsWith(CryptoHelper.FILETRANSFER)) {
+ String key = body.substring(CryptoHelper.FILETRANSFER.length());
+ conversation.setSymmetricKey(CryptoHelper.hexToBytes(key));
+ return null;
+ }
+ if (clientMightSendHtml(conversation.getAccount(), from)) {
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": received OTR message from bad behaving client. escaping HTML…");
+ body = Html.fromHtml(body).toString();
+ }
+
+ final OtrService otrService = conversation.getAccount().getOtrService();
+ Message finishedMessage = new Message(conversation, body, Message.ENCRYPTION_OTR, Message.STATUS_RECEIVED);
+ finishedMessage.setFingerprint(otrService.getFingerprint(otrSession.getRemotePublicKey()));
+ conversation.setLastReceivedOtrMessageId(null);
+
+ return finishedMessage;
+ } catch (Exception e) {
+ conversation.resetOtrSession();
+ return null;
+ }
+ }
+
+ private static boolean clientMightSendHtml(Account account, Jid from) {
+ String resource = from.getResourcepart();
+ if (resource == null) {
+ return false;
+ }
+ Presence presence = account.getRoster().getContact(from).getPresences().getPresences().get(resource);
+ ServiceDiscoveryResult disco = presence == null ? null : presence.getServiceDiscoveryResult();
+ if (disco == null) {
+ return false;
+ }
+ return hasIdentityKnowForSendingHtml(disco.getIdentities());
+ }
+
+ private static boolean hasIdentityKnowForSendingHtml(List<ServiceDiscoveryResult.Identity> identities) {
+ for (ServiceDiscoveryResult.Identity identity : identities) {
+ if (identity.getName() != null) {
+ if (CLIENTS_SENDING_HTML_IN_OTR.contains(identity.getName())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private Message parseAxolotlChat(Element axolotlMessage, Jid from, Conversation conversation, int status) {
+ AxolotlService service = conversation.getAccount().getAxolotlService();
+ XmppAxolotlMessage xmppAxolotlMessage;
+ try {
+ xmppAxolotlMessage = XmppAxolotlMessage.fromElement(axolotlMessage, from.toBareJid());
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": invalid omemo message received " + e.getMessage());
+ return null;
+ }
+ XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = service.processReceivingPayloadMessage(xmppAxolotlMessage);
+ if (plaintextMessage != null) {
+ Message 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());
+ return finishedMessage;
+ } else {
+ return null;
+ }
+ }
+
+ private class Invite {
+ Jid jid;
+ String password;
+
+ Invite(Jid jid, String password) {
+ this.jid = jid;
+ this.password = password;
+ }
+
+ public boolean execute(Account account) {
+ if (jid != null) {
+ Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, jid, true);
+ if (!conversation.getMucOptions().online()) {
+ conversation.getMucOptions().setPassword(password);
+ mXmppConnectionService.databaseBackend.updateConversation(conversation);
+ mXmppConnectionService.joinMuc(conversation);
+ mXmppConnectionService.updateConversationUi();
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private Invite extractInvite(Element message) {
+ Element x = message.findChild("x", "http://jabber.org/protocol/muc#user");
+ if (x != null) {
+ Element invite = x.findChild("invite");
+ if (invite != null) {
+ Element pw = x.findChild("password");
+ return new Invite(message.getAttributeAsJid("from"), pw != null ? pw.getContent() : null);
+ }
+ } else {
+ x = message.findChild("x", "jabber:x:conference");
+ if (x != null) {
+ return new Invite(x.getAttributeAsJid("jid"), x.getAttribute("password"));
+ }
+ }
+ return null;
+ }
+
+ private static String extractStanzaId(Element packet, Jid by) {
+ for (Element child : packet.getChildren()) {
+ if (child.getName().equals("stanza-id")
+ && Xmlns.STANZA_IDS.equals(child.getNamespace())
+ && by.equals(child.getAttributeAsJid("by"))) {
+ return child.getAttribute("id");
+ }
+ }
+ return null;
+ }
+
+ private void parseEvent(final Element event, final Jid from, final Account account) {
+ Element items = event.findChild("items");
+ String node = items == null ? null : items.getAttribute("node");
+ if ("urn:xmpp:avatar:metadata".equals(node)) {
+ Avatar avatar = Avatar.parseMetadata(items);
+ if (avatar != null) {
+ avatar.owner = from.toBareJid();
+ if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
+ if (account.getJid().toBareJid().equals(from)) {
+ if (account.setAvatar(avatar.getFilename())) {
+ mXmppConnectionService.databaseBackend.updateAccount(account);
+ }
+ mXmppConnectionService.getAvatarService().clear(account);
+ mXmppConnectionService.updateConversationUi();
+ mXmppConnectionService.updateAccountUi();
+ } else {
+ Contact contact = account.getRoster().getContact(from);
+ contact.setAvatar(avatar);
+ mXmppConnectionService.getAvatarService().clear(contact);
+ mXmppConnectionService.updateConversationUi();
+ mXmppConnectionService.updateRosterUi();
+ }
+ } else if (mXmppConnectionService.isDataSaverDisabled()) {
+ mXmppConnectionService.fetchAvatar(account, avatar);
+ }
+ }
+ } else if ("http://jabber.org/protocol/nick".equals(node)) {
+ Element i = items.findChild("item");
+ Element nick = i == null ? null : i.findChild("nick", "http://jabber.org/protocol/nick");
+ if (nick != null && nick.getContent() != null) {
+ Contact contact = account.getRoster().getContact(from);
+ contact.setPresenceName(nick.getContent());
+ mXmppConnectionService.getAvatarService().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...");
+ Element item = items.findChild("item");
+ Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
+ AxolotlService axolotlService = account.getAxolotlService();
+ axolotlService.registerDevices(from, deviceIds);
+ mXmppConnectionService.updateAccountUi();
+ }
+ }
+
+ private boolean handleErrorMessage(Account account, MessagePacket packet) {
+ if (packet.getType() == MessagePacket.TYPE_ERROR) {
+ Jid from = packet.getFrom();
+ if (from != null) {
+ Message message = mXmppConnectionService.markMessage(account,
+ from.toBareJid(),
+ packet.getId(),
Message.STATUS_SEND_FAILED,
extractErrorMessage(packet));
if (message != null) {
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
message.getConversation().endOtrIfNeeded();
}
- }
- }
- return true;
- }
- return false;
- }
-
- @Override
- public void onMessagePacketReceived(Account account, MessagePacket original) {
- if (handleErrorMessage(account, original)) {
- return;
- }
- final MessagePacket packet;
- Long timestamp = null;
- final boolean isForwarded;
- boolean isCarbon = false;
- String serverMsgId = null;
- final Element fin = original.findChild("fin", "urn:xmpp:mam:0");
- if (fin != null) {
- mXmppConnectionService.getMessageArchiveService().processFin(fin,original.getFrom());
- return;
- }
- final Element result = original.findChild("result","urn:xmpp:mam:0");
- final MessageArchiveService.Query query = result == null ? null : mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid"));
- if (query != null && query.validFrom(original.getFrom())) {
- Pair<MessagePacket, Long> f = original.getForwardedMessagePacket("result", "urn:xmpp:mam:0");
- if (f == null) {
- return;
- }
- timestamp = f.second;
- packet = f.first;
- isForwarded = true;
- serverMsgId = result.getAttribute("id");
- query.incrementMessageCount();
- } else if (query != null) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": received mam result from invalid sender");
- return;
- } else if (original.fromServer(account)) {
- Pair<MessagePacket, Long> f;
- f = original.getForwardedMessagePacket("received", "urn:xmpp:carbons:2");
- f = f == null ? original.getForwardedMessagePacket("sent", "urn:xmpp:carbons:2") : f;
- packet = f != null ? f.first : original;
- if (handleErrorMessage(account, packet)) {
- return;
- }
- timestamp = f != null ? f.second : null;
- isCarbon = f != null;
- isForwarded = isCarbon;
- } else {
- packet = original;
- isForwarded = false;
- }
-
- if (timestamp == null) {
- timestamp = AbstractParser.parseTimestamp(original,AbstractParser.parseTimestamp(packet));
- }
- 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;
- final Jid to = packet.getTo();
- final Jid from = packet.getFrom();
- final String remoteMsgId = packet.getId();
- boolean notify = false;
-
- if (from == null) {
- Log.d(Config.LOGTAG,"no from in: "+packet.toString());
- return;
- }
-
- boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT;
- boolean isProperlyAddressed = (to != null ) && (!to.isBareJid() || account.countPresences() == 0);
- boolean isMucStatusMessage = from.isBareJid() && mucUserElement != null && mucUserElement.hasChild("status");
- if (packet.fromAccount(account)) {
- status = Message.STATUS_SEND;
- counterpart = to != null ? to : account.getJid();
- } else {
- status = Message.STATUS_RECEIVED;
- counterpart = from;
- }
-
- Invite invite = extractInvite(packet);
- if (invite != null && invite.execute(account)) {
- return;
- }
-
- if (!isTypeGroupChat
- && query == null
- && extractChatState(mXmppConnectionService.find(account, counterpart.toBareJid()), packet)) {
- mXmppConnectionService.updateConversationUi();
- }
-
- if ((body != null || pgpEncrypted != null || axolotlEncrypted != null) && !isMucStatusMessage) {
- Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.toBareJid(), isTypeGroupChat, query);
- final boolean conversationMultiMode = conversation.getMode() == Conversation.MODE_MULTI;
- if (isTypeGroupChat) {
- 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)) {
- return;
- } else if (remoteMsgId == null || Config.IGNORE_ID_REWRITE_IN_MUC) {
- Message message = conversation.findSentMessageWithBody(packet.getBody());
- if (message != null) {
- mXmppConnectionService.markMessage(message, status);
- return;
- }
- }
- } else {
- status = Message.STATUS_RECEIVED;
- }
- }
- final Message message;
- if (body != null && body.startsWith("?OTR") && Config.supportOtr()) {
- if (!isForwarded && !isTypeGroupChat && isProperlyAddressed && !conversationMultiMode) {
- message = parseOtrChat(body, from, remoteMsgId, conversation);
- if (message == null) {
- return;
- }
- } else {
- Log.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()) {
- message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status);
- } else if (axolotlEncrypted != null && Config.supportOmemo()) {
- Jid origin;
- if (conversationMultiMode) {
- final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart);
- origin = getTrueCounterpart(query != null ? mucUserElement : null, fallback);
- if (origin == null) {
- Log.d(Config.LOGTAG,"axolotl message in non anonymous conference received");
- return;
- }
- } else {
- origin = from;
- }
- message = parseAxolotlChat(axolotlEncrypted, origin, conversation, status);
- if (message == null) {
- return;
- }
- if (conversationMultiMode) {
- message.setTrueCounterpart(origin);
- }
- } else {
- message = new Message(conversation, body, Message.ENCRYPTION_NONE, status);
- }
-
- if (serverMsgId == null) {
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onMessagePacketReceived(Account account, MessagePacket original) {
+ if (handleErrorMessage(account, original)) {
+ return;
+ }
+ final MessagePacket packet;
+ Long timestamp = null;
+ final boolean isForwarded;
+ boolean isCarbon = false;
+ String serverMsgId = null;
+ final Element fin = original.findChild("fin", "urn:xmpp:mam:0");
+ if (fin != null) {
+ mXmppConnectionService.getMessageArchiveService().processFin(fin, original.getFrom());
+ return;
+ }
+ final Element result = original.findChild("result", "urn:xmpp:mam:0");
+ final MessageArchiveService.Query query = result == null ? null : mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid"));
+ if (query != null && query.validFrom(original.getFrom())) {
+ Pair<MessagePacket, Long> f = original.getForwardedMessagePacket("result", "urn:xmpp:mam:0");
+ if (f == null) {
+ return;
+ }
+ timestamp = f.second;
+ packet = f.first;
+ isForwarded = true;
+ serverMsgId = result.getAttribute("id");
+ query.incrementMessageCount();
+ } else if (query != null) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": received mam result from invalid sender");
+ return;
+ } else if (original.fromServer(account)) {
+ Pair<MessagePacket, Long> f;
+ f = original.getForwardedMessagePacket("received", "urn:xmpp:carbons:2");
+ f = f == null ? original.getForwardedMessagePacket("sent", "urn:xmpp:carbons:2") : f;
+ packet = f != null ? f.first : original;
+ if (handleErrorMessage(account, packet)) {
+ return;
+ }
+ timestamp = f != null ? f.second : null;
+ isCarbon = f != null;
+ isForwarded = isCarbon;
+ } else {
+ packet = original;
+ isForwarded = false;
+ }
+
+ if (timestamp == null) {
+ timestamp = AbstractParser.parseTimestamp(original, AbstractParser.parseTimestamp(packet));
+ }
+ 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;
+ final Jid to = packet.getTo();
+ final Jid from = packet.getFrom();
+ final String remoteMsgId = packet.getId();
+ boolean notify = false;
+
+ if (from == null) {
+ Log.d(Config.LOGTAG, "no from in: " + packet.toString());
+ return;
+ }
+
+ boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT;
+ boolean isProperlyAddressed = (to != null) && (!to.isBareJid() || account.countPresences() == 0);
+ boolean isMucStatusMessage = from.isBareJid() && mucUserElement != null && mucUserElement.hasChild("status");
+ if (packet.fromAccount(account)) {
+ status = Message.STATUS_SEND;
+ counterpart = to != null ? to : account.getJid();
+ } else {
+ status = Message.STATUS_RECEIVED;
+ counterpart = from;
+ }
+
+ Invite invite = extractInvite(packet);
+ if (invite != null && invite.execute(account)) {
+ return;
+ }
+
+ if (!isTypeGroupChat
+ && query == null
+ && extractChatState(mXmppConnectionService.find(account, counterpart.toBareJid()), packet)) {
+ mXmppConnectionService.updateConversationUi();
+ }
+
+ if ((body != null || pgpEncrypted != null || axolotlEncrypted != null) && !isMucStatusMessage) {
+ Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.toBareJid(), isTypeGroupChat, query);
+ final boolean conversationMultiMode = conversation.getMode() == Conversation.MODE_MULTI;
+ if (isTypeGroupChat) {
+ 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)) {
+ return;
+ } else if (remoteMsgId == null || Config.IGNORE_ID_REWRITE_IN_MUC) {
+ Message message = conversation.findSentMessageWithBody(packet.getBody());
+ if (message != null) {
+ mXmppConnectionService.markMessage(message, status);
+ return;
+ }
+ }
+ } else {
+ status = Message.STATUS_RECEIVED;
+ }
+ }
+ final Message message;
+ if (body != null && body.startsWith("?OTR") && Config.supportOtr()) {
+ if (!isForwarded && !isTypeGroupChat && isProperlyAddressed && !conversationMultiMode) {
+ message = parseOtrChat(body, from, remoteMsgId, conversation);
+ if (message == null) {
+ return;
+ }
+ } else {
+ Log.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()) {
+ message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status);
+ } else if (axolotlEncrypted != null && Config.supportOmemo()) {
+ Jid origin;
+ if (conversationMultiMode) {
+ final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart);
+ origin = getTrueCounterpart(query != null ? mucUserElement : null, fallback);
+ if (origin == null) {
+ Log.d(Config.LOGTAG, "axolotl message in non anonymous conference received");
+ return;
+ }
+ } else {
+ origin = from;
+ }
+ message = parseAxolotlChat(axolotlEncrypted, origin, conversation, status);
+ if (message == null) {
+ return;
+ }
+ if (conversationMultiMode) {
+ message.setTrueCounterpart(origin);
+ }
+ } else {
+ message = new Message(conversation, body, Message.ENCRYPTION_NONE, status);
+ }
+
+ if (serverMsgId == null) {
final Jid by;
final boolean safeToExtract;
if (isTypeGroupChat) {
@@ -443,154 +444,154 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
if (safeToExtract) {
serverMsgId = extractStanzaId(packet, by);
}
- }
-
- message.setCounterpart(counterpart);
- message.setRemoteMsgId(remoteMsgId);
- message.setServerMsgId(serverMsgId);
- message.setCarbon(isCarbon);
- message.setTime(timestamp);
- message.setOob(isOob);
- message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0");
- if (conversationMultiMode) {
- final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart);
- Jid trueCounterpart;
- if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
- trueCounterpart = message.getTrueCounterpart();
- } else if (Config.PARSE_REAL_JID_FROM_MUC_MAM) {
- trueCounterpart = getTrueCounterpart(query != null ? mucUserElement : null, fallback);
- } else {
- trueCounterpart = fallback;
- }
- if (trueCounterpart != null && trueCounterpart.toBareJid().equals(account.getJid().toBareJid())) {
- status = isTypeGroupChat ? Message.STATUS_SEND_RECEIVED : Message.STATUS_SEND;
- }
- message.setStatus(status);
- message.setTrueCounterpart(trueCounterpart);
- if (!isTypeGroupChat) {
- message.setType(Message.TYPE_PRIVATE);
- }
- } else {
- updateLastseen(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 || !conversationMultiMode)) {
- Log.d(Config.LOGTAG, "replaced message '" + replacedMessage.getBody() + "' with '" + message.getBody() + "'");
- synchronized (replacedMessage) {
- 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().discard(replacedMessage);
- conversation.getAccount().getPgpDecryptionService().decrypt(replacedMessage, false);
- }
- }
- 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());
- return;
- }
-
- if (query != null && query.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) {
- conversation.prepend(message);
- } else {
- conversation.add(message);
- }
-
- if (query == null || query.getWith() == null) { //either no mam or catchup
- if (status == Message.STATUS_SEND || status == Message.STATUS_SEND_RECEIVED) {
- mXmppConnectionService.markRead(conversation);
- if (query == null) {
- activateGracePeriod(account);
- }
- } else {
- message.markUnread();
- notify = true;
- }
- }
-
- if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- notify = conversation.getAccount().getPgpDecryptionService().decrypt(message, notify);
- }
-
- if (query == null) {
- mXmppConnectionService.updateConversationUi();
- }
+ }
+
+ message.setCounterpart(counterpart);
+ message.setRemoteMsgId(remoteMsgId);
+ message.setServerMsgId(serverMsgId);
+ message.setCarbon(isCarbon);
+ message.setTime(timestamp);
+ message.setOob(isOob);
+ message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0");
+ if (conversationMultiMode) {
+ final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart);
+ Jid trueCounterpart;
+ if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
+ trueCounterpart = message.getTrueCounterpart();
+ } else if (Config.PARSE_REAL_JID_FROM_MUC_MAM) {
+ trueCounterpart = getTrueCounterpart(query != null ? mucUserElement : null, fallback);
+ } else {
+ trueCounterpart = fallback;
+ }
+ if (trueCounterpart != null && trueCounterpart.toBareJid().equals(account.getJid().toBareJid())) {
+ status = isTypeGroupChat ? Message.STATUS_SEND_RECEIVED : Message.STATUS_SEND;
+ }
+ message.setStatus(status);
+ message.setTrueCounterpart(trueCounterpart);
+ if (!isTypeGroupChat) {
+ message.setType(Message.TYPE_PRIVATE);
+ }
+ } else {
+ updateLastseen(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 || !conversationMultiMode)) {
+ Log.d(Config.LOGTAG, "replaced message '" + replacedMessage.getBody() + "' with '" + message.getBody() + "'");
+ synchronized (replacedMessage) {
+ 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().discard(replacedMessage);
+ conversation.getAccount().getPgpDecryptionService().decrypt(replacedMessage, false);
+ }
+ }
+ 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());
+ return;
+ }
+
+ if (query != null && query.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) {
+ conversation.prepend(message);
+ } else {
+ conversation.add(message);
+ }
+
+ if (query == null || query.getWith() == null) { //either no mam or catchup
+ if (status == Message.STATUS_SEND || status == Message.STATUS_SEND_RECEIVED) {
+ mXmppConnectionService.markRead(conversation);
+ if (query == null) {
+ activateGracePeriod(account);
+ }
+ } else {
+ message.markUnread();
+ notify = true;
+ }
+ }
+
+ if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ notify = conversation.getAccount().getPgpDecryptionService().decrypt(message, notify);
+ }
+
+ if (query == null) {
+ mXmppConnectionService.updateConversationUi();
+ }
if (mXmppConnectionService.confirmMessages() && message.trusted() && remoteMsgId != null && !isForwarded && !isTypeGroupChat) {
- sendMessageReceipts(account, packet);
- }
-
- if (message.getStatus() == Message.STATUS_RECEIVED
- && conversation.getOtrSession() != null
- && !conversation.getOtrSession().getSessionID().getUserID()
- .equals(message.getCounterpart().getResourcepart())) {
- conversation.endOtrIfNeeded();
- }
-
- if (message.getEncryption() == Message.ENCRYPTION_NONE || mXmppConnectionService.saveEncryptedMessages()) {
- 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 (notify) {
- if (query == null) {
- mXmppConnectionService.getNotificationService().push(message);
- } else if (query.getWith() == null) { // mam catchup
- mXmppConnectionService.getNotificationService().pushFromBacklog(message);
- }
- }
- } else if (!packet.hasChild("body")){ //no body
- Conversation conversation = mXmppConnectionService.find(account, from.toBareJid());
- if (isTypeGroupChat) {
- if (packet.hasChild("subject")) {
- if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
- conversation.setHasMessagesLeftOnServer(conversation.countMessages() > 0);
- String subject = packet.findChildContent("subject");
- conversation.getMucOptions().setSubject(subject);
- final Bookmark bookmark = conversation.getBookmark();
- if (bookmark != null && bookmark.getBookmarkName() == null) {
- if (bookmark.setBookmarkName(subject)) {
- mXmppConnectionService.pushBookmarks(account);
- }
- }
- mXmppConnectionService.updateConversationUi();
- return;
- }
- }
- }
- if (conversation != null && mucUserElement != null && from.isBareJid()) {
+ sendMessageReceipts(account, packet);
+ }
+
+ if (message.getStatus() == Message.STATUS_RECEIVED
+ && conversation.getOtrSession() != null
+ && !conversation.getOtrSession().getSessionID().getUserID()
+ .equals(message.getCounterpart().getResourcepart())) {
+ conversation.endOtrIfNeeded();
+ }
+
+ if (message.getEncryption() == Message.ENCRYPTION_NONE || mXmppConnectionService.saveEncryptedMessages()) {
+ 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 (notify) {
+ if (query == null) {
+ mXmppConnectionService.getNotificationService().push(message);
+ } else if (query.getWith() == null) { // mam catchup
+ mXmppConnectionService.getNotificationService().pushFromBacklog(message);
+ }
+ }
+ } else if (!packet.hasChild("body")) { //no body
+ Conversation conversation = mXmppConnectionService.find(account, from.toBareJid());
+ if (isTypeGroupChat) {
+ if (packet.hasChild("subject")) {
+ if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
+ conversation.setHasMessagesLeftOnServer(conversation.countMessages() > 0);
+ String subject = packet.findChildContent("subject");
+ conversation.getMucOptions().setSubject(subject);
+ final Bookmark bookmark = conversation.getBookmark();
+ if (bookmark != null && bookmark.getBookmarkName() == null) {
+ if (bookmark.setBookmarkName(subject)) {
+ mXmppConnectionService.pushBookmarks(account);
+ }
+ }
+ mXmppConnectionService.updateConversationUi();
+ return;
+ }
+ }
+ }
+ if (conversation != null && mucUserElement != null && from.isBareJid()) {
for (Element child : mucUserElement.getChildren()) {
if ("status".equals(child.getName())) {
try {
@@ -598,92 +599,91 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
if ((code >= 170 && code <= 174) || (code >= 102 && code <= 104)) {
mXmppConnectionService.fetchConferenceConfiguration(conversation);
break;
- }
+ }
} catch (Exception e) {
//ignored
}
} else if ("item".equals(child.getName())) {
- MucOptions.User user = AbstractParser.parseItem(conversation,child);
- Log.d(Config.LOGTAG,account.getJid()+": changing affiliation for " + user.getRealJid()+" to " + user.getAffiliation() + " in " + conversation.getJid().toBareJid());
+ MucOptions.User user = AbstractParser.parseItem(conversation, child);
+ Log.d(Config.LOGTAG, account.getJid() + ": changing affiliation for " + user.getRealJid() + " to " + user.getAffiliation() + " in " + conversation.getJid().toBareJid());
if (!user.realJidMatchesAccount()) {
conversation.getMucOptions().updateUser(user);
mXmppConnectionService.getAvatarService().clear(conversation);
mXmppConnectionService.updateMucRosterUi();
mXmppConnectionService.updateConversationUi();
- }
- }
- }
- }
- }
-
-
-
- Element received = packet.findChild("received", "urn:xmpp:chat-markers:0");
- if (received == null) {
- received = packet.findChild("received", "urn:xmpp:receipts");
- }
- if (received != null && !packet.fromAccount(account)) {
- mXmppConnectionService.markMessage(account, from.toBareJid(), received.getAttribute("id"), Message.STATUS_SEND_RECEIVED);
- }
- Element displayed = packet.findChild("displayed", "urn:xmpp:chat-markers:0");
- if (displayed != null) {
- if (packet.fromAccount(account)) {
- Conversation conversation = mXmppConnectionService.find(account,counterpart.toBareJid());
- if (conversation != null) {
- mXmppConnectionService.markRead(conversation);
- }
- } else {
- final Message displayedMessage = mXmppConnectionService.markMessage(account, from.toBareJid(), displayed.getAttribute("id"), Message.STATUS_SEND_DISPLAYED);
- Message message = displayedMessage == null ? null : displayedMessage.prev();
- while (message != null
- && message.getStatus() == Message.STATUS_SEND_RECEIVED
- && message.getTimeSent() < displayedMessage.getTimeSent()) {
- mXmppConnectionService.markMessage(message, Message.STATUS_SEND_DISPLAYED);
- message = message.prev();
- }
- }
- }
-
- Element event = packet.findChild("event", "http://jabber.org/protocol/pubsub#event");
- if (event != null) {
- parseEvent(event, from, account);
- }
-
- String nick = packet.findChildContent("nick", "http://jabber.org/protocol/nick");
- if (nick != null) {
- Contact contact = account.getRoster().getContact(from);
- contact.setPresenceName(nick);
- }
- }
-
- private static Jid getTrueCounterpart(Element mucUserElement, Jid fallback) {
- final Element item = mucUserElement == null ? null : mucUserElement.findChild("item");
- Jid result = item == null ? null : item.getAttributeAsJid("jid");
- return result != null ? result : fallback;
- }
-
- private void sendMessageReceipts(Account account, MessagePacket packet) {
- ArrayList<String> receiptsNamespaces = new ArrayList<>();
- if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) {
- receiptsNamespaces.add("urn:xmpp:chat-markers:0");
- }
- if (packet.hasChild("request", "urn:xmpp:receipts")) {
- receiptsNamespaces.add("urn:xmpp:receipts");
- }
- if (receiptsNamespaces.size() > 0) {
- MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account,
- packet,
- receiptsNamespaces,
- packet.getType());
- mXmppConnectionService.sendMessagePacket(account, receipt);
- }
- }
-
- private static SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss");
-
- private void activateGracePeriod(Account account) {
- long duration = mXmppConnectionService.getPreferences().getLong("race_period_length", 144) * 1000;
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": activating grace period till "+TIME_FORMAT.format(new Date(System.currentTimeMillis() + duration)));
- account.activateGracePeriod(duration);
- }
+ }
+ }
+ }
+ }
+ }
+
+
+ Element received = packet.findChild("received", "urn:xmpp:chat-markers:0");
+ if (received == null) {
+ received = packet.findChild("received", "urn:xmpp:receipts");
+ }
+ if (received != null && !packet.fromAccount(account)) {
+ mXmppConnectionService.markMessage(account, from.toBareJid(), received.getAttribute("id"), Message.STATUS_SEND_RECEIVED);
+ }
+ Element displayed = packet.findChild("displayed", "urn:xmpp:chat-markers:0");
+ if (displayed != null) {
+ if (packet.fromAccount(account)) {
+ Conversation conversation = mXmppConnectionService.find(account, counterpart.toBareJid());
+ if (conversation != null) {
+ mXmppConnectionService.markRead(conversation);
+ }
+ } else {
+ final Message displayedMessage = mXmppConnectionService.markMessage(account, from.toBareJid(), displayed.getAttribute("id"), Message.STATUS_SEND_DISPLAYED);
+ Message message = displayedMessage == null ? null : displayedMessage.prev();
+ while (message != null
+ && message.getStatus() == Message.STATUS_SEND_RECEIVED
+ && message.getTimeSent() < displayedMessage.getTimeSent()) {
+ mXmppConnectionService.markMessage(message, Message.STATUS_SEND_DISPLAYED);
+ message = message.prev();
+ }
+ }
+ }
+
+ Element event = packet.findChild("event", "http://jabber.org/protocol/pubsub#event");
+ if (event != null) {
+ parseEvent(event, from, account);
+ }
+
+ String nick = packet.findChildContent("nick", "http://jabber.org/protocol/nick");
+ if (nick != null) {
+ Contact contact = account.getRoster().getContact(from);
+ contact.setPresenceName(nick);
+ }
+ }
+
+ private static Jid getTrueCounterpart(Element mucUserElement, Jid fallback) {
+ final Element item = mucUserElement == null ? null : mucUserElement.findChild("item");
+ Jid result = item == null ? null : item.getAttributeAsJid("jid");
+ return result != null ? result : fallback;
+ }
+
+ private void sendMessageReceipts(Account account, MessagePacket packet) {
+ ArrayList<String> receiptsNamespaces = new ArrayList<>();
+ if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) {
+ receiptsNamespaces.add("urn:xmpp:chat-markers:0");
+ }
+ if (packet.hasChild("request", "urn:xmpp:receipts")) {
+ receiptsNamespaces.add("urn:xmpp:receipts");
+ }
+ if (receiptsNamespaces.size() > 0) {
+ MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account,
+ packet,
+ receiptsNamespaces,
+ packet.getType());
+ mXmppConnectionService.sendMessagePacket(account, receipt);
+ }
+ }
+
+ private static SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss");
+
+ private void activateGracePeriod(Account account) {
+ long duration = mXmppConnectionService.getPreferences().getLong("race_period_length", 144) * 1000;
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": activating grace period till " + TIME_FORMAT.format(new Date(System.currentTimeMillis() + duration)));
+ account.activateGracePeriod(duration);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/parser/PresenceParser.java b/src/main/java/de/pixart/messenger/parser/PresenceParser.java
index 50f13150b..9ba45b201 100644
--- a/src/main/java/de/pixart/messenger/parser/PresenceParser.java
+++ b/src/main/java/de/pixart/messenger/parser/PresenceParser.java
@@ -24,170 +24,170 @@ import de.pixart.messenger.xmpp.pep.Avatar;
import de.pixart.messenger.xmpp.stanzas.PresencePacket;
public class PresenceParser extends AbstractParser implements
- OnPresencePacketReceived {
+ OnPresencePacketReceived {
- public PresenceParser(XmppConnectionService service) {
- super(service);
- }
+ public PresenceParser(XmppConnectionService service) {
+ super(service);
+ }
- public void parseConferencePresence(PresencePacket packet, Account account) {
- final Conversation conversation = packet.getFrom() == null ? null : mXmppConnectionService.find(account, packet.getFrom().toBareJid());
- if (conversation != null) {
- final MucOptions mucOptions = conversation.getMucOptions();
- boolean before = mucOptions.online();
- int count = mucOptions.getUserCount();
- final List<MucOptions.User> tileUserBefore = mucOptions.getUsers(5);
- processConferencePresence(packet, conversation);
- final List<MucOptions.User> tileUserAfter = mucOptions.getUsers(5);
- if (!tileUserAfter.equals(tileUserBefore)) {
- mXmppConnectionService.getAvatarService().clear(mucOptions);
- }
- if (before != mucOptions.online() || (mucOptions.online() && count != mucOptions.getUserCount())) {
- mXmppConnectionService.updateConversationUi();
- } else if (mucOptions.online()) {
- mXmppConnectionService.updateMucRosterUi();
- }
- }
- }
+ public void parseConferencePresence(PresencePacket packet, Account account) {
+ final Conversation conversation = packet.getFrom() == null ? null : mXmppConnectionService.find(account, packet.getFrom().toBareJid());
+ if (conversation != null) {
+ final MucOptions mucOptions = conversation.getMucOptions();
+ boolean before = mucOptions.online();
+ int count = mucOptions.getUserCount();
+ final List<MucOptions.User> tileUserBefore = mucOptions.getUsers(5);
+ processConferencePresence(packet, conversation);
+ final List<MucOptions.User> tileUserAfter = mucOptions.getUsers(5);
+ if (!tileUserAfter.equals(tileUserBefore)) {
+ mXmppConnectionService.getAvatarService().clear(mucOptions);
+ }
+ if (before != mucOptions.online() || (mucOptions.online() && count != mucOptions.getUserCount())) {
+ mXmppConnectionService.updateConversationUi();
+ } else if (mucOptions.online()) {
+ mXmppConnectionService.updateMucRosterUi();
+ }
+ }
+ }
private void processConferencePresence(PresencePacket packet, Conversation conversation) {
MucOptions mucOptions = conversation.getMucOptions();
- final Jid from = packet.getFrom();
- if (!from.isBareJid()) {
- final String type = packet.getAttribute("type");
- final Element x = packet.findChild("x", "http://jabber.org/protocol/muc#user");
- Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update"));
- final List<String> codes = getStatusCodes(x);
- if (type == null) {
- if (x != null) {
- Element item = x.findChild("item");
- if (item != null && !from.isBareJid()) {
- mucOptions.setError(MucOptions.Error.NONE);
+ final Jid from = packet.getFrom();
+ if (!from.isBareJid()) {
+ final String type = packet.getAttribute("type");
+ final Element x = packet.findChild("x", "http://jabber.org/protocol/muc#user");
+ Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update"));
+ final List<String> codes = getStatusCodes(x);
+ if (type == null) {
+ if (x != null) {
+ Element item = x.findChild("item");
+ if (item != null && !from.isBareJid()) {
+ mucOptions.setError(MucOptions.Error.NONE);
MucOptions.User user = parseItem(conversation, item, from);
- if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(mucOptions.getConversation().getJid())) {
- mucOptions.setOnline();
- mucOptions.setSelf(user);
- if (mucOptions.mNickChangingInProgress) {
- if (mucOptions.onRenameListener != null) {
- mucOptions.onRenameListener.onSuccess();
- }
- mucOptions.mNickChangingInProgress = false;
- }
- } else {
- mucOptions.updateUser(user);
- }
- if (codes.contains(MucOptions.STATUS_CODE_ROOM_CREATED) && mucOptions.autoPushConfiguration()) {
- Log.d(Config.LOGTAG,mucOptions.getAccount().getJid().toBareJid()
- +": room '"
- +mucOptions.getConversation().getJid().toBareJid()
- +"' created. pushing default configuration");
- mXmppConnectionService.pushConferenceConfiguration(mucOptions.getConversation(),
- IqGenerator.defaultRoomConfiguration(),
- null);
- }
- if (mXmppConnectionService.getPgpEngine() != null) {
- Element signed = packet.findChild("x", "jabber:x:signed");
- if (signed != null) {
- Element status = packet.findChild("status");
- String msg = status == null ? "" : status.getContent();
- long keyId = mXmppConnectionService.getPgpEngine().fetchKeyId(mucOptions.getAccount(), msg, signed.getContent());
- if (keyId != 0) {
- user.setPgpKeyId(keyId);
- }
- }
- }
- if (avatar != null) {
- avatar.owner = from;
- if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
- if (user.setAvatar(avatar)) {
- mXmppConnectionService.getAvatarService().clear(user);
- }
- } else if (mXmppConnectionService.isDataSaverDisabled()) {
- mXmppConnectionService.fetchAvatar(mucOptions.getAccount(), avatar);
- }
- }
- }
- }
- } else if (type.equals("unavailable")) {
- if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) ||
- packet.getFrom().equals(mucOptions.getConversation().getJid())) {
- if (codes.contains(MucOptions.STATUS_CODE_CHANGED_NICK)) {
- mucOptions.mNickChangingInProgress = true;
- } else if (codes.contains(MucOptions.STATUS_CODE_KICKED)) {
- mucOptions.setError(MucOptions.Error.KICKED);
- } else if (codes.contains(MucOptions.STATUS_CODE_BANNED)) {
- mucOptions.setError(MucOptions.Error.BANNED);
- } else if (codes.contains(MucOptions.STATUS_CODE_LOST_MEMBERSHIP)) {
- mucOptions.setError(MucOptions.Error.MEMBERS_ONLY);
- } else if (codes.contains(MucOptions.STATUS_CODE_AFFILIATION_CHANGE)) {
- mucOptions.setError(MucOptions.Error.MEMBERS_ONLY);
- } else if (codes.contains(MucOptions.STATUS_CODE_SHUTDOWN)) {
- mucOptions.setError(MucOptions.Error.SHUTDOWN);
- } else {
- mucOptions.setError(MucOptions.Error.UNKNOWN);
- Log.d(Config.LOGTAG, "unknown error in conference: " + packet);
- }
- } else if (!from.isBareJid()){
+ if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(mucOptions.getConversation().getJid())) {
+ mucOptions.setOnline();
+ mucOptions.setSelf(user);
+ if (mucOptions.mNickChangingInProgress) {
+ if (mucOptions.onRenameListener != null) {
+ mucOptions.onRenameListener.onSuccess();
+ }
+ mucOptions.mNickChangingInProgress = false;
+ }
+ } else {
+ mucOptions.updateUser(user);
+ }
+ if (codes.contains(MucOptions.STATUS_CODE_ROOM_CREATED) && mucOptions.autoPushConfiguration()) {
+ Log.d(Config.LOGTAG, mucOptions.getAccount().getJid().toBareJid()
+ + ": room '"
+ + mucOptions.getConversation().getJid().toBareJid()
+ + "' created. pushing default configuration");
+ mXmppConnectionService.pushConferenceConfiguration(mucOptions.getConversation(),
+ IqGenerator.defaultRoomConfiguration(),
+ null);
+ }
+ if (mXmppConnectionService.getPgpEngine() != null) {
+ Element signed = packet.findChild("x", "jabber:x:signed");
+ if (signed != null) {
+ Element status = packet.findChild("status");
+ String msg = status == null ? "" : status.getContent();
+ long keyId = mXmppConnectionService.getPgpEngine().fetchKeyId(mucOptions.getAccount(), msg, signed.getContent());
+ if (keyId != 0) {
+ user.setPgpKeyId(keyId);
+ }
+ }
+ }
+ if (avatar != null) {
+ avatar.owner = from;
+ if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
+ if (user.setAvatar(avatar)) {
+ mXmppConnectionService.getAvatarService().clear(user);
+ }
+ } else if (mXmppConnectionService.isDataSaverDisabled()) {
+ mXmppConnectionService.fetchAvatar(mucOptions.getAccount(), avatar);
+ }
+ }
+ }
+ }
+ } else if (type.equals("unavailable")) {
+ if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) ||
+ packet.getFrom().equals(mucOptions.getConversation().getJid())) {
+ if (codes.contains(MucOptions.STATUS_CODE_CHANGED_NICK)) {
+ mucOptions.mNickChangingInProgress = true;
+ } else if (codes.contains(MucOptions.STATUS_CODE_KICKED)) {
+ mucOptions.setError(MucOptions.Error.KICKED);
+ } else if (codes.contains(MucOptions.STATUS_CODE_BANNED)) {
+ mucOptions.setError(MucOptions.Error.BANNED);
+ } else if (codes.contains(MucOptions.STATUS_CODE_LOST_MEMBERSHIP)) {
+ mucOptions.setError(MucOptions.Error.MEMBERS_ONLY);
+ } else if (codes.contains(MucOptions.STATUS_CODE_AFFILIATION_CHANGE)) {
+ mucOptions.setError(MucOptions.Error.MEMBERS_ONLY);
+ } else if (codes.contains(MucOptions.STATUS_CODE_SHUTDOWN)) {
+ mucOptions.setError(MucOptions.Error.SHUTDOWN);
+ } else {
+ mucOptions.setError(MucOptions.Error.UNKNOWN);
+ Log.d(Config.LOGTAG, "unknown error in conference: " + packet);
+ }
+ } else if (!from.isBareJid()) {
Element item = x.findChild("item");
if (item != null) {
mucOptions.updateUser(parseItem(conversation, item, from));
}
- MucOptions.User user = mucOptions.deleteUser(from);
- if (user != null) {
- mXmppConnectionService.getAvatarService().clear(user);
- }
- }
- } else if (type.equals("error")) {
- Element error = packet.findChild("error");
- if (error != null && error.hasChild("conflict")) {
- if (mucOptions.online()) {
- if (mucOptions.onRenameListener != null) {
- mucOptions.onRenameListener.onFailure();
- }
- } else {
- mucOptions.setError(MucOptions.Error.NICK_IN_USE);
- }
- } else if (error != null && error.hasChild("not-authorized")) {
- mucOptions.setError(MucOptions.Error.PASSWORD_REQUIRED);
- } else if (error != null && error.hasChild("forbidden")) {
- mucOptions.setError(MucOptions.Error.BANNED);
- } else if (error != null && error.hasChild("registration-required")) {
- mucOptions.setError(MucOptions.Error.MEMBERS_ONLY);
- }
- }
- }
- }
+ MucOptions.User user = mucOptions.deleteUser(from);
+ if (user != null) {
+ mXmppConnectionService.getAvatarService().clear(user);
+ }
+ }
+ } else if (type.equals("error")) {
+ Element error = packet.findChild("error");
+ if (error != null && error.hasChild("conflict")) {
+ if (mucOptions.online()) {
+ if (mucOptions.onRenameListener != null) {
+ mucOptions.onRenameListener.onFailure();
+ }
+ } else {
+ mucOptions.setError(MucOptions.Error.NICK_IN_USE);
+ }
+ } else if (error != null && error.hasChild("not-authorized")) {
+ mucOptions.setError(MucOptions.Error.PASSWORD_REQUIRED);
+ } else if (error != null && error.hasChild("forbidden")) {
+ mucOptions.setError(MucOptions.Error.BANNED);
+ } else if (error != null && error.hasChild("registration-required")) {
+ mucOptions.setError(MucOptions.Error.MEMBERS_ONLY);
+ }
+ }
+ }
+ }
- private static List<String> getStatusCodes(Element x) {
- List<String> codes = new ArrayList<>();
- if (x != null) {
- for (Element child : x.getChildren()) {
- if (child.getName().equals("status")) {
- String code = child.getAttribute("code");
- if (code != null) {
- codes.add(code);
- }
- }
- }
- }
- return codes;
- }
+ private static List<String> getStatusCodes(Element x) {
+ List<String> codes = new ArrayList<>();
+ if (x != null) {
+ for (Element child : x.getChildren()) {
+ if (child.getName().equals("status")) {
+ String code = child.getAttribute("code");
+ if (code != null) {
+ codes.add(code);
+ }
+ }
+ }
+ }
+ return codes;
+ }
- public void parseContactPresence(final PresencePacket packet, final Account account) {
- final PresenceGenerator mPresenceGenerator = mXmppConnectionService.getPresenceGenerator();
- final Jid from = packet.getFrom();
- if (from == null || from.equals(account.getJid())) {
- return;
- }
- final String type = packet.getAttribute("type");
- final Contact contact = account.getRoster().getContact(from);
- if (type == null) {
- final String resource = from.isBareJid() ? "" : from.getResourcepart();
- contact.setPresenceName(packet.findChildContent("nick", "http://jabber.org/protocol/nick"));
- Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update"));
+ public void parseContactPresence(final PresencePacket packet, final Account account) {
+ final PresenceGenerator mPresenceGenerator = mXmppConnectionService.getPresenceGenerator();
+ final Jid from = packet.getFrom();
+ if (from == null || from.equals(account.getJid())) {
+ return;
+ }
+ final String type = packet.getAttribute("type");
+ final Contact contact = account.getRoster().getContact(from);
+ if (type == null) {
+ final String resource = from.isBareJid() ? "" : from.getResourcepart();
+ contact.setPresenceName(packet.findChildContent("nick", "http://jabber.org/protocol/nick"));
+ Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update"));
if (avatar != null && (!contact.isSelf() || account.getAvatar() == null)) {
avatar.owner = from.toBareJid();
- if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
+ if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
if (avatar.owner.equals(account.getJid().toBareJid())) {
account.setAvatar(avatar.getFilename());
mXmppConnectionService.databaseBackend.updateAccount(account);
@@ -197,86 +197,86 @@ public class PresenceParser extends AbstractParser implements
} else if (contact.setAvatar(avatar)) {
mXmppConnectionService.getAvatarService().clear(contact);
mXmppConnectionService.updateConversationUi();
- mXmppConnectionService.updateRosterUi();
- }
- } else if (mXmppConnectionService.isDataSaverDisabled()){
- mXmppConnectionService.fetchAvatar(account, avatar);
- }
- }
- int sizeBefore = contact.getPresences().size();
+ mXmppConnectionService.updateRosterUi();
+ }
+ } else if (mXmppConnectionService.isDataSaverDisabled()) {
+ mXmppConnectionService.fetchAvatar(account, avatar);
+ }
+ }
+ int sizeBefore = contact.getPresences().size();
- final String show = packet.findChildContent("show");
- final Element caps = packet.findChild("c", "http://jabber.org/protocol/caps");
- final String message = packet.findChildContent("status");
- final Presence presence = Presence.parse(show, caps, message);
- contact.updatePresence(resource, presence);
- if (presence.hasCaps()) {
- mXmppConnectionService.fetchCaps(account, from, presence);
- }
+ final String show = packet.findChildContent("show");
+ final Element caps = packet.findChild("c", "http://jabber.org/protocol/caps");
+ final String message = packet.findChildContent("status");
+ final Presence presence = Presence.parse(show, caps, message);
+ contact.updatePresence(resource, presence);
+ if (presence.hasCaps()) {
+ mXmppConnectionService.fetchCaps(account, from, presence);
+ }
- final Element idle = packet.findChild("idle","urn:xmpp:idle:1");
- if (idle != null) {
- contact.flagInactive();
- String since = idle.getAttribute("since");
- try {
- contact.setLastseen(AbstractParser.parseTimestamp(since));
- } catch (NullPointerException | ParseException e) {
- contact.setLastseen(System.currentTimeMillis());
- }
- } else {
- contact.flagActive();
- contact.setLastseen(AbstractParser.parseTimestamp(packet));
- }
+ final Element idle = packet.findChild("idle", "urn:xmpp:idle:1");
+ if (idle != null) {
+ contact.flagInactive();
+ String since = idle.getAttribute("since");
+ try {
+ contact.setLastseen(AbstractParser.parseTimestamp(since));
+ } catch (NullPointerException | ParseException e) {
+ contact.setLastseen(System.currentTimeMillis());
+ }
+ } else {
+ contact.flagActive();
+ contact.setLastseen(AbstractParser.parseTimestamp(packet));
+ }
- PgpEngine pgp = mXmppConnectionService.getPgpEngine();
- Element x = packet.findChild("x", "jabber:x:signed");
- if (pgp != null && x != null) {
- Element status = packet.findChild("status");
- String msg = status != null ? status.getContent() : "";
- contact.setPgpKeyId(pgp.fetchKeyId(account, msg, x.getContent()));
- }
- boolean online = sizeBefore < contact.getPresences().size();
- mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, online);
- } else if (type.equals("unavailable")) {
- if (from.isBareJid()) {
- contact.clearPresences();
- } else {
- contact.removePresence(from.getResourcepart());
- }
- mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, false);
- } else if (type.equals("subscribe")) {
- if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
- mXmppConnectionService.sendPresencePacket(account,
- mPresenceGenerator.sendPresenceUpdatesTo(contact));
- } else {
- contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST);
- final Conversation conversation = mXmppConnectionService.findOrCreateConversation(
- account, contact.getJid().toBareJid(), false);
- final String statusMessage = packet.findChildContent("status");
- if (statusMessage != null
- && !statusMessage.isEmpty()
- && conversation.countMessages() == 0) {
- conversation.add(new Message(
- conversation,
- statusMessage,
- Message.ENCRYPTION_NONE,
- Message.STATUS_RECEIVED
- ));
- }
- }
- }
- mXmppConnectionService.updateRosterUi();
- }
+ PgpEngine pgp = mXmppConnectionService.getPgpEngine();
+ Element x = packet.findChild("x", "jabber:x:signed");
+ if (pgp != null && x != null) {
+ Element status = packet.findChild("status");
+ String msg = status != null ? status.getContent() : "";
+ contact.setPgpKeyId(pgp.fetchKeyId(account, msg, x.getContent()));
+ }
+ boolean online = sizeBefore < contact.getPresences().size();
+ mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, online);
+ } else if (type.equals("unavailable")) {
+ if (from.isBareJid()) {
+ contact.clearPresences();
+ } else {
+ contact.removePresence(from.getResourcepart());
+ }
+ mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, false);
+ } else if (type.equals("subscribe")) {
+ if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
+ mXmppConnectionService.sendPresencePacket(account,
+ mPresenceGenerator.sendPresenceUpdatesTo(contact));
+ } else {
+ contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST);
+ final Conversation conversation = mXmppConnectionService.findOrCreateConversation(
+ account, contact.getJid().toBareJid(), false);
+ final String statusMessage = packet.findChildContent("status");
+ if (statusMessage != null
+ && !statusMessage.isEmpty()
+ && conversation.countMessages() == 0) {
+ conversation.add(new Message(
+ conversation,
+ statusMessage,
+ Message.ENCRYPTION_NONE,
+ Message.STATUS_RECEIVED
+ ));
+ }
+ }
+ }
+ mXmppConnectionService.updateRosterUi();
+ }
- @Override
- public void onPresencePacketReceived(Account account, PresencePacket packet) {
- if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) {
- this.parseConferencePresence(packet, account);
- } else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) {
- this.parseConferencePresence(packet, account);
- } else {
- this.parseContactPresence(packet, account);
- }
- }
+ @Override
+ public void onPresencePacketReceived(Account account, PresencePacket packet) {
+ if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) {
+ this.parseConferencePresence(packet, account);
+ } else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) {
+ this.parseConferencePresence(packet, account);
+ } else {
+ this.parseContactPresence(packet, account);
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
index 4ce66edf8..993a42c21 100644
--- a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
+++ b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
@@ -52,1261 +52,1261 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class DatabaseBackend extends SQLiteOpenHelper {
- private static DatabaseBackend instance = null;
-
- public static final String DATABASE_NAME = "history";
- public static final int DATABASE_VERSION = 31;
-
- private static String CREATE_CONTATCS_STATEMENT = "create table "
- + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
- + Contact.SERVERNAME + " TEXT, " + Contact.SYSTEMNAME + " TEXT,"
- + Contact.JID + " TEXT," + Contact.KEYS + " TEXT,"
- + Contact.PHOTOURI + " TEXT," + Contact.OPTIONS + " NUMBER,"
- + Contact.SYSTEMACCOUNT + " NUMBER, " + Contact.AVATAR + " TEXT, "
- + Contact.LAST_PRESENCE + " TEXT, " + Contact.LAST_TIME + " NUMBER, "
- + Contact.GROUPS + " TEXT, FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
- + Account.TABLENAME + "(" + Account.UUID
- + ") ON DELETE CASCADE, UNIQUE(" + Contact.ACCOUNT + ", "
- + Contact.JID + ") ON CONFLICT REPLACE);";
-
- private static String CREATE_DISCOVERY_RESULTS_STATEMENT = "create table "
- + ServiceDiscoveryResult.TABLENAME + "("
- + ServiceDiscoveryResult.HASH + " TEXT, "
- + ServiceDiscoveryResult.VER + " TEXT, "
- + ServiceDiscoveryResult.RESULT + " TEXT, "
- + "UNIQUE(" + ServiceDiscoveryResult.HASH + ", "
- + ServiceDiscoveryResult.VER + ") ON CONFLICT REPLACE);";
-
- private static String CREATE_PRESENCE_TEMPLATES_STATEMENT = "CREATE TABLE "
- + PresenceTemplate.TABELNAME + "("
- + PresenceTemplate.UUID + " TEXT, "
- + PresenceTemplate.LAST_USED + " NUMBER,"
- + PresenceTemplate.MESSAGE + " TEXT,"
- + PresenceTemplate.STATUS + " TEXT,"
- + "UNIQUE("+PresenceTemplate.MESSAGE + "," +PresenceTemplate.STATUS+") ON CONFLICT REPLACE);";
-
- private static String CREATE_PREKEYS_STATEMENT = "CREATE TABLE "
- + SQLiteAxolotlStore.PREKEY_TABLENAME + "("
- + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
- + SQLiteAxolotlStore.ID + " INTEGER, "
- + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
- + SQLiteAxolotlStore.ACCOUNT
- + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
- + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
- + SQLiteAxolotlStore.ID
- + ") ON CONFLICT REPLACE"
- + ");";
-
- private static String CREATE_SIGNED_PREKEYS_STATEMENT = "CREATE TABLE "
- + SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME + "("
- + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
- + SQLiteAxolotlStore.ID + " INTEGER, "
- + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
- + SQLiteAxolotlStore.ACCOUNT
- + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
- + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
- + SQLiteAxolotlStore.ID
- + ") ON CONFLICT REPLACE" +
- ");";
-
- private static String CREATE_SESSIONS_STATEMENT = "CREATE TABLE "
- + SQLiteAxolotlStore.SESSION_TABLENAME + "("
- + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
- + SQLiteAxolotlStore.NAME + " TEXT, "
- + SQLiteAxolotlStore.DEVICE_ID + " INTEGER, "
- + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
- + SQLiteAxolotlStore.ACCOUNT
- + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
- + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
- + SQLiteAxolotlStore.NAME + ", "
- + SQLiteAxolotlStore.DEVICE_ID
- + ") ON CONFLICT REPLACE"
- + ");";
-
- private static String CREATE_IDENTITIES_STATEMENT = "CREATE TABLE "
- + SQLiteAxolotlStore.IDENTITIES_TABLENAME + "("
- + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
- + SQLiteAxolotlStore.NAME + " TEXT, "
- + SQLiteAxolotlStore.OWN + " INTEGER, "
- + SQLiteAxolotlStore.FINGERPRINT + " TEXT, "
- + SQLiteAxolotlStore.CERTIFICATE + " BLOB, "
- + SQLiteAxolotlStore.TRUST + " TEXT, "
- + SQLiteAxolotlStore.ACTIVE + " NUMBER, "
- + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
- + SQLiteAxolotlStore.ACCOUNT
- + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
- + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
- + SQLiteAxolotlStore.NAME + ", "
- + SQLiteAxolotlStore.FINGERPRINT
- + ") ON CONFLICT IGNORE"
- + ");";
-
- private static String START_TIMES_TABLE = "start_times";
- private static String CREATE_START_TIMES_TABLE = "create table "+START_TIMES_TABLE+" (timestamp NUMBER);";
-
- private DatabaseBackend(Context context) {
- super(context, DATABASE_NAME, null, DATABASE_VERSION);
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- db.execSQL("PRAGMA foreign_keys=ON;");
- db.execSQL("create table " + Account.TABLENAME + "(" + Account.UUID+ " TEXT PRIMARY KEY,"
- + Account.USERNAME + " TEXT,"
- + Account.SERVER + " TEXT,"
- + Account.PASSWORD + " TEXT,"
- + Account.DISPLAY_NAME + " TEXT, "
- + Account.STATUS + " TEXT,"
- + Account.STATUS_MESSAGE + " TEXT,"
- + Account.ROSTERVERSION + " TEXT,"
- + Account.OPTIONS + " NUMBER, "
- + Account.AVATAR + " TEXT, "
- + Account.KEYS + " TEXT, "
- + Account.HOSTNAME + " TEXT, "
- + Account.PORT + " NUMBER DEFAULT 5222)");
- db.execSQL("create table " + Conversation.TABLENAME + " ("
- + Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME
- + " TEXT, " + Conversation.CONTACT + " TEXT, "
- + Conversation.ACCOUNT + " TEXT, " + Conversation.CONTACTJID
- + " TEXT, " + Conversation.CREATED + " NUMBER, "
- + Conversation.STATUS + " NUMBER, " + Conversation.MODE
- + " NUMBER, " + Conversation.ATTRIBUTES + " TEXT, FOREIGN KEY("
- + Conversation.ACCOUNT + ") REFERENCES " + Account.TABLENAME
- + "(" + Account.UUID + ") ON DELETE CASCADE);");
- db.execSQL("create table " + Message.TABLENAME + "( " + Message.UUID
- + " TEXT PRIMARY KEY, " + Message.CONVERSATION + " TEXT, "
- + Message.TIME_SENT + " NUMBER, " + Message.COUNTERPART
- + " TEXT, " + Message.TRUE_COUNTERPART + " TEXT,"
- + Message.BODY + " TEXT, " + Message.ENCRYPTION + " NUMBER, "
- + Message.STATUS + " NUMBER," + Message.TYPE + " NUMBER, "
- + Message.RELATIVE_FILE_PATH + " TEXT, "
- + Message.SERVER_MSG_ID + " TEXT, "
- + Message.FINGERPRINT + " TEXT, "
- + Message.CARBON + " INTEGER, "
- + Message.EDITED + " TEXT, "
- + Message.READ + " NUMBER DEFAULT 1, "
- + Message.OOB + " INTEGER, "
- + Message.ERROR_MESSAGE + " TEXT,"
- + Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY("
- + Message.CONVERSATION + ") REFERENCES "
- + Conversation.TABLENAME + "(" + Conversation.UUID
- + ") ON DELETE CASCADE);");
-
- db.execSQL(CREATE_CONTATCS_STATEMENT);
- db.execSQL(CREATE_DISCOVERY_RESULTS_STATEMENT);
- db.execSQL(CREATE_SESSIONS_STATEMENT);
- db.execSQL(CREATE_PREKEYS_STATEMENT);
- db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
- db.execSQL(CREATE_IDENTITIES_STATEMENT);
- db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
- db.execSQL(CREATE_START_TIMES_TABLE);
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion < 2 && newVersion >= 2) {
- db.execSQL("update " + Account.TABLENAME + " set "
- + Account.OPTIONS + " = " + Account.OPTIONS + " | 8");
- }
- if (oldVersion < 3 && newVersion >= 3) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.TYPE + " NUMBER");
- }
- if (oldVersion < 5 && newVersion >= 5) {
- db.execSQL("DROP TABLE " + Contact.TABLENAME);
- db.execSQL(CREATE_CONTATCS_STATEMENT);
- db.execSQL("UPDATE " + Account.TABLENAME + " SET "
- + Account.ROSTERVERSION + " = NULL");
- }
- if (oldVersion < 6 && newVersion >= 6) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.TRUE_COUNTERPART + " TEXT");
- }
- if (oldVersion < 7 && newVersion >= 7) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.REMOTE_MSG_ID + " TEXT");
- db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
- + Contact.AVATAR + " TEXT");
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN "
- + Account.AVATAR + " TEXT");
- }
- if (oldVersion < 8 && newVersion >= 8) {
- db.execSQL("ALTER TABLE " + Conversation.TABLENAME + " ADD COLUMN "
- + Conversation.ATTRIBUTES + " TEXT");
- }
- if (oldVersion < 9 && newVersion >= 9) {
- db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
- + Contact.LAST_TIME + " NUMBER");
- db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
- + Contact.LAST_PRESENCE + " TEXT");
- }
- if (oldVersion < 10 && newVersion >= 10) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.RELATIVE_FILE_PATH + " TEXT");
- }
- if (oldVersion < 11 && newVersion >= 11) {
- db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
- + Contact.GROUPS + " TEXT");
- db.execSQL("delete from " + Contact.TABLENAME);
- db.execSQL("update " + Account.TABLENAME + " set " + Account.ROSTERVERSION + " = NULL");
- }
- if (oldVersion < 12 && newVersion >= 12) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.SERVER_MSG_ID + " TEXT");
- }
- if (oldVersion < 13 && newVersion >= 13) {
- db.execSQL("delete from " + Contact.TABLENAME);
- db.execSQL("update " + Account.TABLENAME + " set " + Account.ROSTERVERSION + " = NULL");
- }
- if (oldVersion < 14 && newVersion >= 14) {
- canonicalizeJids(db);
- }
- if (oldVersion < 15 && newVersion >= 15) {
- recreateAxolotlDb(db);
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.FINGERPRINT + " TEXT");
- } else if (oldVersion < 22 && newVersion >= 22) {
- db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.CERTIFICATE);
- }
- if (oldVersion < 16 && newVersion >= 16) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.CARBON + " INTEGER");
- }
- if (oldVersion < 19 && newVersion >= 19) {
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.DISPLAY_NAME + " TEXT");
- }
- if (oldVersion < 20 && newVersion >= 20) {
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.HOSTNAME + " TEXT");
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.PORT + " NUMBER DEFAULT 5222");
- }
- if (oldVersion < 26 && newVersion >= 26) {
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS + " TEXT");
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS_MESSAGE + " TEXT");
- }
- /* Any migrations that alter the Account table need to happen BEFORE this migration, as it
+ private static DatabaseBackend instance = null;
+
+ public static final String DATABASE_NAME = "history";
+ public static final int DATABASE_VERSION = 31;
+
+ private static String CREATE_CONTATCS_STATEMENT = "create table "
+ + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
+ + Contact.SERVERNAME + " TEXT, " + Contact.SYSTEMNAME + " TEXT,"
+ + Contact.JID + " TEXT," + Contact.KEYS + " TEXT,"
+ + Contact.PHOTOURI + " TEXT," + Contact.OPTIONS + " NUMBER,"
+ + Contact.SYSTEMACCOUNT + " NUMBER, " + Contact.AVATAR + " TEXT, "
+ + Contact.LAST_PRESENCE + " TEXT, " + Contact.LAST_TIME + " NUMBER, "
+ + Contact.GROUPS + " TEXT, FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
+ + Account.TABLENAME + "(" + Account.UUID
+ + ") ON DELETE CASCADE, UNIQUE(" + Contact.ACCOUNT + ", "
+ + Contact.JID + ") ON CONFLICT REPLACE);";
+
+ private static String CREATE_DISCOVERY_RESULTS_STATEMENT = "create table "
+ + ServiceDiscoveryResult.TABLENAME + "("
+ + ServiceDiscoveryResult.HASH + " TEXT, "
+ + ServiceDiscoveryResult.VER + " TEXT, "
+ + ServiceDiscoveryResult.RESULT + " TEXT, "
+ + "UNIQUE(" + ServiceDiscoveryResult.HASH + ", "
+ + ServiceDiscoveryResult.VER + ") ON CONFLICT REPLACE);";
+
+ private static String CREATE_PRESENCE_TEMPLATES_STATEMENT = "CREATE TABLE "
+ + PresenceTemplate.TABELNAME + "("
+ + PresenceTemplate.UUID + " TEXT, "
+ + PresenceTemplate.LAST_USED + " NUMBER,"
+ + PresenceTemplate.MESSAGE + " TEXT,"
+ + PresenceTemplate.STATUS + " TEXT,"
+ + "UNIQUE(" + PresenceTemplate.MESSAGE + "," + PresenceTemplate.STATUS + ") ON CONFLICT REPLACE);";
+
+ private static String CREATE_PREKEYS_STATEMENT = "CREATE TABLE "
+ + SQLiteAxolotlStore.PREKEY_TABLENAME + "("
+ + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
+ + SQLiteAxolotlStore.ID + " INTEGER, "
+ + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
+ + SQLiteAxolotlStore.ACCOUNT
+ + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
+ + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
+ + SQLiteAxolotlStore.ID
+ + ") ON CONFLICT REPLACE"
+ + ");";
+
+ private static String CREATE_SIGNED_PREKEYS_STATEMENT = "CREATE TABLE "
+ + SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME + "("
+ + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
+ + SQLiteAxolotlStore.ID + " INTEGER, "
+ + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
+ + SQLiteAxolotlStore.ACCOUNT
+ + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
+ + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
+ + SQLiteAxolotlStore.ID
+ + ") ON CONFLICT REPLACE" +
+ ");";
+
+ private static String CREATE_SESSIONS_STATEMENT = "CREATE TABLE "
+ + SQLiteAxolotlStore.SESSION_TABLENAME + "("
+ + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
+ + SQLiteAxolotlStore.NAME + " TEXT, "
+ + SQLiteAxolotlStore.DEVICE_ID + " INTEGER, "
+ + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
+ + SQLiteAxolotlStore.ACCOUNT
+ + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
+ + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
+ + SQLiteAxolotlStore.NAME + ", "
+ + SQLiteAxolotlStore.DEVICE_ID
+ + ") ON CONFLICT REPLACE"
+ + ");";
+
+ private static String CREATE_IDENTITIES_STATEMENT = "CREATE TABLE "
+ + SQLiteAxolotlStore.IDENTITIES_TABLENAME + "("
+ + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
+ + SQLiteAxolotlStore.NAME + " TEXT, "
+ + SQLiteAxolotlStore.OWN + " INTEGER, "
+ + SQLiteAxolotlStore.FINGERPRINT + " TEXT, "
+ + SQLiteAxolotlStore.CERTIFICATE + " BLOB, "
+ + SQLiteAxolotlStore.TRUST + " TEXT, "
+ + SQLiteAxolotlStore.ACTIVE + " NUMBER, "
+ + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
+ + SQLiteAxolotlStore.ACCOUNT
+ + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
+ + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
+ + SQLiteAxolotlStore.NAME + ", "
+ + SQLiteAxolotlStore.FINGERPRINT
+ + ") ON CONFLICT IGNORE"
+ + ");";
+
+ private static String START_TIMES_TABLE = "start_times";
+ private static String CREATE_START_TIMES_TABLE = "create table " + START_TIMES_TABLE + " (timestamp NUMBER);";
+
+ private DatabaseBackend(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("PRAGMA foreign_keys=ON;");
+ db.execSQL("create table " + Account.TABLENAME + "(" + Account.UUID + " TEXT PRIMARY KEY,"
+ + Account.USERNAME + " TEXT,"
+ + Account.SERVER + " TEXT,"
+ + Account.PASSWORD + " TEXT,"
+ + Account.DISPLAY_NAME + " TEXT, "
+ + Account.STATUS + " TEXT,"
+ + Account.STATUS_MESSAGE + " TEXT,"
+ + Account.ROSTERVERSION + " TEXT,"
+ + Account.OPTIONS + " NUMBER, "
+ + Account.AVATAR + " TEXT, "
+ + Account.KEYS + " TEXT, "
+ + Account.HOSTNAME + " TEXT, "
+ + Account.PORT + " NUMBER DEFAULT 5222)");
+ db.execSQL("create table " + Conversation.TABLENAME + " ("
+ + Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME
+ + " TEXT, " + Conversation.CONTACT + " TEXT, "
+ + Conversation.ACCOUNT + " TEXT, " + Conversation.CONTACTJID
+ + " TEXT, " + Conversation.CREATED + " NUMBER, "
+ + Conversation.STATUS + " NUMBER, " + Conversation.MODE
+ + " NUMBER, " + Conversation.ATTRIBUTES + " TEXT, FOREIGN KEY("
+ + Conversation.ACCOUNT + ") REFERENCES " + Account.TABLENAME
+ + "(" + Account.UUID + ") ON DELETE CASCADE);");
+ db.execSQL("create table " + Message.TABLENAME + "( " + Message.UUID
+ + " TEXT PRIMARY KEY, " + Message.CONVERSATION + " TEXT, "
+ + Message.TIME_SENT + " NUMBER, " + Message.COUNTERPART
+ + " TEXT, " + Message.TRUE_COUNTERPART + " TEXT,"
+ + Message.BODY + " TEXT, " + Message.ENCRYPTION + " NUMBER, "
+ + Message.STATUS + " NUMBER," + Message.TYPE + " NUMBER, "
+ + Message.RELATIVE_FILE_PATH + " TEXT, "
+ + Message.SERVER_MSG_ID + " TEXT, "
+ + Message.FINGERPRINT + " TEXT, "
+ + Message.CARBON + " INTEGER, "
+ + Message.EDITED + " TEXT, "
+ + Message.READ + " NUMBER DEFAULT 1, "
+ + Message.OOB + " INTEGER, "
+ + Message.ERROR_MESSAGE + " TEXT,"
+ + Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY("
+ + Message.CONVERSATION + ") REFERENCES "
+ + Conversation.TABLENAME + "(" + Conversation.UUID
+ + ") ON DELETE CASCADE);");
+
+ db.execSQL(CREATE_CONTATCS_STATEMENT);
+ db.execSQL(CREATE_DISCOVERY_RESULTS_STATEMENT);
+ db.execSQL(CREATE_SESSIONS_STATEMENT);
+ db.execSQL(CREATE_PREKEYS_STATEMENT);
+ db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
+ db.execSQL(CREATE_IDENTITIES_STATEMENT);
+ db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
+ db.execSQL(CREATE_START_TIMES_TABLE);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ if (oldVersion < 2 && newVersion >= 2) {
+ db.execSQL("update " + Account.TABLENAME + " set "
+ + Account.OPTIONS + " = " + Account.OPTIONS + " | 8");
+ }
+ if (oldVersion < 3 && newVersion >= 3) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.TYPE + " NUMBER");
+ }
+ if (oldVersion < 5 && newVersion >= 5) {
+ db.execSQL("DROP TABLE " + Contact.TABLENAME);
+ db.execSQL(CREATE_CONTATCS_STATEMENT);
+ db.execSQL("UPDATE " + Account.TABLENAME + " SET "
+ + Account.ROSTERVERSION + " = NULL");
+ }
+ if (oldVersion < 6 && newVersion >= 6) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.TRUE_COUNTERPART + " TEXT");
+ }
+ if (oldVersion < 7 && newVersion >= 7) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.REMOTE_MSG_ID + " TEXT");
+ db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
+ + Contact.AVATAR + " TEXT");
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN "
+ + Account.AVATAR + " TEXT");
+ }
+ if (oldVersion < 8 && newVersion >= 8) {
+ db.execSQL("ALTER TABLE " + Conversation.TABLENAME + " ADD COLUMN "
+ + Conversation.ATTRIBUTES + " TEXT");
+ }
+ if (oldVersion < 9 && newVersion >= 9) {
+ db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
+ + Contact.LAST_TIME + " NUMBER");
+ db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
+ + Contact.LAST_PRESENCE + " TEXT");
+ }
+ if (oldVersion < 10 && newVersion >= 10) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.RELATIVE_FILE_PATH + " TEXT");
+ }
+ if (oldVersion < 11 && newVersion >= 11) {
+ db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
+ + Contact.GROUPS + " TEXT");
+ db.execSQL("delete from " + Contact.TABLENAME);
+ db.execSQL("update " + Account.TABLENAME + " set " + Account.ROSTERVERSION + " = NULL");
+ }
+ if (oldVersion < 12 && newVersion >= 12) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.SERVER_MSG_ID + " TEXT");
+ }
+ if (oldVersion < 13 && newVersion >= 13) {
+ db.execSQL("delete from " + Contact.TABLENAME);
+ db.execSQL("update " + Account.TABLENAME + " set " + Account.ROSTERVERSION + " = NULL");
+ }
+ if (oldVersion < 14 && newVersion >= 14) {
+ canonicalizeJids(db);
+ }
+ if (oldVersion < 15 && newVersion >= 15) {
+ recreateAxolotlDb(db);
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.FINGERPRINT + " TEXT");
+ } else if (oldVersion < 22 && newVersion >= 22) {
+ db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.CERTIFICATE);
+ }
+ if (oldVersion < 16 && newVersion >= 16) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.CARBON + " INTEGER");
+ }
+ if (oldVersion < 19 && newVersion >= 19) {
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.DISPLAY_NAME + " TEXT");
+ }
+ if (oldVersion < 20 && newVersion >= 20) {
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.HOSTNAME + " TEXT");
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.PORT + " NUMBER DEFAULT 5222");
+ }
+ if (oldVersion < 26 && newVersion >= 26) {
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS + " TEXT");
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS_MESSAGE + " TEXT");
+ }
+ /* Any migrations that alter the Account table need to happen BEFORE this migration, as it
* depends on account de-serialization.
*/
- if (oldVersion < 17 && newVersion >= 17) {
- List<Account> accounts = getAccounts(db);
- for (Account account : accounts) {
- String ownDeviceIdString = account.getKey(SQLiteAxolotlStore.JSONKEY_REGISTRATION_ID);
- if (ownDeviceIdString == null) {
- continue;
- }
- int ownDeviceId = Integer.valueOf(ownDeviceIdString);
- AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), ownDeviceId);
- deleteSession(db, account, ownAddress);
- IdentityKeyPair identityKeyPair = loadOwnIdentityKeyPair(db, account);
- if (identityKeyPair != null) {
- String[] selectionArgs = {
- account.getUuid(),
- identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", "")
- };
- ContentValues values = new ContentValues();
- values.put(SQLiteAxolotlStore.TRUSTED, 2);
- db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, values,
- SQLiteAxolotlStore.ACCOUNT + " = ? AND "
- + SQLiteAxolotlStore.FINGERPRINT + " = ? ",
- selectionArgs);
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not load own identity key pair");
- }
- }
- }
- if (oldVersion < 18 && newVersion >= 18) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.READ + " NUMBER DEFAULT 1");
- }
-
- if (oldVersion < 21 && newVersion >= 21) {
- List<Account> accounts = getAccounts(db);
- for (Account account : accounts) {
- account.unsetPgpSignature();
- db.update(Account.TABLENAME, account.getContentValues(), Account.UUID
- + "=?", new String[]{account.getUuid()});
- }
- }
-
- if (oldVersion < 23 && newVersion >= 23) {
- db.execSQL(CREATE_DISCOVERY_RESULTS_STATEMENT);
- }
-
- if (oldVersion < 24 && newVersion >= 24) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.EDITED + " TEXT");
- }
-
- if (oldVersion < 25 && newVersion >= 25) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.OOB + " INTEGER");
- }
-
- if (oldVersion < 26 && newVersion >= 26) {
- db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
- }
-
- if (oldVersion < 27 && newVersion >= 27) {
- db.execSQL("DELETE FROM "+ServiceDiscoveryResult.TABLENAME);
- }
-
- if (oldVersion < 28 && newVersion >= 28) {
- canonicalizeJids(db);
- }
-
- if (oldVersion < 29 && newVersion >= 29) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.ERROR_MESSAGE + " TEXT");
- }
-
- if (oldVersion < 30 && newVersion >= 30) {
- db.execSQL(CREATE_START_TIMES_TABLE);
- }
- if (oldVersion < 31 && newVersion >= 31) {
- db.execSQL("ALTER TABLE "+ SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN "+SQLiteAxolotlStore.TRUST + " TEXT");
- db.execSQL("ALTER TABLE "+ SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN "+SQLiteAxolotlStore.ACTIVE + " NUMBER");
- HashMap<Integer,ContentValues> migration = new HashMap<>();
- migration.put(0,createFingerprintStatusContentValues(FingerprintStatus.Trust.UNDECIDED,true));
- migration.put(1,createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, true));
- migration.put(2,createFingerprintStatusContentValues(FingerprintStatus.Trust.UNTRUSTED, true));
- migration.put(3,createFingerprintStatusContentValues(FingerprintStatus.Trust.COMPROMISED, false));
- migration.put(4,createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, false));
- migration.put(5,createFingerprintStatusContentValues(FingerprintStatus.Trust.UNDECIDED, false));
- migration.put(6,createFingerprintStatusContentValues(FingerprintStatus.Trust.UNTRUSTED, false));
- migration.put(7,createFingerprintStatusContentValues(FingerprintStatus.Trust.VERIFIED_X509, true));
- migration.put(8,createFingerprintStatusContentValues(FingerprintStatus.Trust.VERIFIED_X509, false));
- for(Map.Entry<Integer,ContentValues> entry : migration.entrySet()) {
- String whereClause = SQLiteAxolotlStore.TRUSTED+"=?";
- String[] where = {String.valueOf(entry.getKey())};
- db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME,entry.getValue(),whereClause,where);
- }
- }
- }
-
- private static ContentValues createFingerprintStatusContentValues(FingerprintStatus.Trust trust, boolean active) {
- ContentValues values = new ContentValues();
- values.put(SQLiteAxolotlStore.TRUST,trust.toString());
- values.put(SQLiteAxolotlStore.ACTIVE,active ? 1 : 0);
- return values;
- }
-
- private void canonicalizeJids(SQLiteDatabase db) {
- // migrate db to new, canonicalized JID domainpart representation
-
- // Conversation table
- Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME, new String[0]);
- while (cursor.moveToNext()) {
- String newJid;
- try {
- newJid = Jid.fromString(
- cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID))
- ).toPreppedString();
- } catch (InvalidJidException ignored) {
- Log.e(Config.LOGTAG, "Failed to migrate Conversation CONTACTJID "
- + cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID))
- + ": " + ignored + ". Skipping...");
- continue;
- }
-
- String updateArgs[] = {
- newJid,
- cursor.getString(cursor.getColumnIndex(Conversation.UUID)),
- };
- db.execSQL("update " + Conversation.TABLENAME
- + " set " + Conversation.CONTACTJID + " = ? "
- + " where " + Conversation.UUID + " = ?", updateArgs);
- }
- cursor.close();
-
- // Contact table
- cursor = db.rawQuery("select * from " + Contact.TABLENAME, new String[0]);
- while (cursor.moveToNext()) {
- String newJid;
- try {
- newJid = Jid.fromString(
- cursor.getString(cursor.getColumnIndex(Contact.JID))
- ).toPreppedString();
- } catch (InvalidJidException ignored) {
- Log.e(Config.LOGTAG, "Failed to migrate Contact JID "
- + cursor.getString(cursor.getColumnIndex(Contact.JID))
- + ": " + ignored + ". Skipping...");
- continue;
- }
-
- String updateArgs[] = {
- newJid,
- cursor.getString(cursor.getColumnIndex(Contact.ACCOUNT)),
- cursor.getString(cursor.getColumnIndex(Contact.JID)),
- };
- db.execSQL("update " + Contact.TABLENAME
- + " set " + Contact.JID + " = ? "
- + " where " + Contact.ACCOUNT + " = ? "
- + " AND " + Contact.JID + " = ?", updateArgs);
- }
- cursor.close();
-
- // Account table
- cursor = db.rawQuery("select * from " + Account.TABLENAME, new String[0]);
- while (cursor.moveToNext()) {
- String newServer;
- try {
- newServer = Jid.fromParts(
- cursor.getString(cursor.getColumnIndex(Account.USERNAME)),
- cursor.getString(cursor.getColumnIndex(Account.SERVER)),
- "mobile"
- ).getDomainpart();
- } catch (InvalidJidException ignored) {
- Log.e(Config.LOGTAG, "Failed to migrate Account SERVER "
- + cursor.getString(cursor.getColumnIndex(Account.SERVER))
- + ": " + ignored + ". Skipping...");
- continue;
- }
-
- String updateArgs[] = {
- newServer,
- cursor.getString(cursor.getColumnIndex(Account.UUID)),
- };
- db.execSQL("update " + Account.TABLENAME
- + " set " + Account.SERVER + " = ? "
- + " where " + Account.UUID + " = ?", updateArgs);
- }
- cursor.close();
- }
-
- public static synchronized DatabaseBackend getInstance(Context context) {
- if (instance == null) {
- instance = new DatabaseBackend(context);
- }
- return instance;
- }
-
- public void createConversation(Conversation conversation) {
- SQLiteDatabase db = this.getWritableDatabase();
- db.insert(Conversation.TABLENAME, null, conversation.getContentValues());
- }
-
- public void createMessage(Message message) {
- SQLiteDatabase db = this.getWritableDatabase();
- db.insert(Message.TABLENAME, null, message.getContentValues());
- }
-
- public void createAccount(Account account) {
- SQLiteDatabase db = this.getWritableDatabase();
- db.insert(Account.TABLENAME, null, account.getContentValues());
- }
-
- public void insertDiscoveryResult(ServiceDiscoveryResult result) {
- SQLiteDatabase db = this.getWritableDatabase();
- db.insert(ServiceDiscoveryResult.TABLENAME, null, result.getContentValues());
- }
-
- public ServiceDiscoveryResult findDiscoveryResult(final String hash, final String ver) {
- SQLiteDatabase db = this.getReadableDatabase();
- String[] selectionArgs = {hash, ver};
- Cursor cursor = db.query(ServiceDiscoveryResult.TABLENAME, null,
- ServiceDiscoveryResult.HASH + "=? AND " + ServiceDiscoveryResult.VER + "=?",
- selectionArgs, null, null, null);
- if (cursor.getCount() == 0) {
- cursor.close();
- return null;
- }
- cursor.moveToFirst();
-
- ServiceDiscoveryResult result = null;
- try {
- result = new ServiceDiscoveryResult(cursor);
- } catch (JSONException e) { /* result is still null */ }
-
- cursor.close();
- return result;
- }
-
- public void insertPresenceTemplate(PresenceTemplate template) {
- SQLiteDatabase db = this.getWritableDatabase();
- db.insert(PresenceTemplate.TABELNAME, null, template.getContentValues());
- }
-
- public List<PresenceTemplate> getPresenceTemplates() {
- ArrayList<PresenceTemplate> templates = new ArrayList<>();
- SQLiteDatabase db = this.getReadableDatabase();
- Cursor cursor = db.query(PresenceTemplate.TABELNAME,null,null,null,null,null,PresenceTemplate.LAST_USED+" desc");
- while (cursor.moveToNext()) {
- templates.add(PresenceTemplate.fromCursor(cursor));
- }
- cursor.close();
- return templates;
- }
-
- public void deletePresenceTemplate(PresenceTemplate template) {
- Log.d(Config.LOGTAG,"deleting presence template with uuid "+template.getUuid());
- SQLiteDatabase db = this.getWritableDatabase();
- String where = PresenceTemplate.UUID+"=?";
- String[] whereArgs = {template.getUuid()};
- db.delete(PresenceTemplate.TABELNAME,where,whereArgs);
- }
-
- public CopyOnWriteArrayList<Conversation> getConversations(int status) {
- CopyOnWriteArrayList<Conversation> list = new CopyOnWriteArrayList<>();
- SQLiteDatabase db = this.getReadableDatabase();
- String[] selectionArgs = {Integer.toString(status)};
- Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME
- + " where " + Conversation.STATUS + " = ? order by "
- + Conversation.CREATED + " desc", selectionArgs);
- while (cursor.moveToNext()) {
- list.add(Conversation.fromCursor(cursor));
- }
- cursor.close();
- return list;
- }
-
- public ArrayList<Message> getMessages(Conversation conversations, int limit) {
- return getMessages(conversations, limit, -1);
- }
-
- public ArrayList<Message> getMessages(Conversation conversation, int limit,
- long timestamp) {
- ArrayList<Message> list = new ArrayList<>();
- SQLiteDatabase db = this.getReadableDatabase();
- Cursor cursor;
- if (timestamp == -1) {
- String[] selectionArgs = {conversation.getUuid()};
- cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
- + "=?", selectionArgs, null, null, Message.TIME_SENT
- + " DESC", String.valueOf(limit));
- } else {
- String[] selectionArgs = {conversation.getUuid(),
- Long.toString(timestamp)};
- cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
- + "=? and " + Message.TIME_SENT + "<?", selectionArgs,
- null, null, Message.TIME_SENT + " DESC",
- String.valueOf(limit));
- }
- if (cursor.getCount() > 0) {
- cursor.moveToLast();
- do {
- Message message = Message.fromCursor(cursor);
- message.setConversation(conversation);
- list.add(message);
- } while (cursor.moveToPrevious());
- }
- cursor.close();
- return list;
- }
-
- public Iterable<Message> getMessagesIterable(final Conversation conversation) {
- return new Iterable<Message>() {
- @Override
- public Iterator<Message> iterator() {
- class MessageIterator implements Iterator<Message> {
- SQLiteDatabase db = getReadableDatabase();
- String[] selectionArgs = {conversation.getUuid()};
- Cursor cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
- + "=?", selectionArgs, null, null, Message.TIME_SENT
- + " ASC", null);
-
- public MessageIterator() {
- cursor.moveToFirst();
- }
-
- @Override
- public boolean hasNext() {
- return !cursor.isAfterLast();
- }
-
- @Override
- public Message next() {
- Message message = Message.fromCursor(cursor);
- cursor.moveToNext();
- return message;
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- }
- return new MessageIterator();
- }
- };
- }
-
- public Conversation findConversation(final Account account, final Jid contactJid) {
- SQLiteDatabase db = this.getReadableDatabase();
- String[] selectionArgs = {account.getUuid(),
- contactJid.toBareJid().toPreppedString() + "/%",
- contactJid.toBareJid().toPreppedString()
- };
- Cursor cursor = db.query(Conversation.TABLENAME, null,
- Conversation.ACCOUNT + "=? AND (" + Conversation.CONTACTJID
- + " like ? OR " + Conversation.CONTACTJID + "=?)", selectionArgs, null, null, null);
- if (cursor.getCount() == 0) {
- cursor.close();
- return null;
- }
- cursor.moveToFirst();
- Conversation conversation = Conversation.fromCursor(cursor);
- cursor.close();
- return conversation;
- }
-
- public void updateConversation(final Conversation conversation) {
- final SQLiteDatabase db = this.getWritableDatabase();
- final String[] args = {conversation.getUuid()};
- db.update(Conversation.TABLENAME, conversation.getContentValues(),
- Conversation.UUID + "=?", args);
- }
-
- public List<Account> getAccounts() {
- SQLiteDatabase db = this.getReadableDatabase();
- return getAccounts(db);
- }
-
- private List<Account> getAccounts(SQLiteDatabase db) {
- List<Account> list = new ArrayList<>();
- Cursor cursor = db.query(Account.TABLENAME, null, null, null, null,
- null, null);
- while (cursor.moveToNext()) {
- list.add(Account.fromCursor(cursor));
- }
- cursor.close();
- return list;
- }
-
- public boolean updateAccount(Account account) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {account.getUuid()};
- final int rows = db.update(Account.TABLENAME, account.getContentValues(), Account.UUID + "=?", args);
- return rows == 1;
- }
-
- public boolean deleteAccount(Account account) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {account.getUuid()};
- final int rows = db.delete(Account.TABLENAME, Account.UUID + "=?", args);
- return rows == 1;
- }
-
- public boolean hasEnabledAccounts() {
- SQLiteDatabase db = this.getReadableDatabase();
- Cursor cursor = db.rawQuery("select count(" + Account.UUID + ") from "
- + Account.TABLENAME + " where not options & (1 <<1)", null);
- try {
- cursor.moveToFirst();
- int count = cursor.getInt(0);
- return (count > 0);
- } catch (SQLiteCantOpenDatabaseException e) {
- return true; // better safe than sorry
- } catch (RuntimeException e) {
- return true; // better safe than sorry
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- @Override
- public SQLiteDatabase getWritableDatabase() {
- SQLiteDatabase db = super.getWritableDatabase();
- db.execSQL("PRAGMA foreign_keys=ON;");
- return db;
- }
-
- public void updateMessage(Message message) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {message.getUuid()};
- db.update(Message.TABLENAME, message.getContentValues(), Message.UUID
- + "=?", args);
- }
-
- public void updateMessage(Message message, String uuid) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {uuid};
- db.update(Message.TABLENAME, message.getContentValues(), Message.UUID
- + "=?", args);
- }
-
- public void readRoster(Roster roster) {
- SQLiteDatabase db = this.getReadableDatabase();
- Cursor cursor;
- String args[] = {roster.getAccount().getUuid()};
- cursor = db.query(Contact.TABLENAME, null, Contact.ACCOUNT + "=?", args, null, null, null);
- while (cursor.moveToNext()) {
- roster.initContact(Contact.fromCursor(cursor));
- }
- cursor.close();
- }
-
- public void writeRoster(final Roster roster) {
- final Account account = roster.getAccount();
- final SQLiteDatabase db = this.getWritableDatabase();
- db.beginTransaction();
- for (Contact contact : roster.getContacts()) {
- if (contact.getOption(Contact.Options.IN_ROSTER)) {
- db.insert(Contact.TABLENAME, null, contact.getContentValues());
- } else {
- String where = Contact.ACCOUNT + "=? AND " + Contact.JID + "=?";
- String[] whereArgs = {account.getUuid(), contact.getJid().toPreppedString()};
- db.delete(Contact.TABLENAME, where, whereArgs);
- }
- }
- db.setTransactionSuccessful();
- db.endTransaction();
- account.setRosterVersion(roster.getVersion());
- updateAccount(account);
- }
-
- public void deleteMessagesInConversation(Conversation conversation) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {conversation.getUuid()};
- db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args);
- }
-
- public Pair<Long, String> getLastMessageReceived(Account account) {
- Cursor cursor = null;
- try {
- SQLiteDatabase db = this.getReadableDatabase();
- String sql = "select messages.timeSent,messages.serverMsgId from accounts join conversations on accounts.uuid=conversations.accountUuid join messages on conversations.uuid=messages.conversationUuid where accounts.uuid=? and (messages.status=0 or messages.carbon=1 or messages.serverMsgId not null) order by messages.timesent desc limit 1";
- String[] args = {account.getUuid()};
- cursor = db.rawQuery(sql, args);
- if (cursor.getCount() == 0) {
- return null;
- } else {
- cursor.moveToFirst();
- return new Pair<>(cursor.getLong(0), cursor.getString(1));
- }
- } catch (Exception e) {
- return null;
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- public Pair<Long, String> getLastClearDate(Account account) {
- SQLiteDatabase db = this.getReadableDatabase();
- String[] columns = {Conversation.ATTRIBUTES};
- String selection = Conversation.ACCOUNT + "=?";
- String[] args = {account.getUuid()};
- Cursor cursor = db.query(Conversation.TABLENAME, columns, selection, args, null, null, null);
- long maxClearDate = 0;
- while (cursor.moveToNext()) {
- try {
- final JSONObject jsonObject = new JSONObject(cursor.getString(0));
- maxClearDate = Math.max(maxClearDate, jsonObject.getLong(Conversation.ATTRIBUTE_LAST_CLEAR_HISTORY));
- } catch (Exception e) {
- //ignored
- }
- }
- cursor.close();
- return new Pair<>(maxClearDate, null);
- }
-
- private Cursor getCursorForSession(Account account, AxolotlAddress contact) {
- final SQLiteDatabase db = this.getReadableDatabase();
- String[] selectionArgs = {account.getUuid(),
- contact.getName(),
- Integer.toString(contact.getDeviceId())};
- return db.query(SQLiteAxolotlStore.SESSION_TABLENAME,
- null,
- SQLiteAxolotlStore.ACCOUNT + " = ? AND "
- + SQLiteAxolotlStore.NAME + " = ? AND "
- + SQLiteAxolotlStore.DEVICE_ID + " = ? ",
- selectionArgs,
- null, null, null);
- }
-
- public SessionRecord loadSession(Account account, AxolotlAddress contact) {
- SessionRecord session = null;
- Cursor cursor = getCursorForSession(account, contact);
- if (cursor.getCount() != 0) {
- cursor.moveToFirst();
- try {
- session = new SessionRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
- } catch (IOException e) {
- cursor.close();
- throw new AssertionError(e);
- }
- }
- cursor.close();
- return session;
- }
-
- public List<Integer> getSubDeviceSessions(Account account, AxolotlAddress contact) {
- final SQLiteDatabase db = this.getReadableDatabase();
- return getSubDeviceSessions(db, account, contact);
- }
-
- private List<Integer> getSubDeviceSessions(SQLiteDatabase db, Account account, AxolotlAddress contact) {
- List<Integer> devices = new ArrayList<>();
- String[] columns = {SQLiteAxolotlStore.DEVICE_ID};
- String[] selectionArgs = {account.getUuid(),
- contact.getName()};
- Cursor cursor = db.query(SQLiteAxolotlStore.SESSION_TABLENAME,
- columns,
- SQLiteAxolotlStore.ACCOUNT + " = ? AND "
- + SQLiteAxolotlStore.NAME + " = ?",
- selectionArgs,
- null, null, null);
-
- while (cursor.moveToNext()) {
- devices.add(cursor.getInt(
- cursor.getColumnIndex(SQLiteAxolotlStore.DEVICE_ID)));
- }
-
- cursor.close();
- return devices;
- }
-
- public boolean containsSession(Account account, AxolotlAddress contact) {
- Cursor cursor = getCursorForSession(account, contact);
- int count = cursor.getCount();
- cursor.close();
- return count != 0;
- }
-
- public void storeSession(Account account, AxolotlAddress contact, SessionRecord session) {
- SQLiteDatabase db = this.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(SQLiteAxolotlStore.NAME, contact.getName());
- values.put(SQLiteAxolotlStore.DEVICE_ID, contact.getDeviceId());
- values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(session.serialize(), Base64.DEFAULT));
- values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
- db.insert(SQLiteAxolotlStore.SESSION_TABLENAME, null, values);
- }
-
- public void deleteSession(Account account, AxolotlAddress contact) {
- SQLiteDatabase db = this.getWritableDatabase();
- deleteSession(db, account, contact);
- }
-
- private void deleteSession(SQLiteDatabase db, Account account, AxolotlAddress contact) {
- String[] args = {account.getUuid(),
- contact.getName(),
- Integer.toString(contact.getDeviceId())};
- db.delete(SQLiteAxolotlStore.SESSION_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + " = ? AND "
- + SQLiteAxolotlStore.NAME + " = ? AND "
- + SQLiteAxolotlStore.DEVICE_ID + " = ? ",
- args);
- }
-
- public void deleteAllSessions(Account account, AxolotlAddress contact) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {account.getUuid(), contact.getName()};
- db.delete(SQLiteAxolotlStore.SESSION_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + "=? AND "
- + SQLiteAxolotlStore.NAME + " = ?",
- args);
- }
-
- private Cursor getCursorForPreKey(Account account, int preKeyId) {
- SQLiteDatabase db = this.getReadableDatabase();
- String[] columns = {SQLiteAxolotlStore.KEY};
- String[] selectionArgs = {account.getUuid(), Integer.toString(preKeyId)};
- Cursor cursor = db.query(SQLiteAxolotlStore.PREKEY_TABLENAME,
- columns,
- SQLiteAxolotlStore.ACCOUNT + "=? AND "
- + SQLiteAxolotlStore.ID + "=?",
- selectionArgs,
- null, null, null);
-
- return cursor;
- }
-
- public PreKeyRecord loadPreKey(Account account, int preKeyId) {
- PreKeyRecord record = null;
- Cursor cursor = getCursorForPreKey(account, preKeyId);
- if (cursor.getCount() != 0) {
- cursor.moveToFirst();
- try {
- record = new PreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
- } catch (IOException e) {
- throw new AssertionError(e);
- }
- }
- cursor.close();
- return record;
- }
-
- public boolean containsPreKey(Account account, int preKeyId) {
- Cursor cursor = getCursorForPreKey(account, preKeyId);
- int count = cursor.getCount();
- cursor.close();
- return count != 0;
- }
-
- public void storePreKey(Account account, PreKeyRecord record) {
- SQLiteDatabase db = this.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(SQLiteAxolotlStore.ID, record.getId());
- values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(record.serialize(), Base64.DEFAULT));
- values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
- db.insert(SQLiteAxolotlStore.PREKEY_TABLENAME, null, values);
- }
-
- public void deletePreKey(Account account, int preKeyId) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {account.getUuid(), Integer.toString(preKeyId)};
- db.delete(SQLiteAxolotlStore.PREKEY_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + "=? AND "
- + SQLiteAxolotlStore.ID + "=?",
- args);
- }
-
- private Cursor getCursorForSignedPreKey(Account account, int signedPreKeyId) {
- SQLiteDatabase db = this.getReadableDatabase();
- String[] columns = {SQLiteAxolotlStore.KEY};
- String[] selectionArgs = {account.getUuid(), Integer.toString(signedPreKeyId)};
- Cursor cursor = db.query(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
- columns,
- SQLiteAxolotlStore.ACCOUNT + "=? AND " + SQLiteAxolotlStore.ID + "=?",
- selectionArgs,
- null, null, null);
-
- return cursor;
- }
-
- public SignedPreKeyRecord loadSignedPreKey(Account account, int signedPreKeyId) {
- SignedPreKeyRecord record = null;
- Cursor cursor = getCursorForSignedPreKey(account, signedPreKeyId);
- if (cursor.getCount() != 0) {
- cursor.moveToFirst();
- try {
- record = new SignedPreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
- } catch (IOException e) {
- throw new AssertionError(e);
- }
- }
- cursor.close();
- return record;
- }
-
- public List<SignedPreKeyRecord> loadSignedPreKeys(Account account) {
- List<SignedPreKeyRecord> prekeys = new ArrayList<>();
- SQLiteDatabase db = this.getReadableDatabase();
- String[] columns = {SQLiteAxolotlStore.KEY};
- String[] selectionArgs = {account.getUuid()};
- Cursor cursor = db.query(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
- columns,
- SQLiteAxolotlStore.ACCOUNT + "=?",
- selectionArgs,
- null, null, null);
-
- while (cursor.moveToNext()) {
- try {
- prekeys.add(new SignedPreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT)));
- } catch (IOException ignored) {
- }
- }
- cursor.close();
- return prekeys;
- }
-
- public boolean containsSignedPreKey(Account account, int signedPreKeyId) {
- Cursor cursor = getCursorForPreKey(account, signedPreKeyId);
- int count = cursor.getCount();
- cursor.close();
- return count != 0;
- }
-
- public void storeSignedPreKey(Account account, SignedPreKeyRecord record) {
- SQLiteDatabase db = this.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(SQLiteAxolotlStore.ID, record.getId());
- values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(record.serialize(), Base64.DEFAULT));
- values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
- db.insert(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME, null, values);
- }
-
- public void deleteSignedPreKey(Account account, int signedPreKeyId) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {account.getUuid(), Integer.toString(signedPreKeyId)};
- db.delete(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + "=? AND "
- + SQLiteAxolotlStore.ID + "=?",
- args);
- }
-
- private Cursor getIdentityKeyCursor(Account account, String name, boolean own) {
- final SQLiteDatabase db = this.getReadableDatabase();
- return getIdentityKeyCursor(db, account, name, own);
- }
-
- private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String name, boolean own) {
- return getIdentityKeyCursor(db, account, name, own, null);
- }
-
- private Cursor getIdentityKeyCursor(Account account, String fingerprint) {
- final SQLiteDatabase db = this.getReadableDatabase();
- return getIdentityKeyCursor(db, account, fingerprint);
- }
-
- private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String fingerprint) {
- return getIdentityKeyCursor(db, account, null, null, fingerprint);
- }
-
- private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String name, Boolean own, String fingerprint) {
- String[] columns = {SQLiteAxolotlStore.TRUST,
- SQLiteAxolotlStore.ACTIVE,
- SQLiteAxolotlStore.KEY};
- ArrayList<String> selectionArgs = new ArrayList<>(4);
- selectionArgs.add(account.getUuid());
- String selectionString = SQLiteAxolotlStore.ACCOUNT + " = ?";
- if (name != null) {
- selectionArgs.add(name);
- selectionString += " AND " + SQLiteAxolotlStore.NAME + " = ?";
- }
- if (fingerprint != null) {
- selectionArgs.add(fingerprint);
- selectionString += " AND " + SQLiteAxolotlStore.FINGERPRINT + " = ?";
- }
- if (own != null) {
- selectionArgs.add(own ? "1" : "0");
- selectionString += " AND " + SQLiteAxolotlStore.OWN + " = ?";
- }
- Cursor cursor = db.query(SQLiteAxolotlStore.IDENTITIES_TABLENAME,
- columns,
- selectionString,
- selectionArgs.toArray(new String[selectionArgs.size()]),
- null, null, null);
-
- return cursor;
- }
-
- public IdentityKeyPair loadOwnIdentityKeyPair(Account account) {
- SQLiteDatabase db = getReadableDatabase();
- return loadOwnIdentityKeyPair(db, account);
- }
-
- private IdentityKeyPair loadOwnIdentityKeyPair(SQLiteDatabase db, Account account) {
- String name = account.getJid().toBareJid().toPreppedString();
- IdentityKeyPair identityKeyPair = null;
- Cursor cursor = getIdentityKeyCursor(db, account, name, true);
- if (cursor.getCount() != 0) {
- cursor.moveToFirst();
- 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);
- }
- }
- cursor.close();
-
- return identityKeyPair;
- }
-
- public Set<IdentityKey> loadIdentityKeys(Account account, String name) {
- return loadIdentityKeys(account, name, null);
- }
-
- public Set<IdentityKey> loadIdentityKeys(Account account, String name, FingerprintStatus status) {
- Set<IdentityKey> identityKeys = new HashSet<>();
- Cursor cursor = getIdentityKeyCursor(account, name, false);
-
- while (cursor.moveToNext()) {
- if (status != null && !FingerprintStatus.fromCursor(cursor).equals(status)) {
- continue;
- }
- try {
- String key = cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY));
- if (key != null) {
- identityKeys.add(new IdentityKey(Base64.decode(key, Base64.DEFAULT), 0));
- } else {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Missing key (possibly preverified) in database for account" + account.getJid().toBareJid() + ", address: " + name);
- }
- } catch (InvalidKeyException e) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name);
- }
- }
- cursor.close();
-
- return identityKeys;
- }
-
- public long numTrustedKeys(Account account, String name) {
- SQLiteDatabase db = getReadableDatabase();
- String[] args = {
- account.getUuid(),
- name,
- FingerprintStatus.Trust.TRUSTED.toString(),
- FingerprintStatus.Trust.VERIFIED.toString(),
- FingerprintStatus.Trust.VERIFIED_X509.toString()
- };
- return DatabaseUtils.queryNumEntries(db, SQLiteAxolotlStore.IDENTITIES_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + " = ?"
- + " AND " + SQLiteAxolotlStore.NAME + " = ?"
- + " AND (" + SQLiteAxolotlStore.TRUST + " = ? OR " + SQLiteAxolotlStore.TRUST + " = ? OR " +SQLiteAxolotlStore.TRUST +" = ?)"
- + " AND " +SQLiteAxolotlStore.ACTIVE + " > 0",
- args
- );
- }
-
- private void storeIdentityKey(Account account, String name, boolean own, String fingerprint, String base64Serialized, FingerprintStatus status) {
- SQLiteDatabase db = this.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
- values.put(SQLiteAxolotlStore.NAME, name);
- values.put(SQLiteAxolotlStore.OWN, own ? 1 : 0);
- values.put(SQLiteAxolotlStore.FINGERPRINT, fingerprint);
- values.put(SQLiteAxolotlStore.KEY, base64Serialized);
- values.putAll(status.toContentValues());
- String where = SQLiteAxolotlStore.ACCOUNT+"=? AND "+SQLiteAxolotlStore.NAME+"=? AND "+SQLiteAxolotlStore.FINGERPRINT+" =?";
- String[] whereArgs = {account.getUuid(),name,fingerprint};
- int rows = db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME,values,where,whereArgs);
- if (rows == 0) {
- db.insert(SQLiteAxolotlStore.IDENTITIES_TABLENAME, null, values);
- }
- }
-
- public void storePreVerification(Account account, String name, String fingerprint, FingerprintStatus status) {
- SQLiteDatabase db = this.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
- values.put(SQLiteAxolotlStore.NAME, name);
- values.put(SQLiteAxolotlStore.OWN, 0);
- values.put(SQLiteAxolotlStore.FINGERPRINT, fingerprint);
- values.putAll(status.toContentValues());
- db.insert(SQLiteAxolotlStore.IDENTITIES_TABLENAME, null, values);
- }
-
- public FingerprintStatus getFingerprintStatus(Account account, String fingerprint) {
- Cursor cursor = getIdentityKeyCursor(account, fingerprint);
- final FingerprintStatus status;
- if (cursor.getCount() > 0) {
- cursor.moveToFirst();
- status = FingerprintStatus.fromCursor(cursor);
- } else {
- status = null;
- }
- cursor.close();
- return status;
- }
-
- public boolean setIdentityKeyTrust(Account account, String fingerprint, FingerprintStatus fingerprintStatus) {
- SQLiteDatabase db = this.getWritableDatabase();
- return setIdentityKeyTrust(db, account, fingerprint, fingerprintStatus);
- }
-
- private boolean setIdentityKeyTrust(SQLiteDatabase db, Account account, String fingerprint, FingerprintStatus status) {
- String[] selectionArgs = {
- account.getUuid(),
- fingerprint
- };
- int rows = db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, status.toContentValues(),
- SQLiteAxolotlStore.ACCOUNT + " = ? AND "
- + SQLiteAxolotlStore.FINGERPRINT + " = ? ",
- selectionArgs);
- return rows == 1;
- }
-
- public boolean setIdentityKeyCertificate(Account account, String fingerprint, X509Certificate x509Certificate) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] selectionArgs = {
- account.getUuid(),
- fingerprint
- };
- try {
- ContentValues values = new ContentValues();
- values.put(SQLiteAxolotlStore.CERTIFICATE, x509Certificate.getEncoded());
- return db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, values,
- SQLiteAxolotlStore.ACCOUNT + " = ? AND "
- + SQLiteAxolotlStore.FINGERPRINT + " = ? ",
- selectionArgs) == 1;
- } catch (CertificateEncodingException e) {
- Log.d(Config.LOGTAG, "could not encode certificate");
- return false;
- }
- }
-
- public X509Certificate getIdentityKeyCertifcate(Account account, String fingerprint) {
- SQLiteDatabase db = this.getReadableDatabase();
- String[] selectionArgs = {
- account.getUuid(),
- fingerprint
- };
- String[] colums = {SQLiteAxolotlStore.CERTIFICATE};
- String selection = SQLiteAxolotlStore.ACCOUNT + " = ? AND " + SQLiteAxolotlStore.FINGERPRINT + " = ? ";
- Cursor cursor = db.query(SQLiteAxolotlStore.IDENTITIES_TABLENAME, colums, selection, selectionArgs, null, null, null);
- if (cursor.getCount() < 1) {
- return null;
- } else {
- cursor.moveToFirst();
- byte[] certificate = cursor.getBlob(cursor.getColumnIndex(SQLiteAxolotlStore.CERTIFICATE));
- cursor.close();
- if (certificate == null || certificate.length == 0) {
- return null;
- }
- try {
- CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
- return (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(certificate));
- } catch (CertificateException e) {
- Log.d(Config.LOGTAG,"certificate exception "+e.getMessage());
- return null;
- }
- }
- }
-
- public void storeIdentityKey(Account account, String name, IdentityKey identityKey, FingerprintStatus status) {
- storeIdentityKey(account, name, false, identityKey.getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT), status);
- }
-
- public void storeOwnIdentityKeyPair(Account account, IdentityKeyPair identityKeyPair) {
- storeIdentityKey(account, account.getJid().toBareJid().toPreppedString(), true, identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKeyPair.serialize(), Base64.DEFAULT), FingerprintStatus.createActiveVerified(false));
- }
-
- public void recreateAxolotlDb(SQLiteDatabase db) {
- Log.d(Config.LOGTAG, AxolotlService.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);
- db.execSQL(CREATE_PREKEYS_STATEMENT);
- db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME);
- db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
- db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.IDENTITIES_TABLENAME);
- db.execSQL(CREATE_IDENTITIES_STATEMENT);
- }
-
- public void wipeAxolotlDb(Account account) {
- String accountName = account.getUuid();
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ">>> WIPING AXOLOTL DATABASE FOR ACCOUNT " + accountName + " <<<");
- SQLiteDatabase db = this.getWritableDatabase();
- String[] deleteArgs = {
- accountName
- };
- db.delete(SQLiteAxolotlStore.SESSION_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + " = ?",
- deleteArgs);
- db.delete(SQLiteAxolotlStore.PREKEY_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + " = ?",
- deleteArgs);
- db.delete(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + " = ?",
- deleteArgs);
- db.delete(SQLiteAxolotlStore.IDENTITIES_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + " = ?",
- deleteArgs);
- }
-
- public boolean startTimeCountExceedsThreshold() {
- SQLiteDatabase db = this.getWritableDatabase();
- long cleanBeforeTimestamp = System.currentTimeMillis() - Config.FREQUENT_RESTARTS_DETECTION_WINDOW;
- db.execSQL("delete from "+START_TIMES_TABLE+" where timestamp < "+cleanBeforeTimestamp);
- ContentValues values = new ContentValues();
- values.put("timestamp",System.currentTimeMillis());
- db.insert(START_TIMES_TABLE,null,values);
- String[] columns = new String[]{"count(timestamp)"};
- Cursor cursor = db.query(START_TIMES_TABLE,columns,null,null,null,null,null);
- int count;
- if (cursor.moveToFirst()) {
- count = cursor.getInt(0);
- } else {
- count = 0;
- }
- cursor.close();
- Log.d(Config.LOGTAG,"start time counter reached "+count);
- return count >= Config.FREQUENT_RESTARTS_THRESHOLD;
- }
-
- public void clearStartTimeCounter() {
- Log.d(Config.LOGTAG,"resetting start time counter");
- SQLiteDatabase db = this.getWritableDatabase();
- db.execSQL("delete from " + START_TIMES_TABLE);
- }
+ if (oldVersion < 17 && newVersion >= 17) {
+ List<Account> accounts = getAccounts(db);
+ for (Account account : accounts) {
+ String ownDeviceIdString = account.getKey(SQLiteAxolotlStore.JSONKEY_REGISTRATION_ID);
+ if (ownDeviceIdString == null) {
+ continue;
+ }
+ int ownDeviceId = Integer.valueOf(ownDeviceIdString);
+ AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), ownDeviceId);
+ deleteSession(db, account, ownAddress);
+ IdentityKeyPair identityKeyPair = loadOwnIdentityKeyPair(db, account);
+ if (identityKeyPair != null) {
+ String[] selectionArgs = {
+ account.getUuid(),
+ identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", "")
+ };
+ ContentValues values = new ContentValues();
+ values.put(SQLiteAxolotlStore.TRUSTED, 2);
+ db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, values,
+ SQLiteAxolotlStore.ACCOUNT + " = ? AND "
+ + SQLiteAxolotlStore.FINGERPRINT + " = ? ",
+ selectionArgs);
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not load own identity key pair");
+ }
+ }
+ }
+ if (oldVersion < 18 && newVersion >= 18) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.READ + " NUMBER DEFAULT 1");
+ }
+
+ if (oldVersion < 21 && newVersion >= 21) {
+ List<Account> accounts = getAccounts(db);
+ for (Account account : accounts) {
+ account.unsetPgpSignature();
+ db.update(Account.TABLENAME, account.getContentValues(), Account.UUID
+ + "=?", new String[]{account.getUuid()});
+ }
+ }
+
+ if (oldVersion < 23 && newVersion >= 23) {
+ db.execSQL(CREATE_DISCOVERY_RESULTS_STATEMENT);
+ }
+
+ if (oldVersion < 24 && newVersion >= 24) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.EDITED + " TEXT");
+ }
+
+ if (oldVersion < 25 && newVersion >= 25) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.OOB + " INTEGER");
+ }
+
+ if (oldVersion < 26 && newVersion >= 26) {
+ db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
+ }
+
+ if (oldVersion < 27 && newVersion >= 27) {
+ db.execSQL("DELETE FROM " + ServiceDiscoveryResult.TABLENAME);
+ }
+
+ if (oldVersion < 28 && newVersion >= 28) {
+ canonicalizeJids(db);
+ }
+
+ if (oldVersion < 29 && newVersion >= 29) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.ERROR_MESSAGE + " TEXT");
+ }
+
+ if (oldVersion < 30 && newVersion >= 30) {
+ db.execSQL(CREATE_START_TIMES_TABLE);
+ }
+ if (oldVersion < 31 && newVersion >= 31) {
+ db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.TRUST + " TEXT");
+ db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.ACTIVE + " NUMBER");
+ HashMap<Integer, ContentValues> migration = new HashMap<>();
+ migration.put(0, createFingerprintStatusContentValues(FingerprintStatus.Trust.UNDECIDED, true));
+ migration.put(1, createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, true));
+ migration.put(2, createFingerprintStatusContentValues(FingerprintStatus.Trust.UNTRUSTED, true));
+ migration.put(3, createFingerprintStatusContentValues(FingerprintStatus.Trust.COMPROMISED, false));
+ migration.put(4, createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, false));
+ migration.put(5, createFingerprintStatusContentValues(FingerprintStatus.Trust.UNDECIDED, false));
+ migration.put(6, createFingerprintStatusContentValues(FingerprintStatus.Trust.UNTRUSTED, false));
+ migration.put(7, createFingerprintStatusContentValues(FingerprintStatus.Trust.VERIFIED_X509, true));
+ migration.put(8, createFingerprintStatusContentValues(FingerprintStatus.Trust.VERIFIED_X509, false));
+ for (Map.Entry<Integer, ContentValues> entry : migration.entrySet()) {
+ String whereClause = SQLiteAxolotlStore.TRUSTED + "=?";
+ String[] where = {String.valueOf(entry.getKey())};
+ db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, entry.getValue(), whereClause, where);
+ }
+ }
+ }
+
+ private static ContentValues createFingerprintStatusContentValues(FingerprintStatus.Trust trust, boolean active) {
+ ContentValues values = new ContentValues();
+ values.put(SQLiteAxolotlStore.TRUST, trust.toString());
+ values.put(SQLiteAxolotlStore.ACTIVE, active ? 1 : 0);
+ return values;
+ }
+
+ private void canonicalizeJids(SQLiteDatabase db) {
+ // migrate db to new, canonicalized JID domainpart representation
+
+ // Conversation table
+ Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME, new String[0]);
+ while (cursor.moveToNext()) {
+ String newJid;
+ try {
+ newJid = Jid.fromString(
+ cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID))
+ ).toPreppedString();
+ } catch (InvalidJidException ignored) {
+ Log.e(Config.LOGTAG, "Failed to migrate Conversation CONTACTJID "
+ + cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID))
+ + ": " + ignored + ". Skipping...");
+ continue;
+ }
+
+ String updateArgs[] = {
+ newJid,
+ cursor.getString(cursor.getColumnIndex(Conversation.UUID)),
+ };
+ db.execSQL("update " + Conversation.TABLENAME
+ + " set " + Conversation.CONTACTJID + " = ? "
+ + " where " + Conversation.UUID + " = ?", updateArgs);
+ }
+ cursor.close();
+
+ // Contact table
+ cursor = db.rawQuery("select * from " + Contact.TABLENAME, new String[0]);
+ while (cursor.moveToNext()) {
+ String newJid;
+ try {
+ newJid = Jid.fromString(
+ cursor.getString(cursor.getColumnIndex(Contact.JID))
+ ).toPreppedString();
+ } catch (InvalidJidException ignored) {
+ Log.e(Config.LOGTAG, "Failed to migrate Contact JID "
+ + cursor.getString(cursor.getColumnIndex(Contact.JID))
+ + ": " + ignored + ". Skipping...");
+ continue;
+ }
+
+ String updateArgs[] = {
+ newJid,
+ cursor.getString(cursor.getColumnIndex(Contact.ACCOUNT)),
+ cursor.getString(cursor.getColumnIndex(Contact.JID)),
+ };
+ db.execSQL("update " + Contact.TABLENAME
+ + " set " + Contact.JID + " = ? "
+ + " where " + Contact.ACCOUNT + " = ? "
+ + " AND " + Contact.JID + " = ?", updateArgs);
+ }
+ cursor.close();
+
+ // Account table
+ cursor = db.rawQuery("select * from " + Account.TABLENAME, new String[0]);
+ while (cursor.moveToNext()) {
+ String newServer;
+ try {
+ newServer = Jid.fromParts(
+ cursor.getString(cursor.getColumnIndex(Account.USERNAME)),
+ cursor.getString(cursor.getColumnIndex(Account.SERVER)),
+ "mobile"
+ ).getDomainpart();
+ } catch (InvalidJidException ignored) {
+ Log.e(Config.LOGTAG, "Failed to migrate Account SERVER "
+ + cursor.getString(cursor.getColumnIndex(Account.SERVER))
+ + ": " + ignored + ". Skipping...");
+ continue;
+ }
+
+ String updateArgs[] = {
+ newServer,
+ cursor.getString(cursor.getColumnIndex(Account.UUID)),
+ };
+ db.execSQL("update " + Account.TABLENAME
+ + " set " + Account.SERVER + " = ? "
+ + " where " + Account.UUID + " = ?", updateArgs);
+ }
+ cursor.close();
+ }
+
+ public static synchronized DatabaseBackend getInstance(Context context) {
+ if (instance == null) {
+ instance = new DatabaseBackend(context);
+ }
+ return instance;
+ }
+
+ public void createConversation(Conversation conversation) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ db.insert(Conversation.TABLENAME, null, conversation.getContentValues());
+ }
+
+ public void createMessage(Message message) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ db.insert(Message.TABLENAME, null, message.getContentValues());
+ }
+
+ public void createAccount(Account account) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ db.insert(Account.TABLENAME, null, account.getContentValues());
+ }
+
+ public void insertDiscoveryResult(ServiceDiscoveryResult result) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ db.insert(ServiceDiscoveryResult.TABLENAME, null, result.getContentValues());
+ }
+
+ public ServiceDiscoveryResult findDiscoveryResult(final String hash, final String ver) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ String[] selectionArgs = {hash, ver};
+ Cursor cursor = db.query(ServiceDiscoveryResult.TABLENAME, null,
+ ServiceDiscoveryResult.HASH + "=? AND " + ServiceDiscoveryResult.VER + "=?",
+ selectionArgs, null, null, null);
+ if (cursor.getCount() == 0) {
+ cursor.close();
+ return null;
+ }
+ cursor.moveToFirst();
+
+ ServiceDiscoveryResult result = null;
+ try {
+ result = new ServiceDiscoveryResult(cursor);
+ } catch (JSONException e) { /* result is still null */ }
+
+ cursor.close();
+ return result;
+ }
+
+ public void insertPresenceTemplate(PresenceTemplate template) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ db.insert(PresenceTemplate.TABELNAME, null, template.getContentValues());
+ }
+
+ public List<PresenceTemplate> getPresenceTemplates() {
+ ArrayList<PresenceTemplate> templates = new ArrayList<>();
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor cursor = db.query(PresenceTemplate.TABELNAME, null, null, null, null, null, PresenceTemplate.LAST_USED + " desc");
+ while (cursor.moveToNext()) {
+ templates.add(PresenceTemplate.fromCursor(cursor));
+ }
+ cursor.close();
+ return templates;
+ }
+
+ public void deletePresenceTemplate(PresenceTemplate template) {
+ Log.d(Config.LOGTAG, "deleting presence template with uuid " + template.getUuid());
+ SQLiteDatabase db = this.getWritableDatabase();
+ String where = PresenceTemplate.UUID + "=?";
+ String[] whereArgs = {template.getUuid()};
+ db.delete(PresenceTemplate.TABELNAME, where, whereArgs);
+ }
+
+ public CopyOnWriteArrayList<Conversation> getConversations(int status) {
+ CopyOnWriteArrayList<Conversation> list = new CopyOnWriteArrayList<>();
+ SQLiteDatabase db = this.getReadableDatabase();
+ String[] selectionArgs = {Integer.toString(status)};
+ Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME
+ + " where " + Conversation.STATUS + " = ? order by "
+ + Conversation.CREATED + " desc", selectionArgs);
+ while (cursor.moveToNext()) {
+ list.add(Conversation.fromCursor(cursor));
+ }
+ cursor.close();
+ return list;
+ }
+
+ public ArrayList<Message> getMessages(Conversation conversations, int limit) {
+ return getMessages(conversations, limit, -1);
+ }
+
+ public ArrayList<Message> getMessages(Conversation conversation, int limit,
+ long timestamp) {
+ ArrayList<Message> list = new ArrayList<>();
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor cursor;
+ if (timestamp == -1) {
+ String[] selectionArgs = {conversation.getUuid()};
+ cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
+ + "=?", selectionArgs, null, null, Message.TIME_SENT
+ + " DESC", String.valueOf(limit));
+ } else {
+ String[] selectionArgs = {conversation.getUuid(),
+ Long.toString(timestamp)};
+ cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
+ + "=? and " + Message.TIME_SENT + "<?", selectionArgs,
+ null, null, Message.TIME_SENT + " DESC",
+ String.valueOf(limit));
+ }
+ if (cursor.getCount() > 0) {
+ cursor.moveToLast();
+ do {
+ Message message = Message.fromCursor(cursor);
+ message.setConversation(conversation);
+ list.add(message);
+ } while (cursor.moveToPrevious());
+ }
+ cursor.close();
+ return list;
+ }
+
+ public Iterable<Message> getMessagesIterable(final Conversation conversation) {
+ return new Iterable<Message>() {
+ @Override
+ public Iterator<Message> iterator() {
+ class MessageIterator implements Iterator<Message> {
+ SQLiteDatabase db = getReadableDatabase();
+ String[] selectionArgs = {conversation.getUuid()};
+ Cursor cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
+ + "=?", selectionArgs, null, null, Message.TIME_SENT
+ + " ASC", null);
+
+ public MessageIterator() {
+ cursor.moveToFirst();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return !cursor.isAfterLast();
+ }
+
+ @Override
+ public Message next() {
+ Message message = Message.fromCursor(cursor);
+ cursor.moveToNext();
+ return message;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+ return new MessageIterator();
+ }
+ };
+ }
+
+ public Conversation findConversation(final Account account, final Jid contactJid) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ String[] selectionArgs = {account.getUuid(),
+ contactJid.toBareJid().toPreppedString() + "/%",
+ contactJid.toBareJid().toPreppedString()
+ };
+ Cursor cursor = db.query(Conversation.TABLENAME, null,
+ Conversation.ACCOUNT + "=? AND (" + Conversation.CONTACTJID
+ + " like ? OR " + Conversation.CONTACTJID + "=?)", selectionArgs, null, null, null);
+ if (cursor.getCount() == 0) {
+ cursor.close();
+ return null;
+ }
+ cursor.moveToFirst();
+ Conversation conversation = Conversation.fromCursor(cursor);
+ cursor.close();
+ return conversation;
+ }
+
+ public void updateConversation(final Conversation conversation) {
+ final SQLiteDatabase db = this.getWritableDatabase();
+ final String[] args = {conversation.getUuid()};
+ db.update(Conversation.TABLENAME, conversation.getContentValues(),
+ Conversation.UUID + "=?", args);
+ }
+
+ public List<Account> getAccounts() {
+ SQLiteDatabase db = this.getReadableDatabase();
+ return getAccounts(db);
+ }
+
+ private List<Account> getAccounts(SQLiteDatabase db) {
+ List<Account> list = new ArrayList<>();
+ Cursor cursor = db.query(Account.TABLENAME, null, null, null, null,
+ null, null);
+ while (cursor.moveToNext()) {
+ list.add(Account.fromCursor(cursor));
+ }
+ cursor.close();
+ return list;
+ }
+
+ public boolean updateAccount(Account account) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {account.getUuid()};
+ final int rows = db.update(Account.TABLENAME, account.getContentValues(), Account.UUID + "=?", args);
+ return rows == 1;
+ }
+
+ public boolean deleteAccount(Account account) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {account.getUuid()};
+ final int rows = db.delete(Account.TABLENAME, Account.UUID + "=?", args);
+ return rows == 1;
+ }
+
+ public boolean hasEnabledAccounts() {
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor cursor = db.rawQuery("select count(" + Account.UUID + ") from "
+ + Account.TABLENAME + " where not options & (1 <<1)", null);
+ try {
+ cursor.moveToFirst();
+ int count = cursor.getInt(0);
+ return (count > 0);
+ } catch (SQLiteCantOpenDatabaseException e) {
+ return true; // better safe than sorry
+ } catch (RuntimeException e) {
+ return true; // better safe than sorry
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ @Override
+ public SQLiteDatabase getWritableDatabase() {
+ SQLiteDatabase db = super.getWritableDatabase();
+ db.execSQL("PRAGMA foreign_keys=ON;");
+ return db;
+ }
+
+ public void updateMessage(Message message) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {message.getUuid()};
+ db.update(Message.TABLENAME, message.getContentValues(), Message.UUID
+ + "=?", args);
+ }
+
+ public void updateMessage(Message message, String uuid) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {uuid};
+ db.update(Message.TABLENAME, message.getContentValues(), Message.UUID
+ + "=?", args);
+ }
+
+ public void readRoster(Roster roster) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor cursor;
+ String args[] = {roster.getAccount().getUuid()};
+ cursor = db.query(Contact.TABLENAME, null, Contact.ACCOUNT + "=?", args, null, null, null);
+ while (cursor.moveToNext()) {
+ roster.initContact(Contact.fromCursor(cursor));
+ }
+ cursor.close();
+ }
+
+ public void writeRoster(final Roster roster) {
+ final Account account = roster.getAccount();
+ final SQLiteDatabase db = this.getWritableDatabase();
+ db.beginTransaction();
+ for (Contact contact : roster.getContacts()) {
+ if (contact.getOption(Contact.Options.IN_ROSTER)) {
+ db.insert(Contact.TABLENAME, null, contact.getContentValues());
+ } else {
+ String where = Contact.ACCOUNT + "=? AND " + Contact.JID + "=?";
+ String[] whereArgs = {account.getUuid(), contact.getJid().toPreppedString()};
+ db.delete(Contact.TABLENAME, where, whereArgs);
+ }
+ }
+ db.setTransactionSuccessful();
+ db.endTransaction();
+ account.setRosterVersion(roster.getVersion());
+ updateAccount(account);
+ }
+
+ public void deleteMessagesInConversation(Conversation conversation) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {conversation.getUuid()};
+ db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args);
+ }
+
+ public Pair<Long, String> getLastMessageReceived(Account account) {
+ Cursor cursor = null;
+ try {
+ SQLiteDatabase db = this.getReadableDatabase();
+ String sql = "select messages.timeSent,messages.serverMsgId from accounts join conversations on accounts.uuid=conversations.accountUuid join messages on conversations.uuid=messages.conversationUuid where accounts.uuid=? and (messages.status=0 or messages.carbon=1 or messages.serverMsgId not null) order by messages.timesent desc limit 1";
+ String[] args = {account.getUuid()};
+ cursor = db.rawQuery(sql, args);
+ if (cursor.getCount() == 0) {
+ return null;
+ } else {
+ cursor.moveToFirst();
+ return new Pair<>(cursor.getLong(0), cursor.getString(1));
+ }
+ } catch (Exception e) {
+ return null;
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ public Pair<Long, String> getLastClearDate(Account account) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ String[] columns = {Conversation.ATTRIBUTES};
+ String selection = Conversation.ACCOUNT + "=?";
+ String[] args = {account.getUuid()};
+ Cursor cursor = db.query(Conversation.TABLENAME, columns, selection, args, null, null, null);
+ long maxClearDate = 0;
+ while (cursor.moveToNext()) {
+ try {
+ final JSONObject jsonObject = new JSONObject(cursor.getString(0));
+ maxClearDate = Math.max(maxClearDate, jsonObject.getLong(Conversation.ATTRIBUTE_LAST_CLEAR_HISTORY));
+ } catch (Exception e) {
+ //ignored
+ }
+ }
+ cursor.close();
+ return new Pair<>(maxClearDate, null);
+ }
+
+ private Cursor getCursorForSession(Account account, AxolotlAddress contact) {
+ final SQLiteDatabase db = this.getReadableDatabase();
+ String[] selectionArgs = {account.getUuid(),
+ contact.getName(),
+ Integer.toString(contact.getDeviceId())};
+ return db.query(SQLiteAxolotlStore.SESSION_TABLENAME,
+ null,
+ SQLiteAxolotlStore.ACCOUNT + " = ? AND "
+ + SQLiteAxolotlStore.NAME + " = ? AND "
+ + SQLiteAxolotlStore.DEVICE_ID + " = ? ",
+ selectionArgs,
+ null, null, null);
+ }
+
+ public SessionRecord loadSession(Account account, AxolotlAddress contact) {
+ SessionRecord session = null;
+ Cursor cursor = getCursorForSession(account, contact);
+ if (cursor.getCount() != 0) {
+ cursor.moveToFirst();
+ try {
+ session = new SessionRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
+ } catch (IOException e) {
+ cursor.close();
+ throw new AssertionError(e);
+ }
+ }
+ cursor.close();
+ return session;
+ }
+
+ public List<Integer> getSubDeviceSessions(Account account, AxolotlAddress contact) {
+ final SQLiteDatabase db = this.getReadableDatabase();
+ return getSubDeviceSessions(db, account, contact);
+ }
+
+ private List<Integer> getSubDeviceSessions(SQLiteDatabase db, Account account, AxolotlAddress contact) {
+ List<Integer> devices = new ArrayList<>();
+ String[] columns = {SQLiteAxolotlStore.DEVICE_ID};
+ String[] selectionArgs = {account.getUuid(),
+ contact.getName()};
+ Cursor cursor = db.query(SQLiteAxolotlStore.SESSION_TABLENAME,
+ columns,
+ SQLiteAxolotlStore.ACCOUNT + " = ? AND "
+ + SQLiteAxolotlStore.NAME + " = ?",
+ selectionArgs,
+ null, null, null);
+
+ while (cursor.moveToNext()) {
+ devices.add(cursor.getInt(
+ cursor.getColumnIndex(SQLiteAxolotlStore.DEVICE_ID)));
+ }
+
+ cursor.close();
+ return devices;
+ }
+
+ public boolean containsSession(Account account, AxolotlAddress contact) {
+ Cursor cursor = getCursorForSession(account, contact);
+ int count = cursor.getCount();
+ cursor.close();
+ return count != 0;
+ }
+
+ public void storeSession(Account account, AxolotlAddress contact, SessionRecord session) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(SQLiteAxolotlStore.NAME, contact.getName());
+ values.put(SQLiteAxolotlStore.DEVICE_ID, contact.getDeviceId());
+ values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(session.serialize(), Base64.DEFAULT));
+ values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
+ db.insert(SQLiteAxolotlStore.SESSION_TABLENAME, null, values);
+ }
+
+ public void deleteSession(Account account, AxolotlAddress contact) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ deleteSession(db, account, contact);
+ }
+
+ private void deleteSession(SQLiteDatabase db, Account account, AxolotlAddress contact) {
+ String[] args = {account.getUuid(),
+ contact.getName(),
+ Integer.toString(contact.getDeviceId())};
+ db.delete(SQLiteAxolotlStore.SESSION_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + " = ? AND "
+ + SQLiteAxolotlStore.NAME + " = ? AND "
+ + SQLiteAxolotlStore.DEVICE_ID + " = ? ",
+ args);
+ }
+
+ public void deleteAllSessions(Account account, AxolotlAddress contact) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {account.getUuid(), contact.getName()};
+ db.delete(SQLiteAxolotlStore.SESSION_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + "=? AND "
+ + SQLiteAxolotlStore.NAME + " = ?",
+ args);
+ }
+
+ private Cursor getCursorForPreKey(Account account, int preKeyId) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ String[] columns = {SQLiteAxolotlStore.KEY};
+ String[] selectionArgs = {account.getUuid(), Integer.toString(preKeyId)};
+ Cursor cursor = db.query(SQLiteAxolotlStore.PREKEY_TABLENAME,
+ columns,
+ SQLiteAxolotlStore.ACCOUNT + "=? AND "
+ + SQLiteAxolotlStore.ID + "=?",
+ selectionArgs,
+ null, null, null);
+
+ return cursor;
+ }
+
+ public PreKeyRecord loadPreKey(Account account, int preKeyId) {
+ PreKeyRecord record = null;
+ Cursor cursor = getCursorForPreKey(account, preKeyId);
+ if (cursor.getCount() != 0) {
+ cursor.moveToFirst();
+ try {
+ record = new PreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+ cursor.close();
+ return record;
+ }
+
+ public boolean containsPreKey(Account account, int preKeyId) {
+ Cursor cursor = getCursorForPreKey(account, preKeyId);
+ int count = cursor.getCount();
+ cursor.close();
+ return count != 0;
+ }
+
+ public void storePreKey(Account account, PreKeyRecord record) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(SQLiteAxolotlStore.ID, record.getId());
+ values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(record.serialize(), Base64.DEFAULT));
+ values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
+ db.insert(SQLiteAxolotlStore.PREKEY_TABLENAME, null, values);
+ }
+
+ public void deletePreKey(Account account, int preKeyId) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {account.getUuid(), Integer.toString(preKeyId)};
+ db.delete(SQLiteAxolotlStore.PREKEY_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + "=? AND "
+ + SQLiteAxolotlStore.ID + "=?",
+ args);
+ }
+
+ private Cursor getCursorForSignedPreKey(Account account, int signedPreKeyId) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ String[] columns = {SQLiteAxolotlStore.KEY};
+ String[] selectionArgs = {account.getUuid(), Integer.toString(signedPreKeyId)};
+ Cursor cursor = db.query(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
+ columns,
+ SQLiteAxolotlStore.ACCOUNT + "=? AND " + SQLiteAxolotlStore.ID + "=?",
+ selectionArgs,
+ null, null, null);
+
+ return cursor;
+ }
+
+ public SignedPreKeyRecord loadSignedPreKey(Account account, int signedPreKeyId) {
+ SignedPreKeyRecord record = null;
+ Cursor cursor = getCursorForSignedPreKey(account, signedPreKeyId);
+ if (cursor.getCount() != 0) {
+ cursor.moveToFirst();
+ try {
+ record = new SignedPreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+ cursor.close();
+ return record;
+ }
+
+ public List<SignedPreKeyRecord> loadSignedPreKeys(Account account) {
+ List<SignedPreKeyRecord> prekeys = new ArrayList<>();
+ SQLiteDatabase db = this.getReadableDatabase();
+ String[] columns = {SQLiteAxolotlStore.KEY};
+ String[] selectionArgs = {account.getUuid()};
+ Cursor cursor = db.query(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
+ columns,
+ SQLiteAxolotlStore.ACCOUNT + "=?",
+ selectionArgs,
+ null, null, null);
+
+ while (cursor.moveToNext()) {
+ try {
+ prekeys.add(new SignedPreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT)));
+ } catch (IOException ignored) {
+ }
+ }
+ cursor.close();
+ return prekeys;
+ }
+
+ public boolean containsSignedPreKey(Account account, int signedPreKeyId) {
+ Cursor cursor = getCursorForPreKey(account, signedPreKeyId);
+ int count = cursor.getCount();
+ cursor.close();
+ return count != 0;
+ }
+
+ public void storeSignedPreKey(Account account, SignedPreKeyRecord record) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(SQLiteAxolotlStore.ID, record.getId());
+ values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(record.serialize(), Base64.DEFAULT));
+ values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
+ db.insert(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME, null, values);
+ }
+
+ public void deleteSignedPreKey(Account account, int signedPreKeyId) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {account.getUuid(), Integer.toString(signedPreKeyId)};
+ db.delete(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + "=? AND "
+ + SQLiteAxolotlStore.ID + "=?",
+ args);
+ }
+
+ private Cursor getIdentityKeyCursor(Account account, String name, boolean own) {
+ final SQLiteDatabase db = this.getReadableDatabase();
+ return getIdentityKeyCursor(db, account, name, own);
+ }
+
+ private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String name, boolean own) {
+ return getIdentityKeyCursor(db, account, name, own, null);
+ }
+
+ private Cursor getIdentityKeyCursor(Account account, String fingerprint) {
+ final SQLiteDatabase db = this.getReadableDatabase();
+ return getIdentityKeyCursor(db, account, fingerprint);
+ }
+
+ private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String fingerprint) {
+ return getIdentityKeyCursor(db, account, null, null, fingerprint);
+ }
+
+ private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String name, Boolean own, String fingerprint) {
+ String[] columns = {SQLiteAxolotlStore.TRUST,
+ SQLiteAxolotlStore.ACTIVE,
+ SQLiteAxolotlStore.KEY};
+ ArrayList<String> selectionArgs = new ArrayList<>(4);
+ selectionArgs.add(account.getUuid());
+ String selectionString = SQLiteAxolotlStore.ACCOUNT + " = ?";
+ if (name != null) {
+ selectionArgs.add(name);
+ selectionString += " AND " + SQLiteAxolotlStore.NAME + " = ?";
+ }
+ if (fingerprint != null) {
+ selectionArgs.add(fingerprint);
+ selectionString += " AND " + SQLiteAxolotlStore.FINGERPRINT + " = ?";
+ }
+ if (own != null) {
+ selectionArgs.add(own ? "1" : "0");
+ selectionString += " AND " + SQLiteAxolotlStore.OWN + " = ?";
+ }
+ Cursor cursor = db.query(SQLiteAxolotlStore.IDENTITIES_TABLENAME,
+ columns,
+ selectionString,
+ selectionArgs.toArray(new String[selectionArgs.size()]),
+ null, null, null);
+
+ return cursor;
+ }
+
+ public IdentityKeyPair loadOwnIdentityKeyPair(Account account) {
+ SQLiteDatabase db = getReadableDatabase();
+ return loadOwnIdentityKeyPair(db, account);
+ }
+
+ private IdentityKeyPair loadOwnIdentityKeyPair(SQLiteDatabase db, Account account) {
+ String name = account.getJid().toBareJid().toPreppedString();
+ IdentityKeyPair identityKeyPair = null;
+ Cursor cursor = getIdentityKeyCursor(db, account, name, true);
+ if (cursor.getCount() != 0) {
+ cursor.moveToFirst();
+ 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);
+ }
+ }
+ cursor.close();
+
+ return identityKeyPair;
+ }
+
+ public Set<IdentityKey> loadIdentityKeys(Account account, String name) {
+ return loadIdentityKeys(account, name, null);
+ }
+
+ public Set<IdentityKey> loadIdentityKeys(Account account, String name, FingerprintStatus status) {
+ Set<IdentityKey> identityKeys = new HashSet<>();
+ Cursor cursor = getIdentityKeyCursor(account, name, false);
+
+ while (cursor.moveToNext()) {
+ if (status != null && !FingerprintStatus.fromCursor(cursor).equals(status)) {
+ continue;
+ }
+ try {
+ String key = cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY));
+ if (key != null) {
+ identityKeys.add(new IdentityKey(Base64.decode(key, Base64.DEFAULT), 0));
+ } else {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Missing key (possibly preverified) in database for account" + account.getJid().toBareJid() + ", address: " + name);
+ }
+ } catch (InvalidKeyException e) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name);
+ }
+ }
+ cursor.close();
+
+ return identityKeys;
+ }
+
+ public long numTrustedKeys(Account account, String name) {
+ SQLiteDatabase db = getReadableDatabase();
+ String[] args = {
+ account.getUuid(),
+ name,
+ FingerprintStatus.Trust.TRUSTED.toString(),
+ FingerprintStatus.Trust.VERIFIED.toString(),
+ FingerprintStatus.Trust.VERIFIED_X509.toString()
+ };
+ return DatabaseUtils.queryNumEntries(db, SQLiteAxolotlStore.IDENTITIES_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + " = ?"
+ + " AND " + SQLiteAxolotlStore.NAME + " = ?"
+ + " AND (" + SQLiteAxolotlStore.TRUST + " = ? OR " + SQLiteAxolotlStore.TRUST + " = ? OR " + SQLiteAxolotlStore.TRUST + " = ?)"
+ + " AND " + SQLiteAxolotlStore.ACTIVE + " > 0",
+ args
+ );
+ }
+
+ private void storeIdentityKey(Account account, String name, boolean own, String fingerprint, String base64Serialized, FingerprintStatus status) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
+ values.put(SQLiteAxolotlStore.NAME, name);
+ values.put(SQLiteAxolotlStore.OWN, own ? 1 : 0);
+ values.put(SQLiteAxolotlStore.FINGERPRINT, fingerprint);
+ values.put(SQLiteAxolotlStore.KEY, base64Serialized);
+ values.putAll(status.toContentValues());
+ String where = SQLiteAxolotlStore.ACCOUNT + "=? AND " + SQLiteAxolotlStore.NAME + "=? AND " + SQLiteAxolotlStore.FINGERPRINT + " =?";
+ String[] whereArgs = {account.getUuid(), name, fingerprint};
+ int rows = db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, values, where, whereArgs);
+ if (rows == 0) {
+ db.insert(SQLiteAxolotlStore.IDENTITIES_TABLENAME, null, values);
+ }
+ }
+
+ public void storePreVerification(Account account, String name, String fingerprint, FingerprintStatus status) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
+ values.put(SQLiteAxolotlStore.NAME, name);
+ values.put(SQLiteAxolotlStore.OWN, 0);
+ values.put(SQLiteAxolotlStore.FINGERPRINT, fingerprint);
+ values.putAll(status.toContentValues());
+ db.insert(SQLiteAxolotlStore.IDENTITIES_TABLENAME, null, values);
+ }
+
+ public FingerprintStatus getFingerprintStatus(Account account, String fingerprint) {
+ Cursor cursor = getIdentityKeyCursor(account, fingerprint);
+ final FingerprintStatus status;
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ status = FingerprintStatus.fromCursor(cursor);
+ } else {
+ status = null;
+ }
+ cursor.close();
+ return status;
+ }
+
+ public boolean setIdentityKeyTrust(Account account, String fingerprint, FingerprintStatus fingerprintStatus) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ return setIdentityKeyTrust(db, account, fingerprint, fingerprintStatus);
+ }
+
+ private boolean setIdentityKeyTrust(SQLiteDatabase db, Account account, String fingerprint, FingerprintStatus status) {
+ String[] selectionArgs = {
+ account.getUuid(),
+ fingerprint
+ };
+ int rows = db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, status.toContentValues(),
+ SQLiteAxolotlStore.ACCOUNT + " = ? AND "
+ + SQLiteAxolotlStore.FINGERPRINT + " = ? ",
+ selectionArgs);
+ return rows == 1;
+ }
+
+ public boolean setIdentityKeyCertificate(Account account, String fingerprint, X509Certificate x509Certificate) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] selectionArgs = {
+ account.getUuid(),
+ fingerprint
+ };
+ try {
+ ContentValues values = new ContentValues();
+ values.put(SQLiteAxolotlStore.CERTIFICATE, x509Certificate.getEncoded());
+ return db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, values,
+ SQLiteAxolotlStore.ACCOUNT + " = ? AND "
+ + SQLiteAxolotlStore.FINGERPRINT + " = ? ",
+ selectionArgs) == 1;
+ } catch (CertificateEncodingException e) {
+ Log.d(Config.LOGTAG, "could not encode certificate");
+ return false;
+ }
+ }
+
+ public X509Certificate getIdentityKeyCertifcate(Account account, String fingerprint) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ String[] selectionArgs = {
+ account.getUuid(),
+ fingerprint
+ };
+ String[] colums = {SQLiteAxolotlStore.CERTIFICATE};
+ String selection = SQLiteAxolotlStore.ACCOUNT + " = ? AND " + SQLiteAxolotlStore.FINGERPRINT + " = ? ";
+ Cursor cursor = db.query(SQLiteAxolotlStore.IDENTITIES_TABLENAME, colums, selection, selectionArgs, null, null, null);
+ if (cursor.getCount() < 1) {
+ return null;
+ } else {
+ cursor.moveToFirst();
+ byte[] certificate = cursor.getBlob(cursor.getColumnIndex(SQLiteAxolotlStore.CERTIFICATE));
+ cursor.close();
+ if (certificate == null || certificate.length == 0) {
+ return null;
+ }
+ try {
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(certificate));
+ } catch (CertificateException e) {
+ Log.d(Config.LOGTAG, "certificate exception " + e.getMessage());
+ return null;
+ }
+ }
+ }
+
+ public void storeIdentityKey(Account account, String name, IdentityKey identityKey, FingerprintStatus status) {
+ storeIdentityKey(account, name, false, identityKey.getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT), status);
+ }
+
+ public void storeOwnIdentityKeyPair(Account account, IdentityKeyPair identityKeyPair) {
+ storeIdentityKey(account, account.getJid().toBareJid().toPreppedString(), true, identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKeyPair.serialize(), Base64.DEFAULT), FingerprintStatus.createActiveVerified(false));
+ }
+
+ public void recreateAxolotlDb(SQLiteDatabase db) {
+ Log.d(Config.LOGTAG, AxolotlService.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);
+ db.execSQL(CREATE_PREKEYS_STATEMENT);
+ db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME);
+ db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
+ db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.IDENTITIES_TABLENAME);
+ db.execSQL(CREATE_IDENTITIES_STATEMENT);
+ }
+
+ public void wipeAxolotlDb(Account account) {
+ String accountName = account.getUuid();
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ">>> WIPING AXOLOTL DATABASE FOR ACCOUNT " + accountName + " <<<");
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] deleteArgs = {
+ accountName
+ };
+ db.delete(SQLiteAxolotlStore.SESSION_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + " = ?",
+ deleteArgs);
+ db.delete(SQLiteAxolotlStore.PREKEY_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + " = ?",
+ deleteArgs);
+ db.delete(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + " = ?",
+ deleteArgs);
+ db.delete(SQLiteAxolotlStore.IDENTITIES_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + " = ?",
+ deleteArgs);
+ }
+
+ public boolean startTimeCountExceedsThreshold() {
+ SQLiteDatabase db = this.getWritableDatabase();
+ long cleanBeforeTimestamp = System.currentTimeMillis() - Config.FREQUENT_RESTARTS_DETECTION_WINDOW;
+ db.execSQL("delete from " + START_TIMES_TABLE + " where timestamp < " + cleanBeforeTimestamp);
+ ContentValues values = new ContentValues();
+ values.put("timestamp", System.currentTimeMillis());
+ db.insert(START_TIMES_TABLE, null, values);
+ String[] columns = new String[]{"count(timestamp)"};
+ Cursor cursor = db.query(START_TIMES_TABLE, columns, null, null, null, null, null);
+ int count;
+ if (cursor.moveToFirst()) {
+ count = cursor.getInt(0);
+ } else {
+ count = 0;
+ }
+ cursor.close();
+ Log.d(Config.LOGTAG, "start time counter reached " + count);
+ return count >= Config.FREQUENT_RESTARTS_THRESHOLD;
+ }
+
+ public void clearStartTimeCounter() {
+ Log.d(Config.LOGTAG, "resetting start time counter");
+ SQLiteDatabase db = this.getWritableDatabase();
+ db.execSQL("delete from " + START_TIMES_TABLE);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/persistance/FileBackend.java b/src/main/java/de/pixart/messenger/persistance/FileBackend.java
index 9babae1de..893834ad7 100644
--- a/src/main/java/de/pixart/messenger/persistance/FileBackend.java
+++ b/src/main/java/de/pixart/messenger/persistance/FileBackend.java
@@ -58,918 +58,918 @@ import de.pixart.messenger.utils.FileWriterException;
import de.pixart.messenger.xmpp.pep.Avatar;
public class FileBackend {
- private static final SimpleDateFormat fileDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
-
- public static final String CONVERSATIONS_FILE_PROVIDER = "de.pixart.messenger.files";
-
- private XmppConnectionService mXmppConnectionService;
-
- public FileBackend(XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
-
- private void createNoMedia() {
- final File nomedia_files = new File(getConversationsFileDirectory()+".nomedia");
- final File nomedia_audios = new File(getConversationsAudioDirectory()+".nomedia");
- if (!nomedia_files.exists()) {
- try {
- nomedia_files.createNewFile();
- } catch (Exception e) {
- Log.d(Config.LOGTAG, "could not create nomedia file for files directory");
- }
- }
- if (!nomedia_audios.exists()) {
- try {
- nomedia_audios.createNewFile();
- } catch (Exception e) {
- Log.d(Config.LOGTAG, "could not create nomedia file for audio directory");
- }
- }
- }
-
- public void updateMediaScanner(File file) {
- if (file.getAbsolutePath().startsWith(getConversationsImageDirectory())
- || file.getAbsolutePath().startsWith(getConversationsVideoDirectory())) {
- Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
- intent.setData(Uri.fromFile(file));
- mXmppConnectionService.sendBroadcast(intent);
- } else {
- createNoMedia();
- }
- }
-
- public boolean deleteFile(Message message) {
- File file = getFile(message);
- if (file.delete()) {
- updateMediaScanner(file);
- return true;
- } else {
- return false;
- }
- }
-
- public DownloadableFile getFile(Message message) {
- return getFile(message, true);
- }
-
- public DownloadableFile getFile(Message message, boolean decrypted) {
- final boolean encrypted = !decrypted
- && (message.getEncryption() == Message.ENCRYPTION_PGP
- || message.getEncryption() == Message.ENCRYPTION_DECRYPTED);
- final DownloadableFile file;
- String path = message.getRelativeFilePath();
- if (path == null) {
- String filename = fileDateFormat.format(new Date(message.getTimeSent()))+"_"+message.getUuid().substring(0,4);
- path = filename;
- }
- if (path.startsWith("/")) {
- file = new DownloadableFile(path);
- } else {
- String mime = message.getMimeType();
- if (mime != null && mime.startsWith("image")) {
- file = new DownloadableFile(getConversationsImageDirectory() + path);
- } else if (mime != null && mime.startsWith("video")) {
- file = new DownloadableFile(getConversationsVideoDirectory() + path);
- } else if (mime != null && mime.startsWith("audio")) {
- file = new DownloadableFile(getConversationsAudioDirectory() + path);
- } else {
- file = new DownloadableFile(getConversationsFileDirectory() + path);
- }
- }
- if (encrypted) {
- return new DownloadableFile(getConversationsFileDirectory() + file.getName() + ".pgp");
- } else {
- return file;
- }
- }
-
- 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()+"/Pix-Art Messenger/files/";
- }
-
- public static String getConversationsImageDirectory() {
- return Environment.getExternalStorageDirectory().getAbsolutePath()+"/Pix-Art Messenger/images/";
- }
-
- public static String getConversationsVideoDirectory() {
- return Environment.getExternalStorageDirectory().getAbsolutePath()+"/Pix-Art Messenger/videos/";
- }
-
- public static String getConversationsAudioDirectory() {
- return Environment.getExternalStorageDirectory().getAbsolutePath()+"/Pix-Art Messenger/audios/";
- }
-
- public static String getConversationsDirectory() {
- return Environment.getExternalStorageDirectory().getAbsolutePath()+"/Pix-Art Messenger/";
- }
-
- 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;
- }
-
- 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 String getOriginalPath(Uri uri) {
- return FileUtils.getPath(mXmppConnectionService,uri);
- }
-
- public void copyFileToPrivateStorage(File file, Uri uri) throws FileCopyException {
- Log.d(Config.LOGTAG,"copy file ("+uri.toString()+") to private storage "+file.getAbsolutePath());
- file.getParentFile().mkdirs();
- OutputStream os = null;
- InputStream is = null;
- try {
- file.createNewFile();
- os = new FileOutputStream(file);
- is = mXmppConnectionService.getContentResolver().openInputStream(uri);
- byte[] buffer = new byte[1024];
- int length;
- while ((length = is.read(buffer)) > 0) {
- try {
- os.write(buffer, 0, length);
- } catch (IOException e) {
- throw new FileWriterException();
- }
- }
- try {
- os.flush();
- } catch (IOException e) {
- throw new FileWriterException();
- }
- } catch(FileNotFoundException e) {
- throw new FileCopyException(R.string.error_file_not_found);
- } catch(FileWriterException e) {
- throw new FileCopyException(R.string.error_unable_to_create_temporary_file);
- } catch (IOException e) {
- e.printStackTrace();
- throw new FileCopyException(R.string.error_io_exception);
- } finally {
- close(os);
- close(is);
- }
- }
-
- public void copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException {
- String mime = mXmppConnectionService.getContentResolver().getType(uri);
- Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage (mime="+mime+")");
- String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
- if (extension == null) {
- extension = getExtensionFromUri(uri);
- }
- String filename = fileDateFormat.format(new Date(message.getTimeSent()))+"_"+message.getUuid().substring(0,4);
- message.setRelativeFilePath(filename + "." + extension);
- copyFileToPrivateStorage(mXmppConnectionService.getFileBackend().getFile(message), uri);
- }
-
- private String getExtensionFromUri(Uri uri) {
- String[] projection = {MediaStore.MediaColumns.DATA};
- String filename = null;
- Cursor cursor = mXmppConnectionService.getContentResolver().query(uri, projection, null, null, null);
- if (cursor != null) {
- try {
- if (cursor.moveToFirst()) {
- filename = cursor.getString(0);
- }
- } catch (Exception e) {
- filename = null;
- } finally {
- cursor.close();
- }
- }
- int pos = filename == null ? -1 : filename.lastIndexOf('.');
- return pos > 0 ? filename.substring(pos+1) : null;
- }
-
- private void copyImageToPrivateStorage(File file, Uri image, int sampleSize) throws FileCopyException {
- file.getParentFile().mkdirs();
- InputStream is = null;
- OutputStream os = null;
- try {
- if (!file.exists() && !file.createNewFile()) {
- throw new FileCopyException(R.string.error_unable_to_create_temporary_file);
- }
- is = mXmppConnectionService.getContentResolver().openInputStream(image);
- if (is == null) {
- throw new FileCopyException(R.string.error_not_an_image_file);
- }
- 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();
- } 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);
- }
- } finally {
- close(os);
- close(is);
- }
- }
-
- public void copyImageToPrivateStorage(File file, Uri image) throws FileCopyException {
- Log.d(Config.LOGTAG,"copy image ("+image.toString()+") to private storage "+file.getAbsolutePath());
- copyImageToPrivateStorage(file, image, 0);
- }
-
- public void copyImageToPrivateStorage(Message message, Uri image) throws FileCopyException {
- String filename = fileDateFormat.format(new Date(message.getTimeSent()))+"_"+message.getUuid().substring(0,4);
- switch(Config.IMAGE_FORMAT) {
- case JPEG:
- message.setRelativeFilePath(filename+".jpg");
- break;
- case PNG:
- message.setRelativeFilePath(filename+".png");
- break;
- case WEBP:
- message.setRelativeFilePath(filename+".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;
- }
- DownloadableFile file = getFile(message);
- if (file.getMimeType().startsWith("video/")) {
- thumbnail = getVideoPreview(file, size);
- } else {
- Bitmap fullsize = getFullsizeImagePreview(file, size);
- if (fullsize == null) {
- throw new FileNotFoundException();
- }
- thumbnail = resize(fullsize, size);
- thumbnail = rotate(thumbnail, getRotation(file));
- }
- this.mXmppConnectionService.getBitmapCache().put(uuid, thumbnail);
- }
- }
- return thumbnail;
- }
-
- private Bitmap getFullsizeImagePreview(File file, int size) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inSampleSize = calcSampleSize(file, size);
- try {
- return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
- } catch (OutOfMemoryError e) {
- options.inSampleSize *= 2;
- return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
- }
- }
-
- private Bitmap getVideoPreview(File file, int size) {
- MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
- Bitmap frame;
- try {
- metadataRetriever.setDataSource(file.getAbsolutePath());
- frame = metadataRetriever.getFrameAtTime(0);
- metadataRetriever.release();
- frame = resize(frame, size);
- } catch(IllegalArgumentException | NullPointerException e) {
- frame = Bitmap.createBitmap(size,size, Bitmap.Config.ARGB_8888);
- frame.eraseColor(0xff000000);
- }
- Canvas canvas = new Canvas(frame);
- Bitmap play = BitmapFactory.decodeResource(mXmppConnectionService.getResources(), R.drawable.play_video);
- float x = (frame.getWidth() - play.getWidth()) / 2.0f;
- float y = (frame.getHeight() - play.getHeight()) / 2.0f;
- canvas.drawBitmap(play,x,y,null);
- return frame;
- }
-
- private static String getTakePhotoPath() {
- return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)+"/Camera/";
- }
-
- public Uri getTakePhotoUri() {
- File file = new File(getTakePhotoPath()+"IMG_" + fileDateFormat.format(new Date()) + ".jpg");
- file.getParentFile().mkdirs();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- return FileProvider.getUriForFile(mXmppConnectionService, CONVERSATIONS_FILE_PROVIDER, file);
- } else {
- return Uri.fromFile(file);
- }
- }
-
- public static Uri getIndexableTakePhotoUri(Uri original) {
- if ("file".equals(original.getScheme())) {
- return original;
- } else {
- List<String> segments = original.getPathSegments();
- return Uri.parse("file://"+getTakePhotoPath()+segments.get(segments.size() - 1));
- }
- }
-
- 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 mBase64OutputStream = new Base64OutputStream(
- mByteArrayOutputStream, Base64.DEFAULT);
- MessageDigest digest = MessageDigest.getInstance("SHA-1");
- DigestOutputStream mDigestOutputStream = new DigestOutputStream(
- mBase64OutputStream, 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) {
- 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);
- final String mime = file.getMimeType();
- boolean image = message.getType() == Message.TYPE_IMAGE || (mime != null && mime.startsWith("image/"));
- boolean video = mime != null && mime.startsWith("video/");
- if (image || video) {
- try {
- Dimensions dimensions = image ? getImageDimensions(file) : getVideoDimensions(file);
- if (url == null) {
- message.setBody(Long.toString(file.getSize()) + '|' + dimensions.width + '|' + dimensions.height);
- } else {
- message.setBody(url.toString() + "|" + Long.toString(file.getSize()) + '|' + dimensions.width + '|' + dimensions.height);
- }
- return;
- } catch (NotAVideoFile notAVideoFile) {
- Log.d(Config.LOGTAG,"file with mime type "+file.getMimeType()+" was not a video file");
- //fall threw
- }
- }
- if (url != null) {
- message.setBody(url.toString()+"|"+Long.toString(file.getSize()));
- } else {
- message.setBody(Long.toString(file.getSize()));
- }
-
- }
-
- private Dimensions getImageDimensions(File file) {
- 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;
- return new Dimensions(imageHeight, imageWidth);
- }
-
- private Dimensions getVideoDimensions(File file) throws NotAVideoFile {
- MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
- try {
- metadataRetriever.setDataSource(file.getAbsolutePath());
- } catch (Exception e) {
- throw new NotAVideoFile();
- }
- String hasVideo = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO);
- if (hasVideo == null) {
- throw new NotAVideoFile();
- }
- int rotation = extractRotationFromMediaRetriever(metadataRetriever);
- boolean rotated = rotation == 90 || rotation == 270;
- int height;
- try {
- String h = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
- height = Integer.parseInt(h);
- } catch (Exception e) {
- height = -1;
- }
- int width;
- try {
- String w = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
- width = Integer.parseInt(w);
- } catch (Exception e) {
- width = -1;
- }
- metadataRetriever.release();
- Log.d(Config.LOGTAG,"extracted video dims "+width+"x"+height);
- return rotated ? new Dimensions(width, height) : new Dimensions(height, width);
- }
-
- private int extractRotationFromMediaRetriever(MediaMetadataRetriever metadataRetriever) {
- int rotation;
- if (Build.VERSION.SDK_INT >= 17) {
- String r = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
- try {
- rotation = Integer.parseInt(r);
- } catch (Exception e) {
- rotation = 0;
- }
- } else {
- rotation = 0;
- }
- return rotation;
- }
-
- private class Dimensions {
- public final int width;
- public final int height;
-
- public Dimensions(int height, int width) {
- this.width = width;
- this.height = height;
- }
- }
-
- private class NotAVideoFile extends Exception {
-
- }
-
- 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) {
- 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;
- }
- }
-
- public static Bitmap rotateBitmap(File file, Bitmap bitmap, int orientation) {
-
- if (orientation == 1) {
- return bitmap;
- }
-
- Matrix matrix = new Matrix();
- switch (orientation) {
- case 2:
- matrix.setScale(-1, 1);
- break;
- case 3:
- matrix.setRotate(180);
- break;
- case 4:
- matrix.setRotate(180);
- matrix.postScale(-1, 1);
- break;
- case 5:
- matrix.setRotate(90);
- matrix.postScale(-1, 1);
- break;
- case 6:
- matrix.setRotate(90);
- break;
- case 7:
- matrix.setRotate(-90);
- matrix.postScale(-1, 1);
- break;
- case 8:
- matrix.setRotate(-90);
- break;
- default:
- return bitmap;
- }
-
- try {
- Bitmap oriented = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
- bitmap.recycle();
- return oriented;
- } catch (OutOfMemoryError e) {
- e.printStackTrace();
- return bitmap;
- }
- }
+ private static final SimpleDateFormat fileDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
+
+ public static final String CONVERSATIONS_FILE_PROVIDER = "de.pixart.messenger.files";
+
+ private XmppConnectionService mXmppConnectionService;
+
+ public FileBackend(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
+
+ private void createNoMedia() {
+ final File nomedia_files = new File(getConversationsFileDirectory() + ".nomedia");
+ final File nomedia_audios = new File(getConversationsAudioDirectory() + ".nomedia");
+ if (!nomedia_files.exists()) {
+ try {
+ nomedia_files.createNewFile();
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, "could not create nomedia file for files directory");
+ }
+ }
+ if (!nomedia_audios.exists()) {
+ try {
+ nomedia_audios.createNewFile();
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, "could not create nomedia file for audio directory");
+ }
+ }
+ }
+
+ public void updateMediaScanner(File file) {
+ if (file.getAbsolutePath().startsWith(getConversationsImageDirectory())
+ || file.getAbsolutePath().startsWith(getConversationsVideoDirectory())) {
+ Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ intent.setData(Uri.fromFile(file));
+ mXmppConnectionService.sendBroadcast(intent);
+ } else {
+ createNoMedia();
+ }
+ }
+
+ public boolean deleteFile(Message message) {
+ File file = getFile(message);
+ if (file.delete()) {
+ updateMediaScanner(file);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public DownloadableFile getFile(Message message) {
+ return getFile(message, true);
+ }
+
+ public DownloadableFile getFile(Message message, boolean decrypted) {
+ final boolean encrypted = !decrypted
+ && (message.getEncryption() == Message.ENCRYPTION_PGP
+ || message.getEncryption() == Message.ENCRYPTION_DECRYPTED);
+ final DownloadableFile file;
+ String path = message.getRelativeFilePath();
+ if (path == null) {
+ String filename = fileDateFormat.format(new Date(message.getTimeSent())) + "_" + message.getUuid().substring(0, 4);
+ path = filename;
+ }
+ if (path.startsWith("/")) {
+ file = new DownloadableFile(path);
+ } else {
+ String mime = message.getMimeType();
+ if (mime != null && mime.startsWith("image")) {
+ file = new DownloadableFile(getConversationsImageDirectory() + path);
+ } else if (mime != null && mime.startsWith("video")) {
+ file = new DownloadableFile(getConversationsVideoDirectory() + path);
+ } else if (mime != null && mime.startsWith("audio")) {
+ file = new DownloadableFile(getConversationsAudioDirectory() + path);
+ } else {
+ file = new DownloadableFile(getConversationsFileDirectory() + path);
+ }
+ }
+ if (encrypted) {
+ return new DownloadableFile(getConversationsFileDirectory() + file.getName() + ".pgp");
+ } else {
+ return file;
+ }
+ }
+
+ 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() + "/Pix-Art Messenger/files/";
+ }
+
+ public static String getConversationsImageDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pix-Art Messenger/images/";
+ }
+
+ public static String getConversationsVideoDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pix-Art Messenger/videos/";
+ }
+
+ public static String getConversationsAudioDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pix-Art Messenger/audios/";
+ }
+
+ public static String getConversationsDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pix-Art Messenger/";
+ }
+
+ 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;
+ }
+
+ 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 String getOriginalPath(Uri uri) {
+ return FileUtils.getPath(mXmppConnectionService, uri);
+ }
+
+ public void copyFileToPrivateStorage(File file, Uri uri) throws FileCopyException {
+ Log.d(Config.LOGTAG, "copy file (" + uri.toString() + ") to private storage " + file.getAbsolutePath());
+ file.getParentFile().mkdirs();
+ OutputStream os = null;
+ InputStream is = null;
+ try {
+ file.createNewFile();
+ os = new FileOutputStream(file);
+ is = mXmppConnectionService.getContentResolver().openInputStream(uri);
+ byte[] buffer = new byte[1024];
+ int length;
+ while ((length = is.read(buffer)) > 0) {
+ try {
+ os.write(buffer, 0, length);
+ } catch (IOException e) {
+ throw new FileWriterException();
+ }
+ }
+ try {
+ os.flush();
+ } catch (IOException e) {
+ throw new FileWriterException();
+ }
+ } catch (FileNotFoundException e) {
+ throw new FileCopyException(R.string.error_file_not_found);
+ } catch (FileWriterException e) {
+ throw new FileCopyException(R.string.error_unable_to_create_temporary_file);
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new FileCopyException(R.string.error_io_exception);
+ } finally {
+ close(os);
+ close(is);
+ }
+ }
+
+ public void copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException {
+ String mime = mXmppConnectionService.getContentResolver().getType(uri);
+ Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage (mime=" + mime + ")");
+ String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
+ if (extension == null) {
+ extension = getExtensionFromUri(uri);
+ }
+ String filename = fileDateFormat.format(new Date(message.getTimeSent())) + "_" + message.getUuid().substring(0, 4);
+ message.setRelativeFilePath(filename + "." + extension);
+ copyFileToPrivateStorage(mXmppConnectionService.getFileBackend().getFile(message), uri);
+ }
+
+ private String getExtensionFromUri(Uri uri) {
+ String[] projection = {MediaStore.MediaColumns.DATA};
+ String filename = null;
+ Cursor cursor = mXmppConnectionService.getContentResolver().query(uri, projection, null, null, null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ filename = cursor.getString(0);
+ }
+ } catch (Exception e) {
+ filename = null;
+ } finally {
+ cursor.close();
+ }
+ }
+ int pos = filename == null ? -1 : filename.lastIndexOf('.');
+ return pos > 0 ? filename.substring(pos + 1) : null;
+ }
+
+ private void copyImageToPrivateStorage(File file, Uri image, int sampleSize) throws FileCopyException {
+ file.getParentFile().mkdirs();
+ InputStream is = null;
+ OutputStream os = null;
+ try {
+ if (!file.exists() && !file.createNewFile()) {
+ throw new FileCopyException(R.string.error_unable_to_create_temporary_file);
+ }
+ is = mXmppConnectionService.getContentResolver().openInputStream(image);
+ if (is == null) {
+ throw new FileCopyException(R.string.error_not_an_image_file);
+ }
+ 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();
+ } 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);
+ }
+ } finally {
+ close(os);
+ close(is);
+ }
+ }
+
+ public void copyImageToPrivateStorage(File file, Uri image) throws FileCopyException {
+ Log.d(Config.LOGTAG, "copy image (" + image.toString() + ") to private storage " + file.getAbsolutePath());
+ copyImageToPrivateStorage(file, image, 0);
+ }
+
+ public void copyImageToPrivateStorage(Message message, Uri image) throws FileCopyException {
+ String filename = fileDateFormat.format(new Date(message.getTimeSent())) + "_" + message.getUuid().substring(0, 4);
+ switch (Config.IMAGE_FORMAT) {
+ case JPEG:
+ message.setRelativeFilePath(filename + ".jpg");
+ break;
+ case PNG:
+ message.setRelativeFilePath(filename + ".png");
+ break;
+ case WEBP:
+ message.setRelativeFilePath(filename + ".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;
+ }
+ DownloadableFile file = getFile(message);
+ if (file.getMimeType().startsWith("video/")) {
+ thumbnail = getVideoPreview(file, size);
+ } else {
+ Bitmap fullsize = getFullsizeImagePreview(file, size);
+ if (fullsize == null) {
+ throw new FileNotFoundException();
+ }
+ thumbnail = resize(fullsize, size);
+ thumbnail = rotate(thumbnail, getRotation(file));
+ }
+ this.mXmppConnectionService.getBitmapCache().put(uuid, thumbnail);
+ }
+ }
+ return thumbnail;
+ }
+
+ private Bitmap getFullsizeImagePreview(File file, int size) {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = calcSampleSize(file, size);
+ try {
+ return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+ } catch (OutOfMemoryError e) {
+ options.inSampleSize *= 2;
+ return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+ }
+ }
+
+ private Bitmap getVideoPreview(File file, int size) {
+ MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
+ Bitmap frame;
+ try {
+ metadataRetriever.setDataSource(file.getAbsolutePath());
+ frame = metadataRetriever.getFrameAtTime(0);
+ metadataRetriever.release();
+ frame = resize(frame, size);
+ } catch (IllegalArgumentException | NullPointerException e) {
+ frame = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ frame.eraseColor(0xff000000);
+ }
+ Canvas canvas = new Canvas(frame);
+ Bitmap play = BitmapFactory.decodeResource(mXmppConnectionService.getResources(), R.drawable.play_video);
+ float x = (frame.getWidth() - play.getWidth()) / 2.0f;
+ float y = (frame.getHeight() - play.getHeight()) / 2.0f;
+ canvas.drawBitmap(play, x, y, null);
+ return frame;
+ }
+
+ private static String getTakePhotoPath() {
+ return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/Camera/";
+ }
+
+ public Uri getTakePhotoUri() {
+ File file = new File(getTakePhotoPath() + "IMG_" + fileDateFormat.format(new Date()) + ".jpg");
+ file.getParentFile().mkdirs();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ return FileProvider.getUriForFile(mXmppConnectionService, CONVERSATIONS_FILE_PROVIDER, file);
+ } else {
+ return Uri.fromFile(file);
+ }
+ }
+
+ public static Uri getIndexableTakePhotoUri(Uri original) {
+ if ("file".equals(original.getScheme())) {
+ return original;
+ } else {
+ List<String> segments = original.getPathSegments();
+ return Uri.parse("file://" + getTakePhotoPath() + segments.get(segments.size() - 1));
+ }
+ }
+
+ 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 mBase64OutputStream = new Base64OutputStream(
+ mByteArrayOutputStream, Base64.DEFAULT);
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ DigestOutputStream mDigestOutputStream = new DigestOutputStream(
+ mBase64OutputStream, 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) {
+ 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);
+ final String mime = file.getMimeType();
+ boolean image = message.getType() == Message.TYPE_IMAGE || (mime != null && mime.startsWith("image/"));
+ boolean video = mime != null && mime.startsWith("video/");
+ if (image || video) {
+ try {
+ Dimensions dimensions = image ? getImageDimensions(file) : getVideoDimensions(file);
+ if (url == null) {
+ message.setBody(Long.toString(file.getSize()) + '|' + dimensions.width + '|' + dimensions.height);
+ } else {
+ message.setBody(url.toString() + "|" + Long.toString(file.getSize()) + '|' + dimensions.width + '|' + dimensions.height);
+ }
+ return;
+ } catch (NotAVideoFile notAVideoFile) {
+ Log.d(Config.LOGTAG, "file with mime type " + file.getMimeType() + " was not a video file");
+ //fall threw
+ }
+ }
+ if (url != null) {
+ message.setBody(url.toString() + "|" + Long.toString(file.getSize()));
+ } else {
+ message.setBody(Long.toString(file.getSize()));
+ }
+
+ }
+
+ private Dimensions getImageDimensions(File file) {
+ 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;
+ return new Dimensions(imageHeight, imageWidth);
+ }
+
+ private Dimensions getVideoDimensions(File file) throws NotAVideoFile {
+ MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
+ try {
+ metadataRetriever.setDataSource(file.getAbsolutePath());
+ } catch (Exception e) {
+ throw new NotAVideoFile();
+ }
+ String hasVideo = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO);
+ if (hasVideo == null) {
+ throw new NotAVideoFile();
+ }
+ int rotation = extractRotationFromMediaRetriever(metadataRetriever);
+ boolean rotated = rotation == 90 || rotation == 270;
+ int height;
+ try {
+ String h = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
+ height = Integer.parseInt(h);
+ } catch (Exception e) {
+ height = -1;
+ }
+ int width;
+ try {
+ String w = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
+ width = Integer.parseInt(w);
+ } catch (Exception e) {
+ width = -1;
+ }
+ metadataRetriever.release();
+ Log.d(Config.LOGTAG, "extracted video dims " + width + "x" + height);
+ return rotated ? new Dimensions(width, height) : new Dimensions(height, width);
+ }
+
+ private int extractRotationFromMediaRetriever(MediaMetadataRetriever metadataRetriever) {
+ int rotation;
+ if (Build.VERSION.SDK_INT >= 17) {
+ String r = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
+ try {
+ rotation = Integer.parseInt(r);
+ } catch (Exception e) {
+ rotation = 0;
+ }
+ } else {
+ rotation = 0;
+ }
+ return rotation;
+ }
+
+ private class Dimensions {
+ public final int width;
+ public final int height;
+
+ public Dimensions(int height, int width) {
+ this.width = width;
+ this.height = height;
+ }
+ }
+
+ private class NotAVideoFile extends Exception {
+
+ }
+
+ 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) {
+ 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;
+ }
+ }
+
+ public static Bitmap rotateBitmap(File file, Bitmap bitmap, int orientation) {
+
+ if (orientation == 1) {
+ return bitmap;
+ }
+
+ Matrix matrix = new Matrix();
+ switch (orientation) {
+ case 2:
+ matrix.setScale(-1, 1);
+ break;
+ case 3:
+ matrix.setRotate(180);
+ break;
+ case 4:
+ matrix.setRotate(180);
+ matrix.postScale(-1, 1);
+ break;
+ case 5:
+ matrix.setRotate(90);
+ matrix.postScale(-1, 1);
+ break;
+ case 6:
+ matrix.setRotate(90);
+ break;
+ case 7:
+ matrix.setRotate(-90);
+ matrix.postScale(-1, 1);
+ break;
+ case 8:
+ matrix.setRotate(-90);
+ break;
+ default:
+ return bitmap;
+ }
+
+ try {
+ Bitmap oriented = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
+ bitmap.recycle();
+ return oriented;
+ } catch (OutOfMemoryError e) {
+ e.printStackTrace();
+ return bitmap;
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/persistance/OnPhoneContactsMerged.java b/src/main/java/de/pixart/messenger/persistance/OnPhoneContactsMerged.java
index 85e852b5d..d5253f227 100644
--- a/src/main/java/de/pixart/messenger/persistance/OnPhoneContactsMerged.java
+++ b/src/main/java/de/pixart/messenger/persistance/OnPhoneContactsMerged.java
@@ -1,5 +1,5 @@
package de.pixart.messenger.persistance;
public interface OnPhoneContactsMerged {
- public void phoneContactsMerged();
+ public void phoneContactsMerged();
}
diff --git a/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java b/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java
index 6b0bfdc35..fe101edc6 100644
--- a/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java
+++ b/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java
@@ -34,17 +34,17 @@ import de.pixart.messenger.Config;
import de.pixart.messenger.entities.DownloadableFile;
public class AbstractConnectionManager {
- protected XmppConnectionService mXmppConnectionService;
+ protected XmppConnectionService mXmppConnectionService;
- public AbstractConnectionManager(XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
+ public AbstractConnectionManager(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
- public XmppConnectionService getXmppConnectionService() {
- return this.mXmppConnectionService;
- }
+ public XmppConnectionService getXmppConnectionService() {
+ return this.mXmppConnectionService;
+ }
- public long getAutoAcceptFileSize() {
+ public long getAutoAcceptFileSize() {
String config = "0";
if (mXmppConnectionService.isWIFI()) {
config = this.mXmppConnectionService.getPreferences().getString(
@@ -57,96 +57,96 @@ public class AbstractConnectionManager {
"auto_accept_file_size_roaming", "1");
}
try {
- return Long.parseLong(config);
- } catch (NumberFormatException e) {
- return 1048576;
- }
- }
+ return Long.parseLong(config);
+ } catch (NumberFormatException e) {
+ return 1048576;
+ }
+ }
- public boolean hasStoragePermission() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- return mXmppConnectionService.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
- } else {
- return true;
- }
- }
+ public boolean hasStoragePermission() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ return mXmppConnectionService.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
+ } else {
+ return true;
+ }
+ }
- public static Pair<InputStream,Integer> createInputStream(DownloadableFile file, boolean gcm) throws FileNotFoundException {
- FileInputStream is;
- int size;
- is = new FileInputStream(file);
- size = (int) file.getSize();
- if (file.getKey() == null) {
- return new Pair<InputStream,Integer>(is,size);
- }
- try {
- if (gcm) {
- AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
- cipher.init(true, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
- InputStream cis = new org.bouncycastle.crypto.io.CipherInputStream(is, cipher);
- return new Pair<>(cis, cipher.getOutputSize(size));
- } else {
- IvParameterSpec ips = new IvParameterSpec(file.getIv());
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
- cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
- Log.d(Config.LOGTAG, "opening encrypted input stream");
- final int s = Config.REPORT_WRONG_FILESIZE_IN_OTR_JINGLE ? size : (size / 16 + 1) * 16;
- return new Pair<InputStream,Integer>(new CipherInputStream(is, cipher),s);
- }
- } catch (InvalidKeyException e) {
- return null;
- } catch (NoSuchAlgorithmException e) {
- return null;
- } catch (NoSuchPaddingException e) {
- return null;
- } catch (InvalidAlgorithmParameterException e) {
- return null;
- }
- }
+ public static Pair<InputStream, Integer> createInputStream(DownloadableFile file, boolean gcm) throws FileNotFoundException {
+ FileInputStream is;
+ int size;
+ is = new FileInputStream(file);
+ size = (int) file.getSize();
+ if (file.getKey() == null) {
+ return new Pair<InputStream, Integer>(is, size);
+ }
+ try {
+ if (gcm) {
+ AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
+ cipher.init(true, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
+ InputStream cis = new org.bouncycastle.crypto.io.CipherInputStream(is, cipher);
+ return new Pair<>(cis, cipher.getOutputSize(size));
+ } else {
+ IvParameterSpec ips = new IvParameterSpec(file.getIv());
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
+ Log.d(Config.LOGTAG, "opening encrypted input stream");
+ final int s = Config.REPORT_WRONG_FILESIZE_IN_OTR_JINGLE ? size : (size / 16 + 1) * 16;
+ return new Pair<InputStream, Integer>(new CipherInputStream(is, cipher), s);
+ }
+ } catch (InvalidKeyException e) {
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ } catch (NoSuchPaddingException e) {
+ return null;
+ } catch (InvalidAlgorithmParameterException e) {
+ return null;
+ }
+ }
- public static OutputStream createAppendedOutputStream(DownloadableFile file) {
- return createOutputStream(file, false, true);
- }
+ public static OutputStream createAppendedOutputStream(DownloadableFile file) {
+ return createOutputStream(file, false, true);
+ }
- public static OutputStream createOutputStream(DownloadableFile file, boolean gcm) {
- return createOutputStream(file, gcm, false);
- }
+ public static OutputStream createOutputStream(DownloadableFile file, boolean gcm) {
+ return createOutputStream(file, gcm, false);
+ }
- private static OutputStream createOutputStream(DownloadableFile file, boolean gcm, boolean append) {
- FileOutputStream os;
- try {
- os = new FileOutputStream(file, append);
- if (file.getKey() == null) {
- return os;
- }
- } catch (FileNotFoundException e) {
- return null;
- }
- try {
- if (gcm) {
- AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
- cipher.init(false, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
- return new org.bouncycastle.crypto.io.CipherOutputStream(os, cipher);
- } else {
- IvParameterSpec ips = new IvParameterSpec(file.getIv());
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
- cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
- Log.d(Config.LOGTAG, "opening encrypted output stream");
- return new CipherOutputStream(os, cipher);
- }
- } catch (InvalidKeyException e) {
- return null;
- } catch (NoSuchAlgorithmException e) {
- return null;
- } catch (NoSuchPaddingException e) {
- return null;
- } catch (InvalidAlgorithmParameterException e) {
- return null;
- }
- }
+ private static OutputStream createOutputStream(DownloadableFile file, boolean gcm, boolean append) {
+ FileOutputStream os;
+ try {
+ os = new FileOutputStream(file, append);
+ if (file.getKey() == null) {
+ return os;
+ }
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ try {
+ if (gcm) {
+ AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
+ cipher.init(false, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
+ return new org.bouncycastle.crypto.io.CipherOutputStream(os, cipher);
+ } else {
+ IvParameterSpec ips = new IvParameterSpec(file.getIv());
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
+ Log.d(Config.LOGTAG, "opening encrypted output stream");
+ return new CipherOutputStream(os, cipher);
+ }
+ } catch (InvalidKeyException e) {
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ } catch (NoSuchPaddingException e) {
+ return null;
+ } catch (InvalidAlgorithmParameterException e) {
+ return null;
+ }
+ }
- public PowerManager.WakeLock createWakeLock(String name) {
- PowerManager powerManager = (PowerManager) mXmppConnectionService.getSystemService(Context.POWER_SERVICE);
- return powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,name);
- }
+ public PowerManager.WakeLock createWakeLock(String name) {
+ PowerManager powerManager = (PowerManager) mXmppConnectionService.getSystemService(Context.POWER_SERVICE);
+ return powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/services/AlarmReceiver.java b/src/main/java/de/pixart/messenger/services/AlarmReceiver.java
index 0fd3ba049..e20b567dd 100644
--- a/src/main/java/de/pixart/messenger/services/AlarmReceiver.java
+++ b/src/main/java/de/pixart/messenger/services/AlarmReceiver.java
@@ -7,7 +7,7 @@ import android.util.Log;
import de.pixart.messenger.Config;
-public class AlarmReceiver extends BroadcastReceiver{
+public class AlarmReceiver extends BroadcastReceiver {
public static final int SCHEDULE_ALARM_REQUEST_CODE = 523976483;
@Override
diff --git a/src/main/java/de/pixart/messenger/services/AvatarService.java b/src/main/java/de/pixart/messenger/services/AvatarService.java
index 943516271..4e7c69b22 100644
--- a/src/main/java/de/pixart/messenger/services/AvatarService.java
+++ b/src/main/java/de/pixart/messenger/services/AvatarService.java
@@ -26,406 +26,406 @@ import de.pixart.messenger.xmpp.XmppConnection;
public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
- private static final int FG_COLOR = 0xFFFAFAFA;
- private static final int TRANSPARENT = 0x00000000;
- private static final int PLACEHOLDER_COLOR = 0xFF202020;
-
- private static final String PREFIX_CONTACT = "contact";
- private static final String PREFIX_CONVERSATION = "conversation";
- private static final String PREFIX_ACCOUNT = "account";
- private static final String PREFIX_GENERIC = "generic";
-
- final private ArrayList<Integer> sizes = new ArrayList<>();
-
- protected XmppConnectionService mXmppConnectionService = null;
-
- public AvatarService(XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
-
- private Bitmap get(final Contact contact, final int size, boolean cachedOnly) {
- final String KEY = key(contact, size);
- Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY);
- if (avatar != null || cachedOnly) {
- return avatar;
- }
- if (avatar == null && contact.getAvatar() != null) {
- avatar = mXmppConnectionService.getFileBackend().getAvatar(contact.getAvatar(), size);
- }
- if (avatar == null && contact.getProfilePhoto() != null) {
- avatar = mXmppConnectionService.getFileBackend().cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size);
- }
- if (avatar == null) {
+ private static final int FG_COLOR = 0xFFFAFAFA;
+ private static final int TRANSPARENT = 0x00000000;
+ private static final int PLACEHOLDER_COLOR = 0xFF202020;
+
+ private static final String PREFIX_CONTACT = "contact";
+ private static final String PREFIX_CONVERSATION = "conversation";
+ private static final String PREFIX_ACCOUNT = "account";
+ private static final String PREFIX_GENERIC = "generic";
+
+ final private ArrayList<Integer> sizes = new ArrayList<>();
+
+ protected XmppConnectionService mXmppConnectionService = null;
+
+ public AvatarService(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
+
+ private Bitmap get(final Contact contact, final int size, boolean cachedOnly) {
+ final String KEY = key(contact, size);
+ Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY);
+ if (avatar != null || cachedOnly) {
+ return avatar;
+ }
+ if (avatar == null && contact.getAvatar() != null) {
+ avatar = mXmppConnectionService.getFileBackend().getAvatar(contact.getAvatar(), size);
+ }
+ if (avatar == null && contact.getProfilePhoto() != null) {
+ avatar = mXmppConnectionService.getFileBackend().cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size);
+ }
+ if (avatar == null) {
avatar = get(contact.getDisplayName(), size, cachedOnly);
- }
- this.mXmppConnectionService.getBitmapCache().put(KEY, avatar);
- return avatar;
- }
-
- public Bitmap get(final MucOptions.User user, final int size, boolean cachedOnly) {
- Contact c = user.getContact();
- if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null || user.getAvatar() == null)) {
- return get(c, size, cachedOnly);
- } else {
- return getImpl(user, size, cachedOnly);
- }
- }
-
- 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);
- if (avatar != null || cachedOnly) {
- return avatar;
- }
- if (user.getAvatar() != null) {
- avatar = mXmppConnectionService.getFileBackend().getAvatar(user.getAvatar(), size);
- }
- if (avatar == null) {
- Contact contact = user.getContact();
- if (contact != null) {
- avatar = get(contact, size, cachedOnly);
- } else {
- avatar = get(user.getName(), size, cachedOnly);
- }
- }
- this.mXmppConnectionService.getBitmapCache().put(KEY, avatar);
- return avatar;
- }
-
- public void clear(Contact contact) {
- synchronized (this.sizes) {
- for (Integer size : sizes) {
- this.mXmppConnectionService.getBitmapCache().remove(
- key(contact, size));
- }
- }
- for(Conversation conversation : mXmppConnectionService.findAllConferencesWith(contact)) {
- clear(conversation);
- }
- }
-
- private String key(Contact contact, int size) {
- synchronized (this.sizes) {
- if (!this.sizes.contains(size)) {
- this.sizes.add(size);
- }
- }
- return PREFIX_CONTACT + "_" + contact.getAccount().getJid().toBareJid() + "_"
- + contact.getJid() + "_" + String.valueOf(size);
- }
-
- private String key(MucOptions.User user, int size) {
- synchronized (this.sizes) {
- if (!this.sizes.contains(size)) {
- this.sizes.add(size);
- }
- }
- return PREFIX_CONTACT + "_" + user.getAccount().getJid().toBareJid() + "_"
- + user.getFullJid() + "_" + String.valueOf(size);
- }
-
- public Bitmap get(ListItem item, int size) {
- return get(item,size,false);
- }
-
- public Bitmap get(ListItem item, int size, boolean cachedOnly) {
- if (item instanceof Contact) {
- return get((Contact) item, size,cachedOnly);
- } else if (item instanceof Bookmark) {
- Bookmark bookmark = (Bookmark) item;
- if (bookmark.getConversation() != null) {
- return get(bookmark.getConversation(), size, cachedOnly);
- } else {
- return get(bookmark.getDisplayName(), size, cachedOnly);
- }
- } else {
- return get(item.getDisplayName(), size, cachedOnly);
- }
- }
-
- public Bitmap get(Conversation conversation, int size) {
- return get(conversation,size,false);
- }
-
- public Bitmap get(Conversation conversation, int size, boolean cachedOnly) {
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- return get(conversation.getContact(), size, cachedOnly);
- } else {
- return get(conversation.getMucOptions(), size, cachedOnly);
- }
- }
-
- public void clear(Conversation conversation) {
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- clear(conversation.getContact());
- } else {
- clear(conversation.getMucOptions());
- }
- }
-
- private Bitmap get(MucOptions mucOptions, int size, boolean cachedOnly) {
- final String KEY = key(mucOptions, size);
- Bitmap bitmap = this.mXmppConnectionService.getBitmapCache().get(KEY);
- if (bitmap != null || cachedOnly) {
- return bitmap;
- }
- final List<MucOptions.User> users = mucOptions.getUsers();
- int count = users.size();
- bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- bitmap.eraseColor(TRANSPARENT);
-
- if (count == 0) {
- String name = mucOptions.getConversation().getName();
- drawTile(canvas, name, 0, 0, size, size);
- } else if (count == 1) {
- drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
- drawTile(canvas, mucOptions.getConversation().getAccount(), size / 2 + 1, 0, size, size);
- } else if (count == 2) {
- drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
- drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size);
- } else if (count == 3) {
- drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
- drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size / 2 - 1);
- drawTile(canvas, users.get(2), size / 2 + 1, size / 2 + 1, size,
- size);
- } else if (count == 4) {
- drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1);
- drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size);
- drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1);
- drawTile(canvas, users.get(3), size / 2 + 1, size / 2 + 1, size,
- size);
- } else {
- drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1);
- drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size);
- drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1);
- drawTile(canvas, "\u2026", PLACEHOLDER_COLOR, size / 2 + 1, size / 2 + 1,
- size, size);
- }
- this.mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
- return bitmap;
- }
-
- public void clear(MucOptions options) {
- synchronized (this.sizes) {
- for (Integer size : sizes) {
- this.mXmppConnectionService.getBitmapCache().remove(key(options, size));
- }
- }
- }
-
- private String key(MucOptions options, int size) {
- synchronized (this.sizes) {
- if (!this.sizes.contains(size)) {
- this.sizes.add(size);
- }
- }
- return PREFIX_CONVERSATION + "_" + options.getConversation().getUuid()
- + "_" + String.valueOf(size);
- }
-
- public Bitmap get(Account account, int size) {
- return get(account, size, false);
- }
-
- public Bitmap get(Account account, int size, boolean cachedOnly) {
- final String KEY = key(account, size);
- Bitmap avatar = mXmppConnectionService.getBitmapCache().get(KEY);
- if (avatar != null || cachedOnly) {
- return avatar;
- }
- avatar = mXmppConnectionService.getFileBackend().getAvatar(account.getAvatar(), size);
- if (avatar == null) {
- avatar = get(account.getJid().toBareJid().toString(), size,false);
- }
- mXmppConnectionService.getBitmapCache().put(KEY, avatar);
- return avatar;
- }
-
- public Bitmap get(Message message, int size, boolean cachedOnly) {
- final Conversation conversation = message.getConversation();
- if (message.getStatus() == Message.STATUS_RECEIVED) {
- Contact c = message.getContact();
- if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null)) {
- return get(c, size, cachedOnly);
- } else if (message.getConversation().getMode() == Conversation.MODE_MULTI){
- MucOptions.User user = conversation.getMucOptions().findUserByFullJid(message.getCounterpart());
- if (user != null) {
- return getImpl(user,size,cachedOnly);
- }
- }
- return get(UIHelper.getMessageDisplayName(message), size, cachedOnly);
- } else {
- return get(conversation.getAccount(), size, cachedOnly);
- }
- }
-
- public void clear(Account account) {
- synchronized (this.sizes) {
- for (Integer size : sizes) {
- this.mXmppConnectionService.getBitmapCache().remove(key(account, size));
- }
- }
- }
-
- public void clear(MucOptions.User user) {
- synchronized (this.sizes) {
- for (Integer size : sizes) {
- this.mXmppConnectionService.getBitmapCache().remove(key(user, size));
- }
- }
- }
-
- private String key(Account account, int size) {
- synchronized (this.sizes) {
- if (!this.sizes.contains(size)) {
- this.sizes.add(size);
- }
- }
- return PREFIX_ACCOUNT + "_" + account.getUuid() + "_"
- + String.valueOf(size);
- }
-
- public Bitmap get(String name, int size) {
- return get(name,size,false);
- }
-
- public Bitmap get(final String name, final int size, boolean cachedOnly) {
- final String KEY = key(name, size);
- Bitmap bitmap = mXmppConnectionService.getBitmapCache().get(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 == null ? "" : name.trim();
- drawTile(canvas, trimmedName, 0, 0, size, size);
- mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
- return bitmap;
- }
-
- private String key(String name, int size) {
- synchronized (this.sizes) {
- if (!this.sizes.contains(size)) {
- this.sizes.add(size);
- }
- }
- return PREFIX_GENERIC + "_" + name + "_" + String.valueOf(size);
- }
-
- private boolean drawTile(Canvas canvas, String letter, int tileColor,
- int left, int top, int right, int bottom) {
- letter = letter.toUpperCase(Locale.getDefault());
- Paint tilePaint = new Paint(), textPaint = new Paint();
- tilePaint.setColor(tileColor);
- textPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
- textPaint.setColor(FG_COLOR);
- textPaint.setTypeface(Typeface.create("sans-serif-light",
- Typeface.NORMAL));
- textPaint.setTextSize((float) ((right - left) * 0.8));
- Rect rect = new Rect();
-
- canvas.drawRect(new Rect(left, top, right, bottom), tilePaint);
- textPaint.getTextBounds(letter, 0, 1, rect);
- float width = textPaint.measureText(letter);
- canvas.drawText(letter, (right + left) / 2 - width / 2, (top + bottom)
- / 2 + rect.height() / 2, textPaint);
- return true;
- }
-
- private boolean drawTile(Canvas canvas, MucOptions.User user, int left,
- int top, int right, int bottom) {
- Contact contact = user.getContact();
- if (contact != null) {
- Uri uri = null;
- if (contact.getAvatar() != null) {
- uri = mXmppConnectionService.getFileBackend().getAvatarUri(
- contact.getAvatar());
- } else if (contact.getProfilePhoto() != null) {
- uri = Uri.parse(contact.getProfilePhoto());
- }
- if (drawTile(canvas, uri, left, top, right, bottom)) {
- return true;
- }
- } else if (user.getAvatar() != null) {
- Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar());
- if (drawTile(canvas, uri, left, top, right, bottom)) {
- return true;
- }
- } else if (user.getAvatar() != null) {
- Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar());
- if (drawTile(canvas, uri, left, top, right, bottom)) {
- return true;
- }
- } else if (user.getAvatar() != null) {
- Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar());
- if (drawTile(canvas, uri, left, top, right, bottom)) {
- return true;
- }
- }
- String name = contact != null ? contact.getDisplayName() : user.getName();
- drawTile(canvas, name, left, top, right, bottom);
- return true;
- }
-
- 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);
- if (uri != null) {
- if (drawTile(canvas, uri, left, top, right, bottom)) {
- return true;
- }
- }
- }
- return drawTile(canvas, account.getJid().toBareJid().toString(), left, top, right, bottom);
- }
-
- private boolean drawTile(Canvas canvas, String name, int left, int top, int right, int bottom) {
- if (name != null) {
- final String letter = getFirstLetter(name);
- final int color = UIHelper.getColorForName(name);
- drawTile(canvas, letter, color, left, top, right, bottom);
- return true;
- }
- return false;
- }
-
- private static String getFirstLetter(String name) {
- for(Character c : name.toCharArray()) {
- if (Character.isLetterOrDigit(c)) {
- return c.toString();
- }
- }
- return "X";
- }
-
- 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);
- if (bitmap != null) {
- drawTile(canvas, bitmap, left, top, right, bottom);
- return true;
- }
- }
- return false;
- }
-
- 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;
- }
-
- @Override
- 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");
- if (account.getAvatar() != null) {
- mXmppConnectionService.republishAvatarIfNeeded(account);
- }
- }
- }
+ }
+ this.mXmppConnectionService.getBitmapCache().put(KEY, avatar);
+ return avatar;
+ }
+
+ public Bitmap get(final MucOptions.User user, final int size, boolean cachedOnly) {
+ Contact c = user.getContact();
+ if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null || user.getAvatar() == null)) {
+ return get(c, size, cachedOnly);
+ } else {
+ return getImpl(user, size, cachedOnly);
+ }
+ }
+
+ 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);
+ if (avatar != null || cachedOnly) {
+ return avatar;
+ }
+ if (user.getAvatar() != null) {
+ avatar = mXmppConnectionService.getFileBackend().getAvatar(user.getAvatar(), size);
+ }
+ if (avatar == null) {
+ Contact contact = user.getContact();
+ if (contact != null) {
+ avatar = get(contact, size, cachedOnly);
+ } else {
+ avatar = get(user.getName(), size, cachedOnly);
+ }
+ }
+ this.mXmppConnectionService.getBitmapCache().put(KEY, avatar);
+ return avatar;
+ }
+
+ public void clear(Contact contact) {
+ synchronized (this.sizes) {
+ for (Integer size : sizes) {
+ this.mXmppConnectionService.getBitmapCache().remove(
+ key(contact, size));
+ }
+ }
+ for (Conversation conversation : mXmppConnectionService.findAllConferencesWith(contact)) {
+ clear(conversation);
+ }
+ }
+
+ private String key(Contact contact, int size) {
+ synchronized (this.sizes) {
+ if (!this.sizes.contains(size)) {
+ this.sizes.add(size);
+ }
+ }
+ return PREFIX_CONTACT + "_" + contact.getAccount().getJid().toBareJid() + "_"
+ + contact.getJid() + "_" + String.valueOf(size);
+ }
+
+ private String key(MucOptions.User user, int size) {
+ synchronized (this.sizes) {
+ if (!this.sizes.contains(size)) {
+ this.sizes.add(size);
+ }
+ }
+ return PREFIX_CONTACT + "_" + user.getAccount().getJid().toBareJid() + "_"
+ + user.getFullJid() + "_" + String.valueOf(size);
+ }
+
+ public Bitmap get(ListItem item, int size) {
+ return get(item, size, false);
+ }
+
+ public Bitmap get(ListItem item, int size, boolean cachedOnly) {
+ if (item instanceof Contact) {
+ return get((Contact) item, size, cachedOnly);
+ } else if (item instanceof Bookmark) {
+ Bookmark bookmark = (Bookmark) item;
+ if (bookmark.getConversation() != null) {
+ return get(bookmark.getConversation(), size, cachedOnly);
+ } else {
+ return get(bookmark.getDisplayName(), size, cachedOnly);
+ }
+ } else {
+ return get(item.getDisplayName(), size, cachedOnly);
+ }
+ }
+
+ public Bitmap get(Conversation conversation, int size) {
+ return get(conversation, size, false);
+ }
+
+ public Bitmap get(Conversation conversation, int size, boolean cachedOnly) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ return get(conversation.getContact(), size, cachedOnly);
+ } else {
+ return get(conversation.getMucOptions(), size, cachedOnly);
+ }
+ }
+
+ public void clear(Conversation conversation) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ clear(conversation.getContact());
+ } else {
+ clear(conversation.getMucOptions());
+ }
+ }
+
+ private Bitmap get(MucOptions mucOptions, int size, boolean cachedOnly) {
+ final String KEY = key(mucOptions, size);
+ Bitmap bitmap = this.mXmppConnectionService.getBitmapCache().get(KEY);
+ if (bitmap != null || cachedOnly) {
+ return bitmap;
+ }
+ final List<MucOptions.User> users = mucOptions.getUsers();
+ int count = users.size();
+ bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ bitmap.eraseColor(TRANSPARENT);
+
+ if (count == 0) {
+ String name = mucOptions.getConversation().getName();
+ drawTile(canvas, name, 0, 0, size, size);
+ } else if (count == 1) {
+ drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
+ drawTile(canvas, mucOptions.getConversation().getAccount(), size / 2 + 1, 0, size, size);
+ } else if (count == 2) {
+ drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
+ drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size);
+ } else if (count == 3) {
+ drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
+ drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size / 2 - 1);
+ drawTile(canvas, users.get(2), size / 2 + 1, size / 2 + 1, size,
+ size);
+ } else if (count == 4) {
+ drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1);
+ drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size);
+ drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1);
+ drawTile(canvas, users.get(3), size / 2 + 1, size / 2 + 1, size,
+ size);
+ } else {
+ drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1);
+ drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size);
+ drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1);
+ drawTile(canvas, "\u2026", PLACEHOLDER_COLOR, size / 2 + 1, size / 2 + 1,
+ size, size);
+ }
+ this.mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
+ return bitmap;
+ }
+
+ public void clear(MucOptions options) {
+ synchronized (this.sizes) {
+ for (Integer size : sizes) {
+ this.mXmppConnectionService.getBitmapCache().remove(key(options, size));
+ }
+ }
+ }
+
+ private String key(MucOptions options, int size) {
+ synchronized (this.sizes) {
+ if (!this.sizes.contains(size)) {
+ this.sizes.add(size);
+ }
+ }
+ return PREFIX_CONVERSATION + "_" + options.getConversation().getUuid()
+ + "_" + String.valueOf(size);
+ }
+
+ public Bitmap get(Account account, int size) {
+ return get(account, size, false);
+ }
+
+ public Bitmap get(Account account, int size, boolean cachedOnly) {
+ final String KEY = key(account, size);
+ Bitmap avatar = mXmppConnectionService.getBitmapCache().get(KEY);
+ if (avatar != null || cachedOnly) {
+ return avatar;
+ }
+ avatar = mXmppConnectionService.getFileBackend().getAvatar(account.getAvatar(), size);
+ if (avatar == null) {
+ avatar = get(account.getJid().toBareJid().toString(), size, false);
+ }
+ mXmppConnectionService.getBitmapCache().put(KEY, avatar);
+ return avatar;
+ }
+
+ public Bitmap get(Message message, int size, boolean cachedOnly) {
+ final Conversation conversation = message.getConversation();
+ if (message.getStatus() == Message.STATUS_RECEIVED) {
+ Contact c = message.getContact();
+ if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null)) {
+ return get(c, size, cachedOnly);
+ } else if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
+ MucOptions.User user = conversation.getMucOptions().findUserByFullJid(message.getCounterpart());
+ if (user != null) {
+ return getImpl(user, size, cachedOnly);
+ }
+ }
+ return get(UIHelper.getMessageDisplayName(message), size, cachedOnly);
+ } else {
+ return get(conversation.getAccount(), size, cachedOnly);
+ }
+ }
+
+ public void clear(Account account) {
+ synchronized (this.sizes) {
+ for (Integer size : sizes) {
+ this.mXmppConnectionService.getBitmapCache().remove(key(account, size));
+ }
+ }
+ }
+
+ public void clear(MucOptions.User user) {
+ synchronized (this.sizes) {
+ for (Integer size : sizes) {
+ this.mXmppConnectionService.getBitmapCache().remove(key(user, size));
+ }
+ }
+ }
+
+ private String key(Account account, int size) {
+ synchronized (this.sizes) {
+ if (!this.sizes.contains(size)) {
+ this.sizes.add(size);
+ }
+ }
+ return PREFIX_ACCOUNT + "_" + account.getUuid() + "_"
+ + String.valueOf(size);
+ }
+
+ public Bitmap get(String name, int size) {
+ return get(name, size, false);
+ }
+
+ public Bitmap get(final String name, final int size, boolean cachedOnly) {
+ final String KEY = key(name, size);
+ Bitmap bitmap = mXmppConnectionService.getBitmapCache().get(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 == null ? "" : name.trim();
+ drawTile(canvas, trimmedName, 0, 0, size, size);
+ mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
+ return bitmap;
+ }
+
+ private String key(String name, int size) {
+ synchronized (this.sizes) {
+ if (!this.sizes.contains(size)) {
+ this.sizes.add(size);
+ }
+ }
+ return PREFIX_GENERIC + "_" + name + "_" + String.valueOf(size);
+ }
+
+ private boolean drawTile(Canvas canvas, String letter, int tileColor,
+ int left, int top, int right, int bottom) {
+ letter = letter.toUpperCase(Locale.getDefault());
+ Paint tilePaint = new Paint(), textPaint = new Paint();
+ tilePaint.setColor(tileColor);
+ textPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ textPaint.setColor(FG_COLOR);
+ textPaint.setTypeface(Typeface.create("sans-serif-light",
+ Typeface.NORMAL));
+ textPaint.setTextSize((float) ((right - left) * 0.8));
+ Rect rect = new Rect();
+
+ canvas.drawRect(new Rect(left, top, right, bottom), tilePaint);
+ textPaint.getTextBounds(letter, 0, 1, rect);
+ float width = textPaint.measureText(letter);
+ canvas.drawText(letter, (right + left) / 2 - width / 2, (top + bottom)
+ / 2 + rect.height() / 2, textPaint);
+ return true;
+ }
+
+ private boolean drawTile(Canvas canvas, MucOptions.User user, int left,
+ int top, int right, int bottom) {
+ Contact contact = user.getContact();
+ if (contact != null) {
+ Uri uri = null;
+ if (contact.getAvatar() != null) {
+ uri = mXmppConnectionService.getFileBackend().getAvatarUri(
+ contact.getAvatar());
+ } else if (contact.getProfilePhoto() != null) {
+ uri = Uri.parse(contact.getProfilePhoto());
+ }
+ if (drawTile(canvas, uri, left, top, right, bottom)) {
+ return true;
+ }
+ } else if (user.getAvatar() != null) {
+ Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar());
+ if (drawTile(canvas, uri, left, top, right, bottom)) {
+ return true;
+ }
+ } else if (user.getAvatar() != null) {
+ Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar());
+ if (drawTile(canvas, uri, left, top, right, bottom)) {
+ return true;
+ }
+ } else if (user.getAvatar() != null) {
+ Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar());
+ if (drawTile(canvas, uri, left, top, right, bottom)) {
+ return true;
+ }
+ }
+ String name = contact != null ? contact.getDisplayName() : user.getName();
+ drawTile(canvas, name, left, top, right, bottom);
+ return true;
+ }
+
+ 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);
+ if (uri != null) {
+ if (drawTile(canvas, uri, left, top, right, bottom)) {
+ return true;
+ }
+ }
+ }
+ return drawTile(canvas, account.getJid().toBareJid().toString(), left, top, right, bottom);
+ }
+
+ private boolean drawTile(Canvas canvas, String name, int left, int top, int right, int bottom) {
+ if (name != null) {
+ final String letter = getFirstLetter(name);
+ final int color = UIHelper.getColorForName(name);
+ drawTile(canvas, letter, color, left, top, right, bottom);
+ return true;
+ }
+ return false;
+ }
+
+ private static String getFirstLetter(String name) {
+ for (Character c : name.toCharArray()) {
+ if (Character.isLetterOrDigit(c)) {
+ return c.toString();
+ }
+ }
+ return "X";
+ }
+
+ 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);
+ if (bitmap != null) {
+ drawTile(canvas, bitmap, left, top, right, bottom);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ 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;
+ }
+
+ @Override
+ 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");
+ if (account.getAvatar() != null) {
+ mXmppConnectionService.republishAvatarIfNeeded(account);
+ }
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/services/CheckAppVersionService.java b/src/main/java/de/pixart/messenger/services/CheckAppVersionService.java
index 72bc3508a..e3c4353ed 100644
--- a/src/main/java/de/pixart/messenger/services/CheckAppVersionService.java
+++ b/src/main/java/de/pixart/messenger/services/CheckAppVersionService.java
@@ -19,7 +19,7 @@ public class CheckAppVersionService extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
- doPost(request,response);
+ doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
diff --git a/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java b/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java
index 8fcedc095..53a7b6625 100644
--- a/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java
+++ b/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java
@@ -22,62 +22,62 @@ import de.pixart.messenger.ui.ShareWithActivity;
@TargetApi(Build.VERSION_CODES.M)
public class ContactChooserTargetService extends ChooserTargetService implements ServiceConnection {
- private final Object lock = new Object();
+ private final Object lock = new Object();
- private XmppConnectionService mXmppConnectionService;
+ private XmppConnectionService mXmppConnectionService;
- private final int MAX_TARGETS = 5;
+ private final int MAX_TARGETS = 5;
- @Override
- public List<ChooserTarget> onGetChooserTargets(ComponentName targetActivityName, IntentFilter matchedFilter) {
- Intent intent = new Intent(this, XmppConnectionService.class);
- intent.setAction("contact_chooser");
- startService(intent);
- bindService(intent, this, Context.BIND_AUTO_CREATE);
- ArrayList<ChooserTarget> chooserTargets = new ArrayList<>();
- try {
- waitForService();
- final ArrayList<Conversation> conversations = new ArrayList<>();
- if (!mXmppConnectionService.areMessagesInitialized()) {
- return chooserTargets;
- }
- mXmppConnectionService.populateWithOrderedConversations(conversations, false);
- final ComponentName componentName = new ComponentName(this, ShareWithActivity.class);
- final int pixel = (int) (48 * getResources().getDisplayMetrics().density);
- 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 float score = 1 - (1.0f / MAX_TARGETS) * i;
- final Bundle extras = new Bundle();
- extras.putString("uuid", conversation.getUuid());
- chooserTargets.add(new ChooserTarget(name, icon, score, componentName, extras));
- }
- } catch (InterruptedException e) {
- }
- unbindService(this);
- return chooserTargets;
- }
+ @Override
+ public List<ChooserTarget> onGetChooserTargets(ComponentName targetActivityName, IntentFilter matchedFilter) {
+ Intent intent = new Intent(this, XmppConnectionService.class);
+ intent.setAction("contact_chooser");
+ startService(intent);
+ bindService(intent, this, Context.BIND_AUTO_CREATE);
+ ArrayList<ChooserTarget> chooserTargets = new ArrayList<>();
+ try {
+ waitForService();
+ final ArrayList<Conversation> conversations = new ArrayList<>();
+ if (!mXmppConnectionService.areMessagesInitialized()) {
+ return chooserTargets;
+ }
+ mXmppConnectionService.populateWithOrderedConversations(conversations, false);
+ final ComponentName componentName = new ComponentName(this, ShareWithActivity.class);
+ final int pixel = (int) (48 * getResources().getDisplayMetrics().density);
+ 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 float score = 1 - (1.0f / MAX_TARGETS) * i;
+ final Bundle extras = new Bundle();
+ extras.putString("uuid", conversation.getUuid());
+ chooserTargets.add(new ChooserTarget(name, icon, score, componentName, extras));
+ }
+ } catch (InterruptedException e) {
+ }
+ unbindService(this);
+ return chooserTargets;
+ }
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- XmppConnectionService.XmppConnectionBinder binder = (XmppConnectionService.XmppConnectionBinder) service;
- mXmppConnectionService = binder.getService();
- synchronized (this.lock) {
- lock.notifyAll();
- }
- }
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ XmppConnectionService.XmppConnectionBinder binder = (XmppConnectionService.XmppConnectionBinder) service;
+ mXmppConnectionService = binder.getService();
+ synchronized (this.lock) {
+ lock.notifyAll();
+ }
+ }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mXmppConnectionService = null;
- }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mXmppConnectionService = null;
+ }
- private void waitForService() throws InterruptedException {
- if (mXmppConnectionService == null) {
- synchronized (this.lock) {
- lock.wait();
- }
- }
- }
+ private void waitForService() throws InterruptedException {
+ if (mXmppConnectionService == null) {
+ synchronized (this.lock) {
+ lock.wait();
+ }
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/services/EventReceiver.java b/src/main/java/de/pixart/messenger/services/EventReceiver.java
index 63cefe738..cfbbeed48 100644
--- a/src/main/java/de/pixart/messenger/services/EventReceiver.java
+++ b/src/main/java/de/pixart/messenger/services/EventReceiver.java
@@ -7,19 +7,19 @@ import android.content.Intent;
import de.pixart.messenger.persistance.DatabaseBackend;
public class EventReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Intent mIntentForService = new Intent(context,
- XmppConnectionService.class);
- if (intent.getAction() != null) {
- mIntentForService.setAction(intent.getAction());
- } else {
- mIntentForService.setAction("other");
- }
- final String action = intent.getAction();
- if (action.equals("ui") || DatabaseBackend.getInstance(context).hasEnabledAccounts()) {
- context.startService(mIntentForService);
- }
- }
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Intent mIntentForService = new Intent(context,
+ XmppConnectionService.class);
+ if (intent.getAction() != null) {
+ mIntentForService.setAction(intent.getAction());
+ } else {
+ mIntentForService.setAction("other");
+ }
+ final String action = intent.getAction();
+ if (action.equals("ui") || DatabaseBackend.getInstance(context).hasEnabledAccounts()) {
+ context.startService(mIntentForService);
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/services/ExportLogsService.java b/src/main/java/de/pixart/messenger/services/ExportLogsService.java
index 8dcabcaf7..9008985c6 100644
--- a/src/main/java/de/pixart/messenger/services/ExportLogsService.java
+++ b/src/main/java/de/pixart/messenger/services/ExportLogsService.java
@@ -37,170 +37,170 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class ExportLogsService extends Service {
- private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
- private static final String DIRECTORY_STRING_FORMAT = FileBackend.getConversationsDirectory() + "/chats/%s";
- private static final String MESSAGE_STRING_FORMAT = "(%s) %s: %s\n";
- private static final int NOTIFICATION_ID = 1;
- private static AtomicBoolean running = new AtomicBoolean(false);
- private DatabaseBackend mDatabaseBackend;
- private List<Account> mAccounts;
- boolean ReadableLogsEnabled = false;
-
- @Override
- public void onCreate() {
- mDatabaseBackend = DatabaseBackend.getInstance(getBaseContext());
- mAccounts = mDatabaseBackend.getAccounts();
- final SharedPreferences ReadableLogs = PreferenceManager.getDefaultSharedPreferences(this);
- ReadableLogsEnabled = ReadableLogs.getBoolean("export_plain_text_logs", false);
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (running.compareAndSet(false, true)) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- export();
- stopForeground(true);
- running.set(false);
- stopSelf();
- }
- }).start();
- }
- return START_NOT_STICKY;
- }
-
- private void export() {
- List<Conversation> conversations = mDatabaseBackend.getConversations(Conversation.STATUS_AVAILABLE);
- conversations.addAll(mDatabaseBackend.getConversations(Conversation.STATUS_ARCHIVED));
- NotificationManager mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext());
- mBuilder.setContentTitle(getString(R.string.app_name))
- .setContentText(getString(R.string.notification_export_logs_title))
- .setSmallIcon(R.drawable.ic_import_export_white_24dp)
- .setProgress(0, 0, true);
- startForeground(NOTIFICATION_ID, mBuilder.build());
+ private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+ private static final String DIRECTORY_STRING_FORMAT = FileBackend.getConversationsDirectory() + "/chats/%s";
+ private static final String MESSAGE_STRING_FORMAT = "(%s) %s: %s\n";
+ private static final int NOTIFICATION_ID = 1;
+ private static AtomicBoolean running = new AtomicBoolean(false);
+ private DatabaseBackend mDatabaseBackend;
+ private List<Account> mAccounts;
+ boolean ReadableLogsEnabled = false;
+
+ @Override
+ public void onCreate() {
+ mDatabaseBackend = DatabaseBackend.getInstance(getBaseContext());
+ mAccounts = mDatabaseBackend.getAccounts();
+ final SharedPreferences ReadableLogs = PreferenceManager.getDefaultSharedPreferences(this);
+ ReadableLogsEnabled = ReadableLogs.getBoolean("export_plain_text_logs", false);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (running.compareAndSet(false, true)) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ export();
+ stopForeground(true);
+ running.set(false);
+ stopSelf();
+ }
+ }).start();
+ }
+ return START_NOT_STICKY;
+ }
+
+ private void export() {
+ List<Conversation> conversations = mDatabaseBackend.getConversations(Conversation.STATUS_AVAILABLE);
+ conversations.addAll(mDatabaseBackend.getConversations(Conversation.STATUS_ARCHIVED));
+ NotificationManager mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext());
+ mBuilder.setContentTitle(getString(R.string.app_name))
+ .setContentText(getString(R.string.notification_export_logs_title))
+ .setSmallIcon(R.drawable.ic_import_export_white_24dp)
+ .setProgress(0, 0, true);
+ startForeground(NOTIFICATION_ID, mBuilder.build());
mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
- if (ReadableLogsEnabled) {
- for (Conversation conversation : conversations) {
- writeToFile(conversation);
- }
- }
- if (mAccounts.size() == 1) {
- try {
- ExportDatabase();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- private void writeToFile(Conversation conversation) {
- Jid accountJid = resolveAccountUuid(conversation.getAccountUuid());
- Jid contactJid = conversation.getJid();
-
- File dir = new File(String.format(DIRECTORY_STRING_FORMAT,accountJid.toBareJid().toString()));
- dir.mkdirs();
-
- BufferedWriter bw = null;
- try {
- for (Message message : mDatabaseBackend.getMessagesIterable(conversation)) {
- if (message.getType() == Message.TYPE_TEXT || message.hasFileOnRemoteHost()) {
- String date = simpleDateFormat.format(new Date(message.getTimeSent()));
- if (bw == null) {
- bw = new BufferedWriter(new FileWriter(
- new File(dir, contactJid.toBareJid().toString() + ".txt")));
- }
- String jid = null;
- switch (message.getStatus()) {
- case Message.STATUS_RECEIVED:
- jid = getMessageCounterpart(message);
- break;
- case Message.STATUS_SEND:
- case Message.STATUS_SEND_RECEIVED:
- case Message.STATUS_SEND_DISPLAYED:
- jid = accountJid.toBareJid().toString();
- break;
- }
- if (jid != null) {
- String body = message.hasFileOnRemoteHost() ? message.getFileParams().url.toString() : message.getBody();
- bw.write(String.format(MESSAGE_STRING_FORMAT, date, jid,
- body.replace("\\\n", "\\ \n").replace("\n", "\\ \n")));
- }
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (bw != null) {
- bw.close();
- }
- } catch (IOException e1) {
- e1.printStackTrace();
- }
- }
- }
-
- private Jid resolveAccountUuid(String accountUuid) {
- for (Account account : mAccounts) {
- if (account.getUuid().equals(accountUuid)) {
- return account.getJid();
- }
- }
- return null;
- }
-
- private String getMessageCounterpart(Message message) {
- String trueCounterpart = (String) message.getContentValues().get(Message.TRUE_COUNTERPART);
- if (trueCounterpart != null) {
- return trueCounterpart;
- } else {
- return message.getCounterpart().toString();
- }
- }
-
- public void ExportDatabase() throws IOException {
- Account mAccount = mAccounts.get(0);
- // Get hold of the db:
- FileInputStream InputFile = new FileInputStream(this.getDatabasePath(DatabaseBackend.DATABASE_NAME));
- // Set the output folder on the SDcard
- File directory = new File(FileBackend.getConversationsDirectory() + "/database/");
- // Create the folder if it doesn't exist:
- if (!directory.exists()) {
- directory.mkdirs();
- }
- //Delete old database export file
- File temp_db_file = new File(directory + "/database.bak");
- if (temp_db_file.exists()) {
+ if (ReadableLogsEnabled) {
+ for (Conversation conversation : conversations) {
+ writeToFile(conversation);
+ }
+ }
+ if (mAccounts.size() == 1) {
+ try {
+ ExportDatabase();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void writeToFile(Conversation conversation) {
+ Jid accountJid = resolveAccountUuid(conversation.getAccountUuid());
+ Jid contactJid = conversation.getJid();
+
+ File dir = new File(String.format(DIRECTORY_STRING_FORMAT, accountJid.toBareJid().toString()));
+ dir.mkdirs();
+
+ BufferedWriter bw = null;
+ try {
+ for (Message message : mDatabaseBackend.getMessagesIterable(conversation)) {
+ if (message.getType() == Message.TYPE_TEXT || message.hasFileOnRemoteHost()) {
+ String date = simpleDateFormat.format(new Date(message.getTimeSent()));
+ if (bw == null) {
+ bw = new BufferedWriter(new FileWriter(
+ new File(dir, contactJid.toBareJid().toString() + ".txt")));
+ }
+ String jid = null;
+ switch (message.getStatus()) {
+ case Message.STATUS_RECEIVED:
+ jid = getMessageCounterpart(message);
+ break;
+ case Message.STATUS_SEND:
+ case Message.STATUS_SEND_RECEIVED:
+ case Message.STATUS_SEND_DISPLAYED:
+ jid = accountJid.toBareJid().toString();
+ break;
+ }
+ if (jid != null) {
+ String body = message.hasFileOnRemoteHost() ? message.getFileParams().url.toString() : message.getBody();
+ bw.write(String.format(MESSAGE_STRING_FORMAT, date, jid,
+ body.replace("\\\n", "\\ \n").replace("\n", "\\ \n")));
+ }
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (bw != null) {
+ bw.close();
+ }
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
+
+ private Jid resolveAccountUuid(String accountUuid) {
+ for (Account account : mAccounts) {
+ if (account.getUuid().equals(accountUuid)) {
+ return account.getJid();
+ }
+ }
+ return null;
+ }
+
+ private String getMessageCounterpart(Message message) {
+ String trueCounterpart = (String) message.getContentValues().get(Message.TRUE_COUNTERPART);
+ if (trueCounterpart != null) {
+ return trueCounterpart;
+ } else {
+ return message.getCounterpart().toString();
+ }
+ }
+
+ public void ExportDatabase() throws IOException {
+ Account mAccount = mAccounts.get(0);
+ // Get hold of the db:
+ FileInputStream InputFile = new FileInputStream(this.getDatabasePath(DatabaseBackend.DATABASE_NAME));
+ // Set the output folder on the SDcard
+ File directory = new File(FileBackend.getConversationsDirectory() + "/database/");
+ // Create the folder if it doesn't exist:
+ if (!directory.exists()) {
+ directory.mkdirs();
+ }
+ //Delete old database export file
+ File temp_db_file = new File(directory + "/database.bak");
+ if (temp_db_file.exists()) {
Log.d(Config.LOGTAG, "Delete temp database backup file from " + temp_db_file.toString());
- temp_db_file.delete();
- }
- // Set the output file stream up:
- FileOutputStream OutputFile = new FileOutputStream(directory.getPath() + "/database.db.crypt");
-
- String EncryptionKey = mAccount.getPassword(); //get account password
-
- // encrypt database from the input file to the output file
- try {
- EncryptDecryptFile.encrypt(InputFile, OutputFile, EncryptionKey);
- } catch (NoSuchAlgorithmException e) {
- Log.d(Config.LOGTAG,"Database exporter: encryption failed with " + e);
- e.printStackTrace();
- } catch (NoSuchPaddingException e) {
- Log.d(Config.LOGTAG,"Database exporter: encryption failed with " + e);
- e.printStackTrace();
- } catch (InvalidKeyException e) {
- Log.d(Config.LOGTAG,"Database exporter: encryption failed (invalid key) with " + e);
- e.printStackTrace();
- } catch (IOException e) {
- Log.d(Config.LOGTAG,"Database exporter: encryption failed (IO) with " + e);
- e.printStackTrace();
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
+ temp_db_file.delete();
+ }
+ // Set the output file stream up:
+ FileOutputStream OutputFile = new FileOutputStream(directory.getPath() + "/database.db.crypt");
+
+ String EncryptionKey = mAccount.getPassword(); //get account password
+
+ // encrypt database from the input file to the output file
+ try {
+ EncryptDecryptFile.encrypt(InputFile, OutputFile, EncryptionKey);
+ } catch (NoSuchAlgorithmException e) {
+ Log.d(Config.LOGTAG, "Database exporter: encryption failed with " + e);
+ e.printStackTrace();
+ } catch (NoSuchPaddingException e) {
+ Log.d(Config.LOGTAG, "Database exporter: encryption failed with " + e);
+ e.printStackTrace();
+ } catch (InvalidKeyException e) {
+ Log.d(Config.LOGTAG, "Database exporter: encryption failed (invalid key) with " + e);
+ e.printStackTrace();
+ } catch (IOException e) {
+ Log.d(Config.LOGTAG, "Database exporter: encryption failed (IO) with " + e);
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/services/MessageArchiveService.java b/src/main/java/de/pixart/messenger/services/MessageArchiveService.java
index 4748dc86d..702e0d093 100644
--- a/src/main/java/de/pixart/messenger/services/MessageArchiveService.java
+++ b/src/main/java/de/pixart/messenger/services/MessageArchiveService.java
@@ -22,29 +22,29 @@ import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
- private final XmppConnectionService mXmppConnectionService;
-
- private final HashSet<Query> queries = new HashSet<>();
- private final ArrayList<Query> pendingQueries = new ArrayList<>();
-
- public enum PagingOrder {
- NORMAL,
- REVERSE
- }
-
- public MessageArchiveService(final XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
-
- private void catchup(final Account account) {
- synchronized (this.queries) {
- for(Iterator<Query> iterator = this.queries.iterator(); iterator.hasNext();) {
- Query query = iterator.next();
- if (query.getAccount() == account) {
- iterator.remove();
- }
- }
- }
+ private final XmppConnectionService mXmppConnectionService;
+
+ private final HashSet<Query> queries = new HashSet<>();
+ private final ArrayList<Query> pendingQueries = new ArrayList<>();
+
+ public enum PagingOrder {
+ NORMAL,
+ REVERSE
+ }
+
+ public MessageArchiveService(final XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
+
+ private void catchup(final Account account) {
+ synchronized (this.queries) {
+ for (Iterator<Query> iterator = this.queries.iterator(); iterator.hasNext(); ) {
+ Query query = iterator.next();
+ if (query.getAccount() == account) {
+ iterator.remove();
+ }
+ }
+ }
final Pair<Long, String> lastMessageReceived = mXmppConnectionService.databaseBackend.getLastMessageReceived(account);
final Pair<Long, String> lastClearDate = mXmppConnectionService.databaseBackend.getLastClearDate(account);
long startCatchup;
@@ -57,363 +57,363 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
reference = null;
}
long endCatchup = account.getXmppConnection().getLastSessionEstablished();
- final Query query;
- if (startCatchup == 0) {
- return;
- } else if (endCatchup - startCatchup >= Config.MAM_MAX_CATCHUP) {
- startCatchup = endCatchup - Config.MAM_MAX_CATCHUP;
- List<Conversation> conversations = mXmppConnectionService.getConversations();
- for (Conversation conversation : conversations) {
- if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account && startCatchup > conversation.getLastMessageTransmitted()) {
- this.query(conversation,startCatchup);
- }
- }
- query = new Query(account, startCatchup, endCatchup);
- } else {
- query = new Query(account, startCatchup, endCatchup);
- query.reference = reference;
- }
- this.queries.add(query);
- this.execute(query);
- }
-
- public void catchupMUC(final Conversation conversation) {
- if (conversation.getLastMessageTransmitted() < 0 && conversation.countMessages() == 0) {
- query(conversation,
- 0,
- System.currentTimeMillis());
- } else {
- query(conversation,
- conversation.getLastMessageTransmitted(),
- System.currentTimeMillis());
- }
- }
-
- public Query query(final Conversation conversation) {
- if (conversation.getLastMessageTransmitted() < 0 && conversation.countMessages() == 0) {
- return query(conversation,
- 0,
- System.currentTimeMillis());
- } else {
- return query(conversation,
- conversation.getLastMessageTransmitted(),
- conversation.getAccount().getXmppConnection().getLastSessionEstablished());
- }
- }
-
- public Query query(final Conversation conversation, long end) {
- 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 void executePendingQueries(final Account account) {
- List<Query> pending = new ArrayList<>();
- synchronized(this.pendingQueries) {
- for(Iterator<Query> iterator = this.pendingQueries.iterator(); iterator.hasNext();) {
- Query query = iterator.next();
- if (query.getAccount() == account) {
- pending.add(query);
- iterator.remove();
- }
- }
- }
- for(Query query : pending) {
- this.execute(query);
- }
- }
-
- 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());
- IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(query);
- this.mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
- synchronized (MessageArchiveService.this.queries) {
- MessageArchiveService.this.queries.remove(query);
- if (query.hasCallback()) {
- query.callback(false);
- }
- }
- } else if (packet.getType() != IqPacket.TYPE.RESULT) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": error executing mam: " + packet.toString());
- finalizeQuery(query, true);
- }
- }
- });
- } else {
- synchronized (this.pendingQueries) {
- this.pendingQueries.add(query);
- }
- }
- }
-
- private void finalizeQuery(Query query, boolean done) {
- synchronized (this.queries) {
- this.queries.remove(query);
- }
- final Conversation conversation = query.getConversation();
- if (conversation != null) {
- conversation.sort();
- conversation.setHasMessagesLeftOnServer(!done);
- } else {
- for(Conversation tmp : this.mXmppConnectionService.getConversations()) {
- if (tmp.getAccount() == query.getAccount()) {
- tmp.sort();
- }
- }
- }
- if (query.hasCallback()) {
- query.callback(done);
- } else {
- this.mXmppConnectionService.updateConversationUi();
- }
- }
-
- public boolean queryInProgress(Conversation conversation, XmppConnectionService.OnMoreMessagesLoaded callback) {
- synchronized (this.queries) {
- for(Query query : queries) {
- if (query.conversation == conversation) {
- if (!query.hasCallback() && callback != null) {
- query.setCallback(callback);
- }
- return true;
- }
- }
- return false;
- }
- }
-
- public boolean queryInProgress(Conversation conversation) {
- return queryInProgress(conversation, null);
- }
-
- public void processFin(Element fin, Jid from) {
- if (fin == null) {
- return;
- }
- Query query = findQuery(fin.getAttribute("queryid"));
- if (query == null || !query.validFrom(from)) {
- return;
- }
- boolean complete = fin.getAttributeAsBoolean("complete");
- Element set = fin.findChild("set","http://jabber.org/protocol/rsm");
- Element last = set == null ? null : set.findChild("last");
- Element first = set == null ? null : set.findChild("first");
- Element relevant = query.getPagingOrder() == PagingOrder.NORMAL ? last : first;
- boolean abort = (query.getStart() == 0 && query.getTotalCount() >= Config.PAGE_SIZE) || query.getTotalCount() >= Config.MAM_MAX_MESSAGES;
- if (query.getConversation() != null) {
- query.getConversation().setFirstMamReference(first == null ? null : first.getContent());
- }
- 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));
- if (query.getWith() == null && query.getMessageCount() > 0) {
- mXmppConnectionService.getNotificationService().finishBacklog(true);
- }
- } else {
- final Query nextQuery;
- if (query.getPagingOrder() == PagingOrder.NORMAL) {
- nextQuery = query.next(last == null ? null : last.getContent());
- } else {
- nextQuery = query.prev(first == null ? null : first.getContent());
- }
- this.execute(nextQuery);
- this.finalizeQuery(query, false);
- synchronized (this.queries) {
- this.queries.add(nextQuery);
- }
- }
- }
-
- public Query findQuery(String id) {
- if (id == null) {
- return null;
- }
- synchronized (this.queries) {
- for(Query query : this.queries) {
- if (query.getQueryId().equals(id)) {
- return query;
- }
- }
- return null;
- }
- }
-
- @Override
- public void onAdvancedStreamFeaturesAvailable(Account account) {
- if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().mam()) {
- this.catchup(account);
- }
- }
-
- public class Query {
- private int totalCount = 0;
- private int messageCount = 0;
- private long start;
- private long end;
- private String queryId;
- private String reference = null;
- private Account account;
- private Conversation conversation;
- private PagingOrder pagingOrder = PagingOrder.NORMAL;
- private XmppConnectionService.OnMoreMessagesLoaded callback = null;
-
-
- public Query(Conversation conversation, long start, long end) {
- this(conversation.getAccount(), start, end);
- this.conversation = conversation;
- }
-
- public Query(Conversation conversation, long start, long end, PagingOrder order) {
- this(conversation,start,end);
- this.pagingOrder = order;
- }
-
- public Query(Account account, long start, long end) {
- this.account = account;
- this.start = start;
- this.end = end;
- this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
- }
-
- private Query page(String reference) {
- Query query = new Query(this.account,this.start,this.end);
- query.reference = reference;
- query.conversation = conversation;
- query.totalCount = totalCount;
- query.callback = callback;
- return query;
- }
-
- public Query next(String reference) {
- Query query = page(reference);
- query.pagingOrder = PagingOrder.NORMAL;
- return query;
- }
-
- public Query prev(String reference) {
- Query query = page(reference);
- query.pagingOrder = PagingOrder.REVERSE;
- return query;
- }
-
- public String getReference() {
- return reference;
- }
-
- public PagingOrder getPagingOrder() {
- return this.pagingOrder;
- }
-
- public String getQueryId() {
- return queryId;
- }
-
- public Jid getWith() {
- return conversation == null ? null : conversation.getJid().toBareJid();
- }
-
- public boolean muc() {
- return conversation != null && conversation.getMode() == Conversation.MODE_MULTI;
- }
-
- public long getStart() {
- return start;
- }
-
- public void setCallback(XmppConnectionService.OnMoreMessagesLoaded callback) {
- this.callback = callback;
- }
-
- public void callback(boolean done) {
- if (this.callback != null) {
- this.callback.onMoreMessagesLoaded(messageCount,conversation);
- if (done) {
- this.callback.informUser(R.string.no_more_history_on_server);
- }
- }
- }
-
- public long getEnd() {
- return end;
- }
-
- public Conversation getConversation() {
- return conversation;
- }
-
- public Account getAccount() {
- return this.account;
- }
-
- public void incrementMessageCount() {
- this.messageCount++;
- this.totalCount++;
- }
-
- public int getTotalCount() {
- return this.totalCount;
- }
-
- public int getMessageCount() {
- return this.messageCount;
- }
-
- public boolean validFrom(Jid from) {
- if (muc()) {
- return getWith().equals(from);
- } else {
- return (from == null) || account.getJid().toBareJid().equals(from.toBareJid());
- }
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- if (this.muc()) {
- builder.append("to=");
- builder.append(this.getWith().toString());
- } else {
- builder.append("with=");
- if (this.getWith() == null) {
- builder.append("*");
- } else {
- builder.append(getWith().toString());
- }
- }
- builder.append(", start=");
- builder.append(AbstractGenerator.getTimestamp(this.start));
- builder.append(", end=");
- builder.append(AbstractGenerator.getTimestamp(this.end));
- if (this.reference!=null) {
- if (this.pagingOrder == PagingOrder.NORMAL) {
- builder.append(", after=");
- } else {
- builder.append(", before=");
- }
- builder.append(this.reference);
- }
- return builder.toString();
- }
-
- public boolean hasCallback() {
- return this.callback != null;
- }
- }
+ final Query query;
+ if (startCatchup == 0) {
+ return;
+ } else if (endCatchup - startCatchup >= Config.MAM_MAX_CATCHUP) {
+ startCatchup = endCatchup - Config.MAM_MAX_CATCHUP;
+ List<Conversation> conversations = mXmppConnectionService.getConversations();
+ for (Conversation conversation : conversations) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account && startCatchup > conversation.getLastMessageTransmitted()) {
+ this.query(conversation, startCatchup);
+ }
+ }
+ query = new Query(account, startCatchup, endCatchup);
+ } else {
+ query = new Query(account, startCatchup, endCatchup);
+ query.reference = reference;
+ }
+ this.queries.add(query);
+ this.execute(query);
+ }
+
+ public void catchupMUC(final Conversation conversation) {
+ if (conversation.getLastMessageTransmitted() < 0 && conversation.countMessages() == 0) {
+ query(conversation,
+ 0,
+ System.currentTimeMillis());
+ } else {
+ query(conversation,
+ conversation.getLastMessageTransmitted(),
+ System.currentTimeMillis());
+ }
+ }
+
+ public Query query(final Conversation conversation) {
+ if (conversation.getLastMessageTransmitted() < 0 && conversation.countMessages() == 0) {
+ return query(conversation,
+ 0,
+ System.currentTimeMillis());
+ } else {
+ return query(conversation,
+ conversation.getLastMessageTransmitted(),
+ conversation.getAccount().getXmppConnection().getLastSessionEstablished());
+ }
+ }
+
+ public Query query(final Conversation conversation, long end) {
+ 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 void executePendingQueries(final Account account) {
+ List<Query> pending = new ArrayList<>();
+ synchronized (this.pendingQueries) {
+ for (Iterator<Query> iterator = this.pendingQueries.iterator(); iterator.hasNext(); ) {
+ Query query = iterator.next();
+ if (query.getAccount() == account) {
+ pending.add(query);
+ iterator.remove();
+ }
+ }
+ }
+ for (Query query : pending) {
+ this.execute(query);
+ }
+ }
+
+ 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());
+ IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(query);
+ this.mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
+ synchronized (MessageArchiveService.this.queries) {
+ MessageArchiveService.this.queries.remove(query);
+ if (query.hasCallback()) {
+ query.callback(false);
+ }
+ }
+ } else if (packet.getType() != IqPacket.TYPE.RESULT) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": error executing mam: " + packet.toString());
+ finalizeQuery(query, true);
+ }
+ }
+ });
+ } else {
+ synchronized (this.pendingQueries) {
+ this.pendingQueries.add(query);
+ }
+ }
+ }
+
+ private void finalizeQuery(Query query, boolean done) {
+ synchronized (this.queries) {
+ this.queries.remove(query);
+ }
+ final Conversation conversation = query.getConversation();
+ if (conversation != null) {
+ conversation.sort();
+ conversation.setHasMessagesLeftOnServer(!done);
+ } else {
+ for (Conversation tmp : this.mXmppConnectionService.getConversations()) {
+ if (tmp.getAccount() == query.getAccount()) {
+ tmp.sort();
+ }
+ }
+ }
+ if (query.hasCallback()) {
+ query.callback(done);
+ } else {
+ this.mXmppConnectionService.updateConversationUi();
+ }
+ }
+
+ public boolean queryInProgress(Conversation conversation, XmppConnectionService.OnMoreMessagesLoaded callback) {
+ synchronized (this.queries) {
+ for (Query query : queries) {
+ if (query.conversation == conversation) {
+ if (!query.hasCallback() && callback != null) {
+ query.setCallback(callback);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ public boolean queryInProgress(Conversation conversation) {
+ return queryInProgress(conversation, null);
+ }
+
+ public void processFin(Element fin, Jid from) {
+ if (fin == null) {
+ return;
+ }
+ Query query = findQuery(fin.getAttribute("queryid"));
+ if (query == null || !query.validFrom(from)) {
+ return;
+ }
+ boolean complete = fin.getAttributeAsBoolean("complete");
+ Element set = fin.findChild("set", "http://jabber.org/protocol/rsm");
+ Element last = set == null ? null : set.findChild("last");
+ Element first = set == null ? null : set.findChild("first");
+ Element relevant = query.getPagingOrder() == PagingOrder.NORMAL ? last : first;
+ boolean abort = (query.getStart() == 0 && query.getTotalCount() >= Config.PAGE_SIZE) || query.getTotalCount() >= Config.MAM_MAX_MESSAGES;
+ if (query.getConversation() != null) {
+ query.getConversation().setFirstMamReference(first == null ? null : first.getContent());
+ }
+ 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));
+ if (query.getWith() == null && query.getMessageCount() > 0) {
+ mXmppConnectionService.getNotificationService().finishBacklog(true);
+ }
+ } else {
+ final Query nextQuery;
+ if (query.getPagingOrder() == PagingOrder.NORMAL) {
+ nextQuery = query.next(last == null ? null : last.getContent());
+ } else {
+ nextQuery = query.prev(first == null ? null : first.getContent());
+ }
+ this.execute(nextQuery);
+ this.finalizeQuery(query, false);
+ synchronized (this.queries) {
+ this.queries.add(nextQuery);
+ }
+ }
+ }
+
+ public Query findQuery(String id) {
+ if (id == null) {
+ return null;
+ }
+ synchronized (this.queries) {
+ for (Query query : this.queries) {
+ if (query.getQueryId().equals(id)) {
+ return query;
+ }
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public void onAdvancedStreamFeaturesAvailable(Account account) {
+ if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().mam()) {
+ this.catchup(account);
+ }
+ }
+
+ public class Query {
+ private int totalCount = 0;
+ private int messageCount = 0;
+ private long start;
+ private long end;
+ private String queryId;
+ private String reference = null;
+ private Account account;
+ private Conversation conversation;
+ private PagingOrder pagingOrder = PagingOrder.NORMAL;
+ private XmppConnectionService.OnMoreMessagesLoaded callback = null;
+
+
+ public Query(Conversation conversation, long start, long end) {
+ this(conversation.getAccount(), start, end);
+ this.conversation = conversation;
+ }
+
+ public Query(Conversation conversation, long start, long end, PagingOrder order) {
+ this(conversation, start, end);
+ this.pagingOrder = order;
+ }
+
+ public Query(Account account, long start, long end) {
+ this.account = account;
+ this.start = start;
+ this.end = end;
+ this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
+ }
+
+ private Query page(String reference) {
+ Query query = new Query(this.account, this.start, this.end);
+ query.reference = reference;
+ query.conversation = conversation;
+ query.totalCount = totalCount;
+ query.callback = callback;
+ return query;
+ }
+
+ public Query next(String reference) {
+ Query query = page(reference);
+ query.pagingOrder = PagingOrder.NORMAL;
+ return query;
+ }
+
+ public Query prev(String reference) {
+ Query query = page(reference);
+ query.pagingOrder = PagingOrder.REVERSE;
+ return query;
+ }
+
+ public String getReference() {
+ return reference;
+ }
+
+ public PagingOrder getPagingOrder() {
+ return this.pagingOrder;
+ }
+
+ public String getQueryId() {
+ return queryId;
+ }
+
+ public Jid getWith() {
+ return conversation == null ? null : conversation.getJid().toBareJid();
+ }
+
+ public boolean muc() {
+ return conversation != null && conversation.getMode() == Conversation.MODE_MULTI;
+ }
+
+ public long getStart() {
+ return start;
+ }
+
+ public void setCallback(XmppConnectionService.OnMoreMessagesLoaded callback) {
+ this.callback = callback;
+ }
+
+ public void callback(boolean done) {
+ if (this.callback != null) {
+ this.callback.onMoreMessagesLoaded(messageCount, conversation);
+ if (done) {
+ this.callback.informUser(R.string.no_more_history_on_server);
+ }
+ }
+ }
+
+ public long getEnd() {
+ return end;
+ }
+
+ public Conversation getConversation() {
+ return conversation;
+ }
+
+ public Account getAccount() {
+ return this.account;
+ }
+
+ public void incrementMessageCount() {
+ this.messageCount++;
+ this.totalCount++;
+ }
+
+ public int getTotalCount() {
+ return this.totalCount;
+ }
+
+ public int getMessageCount() {
+ return this.messageCount;
+ }
+
+ public boolean validFrom(Jid from) {
+ if (muc()) {
+ return getWith().equals(from);
+ } else {
+ return (from == null) || account.getJid().toBareJid().equals(from.toBareJid());
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ if (this.muc()) {
+ builder.append("to=");
+ builder.append(this.getWith().toString());
+ } else {
+ builder.append("with=");
+ if (this.getWith() == null) {
+ builder.append("*");
+ } else {
+ builder.append(getWith().toString());
+ }
+ }
+ builder.append(", start=");
+ builder.append(AbstractGenerator.getTimestamp(this.start));
+ builder.append(", end=");
+ builder.append(AbstractGenerator.getTimestamp(this.end));
+ if (this.reference != null) {
+ if (this.pagingOrder == PagingOrder.NORMAL) {
+ builder.append(", after=");
+ } else {
+ builder.append(", before=");
+ }
+ builder.append(this.reference);
+ }
+ return builder.toString();
+ }
+
+ public boolean hasCallback() {
+ return this.callback != null;
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/services/NotificationService.java b/src/main/java/de/pixart/messenger/services/NotificationService.java
index a774f7641..8aa8541e9 100644
--- a/src/main/java/de/pixart/messenger/services/NotificationService.java
+++ b/src/main/java/de/pixart/messenger/services/NotificationService.java
@@ -383,7 +383,7 @@ public class NotificationService {
builder.setStyle(messagingStyle);
} else {
builder.setStyle(new NotificationCompat.BigTextStyle().bigText(getMergedBodies(messages)));
- builder.setContentText(UIHelper.getMessagePreview(mXmppConnectionService, messages.get((messages.size()-1))).first);
+ builder.setContentText(UIHelper.getMessagePreview(mXmppConnectionService, messages.get((messages.size() - 1))).first);
}
}
@@ -485,8 +485,8 @@ public class NotificationService {
final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class);
intent.setAction(XmppConnectionService.ACTION_REPLY_TO_CONVERSATION);
intent.putExtra("uuid", conversation.getUuid());
- intent.putExtra("dismiss_notification",dismissAfterReply);
- int id = conversation.getUuid().hashCode() % (dismissAfterReply ? 402359 : 426583);
+ intent.putExtra("dismiss_notification", dismissAfterReply);
+ int id = conversation.getUuid().hashCode() % (dismissAfterReply ? 402359 : 426583);
return PendingIntent.getService(mXmppConnectionService, id, intent, 0);
}
@@ -635,7 +635,7 @@ public class NotificationService {
mBuilder.setSmallIcon(R.drawable.ic_warning_white_24dp);
mBuilder.setContentIntent(PendingIntent.getActivity(mXmppConnectionService,
145,
- new Intent(mXmppConnectionService,ManageAccountActivity.class),
+ new Intent(mXmppConnectionService, ManageAccountActivity.class),
PendingIntent.FLAG_UPDATE_CURRENT));
notificationManager.notify(ERROR_NOTIFICATION_ID, mBuilder.build());
}
diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
index e41943597..96b98fbb3 100644
--- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
+++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
@@ -141,3786 +141,3787 @@ import me.leolin.shortcutbadger.ShortcutBadger;
public class XmppConnectionService extends Service {
- public static final String ACTION_REPLY_TO_CONVERSATION = "reply_to_conversations";
- public static final String ACTION_CLEAR_NOTIFICATION = "clear_notification";
- public static final String ACTION_DISABLE_FOREGROUND = "disable_foreground";
- public static final String ACTION_TRY_AGAIN = "try_again";
- public static final String ACTION_DISMISS_ERROR_NOTIFICATIONS = "dismiss_error";
- public static final String ACTION_IDLE_PING = "idle_ping";
- 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 ReplacingSerialSingleThreadExecutor mContactMergerExecutor = new ReplacingSerialSingleThreadExecutor(true);
- private final IBinder mBinder = new XmppConnectionBinder();
- private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
- private final IqGenerator mIqGenerator = new IqGenerator(this);
- private final List<String> mInProgressAvatarFetches = new ArrayList<>();
- private final HashSet<Jid> mLowPingTimeoutMode = new HashSet<>();
- private WakeLock wakeLock;
- private long mLastActivity = 0;
- public static VideoCompressor CompressVideo;
- private NotificationManager mNotifyManager;
-
- public DatabaseBackend databaseBackend;
- private ContentObserver contactObserver = new ContentObserver(null) {
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- Intent intent = new Intent(getApplicationContext(),
- XmppConnectionService.class);
- intent.setAction(ACTION_MERGE_PHONE_CONTACTS);
- startService(intent);
- }
- };
- private FileBackend fileBackend = new FileBackend(this);
- private MemorizingTrustManager mMemorizingTrustManager;
- private NotificationService mNotificationService = new NotificationService(
- this);
- private OnMessagePacketReceived mMessageParser = new MessageParser(this);
- private OnPresencePacketReceived mPresenceParser = new PresenceParser(this);
- private IqParser mIqParser = new IqParser(this);
- private OnIqPacketReceived mDefaultIqHandler = new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() != IqPacket.TYPE.RESULT) {
- 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);
- }
- }
- }
- };
- private MessageGenerator mMessageGenerator = new MessageGenerator(this);
- private PresenceGenerator mPresenceGenerator = new PresenceGenerator(this);
- private List<Account> accounts;
- private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(
- this);
- public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() {
-
- @Override
- public void onContactStatusChanged(Contact contact, boolean online) {
- Conversation conversation = find(getConversations(), contact);
- if (conversation != null) {
- if (online) {
- conversation.endOtrIfNeeded();
- if (contact.getPresences().size() == 1) {
- sendUnsentMessages(conversation);
- }
- } else {
- //check if the resource we are haveing a conversation with is still online
- if (conversation.hasValidOtrSession()) {
- String otrResource = conversation.getOtrSession().getSessionID().getUserID();
- if (!(Arrays.asList(contact.getPresences().toResourceArray()).contains(otrResource))) {
- conversation.endOtrIfNeeded();
- }
- }
- }
- }
- }
- };
- 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 ConversationsFileObserver fileObserver = new ConversationsFileObserver(
- Environment.getExternalStorageDirectory().getAbsolutePath()
- ) {
- @Override
- public void onEvent(int event, String path) {
- markFileDeleted(path);
- }
- };
- private final OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() {
-
- @Override
- public void onJinglePacketReceived(Account account, JinglePacket packet) {
- mJingleConnectionManager.deliverPacket(account, packet);
- }
- };
- private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() {
-
- @Override
- public void onMessageAcknowledged(Account account, String uuid) {
- for (final Conversation conversation : getConversations()) {
- if (conversation.getAccount() == account) {
- Message message = conversation.findUnsentMessageWithUuid(uuid);
- if (message != null) {
- markMessage(message, Message.STATUS_SEND);
- }
- }
- }
- }
- };
- private int convChangedListenerCount = 0;
- private OnShowErrorToast mOnShowErrorToast = null;
- private int showErrorToastListenerCount = 0;
- private int unreadCount = -1;
- private OnAccountUpdate mOnAccountUpdate = null;
- private OnCaptchaRequested mOnCaptchaRequested = null;
- private int accountChangedListenerCount = 0;
- private int captchaRequestedListenerCount = 0;
- private OnRosterUpdate mOnRosterUpdate = null;
- private OnUpdateBlocklist mOnUpdateBlocklist = null;
- private int updateBlocklistListenerCount = 0;
- private int rosterChangedListenerCount = 0;
- private OnMucRosterUpdate mOnMucRosterUpdate = null;
- private int mucRosterChangedListenerCount = 0;
- private OnKeyStatusUpdated mOnKeyStatusUpdated = null;
- private int keyStatusUpdatedListenerCount = 0;
- private SecureRandom mRandom;
- private LruCache<Pair<String,String>,ServiceDiscoveryResult> discoCache = new LruCache<>(20);
- private final OnBindListener mOnBindListener = new OnBindListener() {
-
- @Override
- public void onBind(final Account account) {
- synchronized (mInProgressAvatarFetches) {
- for (Iterator<String> iterator = mInProgressAvatarFetches.iterator(); iterator.hasNext(); ) {
- final String KEY = iterator.next();
- if (KEY.startsWith(account.getJid().toBareJid() + "_")) {
- iterator.remove();
- }
- }
- }
- account.getRoster().clearPresences();
- mJingleConnectionManager.cancelInTransmission();
- fetchRosterFromServer(account);
- fetchBookmarks(account);
- sendPresence(account);
- if (mPushManagementService.available(account)) {
- mPushManagementService.registerPushTokenOnServer(account);
- }
- connectMultiModeConversations(account);
- syncDirtyContacts(account);
- }
- };
- private OnStatusChanged statusListener = new OnStatusChanged() {
-
- @Override
- public void onStatusChanged(final Account account) {
- XmppConnection connection = account.getXmppConnection();
- if (mOnAccountUpdate != null) {
- mOnAccountUpdate.onAccountUpdate();
- }
- if (account.getStatus() == Account.State.ONLINE) {
- synchronized (mLowPingTimeoutMode) {
- if (mLowPingTimeoutMode.remove(account.getJid().toBareJid())) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": leaving low ping timeout mode");
- }
- }
- if (account.setShowErrorNotification(true)) {
- databaseBackend.updateAccount(account);
- }
- mMessageArchiveService.executePendingQueries(account);
- if (connection != null && connection.getFeatures().csi()) {
- if (checkListeners()) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//inactive");
- connection.sendInactive();
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//active");
- connection.sendActive();
- }
- }
- List<Conversation> conversations = getConversations();
- for (Conversation conversation : conversations) {
- 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");
- }
- sendUnsentMessages(conversation);
- }
- }
- for (Conversation conversation : account.pendingConferenceLeaves) {
- leaveMuc(conversation);
- }
- account.pendingConferenceLeaves.clear();
- for (Conversation conversation : account.pendingConferenceJoins) {
- joinMuc(conversation);
- }
- account.pendingConferenceJoins.clear();
- scheduleWakeUpCall(Config.PING_MAX_INTERVAL, account.getUuid().hashCode());
- } else if (account.getStatus() == Account.State.OFFLINE || account.getStatus() == Account.State.DISABLED) {
- resetSendingToWaiting(account);
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- synchronized (mLowPingTimeoutMode) {
- if (mLowPingTimeoutMode.contains(account.getJid().toBareJid())) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": went into offline state during low ping mode. reconnecting now");
- reconnectAccount(account, true, false);
- } else {
- int timeToReconnect = mRandom.nextInt(20) + 10;
- scheduleWakeUpCall(timeToReconnect, account.getUuid().hashCode());
- }
- }
- }
- } else if (account.getStatus() == Account.State.REGISTRATION_SUCCESSFUL) {
- databaseBackend.updateAccount(account);
- reconnectAccount(account, true, false);
- } else if ((account.getStatus() != Account.State.CONNECTING)
- && (account.getStatus() != Account.State.NO_INTERNET)) {
- resetSendingToWaiting(account);
- if (connection != null) {
- int next = connection.getTimeToNextAttempt();
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
- + ": error connecting account. try again in "
- + next + "s for the "
- + (connection.getAttempt() + 1) + " time");
- scheduleWakeUpCall(next, account.getUuid().hashCode());
- }
- }
- getNotificationService().updateErrorNotification();
- }
- };
- private OpenPgpServiceConnection pgpServiceConnection;
- private PgpEngine mPgpEngine = null;
- private PowerManager pm;
- private LruCache<String, Bitmap> mBitmapCache;
- private EventReceiver mEventReceiver = new EventReceiver();
-
- 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;
- }
-
- public PgpEngine getPgpEngine() {
- if (!Config.supportOpenPgp()) {
- return null;
- } else if (pgpServiceConnection != null && pgpServiceConnection.isBound()) {
- if (this.mPgpEngine == null) {
- this.mPgpEngine = new PgpEngine(new OpenPgpApi(
- getApplicationContext(),
- pgpServiceConnection.getService()), this);
- }
- return mPgpEngine;
- } else {
- return null;
- }
-
- }
-
- public OpenPgpApi getOpenPgpApi() {
- if (!Config.supportOpenPgp()) {
- return null;
- } else if (pgpServiceConnection != null && pgpServiceConnection.isBound()) {
- return new OpenPgpApi(this, pgpServiceConnection.getService());
- } 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);
- final String path = getFileBackend().getOriginalPath(uri);
- Log.d(Config.LOGTAG,"File path = " + path);
- mFileAddingExecutor.execute(new Runnable() {
- @Override
- public void run() {
- if (path != null) {
- message.setRelativeFilePath(path);
- getFileBackend().updateFileParams(message);
- if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- getPgpEngine().encrypt(message, callback);
- } else {
- callback.success(message);
- }
- } else {
- try {
- getFileBackend().copyFileToPrivateStorage(message, uri);
- getFileBackend().updateFileParams(message);
- if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- final PgpEngine pgpEngine = getPgpEngine();
- if (pgpEngine != null) {
- pgpEngine.encrypt(message, callback);
- } else if (callback != null) {
- callback.error(R.string.unable_to_connect_to_keychain, null);
- }
- } 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) {
- final PgpEngine pgpEngine = getPgpEngine();
- if (pgpEngine != null) {
- pgpEngine.encrypt(message, callback);
- } else if (callback != null){
- callback.error(R.string.unable_to_connect_to_keychain, null);
- }
- } else {
- callback.success(message);
- }
- } catch (final FileBackend.FileCopyException e) {
- callback.error(e.getResId(), message);
- }
- }
- });
- }
-
- public void attachVideoToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
- File f = new File(FileUtils.getPath(this, uri));
- long filesize = f.length();
- String path = f.toString();
- final String compressVideos = getCompressVideoPreference();
- boolean sendVideoAsIs = false;
- final Integer NOTIFICATION_ID = (int) (new Date().getTime()/1000);
- mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext());
- mBuilder.setContentTitle(getString(R.string.app_name))
- .setContentText(getString(R.string.compressing_video))
- .setSmallIcon(R.drawable.ic_play_box_outline_white_24dp)
- .setOngoing(true)
- .setProgress(0, 0, true);
- mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
- if (FileBackend.weOwnFile(this, uri)) {
- Log.d(Config.LOGTAG,"trying to attach video that belonged to us");
- mNotifyManager.cancel(NOTIFICATION_ID);
- callback.error(R.string.security_error_invalid_file_access, null);
- return;
- }
- Log.d(Config.LOGTAG,"Video file (size) :" + f.toString() + "("+filesize/1024/1024+"MB)");
- if (filesize == 0) {
- Log.d(Config.LOGTAG,"Error with file, size = 0");
- mNotifyManager.cancel(NOTIFICATION_ID);
- callback.error(R.string.error_file_corrupt, null);
- return;
- }
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
- File compressed_file = new File(FileBackend.getConversationsVideoDirectory() + "/"
- + dateFormat.format(new Date())
- + "_komp.mp4");
- final String compressed_path = compressed_file.toString();
- final Uri compressed_uri = Uri.fromFile(compressed_file);
- if ("never".equals(compressVideos)) {
- sendVideoAsIs = true;
- } else if ("always".equals(compressVideos) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- sendVideoAsIs = false;
- } else if ("auto".equals(compressVideos) && filesize > Config.VIDEO_MAX_SIZE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- sendVideoAsIs = false;
- } else {
- sendVideoAsIs = true;
- }
- if (!sendVideoAsIs){
- CompressVideo = new VideoCompressor(path, compressed_path, new Interface() {
- @Override
- public void videocompressed(boolean result) {
- if (result) {
- Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": sending compressed video.");
- mNotifyManager.cancel(NOTIFICATION_ID);
- attachFileToConversation(conversation, compressed_uri, callback);
- }
- }
- });
- CompressVideo.execute();
- } else {
- Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": not compressing video. sending as file");
- mNotifyManager.cancel(NOTIFICATION_ID);
- attachFileToConversation(conversation, uri, callback);
- }
- }
-
- public interface Interface {
- void videocompressed(boolean result);
- }
-
- class VideoCompressor extends AsyncTask<String, Void, Boolean> {
- private String originalpath;
- private String compressedpath;
- private Interface mListener;
-
- VideoCompressor(String path, String compressed_path, Interface mListener) {
- originalpath = path;
- compressedpath = compressed_path;
- this.mListener = mListener;
- }
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- Log.d(Config.LOGTAG,"Start video compression");
- wakeLock.acquire();
- }
-
- @Override
- protected Boolean doInBackground(String... params) {
- return MediaController.getInstance().convertVideo(originalpath, compressedpath);
- }
-
- @Override
- protected void onPostExecute(Boolean compressed) {
- super.onPostExecute(compressed);
- wakeLock.release();
- File video = new File(compressedpath);
- if (mListener != null) {
- if (video.exists() && video.length() > 0) {
- mListener.videocompressed(compressed);
- Log.d(Config.LOGTAG, "Compression successfully!");
- } else {
- mListener.videocompressed(false);
- Log.d(Config.LOGTAG, "Compression failed!");
- }
- }
- }
- }
-
- public Conversation find(Bookmark bookmark) {
- return find(bookmark.getAccount(), bookmark.getJid());
- }
-
- public Conversation find(final Account account, final Jid jid) {
- return find(getConversations(), account, jid);
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- final String action = intent == null ? null : intent.getAction();
- String pushedAccountHash = null;
- boolean interactive = false;
- if (action != null) {
- final Conversation c = findConversationByUuid(intent.getStringExtra("uuid"));
- switch (action) {
- case ConnectivityManager.CONNECTIVITY_ACTION:
- if (hasInternetConnection() && Config.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE) {
- resetAllAttemptCounts(true);
- }
- break;
- case ACTION_MERGE_PHONE_CONTACTS:
- if (mRestoredFromDatabase) {
- loadPhoneContacts();
- }
- return START_STICKY;
- case Intent.ACTION_SHUTDOWN:
- logoutAndSave(true);
- return START_NOT_STICKY;
- case ACTION_CLEAR_NOTIFICATION:
- if (c != null) {
- mNotificationService.clear(c);
- } else {
- mNotificationService.clear();
- }
- break;
- case ACTION_DISMISS_ERROR_NOTIFICATIONS:
- dismissErrorNotifications();
- break;
- case ACTION_TRY_AGAIN:
- resetAllAttemptCounts(false);
- interactive = true;
- break;
- case ACTION_REPLY_TO_CONVERSATION:
- Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
- if (remoteInput != null && c != null) {
- final CharSequence body = remoteInput.getCharSequence("text_reply");
- if (body != null && body.length() > 0) {
- directReply(c, body.toString(),intent.getBooleanExtra("dismiss_notification",false));
- }
- }
- break;
- case AudioManager.RINGER_MODE_CHANGED_ACTION:
- if (xaOnSilentMode()) {
- refreshAllPresences();
- }
- break;
- case Intent.ACTION_SCREEN_ON:
- deactivateGracePeriod();
- case Intent.ACTION_SCREEN_OFF:
- if (awayWhenScreenOff()) {
- refreshAllPresences();
- }
- break;
- case ACTION_GCM_TOKEN_REFRESH:
- refreshAllGcmTokens();
- break;
- case ACTION_IDLE_PING:
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- scheduleNextIdlePing();
- }
- break;
- case ACTION_GCM_MESSAGE_RECEIVED:
- Log.d(Config.LOGTAG,"gcm push message arrived in service. extras="+intent.getExtras());
- pushedAccountHash = intent.getStringExtra("account");
- break;
- }
- }
- this.wakeLock.acquire();
-
- boolean pingNow = false;
- HashSet<Account> pingCandidates = new HashSet<>();
-
- for (Account account : accounts) {
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- if (!hasInternetConnection()) {
- account.setStatus(Account.State.NO_INTERNET);
- if (statusListener != null) {
- statusListener.onStatusChanged(account);
- }
- } else {
- if (account.getStatus() == Account.State.NO_INTERNET) {
- account.setStatus(Account.State.OFFLINE);
- if (statusListener != null) {
- statusListener.onStatusChanged(account);
- }
- }
- if (account.getStatus() == Account.State.ONLINE) {
- synchronized (mLowPingTimeoutMode) {
- long lastReceived = account.getXmppConnection().getLastPacketReceived();
- long lastSent = account.getXmppConnection().getLastPingSent();
- long pingInterval = "ui".equals(action) ? Config.PING_MIN_INTERVAL * 1000 : Config.PING_MAX_INTERVAL * 1000;
- long msToNextPing = (Math.max(lastReceived, lastSent) + pingInterval) - SystemClock.elapsedRealtime();
- int pingTimeout = mLowPingTimeoutMode.contains(account.getJid().toBareJid()) ? Config.LOW_PING_TIMEOUT * 1000 : Config.PING_TIMEOUT * 1000;
- long pingTimeoutIn = (lastSent + pingTimeout) - SystemClock.elapsedRealtime();
- if (lastSent > lastReceived) {
- if (pingTimeoutIn < 0) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": ping timeout");
- this.reconnectAccount(account, true, interactive);
- } else {
- int secs = (int) (pingTimeoutIn / 1000);
- this.scheduleWakeUpCall(secs, account.getUuid().hashCode());
- }
- } else {
- pingCandidates.add(account);
- if (CryptoHelper.getAccountFingerprint(account).equals(pushedAccountHash)) {
- pingNow = true;
- if (mLowPingTimeoutMode.add(account.getJid().toBareJid())) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": entering low ping timeout mode");
- }
- } else if (msToNextPing <= 0) {
- pingNow = true;
- } else {
- this.scheduleWakeUpCall((int) (msToNextPing / 1000), account.getUuid().hashCode());
- if (mLowPingTimeoutMode.remove(account.getJid().toBareJid())) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": leaving low ping timeout mode");
- }
- }
- }
- }
- } else if (account.getStatus() == Account.State.OFFLINE) {
- reconnectAccount(account, true, interactive);
- } else if (account.getStatus() == Account.State.CONNECTING) {
- long secondsSinceLastConnect = (SystemClock.elapsedRealtime() - account.getXmppConnection().getLastConnect()) / 1000;
- long secondsSinceLastDisco = (SystemClock.elapsedRealtime() - account.getXmppConnection().getLastDiscoStarted()) / 1000;
- 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");
- account.getXmppConnection().resetAttemptCount();
- reconnectAccount(account, true, interactive);
- } else if (discoTimeout < 0) {
- account.getXmppConnection().sendDiscoTimeout();
- scheduleWakeUpCall((int) Math.min(timeout,discoTimeout), account.getUuid().hashCode());
- } else {
- scheduleWakeUpCall((int) Math.min(timeout,discoTimeout), account.getUuid().hashCode());
- }
- } else {
- if (account.getXmppConnection().getTimeToNextAttempt() <= 0) {
- reconnectAccount(account, true, interactive);
- }
- }
- }
- if (mOnAccountUpdate != null) {
- mOnAccountUpdate.onAccountUpdate();
- }
- }
- }
- if (pingNow) {
- for (Account account : pingCandidates) {
- synchronized (mLowPingTimeoutMode) {
- final boolean lowTimeout = mLowPingTimeoutMode.contains(account.getJid().toBareJid());
- account.getXmppConnection().sendPing();
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + " send ping (action=" + action + ",lowTimeout=" + Boolean.toString(lowTimeout) + ")");
- scheduleWakeUpCall(lowTimeout ? Config.LOW_PING_TIMEOUT : Config.PING_TIMEOUT, account.getUuid().hashCode());
- }
- }
- }
- if (wakeLock.isHeld()) {
- try {
- wakeLock.release();
- } catch (final RuntimeException ignored) {
- }
- }
- return START_STICKY;
- }
-
- public boolean isDataSaverDisabled() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
- return !connectivityManager.isActiveNetworkMetered()
- || connectivityManager.getRestrictBackgroundStatus() == ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
- } else {
- return true;
- }
- }
-
- private void directReply(Conversation conversation, String body, final boolean dismissAfterReply) {
- Message message = new Message(conversation,body,conversation.getNextEncryption());
- message.markUnread();
- if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- getPgpEngine().encrypt(message, new UiCallback<Message>() {
- @Override
- public void success(Message message) {
- message.setEncryption(Message.ENCRYPTION_DECRYPTED);
- sendMessage(message);
- if (dismissAfterReply) {
- markRead(message.getConversation(),true);
- } else {
- mNotificationService.pushFromDirectReply(message);
- }
- }
-
- @Override
- public void error(int errorCode, Message object) {
-
- }
-
- @Override
- public void userInputRequried(PendingIntent pi, Message object) {
-
- }
- });
- } else {
- sendMessage(message);
- if (dismissAfterReply) {
- markRead(conversation,true);
- } else {
- mNotificationService.pushFromDirectReply(message);
- }
- }
- }
-
- 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 String getCompressVideoPreference() {
- return getPreferences().getString("video_compression", "auto");
- }
-
- private Presence.Status getTargetPresence() {
- if (xaOnSilentMode() && isPhoneSilenced()) {
- return Presence.Status.XA;
- } else if (awayWhenScreenOff() && !isInteractive()) {
- return Presence.Status.AWAY;
- } else {
- return Presence.Status.ONLINE;
- }
- }
-
- @SuppressLint("NewApi")
- @SuppressWarnings("deprecation")
- public boolean isInteractive() {
- final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
-
- final boolean isScreenOn;
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
- isScreenOn = pm.isScreenOn();
- } else {
- isScreenOn = pm.isInteractive();
- }
- return isScreenOn;
- }
-
- private boolean isPhoneSilenced() {
- AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- try {
- if (treatVibrateAsSilent()) {
- return audioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
- } else {
- return audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT;
- }
- } catch (Throwable throwable) {
- Log.d(Config.LOGTAG,"platform bug in isPhoneSilenced ("+ throwable.getMessage()+")");
- return false;
- }
- }
-
- private void resetAllAttemptCounts(boolean reallyAll) {
- Log.d(Config.LOGTAG, "resetting all attempt counts");
- for (Account account : accounts) {
- if (account.hasErrorStatus() || reallyAll) {
- final XmppConnection connection = account.getXmppConnection();
- if (connection != null) {
- connection.resetAttemptCount();
- }
- }
- if (account.setShowErrorNotification(true)) {
- databaseBackend.updateAccount(account);
- }
- }
- mNotificationService.updateErrorNotification();
- }
-
- private void dismissErrorNotifications() {
- for (final Account account : this.accounts) {
- if (account.hasErrorStatus()) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": dismissing error notification");
- if (account.setShowErrorNotification(false)) {
- databaseBackend.updateAccount(account);
- }
- }
- }
- }
-
- public boolean hasInternetConnection() {
- ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
- return activeNetwork != null && activeNetwork.isConnected();
- }
-
- public boolean isWIFI() {
- ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
- if (activeNetwork != null) { // connected to the internet
- if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
- return true;
- }
- }
- return false;
- }
-
- public boolean isMobile() {
- ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
- if (activeNetwork != null) { // connected to the internet
- if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
- if (!activeNetwork.isRoaming()) {
- return true;
- }
- }
- }
- return false;
- }
-
- public boolean isMobileRoaming() {
- ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
- if (activeNetwork != null) { // connected to the internet
- if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
- if (activeNetwork.isRoaming()) {
- return true;
- }
- }
- }
- return false;
- }
-
- @SuppressLint("TrulyRandom")
- @Override
- public void onCreate() {
- ExceptionHelper.init(getApplicationContext());
- PRNGFixes.apply();
- this.mRandom = new SecureRandom();
- updateMemorizingTrustmanager();
- final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
- final int cacheSize = maxMemory / 8;
- this.mBitmapCache = new LruCache<String, Bitmap>(cacheSize) {
- @Override
- protected int sizeOf(final String key, final Bitmap bitmap) {
- return bitmap.getByteCount() / 1024;
- }
- };
-
- this.databaseBackend = DatabaseBackend.getInstance(getApplicationContext());
- this.accounts = databaseBackend.getAccounts();
-
- if (databaseBackend.startTimeCountExceedsThreshold()) {
- Log.d(Config.LOGTAG,"number of restarts exceeds threshold.");
- }
-
- restoreFromDatabase();
-
- getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contactObserver);
- new Thread(new Runnable() {
- @Override
- public void run() {
- fileObserver.startWatching();
- }
- }).start();
- if (Config.supportOpenPgp()) {
- this.pgpServiceConnection = new OpenPgpServiceConnection(getApplicationContext(), "org.sufficientlysecure.keychain", new OpenPgpServiceConnection.OnBound() {
- @Override
- public void onBound(IOpenPgpService2 service) {
- for (Account account : accounts) {
- final PgpDecryptionService pgp = account.getPgpDecryptionService();
- if(pgp != null) {
- pgp.continueDecryption(true);
- }
- }
- }
-
- @Override
- public void onError(Exception e) {
- }
- });
- this.pgpServiceConnection.bindToService();
- }
-
- this.pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
- this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XmppConnectionService");
- toggleForegroundService();
- updateUnreadCountBadge();
- toggleScreenEventReceiver();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- scheduleNextIdlePing();
- }
- //start export log service every day at given time
- ScheduleAutomaticExport();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- registerReceiver(this.mEventReceiver,new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
- }
- }
-
- @Override
- public void onTrimMemory(int level) {
- super.onTrimMemory(level);
- if (level >= TRIM_MEMORY_COMPLETE) {
- Log.d(Config.LOGTAG, "clear cache due to low memory");
- getBitmapCache().evictAll();
- }
- }
-
- @Override
- public void onDestroy() {
- try {
- unregisterReceiver(this.mEventReceiver);
- } catch (IllegalArgumentException e) {
- //ignored
- }
- fileObserver.stopWatching();
- super.onDestroy();
- // cancel scheduled exporter
- CancelAutomaticExport();
- }
-
- public void toggleScreenEventReceiver() {
- if (awayWhenScreenOff() && !manuallyChangePresence()) {
- final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- registerReceiver(this.mEventReceiver, filter);
- } else {
- try {
- unregisterReceiver(this.mEventReceiver);
- } catch (IllegalArgumentException e) {
- //ignored
- }
- }
- }
-
- public void toggleForegroundService() {
- if (Config.USE_ALWAYS_FOREGROUND) {
- startForeground(NotificationService.FOREGROUND_NOTIFICATION_ID, this.mNotificationService.createForegroundNotification());
- } else {
- stopForeground(true);
- }
- }
-
- @Override
- public void onTaskRemoved(final Intent rootIntent) {
- super.onTaskRemoved(rootIntent);
- }
-
- private void logoutAndSave(boolean stop) {
- int activeAccounts = 0;
- databaseBackend.clearStartTimeCounter(); // regular swipes don't count towards restart counter
- for (final Account account : accounts) {
- if (account.getStatus() != Account.State.DISABLED) {
- activeAccounts++;
- }
- databaseBackend.writeRoster(account.getRoster());
- if (account.getXmppConnection() != null) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- disconnect(account, false);
- }
- }).start();
- }
- }
- if (stop || activeAccounts == 0) {
- Log.d(Config.LOGTAG, "good bye");
- stopSelf();
- }
- }
-
- public void scheduleWakeUpCall(int seconds, int requestCode) {
- final long timeToWake = SystemClock.elapsedRealtime() + (seconds < 0 ? 1 : seconds + 1) * 1000;
- AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- Intent intent = new Intent(this, EventReceiver.class);
- intent.setAction("ping");
- PendingIntent alarmIntent = PendingIntent.getBroadcast(this, requestCode, intent, 0);
- alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, timeToWake, alarmIntent);
- }
-
- @TargetApi(Build.VERSION_CODES.M)
- private void scheduleNextIdlePing() {
- Log.d(Config.LOGTAG,"schedule next idle ping");
- AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- Intent intent = new Intent(this, EventReceiver.class);
- intent.setAction(ACTION_IDLE_PING);
- alarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime()+(Config.IDLE_PING_INTERVAL * 1000),
- PendingIntent.getBroadcast(this,0,intent,0)
- );
- }
-
- public XmppConnection createConnection(final Account account) {
- final SharedPreferences sharedPref = getPreferences();
- String resource;
- try {
- resource = sharedPref.getString("resource", getString(R.string.default_resource)).toLowerCase(Locale.ENGLISH);
- if (resource.trim().isEmpty()) {
- throw new Exception();
- }
- } catch (Exception e) {
- resource = "Pix-Art Messenger";
- }
- account.setResource(resource);
- final XmppConnection connection = new XmppConnection(account, this);
- connection.setOnMessagePacketReceivedListener(this.mMessageParser);
- connection.setOnStatusChangedListener(this.statusListener);
- connection.setOnPresencePacketReceivedListener(this.mPresenceParser);
- connection.setOnUnregisteredIqPacketReceivedListener(this.mIqParser);
- connection.setOnJinglePacketReceivedListener(this.jingleListener);
- connection.setOnBindListener(this.mOnBindListener);
- connection.setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener);
- connection.addOnAdvancedStreamFeaturesAvailableListener(this.mMessageArchiveService);
- connection.addOnAdvancedStreamFeaturesAvailableListener(this.mAvatarService);
- AxolotlService axolotlService = account.getAxolotlService();
- if (axolotlService != null) {
- connection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
- }
- return connection;
- }
-
- public void sendChatState(Conversation conversation) {
- if (sendChatStates()) {
- 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");
- final Account account = message.getConversation().getAccount();
- if (account.httpUploadAvailable(fileBackend.getFile(message,false).getSize())) {
- mHttpConnectionManager.createNewUploadConnection(message, delay);
- } else {
- mJingleConnectionManager.createNewConnection(message);
- }
- }
-
- public void sendMessage(final Message message) {
- sendMessage(message, false, false);
- }
-
- private void sendMessage(final Message message, final boolean resend, final boolean delay) {
- final Account account = message.getConversation().getAccount();
- if (account.setShowErrorNotification(true)) {
- databaseBackend.updateAccount(account);
- mNotificationService.updateErrorNotification();
- }
- final Conversation conversation = message.getConversation();
- account.deactivateGracePeriod();
- MessagePacket packet = null;
- final boolean addToConversation = (conversation.getMode() != Conversation.MODE_MULTI
- || account.getServerIdentity() != XmppConnection.Identity.SLACK)
- && !message.edited();
- boolean saveInDb = addToConversation;
- message.setStatus(Message.STATUS_WAITING);
-
- if (!resend && message.getEncryption() != Message.ENCRYPTION_OTR) {
- message.getConversation().endOtrIfNeeded();
- message.getConversation().findUnsentMessagesWithEncryption(Message.ENCRYPTION_OTR,
- new Conversation.OnMessageFound() {
- @Override
- public void onMessageFound(Message message) {
- markMessage(message, Message.STATUS_SEND_FAILED);
- }
- });
- }
-
- if (account.isOnlineAndConnected()) {
- switch (message.getEncryption()) {
- case Message.ENCRYPTION_NONE:
- if (message.needsUploading()) {
- if (account.httpUploadAvailable(fileBackend.getFile(message,false).getSize())
- || message.fixCounterpart()) {
- this.sendFileMessage(message, delay);
- } else {
- break;
- }
- } else {
- packet = mMessageGenerator.generateChat(message);
- }
- break;
- case Message.ENCRYPTION_PGP:
- case Message.ENCRYPTION_DECRYPTED:
- if (message.needsUploading()) {
- if (account.httpUploadAvailable(fileBackend.getFile(message,false).getSize())
- || message.fixCounterpart()) {
- this.sendFileMessage(message, delay);
- } else {
- break;
- }
- } else {
- packet = mMessageGenerator.generatePgpChat(message);
- }
- break;
- case Message.ENCRYPTION_OTR:
- SessionImpl otrSession = conversation.getOtrSession();
- if (otrSession != null && otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) {
- try {
- message.setCounterpart(Jid.fromSessionID(otrSession.getSessionID()));
- } catch (InvalidJidException e) {
- break;
- }
- if (message.needsUploading()) {
- mJingleConnectionManager.createNewConnection(message);
- } else {
- packet = mMessageGenerator.generateOtrChat(message);
- }
- } else if (otrSession == null) {
- 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());
- break;
- }
- } else {
- Log.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())
- || message.fixCounterpart()) {
- this.sendFileMessage(message, delay);
- } else {
- break;
- }
- } else {
- XmppAxolotlMessage axolotlMessage = account.getAxolotlService().fetchAxolotlMessageFromCache(message);
- if (axolotlMessage == null) {
- account.getAxolotlService().preparePayloadMessage(message, delay);
- } else {
- packet = mMessageGenerator.generateAxolotlChat(message, axolotlMessage);
- }
- }
- break;
-
- }
- if (packet != null) {
- if (account.getXmppConnection().getFeatures().sm() || (conversation.getMode() == Conversation.MODE_MULTI && message.getCounterpart().isBareJid())) {
- message.setStatus(Message.STATUS_UNSEND);
- } else {
- message.setStatus(Message.STATUS_SEND);
- }
- }
- } else {
- switch (message.getEncryption()) {
- case Message.ENCRYPTION_DECRYPTED:
- if (!message.needsUploading()) {
- String pgpBody = message.getEncryptedBody();
- String decryptedBody = message.getBody();
- message.setBody(pgpBody);
- message.setEncryption(Message.ENCRYPTION_PGP);
- if (message.edited()) {
- message.setBody(decryptedBody);
- message.setEncryption(Message.ENCRYPTION_DECRYPTED);
- databaseBackend.updateMessage(message, message.getEditedId());
- updateConversationUi();
- return;
- } else {
- databaseBackend.createMessage(message);
- saveInDb = false;
- message.setBody(decryptedBody);
- message.setEncryption(Message.ENCRYPTION_DECRYPTED);
- }
- }
- 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());
- conversation.startOtrSession(message.getCounterpart().getResourcepart(), false);
- }
- break;
- case Message.ENCRYPTION_AXOLOTL:
- message.setFingerprint(account.getAxolotlService().getOwnFingerprint());
- break;
- }
- }
-
- if (resend) {
- if (packet != null && addToConversation) {
- if (account.getXmppConnection().getFeatures().sm() || (conversation.getMode() == Conversation.MODE_MULTI && message.getCounterpart().isBareJid())) {
- markMessage(message, Message.STATUS_UNSEND);
- } else {
- markMessage(message, Message.STATUS_SEND);
- }
- }
- } else {
- if (addToConversation) {
- conversation.add(message);
- }
- if (message.getEncryption() == Message.ENCRYPTION_NONE || saveEncryptedMessages()) {
- if (saveInDb) {
- databaseBackend.createMessage(message);
- } else if (message.edited()) {
- databaseBackend.updateMessage(message, message.getEditedId());
- }
- }
- updateConversationUi();
- }
- if (packet != null) {
- if (delay) {
- mMessageGenerator.addDelay(packet, message.getTimeSent());
- }
- if (conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
- if (this.sendChatStates()) {
- packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
- }
- }
- sendMessagePacket(account, packet);
- }
- }
-
- private void sendUnsentMessages(final Conversation conversation) {
- conversation.findWaitingMessages(new Conversation.OnMessageFound() {
-
- @Override
- public void onMessageFound(Message message) {
- resendMessage(message, true);
- }
- });
- }
-
- public void resendMessage(final Message message, final boolean delay) {
- sendMessage(message, true, delay);
- }
-
- 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()
- + ": fetching roster version " + account.getRosterVersion());
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching roster");
- }
- iqPacket.query(Xmlns.ROSTER).setAttribute("ver", account.getRosterVersion());
- sendIqPacket(account, iqPacket, mIqParser);
- }
-
- public void fetchBookmarks(final Account account) {
- final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET);
- final Element query = iqPacket.query("jabber:iq:private");
- query.addChild("storage", "storage:bookmarks");
- final OnIqPacketReceived callback = new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- 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")) {
- final Bookmark bookmark = Bookmark.parse(item, account);
- Bookmark old = bookmarks.put(bookmark.getJid(), bookmark);
- if (old != null && old.getBookmarkName() != null && bookmark.getBookmarkName() == null) {
- bookmark.setBookmarkName(old.getBookmarkName());
- }
- Conversation conversation = find(bookmark);
- if (conversation != null) {
- conversation.setBookmark(bookmark);
- } else if (bookmark.autojoin() && bookmark.getJid() != null && autojoin) {
- conversation = findOrCreateConversation(
- account, bookmark.getJid(), true);
- conversation.setBookmark(bookmark);
- joinMuc(conversation);
- }
- }
- }
- }
- account.setBookmarks(new ArrayList<>(bookmarks.values()));
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not fetch bookmarks");
- }
- }
- };
- sendIqPacket(account, iqPacket, callback);
- }
-
- public void pushBookmarks(Account account) {
- Log.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");
- for (Bookmark bookmark : account.getBookmarks()) {
- storage.addChild(bookmark);
- }
- sendIqPacket(account, iqPacket, mDefaultIqHandler);
- }
-
- private void restoreFromDatabase() {
- synchronized (this.conversations) {
- final Map<String, Account> accountLookupTable = new Hashtable<>();
- for (Account account : this.accounts) {
- accountLookupTable.put(account.getUuid(), account);
- }
- this.conversations.addAll(databaseBackend.getConversations(Conversation.STATUS_AVAILABLE));
- for (Conversation conversation : this.conversations) {
- Account account = accountLookupTable.get(conversation.getAccountUuid());
- conversation.setAccount(account);
- }
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- Log.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();
- loadPhoneContacts();
- Log.d(Config.LOGTAG, "restoring messages");
- for (Conversation conversation : conversations) {
- conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
- checkDeletedFiles(conversation);
- conversation.findUnsentTextMessages(new Conversation.OnMessageFound() {
-
- @Override
- public void onMessageFound(Message message) {
- markMessage(message, Message.STATUS_WAITING);
- }
- });
- conversation.findUnreadMessages(new Conversation.OnMessageFound() {
- @Override
- public void onMessageFound(Message message) {
- mNotificationService.pushFromBacklog(message);
- }
- });
- }
- mNotificationService.finishBacklog(false);
- mRestoredFromDatabase = true;
- Log.d(Config.LOGTAG, "restored all messages");
- updateConversationUi();
- }
- };
- mDatabaseExecutor.execute(runnable);
- }
- }
-
- public void loadPhoneContacts() {
- mContactMergerExecutor.execute(new Runnable() {
- @Override
- public void run() {
- PhoneHelper.loadPhoneContacts(XmppConnectionService.this, new OnPhoneContactsLoadedListener() {
- @Override
- public void onPhoneContactsLoaded(List<Bundle> phoneContacts) {
- Log.d(Config.LOGTAG, "start merging phone contacts with roster");
- for (Account account : accounts) {
- List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts();
- for (Bundle phoneContact : phoneContacts) {
- Jid jid;
- try {
- jid = Jid.fromString(phoneContact.getString("jid"));
- } catch (final InvalidJidException e) {
- continue;
- }
- final Contact contact = account.getRoster().getContact(jid);
- String systemAccount = phoneContact.getInt("phoneid")
- + "#"
- + phoneContact.getString("lookup");
- contact.setSystemAccount(systemAccount);
- if (contact.setPhotoUri(phoneContact.getString("photouri"))) {
- getAvatarService().clear(contact);
- }
- contact.setSystemName(phoneContact.getString("displayname"));
- withSystemAccounts.remove(contact);
- }
- for (Contact contact : withSystemAccounts) {
- contact.setSystemAccount(null);
- contact.setSystemName(null);
- if (contact.setPhotoUri(null)) {
- getAvatarService().clear(contact);
- }
- }
- }
- Log.d(Config.LOGTAG, "finished merging phone contacts");
- updateAccountUi();
- }
- });
- }
- });
- }
-
- public List<Conversation> getConversations() {
- return this.conversations;
- }
-
- private void checkDeletedFiles(Conversation conversation) {
- conversation.findMessagesWithFiles(new Conversation.OnMessageFound() {
-
- @Override
- public void onMessageFound(Message message) {
- 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);
- }
- }
- }
- });
- }
-
- private void markFileDeleted(final String path) {
- Log.d(Config.LOGTAG,"deleted file "+path);
- for (Conversation conversation : getConversations()) {
- conversation.findMessagesWithFiles(new Conversation.OnMessageFound() {
- @Override
- public void onMessageFound(Message message) {
- DownloadableFile file = fileBackend.getFile(message);
- if (file.getAbsolutePath().equals(path)) {
- if (!file.exists()) {
- 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();
- }
- } else {
- Log.d(Config.LOGTAG,"found matching message for file "+path+" but file still exists");
- }
- }
- }
- });
- }
- }
-
- public void populateWithOrderedConversations(final List<Conversation> list) {
- populateWithOrderedConversations(list, true);
- }
-
- public void populateWithOrderedConversations(final List<Conversation> list, boolean includeNoFileUpload) {
- list.clear();
- if (includeNoFileUpload) {
- list.addAll(getConversations());
- } else {
- for (Conversation conversation : getConversations()) {
- if (conversation.getMode() == Conversation.MODE_SINGLE
- || conversation.getAccount().httpUploadAvailable()) {
- list.add(conversation);
- }
- }
- }
- try {
- Collections.sort(list);
- } catch (IllegalArgumentException e) {
- //ignore
- }
- }
-
- public void loadMoreMessages(final Conversation conversation, final long timestamp, final OnMoreMessagesLoaded callback) {
- if (XmppConnectionService.this.getMessageArchiveService().queryInProgress(conversation, callback)) {
- return;
- } else if (timestamp == 0) {
- return;
- }
- Log.d(Config.LOGTAG, "load more messages for " + conversation.getName() + " prior to " + MessageGenerator.getTimestamp(timestamp));
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- final Account account = conversation.getAccount();
- List<Message> messages = databaseBackend.getMessages(conversation, 50, timestamp);
- if (messages.size() > 0) {
- conversation.addAll(0, messages);
- checkDeletedFiles(conversation);
- callback.onMoreMessagesLoaded(messages.size(), conversation);
- } else if (conversation.hasMessagesLeftOnServer()
- && account.isOnlineAndConnected()
- && conversation.getLastClearHistory() == 0) {
- 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);
- }
- callback.informUser(R.string.fetching_history_from_server);
- }
- }
- }
- };
- mDatabaseExecutor.execute(runnable);
- }
-
- public List<Account> getAccounts() {
- return this.accounts;
- }
-
- public List<Conversation> findAllConferencesWith(Contact contact) {
- ArrayList<Conversation> results = new ArrayList<>();
- for(Conversation conversation : conversations) {
- if (conversation.getMode() == Conversation.MODE_MULTI
- && conversation.getMucOptions().isContactInRoom(contact)) {
- results.add(conversation);
- }
- }
- return results;
- }
-
- public Conversation find(final Iterable<Conversation> haystack, final Contact contact) {
- for (final Conversation conversation : haystack) {
- if (conversation.getContact() == contact) {
- return conversation;
- }
- }
- return null;
- }
-
- public Conversation find(final Iterable<Conversation> haystack, final Account account, final Jid jid) {
- if (jid == null) {
- return null;
- }
- for (final Conversation conversation : haystack) {
- if ((account == null || conversation.getAccount() == account)
- && (conversation.getJid().toBareJid().equals(jid.toBareJid()))) {
- return conversation;
- }
- }
- return null;
- }
-
- public Conversation findOrCreateConversation(final Account account, final Jid jid, final boolean muc) {
- return this.findOrCreateConversation(account, jid, muc, null);
- }
-
- public Conversation findOrCreateConversation(final Account account, final Jid jid, final boolean muc, final MessageArchiveService.Query query) {
- synchronized (this.conversations) {
- Conversation conversation = find(account, jid);
- if (conversation != null) {
- return conversation;
- }
- conversation = databaseBackend.findConversation(account, jid);
- if (conversation != null) {
- conversation.setStatus(Conversation.STATUS_AVAILABLE);
- conversation.setAccount(account);
- if (muc) {
- conversation.setMode(Conversation.MODE_MULTI);
- conversation.setContactJid(jid);
- } else {
- conversation.setMode(Conversation.MODE_SINGLE);
- conversation.setContactJid(jid.toBareJid());
- }
- conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
- this.databaseBackend.updateConversation(conversation);
- } else {
- String conversationName;
- Contact contact = account.getRoster().getContact(jid);
- if (contact != null) {
- conversationName = contact.getDisplayName();
- } else {
- conversationName = jid.getLocalpart();
- }
- if (muc) {
- conversation = new Conversation(conversationName, account, jid,
- Conversation.MODE_MULTI);
- } else {
- conversation = new Conversation(conversationName, account, jid.toBareJid(),
- Conversation.MODE_SINGLE);
- }
- this.databaseBackend.createConversation(conversation);
- }
- if (account.getXmppConnection() != null
- && account.getXmppConnection().getFeatures().mam()
- && !muc) {
- if (query == null) {
- this.mMessageArchiveService.query(conversation);
- } else {
- if (query.getConversation() == null) {
- this.mMessageArchiveService.query(conversation, query.getStart());
- }
- }
- }
- checkDeletedFiles(conversation);
- this.conversations.add(conversation);
- updateConversationUi();
- return conversation;
- }
- }
-
- public void archiveConversation(Conversation conversation) {
- getNotificationService().clear(conversation);
- conversation.setStatus(Conversation.STATUS_ARCHIVED);
- synchronized (this.conversations) {
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- if (conversation.getAccount().getStatus() == Account.State.ONLINE) {
- Bookmark bookmark = conversation.getBookmark();
- if (bookmark != null && bookmark.autojoin() && respectAutojoin()) {
- bookmark.setAutojoin(false);
- pushBookmarks(bookmark.getAccount());
- }
- }
- leaveMuc(conversation);
- } else {
- conversation.endOtrIfNeeded();
- if (conversation.getContact().getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
- Log.d(Config.LOGTAG, "Canceling presence request from " + conversation.getJid().toString());
- sendPresencePacket(
- conversation.getAccount(),
- mPresenceGenerator.stopPresenceUpdatesTo(conversation.getContact())
- );
- }
- }
- updateConversation(conversation);
- this.conversations.remove(conversation);
- updateConversationUi();
- }
- }
-
- public void createAccount(final Account account) {
- account.initAccountServices(this);
- databaseBackend.createAccount(account);
- this.accounts.add(account);
- this.reconnectAccountInBackground(account);
- updateAccountUi();
- }
-
- public void createAccountFromKey(final String alias, final OnAccountCreated callback) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias);
- Pair<Jid, String> info = CryptoHelper.extractJidAndName(chain[0]);
- if (findAccountByJid(info.first) == null) {
- Account account = new Account(info.first, "");
- account.setPrivateKeyAlias(alias);
- account.setOption(Account.OPTION_DISABLED, true);
- account.setDisplayName(info.second);
- createAccount(account);
- callback.onAccountCreated(account);
- if (Config.X509_VERIFICATION) {
- try {
- getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA");
- } catch (CertificateException e) {
- callback.informUser(R.string.certificate_chain_is_not_trusted);
- }
- }
- } else {
- callback.informUser(R.string.account_already_exists);
- }
- } catch (Exception e) {
- e.printStackTrace();
- callback.informUser(R.string.unable_to_parse_certificate);
- }
- }
- }).start();
-
- }
-
- public void updateKeyInAccount(final Account account, final String alias) {
- Log.d(Config.LOGTAG, "update key in account " + alias);
- try {
- X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias);
- Pair<Jid, String> info = CryptoHelper.extractJidAndName(chain[0]);
- if (account.getJid().toBareJid().equals(info.first)) {
- account.setPrivateKeyAlias(alias);
- account.setDisplayName(info.second);
- databaseBackend.updateAccount(account);
- if (Config.X509_VERIFICATION) {
- try {
- getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA");
- } catch (CertificateException e) {
- showErrorToastInUi(R.string.certificate_chain_is_not_trusted);
- }
- account.getAxolotlService().regenerateKeys(true);
- }
- } else {
- showErrorToastInUi(R.string.jid_does_not_match_certificate);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public boolean updateAccount(final Account account) {
- if (databaseBackend.updateAccount(account)) {
- account.setShowErrorNotification(true);
- this.statusListener.onStatusChanged(account);
- databaseBackend.updateAccount(account);
- reconnectAccountInBackground(account);
- updateAccountUi();
- getNotificationService().updateErrorNotification();
- return true;
- } else {
- return false;
- }
- }
-
- public void updateAccountPasswordOnServer(final Account account, final String newPassword, final OnAccountPasswordChanged callback) {
- final IqPacket iq = getIqGenerator().generateSetPassword(account, newPassword);
- sendIqPacket(account, iq, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- account.setPassword(newPassword);
- account.setOption(Account.OPTION_MAGIC_CREATE, false);
- databaseBackend.updateAccount(account);
- callback.onPasswordChangeSucceeded();
- } else {
- callback.onPasswordChangeFailed();
- }
- }
- });
- }
-
- public void deleteAccount(final Account account) {
- synchronized (this.conversations) {
- for (final Conversation conversation : conversations) {
- if (conversation.getAccount() == account) {
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- leaveMuc(conversation);
- } else if (conversation.getMode() == Conversation.MODE_SINGLE) {
- conversation.endOtrIfNeeded();
- }
- conversations.remove(conversation);
- }
- }
- if (account.getXmppConnection() != null) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- disconnect(account, true);
- }
- }).start();
- }
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- if (!databaseBackend.deleteAccount(account)) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": unable to delete account");
- }
- }
- };
- mDatabaseExecutor.execute(runnable);
- this.accounts.remove(account);
- updateAccountUi();
- getNotificationService().updateErrorNotification();
- }
- }
-
- public void setOnConversationListChangedListener(OnConversationUpdate listener) {
- synchronized (this) {
- this.mLastActivity = System.currentTimeMillis();
- if (checkListeners()) {
- switchToForeground();
- }
- this.mOnConversationUpdate = listener;
- this.mNotificationService.setIsInForeground(true);
- if (this.convChangedListenerCount < 2) {
- this.convChangedListenerCount++;
- }
- }
- }
-
- public void removeOnConversationListChangedListener() {
- synchronized (this) {
- this.convChangedListenerCount--;
- if (this.convChangedListenerCount <= 0) {
- this.convChangedListenerCount = 0;
- this.mOnConversationUpdate = null;
- this.mNotificationService.setIsInForeground(false);
- if (checkListeners()) {
- switchToBackground();
- }
- }
- }
- }
-
- public void setOnShowErrorToastListener(OnShowErrorToast onShowErrorToast) {
- synchronized (this) {
- if (checkListeners()) {
- switchToForeground();
- }
- this.mOnShowErrorToast = onShowErrorToast;
- if (this.showErrorToastListenerCount < 2) {
- this.showErrorToastListenerCount++;
- }
- }
- this.mOnShowErrorToast = onShowErrorToast;
- }
-
- public void removeOnShowErrorToastListener() {
- synchronized (this) {
- this.showErrorToastListenerCount--;
- if (this.showErrorToastListenerCount <= 0) {
- this.showErrorToastListenerCount = 0;
- this.mOnShowErrorToast = null;
- if (checkListeners()) {
- switchToBackground();
- }
- }
- }
- }
-
- public void setOnAccountListChangedListener(OnAccountUpdate listener) {
- synchronized (this) {
- if (checkListeners()) {
- switchToForeground();
- }
- this.mOnAccountUpdate = listener;
- if (this.accountChangedListenerCount < 2) {
- this.accountChangedListenerCount++;
- }
- }
- }
-
- public void removeOnAccountListChangedListener() {
- synchronized (this) {
- this.accountChangedListenerCount--;
- if (this.accountChangedListenerCount <= 0) {
- this.mOnAccountUpdate = null;
- this.accountChangedListenerCount = 0;
- if (checkListeners()) {
- switchToBackground();
- }
- }
- }
- }
-
- public void setOnCaptchaRequestedListener(OnCaptchaRequested listener) {
- synchronized (this) {
- if (checkListeners()) {
- switchToForeground();
- }
- this.mOnCaptchaRequested = listener;
- if (this.captchaRequestedListenerCount < 2) {
- this.captchaRequestedListenerCount++;
- }
- }
- }
-
- public void removeOnCaptchaRequestedListener() {
- synchronized (this) {
- this.captchaRequestedListenerCount--;
- if (this.captchaRequestedListenerCount <= 0) {
- this.mOnCaptchaRequested = null;
- this.captchaRequestedListenerCount = 0;
- if (checkListeners()) {
- switchToBackground();
- }
- }
- }
- }
-
- public void setOnRosterUpdateListener(final OnRosterUpdate listener) {
- synchronized (this) {
- if (checkListeners()) {
- switchToForeground();
- }
- this.mOnRosterUpdate = listener;
- if (this.rosterChangedListenerCount < 2) {
- this.rosterChangedListenerCount++;
- }
- }
- }
-
- public void removeOnRosterUpdateListener() {
- synchronized (this) {
- this.rosterChangedListenerCount--;
- if (this.rosterChangedListenerCount <= 0) {
- this.rosterChangedListenerCount = 0;
- this.mOnRosterUpdate = null;
- if (checkListeners()) {
- switchToBackground();
- }
- }
- }
- }
-
- public void setOnUpdateBlocklistListener(final OnUpdateBlocklist listener) {
- synchronized (this) {
- if (checkListeners()) {
- switchToForeground();
- }
- this.mOnUpdateBlocklist = listener;
- if (this.updateBlocklistListenerCount < 2) {
- this.updateBlocklistListenerCount++;
- }
- }
- }
-
- public void removeOnUpdateBlocklistListener() {
- synchronized (this) {
- this.updateBlocklistListenerCount--;
- if (this.updateBlocklistListenerCount <= 0) {
- this.updateBlocklistListenerCount = 0;
- this.mOnUpdateBlocklist = null;
- if (checkListeners()) {
- switchToBackground();
- }
- }
- }
- }
-
- public void setOnKeyStatusUpdatedListener(final OnKeyStatusUpdated listener) {
- synchronized (this) {
- if (checkListeners()) {
- switchToForeground();
- }
- this.mOnKeyStatusUpdated = listener;
- if (this.keyStatusUpdatedListenerCount < 2) {
- this.keyStatusUpdatedListenerCount++;
- }
- }
- }
-
- public void removeOnNewKeysAvailableListener() {
- synchronized (this) {
- this.keyStatusUpdatedListenerCount--;
- if (this.keyStatusUpdatedListenerCount <= 0) {
- this.keyStatusUpdatedListenerCount = 0;
- this.mOnKeyStatusUpdated = null;
- if (checkListeners()) {
- switchToBackground();
- }
- }
- }
- }
-
- public void setOnMucRosterUpdateListener(OnMucRosterUpdate listener) {
- synchronized (this) {
- if (checkListeners()) {
- switchToForeground();
- }
- this.mOnMucRosterUpdate = listener;
- if (this.mucRosterChangedListenerCount < 2) {
- this.mucRosterChangedListenerCount++;
- }
- }
- }
-
- public void removeOnMucRosterUpdateListener() {
- synchronized (this) {
- this.mucRosterChangedListenerCount--;
- if (this.mucRosterChangedListenerCount <= 0) {
- this.mucRosterChangedListenerCount = 0;
- this.mOnMucRosterUpdate = null;
- if (checkListeners()) {
- switchToBackground();
- }
- }
- }
- }
-
- public boolean checkListeners() {
- return (this.mOnAccountUpdate == null
- && this.mOnConversationUpdate == null
- && this.mOnRosterUpdate == null
- && this.mOnCaptchaRequested == null
- && this.mOnUpdateBlocklist == null
- && this.mOnShowErrorToast == null
- && this.mOnKeyStatusUpdated == null);
- }
-
- private void switchToForeground() {
- final boolean broadcastLastActivity = broadcastLastActivity();
- for (Conversation conversation : getConversations()) {
- conversation.setIncomingChatState(ChatState.ACTIVE);
- }
- for (Account account : getAccounts()) {
- if (account.getStatus() == Account.State.ONLINE) {
- account.deactivateGracePeriod();
- final XmppConnection connection = account.getXmppConnection();
- if (connection != null ) {
- if (connection.getFeatures().csi()) {
- connection.sendActive();
- }
- if (broadcastLastActivity) {
- sendPresence(account, false); //send new presence but don't include idle because we are not
- }
- }
- }
- }
- Log.d(Config.LOGTAG, "app switched into foreground");
- }
-
- private void switchToBackground() {
- final boolean broadcastLastActivity = broadcastLastActivity();
- for (Account account : getAccounts()) {
- if (account.getStatus() == Account.State.ONLINE) {
- XmppConnection connection = account.getXmppConnection();
- if (connection != null) {
- if (broadcastLastActivity) {
- sendPresence(account, broadcastLastActivity);
- }
- if (connection.getFeatures().csi()) {
- connection.sendInactive();
- }
- }
- }
- }
- this.mNotificationService.setIsInForeground(false);
- Log.d(Config.LOGTAG, "app switched into background");
- }
-
- private void connectMultiModeConversations(Account account) {
- List<Conversation> conversations = getConversations();
- for (Conversation conversation : conversations) {
- if (conversation.getMode() == Conversation.MODE_MULTI && conversation.getAccount() == account) {
- joinMuc(conversation);
- }
- }
- }
-
- public void joinMuc(Conversation conversation) {
- joinMuc(conversation, null);
- }
-
- private void joinMuc(Conversation conversation, final OnConferenceJoined onConferenceJoined) {
- Account account = conversation.getAccount();
- account.pendingConferenceJoins.remove(conversation);
- account.pendingConferenceLeaves.remove(conversation);
- if (account.getStatus() == Account.State.ONLINE) {
- conversation.resetMucOptions();
- if (onConferenceJoined != null) {
- conversation.getMucOptions().flagNoAutoPushConfiguration();
- }
- conversation.setHasMessagesLeftOnServer(false);
- fetchConferenceConfiguration(conversation, new OnConferenceConfigurationFetched() {
-
- private void join(Conversation conversation) {
- Account account = conversation.getAccount();
- 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, mucOptions.nonanonymous());
- packet.setTo(joinJid);
- Element x = packet.addChild("x", "http://jabber.org/protocol/muc");
- if (conversation.getMucOptions().getPassword() != null) {
- x.addChild("password").setContent(mucOptions.getPassword());
- }
-
- if (mucOptions.mamSupport()) {
- // Use MAM instead of the limited muc history to get history
- x.addChild("history").setAttribute("maxchars", "0");
- } else {
- // Fallback to muc history
- x.addChild("history").setAttribute("since", PresenceGenerator.getTimestamp(conversation.getLastMessageTransmitted()));
- }
- sendPresencePacket(account, packet);
- if (onConferenceJoined != null) {
- onConferenceJoined.onConferenceJoined(conversation);
- }
- if (!joinJid.equals(conversation.getJid())) {
- conversation.setContactJid(joinJid);
- databaseBackend.updateConversation(conversation);
- }
-
- if (mucOptions.mamSupport()) {
- getMessageArchiveService().catchupMUC(conversation);
- }
- if (mucOptions.membersOnly() && mucOptions.nonanonymous()) {
- fetchConferenceMembers(conversation);
- }
- sendUnsentMessages(conversation);
- }
-
- @Override
- public void onConferenceConfigurationFetched(Conversation conversation) {
- join(conversation);
- }
-
- @Override
- public void onFetchFailed(final Conversation conversation, Element error) {
- if (error != null && "remote-server-not-found".equals(error.getName())) {
- conversation.getMucOptions().setError(MucOptions.Error.SERVER_NOT_FOUND);
- } else {
- join(conversation);
- fetchConferenceConfiguration(conversation);
- }
- }
- });
- updateConversationUi();
- } else {
- account.pendingConferenceJoins.add(conversation);
- conversation.resetMucOptions();
- conversation.setHasMessagesLeftOnServer(false);
- updateConversationUi();
- }
- }
-
- private void fetchConferenceMembers(final Conversation conversation) {
- final Account account = conversation.getAccount();
- final String[] affiliations = {"member","admin","owner"};
- OnIqPacketReceived callback = new OnIqPacketReceived() {
-
- private int i = 0;
-
- @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()) {
- if ("item".equals(child.getName())) {
- MucOptions.User user = AbstractParser.parseItem(conversation,child);
- if (!user.realJidMatchesAccount()) {
- conversation.getMucOptions().updateUser(user);
- }
- }
- }
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not request affiliation "+affiliations[i]+" in "+conversation.getJid().toBareJid());
- }
- ++i;
- if (i >= affiliations.length) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": retrieved members for "+conversation.getJid().toBareJid()+": "+conversation.getMucOptions().getMembers());
- getAvatarService().clear(conversation);
- updateMucRosterUi();
- updateConversationUi();
- }
- }
- };
- for(String affiliation : affiliations) {
- sendIqPacket(account, mIqGenerator.queryAffiliation(conversation, affiliation), callback);
- }
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": fetching members for "+conversation.getName());
- }
-
- public void providePasswordForMuc(Conversation conversation, String password) {
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- conversation.getMucOptions().setPassword(password);
- if (conversation.getBookmark() != null) {
- if (respectAutojoin()) {
- conversation.getBookmark().setAutojoin(true);
- }
- pushBookmarks(conversation.getAccount());
- }
- updateConversation(conversation);
- joinMuc(conversation);
- }
- }
-
- public void renameInMuc(final Conversation conversation, final String nick, final UiCallback<Conversation> callback) {
- final MucOptions options = conversation.getMucOptions();
- final Jid joinJid = options.createJoinJid(nick);
- if (options.online()) {
- Account account = conversation.getAccount();
- options.setOnRenameListener(new OnRenameListener() {
-
- @Override
- public void onSuccess() {
- conversation.setContactJid(joinJid);
- databaseBackend.updateConversation(conversation);
- Bookmark bookmark = conversation.getBookmark();
- if (bookmark != null) {
- bookmark.setNick(nick);
- pushBookmarks(bookmark.getAccount());
- }
- callback.success(conversation);
- }
-
- @Override
- public void onFailure() {
- callback.error(R.string.nick_in_use, conversation);
- }
- });
-
- PresencePacket packet = new PresencePacket();
- packet.setTo(joinJid);
- packet.setFrom(conversation.getAccount().getJid());
-
- String sig = account.getPgpSignature();
- if (sig != null) {
- packet.addChild("status").setContent("online");
- packet.addChild("x", "jabber:x:signed").setContent(sig);
- }
- sendPresencePacket(account, packet);
- } else {
- conversation.setContactJid(joinJid);
- databaseBackend.updateConversation(conversation);
- if (conversation.getAccount().getStatus() == Account.State.ONLINE) {
- Bookmark bookmark = conversation.getBookmark();
- if (bookmark != null) {
- bookmark.setNick(nick);
- pushBookmarks(bookmark.getAccount());
- }
- joinMuc(conversation);
- }
- }
- }
-
- public void leaveMuc(Conversation conversation) {
- leaveMuc(conversation, false);
- }
-
- private void leaveMuc(Conversation conversation, boolean now) {
- Account account = conversation.getAccount();
- account.pendingConferenceJoins.remove(conversation);
- account.pendingConferenceLeaves.remove(conversation);
- if (account.getStatus() == Account.State.ONLINE || now) {
- PresencePacket packet = new PresencePacket();
- packet.setTo(conversation.getMucOptions().getSelf().getFullJid());
- packet.setFrom(conversation.getAccount().getJid());
- packet.setAttribute("type", "unavailable");
- sendPresencePacket(conversation.getAccount(), packet);
- conversation.getMucOptions().setOffline();
- conversation.deregisterWithBookmark();
- Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid()
- + ": leaving muc " + conversation.getJid());
- } else {
- account.pendingConferenceLeaves.add(conversation);
- }
- }
-
- private String findConferenceServer(final Account account) {
- String server;
- if (account.getXmppConnection() != null) {
- server = account.getXmppConnection().getMucServer();
- if (server != null) {
- return server;
- }
- }
- for (Account other : getAccounts()) {
- if (other != account && other.getXmppConnection() != null) {
- server = other.getXmppConnection().getMucServer();
- if (server != null) {
- return server;
- }
- }
- }
- return null;
- }
-
- public void createAdhocConference(final Account account,
- final String subject,
- final Iterable<Jid> jids,
- final UiCallback<Conversation> callback) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": creating adhoc conference with " + jids.toString());
- if (account.getStatus() == Account.State.ONLINE) {
- try {
- String server = findConferenceServer(account);
- if (server == null) {
- if (callback != null) {
- callback.error(R.string.no_conference_server_found, null);
- }
- return;
- }
- final Jid jid = Jid.fromParts(new BigInteger(64, getRNG()).toString(Character.MAX_RADIX), server, null);
- final Conversation conversation = findOrCreateConversation(account, jid, true);
- joinMuc(conversation, new OnConferenceJoined() {
- @Override
- public void onConferenceJoined(final Conversation conversation) {
- pushConferenceConfiguration(conversation, IqGenerator.defaultRoomConfiguration(), new OnConferenceOptionsPushed() {
- @Override
- public void onPushSucceeded() {
- if (subject != null && !subject.trim().isEmpty()) {
- pushSubjectToConference(conversation, subject.trim());
- }
- for (Jid invite : jids) {
- invite(conversation, invite);
- }
- if (account.countPresences() > 1) {
- directInvite(conversation, account.getJid().toBareJid());
- }
- saveConversationAsBookmark(conversation, subject);
- if (callback != null) {
- callback.success(conversation);
- }
- }
-
- @Override
- public void onPushFailed() {
- archiveConversation(conversation);
- if (callback != null) {
- callback.error(R.string.conference_creation_failed, conversation);
- }
- }
- });
- }
- });
- } catch (InvalidJidException e) {
- if (callback != null) {
- callback.error(R.string.conference_creation_failed, null);
- }
- }
- } else {
- if (callback != null) {
- callback.error(R.string.not_connected_try_again, null);
- }
- }
- }
-
- public void fetchConferenceConfiguration(final Conversation conversation) {
- fetchConferenceConfiguration(conversation, null);
- }
-
- public void fetchConferenceConfiguration(final Conversation conversation, final OnConferenceConfigurationFetched callback) {
- IqPacket request = new IqPacket(IqPacket.TYPE.GET);
- request.setTo(conversation.getJid().toBareJid());
- request.query("http://jabber.org/protocol/disco#info");
- sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- Element query = packet.findChild("query","http://jabber.org/protocol/disco#info");
- if (packet.getType() == IqPacket.TYPE.RESULT && query != null) {
- ArrayList<String> features = new ArrayList<>();
- for (Element child : query.getChildren()) {
- if (child != null && child.getName().equals("feature")) {
- String var = child.getAttribute("var");
- if (var != null) {
- features.add(var);
- }
- }
- }
- Element form = query.findChild("x", "jabber:x:data");
- if (form != null) {
- conversation.getMucOptions().updateFormData(Data.parse(form));
- }
- conversation.getMucOptions().updateFeatures(features);
- if (callback != null) {
- callback.onConferenceConfigurationFetched(conversation);
- }
- 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) {
- callback.onFetchFailed(conversation, packet.getError());
- }
- }
- }
- });
- }
-
- public void pushConferenceConfiguration(final Conversation conversation, final Bundle options, final OnConferenceOptionsPushed callback) {
- IqPacket request = new IqPacket(IqPacket.TYPE.GET);
- request.setTo(conversation.getJid().toBareJid());
- request.query("http://jabber.org/protocol/muc#owner");
- sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- Data data = Data.parse(packet.query().findChild("x", "jabber:x:data"));
- for (Field field : data.getFields()) {
- if (options.containsKey(field.getFieldName())) {
- field.setValue(options.getString(field.getFieldName()));
- }
- }
- data.submit();
- IqPacket set = new IqPacket(IqPacket.TYPE.SET);
- set.setTo(conversation.getJid().toBareJid());
- set.query("http://jabber.org/protocol/muc#owner").addChild(data);
- sendIqPacket(account, set, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (callback != null) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- callback.onPushSucceeded();
- } else {
- callback.onPushFailed();
- }
- }
- }
- });
- } else {
- if (callback != null) {
- callback.onPushFailed();
- }
- }
- }
- });
- }
-
- public void pushSubjectToConference(final Conversation conference, final String subject) {
- MessagePacket packet = this.getMessageGenerator().conferenceSubject(conference, subject);
- this.sendMessagePacket(conference.getAccount(), packet);
- final MucOptions mucOptions = conference.getMucOptions();
- final MucOptions.User self = mucOptions.getSelf();
- if (!mucOptions.persistent() && self.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
- Bundle options = new Bundle();
- options.putString("muc#roomconfig_persistentroom", "1");
- this.pushConferenceConfiguration(conference, options, null);
- }
- }
-
- public void changeAffiliationInConference(final Conversation conference, Jid user, final MucOptions.Affiliation affiliation, final OnAffiliationChanged callback) {
- final Jid jid = user.toBareJid();
- IqPacket request = this.mIqGenerator.changeAffiliation(conference, jid, affiliation.toString());
- sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- conference.getMucOptions().changeAffiliation(jid, affiliation);
- getAvatarService().clear(conference);
- callback.onAffiliationChangedSuccessful(jid);
- } else {
- callback.onAffiliationChangeFailed(jid, R.string.could_not_change_affiliation);
- }
- }
- });
- }
-
- public void changeAffiliationsInConference(final Conversation conference, MucOptions.Affiliation before, MucOptions.Affiliation after) {
- List<Jid> jids = new ArrayList<>();
- for (MucOptions.User user : conference.getMucOptions().getUsers()) {
- if (user.getAffiliation() == before && user.getRealJid() != null) {
- jids.add(user.getRealJid());
- }
- }
- IqPacket request = this.mIqGenerator.changeAffiliation(conference, jids, after.toString());
- sendIqPacket(conference.getAccount(), request, mDefaultIqHandler);
- }
-
- 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());
- sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- Log.d(Config.LOGTAG, packet.toString());
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- callback.onRoleChangedSuccessful(nick);
- } else {
- callback.onRoleChangeFailed(nick, R.string.could_not_change_role);
- }
- }
- });
- }
-
- private void disconnect(Account account, boolean force) {
- if ((account.getStatus() == Account.State.ONLINE)
- || (account.getStatus() == Account.State.DISABLED)) {
- final XmppConnection connection = account.getXmppConnection();
- if (!force) {
- List<Conversation> conversations = getConversations();
- for (Conversation conversation : conversations) {
- if (conversation.getAccount() == account) {
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- leaveMuc(conversation, true);
- } else {
- if (conversation.endOtrIfNeeded()) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
- + ": ended otr session with "
- + conversation.getJid());
- }
- }
- }
- }
- sendOfflinePresence(account);
- }
- connection.disconnect(force);
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return mBinder;
- }
-
- public void updateMessage(Message message) {
- databaseBackend.updateMessage(message);
- 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)) {
- pushContactToServer(contact);
- }
- if (contact.getOption(Contact.Options.DIRTY_DELETE)) {
- deleteContactOnServer(contact);
- }
- }
- }
-
- public void createContact(Contact contact) {
- boolean autoGrant = getPreferences().getBoolean("grant_new_contacts", true);
- if (autoGrant) {
- contact.setOption(Contact.Options.PREEMPTIVE_GRANT);
- contact.setOption(Contact.Options.ASKING);
- }
- pushContactToServer(contact);
- }
-
- public void onOtrSessionEstablished(Conversation conversation) {
- final Account account = conversation.getAccount();
- final Session otrSession = conversation.getOtrSession();
- Log.d(Config.LOGTAG,
- account.getJid().toBareJid() + " otr session established with "
- + conversation.getJid() + "/"
- + otrSession.getSessionID().getUserID());
- conversation.findUnsentMessagesWithEncryption(Message.ENCRYPTION_OTR, new Conversation.OnMessageFound() {
-
- @Override
- public void onMessageFound(Message message) {
- SessionID id = otrSession.getSessionID();
- try {
- message.setCounterpart(Jid.fromString(id.getAccountID() + "/" + id.getUserID()));
- } catch (InvalidJidException e) {
- return;
- }
- if (message.needsUploading()) {
- mJingleConnectionManager.createNewConnection(message);
- } else {
- MessagePacket outPacket = mMessageGenerator.generateOtrChat(message);
- if (outPacket != null) {
- mMessageGenerator.addDelay(outPacket, message.getTimeSent());
- message.setStatus(Message.STATUS_SEND);
- databaseBackend.updateMessage(message);
- sendMessagePacket(account, outPacket);
- }
- }
- updateConversationUi();
- }
- });
- }
-
- public boolean renewSymmetricKey(Conversation conversation) {
- Account account = conversation.getAccount();
- byte[] symmetricKey = new byte[32];
- this.mRandom.nextBytes(symmetricKey);
- Session otrSession = conversation.getOtrSession();
- if (otrSession != null) {
- MessagePacket packet = new MessagePacket();
- packet.setType(MessagePacket.TYPE_CHAT);
- packet.setFrom(account.getJid());
- MessageGenerator.addMessageHints(packet);
- packet.setAttribute("to", otrSession.getSessionID().getAccountID() + "/"
- + otrSession.getSessionID().getUserID());
- try {
- packet.setBody(otrSession
- .transformSending(CryptoHelper.FILETRANSFER
- + CryptoHelper.bytesToHex(symmetricKey))[0]);
- sendMessagePacket(account, packet);
- conversation.setSymmetricKey(symmetricKey);
- return true;
- } catch (OtrException e) {
- return false;
- }
- }
- return false;
- }
-
- public void pushContactToServer(final Contact contact) {
- contact.resetOption(Contact.Options.DIRTY_DELETE);
- contact.setOption(Contact.Options.DIRTY_PUSH);
- final Account account = contact.getAccount();
- if (account.getStatus() == Account.State.ONLINE) {
- final boolean ask = contact.getOption(Contact.Options.ASKING);
- final boolean sendUpdates = contact
- .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)
- && contact.getOption(Contact.Options.PREEMPTIVE_GRANT);
- final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- iq.query(Xmlns.ROSTER).addChild(contact.asElement());
- account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler);
- if (sendUpdates) {
- sendPresencePacket(account,
- mPresenceGenerator.sendPresenceUpdatesTo(contact));
- }
- if (ask) {
- sendPresencePacket(account,
- mPresenceGenerator.requestPresenceUpdatesFrom(contact));
- }
- }
- }
-
- 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()) {
- if (account.getJid().toBareJid().equals(avatar.owner) && account.getAvatar() == null) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": had no avatar. replacing with vcard");
- account.setAvatar(avatar.getFilename());
- databaseBackend.updateAccount(account);
- getAvatarService().clear(account);
- updateAccountUi();
- } else {
- Contact contact = account.getRoster().getContact(avatar.owner);
- contact.setAvatar(avatar);
- getAvatarService().clear(contact);
- updateRosterUi();
- }
- updateConversationUi();
- } 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);
- contact.setOption(Contact.Options.DIRTY_DELETE);
- Account account = contact.getAccount();
- if (account.getStatus() == Account.State.ONLINE) {
- IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- Element item = iq.query(Xmlns.ROSTER).addChild("item");
- item.setAttribute("jid", contact.getJid().toString());
- item.setAttribute("subscription", "remove");
- account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler);
- }
- }
-
- public void updateConversation(final Conversation conversation) {
- mDatabaseExecutor.execute(new Runnable() {
- @Override
- public void run() {
- databaseBackend.updateConversation(conversation);
- }
- });
- }
-
- private void reconnectAccount(final Account account, final boolean force, final boolean interactive) {
- synchronized (account) {
- XmppConnection connection = account.getXmppConnection();
- if (connection == null) {
- connection = createConnection(account);
- account.setXmppConnection(connection);
- } else {
- connection.interrupt();
- }
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- if (!force) {
- disconnect(account, false);
- }
- Thread thread = new Thread(connection);
- connection.setInteractive(interactive);
- connection.prepareNewConnection();
- thread.start();
- scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode());
- } else {
- disconnect(account, force);
- account.getRoster().clearPresences();
- connection.resetEverything();
- account.getAxolotlService().resetBrokenness();
- }
- }
- }
-
- public void reconnectAccountInBackground(final Account account) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- reconnectAccount(account, false, true);
- }
- }).start();
- }
-
- public void invite(Conversation conversation, Jid contact) {
- Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": inviting " + contact + " to " + conversation.getJid().toBareJid());
- MessagePacket packet = mMessageGenerator.invite(conversation, contact);
- sendMessagePacket(conversation.getAccount(), packet);
- }
-
- public void directInvite(Conversation conversation, Jid jid) {
- MessagePacket packet = mMessageGenerator.directInvite(conversation, jid);
- sendMessagePacket(conversation.getAccount(), packet);
- }
-
- public void resetSendingToWaiting(Account account) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": reset 'sending' messages to 'waiting'");
- for (Conversation conversation : getConversations()) {
- if (conversation.getAccount() == account) {
- conversation.findUnsentTextMessages(new Conversation.OnMessageFound() {
-
- @Override
- public void onMessageFound(Message message) {
- markMessage(message, Message.STATUS_WAITING);
- }
- });
- }
- }
- }
-
- public Message markMessage(final Account account, final Jid recipient, final String uuid, final int status) {
- return markMessage(account, recipient, uuid, status, null);
- }
-
- public Message markMessage(final Account account, final Jid recipient, final String uuid, final int status, String errorMessage) {
- if (uuid == null) {
- return null;
- }
- for (Conversation conversation : getConversations()) {
- if (conversation.getJid().toBareJid().equals(recipient) && conversation.getAccount() == account) {
- final Message message = conversation.findSentMessageWithUuidOrRemoteId(uuid);
- if (message != null) {
- markMessage(message, status, errorMessage);
- }
- return message;
- }
- }
- 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) {
- markMessage(message, status, null);
- }
-
- public void markMessage(Message message, int status, String errorMessage) {
- if (status == Message.STATUS_SEND_FAILED
- && (message.getStatus() == Message.STATUS_SEND_RECEIVED || message
- .getStatus() == Message.STATUS_SEND_DISPLAYED)) {
- return;
- }
- message.setErrorMessage(errorMessage);
- 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", true);
- }
-
- public boolean sendChatStates() {
- return getPreferences().getBoolean("chat_states", true);
- }
-
- 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", true);
- }
-
- public boolean useTorToConnect() {
- return Config.FORCE_ORBOT || getPreferences().getBoolean("use_tor", false);
- }
-
- public boolean showExtendedConnectionOptions() {
- return getPreferences().getBoolean("show_connection_options", false);
- }
-
- public boolean broadcastLastActivity() {
- return getPreferences().getBoolean("last_activity", true);
- }
-
- public int unreadCount() {
- int count = 0;
- for (Conversation conversation : getConversations()) {
- count += conversation.unreadCount();
- }
- return count;
- }
-
- public void vibrate() {
- Log.d(Config.LOGTAG,"Notification: short vibrate");
- Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
- vibrator.vibrate(100);
- }
-
-
- public void showErrorToastInUi(int resId) {
- if (mOnShowErrorToast != null) {
- mOnShowErrorToast.onShowErrorToast(resId);
- }
- }
-
- public void updateConversationUi() {
- if (mOnConversationUpdate != null) {
- mOnConversationUpdate.onConversationUpdate();
- }
- }
-
- public void updateAccountUi() {
- if (mOnAccountUpdate != null) {
- mOnAccountUpdate.onAccountUpdate();
- }
- }
-
- public void updateRosterUi() {
- if (mOnRosterUpdate != null) {
- mOnRosterUpdate.onRosterUpdate();
- }
- }
-
- public boolean displayCaptchaRequest(Account account, String id, Data data, Bitmap captcha) {
- if (mOnCaptchaRequested != null) {
- DisplayMetrics metrics = getApplicationContext().getResources().getDisplayMetrics();
- Bitmap scaled = Bitmap.createScaledBitmap(captcha, (int) (captcha.getWidth() * metrics.scaledDensity),
- (int) (captcha.getHeight() * metrics.scaledDensity), false);
-
- mOnCaptchaRequested.onCaptchaRequested(account, id, data, scaled);
- return true;
- }
- return false;
- }
-
- public void updateBlocklistUi(final OnUpdateBlocklist.Status status) {
- if (mOnUpdateBlocklist != null) {
- mOnUpdateBlocklist.OnUpdateBlocklist(status);
- }
- }
-
- public void updateMucRosterUi() {
- if (mOnMucRosterUpdate != null) {
- mOnMucRosterUpdate.onMucRosterUpdate();
- }
- }
-
- public void keyStatusUpdated(AxolotlService.FetchStatus report) {
- if (mOnKeyStatusUpdated != null) {
- mOnKeyStatusUpdated.onKeyStatusUpdated(report);
- }
- }
-
- public Account findAccountByJid(final Jid accountJid) {
- for (Account account : this.accounts) {
- if (account.getJid().toBareJid().equals(accountJid.toBareJid())) {
- return account;
- }
- }
- return null;
- }
-
- public Conversation findConversationByUuid(String uuid) {
- for (Conversation conversation : getConversations()) {
- if (conversation.getUuid().equals(uuid)) {
- return conversation;
- }
- }
- return null;
- }
-
- public boolean markRead(final Conversation conversation) {
- return markRead(conversation,true);
- }
-
- public boolean markRead(final Conversation conversation, boolean clear) {
- if (clear) {
- mNotificationService.clear(conversation);
- }
- final List<Message> readMessages = conversation.markRead();
- if (readMessages.size() > 0) {
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- for (Message message : readMessages) {
- databaseBackend.updateMessage(message);
- }
- }
- };
- mDatabaseExecutor.execute(runnable);
- updateUnreadCountBadge();
- return true;
- } else {
- return false;
- }
- }
-
- public synchronized void updateUnreadCountBadge() {
- int count = unreadCount();
- if (unreadCount != count) {
- Log.d(Config.LOGTAG, "update unread count to " + count);
- if (count > 0) {
- ShortcutBadger.applyCount(getApplicationContext(), count);
- } else {
- ShortcutBadger.removeCount(getApplicationContext());
- }
- unreadCount = count;
- }
- }
-
- public void sendReadMarker(final Conversation conversation) {
- final Message markable = conversation.getLatestMarkableMessage();
- if (this.markRead(conversation)) {
- updateConversationUi();
- }
- if (confirmMessages() && markable != null && markable.trusted() && markable.getRemoteMsgId() != null) {
- Log.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());
- this.sendMessagePacket(conversation.getAccount(), packet);
- }
- }
-
- public SecureRandom getRNG() {
- return this.mRandom;
- }
-
- public MemorizingTrustManager getMemorizingTrustManager() {
- return this.mMemorizingTrustManager;
- }
-
- public void setMemorizingTrustManager(MemorizingTrustManager trustManager) {
- this.mMemorizingTrustManager = trustManager;
- }
-
- public void updateMemorizingTrustmanager() {
- final MemorizingTrustManager tm;
- final boolean dontTrustSystemCAs = getPreferences().getBoolean("dont_trust_system_cas", false);
- if (dontTrustSystemCAs) {
- tm = new MemorizingTrustManager(getApplicationContext(), null);
- } else {
- tm = new MemorizingTrustManager(getApplicationContext());
- }
- setMemorizingTrustManager(tm);
- }
-
- public PowerManager getPowerManager() {
- return this.pm;
- }
-
- public LruCache<String, Bitmap> getBitmapCache() {
- return this.mBitmapCache;
- }
-
- public void syncRosterToDisk(final Account account) {
- Runnable runnable = new Runnable() {
-
- @Override
- public void run() {
- databaseBackend.writeRoster(account.getRoster());
- }
- };
- mDatabaseExecutor.execute(runnable);
-
- }
-
- public List<String> getKnownHosts() {
- final List<String> hosts = new ArrayList<>();
- for (final Account account : getAccounts()) {
- if (!hosts.contains(account.getServer().toString())) {
- hosts.add(account.getServer().toString());
- }
- for (final Contact contact : account.getRoster().getContacts()) {
- if (contact.showInRoster()) {
- final String server = contact.getServer().toString();
- if (server != null && !hosts.contains(server)) {
- hosts.add(server);
- }
- }
- }
- }
- if(Config.DOMAIN_LOCK != null && !hosts.contains(Config.DOMAIN_LOCK)) {
- hosts.add(Config.DOMAIN_LOCK);
- }
- if(Config.MAGIC_CREATE_DOMAIN != null && !hosts.contains(Config.MAGIC_CREATE_DOMAIN)) {
- hosts.add(Config.MAGIC_CREATE_DOMAIN);
- }
- return hosts;
- }
-
- public List<String> getKnownConferenceHosts() {
- final ArrayList<String> mucServers = new ArrayList<>();
- for (final Account account : accounts) {
- if (account.getXmppConnection() != null) {
- final String server = account.getXmppConnection().getMucServer();
- if (server != null && !mucServers.contains(server)) {
- mucServers.add(server);
- }
- }
- }
- return mucServers;
- }
-
- public void sendMessagePacket(Account account, MessagePacket packet) {
- XmppConnection connection = account.getXmppConnection();
- if (connection != null) {
- connection.sendMessagePacket(packet);
- }
- }
-
- public void sendPresencePacket(Account account, PresencePacket packet) {
- XmppConnection connection = account.getXmppConnection();
- if (connection != null) {
- connection.sendPresencePacket(packet);
- }
- }
-
- public void sendCreateAccountWithCaptchaPacket(Account account, String id, Data data) {
- final XmppConnection connection = account.getXmppConnection();
- if (connection != null) {
- IqPacket request = mIqGenerator.generateCreateAccountWithCaptcha(account, id, data);
- connection.sendUnmodifiedIqPacket(request, connection.registrationResponseListener);
- }
- }
-
- public void sendIqPacket(final Account account, final IqPacket packet, final OnIqPacketReceived callback) {
- final XmppConnection connection = account.getXmppConnection();
- if (connection != null) {
- connection.sendIqPacket(packet, callback);
- }
- }
-
- public void sendPresence(final Account account) {
- sendPresence(account, checkListeners() && broadcastLastActivity());
- }
-
- private void sendPresence(final Account account, final boolean includeIdleTimestamp) {
- PresencePacket packet;
- if (manuallyChangePresence()) {
- packet = mPresenceGenerator.selfPresence(account, account.getPresenceStatus());
- String message = account.getPresenceStatusMessage();
- if (message != null && !message.isEmpty()) {
- packet.addChild(new Element("status").setContent(message));
- }
- } else {
- packet = mPresenceGenerator.selfPresence(account, getTargetPresence());
- }
- if (mLastActivity > 0 && includeIdleTimestamp) {
- long since = Math.min(mLastActivity, System.currentTimeMillis()); //don't send future dates
- packet.addChild("idle","urn:xmpp:idle:1").setAttribute("since", AbstractGenerator.getTimestamp(since));
- }
- sendPresencePacket(account, packet);
- }
-
- private void deactivateGracePeriod() {
- for(Account account : getAccounts()) {
- account.deactivateGracePeriod();
- }
- }
-
- public void refreshAllPresences() {
- boolean includeIdleTimestamp = checkListeners() && broadcastLastActivity();
- for (Account account : getAccounts()) {
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- sendPresence(account, includeIdleTimestamp);
- }
- }
- }
-
- private void refreshAllGcmTokens() {
- for(Account account : getAccounts()) {
- if (account.isOnlineAndConnected() && mPushManagementService.available(account)) {
- mPushManagementService.registerPushTokenOnServer(account);
- }
- }
- }
-
- private void sendOfflinePresence(final Account account) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": sending offline presence");
- sendPresencePacket(account, mPresenceGenerator.sendOfflinePresence(account));
- }
-
- public MessageGenerator getMessageGenerator() {
- return this.mMessageGenerator;
- }
-
- public PresenceGenerator getPresenceGenerator() {
- return this.mPresenceGenerator;
- }
-
- public IqGenerator getIqGenerator() {
- return this.mIqGenerator;
- }
-
- public IqParser getIqParser() {
- return this.mIqParser;
- }
-
- public JingleConnectionManager getJingleConnectionManager() {
- return this.mJingleConnectionManager;
- }
-
- public MessageArchiveService getMessageArchiveService() {
- return this.mMessageArchiveService;
- }
-
- public List<Contact> findContacts(Jid jid) {
- ArrayList<Contact> contacts = new ArrayList<>();
- for (Account account : getAccounts()) {
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- Contact contact = account.getRoster().getContactFromRoster(jid);
- if (contact != null) {
- contacts.add(contact);
- }
- }
- }
- return contacts;
- }
-
- public Conversation findFirstMuc(Jid jid) {
- for(Conversation conversation : getConversations()) {
- if (conversation.getJid().toBareJid().equals(jid.toBareJid())
- && conversation.getMode() == Conversation.MODE_MULTI) {
- return conversation;
- }
- }
- return null;
- }
-
- public NotificationService getNotificationService() {
- return this.mNotificationService;
- }
-
- public HttpConnectionManager getHttpConnectionManager() {
- return this.mHttpConnectionManager;
- }
-
- 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);
- }
- }
-
- public void clearConversationHistory(final Conversation conversation) {
- conversation.clearMessages();
- conversation.setHasMessagesLeftOnServer(false); //avoid messages getting loaded through mam
- conversation.setLastClearHistory(System.currentTimeMillis());
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- databaseBackend.deleteMessagesInConversation(conversation);
- databaseBackend.updateConversation(conversation);
-
- }
- };
- mDatabaseExecutor.execute(runnable);
- }
-
- public void sendBlockRequest(final Blockable blockable, boolean reportSpam) {
- if (blockable != null && blockable.getBlockedJid() != null) {
- final Jid jid = blockable.getBlockedJid();
- this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetBlockRequest(jid, reportSpam), new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- account.getBlocklist().add(jid);
- updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
- }
- }
- });
- }
- }
-
- public void sendUnblockRequest(final Blockable blockable) {
- if (blockable != null && blockable.getJid() != null) {
- final Jid jid = blockable.getBlockedJid();
- this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetUnblockRequest(jid), new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- account.getBlocklist().remove(jid);
- updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
- }
- }
- });
- }
- }
-
- public void publishDisplayName(Account account) {
- String displayName = account.getDisplayName();
- if (displayName != null && !displayName.isEmpty()) {
- IqPacket publish = mIqGenerator.publishNick(displayName);
- sendIqPacket(account, publish, new OnIqPacketReceived() {
- @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");
- }
- }
- });
- }
- }
-
- public ServiceDiscoveryResult getCachedServiceDiscoveryResult(Pair<String, String> key) {
- ServiceDiscoveryResult result = discoCache.get(key);
- if (result != null) {
- return result;
- } else {
- result = databaseBackend.findDiscoveryResult(key.first, key.second);
- if (result != null) {
- discoCache.put(key, result);
- }
- return result;
- }
- }
-
- public void fetchCaps(Account account, final Jid jid, final Presence presence) {
- final Pair<String,String> key = new Pair<>(presence.getHash(), presence.getVer());
- ServiceDiscoveryResult disco = getCachedServiceDiscoveryResult(key);
- if (disco != null) {
- presence.setServiceDiscoveryResult(disco);
- } else {
- if (!account.inProgressDiscoFetches.contains(key)) {
- account.inProgressDiscoFetches.add(key);
- IqPacket request = new IqPacket(IqPacket.TYPE.GET);
- request.setTo(jid);
- request.query("http://jabber.org/protocol/disco#info");
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": making disco request for "+key.second+" to "+jid);
- sendIqPacket(account, request, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket discoPacket) {
- if (discoPacket.getType() == IqPacket.TYPE.RESULT) {
- ServiceDiscoveryResult disco = new ServiceDiscoveryResult(discoPacket);
- if (presence.getVer().equals(disco.getVer())) {
- 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());
- }
- }
- account.inProgressDiscoFetches.remove(key);
- }
- });
- }
- }
- }
-
- private void injectServiceDiscorveryResult(Roster roster, String hash, String ver, ServiceDiscoveryResult disco) {
- for(Contact contact : roster.getContacts()) {
- for(Presence presence : contact.getPresences().getPresences().values()) {
- if (hash.equals(presence.getHash()) && ver.equals(presence.getVer())) {
- presence.setServiceDiscoveryResult(disco);
- }
- }
- }
- }
-
- public void fetchMamPreferences(Account account, final OnMamPreferencesFetched callback) {
- IqPacket request = new IqPacket(IqPacket.TYPE.GET);
- request.addChild("prefs","urn:xmpp:mam:0");
- sendIqPacket(account, request, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- Element prefs = packet.findChild("prefs","urn:xmpp:mam:0");
- if (packet.getType() == IqPacket.TYPE.RESULT && prefs != null) {
- callback.onPreferencesFetched(prefs);
- } else {
- callback.onPreferencesFetchFailed();
- }
- }
- });
- }
-
- public PushManagementService getPushManagementService() {
- return mPushManagementService;
- }
-
- public Account getPendingAccount() {
- Account pending = null;
- for(Account account : getAccounts()) {
- if (account.isOptionSet(Account.OPTION_REGISTER)) {
- pending = account;
- } else {
- return null;
- }
- }
- return pending;
- }
-
- public void changeStatus(Account account, Presence.Status status, String statusMessage, boolean send) {
- if (!statusMessage.isEmpty()) {
- databaseBackend.insertPresenceTemplate(new PresenceTemplate(status, statusMessage));
- }
- changeStatusReal(account, status, statusMessage, send);
- }
-
- private void changeStatusReal(Account account, Presence.Status status, String statusMessage, boolean send) {
- account.setPresenceStatus(status);
- account.setPresenceStatusMessage(statusMessage);
- databaseBackend.updateAccount(account);
- if (!account.isOptionSet(Account.OPTION_DISABLED) && send) {
- sendPresence(account);
- }
- }
-
- public void changeStatus(Presence.Status status, String statusMessage) {
- if (!statusMessage.isEmpty()) {
- databaseBackend.insertPresenceTemplate(new PresenceTemplate(status, statusMessage));
- }
- for(Account account : getAccounts()) {
- changeStatusReal(account, status, statusMessage, true);
- }
- }
-
- public List<PresenceTemplate> getPresenceTemplates(Account account) {
- List<PresenceTemplate> templates = databaseBackend.getPresenceTemplates();
- for(PresenceTemplate template : account.getSelfContact().getPresences().asTemplates()) {
- if (!templates.contains(template)) {
- templates.add(0, template);
- }
- }
- return templates;
- }
-
- public void saveConversationAsBookmark(Conversation conversation, String name) {
- Account account = conversation.getAccount();
- Bookmark bookmark = new Bookmark(account, conversation.getJid().toBareJid());
- if (!conversation.getJid().isBareJid()) {
- bookmark.setNick(conversation.getJid().getResourcepart());
- }
- if (name != null && !name.trim().isEmpty()) {
- bookmark.setBookmarkName(name.trim());
- }
- bookmark.setAutojoin(getPreferences().getBoolean("autojoin",true));
- account.getBookmarks().add(bookmark);
- pushBookmarks(account);
- conversation.setBookmark(bookmark);
- }
-
- public void clearStartTimeCounter() {
- mDatabaseExecutor.execute(new Runnable() {
- @Override
- public void run() {
- databaseBackend.clearStartTimeCounter();
- }
- });
- }
-
- public void verifyFingerprints(Contact contact, List<XmppUri.Fingerprint> fingerprints) {
- boolean needsRosterWrite = false;
- final AxolotlService axolotlService = contact.getAccount().getAxolotlService();
- for(XmppUri.Fingerprint fp : fingerprints) {
- if (fp.type == XmppUri.FingerprintType.OTR) {
- needsRosterWrite |= contact.addOtrFingerprint(fp.fingerprint);
- } else if (fp.type == XmppUri.FingerprintType.OMEMO) {
- String fingerprint = "05"+fp.fingerprint.replaceAll("\\s","");
- FingerprintStatus fingerprintStatus = axolotlService.getFingerprintTrust(fingerprint);
- if (fingerprintStatus != null) {
- if (!fingerprintStatus.isVerified()) {
- axolotlService.setFingerprintTrust(fingerprint,fingerprintStatus.toVerified());
- }
- } else {
- axolotlService.preVerifyFingerprint(contact,fingerprint);
- }
- }
- }
- if (needsRosterWrite) {
- syncRosterToDisk(contact.getAccount());
- }
- }
-
- public boolean verifyFingerprints(Account account, List<XmppUri.Fingerprint> fingerprints) {
- final AxolotlService axolotlService = account.getAxolotlService();
- boolean verifiedSomething = false;
- for(XmppUri.Fingerprint fp : fingerprints) {
- if (fp.type == XmppUri.FingerprintType.OMEMO) {
- String fingerprint = "05"+fp.fingerprint.replaceAll("\\s","");
- Log.d(Config.LOGTAG,"trying to verify own fp="+fingerprint);
- FingerprintStatus fingerprintStatus = axolotlService.getFingerprintTrust(fingerprint);
- if (fingerprintStatus != null) {
- if (!fingerprintStatus.isVerified()) {
- axolotlService.setFingerprintTrust(fingerprint,fingerprintStatus.toVerified());
- verifiedSomething = true;
- }
- } else {
- axolotlService.preVerifyFingerprint(account,fingerprint);
- verifiedSomething = true;
- }
- }
- }
- return verifiedSomething;
- }
-
- public interface OnMamPreferencesFetched {
- void onPreferencesFetched(Element prefs);
- void onPreferencesFetchFailed();
- }
-
- public void pushMamPreferences(Account account, Element prefs) {
- IqPacket set = new IqPacket(IqPacket.TYPE.SET);
- set.addChild(prefs);
- sendIqPacket(account, set, null);
- }
-
- public interface OnAccountCreated {
- void onAccountCreated(Account account);
-
- void informUser(int r);
- }
-
- public interface OnMoreMessagesLoaded {
- void onMoreMessagesLoaded(int count, Conversation conversation);
-
- void informUser(int r);
- }
-
- public interface OnAccountPasswordChanged {
- void onPasswordChangeSucceeded();
-
- void onPasswordChangeFailed();
- }
-
- public interface OnAffiliationChanged {
- void onAffiliationChangedSuccessful(Jid jid);
-
- void onAffiliationChangeFailed(Jid jid, int resId);
- }
-
- public interface OnRoleChanged {
- void onRoleChangedSuccessful(String nick);
-
- void onRoleChangeFailed(String nick, int resid);
- }
-
- public interface OnConversationUpdate {
- void onConversationUpdate();
- }
-
- public interface OnAccountUpdate {
- void onAccountUpdate();
- }
-
- public interface OnCaptchaRequested {
- void onCaptchaRequested(Account account,
- String id,
- Data data,
- Bitmap captcha);
- }
-
- public interface OnRosterUpdate {
- void onRosterUpdate();
- }
-
- public interface OnMucRosterUpdate {
- void onMucRosterUpdate();
- }
-
- public interface OnConferenceConfigurationFetched {
- void onConferenceConfigurationFetched(Conversation conversation);
-
- void onFetchFailed(Conversation conversation, Element error);
- }
-
- public interface OnConferenceJoined {
- void onConferenceJoined(Conversation conversation);
- }
-
- public interface OnConferenceOptionsPushed {
- void onPushSucceeded();
-
- void onPushFailed();
- }
-
- public interface OnShowErrorToast {
- void onShowErrorToast(int resId);
- }
-
- public class XmppConnectionBinder extends Binder {
- public XmppConnectionService getService() {
- return XmppConnectionService.this;
- }
- }
-
- public void ScheduleAutomaticExport() {
- //start export log service every day at given time
- if (Config.ExportLogs) {
- if (Config.ExportLogs_Hour >= 0 && Config.ExportLogs_Hour <= 23 && Config.ExportLogs_Minute >= 0 && Config.ExportLogs_Minute <= 59) {
- Log.d(Config.LOGTAG, "Schedule automatic export logs at " + Config.ExportLogs_Hour + ":" + Config.ExportLogs_Minute);
- Calendar calendar = Calendar.getInstance();
- calendar.setTimeInMillis(System.currentTimeMillis());
- calendar.set(Calendar.HOUR_OF_DAY, Config.ExportLogs_Hour);
- calendar.set(Calendar.MINUTE, Config.ExportLogs_Minute);
- Intent intent = new Intent(this, AlarmReceiver.class);
- intent.setAction("exportlogs");
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, AlarmReceiver.SCHEDULE_ALARM_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
- ((AlarmManager) getSystemService(ALARM_SERVICE)).setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
- }
- }
- }
-
- public void CancelAutomaticExport() {
- if (Config.ExportLogs) {
- Log.d(Config.LOGTAG, "Cancel scheduled automatic export");
- Intent intent = new Intent(this, AlarmReceiver.class);
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, AlarmReceiver.SCHEDULE_ALARM_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
- ((AlarmManager) this.getSystemService(ALARM_SERVICE)).cancel(pendingIntent);
- }
- }
+ public static final String ACTION_REPLY_TO_CONVERSATION = "reply_to_conversations";
+ public static final String ACTION_CLEAR_NOTIFICATION = "clear_notification";
+ public static final String ACTION_DISABLE_FOREGROUND = "disable_foreground";
+ public static final String ACTION_TRY_AGAIN = "try_again";
+ public static final String ACTION_DISMISS_ERROR_NOTIFICATIONS = "dismiss_error";
+ public static final String ACTION_IDLE_PING = "idle_ping";
+ 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 ReplacingSerialSingleThreadExecutor mContactMergerExecutor = new ReplacingSerialSingleThreadExecutor(true);
+ private final IBinder mBinder = new XmppConnectionBinder();
+ private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
+ private final IqGenerator mIqGenerator = new IqGenerator(this);
+ private final List<String> mInProgressAvatarFetches = new ArrayList<>();
+ private final HashSet<Jid> mLowPingTimeoutMode = new HashSet<>();
+ private WakeLock wakeLock;
+ private long mLastActivity = 0;
+ public static VideoCompressor CompressVideo;
+ private NotificationManager mNotifyManager;
+
+ public DatabaseBackend databaseBackend;
+ private ContentObserver contactObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ Intent intent = new Intent(getApplicationContext(),
+ XmppConnectionService.class);
+ intent.setAction(ACTION_MERGE_PHONE_CONTACTS);
+ startService(intent);
+ }
+ };
+ private FileBackend fileBackend = new FileBackend(this);
+ private MemorizingTrustManager mMemorizingTrustManager;
+ private NotificationService mNotificationService = new NotificationService(
+ this);
+ private OnMessagePacketReceived mMessageParser = new MessageParser(this);
+ private OnPresencePacketReceived mPresenceParser = new PresenceParser(this);
+ private IqParser mIqParser = new IqParser(this);
+ private OnIqPacketReceived mDefaultIqHandler = new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() != IqPacket.TYPE.RESULT) {
+ 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);
+ }
+ }
+ }
+ };
+ private MessageGenerator mMessageGenerator = new MessageGenerator(this);
+ private PresenceGenerator mPresenceGenerator = new PresenceGenerator(this);
+ private List<Account> accounts;
+ private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(
+ this);
+ public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() {
+
+ @Override
+ public void onContactStatusChanged(Contact contact, boolean online) {
+ Conversation conversation = find(getConversations(), contact);
+ if (conversation != null) {
+ if (online) {
+ conversation.endOtrIfNeeded();
+ if (contact.getPresences().size() == 1) {
+ sendUnsentMessages(conversation);
+ }
+ } else {
+ //check if the resource we are haveing a conversation with is still online
+ if (conversation.hasValidOtrSession()) {
+ String otrResource = conversation.getOtrSession().getSessionID().getUserID();
+ if (!(Arrays.asList(contact.getPresences().toResourceArray()).contains(otrResource))) {
+ conversation.endOtrIfNeeded();
+ }
+ }
+ }
+ }
+ }
+ };
+ 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 ConversationsFileObserver fileObserver = new ConversationsFileObserver(
+ Environment.getExternalStorageDirectory().getAbsolutePath()
+ ) {
+ @Override
+ public void onEvent(int event, String path) {
+ markFileDeleted(path);
+ }
+ };
+ private final OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() {
+
+ @Override
+ public void onJinglePacketReceived(Account account, JinglePacket packet) {
+ mJingleConnectionManager.deliverPacket(account, packet);
+ }
+ };
+ private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() {
+
+ @Override
+ public void onMessageAcknowledged(Account account, String uuid) {
+ for (final Conversation conversation : getConversations()) {
+ if (conversation.getAccount() == account) {
+ Message message = conversation.findUnsentMessageWithUuid(uuid);
+ if (message != null) {
+ markMessage(message, Message.STATUS_SEND);
+ }
+ }
+ }
+ }
+ };
+ private int convChangedListenerCount = 0;
+ private OnShowErrorToast mOnShowErrorToast = null;
+ private int showErrorToastListenerCount = 0;
+ private int unreadCount = -1;
+ private OnAccountUpdate mOnAccountUpdate = null;
+ private OnCaptchaRequested mOnCaptchaRequested = null;
+ private int accountChangedListenerCount = 0;
+ private int captchaRequestedListenerCount = 0;
+ private OnRosterUpdate mOnRosterUpdate = null;
+ private OnUpdateBlocklist mOnUpdateBlocklist = null;
+ private int updateBlocklistListenerCount = 0;
+ private int rosterChangedListenerCount = 0;
+ private OnMucRosterUpdate mOnMucRosterUpdate = null;
+ private int mucRosterChangedListenerCount = 0;
+ private OnKeyStatusUpdated mOnKeyStatusUpdated = null;
+ private int keyStatusUpdatedListenerCount = 0;
+ private SecureRandom mRandom;
+ private LruCache<Pair<String, String>, ServiceDiscoveryResult> discoCache = new LruCache<>(20);
+ private final OnBindListener mOnBindListener = new OnBindListener() {
+
+ @Override
+ public void onBind(final Account account) {
+ synchronized (mInProgressAvatarFetches) {
+ for (Iterator<String> iterator = mInProgressAvatarFetches.iterator(); iterator.hasNext(); ) {
+ final String KEY = iterator.next();
+ if (KEY.startsWith(account.getJid().toBareJid() + "_")) {
+ iterator.remove();
+ }
+ }
+ }
+ account.getRoster().clearPresences();
+ mJingleConnectionManager.cancelInTransmission();
+ fetchRosterFromServer(account);
+ fetchBookmarks(account);
+ sendPresence(account);
+ if (mPushManagementService.available(account)) {
+ mPushManagementService.registerPushTokenOnServer(account);
+ }
+ connectMultiModeConversations(account);
+ syncDirtyContacts(account);
+ }
+ };
+ private OnStatusChanged statusListener = new OnStatusChanged() {
+
+ @Override
+ public void onStatusChanged(final Account account) {
+ XmppConnection connection = account.getXmppConnection();
+ if (mOnAccountUpdate != null) {
+ mOnAccountUpdate.onAccountUpdate();
+ }
+ if (account.getStatus() == Account.State.ONLINE) {
+ synchronized (mLowPingTimeoutMode) {
+ if (mLowPingTimeoutMode.remove(account.getJid().toBareJid())) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": leaving low ping timeout mode");
+ }
+ }
+ if (account.setShowErrorNotification(true)) {
+ databaseBackend.updateAccount(account);
+ }
+ mMessageArchiveService.executePendingQueries(account);
+ if (connection != null && connection.getFeatures().csi()) {
+ if (checkListeners()) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//inactive");
+ connection.sendInactive();
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//active");
+ connection.sendActive();
+ }
+ }
+ List<Conversation> conversations = getConversations();
+ for (Conversation conversation : conversations) {
+ 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");
+ }
+ sendUnsentMessages(conversation);
+ }
+ }
+ for (Conversation conversation : account.pendingConferenceLeaves) {
+ leaveMuc(conversation);
+ }
+ account.pendingConferenceLeaves.clear();
+ for (Conversation conversation : account.pendingConferenceJoins) {
+ joinMuc(conversation);
+ }
+ account.pendingConferenceJoins.clear();
+ scheduleWakeUpCall(Config.PING_MAX_INTERVAL, account.getUuid().hashCode());
+ } else if (account.getStatus() == Account.State.OFFLINE || account.getStatus() == Account.State.DISABLED) {
+ resetSendingToWaiting(account);
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ synchronized (mLowPingTimeoutMode) {
+ if (mLowPingTimeoutMode.contains(account.getJid().toBareJid())) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": went into offline state during low ping mode. reconnecting now");
+ reconnectAccount(account, true, false);
+ } else {
+ int timeToReconnect = mRandom.nextInt(20) + 10;
+ scheduleWakeUpCall(timeToReconnect, account.getUuid().hashCode());
+ }
+ }
+ }
+ } else if (account.getStatus() == Account.State.REGISTRATION_SUCCESSFUL) {
+ databaseBackend.updateAccount(account);
+ reconnectAccount(account, true, false);
+ } else if ((account.getStatus() != Account.State.CONNECTING)
+ && (account.getStatus() != Account.State.NO_INTERNET)) {
+ resetSendingToWaiting(account);
+ if (connection != null) {
+ int next = connection.getTimeToNextAttempt();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ + ": error connecting account. try again in "
+ + next + "s for the "
+ + (connection.getAttempt() + 1) + " time");
+ scheduleWakeUpCall(next, account.getUuid().hashCode());
+ }
+ }
+ getNotificationService().updateErrorNotification();
+ }
+ };
+ private OpenPgpServiceConnection pgpServiceConnection;
+ private PgpEngine mPgpEngine = null;
+ private PowerManager pm;
+ private LruCache<String, Bitmap> mBitmapCache;
+ private EventReceiver mEventReceiver = new EventReceiver();
+
+ 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;
+ }
+
+ public PgpEngine getPgpEngine() {
+ if (!Config.supportOpenPgp()) {
+ return null;
+ } else if (pgpServiceConnection != null && pgpServiceConnection.isBound()) {
+ if (this.mPgpEngine == null) {
+ this.mPgpEngine = new PgpEngine(new OpenPgpApi(
+ getApplicationContext(),
+ pgpServiceConnection.getService()), this);
+ }
+ return mPgpEngine;
+ } else {
+ return null;
+ }
+
+ }
+
+ public OpenPgpApi getOpenPgpApi() {
+ if (!Config.supportOpenPgp()) {
+ return null;
+ } else if (pgpServiceConnection != null && pgpServiceConnection.isBound()) {
+ return new OpenPgpApi(this, pgpServiceConnection.getService());
+ } 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);
+ final String path = getFileBackend().getOriginalPath(uri);
+ Log.d(Config.LOGTAG, "File path = " + path);
+ mFileAddingExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ if (path != null) {
+ message.setRelativeFilePath(path);
+ getFileBackend().updateFileParams(message);
+ if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ getPgpEngine().encrypt(message, callback);
+ } else {
+ callback.success(message);
+ }
+ } else {
+ try {
+ getFileBackend().copyFileToPrivateStorage(message, uri);
+ getFileBackend().updateFileParams(message);
+ if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ final PgpEngine pgpEngine = getPgpEngine();
+ if (pgpEngine != null) {
+ pgpEngine.encrypt(message, callback);
+ } else if (callback != null) {
+ callback.error(R.string.unable_to_connect_to_keychain, null);
+ }
+ } 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) {
+ final PgpEngine pgpEngine = getPgpEngine();
+ if (pgpEngine != null) {
+ pgpEngine.encrypt(message, callback);
+ } else if (callback != null) {
+ callback.error(R.string.unable_to_connect_to_keychain, null);
+ }
+ } else {
+ callback.success(message);
+ }
+ } catch (final FileBackend.FileCopyException e) {
+ callback.error(e.getResId(), message);
+ }
+ }
+ });
+ }
+
+ public void attachVideoToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
+ File f = new File(FileUtils.getPath(this, uri));
+ long filesize = f.length();
+ String path = f.toString();
+ final String compressVideos = getCompressVideoPreference();
+ boolean sendVideoAsIs = false;
+ final Integer NOTIFICATION_ID = (int) (new Date().getTime() / 1000);
+ mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext());
+ mBuilder.setContentTitle(getString(R.string.app_name))
+ .setContentText(getString(R.string.compressing_video))
+ .setSmallIcon(R.drawable.ic_play_box_outline_white_24dp)
+ .setOngoing(true)
+ .setProgress(0, 0, true);
+ mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
+ if (FileBackend.weOwnFile(this, uri)) {
+ Log.d(Config.LOGTAG, "trying to attach video that belonged to us");
+ mNotifyManager.cancel(NOTIFICATION_ID);
+ callback.error(R.string.security_error_invalid_file_access, null);
+ return;
+ }
+ Log.d(Config.LOGTAG, "Video file (size) :" + f.toString() + "(" + filesize / 1024 / 1024 + "MB)");
+ if (filesize == 0) {
+ Log.d(Config.LOGTAG, "Error with file, size = 0");
+ mNotifyManager.cancel(NOTIFICATION_ID);
+ callback.error(R.string.error_file_corrupt, null);
+ return;
+ }
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
+ File compressed_file = new File(FileBackend.getConversationsVideoDirectory() + "/"
+ + dateFormat.format(new Date())
+ + "_komp.mp4");
+ final String compressed_path = compressed_file.toString();
+ final Uri compressed_uri = Uri.fromFile(compressed_file);
+ if ("never".equals(compressVideos)) {
+ sendVideoAsIs = true;
+ } else if ("always".equals(compressVideos) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ sendVideoAsIs = false;
+ } else if ("auto".equals(compressVideos) && filesize > Config.VIDEO_MAX_SIZE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ sendVideoAsIs = false;
+ } else {
+ sendVideoAsIs = true;
+ }
+ if (!sendVideoAsIs) {
+ CompressVideo = new VideoCompressor(path, compressed_path, new Interface() {
+ @Override
+ public void videocompressed(boolean result) {
+ if (result) {
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": sending compressed video.");
+ mNotifyManager.cancel(NOTIFICATION_ID);
+ attachFileToConversation(conversation, compressed_uri, callback);
+ }
+ }
+ });
+ CompressVideo.execute();
+ } else {
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": not compressing video. sending as file");
+ mNotifyManager.cancel(NOTIFICATION_ID);
+ attachFileToConversation(conversation, uri, callback);
+ }
+ }
+
+ public interface Interface {
+ void videocompressed(boolean result);
+ }
+
+ class VideoCompressor extends AsyncTask<String, Void, Boolean> {
+ private String originalpath;
+ private String compressedpath;
+ private Interface mListener;
+
+ VideoCompressor(String path, String compressed_path, Interface mListener) {
+ originalpath = path;
+ compressedpath = compressed_path;
+ this.mListener = mListener;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ Log.d(Config.LOGTAG, "Start video compression");
+ wakeLock.acquire();
+ }
+
+ @Override
+ protected Boolean doInBackground(String... params) {
+ return MediaController.getInstance().convertVideo(originalpath, compressedpath);
+ }
+
+ @Override
+ protected void onPostExecute(Boolean compressed) {
+ super.onPostExecute(compressed);
+ wakeLock.release();
+ File video = new File(compressedpath);
+ if (mListener != null) {
+ if (video.exists() && video.length() > 0) {
+ mListener.videocompressed(compressed);
+ Log.d(Config.LOGTAG, "Compression successfully!");
+ } else {
+ mListener.videocompressed(false);
+ Log.d(Config.LOGTAG, "Compression failed!");
+ }
+ }
+ }
+ }
+
+ public Conversation find(Bookmark bookmark) {
+ return find(bookmark.getAccount(), bookmark.getJid());
+ }
+
+ public Conversation find(final Account account, final Jid jid) {
+ return find(getConversations(), account, jid);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ final String action = intent == null ? null : intent.getAction();
+ String pushedAccountHash = null;
+ boolean interactive = false;
+ if (action != null) {
+ final Conversation c = findConversationByUuid(intent.getStringExtra("uuid"));
+ switch (action) {
+ case ConnectivityManager.CONNECTIVITY_ACTION:
+ if (hasInternetConnection() && Config.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE) {
+ resetAllAttemptCounts(true);
+ }
+ break;
+ case ACTION_MERGE_PHONE_CONTACTS:
+ if (mRestoredFromDatabase) {
+ loadPhoneContacts();
+ }
+ return START_STICKY;
+ case Intent.ACTION_SHUTDOWN:
+ logoutAndSave(true);
+ return START_NOT_STICKY;
+ case ACTION_CLEAR_NOTIFICATION:
+ if (c != null) {
+ mNotificationService.clear(c);
+ } else {
+ mNotificationService.clear();
+ }
+ break;
+ case ACTION_DISMISS_ERROR_NOTIFICATIONS:
+ dismissErrorNotifications();
+ break;
+ case ACTION_TRY_AGAIN:
+ resetAllAttemptCounts(false);
+ interactive = true;
+ break;
+ case ACTION_REPLY_TO_CONVERSATION:
+ Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
+ if (remoteInput != null && c != null) {
+ final CharSequence body = remoteInput.getCharSequence("text_reply");
+ if (body != null && body.length() > 0) {
+ directReply(c, body.toString(), intent.getBooleanExtra("dismiss_notification", false));
+ }
+ }
+ break;
+ case AudioManager.RINGER_MODE_CHANGED_ACTION:
+ if (xaOnSilentMode()) {
+ refreshAllPresences();
+ }
+ break;
+ case Intent.ACTION_SCREEN_ON:
+ deactivateGracePeriod();
+ case Intent.ACTION_SCREEN_OFF:
+ if (awayWhenScreenOff()) {
+ refreshAllPresences();
+ }
+ break;
+ case ACTION_GCM_TOKEN_REFRESH:
+ refreshAllGcmTokens();
+ break;
+ case ACTION_IDLE_PING:
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ scheduleNextIdlePing();
+ }
+ break;
+ case ACTION_GCM_MESSAGE_RECEIVED:
+ Log.d(Config.LOGTAG, "gcm push message arrived in service. extras=" + intent.getExtras());
+ pushedAccountHash = intent.getStringExtra("account");
+ break;
+ }
+ }
+ this.wakeLock.acquire();
+
+ boolean pingNow = false;
+ HashSet<Account> pingCandidates = new HashSet<>();
+
+ for (Account account : accounts) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ if (!hasInternetConnection()) {
+ account.setStatus(Account.State.NO_INTERNET);
+ if (statusListener != null) {
+ statusListener.onStatusChanged(account);
+ }
+ } else {
+ if (account.getStatus() == Account.State.NO_INTERNET) {
+ account.setStatus(Account.State.OFFLINE);
+ if (statusListener != null) {
+ statusListener.onStatusChanged(account);
+ }
+ }
+ if (account.getStatus() == Account.State.ONLINE) {
+ synchronized (mLowPingTimeoutMode) {
+ long lastReceived = account.getXmppConnection().getLastPacketReceived();
+ long lastSent = account.getXmppConnection().getLastPingSent();
+ long pingInterval = "ui".equals(action) ? Config.PING_MIN_INTERVAL * 1000 : Config.PING_MAX_INTERVAL * 1000;
+ long msToNextPing = (Math.max(lastReceived, lastSent) + pingInterval) - SystemClock.elapsedRealtime();
+ int pingTimeout = mLowPingTimeoutMode.contains(account.getJid().toBareJid()) ? Config.LOW_PING_TIMEOUT * 1000 : Config.PING_TIMEOUT * 1000;
+ long pingTimeoutIn = (lastSent + pingTimeout) - SystemClock.elapsedRealtime();
+ if (lastSent > lastReceived) {
+ if (pingTimeoutIn < 0) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": ping timeout");
+ this.reconnectAccount(account, true, interactive);
+ } else {
+ int secs = (int) (pingTimeoutIn / 1000);
+ this.scheduleWakeUpCall(secs, account.getUuid().hashCode());
+ }
+ } else {
+ pingCandidates.add(account);
+ if (CryptoHelper.getAccountFingerprint(account).equals(pushedAccountHash)) {
+ pingNow = true;
+ if (mLowPingTimeoutMode.add(account.getJid().toBareJid())) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": entering low ping timeout mode");
+ }
+ } else if (msToNextPing <= 0) {
+ pingNow = true;
+ } else {
+ this.scheduleWakeUpCall((int) (msToNextPing / 1000), account.getUuid().hashCode());
+ if (mLowPingTimeoutMode.remove(account.getJid().toBareJid())) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": leaving low ping timeout mode");
+ }
+ }
+ }
+ }
+ } else if (account.getStatus() == Account.State.OFFLINE) {
+ reconnectAccount(account, true, interactive);
+ } else if (account.getStatus() == Account.State.CONNECTING) {
+ long secondsSinceLastConnect = (SystemClock.elapsedRealtime() - account.getXmppConnection().getLastConnect()) / 1000;
+ long secondsSinceLastDisco = (SystemClock.elapsedRealtime() - account.getXmppConnection().getLastDiscoStarted()) / 1000;
+ 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");
+ account.getXmppConnection().resetAttemptCount();
+ reconnectAccount(account, true, interactive);
+ } else if (discoTimeout < 0) {
+ account.getXmppConnection().sendDiscoTimeout();
+ scheduleWakeUpCall((int) Math.min(timeout, discoTimeout), account.getUuid().hashCode());
+ } else {
+ scheduleWakeUpCall((int) Math.min(timeout, discoTimeout), account.getUuid().hashCode());
+ }
+ } else {
+ if (account.getXmppConnection().getTimeToNextAttempt() <= 0) {
+ reconnectAccount(account, true, interactive);
+ }
+ }
+ }
+ if (mOnAccountUpdate != null) {
+ mOnAccountUpdate.onAccountUpdate();
+ }
+ }
+ }
+ if (pingNow) {
+ for (Account account : pingCandidates) {
+ synchronized (mLowPingTimeoutMode) {
+ final boolean lowTimeout = mLowPingTimeoutMode.contains(account.getJid().toBareJid());
+ account.getXmppConnection().sendPing();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + " send ping (action=" + action + ",lowTimeout=" + Boolean.toString(lowTimeout) + ")");
+ scheduleWakeUpCall(lowTimeout ? Config.LOW_PING_TIMEOUT : Config.PING_TIMEOUT, account.getUuid().hashCode());
+ }
+ }
+ }
+ if (wakeLock.isHeld()) {
+ try {
+ wakeLock.release();
+ } catch (final RuntimeException ignored) {
+ }
+ }
+ return START_STICKY;
+ }
+
+ public boolean isDataSaverDisabled() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
+ return !connectivityManager.isActiveNetworkMetered()
+ || connectivityManager.getRestrictBackgroundStatus() == ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+ } else {
+ return true;
+ }
+ }
+
+ private void directReply(Conversation conversation, String body, final boolean dismissAfterReply) {
+ Message message = new Message(conversation, body, conversation.getNextEncryption());
+ message.markUnread();
+ if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ getPgpEngine().encrypt(message, new UiCallback<Message>() {
+ @Override
+ public void success(Message message) {
+ message.setEncryption(Message.ENCRYPTION_DECRYPTED);
+ sendMessage(message);
+ if (dismissAfterReply) {
+ markRead(message.getConversation(), true);
+ } else {
+ mNotificationService.pushFromDirectReply(message);
+ }
+ }
+
+ @Override
+ public void error(int errorCode, Message object) {
+
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Message object) {
+
+ }
+ });
+ } else {
+ sendMessage(message);
+ if (dismissAfterReply) {
+ markRead(conversation, true);
+ } else {
+ mNotificationService.pushFromDirectReply(message);
+ }
+ }
+ }
+
+ 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 String getCompressVideoPreference() {
+ return getPreferences().getString("video_compression", "auto");
+ }
+
+ private Presence.Status getTargetPresence() {
+ if (xaOnSilentMode() && isPhoneSilenced()) {
+ return Presence.Status.XA;
+ } else if (awayWhenScreenOff() && !isInteractive()) {
+ return Presence.Status.AWAY;
+ } else {
+ return Presence.Status.ONLINE;
+ }
+ }
+
+ @SuppressLint("NewApi")
+ @SuppressWarnings("deprecation")
+ public boolean isInteractive() {
+ final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+
+ final boolean isScreenOn;
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ isScreenOn = pm.isScreenOn();
+ } else {
+ isScreenOn = pm.isInteractive();
+ }
+ return isScreenOn;
+ }
+
+ private boolean isPhoneSilenced() {
+ AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ try {
+ if (treatVibrateAsSilent()) {
+ return audioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
+ } else {
+ return audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT;
+ }
+ } catch (Throwable throwable) {
+ Log.d(Config.LOGTAG, "platform bug in isPhoneSilenced (" + throwable.getMessage() + ")");
+ return false;
+ }
+ }
+
+ private void resetAllAttemptCounts(boolean reallyAll) {
+ Log.d(Config.LOGTAG, "resetting all attempt counts");
+ for (Account account : accounts) {
+ if (account.hasErrorStatus() || reallyAll) {
+ final XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ connection.resetAttemptCount();
+ }
+ }
+ if (account.setShowErrorNotification(true)) {
+ databaseBackend.updateAccount(account);
+ }
+ }
+ mNotificationService.updateErrorNotification();
+ }
+
+ private void dismissErrorNotifications() {
+ for (final Account account : this.accounts) {
+ if (account.hasErrorStatus()) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": dismissing error notification");
+ if (account.setShowErrorNotification(false)) {
+ databaseBackend.updateAccount(account);
+ }
+ }
+ }
+ }
+
+ public boolean hasInternetConnection() {
+ ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+ return activeNetwork != null && activeNetwork.isConnected();
+ }
+
+ public boolean isWIFI() {
+ ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+ if (activeNetwork != null) { // connected to the internet
+ if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isMobile() {
+ ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+ if (activeNetwork != null) { // connected to the internet
+ if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
+ if (!activeNetwork.isRoaming()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean isMobileRoaming() {
+ ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+ if (activeNetwork != null) { // connected to the internet
+ if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
+ if (activeNetwork.isRoaming()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @SuppressLint("TrulyRandom")
+ @Override
+ public void onCreate() {
+ ExceptionHelper.init(getApplicationContext());
+ PRNGFixes.apply();
+ this.mRandom = new SecureRandom();
+ updateMemorizingTrustmanager();
+ final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
+ final int cacheSize = maxMemory / 8;
+ this.mBitmapCache = new LruCache<String, Bitmap>(cacheSize) {
+ @Override
+ protected int sizeOf(final String key, final Bitmap bitmap) {
+ return bitmap.getByteCount() / 1024;
+ }
+ };
+
+ this.databaseBackend = DatabaseBackend.getInstance(getApplicationContext());
+ this.accounts = databaseBackend.getAccounts();
+
+ if (databaseBackend.startTimeCountExceedsThreshold()) {
+ Log.d(Config.LOGTAG, "number of restarts exceeds threshold.");
+ }
+
+ restoreFromDatabase();
+
+ getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contactObserver);
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ fileObserver.startWatching();
+ }
+ }).start();
+ if (Config.supportOpenPgp()) {
+ this.pgpServiceConnection = new OpenPgpServiceConnection(getApplicationContext(), "org.sufficientlysecure.keychain", new OpenPgpServiceConnection.OnBound() {
+ @Override
+ public void onBound(IOpenPgpService2 service) {
+ for (Account account : accounts) {
+ final PgpDecryptionService pgp = account.getPgpDecryptionService();
+ if (pgp != null) {
+ pgp.continueDecryption(true);
+ }
+ }
+ }
+
+ @Override
+ public void onError(Exception e) {
+ }
+ });
+ this.pgpServiceConnection.bindToService();
+ }
+
+ this.pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XmppConnectionService");
+ toggleForegroundService();
+ updateUnreadCountBadge();
+ toggleScreenEventReceiver();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ scheduleNextIdlePing();
+ }
+ //start export log service every day at given time
+ ScheduleAutomaticExport();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ registerReceiver(this.mEventReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
+ }
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+ if (level >= TRIM_MEMORY_COMPLETE) {
+ Log.d(Config.LOGTAG, "clear cache due to low memory");
+ getBitmapCache().evictAll();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ try {
+ unregisterReceiver(this.mEventReceiver);
+ } catch (IllegalArgumentException e) {
+ //ignored
+ }
+ fileObserver.stopWatching();
+ super.onDestroy();
+ // cancel scheduled exporter
+ CancelAutomaticExport();
+ }
+
+ public void toggleScreenEventReceiver() {
+ if (awayWhenScreenOff() && !manuallyChangePresence()) {
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ registerReceiver(this.mEventReceiver, filter);
+ } else {
+ try {
+ unregisterReceiver(this.mEventReceiver);
+ } catch (IllegalArgumentException e) {
+ //ignored
+ }
+ }
+ }
+
+ public void toggleForegroundService() {
+ if (Config.USE_ALWAYS_FOREGROUND) {
+ startForeground(NotificationService.FOREGROUND_NOTIFICATION_ID, this.mNotificationService.createForegroundNotification());
+ } else {
+ stopForeground(true);
+ }
+ }
+
+ @Override
+ public void onTaskRemoved(final Intent rootIntent) {
+ super.onTaskRemoved(rootIntent);
+ }
+
+ private void logoutAndSave(boolean stop) {
+ int activeAccounts = 0;
+ databaseBackend.clearStartTimeCounter(); // regular swipes don't count towards restart counter
+ for (final Account account : accounts) {
+ if (account.getStatus() != Account.State.DISABLED) {
+ activeAccounts++;
+ }
+ databaseBackend.writeRoster(account.getRoster());
+ if (account.getXmppConnection() != null) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ disconnect(account, false);
+ }
+ }).start();
+ }
+ }
+ if (stop || activeAccounts == 0) {
+ Log.d(Config.LOGTAG, "good bye");
+ stopSelf();
+ }
+ }
+
+ public void scheduleWakeUpCall(int seconds, int requestCode) {
+ final long timeToWake = SystemClock.elapsedRealtime() + (seconds < 0 ? 1 : seconds + 1) * 1000;
+ AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+ Intent intent = new Intent(this, EventReceiver.class);
+ intent.setAction("ping");
+ PendingIntent alarmIntent = PendingIntent.getBroadcast(this, requestCode, intent, 0);
+ alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, timeToWake, alarmIntent);
+ }
+
+ @TargetApi(Build.VERSION_CODES.M)
+ private void scheduleNextIdlePing() {
+ Log.d(Config.LOGTAG, "schedule next idle ping");
+ AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+ Intent intent = new Intent(this, EventReceiver.class);
+ intent.setAction(ACTION_IDLE_PING);
+ alarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + (Config.IDLE_PING_INTERVAL * 1000),
+ PendingIntent.getBroadcast(this, 0, intent, 0)
+ );
+ }
+
+ public XmppConnection createConnection(final Account account) {
+ final SharedPreferences sharedPref = getPreferences();
+ String resource;
+ try {
+ resource = sharedPref.getString("resource", getString(R.string.default_resource)).toLowerCase(Locale.ENGLISH);
+ if (resource.trim().isEmpty()) {
+ throw new Exception();
+ }
+ } catch (Exception e) {
+ resource = "Pix-Art Messenger";
+ }
+ account.setResource(resource);
+ final XmppConnection connection = new XmppConnection(account, this);
+ connection.setOnMessagePacketReceivedListener(this.mMessageParser);
+ connection.setOnStatusChangedListener(this.statusListener);
+ connection.setOnPresencePacketReceivedListener(this.mPresenceParser);
+ connection.setOnUnregisteredIqPacketReceivedListener(this.mIqParser);
+ connection.setOnJinglePacketReceivedListener(this.jingleListener);
+ connection.setOnBindListener(this.mOnBindListener);
+ connection.setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener);
+ connection.addOnAdvancedStreamFeaturesAvailableListener(this.mMessageArchiveService);
+ connection.addOnAdvancedStreamFeaturesAvailableListener(this.mAvatarService);
+ AxolotlService axolotlService = account.getAxolotlService();
+ if (axolotlService != null) {
+ connection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
+ }
+ return connection;
+ }
+
+ public void sendChatState(Conversation conversation) {
+ if (sendChatStates()) {
+ 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");
+ final Account account = message.getConversation().getAccount();
+ if (account.httpUploadAvailable(fileBackend.getFile(message, false).getSize())) {
+ mHttpConnectionManager.createNewUploadConnection(message, delay);
+ } else {
+ mJingleConnectionManager.createNewConnection(message);
+ }
+ }
+
+ public void sendMessage(final Message message) {
+ sendMessage(message, false, false);
+ }
+
+ private void sendMessage(final Message message, final boolean resend, final boolean delay) {
+ final Account account = message.getConversation().getAccount();
+ if (account.setShowErrorNotification(true)) {
+ databaseBackend.updateAccount(account);
+ mNotificationService.updateErrorNotification();
+ }
+ final Conversation conversation = message.getConversation();
+ account.deactivateGracePeriod();
+ MessagePacket packet = null;
+ final boolean addToConversation = (conversation.getMode() != Conversation.MODE_MULTI
+ || account.getServerIdentity() != XmppConnection.Identity.SLACK)
+ && !message.edited();
+ boolean saveInDb = addToConversation;
+ message.setStatus(Message.STATUS_WAITING);
+
+ if (!resend && message.getEncryption() != Message.ENCRYPTION_OTR) {
+ message.getConversation().endOtrIfNeeded();
+ message.getConversation().findUnsentMessagesWithEncryption(Message.ENCRYPTION_OTR,
+ new Conversation.OnMessageFound() {
+ @Override
+ public void onMessageFound(Message message) {
+ markMessage(message, Message.STATUS_SEND_FAILED);
+ }
+ });
+ }
+
+ if (account.isOnlineAndConnected()) {
+ switch (message.getEncryption()) {
+ case Message.ENCRYPTION_NONE:
+ if (message.needsUploading()) {
+ if (account.httpUploadAvailable(fileBackend.getFile(message, false).getSize())
+ || message.fixCounterpart()) {
+ this.sendFileMessage(message, delay);
+ } else {
+ break;
+ }
+ } else {
+ packet = mMessageGenerator.generateChat(message);
+ }
+ break;
+ case Message.ENCRYPTION_PGP:
+ case Message.ENCRYPTION_DECRYPTED:
+ if (message.needsUploading()) {
+ if (account.httpUploadAvailable(fileBackend.getFile(message, false).getSize())
+ || message.fixCounterpart()) {
+ this.sendFileMessage(message, delay);
+ } else {
+ break;
+ }
+ } else {
+ packet = mMessageGenerator.generatePgpChat(message);
+ }
+ break;
+ case Message.ENCRYPTION_OTR:
+ SessionImpl otrSession = conversation.getOtrSession();
+ if (otrSession != null && otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) {
+ try {
+ message.setCounterpart(Jid.fromSessionID(otrSession.getSessionID()));
+ } catch (InvalidJidException e) {
+ break;
+ }
+ if (message.needsUploading()) {
+ mJingleConnectionManager.createNewConnection(message);
+ } else {
+ packet = mMessageGenerator.generateOtrChat(message);
+ }
+ } else if (otrSession == null) {
+ 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());
+ break;
+ }
+ } else {
+ Log.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())
+ || message.fixCounterpart()) {
+ this.sendFileMessage(message, delay);
+ } else {
+ break;
+ }
+ } else {
+ XmppAxolotlMessage axolotlMessage = account.getAxolotlService().fetchAxolotlMessageFromCache(message);
+ if (axolotlMessage == null) {
+ account.getAxolotlService().preparePayloadMessage(message, delay);
+ } else {
+ packet = mMessageGenerator.generateAxolotlChat(message, axolotlMessage);
+ }
+ }
+ break;
+
+ }
+ if (packet != null) {
+ if (account.getXmppConnection().getFeatures().sm() || (conversation.getMode() == Conversation.MODE_MULTI && message.getCounterpart().isBareJid())) {
+ message.setStatus(Message.STATUS_UNSEND);
+ } else {
+ message.setStatus(Message.STATUS_SEND);
+ }
+ }
+ } else {
+ switch (message.getEncryption()) {
+ case Message.ENCRYPTION_DECRYPTED:
+ if (!message.needsUploading()) {
+ String pgpBody = message.getEncryptedBody();
+ String decryptedBody = message.getBody();
+ message.setBody(pgpBody);
+ message.setEncryption(Message.ENCRYPTION_PGP);
+ if (message.edited()) {
+ message.setBody(decryptedBody);
+ message.setEncryption(Message.ENCRYPTION_DECRYPTED);
+ databaseBackend.updateMessage(message, message.getEditedId());
+ updateConversationUi();
+ return;
+ } else {
+ databaseBackend.createMessage(message);
+ saveInDb = false;
+ message.setBody(decryptedBody);
+ message.setEncryption(Message.ENCRYPTION_DECRYPTED);
+ }
+ }
+ 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());
+ conversation.startOtrSession(message.getCounterpart().getResourcepart(), false);
+ }
+ break;
+ case Message.ENCRYPTION_AXOLOTL:
+ message.setFingerprint(account.getAxolotlService().getOwnFingerprint());
+ break;
+ }
+ }
+
+ if (resend) {
+ if (packet != null && addToConversation) {
+ if (account.getXmppConnection().getFeatures().sm() || (conversation.getMode() == Conversation.MODE_MULTI && message.getCounterpart().isBareJid())) {
+ markMessage(message, Message.STATUS_UNSEND);
+ } else {
+ markMessage(message, Message.STATUS_SEND);
+ }
+ }
+ } else {
+ if (addToConversation) {
+ conversation.add(message);
+ }
+ if (message.getEncryption() == Message.ENCRYPTION_NONE || saveEncryptedMessages()) {
+ if (saveInDb) {
+ databaseBackend.createMessage(message);
+ } else if (message.edited()) {
+ databaseBackend.updateMessage(message, message.getEditedId());
+ }
+ }
+ updateConversationUi();
+ }
+ if (packet != null) {
+ if (delay) {
+ mMessageGenerator.addDelay(packet, message.getTimeSent());
+ }
+ if (conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
+ if (this.sendChatStates()) {
+ packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
+ }
+ }
+ sendMessagePacket(account, packet);
+ }
+ }
+
+ private void sendUnsentMessages(final Conversation conversation) {
+ conversation.findWaitingMessages(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
+ resendMessage(message, true);
+ }
+ });
+ }
+
+ public void resendMessage(final Message message, final boolean delay) {
+ sendMessage(message, true, delay);
+ }
+
+ 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()
+ + ": fetching roster version " + account.getRosterVersion());
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching roster");
+ }
+ iqPacket.query(Xmlns.ROSTER).setAttribute("ver", account.getRosterVersion());
+ sendIqPacket(account, iqPacket, mIqParser);
+ }
+
+ public void fetchBookmarks(final Account account) {
+ final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET);
+ final Element query = iqPacket.query("jabber:iq:private");
+ query.addChild("storage", "storage:bookmarks");
+ final OnIqPacketReceived callback = new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ 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")) {
+ final Bookmark bookmark = Bookmark.parse(item, account);
+ Bookmark old = bookmarks.put(bookmark.getJid(), bookmark);
+ if (old != null && old.getBookmarkName() != null && bookmark.getBookmarkName() == null) {
+ bookmark.setBookmarkName(old.getBookmarkName());
+ }
+ Conversation conversation = find(bookmark);
+ if (conversation != null) {
+ conversation.setBookmark(bookmark);
+ } else if (bookmark.autojoin() && bookmark.getJid() != null && autojoin) {
+ conversation = findOrCreateConversation(
+ account, bookmark.getJid(), true);
+ conversation.setBookmark(bookmark);
+ joinMuc(conversation);
+ }
+ }
+ }
+ }
+ account.setBookmarks(new ArrayList<>(bookmarks.values()));
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not fetch bookmarks");
+ }
+ }
+ };
+ sendIqPacket(account, iqPacket, callback);
+ }
+
+ public void pushBookmarks(Account account) {
+ Log.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");
+ for (Bookmark bookmark : account.getBookmarks()) {
+ storage.addChild(bookmark);
+ }
+ sendIqPacket(account, iqPacket, mDefaultIqHandler);
+ }
+
+ private void restoreFromDatabase() {
+ synchronized (this.conversations) {
+ final Map<String, Account> accountLookupTable = new Hashtable<>();
+ for (Account account : this.accounts) {
+ accountLookupTable.put(account.getUuid(), account);
+ }
+ this.conversations.addAll(databaseBackend.getConversations(Conversation.STATUS_AVAILABLE));
+ for (Conversation conversation : this.conversations) {
+ Account account = accountLookupTable.get(conversation.getAccountUuid());
+ conversation.setAccount(account);
+ }
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ Log.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();
+ loadPhoneContacts();
+ Log.d(Config.LOGTAG, "restoring messages");
+ for (Conversation conversation : conversations) {
+ conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
+ checkDeletedFiles(conversation);
+ conversation.findUnsentTextMessages(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
+ markMessage(message, Message.STATUS_WAITING);
+ }
+ });
+ conversation.findUnreadMessages(new Conversation.OnMessageFound() {
+ @Override
+ public void onMessageFound(Message message) {
+ mNotificationService.pushFromBacklog(message);
+ }
+ });
+ }
+ mNotificationService.finishBacklog(false);
+ mRestoredFromDatabase = true;
+ Log.d(Config.LOGTAG, "restored all messages");
+ updateConversationUi();
+ }
+ };
+ mDatabaseExecutor.execute(runnable);
+ }
+ }
+
+ public void loadPhoneContacts() {
+ mContactMergerExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ PhoneHelper.loadPhoneContacts(XmppConnectionService.this, new OnPhoneContactsLoadedListener() {
+ @Override
+ public void onPhoneContactsLoaded(List<Bundle> phoneContacts) {
+ Log.d(Config.LOGTAG, "start merging phone contacts with roster");
+ for (Account account : accounts) {
+ List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts();
+ for (Bundle phoneContact : phoneContacts) {
+ Jid jid;
+ try {
+ jid = Jid.fromString(phoneContact.getString("jid"));
+ } catch (final InvalidJidException e) {
+ continue;
+ }
+ final Contact contact = account.getRoster().getContact(jid);
+ String systemAccount = phoneContact.getInt("phoneid")
+ + "#"
+ + phoneContact.getString("lookup");
+ contact.setSystemAccount(systemAccount);
+ if (contact.setPhotoUri(phoneContact.getString("photouri"))) {
+ getAvatarService().clear(contact);
+ }
+ contact.setSystemName(phoneContact.getString("displayname"));
+ withSystemAccounts.remove(contact);
+ }
+ for (Contact contact : withSystemAccounts) {
+ contact.setSystemAccount(null);
+ contact.setSystemName(null);
+ if (contact.setPhotoUri(null)) {
+ getAvatarService().clear(contact);
+ }
+ }
+ }
+ Log.d(Config.LOGTAG, "finished merging phone contacts");
+ updateAccountUi();
+ }
+ });
+ }
+ });
+ }
+
+ public List<Conversation> getConversations() {
+ return this.conversations;
+ }
+
+ private void checkDeletedFiles(Conversation conversation) {
+ conversation.findMessagesWithFiles(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
+ 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);
+ }
+ }
+ }
+ });
+ }
+
+ private void markFileDeleted(final String path) {
+ Log.d(Config.LOGTAG, "deleted file " + path);
+ for (Conversation conversation : getConversations()) {
+ conversation.findMessagesWithFiles(new Conversation.OnMessageFound() {
+ @Override
+ public void onMessageFound(Message message) {
+ DownloadableFile file = fileBackend.getFile(message);
+ if (file.getAbsolutePath().equals(path)) {
+ if (!file.exists()) {
+ 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();
+ }
+ } else {
+ Log.d(Config.LOGTAG, "found matching message for file " + path + " but file still exists");
+ }
+ }
+ }
+ });
+ }
+ }
+
+ public void populateWithOrderedConversations(final List<Conversation> list) {
+ populateWithOrderedConversations(list, true);
+ }
+
+ public void populateWithOrderedConversations(final List<Conversation> list, boolean includeNoFileUpload) {
+ list.clear();
+ if (includeNoFileUpload) {
+ list.addAll(getConversations());
+ } else {
+ for (Conversation conversation : getConversations()) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE
+ || conversation.getAccount().httpUploadAvailable()) {
+ list.add(conversation);
+ }
+ }
+ }
+ try {
+ Collections.sort(list);
+ } catch (IllegalArgumentException e) {
+ //ignore
+ }
+ }
+
+ public void loadMoreMessages(final Conversation conversation, final long timestamp, final OnMoreMessagesLoaded callback) {
+ if (XmppConnectionService.this.getMessageArchiveService().queryInProgress(conversation, callback)) {
+ return;
+ } else if (timestamp == 0) {
+ return;
+ }
+ Log.d(Config.LOGTAG, "load more messages for " + conversation.getName() + " prior to " + MessageGenerator.getTimestamp(timestamp));
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ final Account account = conversation.getAccount();
+ List<Message> messages = databaseBackend.getMessages(conversation, 50, timestamp);
+ if (messages.size() > 0) {
+ conversation.addAll(0, messages);
+ checkDeletedFiles(conversation);
+ callback.onMoreMessagesLoaded(messages.size(), conversation);
+ } else if (conversation.hasMessagesLeftOnServer()
+ && account.isOnlineAndConnected()
+ && conversation.getLastClearHistory() == 0) {
+ 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);
+ }
+ callback.informUser(R.string.fetching_history_from_server);
+ }
+ }
+ }
+ };
+ mDatabaseExecutor.execute(runnable);
+ }
+
+ public List<Account> getAccounts() {
+ return this.accounts;
+ }
+
+ public List<Conversation> findAllConferencesWith(Contact contact) {
+ ArrayList<Conversation> results = new ArrayList<>();
+ for (Conversation conversation : conversations) {
+ if (conversation.getMode() == Conversation.MODE_MULTI
+ && conversation.getMucOptions().isContactInRoom(contact)) {
+ results.add(conversation);
+ }
+ }
+ return results;
+ }
+
+ public Conversation find(final Iterable<Conversation> haystack, final Contact contact) {
+ for (final Conversation conversation : haystack) {
+ if (conversation.getContact() == contact) {
+ return conversation;
+ }
+ }
+ return null;
+ }
+
+ public Conversation find(final Iterable<Conversation> haystack, final Account account, final Jid jid) {
+ if (jid == null) {
+ return null;
+ }
+ for (final Conversation conversation : haystack) {
+ if ((account == null || conversation.getAccount() == account)
+ && (conversation.getJid().toBareJid().equals(jid.toBareJid()))) {
+ return conversation;
+ }
+ }
+ return null;
+ }
+
+ public Conversation findOrCreateConversation(final Account account, final Jid jid, final boolean muc) {
+ return this.findOrCreateConversation(account, jid, muc, null);
+ }
+
+ public Conversation findOrCreateConversation(final Account account, final Jid jid, final boolean muc, final MessageArchiveService.Query query) {
+ synchronized (this.conversations) {
+ Conversation conversation = find(account, jid);
+ if (conversation != null) {
+ return conversation;
+ }
+ conversation = databaseBackend.findConversation(account, jid);
+ if (conversation != null) {
+ conversation.setStatus(Conversation.STATUS_AVAILABLE);
+ conversation.setAccount(account);
+ if (muc) {
+ conversation.setMode(Conversation.MODE_MULTI);
+ conversation.setContactJid(jid);
+ } else {
+ conversation.setMode(Conversation.MODE_SINGLE);
+ conversation.setContactJid(jid.toBareJid());
+ }
+ conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
+ this.databaseBackend.updateConversation(conversation);
+ } else {
+ String conversationName;
+ Contact contact = account.getRoster().getContact(jid);
+ if (contact != null) {
+ conversationName = contact.getDisplayName();
+ } else {
+ conversationName = jid.getLocalpart();
+ }
+ if (muc) {
+ conversation = new Conversation(conversationName, account, jid,
+ Conversation.MODE_MULTI);
+ } else {
+ conversation = new Conversation(conversationName, account, jid.toBareJid(),
+ Conversation.MODE_SINGLE);
+ }
+ this.databaseBackend.createConversation(conversation);
+ }
+ if (account.getXmppConnection() != null
+ && account.getXmppConnection().getFeatures().mam()
+ && !muc) {
+ if (query == null) {
+ this.mMessageArchiveService.query(conversation);
+ } else {
+ if (query.getConversation() == null) {
+ this.mMessageArchiveService.query(conversation, query.getStart());
+ }
+ }
+ }
+ checkDeletedFiles(conversation);
+ this.conversations.add(conversation);
+ updateConversationUi();
+ return conversation;
+ }
+ }
+
+ public void archiveConversation(Conversation conversation) {
+ getNotificationService().clear(conversation);
+ conversation.setStatus(Conversation.STATUS_ARCHIVED);
+ synchronized (this.conversations) {
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ if (conversation.getAccount().getStatus() == Account.State.ONLINE) {
+ Bookmark bookmark = conversation.getBookmark();
+ if (bookmark != null && bookmark.autojoin() && respectAutojoin()) {
+ bookmark.setAutojoin(false);
+ pushBookmarks(bookmark.getAccount());
+ }
+ }
+ leaveMuc(conversation);
+ } else {
+ conversation.endOtrIfNeeded();
+ if (conversation.getContact().getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
+ Log.d(Config.LOGTAG, "Canceling presence request from " + conversation.getJid().toString());
+ sendPresencePacket(
+ conversation.getAccount(),
+ mPresenceGenerator.stopPresenceUpdatesTo(conversation.getContact())
+ );
+ }
+ }
+ updateConversation(conversation);
+ this.conversations.remove(conversation);
+ updateConversationUi();
+ }
+ }
+
+ public void createAccount(final Account account) {
+ account.initAccountServices(this);
+ databaseBackend.createAccount(account);
+ this.accounts.add(account);
+ this.reconnectAccountInBackground(account);
+ updateAccountUi();
+ }
+
+ public void createAccountFromKey(final String alias, final OnAccountCreated callback) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias);
+ Pair<Jid, String> info = CryptoHelper.extractJidAndName(chain[0]);
+ if (findAccountByJid(info.first) == null) {
+ Account account = new Account(info.first, "");
+ account.setPrivateKeyAlias(alias);
+ account.setOption(Account.OPTION_DISABLED, true);
+ account.setDisplayName(info.second);
+ createAccount(account);
+ callback.onAccountCreated(account);
+ if (Config.X509_VERIFICATION) {
+ try {
+ getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA");
+ } catch (CertificateException e) {
+ callback.informUser(R.string.certificate_chain_is_not_trusted);
+ }
+ }
+ } else {
+ callback.informUser(R.string.account_already_exists);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ callback.informUser(R.string.unable_to_parse_certificate);
+ }
+ }
+ }).start();
+
+ }
+
+ public void updateKeyInAccount(final Account account, final String alias) {
+ Log.d(Config.LOGTAG, "update key in account " + alias);
+ try {
+ X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias);
+ Pair<Jid, String> info = CryptoHelper.extractJidAndName(chain[0]);
+ if (account.getJid().toBareJid().equals(info.first)) {
+ account.setPrivateKeyAlias(alias);
+ account.setDisplayName(info.second);
+ databaseBackend.updateAccount(account);
+ if (Config.X509_VERIFICATION) {
+ try {
+ getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA");
+ } catch (CertificateException e) {
+ showErrorToastInUi(R.string.certificate_chain_is_not_trusted);
+ }
+ account.getAxolotlService().regenerateKeys(true);
+ }
+ } else {
+ showErrorToastInUi(R.string.jid_does_not_match_certificate);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public boolean updateAccount(final Account account) {
+ if (databaseBackend.updateAccount(account)) {
+ account.setShowErrorNotification(true);
+ this.statusListener.onStatusChanged(account);
+ databaseBackend.updateAccount(account);
+ reconnectAccountInBackground(account);
+ updateAccountUi();
+ getNotificationService().updateErrorNotification();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void updateAccountPasswordOnServer(final Account account, final String newPassword, final OnAccountPasswordChanged callback) {
+ final IqPacket iq = getIqGenerator().generateSetPassword(account, newPassword);
+ sendIqPacket(account, iq, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ account.setPassword(newPassword);
+ account.setOption(Account.OPTION_MAGIC_CREATE, false);
+ databaseBackend.updateAccount(account);
+ callback.onPasswordChangeSucceeded();
+ } else {
+ callback.onPasswordChangeFailed();
+ }
+ }
+ });
+ }
+
+ public void deleteAccount(final Account account) {
+ synchronized (this.conversations) {
+ for (final Conversation conversation : conversations) {
+ if (conversation.getAccount() == account) {
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ leaveMuc(conversation);
+ } else if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ conversation.endOtrIfNeeded();
+ }
+ conversations.remove(conversation);
+ }
+ }
+ if (account.getXmppConnection() != null) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ disconnect(account, true);
+ }
+ }).start();
+ }
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ if (!databaseBackend.deleteAccount(account)) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": unable to delete account");
+ }
+ }
+ };
+ mDatabaseExecutor.execute(runnable);
+ this.accounts.remove(account);
+ updateAccountUi();
+ getNotificationService().updateErrorNotification();
+ }
+ }
+
+ public void setOnConversationListChangedListener(OnConversationUpdate listener) {
+ synchronized (this) {
+ this.mLastActivity = System.currentTimeMillis();
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnConversationUpdate = listener;
+ this.mNotificationService.setIsInForeground(true);
+ if (this.convChangedListenerCount < 2) {
+ this.convChangedListenerCount++;
+ }
+ }
+ }
+
+ public void removeOnConversationListChangedListener() {
+ synchronized (this) {
+ this.convChangedListenerCount--;
+ if (this.convChangedListenerCount <= 0) {
+ this.convChangedListenerCount = 0;
+ this.mOnConversationUpdate = null;
+ this.mNotificationService.setIsInForeground(false);
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
+ public void setOnShowErrorToastListener(OnShowErrorToast onShowErrorToast) {
+ synchronized (this) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnShowErrorToast = onShowErrorToast;
+ if (this.showErrorToastListenerCount < 2) {
+ this.showErrorToastListenerCount++;
+ }
+ }
+ this.mOnShowErrorToast = onShowErrorToast;
+ }
+
+ public void removeOnShowErrorToastListener() {
+ synchronized (this) {
+ this.showErrorToastListenerCount--;
+ if (this.showErrorToastListenerCount <= 0) {
+ this.showErrorToastListenerCount = 0;
+ this.mOnShowErrorToast = null;
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
+ public void setOnAccountListChangedListener(OnAccountUpdate listener) {
+ synchronized (this) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnAccountUpdate = listener;
+ if (this.accountChangedListenerCount < 2) {
+ this.accountChangedListenerCount++;
+ }
+ }
+ }
+
+ public void removeOnAccountListChangedListener() {
+ synchronized (this) {
+ this.accountChangedListenerCount--;
+ if (this.accountChangedListenerCount <= 0) {
+ this.mOnAccountUpdate = null;
+ this.accountChangedListenerCount = 0;
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
+ public void setOnCaptchaRequestedListener(OnCaptchaRequested listener) {
+ synchronized (this) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnCaptchaRequested = listener;
+ if (this.captchaRequestedListenerCount < 2) {
+ this.captchaRequestedListenerCount++;
+ }
+ }
+ }
+
+ public void removeOnCaptchaRequestedListener() {
+ synchronized (this) {
+ this.captchaRequestedListenerCount--;
+ if (this.captchaRequestedListenerCount <= 0) {
+ this.mOnCaptchaRequested = null;
+ this.captchaRequestedListenerCount = 0;
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
+ public void setOnRosterUpdateListener(final OnRosterUpdate listener) {
+ synchronized (this) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnRosterUpdate = listener;
+ if (this.rosterChangedListenerCount < 2) {
+ this.rosterChangedListenerCount++;
+ }
+ }
+ }
+
+ public void removeOnRosterUpdateListener() {
+ synchronized (this) {
+ this.rosterChangedListenerCount--;
+ if (this.rosterChangedListenerCount <= 0) {
+ this.rosterChangedListenerCount = 0;
+ this.mOnRosterUpdate = null;
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
+ public void setOnUpdateBlocklistListener(final OnUpdateBlocklist listener) {
+ synchronized (this) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnUpdateBlocklist = listener;
+ if (this.updateBlocklistListenerCount < 2) {
+ this.updateBlocklistListenerCount++;
+ }
+ }
+ }
+
+ public void removeOnUpdateBlocklistListener() {
+ synchronized (this) {
+ this.updateBlocklistListenerCount--;
+ if (this.updateBlocklistListenerCount <= 0) {
+ this.updateBlocklistListenerCount = 0;
+ this.mOnUpdateBlocklist = null;
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
+ public void setOnKeyStatusUpdatedListener(final OnKeyStatusUpdated listener) {
+ synchronized (this) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnKeyStatusUpdated = listener;
+ if (this.keyStatusUpdatedListenerCount < 2) {
+ this.keyStatusUpdatedListenerCount++;
+ }
+ }
+ }
+
+ public void removeOnNewKeysAvailableListener() {
+ synchronized (this) {
+ this.keyStatusUpdatedListenerCount--;
+ if (this.keyStatusUpdatedListenerCount <= 0) {
+ this.keyStatusUpdatedListenerCount = 0;
+ this.mOnKeyStatusUpdated = null;
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
+ public void setOnMucRosterUpdateListener(OnMucRosterUpdate listener) {
+ synchronized (this) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnMucRosterUpdate = listener;
+ if (this.mucRosterChangedListenerCount < 2) {
+ this.mucRosterChangedListenerCount++;
+ }
+ }
+ }
+
+ public void removeOnMucRosterUpdateListener() {
+ synchronized (this) {
+ this.mucRosterChangedListenerCount--;
+ if (this.mucRosterChangedListenerCount <= 0) {
+ this.mucRosterChangedListenerCount = 0;
+ this.mOnMucRosterUpdate = null;
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
+ public boolean checkListeners() {
+ return (this.mOnAccountUpdate == null
+ && this.mOnConversationUpdate == null
+ && this.mOnRosterUpdate == null
+ && this.mOnCaptchaRequested == null
+ && this.mOnUpdateBlocklist == null
+ && this.mOnShowErrorToast == null
+ && this.mOnKeyStatusUpdated == null);
+ }
+
+ private void switchToForeground() {
+ final boolean broadcastLastActivity = broadcastLastActivity();
+ for (Conversation conversation : getConversations()) {
+ conversation.setIncomingChatState(ChatState.ACTIVE);
+ }
+ for (Account account : getAccounts()) {
+ if (account.getStatus() == Account.State.ONLINE) {
+ account.deactivateGracePeriod();
+ final XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ if (connection.getFeatures().csi()) {
+ connection.sendActive();
+ }
+ if (broadcastLastActivity) {
+ sendPresence(account, false); //send new presence but don't include idle because we are not
+ }
+ }
+ }
+ }
+ Log.d(Config.LOGTAG, "app switched into foreground");
+ }
+
+ private void switchToBackground() {
+ final boolean broadcastLastActivity = broadcastLastActivity();
+ for (Account account : getAccounts()) {
+ if (account.getStatus() == Account.State.ONLINE) {
+ XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ if (broadcastLastActivity) {
+ sendPresence(account, broadcastLastActivity);
+ }
+ if (connection.getFeatures().csi()) {
+ connection.sendInactive();
+ }
+ }
+ }
+ }
+ this.mNotificationService.setIsInForeground(false);
+ Log.d(Config.LOGTAG, "app switched into background");
+ }
+
+ private void connectMultiModeConversations(Account account) {
+ List<Conversation> conversations = getConversations();
+ for (Conversation conversation : conversations) {
+ if (conversation.getMode() == Conversation.MODE_MULTI && conversation.getAccount() == account) {
+ joinMuc(conversation);
+ }
+ }
+ }
+
+ public void joinMuc(Conversation conversation) {
+ joinMuc(conversation, null);
+ }
+
+ private void joinMuc(Conversation conversation, final OnConferenceJoined onConferenceJoined) {
+ Account account = conversation.getAccount();
+ account.pendingConferenceJoins.remove(conversation);
+ account.pendingConferenceLeaves.remove(conversation);
+ if (account.getStatus() == Account.State.ONLINE) {
+ conversation.resetMucOptions();
+ if (onConferenceJoined != null) {
+ conversation.getMucOptions().flagNoAutoPushConfiguration();
+ }
+ conversation.setHasMessagesLeftOnServer(false);
+ fetchConferenceConfiguration(conversation, new OnConferenceConfigurationFetched() {
+
+ private void join(Conversation conversation) {
+ Account account = conversation.getAccount();
+ 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, mucOptions.nonanonymous());
+ packet.setTo(joinJid);
+ Element x = packet.addChild("x", "http://jabber.org/protocol/muc");
+ if (conversation.getMucOptions().getPassword() != null) {
+ x.addChild("password").setContent(mucOptions.getPassword());
+ }
+
+ if (mucOptions.mamSupport()) {
+ // Use MAM instead of the limited muc history to get history
+ x.addChild("history").setAttribute("maxchars", "0");
+ } else {
+ // Fallback to muc history
+ x.addChild("history").setAttribute("since", PresenceGenerator.getTimestamp(conversation.getLastMessageTransmitted()));
+ }
+ sendPresencePacket(account, packet);
+ if (onConferenceJoined != null) {
+ onConferenceJoined.onConferenceJoined(conversation);
+ }
+ if (!joinJid.equals(conversation.getJid())) {
+ conversation.setContactJid(joinJid);
+ databaseBackend.updateConversation(conversation);
+ }
+
+ if (mucOptions.mamSupport()) {
+ getMessageArchiveService().catchupMUC(conversation);
+ }
+ if (mucOptions.membersOnly() && mucOptions.nonanonymous()) {
+ fetchConferenceMembers(conversation);
+ }
+ sendUnsentMessages(conversation);
+ }
+
+ @Override
+ public void onConferenceConfigurationFetched(Conversation conversation) {
+ join(conversation);
+ }
+
+ @Override
+ public void onFetchFailed(final Conversation conversation, Element error) {
+ if (error != null && "remote-server-not-found".equals(error.getName())) {
+ conversation.getMucOptions().setError(MucOptions.Error.SERVER_NOT_FOUND);
+ } else {
+ join(conversation);
+ fetchConferenceConfiguration(conversation);
+ }
+ }
+ });
+ updateConversationUi();
+ } else {
+ account.pendingConferenceJoins.add(conversation);
+ conversation.resetMucOptions();
+ conversation.setHasMessagesLeftOnServer(false);
+ updateConversationUi();
+ }
+ }
+
+ private void fetchConferenceMembers(final Conversation conversation) {
+ final Account account = conversation.getAccount();
+ final String[] affiliations = {"member", "admin", "owner"};
+ OnIqPacketReceived callback = new OnIqPacketReceived() {
+
+ private int i = 0;
+
+ @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()) {
+ if ("item".equals(child.getName())) {
+ MucOptions.User user = AbstractParser.parseItem(conversation, child);
+ if (!user.realJidMatchesAccount()) {
+ conversation.getMucOptions().updateUser(user);
+ }
+ }
+ }
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not request affiliation " + affiliations[i] + " in " + conversation.getJid().toBareJid());
+ }
+ ++i;
+ if (i >= affiliations.length) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": retrieved members for " + conversation.getJid().toBareJid() + ": " + conversation.getMucOptions().getMembers());
+ getAvatarService().clear(conversation);
+ updateMucRosterUi();
+ updateConversationUi();
+ }
+ }
+ };
+ for (String affiliation : affiliations) {
+ sendIqPacket(account, mIqGenerator.queryAffiliation(conversation, affiliation), callback);
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching members for " + conversation.getName());
+ }
+
+ public void providePasswordForMuc(Conversation conversation, String password) {
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ conversation.getMucOptions().setPassword(password);
+ if (conversation.getBookmark() != null) {
+ if (respectAutojoin()) {
+ conversation.getBookmark().setAutojoin(true);
+ }
+ pushBookmarks(conversation.getAccount());
+ }
+ updateConversation(conversation);
+ joinMuc(conversation);
+ }
+ }
+
+ public void renameInMuc(final Conversation conversation, final String nick, final UiCallback<Conversation> callback) {
+ final MucOptions options = conversation.getMucOptions();
+ final Jid joinJid = options.createJoinJid(nick);
+ if (options.online()) {
+ Account account = conversation.getAccount();
+ options.setOnRenameListener(new OnRenameListener() {
+
+ @Override
+ public void onSuccess() {
+ conversation.setContactJid(joinJid);
+ databaseBackend.updateConversation(conversation);
+ Bookmark bookmark = conversation.getBookmark();
+ if (bookmark != null) {
+ bookmark.setNick(nick);
+ pushBookmarks(bookmark.getAccount());
+ }
+ callback.success(conversation);
+ }
+
+ @Override
+ public void onFailure() {
+ callback.error(R.string.nick_in_use, conversation);
+ }
+ });
+
+ PresencePacket packet = new PresencePacket();
+ packet.setTo(joinJid);
+ packet.setFrom(conversation.getAccount().getJid());
+
+ String sig = account.getPgpSignature();
+ if (sig != null) {
+ packet.addChild("status").setContent("online");
+ packet.addChild("x", "jabber:x:signed").setContent(sig);
+ }
+ sendPresencePacket(account, packet);
+ } else {
+ conversation.setContactJid(joinJid);
+ databaseBackend.updateConversation(conversation);
+ if (conversation.getAccount().getStatus() == Account.State.ONLINE) {
+ Bookmark bookmark = conversation.getBookmark();
+ if (bookmark != null) {
+ bookmark.setNick(nick);
+ pushBookmarks(bookmark.getAccount());
+ }
+ joinMuc(conversation);
+ }
+ }
+ }
+
+ public void leaveMuc(Conversation conversation) {
+ leaveMuc(conversation, false);
+ }
+
+ private void leaveMuc(Conversation conversation, boolean now) {
+ Account account = conversation.getAccount();
+ account.pendingConferenceJoins.remove(conversation);
+ account.pendingConferenceLeaves.remove(conversation);
+ if (account.getStatus() == Account.State.ONLINE || now) {
+ PresencePacket packet = new PresencePacket();
+ packet.setTo(conversation.getMucOptions().getSelf().getFullJid());
+ packet.setFrom(conversation.getAccount().getJid());
+ packet.setAttribute("type", "unavailable");
+ sendPresencePacket(conversation.getAccount(), packet);
+ conversation.getMucOptions().setOffline();
+ conversation.deregisterWithBookmark();
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid()
+ + ": leaving muc " + conversation.getJid());
+ } else {
+ account.pendingConferenceLeaves.add(conversation);
+ }
+ }
+
+ private String findConferenceServer(final Account account) {
+ String server;
+ if (account.getXmppConnection() != null) {
+ server = account.getXmppConnection().getMucServer();
+ if (server != null) {
+ return server;
+ }
+ }
+ for (Account other : getAccounts()) {
+ if (other != account && other.getXmppConnection() != null) {
+ server = other.getXmppConnection().getMucServer();
+ if (server != null) {
+ return server;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void createAdhocConference(final Account account,
+ final String subject,
+ final Iterable<Jid> jids,
+ final UiCallback<Conversation> callback) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": creating adhoc conference with " + jids.toString());
+ if (account.getStatus() == Account.State.ONLINE) {
+ try {
+ String server = findConferenceServer(account);
+ if (server == null) {
+ if (callback != null) {
+ callback.error(R.string.no_conference_server_found, null);
+ }
+ return;
+ }
+ final Jid jid = Jid.fromParts(new BigInteger(64, getRNG()).toString(Character.MAX_RADIX), server, null);
+ final Conversation conversation = findOrCreateConversation(account, jid, true);
+ joinMuc(conversation, new OnConferenceJoined() {
+ @Override
+ public void onConferenceJoined(final Conversation conversation) {
+ pushConferenceConfiguration(conversation, IqGenerator.defaultRoomConfiguration(), new OnConferenceOptionsPushed() {
+ @Override
+ public void onPushSucceeded() {
+ if (subject != null && !subject.trim().isEmpty()) {
+ pushSubjectToConference(conversation, subject.trim());
+ }
+ for (Jid invite : jids) {
+ invite(conversation, invite);
+ }
+ if (account.countPresences() > 1) {
+ directInvite(conversation, account.getJid().toBareJid());
+ }
+ saveConversationAsBookmark(conversation, subject);
+ if (callback != null) {
+ callback.success(conversation);
+ }
+ }
+
+ @Override
+ public void onPushFailed() {
+ archiveConversation(conversation);
+ if (callback != null) {
+ callback.error(R.string.conference_creation_failed, conversation);
+ }
+ }
+ });
+ }
+ });
+ } catch (InvalidJidException e) {
+ if (callback != null) {
+ callback.error(R.string.conference_creation_failed, null);
+ }
+ }
+ } else {
+ if (callback != null) {
+ callback.error(R.string.not_connected_try_again, null);
+ }
+ }
+ }
+
+ public void fetchConferenceConfiguration(final Conversation conversation) {
+ fetchConferenceConfiguration(conversation, null);
+ }
+
+ public void fetchConferenceConfiguration(final Conversation conversation, final OnConferenceConfigurationFetched callback) {
+ IqPacket request = new IqPacket(IqPacket.TYPE.GET);
+ request.setTo(conversation.getJid().toBareJid());
+ request.query("http://jabber.org/protocol/disco#info");
+ sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Element query = packet.findChild("query", "http://jabber.org/protocol/disco#info");
+ if (packet.getType() == IqPacket.TYPE.RESULT && query != null) {
+ ArrayList<String> features = new ArrayList<>();
+ for (Element child : query.getChildren()) {
+ if (child != null && child.getName().equals("feature")) {
+ String var = child.getAttribute("var");
+ if (var != null) {
+ features.add(var);
+ }
+ }
+ }
+ Element form = query.findChild("x", "jabber:x:data");
+ if (form != null) {
+ conversation.getMucOptions().updateFormData(Data.parse(form));
+ }
+ conversation.getMucOptions().updateFeatures(features);
+ if (callback != null) {
+ callback.onConferenceConfigurationFetched(conversation);
+ }
+ 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) {
+ callback.onFetchFailed(conversation, packet.getError());
+ }
+ }
+ }
+ });
+ }
+
+ public void pushConferenceConfiguration(final Conversation conversation, final Bundle options, final OnConferenceOptionsPushed callback) {
+ IqPacket request = new IqPacket(IqPacket.TYPE.GET);
+ request.setTo(conversation.getJid().toBareJid());
+ request.query("http://jabber.org/protocol/muc#owner");
+ sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ Data data = Data.parse(packet.query().findChild("x", "jabber:x:data"));
+ for (Field field : data.getFields()) {
+ if (options.containsKey(field.getFieldName())) {
+ field.setValue(options.getString(field.getFieldName()));
+ }
+ }
+ data.submit();
+ IqPacket set = new IqPacket(IqPacket.TYPE.SET);
+ set.setTo(conversation.getJid().toBareJid());
+ set.query("http://jabber.org/protocol/muc#owner").addChild(data);
+ sendIqPacket(account, set, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (callback != null) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ callback.onPushSucceeded();
+ } else {
+ callback.onPushFailed();
+ }
+ }
+ }
+ });
+ } else {
+ if (callback != null) {
+ callback.onPushFailed();
+ }
+ }
+ }
+ });
+ }
+
+ public void pushSubjectToConference(final Conversation conference, final String subject) {
+ MessagePacket packet = this.getMessageGenerator().conferenceSubject(conference, subject);
+ this.sendMessagePacket(conference.getAccount(), packet);
+ final MucOptions mucOptions = conference.getMucOptions();
+ final MucOptions.User self = mucOptions.getSelf();
+ if (!mucOptions.persistent() && self.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
+ Bundle options = new Bundle();
+ options.putString("muc#roomconfig_persistentroom", "1");
+ this.pushConferenceConfiguration(conference, options, null);
+ }
+ }
+
+ public void changeAffiliationInConference(final Conversation conference, Jid user, final MucOptions.Affiliation affiliation, final OnAffiliationChanged callback) {
+ final Jid jid = user.toBareJid();
+ IqPacket request = this.mIqGenerator.changeAffiliation(conference, jid, affiliation.toString());
+ sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ conference.getMucOptions().changeAffiliation(jid, affiliation);
+ getAvatarService().clear(conference);
+ callback.onAffiliationChangedSuccessful(jid);
+ } else {
+ callback.onAffiliationChangeFailed(jid, R.string.could_not_change_affiliation);
+ }
+ }
+ });
+ }
+
+ public void changeAffiliationsInConference(final Conversation conference, MucOptions.Affiliation before, MucOptions.Affiliation after) {
+ List<Jid> jids = new ArrayList<>();
+ for (MucOptions.User user : conference.getMucOptions().getUsers()) {
+ if (user.getAffiliation() == before && user.getRealJid() != null) {
+ jids.add(user.getRealJid());
+ }
+ }
+ IqPacket request = this.mIqGenerator.changeAffiliation(conference, jids, after.toString());
+ sendIqPacket(conference.getAccount(), request, mDefaultIqHandler);
+ }
+
+ 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());
+ sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Log.d(Config.LOGTAG, packet.toString());
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ callback.onRoleChangedSuccessful(nick);
+ } else {
+ callback.onRoleChangeFailed(nick, R.string.could_not_change_role);
+ }
+ }
+ });
+ }
+
+ private void disconnect(Account account, boolean force) {
+ if ((account.getStatus() == Account.State.ONLINE)
+ || (account.getStatus() == Account.State.DISABLED)) {
+ final XmppConnection connection = account.getXmppConnection();
+ if (!force) {
+ List<Conversation> conversations = getConversations();
+ for (Conversation conversation : conversations) {
+ if (conversation.getAccount() == account) {
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ leaveMuc(conversation, true);
+ } else {
+ if (conversation.endOtrIfNeeded()) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ + ": ended otr session with "
+ + conversation.getJid());
+ }
+ }
+ }
+ }
+ sendOfflinePresence(account);
+ }
+ connection.disconnect(force);
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ public void updateMessage(Message message) {
+ databaseBackend.updateMessage(message);
+ 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)) {
+ pushContactToServer(contact);
+ }
+ if (contact.getOption(Contact.Options.DIRTY_DELETE)) {
+ deleteContactOnServer(contact);
+ }
+ }
+ }
+
+ public void createContact(Contact contact) {
+ boolean autoGrant = getPreferences().getBoolean("grant_new_contacts", true);
+ if (autoGrant) {
+ contact.setOption(Contact.Options.PREEMPTIVE_GRANT);
+ contact.setOption(Contact.Options.ASKING);
+ }
+ pushContactToServer(contact);
+ }
+
+ public void onOtrSessionEstablished(Conversation conversation) {
+ final Account account = conversation.getAccount();
+ final Session otrSession = conversation.getOtrSession();
+ Log.d(Config.LOGTAG,
+ account.getJid().toBareJid() + " otr session established with "
+ + conversation.getJid() + "/"
+ + otrSession.getSessionID().getUserID());
+ conversation.findUnsentMessagesWithEncryption(Message.ENCRYPTION_OTR, new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
+ SessionID id = otrSession.getSessionID();
+ try {
+ message.setCounterpart(Jid.fromString(id.getAccountID() + "/" + id.getUserID()));
+ } catch (InvalidJidException e) {
+ return;
+ }
+ if (message.needsUploading()) {
+ mJingleConnectionManager.createNewConnection(message);
+ } else {
+ MessagePacket outPacket = mMessageGenerator.generateOtrChat(message);
+ if (outPacket != null) {
+ mMessageGenerator.addDelay(outPacket, message.getTimeSent());
+ message.setStatus(Message.STATUS_SEND);
+ databaseBackend.updateMessage(message);
+ sendMessagePacket(account, outPacket);
+ }
+ }
+ updateConversationUi();
+ }
+ });
+ }
+
+ public boolean renewSymmetricKey(Conversation conversation) {
+ Account account = conversation.getAccount();
+ byte[] symmetricKey = new byte[32];
+ this.mRandom.nextBytes(symmetricKey);
+ Session otrSession = conversation.getOtrSession();
+ if (otrSession != null) {
+ MessagePacket packet = new MessagePacket();
+ packet.setType(MessagePacket.TYPE_CHAT);
+ packet.setFrom(account.getJid());
+ MessageGenerator.addMessageHints(packet);
+ packet.setAttribute("to", otrSession.getSessionID().getAccountID() + "/"
+ + otrSession.getSessionID().getUserID());
+ try {
+ packet.setBody(otrSession
+ .transformSending(CryptoHelper.FILETRANSFER
+ + CryptoHelper.bytesToHex(symmetricKey))[0]);
+ sendMessagePacket(account, packet);
+ conversation.setSymmetricKey(symmetricKey);
+ return true;
+ } catch (OtrException e) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ public void pushContactToServer(final Contact contact) {
+ contact.resetOption(Contact.Options.DIRTY_DELETE);
+ contact.setOption(Contact.Options.DIRTY_PUSH);
+ final Account account = contact.getAccount();
+ if (account.getStatus() == Account.State.ONLINE) {
+ final boolean ask = contact.getOption(Contact.Options.ASKING);
+ final boolean sendUpdates = contact
+ .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)
+ && contact.getOption(Contact.Options.PREEMPTIVE_GRANT);
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ iq.query(Xmlns.ROSTER).addChild(contact.asElement());
+ account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler);
+ if (sendUpdates) {
+ sendPresencePacket(account,
+ mPresenceGenerator.sendPresenceUpdatesTo(contact));
+ }
+ if (ask) {
+ sendPresencePacket(account,
+ mPresenceGenerator.requestPresenceUpdatesFrom(contact));
+ }
+ }
+ }
+
+ 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()) {
+ if (account.getJid().toBareJid().equals(avatar.owner) && account.getAvatar() == null) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": had no avatar. replacing with vcard");
+ account.setAvatar(avatar.getFilename());
+ databaseBackend.updateAccount(account);
+ getAvatarService().clear(account);
+ updateAccountUi();
+ } else {
+ Contact contact = account.getRoster().getContact(avatar.owner);
+ contact.setAvatar(avatar);
+ getAvatarService().clear(contact);
+ updateRosterUi();
+ }
+ updateConversationUi();
+ } 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);
+ contact.setOption(Contact.Options.DIRTY_DELETE);
+ Account account = contact.getAccount();
+ if (account.getStatus() == Account.State.ONLINE) {
+ IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ Element item = iq.query(Xmlns.ROSTER).addChild("item");
+ item.setAttribute("jid", contact.getJid().toString());
+ item.setAttribute("subscription", "remove");
+ account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler);
+ }
+ }
+
+ public void updateConversation(final Conversation conversation) {
+ mDatabaseExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ databaseBackend.updateConversation(conversation);
+ }
+ });
+ }
+
+ private void reconnectAccount(final Account account, final boolean force, final boolean interactive) {
+ synchronized (account) {
+ XmppConnection connection = account.getXmppConnection();
+ if (connection == null) {
+ connection = createConnection(account);
+ account.setXmppConnection(connection);
+ } else {
+ connection.interrupt();
+ }
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ if (!force) {
+ disconnect(account, false);
+ }
+ Thread thread = new Thread(connection);
+ connection.setInteractive(interactive);
+ connection.prepareNewConnection();
+ thread.start();
+ scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode());
+ } else {
+ disconnect(account, force);
+ account.getRoster().clearPresences();
+ connection.resetEverything();
+ account.getAxolotlService().resetBrokenness();
+ }
+ }
+ }
+
+ public void reconnectAccountInBackground(final Account account) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ reconnectAccount(account, false, true);
+ }
+ }).start();
+ }
+
+ public void invite(Conversation conversation, Jid contact) {
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": inviting " + contact + " to " + conversation.getJid().toBareJid());
+ MessagePacket packet = mMessageGenerator.invite(conversation, contact);
+ sendMessagePacket(conversation.getAccount(), packet);
+ }
+
+ public void directInvite(Conversation conversation, Jid jid) {
+ MessagePacket packet = mMessageGenerator.directInvite(conversation, jid);
+ sendMessagePacket(conversation.getAccount(), packet);
+ }
+
+ public void resetSendingToWaiting(Account account) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": reset 'sending' messages to 'waiting'");
+ for (Conversation conversation : getConversations()) {
+ if (conversation.getAccount() == account) {
+ conversation.findUnsentTextMessages(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
+ markMessage(message, Message.STATUS_WAITING);
+ }
+ });
+ }
+ }
+ }
+
+ public Message markMessage(final Account account, final Jid recipient, final String uuid, final int status) {
+ return markMessage(account, recipient, uuid, status, null);
+ }
+
+ public Message markMessage(final Account account, final Jid recipient, final String uuid, final int status, String errorMessage) {
+ if (uuid == null) {
+ return null;
+ }
+ for (Conversation conversation : getConversations()) {
+ if (conversation.getJid().toBareJid().equals(recipient) && conversation.getAccount() == account) {
+ final Message message = conversation.findSentMessageWithUuidOrRemoteId(uuid);
+ if (message != null) {
+ markMessage(message, status, errorMessage);
+ }
+ return message;
+ }
+ }
+ 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) {
+ markMessage(message, status, null);
+ }
+
+ public void markMessage(Message message, int status, String errorMessage) {
+ if (status == Message.STATUS_SEND_FAILED
+ && (message.getStatus() == Message.STATUS_SEND_RECEIVED || message
+ .getStatus() == Message.STATUS_SEND_DISPLAYED)) {
+ return;
+ }
+ message.setErrorMessage(errorMessage);
+ 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", true);
+ }
+
+ public boolean sendChatStates() {
+ return getPreferences().getBoolean("chat_states", true);
+ }
+
+ 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", true);
+ }
+
+ public boolean useTorToConnect() {
+ return Config.FORCE_ORBOT || getPreferences().getBoolean("use_tor", false);
+ }
+
+ public boolean showExtendedConnectionOptions() {
+ return getPreferences().getBoolean("show_connection_options", false);
+ }
+
+ public boolean broadcastLastActivity() {
+ return getPreferences().getBoolean("last_activity", true);
+ }
+
+ public int unreadCount() {
+ int count = 0;
+ for (Conversation conversation : getConversations()) {
+ count += conversation.unreadCount();
+ }
+ return count;
+ }
+
+ public void vibrate() {
+ Log.d(Config.LOGTAG, "Notification: short vibrate");
+ Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
+ vibrator.vibrate(100);
+ }
+
+
+ public void showErrorToastInUi(int resId) {
+ if (mOnShowErrorToast != null) {
+ mOnShowErrorToast.onShowErrorToast(resId);
+ }
+ }
+
+ public void updateConversationUi() {
+ if (mOnConversationUpdate != null) {
+ mOnConversationUpdate.onConversationUpdate();
+ }
+ }
+
+ public void updateAccountUi() {
+ if (mOnAccountUpdate != null) {
+ mOnAccountUpdate.onAccountUpdate();
+ }
+ }
+
+ public void updateRosterUi() {
+ if (mOnRosterUpdate != null) {
+ mOnRosterUpdate.onRosterUpdate();
+ }
+ }
+
+ public boolean displayCaptchaRequest(Account account, String id, Data data, Bitmap captcha) {
+ if (mOnCaptchaRequested != null) {
+ DisplayMetrics metrics = getApplicationContext().getResources().getDisplayMetrics();
+ Bitmap scaled = Bitmap.createScaledBitmap(captcha, (int) (captcha.getWidth() * metrics.scaledDensity),
+ (int) (captcha.getHeight() * metrics.scaledDensity), false);
+
+ mOnCaptchaRequested.onCaptchaRequested(account, id, data, scaled);
+ return true;
+ }
+ return false;
+ }
+
+ public void updateBlocklistUi(final OnUpdateBlocklist.Status status) {
+ if (mOnUpdateBlocklist != null) {
+ mOnUpdateBlocklist.OnUpdateBlocklist(status);
+ }
+ }
+
+ public void updateMucRosterUi() {
+ if (mOnMucRosterUpdate != null) {
+ mOnMucRosterUpdate.onMucRosterUpdate();
+ }
+ }
+
+ public void keyStatusUpdated(AxolotlService.FetchStatus report) {
+ if (mOnKeyStatusUpdated != null) {
+ mOnKeyStatusUpdated.onKeyStatusUpdated(report);
+ }
+ }
+
+ public Account findAccountByJid(final Jid accountJid) {
+ for (Account account : this.accounts) {
+ if (account.getJid().toBareJid().equals(accountJid.toBareJid())) {
+ return account;
+ }
+ }
+ return null;
+ }
+
+ public Conversation findConversationByUuid(String uuid) {
+ for (Conversation conversation : getConversations()) {
+ if (conversation.getUuid().equals(uuid)) {
+ return conversation;
+ }
+ }
+ return null;
+ }
+
+ public boolean markRead(final Conversation conversation) {
+ return markRead(conversation, true);
+ }
+
+ public boolean markRead(final Conversation conversation, boolean clear) {
+ if (clear) {
+ mNotificationService.clear(conversation);
+ }
+ final List<Message> readMessages = conversation.markRead();
+ if (readMessages.size() > 0) {
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ for (Message message : readMessages) {
+ databaseBackend.updateMessage(message);
+ }
+ }
+ };
+ mDatabaseExecutor.execute(runnable);
+ updateUnreadCountBadge();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public synchronized void updateUnreadCountBadge() {
+ int count = unreadCount();
+ if (unreadCount != count) {
+ Log.d(Config.LOGTAG, "update unread count to " + count);
+ if (count > 0) {
+ ShortcutBadger.applyCount(getApplicationContext(), count);
+ } else {
+ ShortcutBadger.removeCount(getApplicationContext());
+ }
+ unreadCount = count;
+ }
+ }
+
+ public void sendReadMarker(final Conversation conversation) {
+ final Message markable = conversation.getLatestMarkableMessage();
+ if (this.markRead(conversation)) {
+ updateConversationUi();
+ }
+ if (confirmMessages() && markable != null && markable.trusted() && markable.getRemoteMsgId() != null) {
+ Log.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());
+ this.sendMessagePacket(conversation.getAccount(), packet);
+ }
+ }
+
+ public SecureRandom getRNG() {
+ return this.mRandom;
+ }
+
+ public MemorizingTrustManager getMemorizingTrustManager() {
+ return this.mMemorizingTrustManager;
+ }
+
+ public void setMemorizingTrustManager(MemorizingTrustManager trustManager) {
+ this.mMemorizingTrustManager = trustManager;
+ }
+
+ public void updateMemorizingTrustmanager() {
+ final MemorizingTrustManager tm;
+ final boolean dontTrustSystemCAs = getPreferences().getBoolean("dont_trust_system_cas", false);
+ if (dontTrustSystemCAs) {
+ tm = new MemorizingTrustManager(getApplicationContext(), null);
+ } else {
+ tm = new MemorizingTrustManager(getApplicationContext());
+ }
+ setMemorizingTrustManager(tm);
+ }
+
+ public PowerManager getPowerManager() {
+ return this.pm;
+ }
+
+ public LruCache<String, Bitmap> getBitmapCache() {
+ return this.mBitmapCache;
+ }
+
+ public void syncRosterToDisk(final Account account) {
+ Runnable runnable = new Runnable() {
+
+ @Override
+ public void run() {
+ databaseBackend.writeRoster(account.getRoster());
+ }
+ };
+ mDatabaseExecutor.execute(runnable);
+
+ }
+
+ public List<String> getKnownHosts() {
+ final List<String> hosts = new ArrayList<>();
+ for (final Account account : getAccounts()) {
+ if (!hosts.contains(account.getServer().toString())) {
+ hosts.add(account.getServer().toString());
+ }
+ for (final Contact contact : account.getRoster().getContacts()) {
+ if (contact.showInRoster()) {
+ final String server = contact.getServer().toString();
+ if (server != null && !hosts.contains(server)) {
+ hosts.add(server);
+ }
+ }
+ }
+ }
+ if (Config.DOMAIN_LOCK != null && !hosts.contains(Config.DOMAIN_LOCK)) {
+ hosts.add(Config.DOMAIN_LOCK);
+ }
+ if (Config.MAGIC_CREATE_DOMAIN != null && !hosts.contains(Config.MAGIC_CREATE_DOMAIN)) {
+ hosts.add(Config.MAGIC_CREATE_DOMAIN);
+ }
+ return hosts;
+ }
+
+ public List<String> getKnownConferenceHosts() {
+ final ArrayList<String> mucServers = new ArrayList<>();
+ for (final Account account : accounts) {
+ if (account.getXmppConnection() != null) {
+ final String server = account.getXmppConnection().getMucServer();
+ if (server != null && !mucServers.contains(server)) {
+ mucServers.add(server);
+ }
+ }
+ }
+ return mucServers;
+ }
+
+ public void sendMessagePacket(Account account, MessagePacket packet) {
+ XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ connection.sendMessagePacket(packet);
+ }
+ }
+
+ public void sendPresencePacket(Account account, PresencePacket packet) {
+ XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ connection.sendPresencePacket(packet);
+ }
+ }
+
+ public void sendCreateAccountWithCaptchaPacket(Account account, String id, Data data) {
+ final XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ IqPacket request = mIqGenerator.generateCreateAccountWithCaptcha(account, id, data);
+ connection.sendUnmodifiedIqPacket(request, connection.registrationResponseListener);
+ }
+ }
+
+ public void sendIqPacket(final Account account, final IqPacket packet, final OnIqPacketReceived callback) {
+ final XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ connection.sendIqPacket(packet, callback);
+ }
+ }
+
+ public void sendPresence(final Account account) {
+ sendPresence(account, checkListeners() && broadcastLastActivity());
+ }
+
+ private void sendPresence(final Account account, final boolean includeIdleTimestamp) {
+ PresencePacket packet;
+ if (manuallyChangePresence()) {
+ packet = mPresenceGenerator.selfPresence(account, account.getPresenceStatus());
+ String message = account.getPresenceStatusMessage();
+ if (message != null && !message.isEmpty()) {
+ packet.addChild(new Element("status").setContent(message));
+ }
+ } else {
+ packet = mPresenceGenerator.selfPresence(account, getTargetPresence());
+ }
+ if (mLastActivity > 0 && includeIdleTimestamp) {
+ long since = Math.min(mLastActivity, System.currentTimeMillis()); //don't send future dates
+ packet.addChild("idle", "urn:xmpp:idle:1").setAttribute("since", AbstractGenerator.getTimestamp(since));
+ }
+ sendPresencePacket(account, packet);
+ }
+
+ private void deactivateGracePeriod() {
+ for (Account account : getAccounts()) {
+ account.deactivateGracePeriod();
+ }
+ }
+
+ public void refreshAllPresences() {
+ boolean includeIdleTimestamp = checkListeners() && broadcastLastActivity();
+ for (Account account : getAccounts()) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ sendPresence(account, includeIdleTimestamp);
+ }
+ }
+ }
+
+ private void refreshAllGcmTokens() {
+ for (Account account : getAccounts()) {
+ if (account.isOnlineAndConnected() && mPushManagementService.available(account)) {
+ mPushManagementService.registerPushTokenOnServer(account);
+ }
+ }
+ }
+
+ private void sendOfflinePresence(final Account account) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": sending offline presence");
+ sendPresencePacket(account, mPresenceGenerator.sendOfflinePresence(account));
+ }
+
+ public MessageGenerator getMessageGenerator() {
+ return this.mMessageGenerator;
+ }
+
+ public PresenceGenerator getPresenceGenerator() {
+ return this.mPresenceGenerator;
+ }
+
+ public IqGenerator getIqGenerator() {
+ return this.mIqGenerator;
+ }
+
+ public IqParser getIqParser() {
+ return this.mIqParser;
+ }
+
+ public JingleConnectionManager getJingleConnectionManager() {
+ return this.mJingleConnectionManager;
+ }
+
+ public MessageArchiveService getMessageArchiveService() {
+ return this.mMessageArchiveService;
+ }
+
+ public List<Contact> findContacts(Jid jid) {
+ ArrayList<Contact> contacts = new ArrayList<>();
+ for (Account account : getAccounts()) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ Contact contact = account.getRoster().getContactFromRoster(jid);
+ if (contact != null) {
+ contacts.add(contact);
+ }
+ }
+ }
+ return contacts;
+ }
+
+ public Conversation findFirstMuc(Jid jid) {
+ for (Conversation conversation : getConversations()) {
+ if (conversation.getJid().toBareJid().equals(jid.toBareJid())
+ && conversation.getMode() == Conversation.MODE_MULTI) {
+ return conversation;
+ }
+ }
+ return null;
+ }
+
+ public NotificationService getNotificationService() {
+ return this.mNotificationService;
+ }
+
+ public HttpConnectionManager getHttpConnectionManager() {
+ return this.mHttpConnectionManager;
+ }
+
+ 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);
+ }
+ }
+
+ public void clearConversationHistory(final Conversation conversation) {
+ conversation.clearMessages();
+ conversation.setHasMessagesLeftOnServer(false); //avoid messages getting loaded through mam
+ conversation.setLastClearHistory(System.currentTimeMillis());
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ databaseBackend.deleteMessagesInConversation(conversation);
+ databaseBackend.updateConversation(conversation);
+
+ }
+ };
+ mDatabaseExecutor.execute(runnable);
+ }
+
+ public void sendBlockRequest(final Blockable blockable, boolean reportSpam) {
+ if (blockable != null && blockable.getBlockedJid() != null) {
+ final Jid jid = blockable.getBlockedJid();
+ this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetBlockRequest(jid, reportSpam), new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ account.getBlocklist().add(jid);
+ updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
+ }
+ }
+ });
+ }
+ }
+
+ public void sendUnblockRequest(final Blockable blockable) {
+ if (blockable != null && blockable.getJid() != null) {
+ final Jid jid = blockable.getBlockedJid();
+ this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetUnblockRequest(jid), new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ account.getBlocklist().remove(jid);
+ updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
+ }
+ }
+ });
+ }
+ }
+
+ public void publishDisplayName(Account account) {
+ String displayName = account.getDisplayName();
+ if (displayName != null && !displayName.isEmpty()) {
+ IqPacket publish = mIqGenerator.publishNick(displayName);
+ sendIqPacket(account, publish, new OnIqPacketReceived() {
+ @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");
+ }
+ }
+ });
+ }
+ }
+
+ public ServiceDiscoveryResult getCachedServiceDiscoveryResult(Pair<String, String> key) {
+ ServiceDiscoveryResult result = discoCache.get(key);
+ if (result != null) {
+ return result;
+ } else {
+ result = databaseBackend.findDiscoveryResult(key.first, key.second);
+ if (result != null) {
+ discoCache.put(key, result);
+ }
+ return result;
+ }
+ }
+
+ public void fetchCaps(Account account, final Jid jid, final Presence presence) {
+ final Pair<String, String> key = new Pair<>(presence.getHash(), presence.getVer());
+ ServiceDiscoveryResult disco = getCachedServiceDiscoveryResult(key);
+ if (disco != null) {
+ presence.setServiceDiscoveryResult(disco);
+ } else {
+ if (!account.inProgressDiscoFetches.contains(key)) {
+ account.inProgressDiscoFetches.add(key);
+ IqPacket request = new IqPacket(IqPacket.TYPE.GET);
+ request.setTo(jid);
+ request.query("http://jabber.org/protocol/disco#info");
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": making disco request for " + key.second + " to " + jid);
+ sendIqPacket(account, request, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket discoPacket) {
+ if (discoPacket.getType() == IqPacket.TYPE.RESULT) {
+ ServiceDiscoveryResult disco = new ServiceDiscoveryResult(discoPacket);
+ if (presence.getVer().equals(disco.getVer())) {
+ 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());
+ }
+ }
+ account.inProgressDiscoFetches.remove(key);
+ }
+ });
+ }
+ }
+ }
+
+ private void injectServiceDiscorveryResult(Roster roster, String hash, String ver, ServiceDiscoveryResult disco) {
+ for (Contact contact : roster.getContacts()) {
+ for (Presence presence : contact.getPresences().getPresences().values()) {
+ if (hash.equals(presence.getHash()) && ver.equals(presence.getVer())) {
+ presence.setServiceDiscoveryResult(disco);
+ }
+ }
+ }
+ }
+
+ public void fetchMamPreferences(Account account, final OnMamPreferencesFetched callback) {
+ IqPacket request = new IqPacket(IqPacket.TYPE.GET);
+ request.addChild("prefs", "urn:xmpp:mam:0");
+ sendIqPacket(account, request, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Element prefs = packet.findChild("prefs", "urn:xmpp:mam:0");
+ if (packet.getType() == IqPacket.TYPE.RESULT && prefs != null) {
+ callback.onPreferencesFetched(prefs);
+ } else {
+ callback.onPreferencesFetchFailed();
+ }
+ }
+ });
+ }
+
+ public PushManagementService getPushManagementService() {
+ return mPushManagementService;
+ }
+
+ public Account getPendingAccount() {
+ Account pending = null;
+ for (Account account : getAccounts()) {
+ if (account.isOptionSet(Account.OPTION_REGISTER)) {
+ pending = account;
+ } else {
+ return null;
+ }
+ }
+ return pending;
+ }
+
+ public void changeStatus(Account account, Presence.Status status, String statusMessage, boolean send) {
+ if (!statusMessage.isEmpty()) {
+ databaseBackend.insertPresenceTemplate(new PresenceTemplate(status, statusMessage));
+ }
+ changeStatusReal(account, status, statusMessage, send);
+ }
+
+ private void changeStatusReal(Account account, Presence.Status status, String statusMessage, boolean send) {
+ account.setPresenceStatus(status);
+ account.setPresenceStatusMessage(statusMessage);
+ databaseBackend.updateAccount(account);
+ if (!account.isOptionSet(Account.OPTION_DISABLED) && send) {
+ sendPresence(account);
+ }
+ }
+
+ public void changeStatus(Presence.Status status, String statusMessage) {
+ if (!statusMessage.isEmpty()) {
+ databaseBackend.insertPresenceTemplate(new PresenceTemplate(status, statusMessage));
+ }
+ for (Account account : getAccounts()) {
+ changeStatusReal(account, status, statusMessage, true);
+ }
+ }
+
+ public List<PresenceTemplate> getPresenceTemplates(Account account) {
+ List<PresenceTemplate> templates = databaseBackend.getPresenceTemplates();
+ for (PresenceTemplate template : account.getSelfContact().getPresences().asTemplates()) {
+ if (!templates.contains(template)) {
+ templates.add(0, template);
+ }
+ }
+ return templates;
+ }
+
+ public void saveConversationAsBookmark(Conversation conversation, String name) {
+ Account account = conversation.getAccount();
+ Bookmark bookmark = new Bookmark(account, conversation.getJid().toBareJid());
+ if (!conversation.getJid().isBareJid()) {
+ bookmark.setNick(conversation.getJid().getResourcepart());
+ }
+ if (name != null && !name.trim().isEmpty()) {
+ bookmark.setBookmarkName(name.trim());
+ }
+ bookmark.setAutojoin(getPreferences().getBoolean("autojoin", true));
+ account.getBookmarks().add(bookmark);
+ pushBookmarks(account);
+ conversation.setBookmark(bookmark);
+ }
+
+ public void clearStartTimeCounter() {
+ mDatabaseExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ databaseBackend.clearStartTimeCounter();
+ }
+ });
+ }
+
+ public void verifyFingerprints(Contact contact, List<XmppUri.Fingerprint> fingerprints) {
+ boolean needsRosterWrite = false;
+ final AxolotlService axolotlService = contact.getAccount().getAxolotlService();
+ for (XmppUri.Fingerprint fp : fingerprints) {
+ if (fp.type == XmppUri.FingerprintType.OTR) {
+ needsRosterWrite |= contact.addOtrFingerprint(fp.fingerprint);
+ } else if (fp.type == XmppUri.FingerprintType.OMEMO) {
+ String fingerprint = "05" + fp.fingerprint.replaceAll("\\s", "");
+ FingerprintStatus fingerprintStatus = axolotlService.getFingerprintTrust(fingerprint);
+ if (fingerprintStatus != null) {
+ if (!fingerprintStatus.isVerified()) {
+ axolotlService.setFingerprintTrust(fingerprint, fingerprintStatus.toVerified());
+ }
+ } else {
+ axolotlService.preVerifyFingerprint(contact, fingerprint);
+ }
+ }
+ }
+ if (needsRosterWrite) {
+ syncRosterToDisk(contact.getAccount());
+ }
+ }
+
+ public boolean verifyFingerprints(Account account, List<XmppUri.Fingerprint> fingerprints) {
+ final AxolotlService axolotlService = account.getAxolotlService();
+ boolean verifiedSomething = false;
+ for (XmppUri.Fingerprint fp : fingerprints) {
+ if (fp.type == XmppUri.FingerprintType.OMEMO) {
+ String fingerprint = "05" + fp.fingerprint.replaceAll("\\s", "");
+ Log.d(Config.LOGTAG, "trying to verify own fp=" + fingerprint);
+ FingerprintStatus fingerprintStatus = axolotlService.getFingerprintTrust(fingerprint);
+ if (fingerprintStatus != null) {
+ if (!fingerprintStatus.isVerified()) {
+ axolotlService.setFingerprintTrust(fingerprint, fingerprintStatus.toVerified());
+ verifiedSomething = true;
+ }
+ } else {
+ axolotlService.preVerifyFingerprint(account, fingerprint);
+ verifiedSomething = true;
+ }
+ }
+ }
+ return verifiedSomething;
+ }
+
+ public interface OnMamPreferencesFetched {
+ void onPreferencesFetched(Element prefs);
+
+ void onPreferencesFetchFailed();
+ }
+
+ public void pushMamPreferences(Account account, Element prefs) {
+ IqPacket set = new IqPacket(IqPacket.TYPE.SET);
+ set.addChild(prefs);
+ sendIqPacket(account, set, null);
+ }
+
+ public interface OnAccountCreated {
+ void onAccountCreated(Account account);
+
+ void informUser(int r);
+ }
+
+ public interface OnMoreMessagesLoaded {
+ void onMoreMessagesLoaded(int count, Conversation conversation);
+
+ void informUser(int r);
+ }
+
+ public interface OnAccountPasswordChanged {
+ void onPasswordChangeSucceeded();
+
+ void onPasswordChangeFailed();
+ }
+
+ public interface OnAffiliationChanged {
+ void onAffiliationChangedSuccessful(Jid jid);
+
+ void onAffiliationChangeFailed(Jid jid, int resId);
+ }
+
+ public interface OnRoleChanged {
+ void onRoleChangedSuccessful(String nick);
+
+ void onRoleChangeFailed(String nick, int resid);
+ }
+
+ public interface OnConversationUpdate {
+ void onConversationUpdate();
+ }
+
+ public interface OnAccountUpdate {
+ void onAccountUpdate();
+ }
+
+ public interface OnCaptchaRequested {
+ void onCaptchaRequested(Account account,
+ String id,
+ Data data,
+ Bitmap captcha);
+ }
+
+ public interface OnRosterUpdate {
+ void onRosterUpdate();
+ }
+
+ public interface OnMucRosterUpdate {
+ void onMucRosterUpdate();
+ }
+
+ public interface OnConferenceConfigurationFetched {
+ void onConferenceConfigurationFetched(Conversation conversation);
+
+ void onFetchFailed(Conversation conversation, Element error);
+ }
+
+ public interface OnConferenceJoined {
+ void onConferenceJoined(Conversation conversation);
+ }
+
+ public interface OnConferenceOptionsPushed {
+ void onPushSucceeded();
+
+ void onPushFailed();
+ }
+
+ public interface OnShowErrorToast {
+ void onShowErrorToast(int resId);
+ }
+
+ public class XmppConnectionBinder extends Binder {
+ public XmppConnectionService getService() {
+ return XmppConnectionService.this;
+ }
+ }
+
+ public void ScheduleAutomaticExport() {
+ //start export log service every day at given time
+ if (Config.ExportLogs) {
+ if (Config.ExportLogs_Hour >= 0 && Config.ExportLogs_Hour <= 23 && Config.ExportLogs_Minute >= 0 && Config.ExportLogs_Minute <= 59) {
+ Log.d(Config.LOGTAG, "Schedule automatic export logs at " + Config.ExportLogs_Hour + ":" + Config.ExportLogs_Minute);
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(System.currentTimeMillis());
+ calendar.set(Calendar.HOUR_OF_DAY, Config.ExportLogs_Hour);
+ calendar.set(Calendar.MINUTE, Config.ExportLogs_Minute);
+ Intent intent = new Intent(this, AlarmReceiver.class);
+ intent.setAction("exportlogs");
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, AlarmReceiver.SCHEDULE_ALARM_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ ((AlarmManager) getSystemService(ALARM_SERVICE)).setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
+ }
+ }
+ }
+
+ public void CancelAutomaticExport() {
+ if (Config.ExportLogs) {
+ Log.d(Config.LOGTAG, "Cancel scheduled automatic export");
+ Intent intent = new Intent(this, AlarmReceiver.class);
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, AlarmReceiver.SCHEDULE_ALARM_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ ((AlarmManager) this.getSystemService(ALARM_SERVICE)).cancel(pendingIntent);
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/AboutPreference.java b/src/main/java/de/pixart/messenger/ui/AboutPreference.java
index d4cfa982b..239ef53fb 100644
--- a/src/main/java/de/pixart/messenger/ui/AboutPreference.java
+++ b/src/main/java/de/pixart/messenger/ui/AboutPreference.java
@@ -8,15 +8,15 @@ import android.util.AttributeSet;
import de.pixart.messenger.utils.PhoneHelper;
public class AboutPreference extends Preference {
- public AboutPreference(final Context context, final AttributeSet attrs, final int defStyle) {
- super(context, attrs, defStyle);
- setSummary();
- }
+ public AboutPreference(final Context context, final AttributeSet attrs, final int defStyle) {
+ super(context, attrs, defStyle);
+ setSummary();
+ }
- public AboutPreference(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- setSummary();
- }
+ public AboutPreference(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+ setSummary();
+ }
@Override
protected void onClick() {
@@ -26,7 +26,7 @@ public class AboutPreference extends Preference {
}
private void setSummary() {
- setSummary("Pix-Art Messenger " + PhoneHelper.getVersionName(getContext()));
- }
+ setSummary("Pix-Art Messenger " + PhoneHelper.getVersionName(getContext()));
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/AbstractSearchableListItemActivity.java b/src/main/java/de/pixart/messenger/ui/AbstractSearchableListItemActivity.java
index 9b12e43ef..768e738ba 100644
--- a/src/main/java/de/pixart/messenger/ui/AbstractSearchableListItemActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/AbstractSearchableListItemActivity.java
@@ -20,105 +20,105 @@ import de.pixart.messenger.entities.ListItem;
import de.pixart.messenger.ui.adapter.ListItemAdapter;
public abstract class AbstractSearchableListItemActivity extends XmppActivity {
- private ListView mListView;
- private final List<ListItem> listItems = new ArrayList<>();
- private ArrayAdapter<ListItem> mListItemsAdapter;
-
- private EditText mSearchEditText;
-
- private final MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() {
-
- @Override
- public boolean onMenuItemActionExpand(final MenuItem item) {
- mSearchEditText.post(new Runnable() {
-
- @Override
- public void run() {
- mSearchEditText.requestFocus();
- final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.showSoftInput(mSearchEditText,
- InputMethodManager.SHOW_IMPLICIT);
- }
- });
-
- return true;
- }
-
- @Override
- public boolean onMenuItemActionCollapse(final MenuItem item) {
- final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(),
- InputMethodManager.HIDE_IMPLICIT_ONLY);
- mSearchEditText.setText("");
- filterContacts();
- return true;
- }
- };
-
- private final TextWatcher mSearchTextWatcher = new TextWatcher() {
-
- @Override
- public void afterTextChanged(final Editable editable) {
- filterContacts(editable.toString());
- }
-
- @Override
- public void beforeTextChanged(final CharSequence s, final int start, final int count,
- final int after) {
- }
-
- @Override
- public void onTextChanged(final CharSequence s, final int start, final int before,
- final int count) {
- }
- };
-
- public ListView getListView() {
- return mListView;
- }
-
- public List<ListItem> getListItems() {
- return listItems;
- }
-
- public EditText getSearchEditText() {
- return mSearchEditText;
- }
-
- public ArrayAdapter<ListItem> getListItemAdapter() {
- return mListItemsAdapter;
- }
-
- @Override
- public void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_choose_contact);
- mListView = (ListView) findViewById(R.id.choose_contact_list);
- mListView.setFastScrollEnabled(true);
- mListItemsAdapter = new ListItemAdapter(this, listItems);
- mListView.setAdapter(mListItemsAdapter);
- }
-
- @Override
- public boolean onCreateOptionsMenu(final Menu menu) {
- getMenuInflater().inflate(R.menu.choose_contact, menu);
- final MenuItem menuSearchView = menu.findItem(R.id.action_search);
- final View mSearchView = menuSearchView.getActionView();
- mSearchEditText = (EditText) mSearchView
- .findViewById(R.id.search_field);
- mSearchEditText.addTextChangedListener(mSearchTextWatcher);
- menuSearchView.setOnActionExpandListener(mOnActionExpandListener);
- return true;
- }
-
- protected void filterContacts() {
- filterContacts(null);
- }
-
- protected abstract void filterContacts(final String needle);
-
- @Override
- void onBackendConnected() {
- filterContacts();
- }
+ private ListView mListView;
+ private final List<ListItem> listItems = new ArrayList<>();
+ private ArrayAdapter<ListItem> mListItemsAdapter;
+
+ private EditText mSearchEditText;
+
+ private final MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() {
+
+ @Override
+ public boolean onMenuItemActionExpand(final MenuItem item) {
+ mSearchEditText.post(new Runnable() {
+
+ @Override
+ public void run() {
+ mSearchEditText.requestFocus();
+ final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mSearchEditText,
+ InputMethodManager.SHOW_IMPLICIT);
+ }
+ });
+
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(final MenuItem item) {
+ final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(),
+ InputMethodManager.HIDE_IMPLICIT_ONLY);
+ mSearchEditText.setText("");
+ filterContacts();
+ return true;
+ }
+ };
+
+ private final TextWatcher mSearchTextWatcher = new TextWatcher() {
+
+ @Override
+ public void afterTextChanged(final Editable editable) {
+ filterContacts(editable.toString());
+ }
+
+ @Override
+ public void beforeTextChanged(final CharSequence s, final int start, final int count,
+ final int after) {
+ }
+
+ @Override
+ public void onTextChanged(final CharSequence s, final int start, final int before,
+ final int count) {
+ }
+ };
+
+ public ListView getListView() {
+ return mListView;
+ }
+
+ public List<ListItem> getListItems() {
+ return listItems;
+ }
+
+ public EditText getSearchEditText() {
+ return mSearchEditText;
+ }
+
+ public ArrayAdapter<ListItem> getListItemAdapter() {
+ return mListItemsAdapter;
+ }
+
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_choose_contact);
+ mListView = (ListView) findViewById(R.id.choose_contact_list);
+ mListView.setFastScrollEnabled(true);
+ mListItemsAdapter = new ListItemAdapter(this, listItems);
+ mListView.setAdapter(mListItemsAdapter);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ getMenuInflater().inflate(R.menu.choose_contact, menu);
+ final MenuItem menuSearchView = menu.findItem(R.id.action_search);
+ final View mSearchView = menuSearchView.getActionView();
+ mSearchEditText = (EditText) mSearchView
+ .findViewById(R.id.search_field);
+ mSearchEditText.addTextChangedListener(mSearchTextWatcher);
+ menuSearchView.setOnActionExpandListener(mOnActionExpandListener);
+ return true;
+ }
+
+ protected void filterContacts() {
+ filterContacts(null);
+ }
+
+ protected abstract void filterContacts(final String needle);
+
+ @Override
+ void onBackendConnected() {
+ filterContacts();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/BlockContactDialog.java b/src/main/java/de/pixart/messenger/ui/BlockContactDialog.java
index f72a365b9..d1c6b56d6 100644
--- a/src/main/java/de/pixart/messenger/ui/BlockContactDialog.java
+++ b/src/main/java/de/pixart/messenger/ui/BlockContactDialog.java
@@ -17,48 +17,48 @@ import de.pixart.messenger.entities.Blockable;
import de.pixart.messenger.services.XmppConnectionService;
public final class BlockContactDialog {
- public static void show(final Context context,
- final XmppConnectionService xmppConnectionService,
- final Blockable blockable) {
- final AlertDialog.Builder builder = new AlertDialog.Builder(context);
- final boolean isBlocked = blockable.isBlocked();
- builder.setNegativeButton(R.string.cancel, null);
- LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- LinearLayout view = (LinearLayout) inflater.inflate(R.layout.dialog_block_contact,null);
- TextView message = (TextView) view.findViewById(R.id.text);
- final CheckBox report = (CheckBox) view.findViewById(R.id.report_spam);
- final boolean reporting = blockable.getAccount().getXmppConnection().getFeatures().spamReporting();
- report.setVisibility(!isBlocked && reporting ? View.VISIBLE : View.GONE);
- builder.setView(view);
+ public static void show(final Context context,
+ final XmppConnectionService xmppConnectionService,
+ final Blockable blockable) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ final boolean isBlocked = blockable.isBlocked();
+ builder.setNegativeButton(R.string.cancel, null);
+ LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ LinearLayout view = (LinearLayout) inflater.inflate(R.layout.dialog_block_contact, null);
+ TextView message = (TextView) view.findViewById(R.id.text);
+ final CheckBox report = (CheckBox) view.findViewById(R.id.report_spam);
+ final boolean reporting = blockable.getAccount().getXmppConnection().getFeatures().spamReporting();
+ report.setVisibility(!isBlocked && reporting ? View.VISIBLE : View.GONE);
+ builder.setView(view);
- String value;
- SpannableString spannable;
- if (blockable.getJid().isDomainJid() || blockable.getAccount().isBlocked(blockable.getJid().toDomainJid())) {
- builder.setTitle(isBlocked ? R.string.action_unblock_domain : R.string.action_block_domain);
- value = blockable.getJid().toDomainJid().toString();
- spannable = new SpannableString(context.getString(isBlocked ? R.string.unblock_domain_text : R.string.block_domain_text, value));
- message.setText(spannable);
- } else {
- builder.setTitle(isBlocked ? R.string.action_unblock_contact : R.string.action_block_contact);
- value = blockable.getJid().toBareJid().toString();
- spannable = new SpannableString(context.getString(isBlocked ? R.string.unblock_contact_text : R.string.block_contact_text, value));
- }
- int start = spannable.toString().indexOf(value);
- if (start >= 0) {
- spannable.setSpan(new TypefaceSpan("monospace"),start,start + value.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- message.setText(spannable);
- builder.setPositiveButton(isBlocked ? R.string.unblock : R.string.block, new DialogInterface.OnClickListener() {
+ String value;
+ SpannableString spannable;
+ if (blockable.getJid().isDomainJid() || blockable.getAccount().isBlocked(blockable.getJid().toDomainJid())) {
+ builder.setTitle(isBlocked ? R.string.action_unblock_domain : R.string.action_block_domain);
+ value = blockable.getJid().toDomainJid().toString();
+ spannable = new SpannableString(context.getString(isBlocked ? R.string.unblock_domain_text : R.string.block_domain_text, value));
+ message.setText(spannable);
+ } else {
+ builder.setTitle(isBlocked ? R.string.action_unblock_contact : R.string.action_block_contact);
+ value = blockable.getJid().toBareJid().toString();
+ spannable = new SpannableString(context.getString(isBlocked ? R.string.unblock_contact_text : R.string.block_contact_text, value));
+ }
+ int start = spannable.toString().indexOf(value);
+ if (start >= 0) {
+ spannable.setSpan(new TypefaceSpan("monospace"), start, start + value.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ message.setText(spannable);
+ builder.setPositiveButton(isBlocked ? R.string.unblock : R.string.block, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(final DialogInterface dialog, final int which) {
- if (isBlocked) {
- xmppConnectionService.sendUnblockRequest(blockable);
- } else {
- xmppConnectionService.sendBlockRequest(blockable, report.isChecked());
- }
- }
- });
- builder.create().show();
- }
+ @Override
+ public void onClick(final DialogInterface dialog, final int which) {
+ if (isBlocked) {
+ xmppConnectionService.sendUnblockRequest(blockable);
+ } else {
+ xmppConnectionService.sendBlockRequest(blockable, report.isChecked());
+ }
+ }
+ });
+ builder.create().show();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/BlocklistActivity.java b/src/main/java/de/pixart/messenger/ui/BlocklistActivity.java
index cefa17de6..cb5a0112b 100644
--- a/src/main/java/de/pixart/messenger/ui/BlocklistActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/BlocklistActivity.java
@@ -14,61 +14,61 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class BlocklistActivity extends AbstractSearchableListItemActivity implements OnUpdateBlocklist {
- private Account account = null;
+ private Account account = null;
- @Override
- public void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
- @Override
- public boolean onItemLongClick(final AdapterView<?> parent,
- final View view,
- final int position,
- final long id) {
- BlockContactDialog.show(parent.getContext(), xmppConnectionService,(Contact) getListItems().get(position));
- return true;
- }
- });
- }
+ @Override
+ public boolean onItemLongClick(final AdapterView<?> parent,
+ final View view,
+ final int position,
+ final long id) {
+ BlockContactDialog.show(parent.getContext(), xmppConnectionService, (Contact) getListItems().get(position));
+ return true;
+ }
+ });
+ }
- @Override
- public void onBackendConnected() {
- for (final Account account : xmppConnectionService.getAccounts()) {
- if (account.getJid().toString().equals(getIntent().getStringExtra(EXTRA_ACCOUNT))) {
- this.account = account;
- break;
- }
- }
- filterContacts();
- }
+ @Override
+ public void onBackendConnected() {
+ for (final Account account : xmppConnectionService.getAccounts()) {
+ if (account.getJid().toString().equals(getIntent().getStringExtra(EXTRA_ACCOUNT))) {
+ this.account = account;
+ break;
+ }
+ }
+ filterContacts();
+ }
- @Override
- protected void filterContacts(final String needle) {
- getListItems().clear();
- if (account != null) {
- for (final Jid jid : account.getBlocklist()) {
- final Contact contact = account.getRoster().getContact(jid);
- if (contact.match(this, needle) && contact.isBlocked()) {
- getListItems().add(contact);
- }
- }
- Collections.sort(getListItems());
- }
- getListItemAdapter().notifyDataSetChanged();
- }
+ @Override
+ protected void filterContacts(final String needle) {
+ getListItems().clear();
+ if (account != null) {
+ for (final Jid jid : account.getBlocklist()) {
+ final Contact contact = account.getRoster().getContact(jid);
+ if (contact.match(this, needle) && contact.isBlocked()) {
+ getListItems().add(contact);
+ }
+ }
+ Collections.sort(getListItems());
+ }
+ getListItemAdapter().notifyDataSetChanged();
+ }
- protected void refreshUiReal() {
- final Editable editable = getSearchEditText().getText();
- if (editable != null) {
- filterContacts(editable.toString());
- } else {
- filterContacts();
- }
- }
+ protected void refreshUiReal() {
+ final Editable editable = getSearchEditText().getText();
+ if (editable != null) {
+ filterContacts(editable.toString());
+ } else {
+ filterContacts();
+ }
+ }
- @Override
- public void OnUpdateBlocklist(final OnUpdateBlocklist.Status status) {
- refreshUi();
- }
+ @Override
+ public void OnUpdateBlocklist(final OnUpdateBlocklist.Status status) {
+ refreshUi();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ChangePasswordActivity.java b/src/main/java/de/pixart/messenger/ui/ChangePasswordActivity.java
index 361caf182..ddbbbca36 100644
--- a/src/main/java/de/pixart/messenger/ui/ChangePasswordActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ChangePasswordActivity.java
@@ -14,109 +14,109 @@ import de.pixart.messenger.services.XmppConnectionService;
public class ChangePasswordActivity extends XmppActivity implements XmppConnectionService.OnAccountPasswordChanged {
- private Button mChangePasswordButton;
- private View.OnClickListener mOnChangePasswordButtonClicked = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mAccount != null) {
- final String currentPassword = mCurrentPassword.getText().toString();
- final String newPassword = mNewPassword.getText().toString();
- final String newPasswordConfirm = mNewPasswordConfirm.getText().toString();
- if (!mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && !currentPassword.equals(mAccount.getPassword())) {
- 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));
- } else if (newPassword.trim().isEmpty()) {
- mNewPassword.requestFocus();
- mNewPassword.setError(getString(R.string.password_should_not_be_empty));
- } 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);
- }
- }
- }
- };
- private TextView mCurrentPasswordLabel;
- private EditText mCurrentPassword;
- private EditText mNewPassword;
- private EditText mNewPasswordConfirm;
- private Account mAccount;
+ private Button mChangePasswordButton;
+ private View.OnClickListener mOnChangePasswordButtonClicked = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (mAccount != null) {
+ final String currentPassword = mCurrentPassword.getText().toString();
+ final String newPassword = mNewPassword.getText().toString();
+ final String newPasswordConfirm = mNewPasswordConfirm.getText().toString();
+ if (!mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && !currentPassword.equals(mAccount.getPassword())) {
+ 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));
+ } else if (newPassword.trim().isEmpty()) {
+ mNewPassword.requestFocus();
+ mNewPassword.setError(getString(R.string.password_should_not_be_empty));
+ } 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);
+ }
+ }
+ }
+ };
+ private TextView mCurrentPasswordLabel;
+ private EditText mCurrentPassword;
+ private EditText mNewPassword;
+ private EditText mNewPasswordConfirm;
+ private Account mAccount;
- @Override
- void onBackendConnected() {
- this.mAccount = extractAccount(getIntent());
- if (this.mAccount != null && this.mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
- this.mCurrentPasswordLabel.setVisibility(View.GONE);
- this.mCurrentPassword.setVisibility(View.GONE);
- } else {
- this.mCurrentPasswordLabel.setVisibility(View.VISIBLE);
- this.mCurrentPassword.setVisibility(View.VISIBLE);
- }
- }
+ @Override
+ void onBackendConnected() {
+ this.mAccount = extractAccount(getIntent());
+ if (this.mAccount != null && this.mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
+ this.mCurrentPasswordLabel.setVisibility(View.GONE);
+ this.mCurrentPassword.setVisibility(View.GONE);
+ } else {
+ this.mCurrentPasswordLabel.setVisibility(View.VISIBLE);
+ this.mCurrentPassword.setVisibility(View.VISIBLE);
+ }
+ }
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_change_password);
- Button mCancelButton = (Button) findViewById(R.id.left_button);
- mCancelButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- finish();
- }
- });
- this.mChangePasswordButton = (Button) findViewById(R.id.right_button);
- this.mChangePasswordButton.setOnClickListener(this.mOnChangePasswordButtonClicked);
- this.mCurrentPasswordLabel = (TextView) findViewById(R.id.current_password_label);
- this.mCurrentPassword = (EditText) findViewById(R.id.current_password);
- this.mNewPassword = (EditText) findViewById(R.id.new_password);
- this.mNewPasswordConfirm = (EditText) findViewById(R.id.new_password_confirm);
- }
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_change_password);
+ Button mCancelButton = (Button) findViewById(R.id.left_button);
+ mCancelButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ finish();
+ }
+ });
+ this.mChangePasswordButton = (Button) findViewById(R.id.right_button);
+ this.mChangePasswordButton.setOnClickListener(this.mOnChangePasswordButtonClicked);
+ this.mCurrentPasswordLabel = (TextView) findViewById(R.id.current_password_label);
+ this.mCurrentPassword = (EditText) findViewById(R.id.current_password);
+ this.mNewPassword = (EditText) findViewById(R.id.new_password);
+ this.mNewPasswordConfirm = (EditText) findViewById(R.id.new_password_confirm);
+ }
- @Override
- protected void onStart() {
- super.onStart();
- Intent intent = getIntent();
- String password = intent != null ? intent.getStringExtra("password") : null;
- if (password != null) {
- this.mNewPassword.getEditableText().clear();
- this.mNewPassword.getEditableText().append(password);
- }
- }
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Intent intent = getIntent();
+ String password = intent != null ? intent.getStringExtra("password") : null;
+ if (password != null) {
+ this.mNewPassword.getEditableText().clear();
+ this.mNewPassword.getEditableText().append(password);
+ }
+ }
- @Override
- public void onPasswordChangeSucceeded() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(ChangePasswordActivity.this,R.string.password_changed,Toast.LENGTH_LONG).show();
- finish();
- }
- });
- }
+ @Override
+ public void onPasswordChangeSucceeded() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(ChangePasswordActivity.this, R.string.password_changed, Toast.LENGTH_LONG).show();
+ finish();
+ }
+ });
+ }
- @Override
- public void onPasswordChangeFailed() {
- runOnUiThread(new Runnable() {
- @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);
- }
- });
+ @Override
+ public void onPasswordChangeFailed() {
+ runOnUiThread(new Runnable() {
+ @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);
+ }
+ });
- }
+ }
- public void refreshUiReal() {
+ public void refreshUiReal() {
- }
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/ChooseContactActivity.java b/src/main/java/de/pixart/messenger/ui/ChooseContactActivity.java
index e38433893..b1dd48a5f 100644
--- a/src/main/java/de/pixart/messenger/ui/ChooseContactActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ChooseContactActivity.java
@@ -29,218 +29,218 @@ import de.pixart.messenger.entities.ListItem;
import de.pixart.messenger.xmpp.jid.Jid;
public class ChooseContactActivity extends AbstractSearchableListItemActivity {
- private List<String> mActivatedAccounts = new ArrayList<String>();
- private List<String> mKnownHosts;
-
- private Set<Contact> selected;
- private Set<String> filterContacts;
- public static final String EXTRA_TITLE_RES_ID = "extra_title_res_id";
-
- @Override
- public void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- filterContacts = new HashSet<>();
- String[] contacts = getIntent().getStringArrayExtra("filter_contacts");
- if (contacts != null) {
- Collections.addAll(filterContacts, contacts);
- }
-
- if (getIntent().getBooleanExtra("multiple", false)) {
- getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
- getListView().setMultiChoiceModeListener(new MultiChoiceModeListener() {
-
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return false;
- }
-
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(),
- InputMethodManager.HIDE_IMPLICIT_ONLY);
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.select_multiple, menu);
- selected = new HashSet<Contact>();
- return true;
- }
-
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- }
-
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- switch(item.getItemId()) {
- case R.id.selection_submit:
- final Intent request = getIntent();
- final Intent data = new Intent();
- data.putExtra("conversation",
- request.getStringExtra("conversation"));
- String[] selection = getSelectedContactJids();
- data.putExtra("contacts", selection);
- data.putExtra("multiple", true);
- data.putExtra(EXTRA_ACCOUNT,request.getStringExtra(EXTRA_ACCOUNT));
- data.putExtra("subject", request.getStringExtra("subject"));
- setResult(RESULT_OK, data);
- finish();
- return true;
- }
- return false;
- }
-
- @Override
- public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
- Contact item = (Contact) getListItems().get(position);
- if (checked) {
- selected.add(item);
- } else {
- selected.remove(item);
- }
- int numSelected = selected.size();
- MenuItem selectButton = mode.getMenu().findItem(R.id.selection_submit);
- String buttonText = getResources().getQuantityString(R.plurals.select_contact,
- numSelected, numSelected);
- selectButton.setTitle(buttonText);
- }
- });
- }
-
- getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
-
- @Override
- public void onItemClick(final AdapterView<?> parent, final View view,
- final int position, final long id) {
- final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(),
- InputMethodManager.HIDE_IMPLICIT_ONLY);
- final Intent request = getIntent();
- final Intent data = new Intent();
- final ListItem mListItem = getListItems().get(position);
- data.putExtra("contact", mListItem.getJid().toString());
- String account = request.getStringExtra(EXTRA_ACCOUNT);
- if (account == null && mListItem instanceof Contact) {
- account = ((Contact) mListItem).getAccount().getJid().toBareJid().toString();
- }
- data.putExtra(EXTRA_ACCOUNT, account);
- data.putExtra("conversation",
- request.getStringExtra("conversation"));
- data.putExtra("multiple", false);
- data.putExtra("subject", request.getStringExtra("subject"));
- setResult(RESULT_OK, data);
- finish();
- }
- });
-
- }
-
- @Override
- public void onStart() {
- super.onStart();
- Intent intent = getIntent();
- @StringRes
- int res = intent != null ? intent.getIntExtra(EXTRA_TITLE_RES_ID,R.string.title_activity_choose_contact) : R.string.title_activity_choose_contact;
- ActionBar bar = getActionBar();
- if (bar != null) {
- try {
- bar.setTitle(res);
- } catch (Exception e) {
- bar.setTitle(R.string.title_activity_choose_contact);
- }
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(final Menu menu) {
- super.onCreateOptionsMenu(menu);
- final Intent i = getIntent();
- boolean showEnterJid = i != null && i.getBooleanExtra("show_enter_jid", false);
- menu.findItem(R.id.action_create_contact).setVisible(showEnterJid);
- return true;
- }
-
- protected void filterContacts(final String needle) {
- getListItems().clear();
- for (final Account account : xmppConnectionService.getAccounts()) {
- if (account.getStatus() != Account.State.DISABLED) {
- for (final Contact contact : account.getRoster().getContacts()) {
- if (contact.showInRoster() &&
- !filterContacts.contains(contact.getJid().toBareJid().toString())
- && contact.match(this, needle)) {
- getListItems().add(contact);
- }
- }
- }
- }
- Collections.sort(getListItems());
- getListItemAdapter().notifyDataSetChanged();
- }
-
- private String[] getSelectedContactJids() {
- List<String> result = new ArrayList<>();
- for (Contact contact : selected) {
- result.add(contact.getJid().toString());
- }
- return result.toArray(new String[result.size()]);
- }
-
-
- public void refreshUiReal() {
- //nothing to do. This Activity doesn't implement any listeners
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_create_contact:
- showEnterJidDialog();
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- protected void showEnterJidDialog() {
- EnterJidDialog dialog = new EnterJidDialog(
- this, mKnownHosts, mActivatedAccounts,
- getString(R.string.enter_contact), getString(R.string.select),
- null, getIntent().getStringExtra(EXTRA_ACCOUNT), true
- );
-
- dialog.setOnEnterJidDialogPositiveListener(new EnterJidDialog.OnEnterJidDialogPositiveListener() {
- @Override
- public boolean onEnterJidDialogPositive(Jid accountJid, Jid contactJid) throws EnterJidDialog.JidError {
- final Intent request = getIntent();
- final Intent data = new Intent();
- data.putExtra("contact", contactJid.toString());
- data.putExtra(EXTRA_ACCOUNT, accountJid.toString());
- data.putExtra("conversation",
- request.getStringExtra("conversation"));
- data.putExtra("multiple", false);
- data.putExtra("subject", request.getStringExtra("subject"));
- setResult(RESULT_OK, data);
- finish();
-
- return true;
- }
- });
-
- dialog.show();
- }
-
- @Override
- void onBackendConnected() {
- filterContacts();
-
- this.mActivatedAccounts.clear();
- for (Account account : xmppConnectionService.getAccounts()) {
- if (account.getStatus() != Account.State.DISABLED) {
- if (Config.DOMAIN_LOCK != null) {
- this.mActivatedAccounts.add(account.getJid().getLocalpart());
- } else {
- this.mActivatedAccounts.add(account.getJid().toBareJid().toString());
- }
- }
- }
- this.mKnownHosts = xmppConnectionService.getKnownHosts();
- }
+ private List<String> mActivatedAccounts = new ArrayList<String>();
+ private List<String> mKnownHosts;
+
+ private Set<Contact> selected;
+ private Set<String> filterContacts;
+ public static final String EXTRA_TITLE_RES_ID = "extra_title_res_id";
+
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ filterContacts = new HashSet<>();
+ String[] contacts = getIntent().getStringArrayExtra("filter_contacts");
+ if (contacts != null) {
+ Collections.addAll(filterContacts, contacts);
+ }
+
+ if (getIntent().getBooleanExtra("multiple", false)) {
+ getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
+ getListView().setMultiChoiceModeListener(new MultiChoiceModeListener() {
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(),
+ InputMethodManager.HIDE_IMPLICIT_ONLY);
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.select_multiple, menu);
+ selected = new HashSet<Contact>();
+ return true;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.selection_submit:
+ final Intent request = getIntent();
+ final Intent data = new Intent();
+ data.putExtra("conversation",
+ request.getStringExtra("conversation"));
+ String[] selection = getSelectedContactJids();
+ data.putExtra("contacts", selection);
+ data.putExtra("multiple", true);
+ data.putExtra(EXTRA_ACCOUNT, request.getStringExtra(EXTRA_ACCOUNT));
+ data.putExtra("subject", request.getStringExtra("subject"));
+ setResult(RESULT_OK, data);
+ finish();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
+ Contact item = (Contact) getListItems().get(position);
+ if (checked) {
+ selected.add(item);
+ } else {
+ selected.remove(item);
+ }
+ int numSelected = selected.size();
+ MenuItem selectButton = mode.getMenu().findItem(R.id.selection_submit);
+ String buttonText = getResources().getQuantityString(R.plurals.select_contact,
+ numSelected, numSelected);
+ selectButton.setTitle(buttonText);
+ }
+ });
+ }
+
+ getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
+
+ @Override
+ public void onItemClick(final AdapterView<?> parent, final View view,
+ final int position, final long id) {
+ final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(),
+ InputMethodManager.HIDE_IMPLICIT_ONLY);
+ final Intent request = getIntent();
+ final Intent data = new Intent();
+ final ListItem mListItem = getListItems().get(position);
+ data.putExtra("contact", mListItem.getJid().toString());
+ String account = request.getStringExtra(EXTRA_ACCOUNT);
+ if (account == null && mListItem instanceof Contact) {
+ account = ((Contact) mListItem).getAccount().getJid().toBareJid().toString();
+ }
+ data.putExtra(EXTRA_ACCOUNT, account);
+ data.putExtra("conversation",
+ request.getStringExtra("conversation"));
+ data.putExtra("multiple", false);
+ data.putExtra("subject", request.getStringExtra("subject"));
+ setResult(RESULT_OK, data);
+ finish();
+ }
+ });
+
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ Intent intent = getIntent();
+ @StringRes
+ int res = intent != null ? intent.getIntExtra(EXTRA_TITLE_RES_ID, R.string.title_activity_choose_contact) : R.string.title_activity_choose_contact;
+ ActionBar bar = getActionBar();
+ if (bar != null) {
+ try {
+ bar.setTitle(res);
+ } catch (Exception e) {
+ bar.setTitle(R.string.title_activity_choose_contact);
+ }
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ final Intent i = getIntent();
+ boolean showEnterJid = i != null && i.getBooleanExtra("show_enter_jid", false);
+ menu.findItem(R.id.action_create_contact).setVisible(showEnterJid);
+ return true;
+ }
+
+ protected void filterContacts(final String needle) {
+ getListItems().clear();
+ for (final Account account : xmppConnectionService.getAccounts()) {
+ if (account.getStatus() != Account.State.DISABLED) {
+ for (final Contact contact : account.getRoster().getContacts()) {
+ if (contact.showInRoster() &&
+ !filterContacts.contains(contact.getJid().toBareJid().toString())
+ && contact.match(this, needle)) {
+ getListItems().add(contact);
+ }
+ }
+ }
+ }
+ Collections.sort(getListItems());
+ getListItemAdapter().notifyDataSetChanged();
+ }
+
+ private String[] getSelectedContactJids() {
+ List<String> result = new ArrayList<>();
+ for (Contact contact : selected) {
+ result.add(contact.getJid().toString());
+ }
+ return result.toArray(new String[result.size()]);
+ }
+
+
+ public void refreshUiReal() {
+ //nothing to do. This Activity doesn't implement any listeners
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_create_contact:
+ showEnterJidDialog();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ protected void showEnterJidDialog() {
+ EnterJidDialog dialog = new EnterJidDialog(
+ this, mKnownHosts, mActivatedAccounts,
+ getString(R.string.enter_contact), getString(R.string.select),
+ null, getIntent().getStringExtra(EXTRA_ACCOUNT), true
+ );
+
+ dialog.setOnEnterJidDialogPositiveListener(new EnterJidDialog.OnEnterJidDialogPositiveListener() {
+ @Override
+ public boolean onEnterJidDialogPositive(Jid accountJid, Jid contactJid) throws EnterJidDialog.JidError {
+ final Intent request = getIntent();
+ final Intent data = new Intent();
+ data.putExtra("contact", contactJid.toString());
+ data.putExtra(EXTRA_ACCOUNT, accountJid.toString());
+ data.putExtra("conversation",
+ request.getStringExtra("conversation"));
+ data.putExtra("multiple", false);
+ data.putExtra("subject", request.getStringExtra("subject"));
+ setResult(RESULT_OK, data);
+ finish();
+
+ return true;
+ }
+ });
+
+ dialog.show();
+ }
+
+ @Override
+ void onBackendConnected() {
+ filterContacts();
+
+ this.mActivatedAccounts.clear();
+ for (Account account : xmppConnectionService.getAccounts()) {
+ if (account.getStatus() != Account.State.DISABLED) {
+ if (Config.DOMAIN_LOCK != null) {
+ this.mActivatedAccounts.add(account.getJid().getLocalpart());
+ } else {
+ this.mActivatedAccounts.add(account.getJid().toBareJid().toString());
+ }
+ }
+ }
+ this.mKnownHosts = xmppConnectionService.getKnownHosts();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java b/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java
index d91c6e192..672dec4a1 100644
--- a/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java
@@ -41,232 +41,232 @@ import de.pixart.messenger.services.XmppConnectionService.OnMucRosterUpdate;
import de.pixart.messenger.xmpp.jid.Jid;
public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnRoleChanged, XmppConnectionService.OnConferenceOptionsPushed {
- public static final String ACTION_VIEW_MUC = "view_muc";
- private Conversation mConversation;
- private OnClickListener inviteListener = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- inviteToConversation(mConversation);
- }
- };
- private TextView mYourNick;
- private ImageView mYourPhoto;
- private ImageButton mEditNickButton;
- private TextView mRoleAffiliaton;
- private TextView mFullJid;
- private TextView mAccountJid;
- private LinearLayout membersView;
- private LinearLayout mMoreDetails;
- private TextView mConferenceType;
- private LinearLayout mConferenceInfoTable;
- private TextView mConferenceInfoMam;
- private TextView mNotifyStatusText;
- private ImageButton mChangeConferenceSettingsButton;
- private ImageButton mNotifyStatusButton;
- private Button mInviteButton;
- private String uuid = null;
- private User mSelectedUser = null;
-
- private boolean mAdvancedMode = false;
-
- private UiCallback<Conversation> renameCallback = new UiCallback<Conversation>() {
- @Override
- public void success(Conversation object) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(ConferenceDetailsActivity.this,getString(R.string.your_nick_has_been_changed),Toast.LENGTH_SHORT).show();
- updateView();
- }
- });
-
- }
-
- @Override
- public void error(final int errorCode, Conversation object) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(ConferenceDetailsActivity.this,getString(errorCode),Toast.LENGTH_SHORT).show();
- }
- });
- }
-
- @Override
- public void userInputRequried(PendingIntent pi, Conversation object) {
-
- }
- };
-
- private OnClickListener mNotifyStatusClickListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this);
- builder.setTitle(R.string.pref_notification_settings);
- String[] choices = {
- getString(R.string.notify_on_all_messages),
- getString(R.string.notify_only_when_highlighted),
- getString(R.string.notify_never)
- };
- final AtomicInteger choice;
- if (mConversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL,0) == Long.MAX_VALUE) {
- choice = new AtomicInteger(2);
- } else {
- choice = new AtomicInteger(mConversation.alwaysNotify() ? 0 : 1);
- }
- builder.setSingleChoiceItems(choices, choice.get(), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- choice.set(which);
- }
- });
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (choice.get() == 2) {
- mConversation.setMutedTill(Long.MAX_VALUE);
- } else {
- mConversation.setMutedTill(0);
- mConversation.setAttribute(Conversation.ATTRIBUTE_ALWAYS_NOTIFY,String.valueOf(choice.get() == 0));
- }
- xmppConnectionService.updateConversation(mConversation);
- updateView();
- }
- });
- builder.create().show();
- }
- };
-
- private OnClickListener mChangeConferenceSettings = new OnClickListener() {
- @Override
- public void onClick(View v) {
- final MucOptions mucOptions = mConversation.getMucOptions();
- AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this);
- builder.setTitle(R.string.conference_options);
- final String[] options;
- final boolean[] values;
- if (mAdvancedMode) {
- options = new String[]{
- getString(R.string.members_only),
- getString(R.string.moderated),
- getString(R.string.non_anonymous)
- };
- values = new boolean[]{
- mucOptions.membersOnly(),
- mucOptions.moderated(),
- mucOptions.nonanonymous()
- };
- } else {
- options = new String[]{
- getString(R.string.members_only),
- getString(R.string.non_anonymous)
- };
- values = new boolean[]{
- mucOptions.membersOnly(),
- mucOptions.nonanonymous()
- };
- }
- builder.setMultiChoiceItems(options,values,new DialogInterface.OnMultiChoiceClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which, boolean isChecked) {
- values[which] = isChecked;
- }
- });
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(R.string.confirm,new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (!mucOptions.membersOnly() && values[0]) {
- xmppConnectionService.changeAffiliationsInConference(mConversation,
- MucOptions.Affiliation.NONE,
- MucOptions.Affiliation.MEMBER);
- }
- Bundle options = new Bundle();
- options.putString("muc#roomconfig_membersonly", values[0] ? "1" : "0");
- if (values.length == 2) {
- options.putString("muc#roomconfig_whois", values[1] ? "anyone" : "moderators");
- } else if (values.length == 3) {
- options.putString("muc#roomconfig_moderatedroom", values[1] ? "1" : "0");
- options.putString("muc#roomconfig_whois", values[2] ? "anyone" : "moderators");
- }
- options.putString("muc#roomconfig_persistentroom", "1");
- xmppConnectionService.pushConferenceConfiguration(mConversation,
- options,
- ConferenceDetailsActivity.this);
- }
- });
- builder.create().show();
- }
- };
- private OnValueEdited onSubjectEdited = new OnValueEdited() {
-
- @Override
- public void onValueEdited(String value) {
- xmppConnectionService.pushSubjectToConference(mConversation,value);
- }
- };
-
- @Override
- public void onConversationUpdate() {
- refreshUi();
- }
-
- @Override
- public void onMucRosterUpdate() {
- refreshUi();
- }
-
- @Override
- protected void refreshUiReal() {
- updateView();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_muc_details);
- mYourNick = (TextView) findViewById(R.id.muc_your_nick);
- mYourPhoto = (ImageView) findViewById(R.id.your_photo);
- mEditNickButton = (ImageButton) findViewById(R.id.edit_nick_button);
- mFullJid = (TextView) findViewById(R.id.muc_jabberid);
- membersView = (LinearLayout) findViewById(R.id.muc_members);
- mAccountJid = (TextView) findViewById(R.id.details_account);
- mMoreDetails = (LinearLayout) findViewById(R.id.muc_more_details);
- mMoreDetails.setVisibility(View.GONE);
- mChangeConferenceSettingsButton = (ImageButton) findViewById(R.id.change_conference_button);
- mChangeConferenceSettingsButton.setOnClickListener(this.mChangeConferenceSettings);
- mInviteButton = (Button) findViewById(R.id.invite);
- mInviteButton.setOnClickListener(inviteListener);
- mConferenceType = (TextView) findViewById(R.id.muc_conference_type);
- if (getActionBar() != null) {
- getActionBar().setHomeButtonEnabled(true);
- getActionBar().setDisplayHomeAsUpEnabled(true);
- }
- mEditNickButton.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- quickEdit(mConversation.getMucOptions().getActualNick(),
- 0,
- new OnValueEdited() {
-
- @Override
- public void onValueEdited(String value) {
- xmppConnectionService.renameInMuc(mConversation,value,renameCallback);
- }
- });
- }
- });
- this.mAdvancedMode = getPreferences().getBoolean("advanced_muc_mode", false);
- this.mConferenceInfoTable = (LinearLayout) findViewById(R.id.muc_info_more);
- mConferenceInfoTable.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE);
- this.mConferenceInfoMam = (TextView) findViewById(R.id.muc_info_mam);
- this.mNotifyStatusButton = (ImageButton) findViewById(R.id.notification_status_button);
- this.mNotifyStatusButton.setOnClickListener(this.mNotifyStatusClickListener);
- this.mNotifyStatusText = (TextView) findViewById(R.id.notification_status_text);
- }
+ public static final String ACTION_VIEW_MUC = "view_muc";
+ private Conversation mConversation;
+ private OnClickListener inviteListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ inviteToConversation(mConversation);
+ }
+ };
+ private TextView mYourNick;
+ private ImageView mYourPhoto;
+ private ImageButton mEditNickButton;
+ private TextView mRoleAffiliaton;
+ private TextView mFullJid;
+ private TextView mAccountJid;
+ private LinearLayout membersView;
+ private LinearLayout mMoreDetails;
+ private TextView mConferenceType;
+ private LinearLayout mConferenceInfoTable;
+ private TextView mConferenceInfoMam;
+ private TextView mNotifyStatusText;
+ private ImageButton mChangeConferenceSettingsButton;
+ private ImageButton mNotifyStatusButton;
+ private Button mInviteButton;
+ private String uuid = null;
+ private User mSelectedUser = null;
+
+ private boolean mAdvancedMode = false;
+
+ private UiCallback<Conversation> renameCallback = new UiCallback<Conversation>() {
+ @Override
+ public void success(Conversation object) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(ConferenceDetailsActivity.this, getString(R.string.your_nick_has_been_changed), Toast.LENGTH_SHORT).show();
+ updateView();
+ }
+ });
+
+ }
+
+ @Override
+ public void error(final int errorCode, Conversation object) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(ConferenceDetailsActivity.this, getString(errorCode), Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Conversation object) {
+
+ }
+ };
+
+ private OnClickListener mNotifyStatusClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this);
+ builder.setTitle(R.string.pref_notification_settings);
+ String[] choices = {
+ getString(R.string.notify_on_all_messages),
+ getString(R.string.notify_only_when_highlighted),
+ getString(R.string.notify_never)
+ };
+ final AtomicInteger choice;
+ if (mConversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0) == Long.MAX_VALUE) {
+ choice = new AtomicInteger(2);
+ } else {
+ choice = new AtomicInteger(mConversation.alwaysNotify() ? 0 : 1);
+ }
+ builder.setSingleChoiceItems(choices, choice.get(), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ choice.set(which);
+ }
+ });
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (choice.get() == 2) {
+ mConversation.setMutedTill(Long.MAX_VALUE);
+ } else {
+ mConversation.setMutedTill(0);
+ mConversation.setAttribute(Conversation.ATTRIBUTE_ALWAYS_NOTIFY, String.valueOf(choice.get() == 0));
+ }
+ xmppConnectionService.updateConversation(mConversation);
+ updateView();
+ }
+ });
+ builder.create().show();
+ }
+ };
+
+ private OnClickListener mChangeConferenceSettings = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final MucOptions mucOptions = mConversation.getMucOptions();
+ AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this);
+ builder.setTitle(R.string.conference_options);
+ final String[] options;
+ final boolean[] values;
+ if (mAdvancedMode) {
+ options = new String[]{
+ getString(R.string.members_only),
+ getString(R.string.moderated),
+ getString(R.string.non_anonymous)
+ };
+ values = new boolean[]{
+ mucOptions.membersOnly(),
+ mucOptions.moderated(),
+ mucOptions.nonanonymous()
+ };
+ } else {
+ options = new String[]{
+ getString(R.string.members_only),
+ getString(R.string.non_anonymous)
+ };
+ values = new boolean[]{
+ mucOptions.membersOnly(),
+ mucOptions.nonanonymous()
+ };
+ }
+ builder.setMultiChoiceItems(options, values, new DialogInterface.OnMultiChoiceClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+ values[which] = isChecked;
+ }
+ });
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (!mucOptions.membersOnly() && values[0]) {
+ xmppConnectionService.changeAffiliationsInConference(mConversation,
+ MucOptions.Affiliation.NONE,
+ MucOptions.Affiliation.MEMBER);
+ }
+ Bundle options = new Bundle();
+ options.putString("muc#roomconfig_membersonly", values[0] ? "1" : "0");
+ if (values.length == 2) {
+ options.putString("muc#roomconfig_whois", values[1] ? "anyone" : "moderators");
+ } else if (values.length == 3) {
+ options.putString("muc#roomconfig_moderatedroom", values[1] ? "1" : "0");
+ options.putString("muc#roomconfig_whois", values[2] ? "anyone" : "moderators");
+ }
+ options.putString("muc#roomconfig_persistentroom", "1");
+ xmppConnectionService.pushConferenceConfiguration(mConversation,
+ options,
+ ConferenceDetailsActivity.this);
+ }
+ });
+ builder.create().show();
+ }
+ };
+ private OnValueEdited onSubjectEdited = new OnValueEdited() {
+
+ @Override
+ public void onValueEdited(String value) {
+ xmppConnectionService.pushSubjectToConference(mConversation, value);
+ }
+ };
+
+ @Override
+ public void onConversationUpdate() {
+ refreshUi();
+ }
+
+ @Override
+ public void onMucRosterUpdate() {
+ refreshUi();
+ }
+
+ @Override
+ protected void refreshUiReal() {
+ updateView();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_muc_details);
+ mYourNick = (TextView) findViewById(R.id.muc_your_nick);
+ mYourPhoto = (ImageView) findViewById(R.id.your_photo);
+ mEditNickButton = (ImageButton) findViewById(R.id.edit_nick_button);
+ mFullJid = (TextView) findViewById(R.id.muc_jabberid);
+ membersView = (LinearLayout) findViewById(R.id.muc_members);
+ mAccountJid = (TextView) findViewById(R.id.details_account);
+ mMoreDetails = (LinearLayout) findViewById(R.id.muc_more_details);
+ mMoreDetails.setVisibility(View.GONE);
+ mChangeConferenceSettingsButton = (ImageButton) findViewById(R.id.change_conference_button);
+ mChangeConferenceSettingsButton.setOnClickListener(this.mChangeConferenceSettings);
+ mInviteButton = (Button) findViewById(R.id.invite);
+ mInviteButton.setOnClickListener(inviteListener);
+ mConferenceType = (TextView) findViewById(R.id.muc_conference_type);
+ if (getActionBar() != null) {
+ getActionBar().setHomeButtonEnabled(true);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+ mEditNickButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ quickEdit(mConversation.getMucOptions().getActualNick(),
+ 0,
+ new OnValueEdited() {
+
+ @Override
+ public void onValueEdited(String value) {
+ xmppConnectionService.renameInMuc(mConversation, value, renameCallback);
+ }
+ });
+ }
+ });
+ this.mAdvancedMode = getPreferences().getBoolean("advanced_muc_mode", false);
+ this.mConferenceInfoTable = (LinearLayout) findViewById(R.id.muc_info_more);
+ mConferenceInfoTable.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE);
+ this.mConferenceInfoMam = (TextView) findViewById(R.id.muc_info_mam);
+ this.mNotifyStatusButton = (ImageButton) findViewById(R.id.notification_status_button);
+ this.mNotifyStatusButton.setOnClickListener(this.mNotifyStatusClickListener);
+ this.mNotifyStatusText = (TextView) findViewById(R.id.notification_status_text);
+ }
@Override
protected void onStart() {
@@ -277,245 +277,245 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
}
- @Override
- public boolean onOptionsItemSelected(MenuItem menuItem) {
- switch (menuItem.getItemId()) {
- case android.R.id.home:
- finish();
- break;
- case R.id.action_edit_subject:
- if (mConversation != null) {
- quickEdit(mConversation.getMucOptions().getSubject(),
- R.string.edit_subject_hint,
- this.onSubjectEdited);
- }
- break;
- case R.id.action_share:
- shareUri();
- break;
- case R.id.action_save_as_bookmark:
- saveAsBookmark();
- break;
- case R.id.action_delete_bookmark:
- deleteBookmark();
- break;
- case R.id.action_advanced_mode:
- this.mAdvancedMode = !menuItem.isChecked();
- menuItem.setChecked(this.mAdvancedMode);
- getPreferences().edit().putBoolean("advanced_muc_mode", mAdvancedMode).commit();
- mConferenceInfoTable.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE);
- invalidateOptionsMenu();
- updateView();
- break;
- }
- return super.onOptionsItemSelected(menuItem);
- }
-
- @Override
- protected String getShareableUri() {
- if (mConversation != null) {
- return "xmpp:" + mConversation.getJid().toBareJid().toString() + "?join";
- } else {
- return "";
- }
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- MenuItem menuItemSaveBookmark = menu.findItem(R.id.action_save_as_bookmark);
- MenuItem menuItemDeleteBookmark = menu.findItem(R.id.action_delete_bookmark);
- MenuItem menuItemAdvancedMode = menu.findItem(R.id.action_advanced_mode);
- MenuItem menuItemChangeSubject = menu.findItem(R.id.action_edit_subject);
- menuItemAdvancedMode.setChecked(mAdvancedMode);
- if (mConversation == null) {
- return true;
- }
- Account account = mConversation.getAccount();
- if (account.hasBookmarkFor(mConversation.getJid().toBareJid())) {
- menuItemSaveBookmark.setVisible(false);
- menuItemDeleteBookmark.setVisible(true);
- } else {
- menuItemDeleteBookmark.setVisible(false);
- menuItemSaveBookmark.setVisible(true);
- }
- menuItemChangeSubject.setVisible(mConversation.getMucOptions().canChangeSubject());
- return true;
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.muc_details, menu);
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
- Object tag = v.getTag();
- if (tag instanceof User) {
- getMenuInflater().inflate(R.menu.muc_details_context,menu);
- final User user = (User) tag;
- final User self = mConversation.getMucOptions().getSelf();
- this.mSelectedUser = user;
- String name;
- final Contact contact = user.getContact();
- if (contact != null) {
- name = contact.getDisplayName();
- } else if (user.getRealJid() != null){
- name = user.getRealJid().toBareJid().toString();
- } else {
- name = user.getName();
- }
- menu.setHeaderTitle(name);
- if (user.getRealJid() != null) {
- MenuItem startConversation = menu.findItem(R.id.start_conversation);
- MenuItem giveMembership = menu.findItem(R.id.give_membership);
- MenuItem removeMembership = menu.findItem(R.id.remove_membership);
- MenuItem giveAdminPrivileges = menu.findItem(R.id.give_admin_privileges);
- MenuItem removeAdminPrivileges = menu.findItem(R.id.remove_admin_privileges);
- MenuItem removeFromRoom = menu.findItem(R.id.remove_from_room);
- MenuItem banFromConference = menu.findItem(R.id.ban_from_conference);
- MenuItem invite = menu.findItem(R.id.invite);
- startConversation.setVisible(true);
- if (user.getRole() == MucOptions.Role.NONE) {
- invite.setVisible(true);
- }
- if (self.getAffiliation().ranks(MucOptions.Affiliation.ADMIN) &&
- self.getAffiliation().outranks(user.getAffiliation())) {
- if (mAdvancedMode) {
- if (user.getAffiliation() == MucOptions.Affiliation.NONE) {
- giveMembership.setVisible(true);
- } else {
- removeMembership.setVisible(true);
- }
- banFromConference.setVisible(true);
- } else {
- removeFromRoom.setVisible(true);
- }
- if (user.getAffiliation() != MucOptions.Affiliation.ADMIN) {
- giveAdminPrivileges.setVisible(true);
- } else {
- removeAdminPrivileges.setVisible(true);
- }
- }
- } else {
- MenuItem sendPrivateMessage = menu.findItem(R.id.send_private_message);
- sendPrivateMessage.setVisible(user.getRole().ranks(MucOptions.Role.PARTICIPANT));
- }
-
- }
- super.onCreateContextMenu(menu, v, menuInfo);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- Jid jid = mSelectedUser.getRealJid();
- switch (item.getItemId()) {
- case R.id.start_conversation:
- startConversation(mSelectedUser);
- return true;
- case R.id.give_admin_privileges:
- xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.ADMIN,this);
- return true;
- case R.id.give_membership:
- xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.MEMBER,this);
- return true;
- case R.id.remove_membership:
- xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.NONE,this);
- return true;
- case R.id.remove_admin_privileges:
- xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.MEMBER,this);
- return true;
- case R.id.remove_from_room:
- removeFromRoom(mSelectedUser);
- return true;
- case R.id.ban_from_conference:
- xmppConnectionService.changeAffiliationInConference(mConversation,jid, MucOptions.Affiliation.OUTCAST,this);
- if (mSelectedUser.getRole() != MucOptions.Role.NONE) {
- xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, this);
- }
- return true;
- case R.id.send_private_message:
- privateMsgInMuc(mConversation,mSelectedUser.getName());
- return true;
- case R.id.invite:
- xmppConnectionService.directInvite(mConversation, jid);
- return true;
- default:
- return super.onContextItemSelected(item);
- }
- }
-
- private void removeFromRoom(final User user) {
- if (mConversation.getMucOptions().membersOnly()) {
- xmppConnectionService.changeAffiliationInConference(mConversation,user.getRealJid(), MucOptions.Affiliation.NONE,this);
- if (user.getRole() != MucOptions.Role.NONE) {
- xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, ConferenceDetailsActivity.this);
- }
- } else {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.ban_from_conference);
- builder.setMessage(getString(R.string.removing_from_public_conference,user.getName()));
- builder.setNegativeButton(R.string.cancel,null);
- builder.setPositiveButton(R.string.ban_now,new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- xmppConnectionService.changeAffiliationInConference(mConversation,user.getRealJid(), MucOptions.Affiliation.OUTCAST,ConferenceDetailsActivity.this);
- if (user.getRole() != MucOptions.Role.NONE) {
- xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, ConferenceDetailsActivity.this);
- }
- }
- });
- builder.create().show();
- }
- }
-
- protected void startConversation(User user) {
- if (user.getRealJid() != null) {
- Conversation conversation = xmppConnectionService.findOrCreateConversation(this.mConversation.getAccount(),user.getRealJid().toBareJid(),false);
- switchToConversation(conversation);
- }
- }
-
- protected void saveAsBookmark() {
- xmppConnectionService.saveConversationAsBookmark(mConversation,
- mConversation.getMucOptions().getSubject());
- }
-
- protected void deleteBookmark() {
- Account account = mConversation.getAccount();
- Bookmark bookmark = mConversation.getBookmark();
- bookmark.unregisterConversation();
- account.getBookmarks().remove(bookmark);
- xmppConnectionService.pushBookmarks(account);
- }
-
- @Override
- void onBackendConnected() {
- if (mPendingConferenceInvite != null) {
- mPendingConferenceInvite.execute(this);
- mPendingConferenceInvite = null;
- }
- if (getIntent().getAction().equals(ACTION_VIEW_MUC)) {
- this.uuid = getIntent().getExtras().getString("uuid");
- }
- if (uuid != null) {
- this.mConversation = xmppConnectionService
- .findConversationByUuid(uuid);
- if (this.mConversation != null) {
- updateView();
- }
- }
- }
-
- private void updateView() {
- final MucOptions mucOptions = mConversation.getMucOptions();
- final User self = mucOptions.getSelf();
- String account;
- if (Config.DOMAIN_LOCK != null) {
- account = mConversation.getAccount().getJid().getLocalpart();
- } else {
- account = mConversation.getAccount().getJid().toBareJid().toString();
- }
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ switch (menuItem.getItemId()) {
+ case android.R.id.home:
+ finish();
+ break;
+ case R.id.action_edit_subject:
+ if (mConversation != null) {
+ quickEdit(mConversation.getMucOptions().getSubject(),
+ R.string.edit_subject_hint,
+ this.onSubjectEdited);
+ }
+ break;
+ case R.id.action_share:
+ shareUri();
+ break;
+ case R.id.action_save_as_bookmark:
+ saveAsBookmark();
+ break;
+ case R.id.action_delete_bookmark:
+ deleteBookmark();
+ break;
+ case R.id.action_advanced_mode:
+ this.mAdvancedMode = !menuItem.isChecked();
+ menuItem.setChecked(this.mAdvancedMode);
+ getPreferences().edit().putBoolean("advanced_muc_mode", mAdvancedMode).commit();
+ mConferenceInfoTable.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE);
+ invalidateOptionsMenu();
+ updateView();
+ break;
+ }
+ return super.onOptionsItemSelected(menuItem);
+ }
+
+ @Override
+ protected String getShareableUri() {
+ if (mConversation != null) {
+ return "xmpp:" + mConversation.getJid().toBareJid().toString() + "?join";
+ } else {
+ return "";
+ }
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ MenuItem menuItemSaveBookmark = menu.findItem(R.id.action_save_as_bookmark);
+ MenuItem menuItemDeleteBookmark = menu.findItem(R.id.action_delete_bookmark);
+ MenuItem menuItemAdvancedMode = menu.findItem(R.id.action_advanced_mode);
+ MenuItem menuItemChangeSubject = menu.findItem(R.id.action_edit_subject);
+ menuItemAdvancedMode.setChecked(mAdvancedMode);
+ if (mConversation == null) {
+ return true;
+ }
+ Account account = mConversation.getAccount();
+ if (account.hasBookmarkFor(mConversation.getJid().toBareJid())) {
+ menuItemSaveBookmark.setVisible(false);
+ menuItemDeleteBookmark.setVisible(true);
+ } else {
+ menuItemDeleteBookmark.setVisible(false);
+ menuItemSaveBookmark.setVisible(true);
+ }
+ menuItemChangeSubject.setVisible(mConversation.getMucOptions().canChangeSubject());
+ return true;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.muc_details, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ Object tag = v.getTag();
+ if (tag instanceof User) {
+ getMenuInflater().inflate(R.menu.muc_details_context, menu);
+ final User user = (User) tag;
+ final User self = mConversation.getMucOptions().getSelf();
+ this.mSelectedUser = user;
+ String name;
+ final Contact contact = user.getContact();
+ if (contact != null) {
+ name = contact.getDisplayName();
+ } else if (user.getRealJid() != null) {
+ name = user.getRealJid().toBareJid().toString();
+ } else {
+ name = user.getName();
+ }
+ menu.setHeaderTitle(name);
+ if (user.getRealJid() != null) {
+ MenuItem startConversation = menu.findItem(R.id.start_conversation);
+ MenuItem giveMembership = menu.findItem(R.id.give_membership);
+ MenuItem removeMembership = menu.findItem(R.id.remove_membership);
+ MenuItem giveAdminPrivileges = menu.findItem(R.id.give_admin_privileges);
+ MenuItem removeAdminPrivileges = menu.findItem(R.id.remove_admin_privileges);
+ MenuItem removeFromRoom = menu.findItem(R.id.remove_from_room);
+ MenuItem banFromConference = menu.findItem(R.id.ban_from_conference);
+ MenuItem invite = menu.findItem(R.id.invite);
+ startConversation.setVisible(true);
+ if (user.getRole() == MucOptions.Role.NONE) {
+ invite.setVisible(true);
+ }
+ if (self.getAffiliation().ranks(MucOptions.Affiliation.ADMIN) &&
+ self.getAffiliation().outranks(user.getAffiliation())) {
+ if (mAdvancedMode) {
+ if (user.getAffiliation() == MucOptions.Affiliation.NONE) {
+ giveMembership.setVisible(true);
+ } else {
+ removeMembership.setVisible(true);
+ }
+ banFromConference.setVisible(true);
+ } else {
+ removeFromRoom.setVisible(true);
+ }
+ if (user.getAffiliation() != MucOptions.Affiliation.ADMIN) {
+ giveAdminPrivileges.setVisible(true);
+ } else {
+ removeAdminPrivileges.setVisible(true);
+ }
+ }
+ } else {
+ MenuItem sendPrivateMessage = menu.findItem(R.id.send_private_message);
+ sendPrivateMessage.setVisible(user.getRole().ranks(MucOptions.Role.PARTICIPANT));
+ }
+
+ }
+ super.onCreateContextMenu(menu, v, menuInfo);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ Jid jid = mSelectedUser.getRealJid();
+ switch (item.getItemId()) {
+ case R.id.start_conversation:
+ startConversation(mSelectedUser);
+ return true;
+ case R.id.give_admin_privileges:
+ xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.ADMIN, this);
+ return true;
+ case R.id.give_membership:
+ xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.MEMBER, this);
+ return true;
+ case R.id.remove_membership:
+ xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.NONE, this);
+ return true;
+ case R.id.remove_admin_privileges:
+ xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.MEMBER, this);
+ return true;
+ case R.id.remove_from_room:
+ removeFromRoom(mSelectedUser);
+ return true;
+ case R.id.ban_from_conference:
+ xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.OUTCAST, this);
+ if (mSelectedUser.getRole() != MucOptions.Role.NONE) {
+ xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, this);
+ }
+ return true;
+ case R.id.send_private_message:
+ privateMsgInMuc(mConversation, mSelectedUser.getName());
+ return true;
+ case R.id.invite:
+ xmppConnectionService.directInvite(mConversation, jid);
+ return true;
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ private void removeFromRoom(final User user) {
+ if (mConversation.getMucOptions().membersOnly()) {
+ xmppConnectionService.changeAffiliationInConference(mConversation, user.getRealJid(), MucOptions.Affiliation.NONE, this);
+ if (user.getRole() != MucOptions.Role.NONE) {
+ xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, ConferenceDetailsActivity.this);
+ }
+ } else {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.ban_from_conference);
+ builder.setMessage(getString(R.string.removing_from_public_conference, user.getName()));
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.ban_now, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ xmppConnectionService.changeAffiliationInConference(mConversation, user.getRealJid(), MucOptions.Affiliation.OUTCAST, ConferenceDetailsActivity.this);
+ if (user.getRole() != MucOptions.Role.NONE) {
+ xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, ConferenceDetailsActivity.this);
+ }
+ }
+ });
+ builder.create().show();
+ }
+ }
+
+ protected void startConversation(User user) {
+ if (user.getRealJid() != null) {
+ Conversation conversation = xmppConnectionService.findOrCreateConversation(this.mConversation.getAccount(), user.getRealJid().toBareJid(), false);
+ switchToConversation(conversation);
+ }
+ }
+
+ protected void saveAsBookmark() {
+ xmppConnectionService.saveConversationAsBookmark(mConversation,
+ mConversation.getMucOptions().getSubject());
+ }
+
+ protected void deleteBookmark() {
+ Account account = mConversation.getAccount();
+ Bookmark bookmark = mConversation.getBookmark();
+ bookmark.unregisterConversation();
+ account.getBookmarks().remove(bookmark);
+ xmppConnectionService.pushBookmarks(account);
+ }
+
+ @Override
+ void onBackendConnected() {
+ if (mPendingConferenceInvite != null) {
+ mPendingConferenceInvite.execute(this);
+ mPendingConferenceInvite = null;
+ }
+ if (getIntent().getAction().equals(ACTION_VIEW_MUC)) {
+ this.uuid = getIntent().getExtras().getString("uuid");
+ }
+ if (uuid != null) {
+ this.mConversation = xmppConnectionService
+ .findConversationByUuid(uuid);
+ if (this.mConversation != null) {
+ updateView();
+ }
+ }
+ }
+
+ private void updateView() {
+ final MucOptions mucOptions = mConversation.getMucOptions();
+ final User self = mucOptions.getSelf();
+ String account;
+ if (Config.DOMAIN_LOCK != null) {
+ account = mConversation.getAccount().getJid().getLocalpart();
+ } else {
+ account = mConversation.getAccount().getJid().toBareJid().toString();
+ }
if (getActionBar() != null) {
final ActionBar ab = getActionBar();
ab.setCustomView(R.layout.ab_title);
@@ -528,61 +528,61 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
absubtitle.setVisibility(View.GONE);
absubtitle.setClickable(false);
}
- mAccountJid.setText(getString(R.string.using_account, account));
- mYourPhoto.setImageBitmap(avatarService().get(mConversation.getAccount(), getPixel(48)));
- setTitle(mConversation.getName());
- mFullJid.setText(mConversation.getJid().toBareJid().toString());
- mYourNick.setText(mucOptions.getActualNick());
- mRoleAffiliaton = (TextView) findViewById(R.id.muc_role);
- if (mucOptions.online()) {
- mMoreDetails.setVisibility(View.VISIBLE);
- final String status = getStatus(self);
- if (status != null) {
- mRoleAffiliaton.setVisibility(View.VISIBLE);
- mRoleAffiliaton.setText(status);
- } else {
- mRoleAffiliaton.setVisibility(View.GONE);
- }
- if (mucOptions.membersOnly()) {
- mConferenceType.setText(R.string.private_conference);
- } else {
- mConferenceType.setText(R.string.public_conference);
- }
- if (mucOptions.mamSupport()) {
- mConferenceInfoMam.setText(R.string.server_info_available);
- } else {
- mConferenceInfoMam.setText(R.string.server_info_unavailable);
- }
- if (self.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
- mChangeConferenceSettingsButton.setVisibility(View.VISIBLE);
- } else {
- mChangeConferenceSettingsButton.setVisibility(View.GONE);
- }
- }
-
- long mutedTill = mConversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL,0);
- if (mutedTill == Long.MAX_VALUE) {
- mNotifyStatusText.setText(R.string.notify_never);
- mNotifyStatusButton.setImageResource(R.drawable.ic_notifications_off_grey600_24dp);
- } else if (System.currentTimeMillis() < mutedTill) {
- mNotifyStatusText.setText(R.string.notify_paused);
- mNotifyStatusButton.setImageResource(R.drawable.ic_notifications_paused_grey600_24dp);
- } else if (mConversation.alwaysNotify()) {
- mNotifyStatusButton.setImageResource(R.drawable.ic_notifications_grey600_24dp);
- mNotifyStatusText.setText(R.string.notify_on_all_messages);
- } else {
- mNotifyStatusButton.setImageResource(R.drawable.ic_notifications_none_grey600_24dp);
- mNotifyStatusText.setText(R.string.notify_only_when_highlighted);
- }
-
- LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- membersView.removeAllViews();
- final ArrayList<User> users = mucOptions.getUsers();
+ mAccountJid.setText(getString(R.string.using_account, account));
+ mYourPhoto.setImageBitmap(avatarService().get(mConversation.getAccount(), getPixel(48)));
+ setTitle(mConversation.getName());
+ mFullJid.setText(mConversation.getJid().toBareJid().toString());
+ mYourNick.setText(mucOptions.getActualNick());
+ mRoleAffiliaton = (TextView) findViewById(R.id.muc_role);
+ if (mucOptions.online()) {
+ mMoreDetails.setVisibility(View.VISIBLE);
+ final String status = getStatus(self);
+ if (status != null) {
+ mRoleAffiliaton.setVisibility(View.VISIBLE);
+ mRoleAffiliaton.setText(status);
+ } else {
+ mRoleAffiliaton.setVisibility(View.GONE);
+ }
+ if (mucOptions.membersOnly()) {
+ mConferenceType.setText(R.string.private_conference);
+ } else {
+ mConferenceType.setText(R.string.public_conference);
+ }
+ if (mucOptions.mamSupport()) {
+ mConferenceInfoMam.setText(R.string.server_info_available);
+ } else {
+ mConferenceInfoMam.setText(R.string.server_info_unavailable);
+ }
+ if (self.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
+ mChangeConferenceSettingsButton.setVisibility(View.VISIBLE);
+ } else {
+ mChangeConferenceSettingsButton.setVisibility(View.GONE);
+ }
+ }
+
+ long mutedTill = mConversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0);
+ if (mutedTill == Long.MAX_VALUE) {
+ mNotifyStatusText.setText(R.string.notify_never);
+ mNotifyStatusButton.setImageResource(R.drawable.ic_notifications_off_grey600_24dp);
+ } else if (System.currentTimeMillis() < mutedTill) {
+ mNotifyStatusText.setText(R.string.notify_paused);
+ mNotifyStatusButton.setImageResource(R.drawable.ic_notifications_paused_grey600_24dp);
+ } else if (mConversation.alwaysNotify()) {
+ mNotifyStatusButton.setImageResource(R.drawable.ic_notifications_grey600_24dp);
+ mNotifyStatusText.setText(R.string.notify_on_all_messages);
+ } else {
+ mNotifyStatusButton.setImageResource(R.drawable.ic_notifications_none_grey600_24dp);
+ mNotifyStatusText.setText(R.string.notify_only_when_highlighted);
+ }
+
+ LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ membersView.removeAllViews();
+ final ArrayList<User> users = mucOptions.getUsers();
Collections.sort(users);
- for (final User user : users) {
+ for (final User user : users) {
final Contact contact = user.getContact();
- View view = inflater.inflate(R.layout.contact, membersView,false);
- this.setListItemBackgroundOnView(view);
+ View view = inflater.inflate(R.layout.contact, membersView, false);
+ this.setListItemBackgroundOnView(view);
if (contact != null) {
view.setOnClickListener(new OnClickListener() {
@Override
@@ -591,105 +591,105 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
});
}
- registerForContextMenu(view);
- view.setTag(user);
- TextView tvDisplayName = (TextView) view.findViewById(R.id.contact_display_name);
- TextView tvKey = (TextView) view.findViewById(R.id.key);
- TextView tvStatus = (TextView) view.findViewById(R.id.contact_jid);
- if (mAdvancedMode && user.getPgpKeyId() != 0) {
- tvKey.setVisibility(View.VISIBLE);
- tvKey.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- viewPgpKey(user);
- }
- });
- tvKey.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
- }
- String name = user.getName();
- if (contact != null) {
- tvDisplayName.setText(contact.getDisplayName());
- tvStatus.setText((name != null ? name+ " \u2022 " : "") + getStatus(user));
- } else {
- tvDisplayName.setText(name == null ? "" : name);
- tvStatus.setText(getStatus(user));
-
- }
- ImageView iv = (ImageView) view.findViewById(R.id.contact_photo);
- iv.setImageBitmap(avatarService().get(user, getPixel(48), false));
- membersView.addView(view);
- if (mConversation.getMucOptions().canInvite()) {
- mInviteButton.setVisibility(View.VISIBLE);
- } else {
- mInviteButton.setVisibility(View.GONE);
- }
- }
- }
-
- private String getStatus(User user) {
- if (mAdvancedMode) {
- StringBuilder builder = new StringBuilder();
- builder.append(getString(user.getAffiliation().getResId()));
- builder.append(" (");
- builder.append(getString(user.getRole().getResId()));
- builder.append(')');
- return builder.toString();
- } else {
- return getString(user.getAffiliation().getResId());
- }
- }
-
- private void viewPgpKey(User user) {
- PgpEngine pgp = xmppConnectionService.getPgpEngine();
- if (pgp != null) {
- PendingIntent intent = pgp.getIntentForKey(user.getPgpKeyId());
- if (intent != null) {
- try {
- startIntentSenderForResult(intent.getIntentSender(), 0, null, 0, 0, 0);
- } catch (SendIntentException ignored) {
-
- }
- }
- }
- }
-
- @Override
- public void onAffiliationChangedSuccessful(Jid jid) {
- refreshUi();
- }
-
- @Override
- public void onAffiliationChangeFailed(Jid jid, int resId) {
- displayToast(getString(resId,jid.toBareJid().toString()));
- }
-
- @Override
- public void onRoleChangedSuccessful(String nick) {
-
- }
-
- @Override
- public void onRoleChangeFailed(String nick, int resId) {
- displayToast(getString(resId,nick));
- }
-
- @Override
- public void onPushSucceeded() {
- displayToast(getString(R.string.modified_conference_options));
- }
-
- @Override
- public void onPushFailed() {
- displayToast(getString(R.string.could_not_modify_conference_options));
- }
-
- private void displayToast(final String msg) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(ConferenceDetailsActivity.this,msg,Toast.LENGTH_SHORT).show();
- }
- });
- }
+ registerForContextMenu(view);
+ view.setTag(user);
+ TextView tvDisplayName = (TextView) view.findViewById(R.id.contact_display_name);
+ TextView tvKey = (TextView) view.findViewById(R.id.key);
+ TextView tvStatus = (TextView) view.findViewById(R.id.contact_jid);
+ if (mAdvancedMode && user.getPgpKeyId() != 0) {
+ tvKey.setVisibility(View.VISIBLE);
+ tvKey.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ viewPgpKey(user);
+ }
+ });
+ tvKey.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
+ }
+ String name = user.getName();
+ if (contact != null) {
+ tvDisplayName.setText(contact.getDisplayName());
+ tvStatus.setText((name != null ? name + " \u2022 " : "") + getStatus(user));
+ } else {
+ tvDisplayName.setText(name == null ? "" : name);
+ tvStatus.setText(getStatus(user));
+
+ }
+ ImageView iv = (ImageView) view.findViewById(R.id.contact_photo);
+ iv.setImageBitmap(avatarService().get(user, getPixel(48), false));
+ membersView.addView(view);
+ if (mConversation.getMucOptions().canInvite()) {
+ mInviteButton.setVisibility(View.VISIBLE);
+ } else {
+ mInviteButton.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ private String getStatus(User user) {
+ if (mAdvancedMode) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(getString(user.getAffiliation().getResId()));
+ builder.append(" (");
+ builder.append(getString(user.getRole().getResId()));
+ builder.append(')');
+ return builder.toString();
+ } else {
+ return getString(user.getAffiliation().getResId());
+ }
+ }
+
+ private void viewPgpKey(User user) {
+ PgpEngine pgp = xmppConnectionService.getPgpEngine();
+ if (pgp != null) {
+ PendingIntent intent = pgp.getIntentForKey(user.getPgpKeyId());
+ if (intent != null) {
+ try {
+ startIntentSenderForResult(intent.getIntentSender(), 0, null, 0, 0, 0);
+ } catch (SendIntentException ignored) {
+
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onAffiliationChangedSuccessful(Jid jid) {
+ refreshUi();
+ }
+
+ @Override
+ public void onAffiliationChangeFailed(Jid jid, int resId) {
+ displayToast(getString(resId, jid.toBareJid().toString()));
+ }
+
+ @Override
+ public void onRoleChangedSuccessful(String nick) {
+
+ }
+
+ @Override
+ public void onRoleChangeFailed(String nick, int resId) {
+ displayToast(getString(resId, nick));
+ }
+
+ @Override
+ public void onPushSucceeded() {
+ displayToast(getString(R.string.modified_conference_options));
+ }
+
+ @Override
+ public void onPushFailed() {
+ displayToast(getString(R.string.could_not_modify_conference_options));
+ }
+
+ private void displayToast(final String msg) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(ConferenceDetailsActivity.this, msg, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java b/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java
index 51b9b33ec..31d131549 100644
--- a/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java
@@ -54,502 +54,502 @@ import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
public class ContactDetailsActivity extends OmemoActivity implements OnAccountUpdate, OnRosterUpdate, OnUpdateBlocklist, OnKeyStatusUpdated {
- public static final String ACTION_VIEW_CONTACT = "view_contact";
-
- private Conversation mConversation;
- private Contact contact;
- private DialogInterface.OnClickListener removeFromRoster = new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- xmppConnectionService.deleteContactOnServer(contact);
- }
- };
- private OnCheckedChangeListener mOnSendCheckedChange = new OnCheckedChangeListener() {
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView,
- boolean isChecked) {
- if (isChecked) {
- if (contact
- .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
- xmppConnectionService.sendPresencePacket(contact
- .getAccount(),
- xmppConnectionService.getPresenceGenerator()
- .sendPresenceUpdatesTo(contact));
- } else {
- contact.setOption(Contact.Options.PREEMPTIVE_GRANT);
- }
- } else {
- contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
- xmppConnectionService.sendPresencePacket(contact.getAccount(),
- xmppConnectionService.getPresenceGenerator()
- .stopPresenceUpdatesTo(contact));
- }
- }
- };
- private OnCheckedChangeListener mOnReceiveCheckedChange = new OnCheckedChangeListener() {
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView,
- boolean isChecked) {
- if (isChecked) {
- xmppConnectionService.sendPresencePacket(contact.getAccount(),
- xmppConnectionService.getPresenceGenerator()
- .requestPresenceUpdatesFrom(contact));
- } else {
- xmppConnectionService.sendPresencePacket(contact.getAccount(),
- xmppConnectionService.getPresenceGenerator()
- .stopPresenceUpdatesFrom(contact));
- }
- }
- };
- private Jid accountJid;
- private TextView lastseen;
- private Jid contactJid;
- private TextView contactJidTv;
- private TextView accountJidTv;
- private TextView statusMessage;
- private CheckBox send;
- private CheckBox receive;
- private Button addContactButton;
- private QuickContactBadge badge;
- private LinearLayout keys;
- private FlowLayout tags;
- private boolean showDynamicTags = false;
- private boolean showLastSeen = false;
- private String messageFingerprint;
-
- private DialogInterface.OnClickListener addToPhonebook = new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
- intent.setType(Contacts.CONTENT_ITEM_TYPE);
- intent.putExtra(Intents.Insert.IM_HANDLE, contact.getJid().toString());
- intent.putExtra(Intents.Insert.IM_PROTOCOL,
- CommonDataKinds.Im.PROTOCOL_JABBER);
- intent.putExtra("finishActivityOnSaveCompleted", true);
- ContactDetailsActivity.this.startActivityForResult(intent, 0);
- }
- };
-
- private OnClickListener onBadgeClick = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- Uri systemAccount = contact.getSystemAccount();
- if (systemAccount == null) {
- AlertDialog.Builder builder = new AlertDialog.Builder(
- ContactDetailsActivity.this);
- builder.setTitle(getString(R.string.action_add_phone_book));
- builder.setMessage(getString(R.string.add_phone_book_text,
- contact.getDisplayJid()));
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.setPositiveButton(getString(R.string.add), addToPhonebook);
- builder.create().show();
- } else {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(systemAccount);
- startActivity(intent);
- }
- }
- };
-
- @Override
- public void onRosterUpdate() {
- refreshUi();
- }
-
- @Override
- public void onAccountUpdate() {
- refreshUi();
- }
-
- @Override
- public void OnUpdateBlocklist(final Status status) {
- refreshUi();
- }
-
- @Override
- protected void refreshUiReal() {
- invalidateOptionsMenu();
- populateView();
- }
-
- @Override
- protected String getShareableUri() {
- if (contact != null) {
- return "xmpp:"+contact.getJid().toBareJid().toString();
- } else {
- return "";
- }
- }
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (getIntent().getAction().equals(ACTION_VIEW_CONTACT)) {
- try {
- this.accountJid = Jid.fromString(getIntent().getExtras().getString(EXTRA_ACCOUNT));
- } catch (final InvalidJidException ignored) {
- }
- try {
- this.contactJid = Jid.fromString(getIntent().getExtras().getString("contact"));
- } catch (final InvalidJidException ignored) {
- }
- }
- this.messageFingerprint = getIntent().getStringExtra("fingerprint");
- setContentView(R.layout.activity_contact_details);
-
- contactJidTv = (TextView) findViewById(R.id.details_contactjid);
- accountJidTv = (TextView) findViewById(R.id.details_account);
- lastseen = (TextView) findViewById(R.id.details_lastseen);
- statusMessage = (TextView) findViewById(R.id.status_message);
- send = (CheckBox) findViewById(R.id.details_send_presence);
- receive = (CheckBox) findViewById(R.id.details_receive_presence);
- badge = (QuickContactBadge) findViewById(R.id.details_contact_badge);
- addContactButton = (Button) findViewById(R.id.add_contact_button);
- addContactButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- showAddToRosterDialog(contact);
- }
- });
- keys = (LinearLayout) findViewById(R.id.details_contact_keys);
- tags = (FlowLayout) findViewById(R.id.tags);
- if (getActionBar() != null) {
- getActionBar().setHomeButtonEnabled(true);
- getActionBar().setDisplayHomeAsUpEnabled(true);
- }
- }
-
- @Override
- public void onStart() {
- super.onStart();
- final int theme = findTheme();
- if (this.mTheme != theme) {
- recreate();
- } else {
- final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
- this.showDynamicTags = preferences.getBoolean("show_dynamic_tags", false);
- this.showLastSeen = preferences.getBoolean("last_activity", false);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem menuItem) {
- final AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setNegativeButton(getString(R.string.cancel), null);
- switch (menuItem.getItemId()) {
- case android.R.id.home:
- finish();
- break;
- case R.id.action_share:
- shareUri();
- break;
- case R.id.action_delete_contact:
- builder.setTitle(getString(R.string.action_delete_contact))
- .setMessage(
- getString(R.string.remove_contact_text,
- contact.getDisplayJid()))
- .setPositiveButton(getString(R.string.delete),
- removeFromRoster).create().show();
- break;
- case R.id.action_edit_contact:
- Uri systemAccount = contact.getSystemAccount();
- if (systemAccount == null) {
- quickEdit(contact.getDisplayName(), 0, new OnValueEdited() {
-
- @Override
- public void onValueEdited(String value) {
- contact.setServerName(value);
- ContactDetailsActivity.this.xmppConnectionService
- .pushContactToServer(contact);
- populateView();
- }
- });
- } else {
- Intent intent = new Intent(Intent.ACTION_EDIT);
- intent.setDataAndType(systemAccount, Contacts.CONTENT_ITEM_TYPE);
- intent.putExtra("finishActivityOnSaveCompleted", true);
- startActivity(intent);
- }
- break;
- case R.id.action_block:
- BlockContactDialog.show(this, xmppConnectionService, contact);
- break;
- case R.id.action_unblock:
- BlockContactDialog.show(this, xmppConnectionService, contact);
- break;
- }
- return super.onOptionsItemSelected(menuItem);
- }
-
- @Override
- public boolean onCreateOptionsMenu(final Menu menu) {
- getMenuInflater().inflate(R.menu.contact_details, menu);
- MenuItem block = menu.findItem(R.id.action_block);
- MenuItem unblock = menu.findItem(R.id.action_unblock);
- MenuItem edit = menu.findItem(R.id.action_edit_contact);
- MenuItem delete = menu.findItem(R.id.action_delete_contact);
- if (contact == null) {
- return true;
- }
- final XmppConnection connection = contact.getAccount().getXmppConnection();
- if (connection != null && connection.getFeatures().blocking()) {
- if (this.contact.isBlocked()) {
- block.setVisible(false);
- } else {
- unblock.setVisible(false);
- }
- } else {
- unblock.setVisible(false);
- block.setVisible(false);
- }
- if (!contact.showInRoster()) {
- edit.setVisible(false);
- delete.setVisible(false);
- }
- return super.onCreateOptionsMenu(menu);
- }
-
- private void populateView() {
- if (contact == null) {
- return;
- }
- if (getActionBar() != null) {
- final ActionBar ab = getActionBar();
- ab.setCustomView(R.layout.ab_title);
- ab.setDisplayShowCustomEnabled(true);
- TextView abtitle = (TextView) findViewById(android.R.id.text1);
- TextView absubtitle = (TextView) findViewById(android.R.id.text2);
- abtitle.setText(contact.getDisplayName());
- abtitle.setSelected(true);
- abtitle.setClickable(false);
- absubtitle.setVisibility(View.GONE);
- absubtitle.setClickable(false);
- }
-
- invalidateOptionsMenu();
- setTitle(contact.getDisplayName());
- if (contact.showInRoster()) {
- send.setVisibility(View.VISIBLE);
- receive.setVisibility(View.VISIBLE);
- addContactButton.setVisibility(View.GONE);
- send.setOnCheckedChangeListener(null);
- receive.setOnCheckedChangeListener(null);
-
- List<String> statusMessages = contact.getPresences().getStatusMessages();
- if (statusMessages.size() == 0) {
- statusMessage.setVisibility(View.GONE);
- } else {
- StringBuilder builder = new StringBuilder();
- statusMessage.setVisibility(View.VISIBLE);
- int s = statusMessages.size();
- for(int i = 0; i < s; ++i) {
- if (s > 1) {
- builder.append("• ");
- }
- builder.append(statusMessages.get(i));
- if (i < s - 1) {
- builder.append("\n");
- }
- }
- statusMessage.setText(builder);
- }
-
- if (contact.getOption(Contact.Options.FROM)) {
- send.setText(R.string.send_presence_updates);
- send.setChecked(true);
- } else if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
- send.setChecked(false);
- send.setText(R.string.send_presence_updates);
- } else {
- send.setText(R.string.preemptively_grant);
- if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
- send.setChecked(true);
- } else {
- send.setChecked(false);
- }
- }
- if (contact.getOption(Contact.Options.TO)) {
- receive.setText(R.string.receive_presence_updates);
- receive.setChecked(true);
- } else {
- receive.setText(R.string.ask_for_presence_updates);
- if (contact.getOption(Contact.Options.ASKING)) {
- receive.setChecked(true);
- } else {
- receive.setChecked(false);
- }
- }
- if (contact.getAccount().isOnlineAndConnected()) {
- receive.setEnabled(true);
- send.setEnabled(true);
- } else {
- 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.setVisibility(View.VISIBLE);
- lastseen.setText(R.string.contact_blocked);
- } else {
- if (showLastSeen && contact.getLastseen() > 0) {
- lastseen.setVisibility(View.VISIBLE);
- lastseen.setText(UIHelper.lastseen(getApplicationContext(), contact.isActive(), contact.getLastseen()));
- } else {
- lastseen.setVisibility(View.GONE);
- }
- }
-
- if (contact.getPresences().size() > 1) {
- contactJidTv.setText(contact.getDisplayJid() + " ("
- + contact.getPresences().size() + ")");
- } else {
- contactJidTv.setText(contact.getDisplayJid());
- }
- String account;
- if (Config.DOMAIN_LOCK != null) {
- account = contact.getAccount().getJid().getLocalpart();
- } else {
- account = contact.getAccount().getJid().toBareJid().toString();
- }
- accountJidTv.setText(getString(R.string.using_account, account));
- badge.setImageBitmap(avatarService().get(contact, getPixel(Config.AVATAR_SIZE)));
- badge.setOnClickListener(this.onBadgeClick);
-
- keys.removeAllViews();
- boolean hasKeys = false;
- LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- if (Config.supportOtr()) {
- for (final String otrFingerprint : contact.getOtrFingerprints()) {
- hasKeys = true;
- View view = inflater.inflate(R.layout.contact_key, keys, false);
- TextView key = (TextView) view.findViewById(R.id.key);
- TextView keyType = (TextView) view.findViewById(R.id.key_type);
- ImageButton removeButton = (ImageButton) view
- .findViewById(R.id.button_remove);
- removeButton.setVisibility(View.VISIBLE);
- key.setText(CryptoHelper.prettifyFingerprint(otrFingerprint));
- if (otrFingerprint != null && otrFingerprint.equals(messageFingerprint)) {
- keyType.setText(R.string.otr_fingerprint_selected_message);
- keyType.setTextColor(getResources().getColor(R.color.accent));
- } else {
- keyType.setText(R.string.otr_fingerprint);
- }
- keys.addView(view);
- removeButton.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- confirmToDeleteFingerprint(otrFingerprint);
- }
- });
- }
- }
- if (Config.supportOmemo()) {
- for (final String fingerprint : contact.getAccount().getAxolotlService().getFingerprintsForContact(contact)) {
- boolean highlight = fingerprint.equals(messageFingerprint);
- hasKeys |= addFingerprintRow(keys, contact.getAccount(), fingerprint, highlight);
- }
- }
- if (Config.supportOpenPgp() && contact.getPgpKeyId() != 0) {
- hasKeys = true;
- View view = inflater.inflate(R.layout.contact_key, keys, false);
- TextView key = (TextView) view.findViewById(R.id.key);
- TextView keyType = (TextView) view.findViewById(R.id.key_type);
- keyType.setText(R.string.openpgp_key_id);
- if ("pgp".equals(messageFingerprint)) {
- keyType.setTextColor(getResources().getColor(R.color.accent));
- }
- key.setText(OpenPgpUtils.convertKeyIdToHex(contact.getPgpKeyId()));
- view.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- PgpEngine pgp = ContactDetailsActivity.this.xmppConnectionService
- .getPgpEngine();
- if (pgp != null) {
- PendingIntent intent = pgp.getIntentForKey(contact);
- if (intent != null) {
- try {
- startIntentSenderForResult(
- intent.getIntentSender(), 0, null, 0,
- 0, 0);
- } catch (SendIntentException e) {
-
- }
- }
- }
- }
- });
- keys.addView(view);
- }
- if (hasKeys) {
- keys.setVisibility(View.VISIBLE);
- } else {
- keys.setVisibility(View.GONE);
- }
-
- List<ListItem.Tag> tagList = contact.getTags(this);
- if (tagList.size() == 0 || !this.showDynamicTags) {
- tags.setVisibility(View.GONE);
- } else {
- tags.setVisibility(View.VISIBLE);
- tags.removeAllViewsInLayout();
- for(final ListItem.Tag tag : tagList) {
- final TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag,tags,false);
- tv.setText(tag.getName());
- tv.setBackgroundColor(tag.getColor());
- tags.addView(tv);
- }
- }
- }
-
- protected void confirmToDeleteFingerprint(final String fingerprint) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.delete_fingerprint);
- builder.setMessage(R.string.sure_delete_fingerprint);
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(R.string.delete,
- new android.content.DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (contact.deleteOtrFingerprint(fingerprint)) {
- populateView();
- xmppConnectionService.syncRosterToDisk(contact.getAccount());
- }
- }
-
- });
- builder.create().show();
- }
-
- public void onBackendConnected() {
- if ((accountJid != null) && (contactJid != null)) {
- Account account = xmppConnectionService
- .findAccountByJid(accountJid);
- if (account == null) {
- return;
- }
- this.contact = account.getRoster().getContact(contactJid);
- populateView();
- }
- }
-
- @Override
- public void onKeyStatusUpdated(AxolotlService.FetchStatus report) {
- refreshUi();
- }
+ public static final String ACTION_VIEW_CONTACT = "view_contact";
+
+ private Conversation mConversation;
+ private Contact contact;
+ private DialogInterface.OnClickListener removeFromRoster = new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ xmppConnectionService.deleteContactOnServer(contact);
+ }
+ };
+ private OnCheckedChangeListener mOnSendCheckedChange = new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ if (isChecked) {
+ if (contact
+ .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
+ xmppConnectionService.sendPresencePacket(contact
+ .getAccount(),
+ xmppConnectionService.getPresenceGenerator()
+ .sendPresenceUpdatesTo(contact));
+ } else {
+ contact.setOption(Contact.Options.PREEMPTIVE_GRANT);
+ }
+ } else {
+ contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
+ xmppConnectionService.sendPresencePacket(contact.getAccount(),
+ xmppConnectionService.getPresenceGenerator()
+ .stopPresenceUpdatesTo(contact));
+ }
+ }
+ };
+ private OnCheckedChangeListener mOnReceiveCheckedChange = new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ if (isChecked) {
+ xmppConnectionService.sendPresencePacket(contact.getAccount(),
+ xmppConnectionService.getPresenceGenerator()
+ .requestPresenceUpdatesFrom(contact));
+ } else {
+ xmppConnectionService.sendPresencePacket(contact.getAccount(),
+ xmppConnectionService.getPresenceGenerator()
+ .stopPresenceUpdatesFrom(contact));
+ }
+ }
+ };
+ private Jid accountJid;
+ private TextView lastseen;
+ private Jid contactJid;
+ private TextView contactJidTv;
+ private TextView accountJidTv;
+ private TextView statusMessage;
+ private CheckBox send;
+ private CheckBox receive;
+ private Button addContactButton;
+ private QuickContactBadge badge;
+ private LinearLayout keys;
+ private FlowLayout tags;
+ private boolean showDynamicTags = false;
+ private boolean showLastSeen = false;
+ private String messageFingerprint;
+
+ private DialogInterface.OnClickListener addToPhonebook = new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+ intent.setType(Contacts.CONTENT_ITEM_TYPE);
+ intent.putExtra(Intents.Insert.IM_HANDLE, contact.getJid().toString());
+ intent.putExtra(Intents.Insert.IM_PROTOCOL,
+ CommonDataKinds.Im.PROTOCOL_JABBER);
+ intent.putExtra("finishActivityOnSaveCompleted", true);
+ ContactDetailsActivity.this.startActivityForResult(intent, 0);
+ }
+ };
+
+ private OnClickListener onBadgeClick = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Uri systemAccount = contact.getSystemAccount();
+ if (systemAccount == null) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(
+ ContactDetailsActivity.this);
+ builder.setTitle(getString(R.string.action_add_phone_book));
+ builder.setMessage(getString(R.string.add_phone_book_text,
+ contact.getDisplayJid()));
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.setPositiveButton(getString(R.string.add), addToPhonebook);
+ builder.create().show();
+ } else {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(systemAccount);
+ startActivity(intent);
+ }
+ }
+ };
+
+ @Override
+ public void onRosterUpdate() {
+ refreshUi();
+ }
+
+ @Override
+ public void onAccountUpdate() {
+ refreshUi();
+ }
+
+ @Override
+ public void OnUpdateBlocklist(final Status status) {
+ refreshUi();
+ }
+
+ @Override
+ protected void refreshUiReal() {
+ invalidateOptionsMenu();
+ populateView();
+ }
+
+ @Override
+ protected String getShareableUri() {
+ if (contact != null) {
+ return "xmpp:" + contact.getJid().toBareJid().toString();
+ } else {
+ return "";
+ }
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getIntent().getAction().equals(ACTION_VIEW_CONTACT)) {
+ try {
+ this.accountJid = Jid.fromString(getIntent().getExtras().getString(EXTRA_ACCOUNT));
+ } catch (final InvalidJidException ignored) {
+ }
+ try {
+ this.contactJid = Jid.fromString(getIntent().getExtras().getString("contact"));
+ } catch (final InvalidJidException ignored) {
+ }
+ }
+ this.messageFingerprint = getIntent().getStringExtra("fingerprint");
+ setContentView(R.layout.activity_contact_details);
+
+ contactJidTv = (TextView) findViewById(R.id.details_contactjid);
+ accountJidTv = (TextView) findViewById(R.id.details_account);
+ lastseen = (TextView) findViewById(R.id.details_lastseen);
+ statusMessage = (TextView) findViewById(R.id.status_message);
+ send = (CheckBox) findViewById(R.id.details_send_presence);
+ receive = (CheckBox) findViewById(R.id.details_receive_presence);
+ badge = (QuickContactBadge) findViewById(R.id.details_contact_badge);
+ addContactButton = (Button) findViewById(R.id.add_contact_button);
+ addContactButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ showAddToRosterDialog(contact);
+ }
+ });
+ keys = (LinearLayout) findViewById(R.id.details_contact_keys);
+ tags = (FlowLayout) findViewById(R.id.tags);
+ if (getActionBar() != null) {
+ getActionBar().setHomeButtonEnabled(true);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ final int theme = findTheme();
+ if (this.mTheme != theme) {
+ recreate();
+ } else {
+ final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+ this.showDynamicTags = preferences.getBoolean("show_dynamic_tags", false);
+ this.showLastSeen = preferences.getBoolean("last_activity", false);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem menuItem) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ switch (menuItem.getItemId()) {
+ case android.R.id.home:
+ finish();
+ break;
+ case R.id.action_share:
+ shareUri();
+ break;
+ case R.id.action_delete_contact:
+ builder.setTitle(getString(R.string.action_delete_contact))
+ .setMessage(
+ getString(R.string.remove_contact_text,
+ contact.getDisplayJid()))
+ .setPositiveButton(getString(R.string.delete),
+ removeFromRoster).create().show();
+ break;
+ case R.id.action_edit_contact:
+ Uri systemAccount = contact.getSystemAccount();
+ if (systemAccount == null) {
+ quickEdit(contact.getDisplayName(), 0, new OnValueEdited() {
+
+ @Override
+ public void onValueEdited(String value) {
+ contact.setServerName(value);
+ ContactDetailsActivity.this.xmppConnectionService
+ .pushContactToServer(contact);
+ populateView();
+ }
+ });
+ } else {
+ Intent intent = new Intent(Intent.ACTION_EDIT);
+ intent.setDataAndType(systemAccount, Contacts.CONTENT_ITEM_TYPE);
+ intent.putExtra("finishActivityOnSaveCompleted", true);
+ startActivity(intent);
+ }
+ break;
+ case R.id.action_block:
+ BlockContactDialog.show(this, xmppConnectionService, contact);
+ break;
+ case R.id.action_unblock:
+ BlockContactDialog.show(this, xmppConnectionService, contact);
+ break;
+ }
+ return super.onOptionsItemSelected(menuItem);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ getMenuInflater().inflate(R.menu.contact_details, menu);
+ MenuItem block = menu.findItem(R.id.action_block);
+ MenuItem unblock = menu.findItem(R.id.action_unblock);
+ MenuItem edit = menu.findItem(R.id.action_edit_contact);
+ MenuItem delete = menu.findItem(R.id.action_delete_contact);
+ if (contact == null) {
+ return true;
+ }
+ final XmppConnection connection = contact.getAccount().getXmppConnection();
+ if (connection != null && connection.getFeatures().blocking()) {
+ if (this.contact.isBlocked()) {
+ block.setVisible(false);
+ } else {
+ unblock.setVisible(false);
+ }
+ } else {
+ unblock.setVisible(false);
+ block.setVisible(false);
+ }
+ if (!contact.showInRoster()) {
+ edit.setVisible(false);
+ delete.setVisible(false);
+ }
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ private void populateView() {
+ if (contact == null) {
+ return;
+ }
+ if (getActionBar() != null) {
+ final ActionBar ab = getActionBar();
+ ab.setCustomView(R.layout.ab_title);
+ ab.setDisplayShowCustomEnabled(true);
+ TextView abtitle = (TextView) findViewById(android.R.id.text1);
+ TextView absubtitle = (TextView) findViewById(android.R.id.text2);
+ abtitle.setText(contact.getDisplayName());
+ abtitle.setSelected(true);
+ abtitle.setClickable(false);
+ absubtitle.setVisibility(View.GONE);
+ absubtitle.setClickable(false);
+ }
+
+ invalidateOptionsMenu();
+ setTitle(contact.getDisplayName());
+ if (contact.showInRoster()) {
+ send.setVisibility(View.VISIBLE);
+ receive.setVisibility(View.VISIBLE);
+ addContactButton.setVisibility(View.GONE);
+ send.setOnCheckedChangeListener(null);
+ receive.setOnCheckedChangeListener(null);
+
+ List<String> statusMessages = contact.getPresences().getStatusMessages();
+ if (statusMessages.size() == 0) {
+ statusMessage.setVisibility(View.GONE);
+ } else {
+ StringBuilder builder = new StringBuilder();
+ statusMessage.setVisibility(View.VISIBLE);
+ int s = statusMessages.size();
+ for (int i = 0; i < s; ++i) {
+ if (s > 1) {
+ builder.append("• ");
+ }
+ builder.append(statusMessages.get(i));
+ if (i < s - 1) {
+ builder.append("\n");
+ }
+ }
+ statusMessage.setText(builder);
+ }
+
+ if (contact.getOption(Contact.Options.FROM)) {
+ send.setText(R.string.send_presence_updates);
+ send.setChecked(true);
+ } else if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
+ send.setChecked(false);
+ send.setText(R.string.send_presence_updates);
+ } else {
+ send.setText(R.string.preemptively_grant);
+ if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
+ send.setChecked(true);
+ } else {
+ send.setChecked(false);
+ }
+ }
+ if (contact.getOption(Contact.Options.TO)) {
+ receive.setText(R.string.receive_presence_updates);
+ receive.setChecked(true);
+ } else {
+ receive.setText(R.string.ask_for_presence_updates);
+ if (contact.getOption(Contact.Options.ASKING)) {
+ receive.setChecked(true);
+ } else {
+ receive.setChecked(false);
+ }
+ }
+ if (contact.getAccount().isOnlineAndConnected()) {
+ receive.setEnabled(true);
+ send.setEnabled(true);
+ } else {
+ 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.setVisibility(View.VISIBLE);
+ lastseen.setText(R.string.contact_blocked);
+ } else {
+ if (showLastSeen && contact.getLastseen() > 0) {
+ lastseen.setVisibility(View.VISIBLE);
+ lastseen.setText(UIHelper.lastseen(getApplicationContext(), contact.isActive(), contact.getLastseen()));
+ } else {
+ lastseen.setVisibility(View.GONE);
+ }
+ }
+
+ if (contact.getPresences().size() > 1) {
+ contactJidTv.setText(contact.getDisplayJid() + " ("
+ + contact.getPresences().size() + ")");
+ } else {
+ contactJidTv.setText(contact.getDisplayJid());
+ }
+ String account;
+ if (Config.DOMAIN_LOCK != null) {
+ account = contact.getAccount().getJid().getLocalpart();
+ } else {
+ account = contact.getAccount().getJid().toBareJid().toString();
+ }
+ accountJidTv.setText(getString(R.string.using_account, account));
+ badge.setImageBitmap(avatarService().get(contact, getPixel(Config.AVATAR_SIZE)));
+ badge.setOnClickListener(this.onBadgeClick);
+
+ keys.removeAllViews();
+ boolean hasKeys = false;
+ LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ if (Config.supportOtr()) {
+ for (final String otrFingerprint : contact.getOtrFingerprints()) {
+ hasKeys = true;
+ View view = inflater.inflate(R.layout.contact_key, keys, false);
+ TextView key = (TextView) view.findViewById(R.id.key);
+ TextView keyType = (TextView) view.findViewById(R.id.key_type);
+ ImageButton removeButton = (ImageButton) view
+ .findViewById(R.id.button_remove);
+ removeButton.setVisibility(View.VISIBLE);
+ key.setText(CryptoHelper.prettifyFingerprint(otrFingerprint));
+ if (otrFingerprint != null && otrFingerprint.equals(messageFingerprint)) {
+ keyType.setText(R.string.otr_fingerprint_selected_message);
+ keyType.setTextColor(getResources().getColor(R.color.accent));
+ } else {
+ keyType.setText(R.string.otr_fingerprint);
+ }
+ keys.addView(view);
+ removeButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ confirmToDeleteFingerprint(otrFingerprint);
+ }
+ });
+ }
+ }
+ if (Config.supportOmemo()) {
+ for (final String fingerprint : contact.getAccount().getAxolotlService().getFingerprintsForContact(contact)) {
+ boolean highlight = fingerprint.equals(messageFingerprint);
+ hasKeys |= addFingerprintRow(keys, contact.getAccount(), fingerprint, highlight);
+ }
+ }
+ if (Config.supportOpenPgp() && contact.getPgpKeyId() != 0) {
+ hasKeys = true;
+ View view = inflater.inflate(R.layout.contact_key, keys, false);
+ TextView key = (TextView) view.findViewById(R.id.key);
+ TextView keyType = (TextView) view.findViewById(R.id.key_type);
+ keyType.setText(R.string.openpgp_key_id);
+ if ("pgp".equals(messageFingerprint)) {
+ keyType.setTextColor(getResources().getColor(R.color.accent));
+ }
+ key.setText(OpenPgpUtils.convertKeyIdToHex(contact.getPgpKeyId()));
+ view.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ PgpEngine pgp = ContactDetailsActivity.this.xmppConnectionService
+ .getPgpEngine();
+ if (pgp != null) {
+ PendingIntent intent = pgp.getIntentForKey(contact);
+ if (intent != null) {
+ try {
+ startIntentSenderForResult(
+ intent.getIntentSender(), 0, null, 0,
+ 0, 0);
+ } catch (SendIntentException e) {
+
+ }
+ }
+ }
+ }
+ });
+ keys.addView(view);
+ }
+ if (hasKeys) {
+ keys.setVisibility(View.VISIBLE);
+ } else {
+ keys.setVisibility(View.GONE);
+ }
+
+ List<ListItem.Tag> tagList = contact.getTags(this);
+ if (tagList.size() == 0 || !this.showDynamicTags) {
+ tags.setVisibility(View.GONE);
+ } else {
+ tags.setVisibility(View.VISIBLE);
+ tags.removeAllViewsInLayout();
+ for (final ListItem.Tag tag : tagList) {
+ final TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag, tags, false);
+ tv.setText(tag.getName());
+ tv.setBackgroundColor(tag.getColor());
+ tags.addView(tv);
+ }
+ }
+ }
+
+ protected void confirmToDeleteFingerprint(final String fingerprint) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.delete_fingerprint);
+ builder.setMessage(R.string.sure_delete_fingerprint);
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.delete,
+ new android.content.DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (contact.deleteOtrFingerprint(fingerprint)) {
+ populateView();
+ xmppConnectionService.syncRosterToDisk(contact.getAccount());
+ }
+ }
+
+ });
+ builder.create().show();
+ }
+
+ public void onBackendConnected() {
+ if ((accountJid != null) && (contactJid != null)) {
+ Account account = xmppConnectionService
+ .findAccountByJid(accountJid);
+ if (account == null) {
+ return;
+ }
+ this.contact = account.getRoster().getContact(contactJid);
+ populateView();
+ }
+ }
+
+ @Override
+ public void onKeyStatusUpdated(AxolotlService.FetchStatus report) {
+ refreshUi();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationActivity.java b/src/main/java/de/pixart/messenger/ui/ConversationActivity.java
index 1fb23ff69..47992b221 100644
--- a/src/main/java/de/pixart/messenger/ui/ConversationActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ConversationActivity.java
@@ -86,119 +86,119 @@ import de.pixart.messenger.xmpp.jid.Jid;
import de.timroes.android.listview.EnhancedListView;
public class ConversationActivity extends XmppActivity
- implements OnAccountUpdate, OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast, View.OnClickListener {
-
- public static final String ACTION_VIEW_CONVERSATION = "de.pixart.messenger.VIEW";
- public static final String CONVERSATION = "conversationUuid";
- public static final String EXTRA_DOWNLOAD_UUID = "de.pixart.messenger.download_uuid";
- public static final String TEXT = "text";
- public static final String NICK = "nick";
- public static final String PRIVATE_MESSAGE = "pm";
-
- public static final int REQUEST_SEND_MESSAGE = 0x0201;
- public static final int REQUEST_DECRYPT_PGP = 0x0202;
- public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
- public static final int REQUEST_TRUST_KEYS_TEXT = 0x0208;
- public static final int REQUEST_TRUST_KEYS_MENU = 0x0209;
- public static final int REQUEST_START_DOWNLOAD = 0x0210;
- public static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
- public static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
- public static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
- public static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304;
- public static final int ATTACHMENT_CHOICE_LOCATION = 0x0305;
+ implements OnAccountUpdate, OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast, View.OnClickListener {
+
+ public static final String ACTION_VIEW_CONVERSATION = "de.pixart.messenger.VIEW";
+ public static final String CONVERSATION = "conversationUuid";
+ public static final String EXTRA_DOWNLOAD_UUID = "de.pixart.messenger.download_uuid";
+ public static final String TEXT = "text";
+ public static final String NICK = "nick";
+ public static final String PRIVATE_MESSAGE = "pm";
+
+ public static final int REQUEST_SEND_MESSAGE = 0x0201;
+ public static final int REQUEST_DECRYPT_PGP = 0x0202;
+ public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
+ public static final int REQUEST_TRUST_KEYS_TEXT = 0x0208;
+ public static final int REQUEST_TRUST_KEYS_MENU = 0x0209;
+ public static final int REQUEST_START_DOWNLOAD = 0x0210;
+ public static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
+ public static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
+ public static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
+ public static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304;
+ public static final int ATTACHMENT_CHOICE_LOCATION = 0x0305;
public static final int ATTACHMENT_CHOICE_CHOOSE_VIDEO = 0x0306;
public static final int ATTACHMENT_CHOICE_INVALID = 0x0399;
- private static final String STATE_OPEN_CONVERSATION = "state_open_conversation";
- private static final String STATE_PANEL_OPEN = "state_panel_open";
- private static final String STATE_PENDING_IMAGE_URI = "state_pending_image_uri";
+ private static final String STATE_OPEN_CONVERSATION = "state_open_conversation";
+ private static final String STATE_PANEL_OPEN = "state_panel_open";
+ private static final String STATE_PENDING_IMAGE_URI = "state_pending_image_uri";
private static final String STATE_PENDING_PHOTO_URI = "state_pending_photo_uri";
private static final String STATE_FIRST_VISIBLE = "first_visible";
private static final String STATE_OFFSET_FROM_TOP = "offset_from_top";
- final private List<Uri> mPendingImageUris = new ArrayList<>();
+ final private List<Uri> mPendingImageUris = new ArrayList<>();
final private List<Uri> mPendingPhotoUris = new ArrayList<>();
- final private List<Uri> mPendingFileUris = new ArrayList<>();
+ final private List<Uri> mPendingFileUris = new ArrayList<>();
final private List<Uri> mPendingVideoUris = new ArrayList<>();
- private String mOpenConversation = null;
- private boolean mPanelOpen = true;
- private Pair<Integer,Integer> mScrollPosition = null;
- private Uri mPendingGeoUri = null;
- private boolean forbidProcessingPendings = false;
- private Message mPendingDownloadableMessage = null;
+ private String mOpenConversation = null;
+ private boolean mPanelOpen = true;
+ private Pair<Integer, Integer> mScrollPosition = null;
+ private Uri mPendingGeoUri = null;
+ private boolean forbidProcessingPendings = false;
+ private Message mPendingDownloadableMessage = null;
- private boolean conversationWasSelectedByKeyboard = false;
+ private boolean conversationWasSelectedByKeyboard = false;
- private View mContentView;
+ private View mContentView;
- private List<Conversation> conversationList = new ArrayList<>();
- private Conversation swipedConversation = null;
- private Conversation mSelectedConversation = null;
- private EnhancedListView listView;
- private ConversationFragment mConversationFragment;
+ private List<Conversation> conversationList = new ArrayList<>();
+ private Conversation swipedConversation = null;
+ private Conversation mSelectedConversation = null;
+ private EnhancedListView listView;
+ private ConversationFragment mConversationFragment;
- private ArrayAdapter<Conversation> listAdapter;
+ private ArrayAdapter<Conversation> listAdapter;
- private boolean mActivityPaused = false;
- private AtomicBoolean mRedirected = new AtomicBoolean(false);
- private Pair<Integer, Intent> mPostponedActivityResult;
+ private boolean mActivityPaused = false;
+ private AtomicBoolean mRedirected = new AtomicBoolean(false);
+ private Pair<Integer, Intent> mPostponedActivityResult;
private boolean mUnprocessedNewIntent = false;
long FirstStartTime = -1;
- public Conversation getSelectedConversation() {
- return this.mSelectedConversation;
- }
-
- public void setSelectedConversation(Conversation conversation) {
- this.mSelectedConversation = conversation;
- }
-
- public void showConversationsOverview() {
- if (mContentView instanceof SlidingPaneLayout) {
- SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
- mSlidingPaneLayout.openPane();
- }
- }
-
- @Override
- protected String getShareableUri() {
- Conversation conversation = getSelectedConversation();
- if (conversation != null) {
- return conversation.getAccount().getShareableUri();
- } else {
- return "";
- }
- }
-
- public void hideConversationsOverview() {
- if (mContentView instanceof SlidingPaneLayout) {
- SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
- mSlidingPaneLayout.closePane();
- }
- }
-
- public boolean isConversationsOverviewHideable() {
- if (mContentView instanceof SlidingPaneLayout) {
- return true;
- } else {
- return false;
- }
- }
-
- public boolean isConversationsOverviewVisable() {
- if (mContentView instanceof SlidingPaneLayout) {
- SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
- return mSlidingPaneLayout.isOpen();
- } else {
- return true;
- }
- }
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (savedInstanceState != null) {
- mOpenConversation = savedInstanceState.getString(STATE_OPEN_CONVERSATION, null);
- mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true);
+ public Conversation getSelectedConversation() {
+ return this.mSelectedConversation;
+ }
+
+ public void setSelectedConversation(Conversation conversation) {
+ this.mSelectedConversation = conversation;
+ }
+
+ public void showConversationsOverview() {
+ if (mContentView instanceof SlidingPaneLayout) {
+ SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
+ mSlidingPaneLayout.openPane();
+ }
+ }
+
+ @Override
+ protected String getShareableUri() {
+ Conversation conversation = getSelectedConversation();
+ if (conversation != null) {
+ return conversation.getAccount().getShareableUri();
+ } else {
+ return "";
+ }
+ }
+
+ public void hideConversationsOverview() {
+ if (mContentView instanceof SlidingPaneLayout) {
+ SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
+ mSlidingPaneLayout.closePane();
+ }
+ }
+
+ public boolean isConversationsOverviewHideable() {
+ if (mContentView instanceof SlidingPaneLayout) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isConversationsOverviewVisable() {
+ if (mContentView instanceof SlidingPaneLayout) {
+ SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
+ return mSlidingPaneLayout.isOpen();
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ mOpenConversation = savedInstanceState.getString(STATE_OPEN_CONVERSATION, null);
+ mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true);
int pos = savedInstanceState.getInt(STATE_FIRST_VISIBLE, -1);
int offset = savedInstanceState.getInt(STATE_OFFSET_FROM_TOP, 1);
if (pos >= 0 && offset <= 0) {
@@ -208,166 +208,166 @@ public class ConversationActivity extends XmppActivity
mScrollPosition = null;
}
String pending_image = savedInstanceState.getString(STATE_PENDING_IMAGE_URI, null);
- if (pending_image != null) {
- Log.d(Config.LOGTAG,"ConversationActivity.onCreate() - restoring pending image uri");
- mPendingImageUris.clear();
- mPendingImageUris.add(Uri.parse(pending_image));
- }
+ if (pending_image != null) {
+ Log.d(Config.LOGTAG, "ConversationActivity.onCreate() - restoring pending image uri");
+ mPendingImageUris.clear();
+ mPendingImageUris.add(Uri.parse(pending_image));
+ }
String pending_photo = savedInstanceState.getString(STATE_PENDING_PHOTO_URI, null);
if (pending_photo != null) {
- Log.d(Config.LOGTAG,"ConversationActivity.onCreate() - restoring pending photo uri");
+ Log.d(Config.LOGTAG, "ConversationActivity.onCreate() - restoring pending photo uri");
mPendingPhotoUris.clear();
mPendingPhotoUris.add(Uri.parse(pending_photo));
}
- }
+ }
- setContentView(R.layout.fragment_conversations_overview);
+ setContentView(R.layout.fragment_conversations_overview);
this.mConversationFragment = new ConversationFragment();
- FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.replace(R.id.selected_conversation, this.mConversationFragment, "conversation");
- transaction.commit();
-
- listView = (EnhancedListView) findViewById(R.id.list);
- this.listAdapter = new ConversationAdapter(this, conversationList);
- listView.setAdapter(this.listAdapter);
-
- if (getActionBar() != null) {
- getActionBar().setDisplayHomeAsUpEnabled(false);
- getActionBar().setHomeButtonEnabled(false);
- }
-
- listView.setOnItemClickListener(new OnItemClickListener() {
-
- @Override
- public void onItemClick(AdapterView<?> arg0, View clickedView,
- int position, long arg3) {
- if (getSelectedConversation() != conversationList.get(position)) {
- setSelectedConversation(conversationList.get(position));
- ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation());
- conversationWasSelectedByKeyboard = false;
- }
- hideConversationsOverview();
- openConversation();
- }
- });
-
- listView.setDismissCallback(new EnhancedListView.OnDismissCallback() {
-
- @Override
- public EnhancedListView.Undoable onDismiss(final EnhancedListView enhancedListView, final int position) {
-
- final int index = listView.getFirstVisiblePosition();
- View v = listView.getChildAt(0);
- final int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop());
-
- try {
- swipedConversation = listAdapter.getItem(position);
- } catch (IndexOutOfBoundsException e) {
- return null;
- }
- listAdapter.remove(swipedConversation);
- xmppConnectionService.markRead(swipedConversation);
-
- final boolean formerlySelected = (getSelectedConversation() == swipedConversation);
- if (position == 0 && listAdapter.getCount() == 0) {
- endConversation(swipedConversation, false, true);
- return null;
- } else if (formerlySelected) {
- setSelectedConversation(listAdapter.getItem(0));
- ConversationActivity.this.mConversationFragment
- .reInit(getSelectedConversation());
- }
-
- return new EnhancedListView.Undoable() {
-
- @Override
- public void undo() {
- listAdapter.insert(swipedConversation, position);
- if (formerlySelected) {
- setSelectedConversation(swipedConversation);
- ConversationActivity.this.mConversationFragment
- .reInit(getSelectedConversation());
- }
- swipedConversation = null;
- listView.setSelectionFromTop(index + (listView.getChildCount() < position ? 1 : 0), top);
- }
-
- @Override
- public void discard() {
- if (!swipedConversation.isRead()
- && swipedConversation.getMode() == Conversation.MODE_SINGLE) {
- swipedConversation = null;
- return;
- }
- endConversation(swipedConversation, false, false);
- swipedConversation = null;
- }
-
- @Override
- public String getTitle() {
- if (swipedConversation.getMode() == Conversation.MODE_MULTI) {
- return getResources().getString(R.string.title_undo_swipe_out_muc);
- } else {
- return getResources().getString(R.string.title_undo_swipe_out_conversation);
- }
- }
- };
- }
- });
- //listView.enableSwipeToDismiss();
- listView.setSwipingLayout(R.id.swipeable_item);
- listView.setUndoStyle(EnhancedListView.UndoStyle.SINGLE_POPUP);
- listView.setUndoHideDelay(10000);
- listView.setRequireTouchBeforeDismiss(false);
- listView.setSwipeDirection(EnhancedListView.SwipeDirection.START); // swipe to left to close conversation
-
- mContentView = findViewById(R.id.content_view_spl);
- if (mContentView == null) {
- mContentView = findViewById(R.id.content_view_ll);
- }
- if (mContentView instanceof SlidingPaneLayout) {
- SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
- mSlidingPaneLayout.setParallaxDistance(150);
- mSlidingPaneLayout
- .setShadowResourceLeft(R.drawable.es_slidingpane_shadow);
- mSlidingPaneLayout.setSliderFadeColor(0);
- mSlidingPaneLayout.setPanelSlideListener(new PanelSlideListener() {
-
- @Override
- public void onPanelOpened(View arg0) {
- updateActionBarTitle();
- invalidateOptionsMenu();
- hideKeyboard();
- if (xmppConnectionServiceBound) {
- xmppConnectionService.getNotificationService()
- .setOpenConversation(null);
- }
- closeContextMenu();
- }
-
- @Override
- public void onPanelClosed(View arg0) {
- listView.discardUndo();
- openConversation();
- }
-
- @Override
- public void onPanelSlide(View arg0, float arg1) {
- // TODO Auto-generated method stub
-
- }
- });
- }
- }
-
- private boolean isPackageInstalled(String targetPackage){
+ FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ transaction.replace(R.id.selected_conversation, this.mConversationFragment, "conversation");
+ transaction.commit();
+
+ listView = (EnhancedListView) findViewById(R.id.list);
+ this.listAdapter = new ConversationAdapter(this, conversationList);
+ listView.setAdapter(this.listAdapter);
+
+ if (getActionBar() != null) {
+ getActionBar().setDisplayHomeAsUpEnabled(false);
+ getActionBar().setHomeButtonEnabled(false);
+ }
+
+ listView.setOnItemClickListener(new OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View clickedView,
+ int position, long arg3) {
+ if (getSelectedConversation() != conversationList.get(position)) {
+ setSelectedConversation(conversationList.get(position));
+ ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation());
+ conversationWasSelectedByKeyboard = false;
+ }
+ hideConversationsOverview();
+ openConversation();
+ }
+ });
+
+ listView.setDismissCallback(new EnhancedListView.OnDismissCallback() {
+
+ @Override
+ public EnhancedListView.Undoable onDismiss(final EnhancedListView enhancedListView, final int position) {
+
+ final int index = listView.getFirstVisiblePosition();
+ View v = listView.getChildAt(0);
+ final int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop());
+
+ try {
+ swipedConversation = listAdapter.getItem(position);
+ } catch (IndexOutOfBoundsException e) {
+ return null;
+ }
+ listAdapter.remove(swipedConversation);
+ xmppConnectionService.markRead(swipedConversation);
+
+ final boolean formerlySelected = (getSelectedConversation() == swipedConversation);
+ if (position == 0 && listAdapter.getCount() == 0) {
+ endConversation(swipedConversation, false, true);
+ return null;
+ } else if (formerlySelected) {
+ setSelectedConversation(listAdapter.getItem(0));
+ ConversationActivity.this.mConversationFragment
+ .reInit(getSelectedConversation());
+ }
+
+ return new EnhancedListView.Undoable() {
+
+ @Override
+ public void undo() {
+ listAdapter.insert(swipedConversation, position);
+ if (formerlySelected) {
+ setSelectedConversation(swipedConversation);
+ ConversationActivity.this.mConversationFragment
+ .reInit(getSelectedConversation());
+ }
+ swipedConversation = null;
+ listView.setSelectionFromTop(index + (listView.getChildCount() < position ? 1 : 0), top);
+ }
+
+ @Override
+ public void discard() {
+ if (!swipedConversation.isRead()
+ && swipedConversation.getMode() == Conversation.MODE_SINGLE) {
+ swipedConversation = null;
+ return;
+ }
+ endConversation(swipedConversation, false, false);
+ swipedConversation = null;
+ }
+
+ @Override
+ public String getTitle() {
+ if (swipedConversation.getMode() == Conversation.MODE_MULTI) {
+ return getResources().getString(R.string.title_undo_swipe_out_muc);
+ } else {
+ return getResources().getString(R.string.title_undo_swipe_out_conversation);
+ }
+ }
+ };
+ }
+ });
+ //listView.enableSwipeToDismiss();
+ listView.setSwipingLayout(R.id.swipeable_item);
+ listView.setUndoStyle(EnhancedListView.UndoStyle.SINGLE_POPUP);
+ listView.setUndoHideDelay(10000);
+ listView.setRequireTouchBeforeDismiss(false);
+ listView.setSwipeDirection(EnhancedListView.SwipeDirection.START); // swipe to left to close conversation
+
+ mContentView = findViewById(R.id.content_view_spl);
+ if (mContentView == null) {
+ mContentView = findViewById(R.id.content_view_ll);
+ }
+ if (mContentView instanceof SlidingPaneLayout) {
+ SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
+ mSlidingPaneLayout.setParallaxDistance(150);
+ mSlidingPaneLayout
+ .setShadowResourceLeft(R.drawable.es_slidingpane_shadow);
+ mSlidingPaneLayout.setSliderFadeColor(0);
+ mSlidingPaneLayout.setPanelSlideListener(new PanelSlideListener() {
+
+ @Override
+ public void onPanelOpened(View arg0) {
+ updateActionBarTitle();
+ invalidateOptionsMenu();
+ hideKeyboard();
+ if (xmppConnectionServiceBound) {
+ xmppConnectionService.getNotificationService()
+ .setOpenConversation(null);
+ }
+ closeContextMenu();
+ }
+
+ @Override
+ public void onPanelClosed(View arg0) {
+ listView.discardUndo();
+ openConversation();
+ }
+
+ @Override
+ public void onPanelSlide(View arg0, float arg1) {
+ // TODO Auto-generated method stub
+
+ }
+ });
+ }
+ }
+
+ private boolean isPackageInstalled(String targetPackage) {
List<ApplicationInfo> packages;
PackageManager pm;
pm = getPackageManager();
packages = pm.getInstalledApplications(0);
for (ApplicationInfo packageInfo : packages) {
- if(packageInfo.packageName.equals(targetPackage)) return true;
+ if (packageInfo.packageName.equals(targetPackage)) return true;
}
return false;
}
@@ -454,36 +454,36 @@ public class ConversationActivity extends XmppActivity
}
}
- @Override
- public void switchToConversation(Conversation conversation) {
- setSelectedConversation(conversation);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation());
- openConversation();
- }
- });
- }
+ @Override
+ public void switchToConversation(Conversation conversation) {
+ setSelectedConversation(conversation);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation());
+ openConversation();
+ }
+ });
+ }
- private void updateActionBarTitle() {
- updateActionBarTitle(isConversationsOverviewHideable() && !isConversationsOverviewVisable());
- }
+ private void updateActionBarTitle() {
+ updateActionBarTitle(isConversationsOverviewHideable() && !isConversationsOverviewVisable());
+ }
- private void updateActionBarTitle(boolean titleShouldBeName) {
+ private void updateActionBarTitle(boolean titleShouldBeName) {
final ActionBar ab = getActionBar();
final Conversation conversation = getSelectedConversation();
if (ab != null) {
if (titleShouldBeName && conversation != null) {
ab.setDisplayHomeAsUpEnabled(true);
ab.setHomeButtonEnabled(true);
- ab.setDisplayShowTitleEnabled(false);
- ab.setDisplayShowCustomEnabled(true);
- ab.setCustomView(R.layout.ab_title);
+ ab.setDisplayShowTitleEnabled(false);
+ ab.setDisplayShowCustomEnabled(true);
+ ab.setCustomView(R.layout.ab_title);
TextView abtitle = (TextView) findViewById(android.R.id.text1);
TextView absubtitle = (TextView) findViewById(android.R.id.text2);
if (conversation.getMode() == Conversation.MODE_SINGLE || useSubjectToIdentifyConference()) {
- abtitle.setText(conversation.getName());
+ abtitle.setText(conversation.getName());
abtitle.setOnClickListener(this);
abtitle.setSelected(true);
if (conversation.getMode() == Conversation.MODE_SINGLE && !this.getSelectedConversation().withSelf()) {
@@ -510,14 +510,14 @@ public class ConversationActivity extends XmppActivity
}
} else if (useSubjectToIdentifyConference()) {
if (conversation.getParticipants() != null) {
- absubtitle.setText(conversation.getParticipants());
+ absubtitle.setText(conversation.getParticipants());
absubtitle.setSelected(true);
- absubtitle.setOnClickListener(this);
- } else {
+ absubtitle.setOnClickListener(this);
+ } else {
absubtitle.setText(R.string.no_participants);
abtitle.setSelected(true);
absubtitle.setOnClickListener(this);
- }
+ }
}
} else {
abtitle.setText(conversation.getJid().toBareJid().toString());
@@ -537,206 +537,206 @@ public class ConversationActivity extends XmppActivity
}
}
- private void openConversation() {
- this.updateActionBarTitle();
- this.invalidateOptionsMenu();
- if (xmppConnectionServiceBound) {
- final Conversation conversation = getSelectedConversation();
- xmppConnectionService.getNotificationService().setOpenConversation(conversation);
- sendReadMarkerIfNecessary(conversation);
- }
- listAdapter.notifyDataSetChanged();
- }
+ private void openConversation() {
+ this.updateActionBarTitle();
+ this.invalidateOptionsMenu();
+ if (xmppConnectionServiceBound) {
+ final Conversation conversation = getSelectedConversation();
+ xmppConnectionService.getNotificationService().setOpenConversation(conversation);
+ sendReadMarkerIfNecessary(conversation);
+ }
+ listAdapter.notifyDataSetChanged();
+ }
- public void sendReadMarkerIfNecessary(final Conversation conversation) {
- if (!mActivityPaused && !mUnprocessedNewIntent && conversation != null) {
- xmppConnectionService.sendReadMarker(conversation);
- }
- }
+ public void sendReadMarkerIfNecessary(final Conversation conversation) {
+ if (!mActivityPaused && !mUnprocessedNewIntent && conversation != null) {
+ xmppConnectionService.sendReadMarker(conversation);
+ }
+ }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.conversations, menu);
- final MenuItem menuSecure = menu.findItem(R.id.action_security);
- final MenuItem menuArchiveChat = menu.findItem(R.id.action_archive_chat);
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.conversations, menu);
+ final MenuItem menuSecure = menu.findItem(R.id.action_security);
+ final MenuItem menuArchiveChat = menu.findItem(R.id.action_archive_chat);
final MenuItem menuArchiveMuc = menu.findItem(R.id.action_archive_muc);
- final MenuItem menuAttach = menu.findItem(R.id.action_attach_file);
- final MenuItem menuClearHistory = menu.findItem(R.id.action_clear_history);
- final MenuItem menuAdd = menu.findItem(R.id.action_add);
- final MenuItem menuInviteContact = menu.findItem(R.id.action_invite);
- final MenuItem menuMute = menu.findItem(R.id.action_mute);
- final MenuItem menuUnmute = menu.findItem(R.id.action_unmute);
- final MenuItem menuUpdater = menu.findItem(R.id.action_check_updates);
- final MenuItem menuInviteUser = menu.findItem(R.id.action_invite_user);
-
- if (isConversationsOverviewVisable() && isConversationsOverviewHideable()) {
- menuArchiveChat.setVisible(false);
+ final MenuItem menuAttach = menu.findItem(R.id.action_attach_file);
+ final MenuItem menuClearHistory = menu.findItem(R.id.action_clear_history);
+ final MenuItem menuAdd = menu.findItem(R.id.action_add);
+ final MenuItem menuInviteContact = menu.findItem(R.id.action_invite);
+ final MenuItem menuMute = menu.findItem(R.id.action_mute);
+ final MenuItem menuUnmute = menu.findItem(R.id.action_unmute);
+ final MenuItem menuUpdater = menu.findItem(R.id.action_check_updates);
+ final MenuItem menuInviteUser = menu.findItem(R.id.action_invite_user);
+
+ if (isConversationsOverviewVisable() && isConversationsOverviewHideable()) {
+ menuArchiveChat.setVisible(false);
menuArchiveMuc.setVisible(false);
- menuSecure.setVisible(false);
- menuInviteContact.setVisible(false);
- menuAttach.setVisible(false);
- menuClearHistory.setVisible(false);
- menuMute.setVisible(false);
- menuUnmute.setVisible(false);
- } else {
- menuAdd.setVisible(!isConversationsOverviewHideable());
- //hide settings, accounts and updater in all menus except in main window
- menuUpdater.setVisible(false);
- menuInviteUser.setVisible(false);
-
- if (this.getSelectedConversation() != null) {
+ menuSecure.setVisible(false);
+ menuInviteContact.setVisible(false);
+ menuAttach.setVisible(false);
+ menuClearHistory.setVisible(false);
+ menuMute.setVisible(false);
+ menuUnmute.setVisible(false);
+ } else {
+ menuAdd.setVisible(!isConversationsOverviewHideable());
+ //hide settings, accounts and updater in all menus except in main window
+ menuUpdater.setVisible(false);
+ menuInviteUser.setVisible(false);
+
+ if (this.getSelectedConversation() != null) {
if (this.getSelectedConversation().getMode() == Conversation.MODE_SINGLE) {
menuArchiveMuc.setVisible(false);
} else {
menuArchiveChat.setVisible(false);
}
- if (this.getSelectedConversation().getNextEncryption() != Message.ENCRYPTION_NONE) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- menuSecure.setIcon(R.drawable.ic_lock_white_24dp);
- } else {
- menuSecure.setIcon(R.drawable.ic_action_secure);
- }
- }
- if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) {
- menuAttach.setVisible(getSelectedConversation().getAccount().httpUploadAvailable() && getSelectedConversation().getMucOptions().participating());
- menuInviteContact.setVisible(getSelectedConversation().getMucOptions().canInvite());
- menuSecure.setVisible((Config.supportOpenPgp() || Config.supportOmemo()) && Config.multipleEncryptionChoices()); //only if pgp is supported we have a choice
- } else {
- menuSecure.setVisible(Config.multipleEncryptionChoices());
- }
- if (this.getSelectedConversation().isMuted()) {
- menuMute.setVisible(false);
- } else {
- menuUnmute.setVisible(false);
- }
- }
- }
- if (Config.supportOmemo()) {
- new Handler().post(new Runnable() {
- @Override
- public void run() {
- View view = findViewById(R.id.action_security);
- if (view != null) {
- view.setOnLongClickListener(new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- return quickOmemoDebugger(getSelectedConversation());
- }
- });
- }
- }
- });
- }
- return super.onCreateOptionsMenu(menu);
- }
-
- private boolean quickOmemoDebugger(Conversation c) {
- if (c != null) {
+ if (this.getSelectedConversation().getNextEncryption() != Message.ENCRYPTION_NONE) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ menuSecure.setIcon(R.drawable.ic_lock_white_24dp);
+ } else {
+ menuSecure.setIcon(R.drawable.ic_action_secure);
+ }
+ }
+ if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) {
+ menuAttach.setVisible(getSelectedConversation().getAccount().httpUploadAvailable() && getSelectedConversation().getMucOptions().participating());
+ menuInviteContact.setVisible(getSelectedConversation().getMucOptions().canInvite());
+ menuSecure.setVisible((Config.supportOpenPgp() || Config.supportOmemo()) && Config.multipleEncryptionChoices()); //only if pgp is supported we have a choice
+ } else {
+ menuSecure.setVisible(Config.multipleEncryptionChoices());
+ }
+ if (this.getSelectedConversation().isMuted()) {
+ menuMute.setVisible(false);
+ } else {
+ menuUnmute.setVisible(false);
+ }
+ }
+ }
+ if (Config.supportOmemo()) {
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ View view = findViewById(R.id.action_security);
+ if (view != null) {
+ view.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ return quickOmemoDebugger(getSelectedConversation());
+ }
+ });
+ }
+ }
+ });
+ }
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ private boolean quickOmemoDebugger(Conversation c) {
+ if (c != null) {
boolean single = c.getMode() == Conversation.MODE_SINGLE;
- AxolotlService axolotlService = c.getAccount().getAxolotlService();
- Pair<AxolotlService.AxolotlCapability,Jid> capabilityJidPair = axolotlService.isConversationAxolotlCapableDetailed(c);
- switch (capabilityJidPair.first) {
- case MISSING_PRESENCE:
- Toast.makeText(ConversationActivity.this,single ? getString(R.string.missing_presence_subscription) : getString(R.string.missing_presence_subscription_with_x,capabilityJidPair.second.toBareJid().toString()),Toast.LENGTH_SHORT).show();
- return true;
- case MISSING_KEYS:
- Toast.makeText(ConversationActivity.this,single ? getString(R.string.missing_omemo_keys) : getString(R.string.missing_keys_from_x,capabilityJidPair.second.toBareJid().toString()),Toast.LENGTH_SHORT).show();
+ AxolotlService axolotlService = c.getAccount().getAxolotlService();
+ Pair<AxolotlService.AxolotlCapability, Jid> capabilityJidPair = axolotlService.isConversationAxolotlCapableDetailed(c);
+ switch (capabilityJidPair.first) {
+ case MISSING_PRESENCE:
+ Toast.makeText(ConversationActivity.this, single ? getString(R.string.missing_presence_subscription) : getString(R.string.missing_presence_subscription_with_x, capabilityJidPair.second.toBareJid().toString()), Toast.LENGTH_SHORT).show();
return true;
- case WRONG_CONFIGURATION:
- Toast.makeText(ConversationActivity.this,R.string.wrong_conference_configuration, Toast.LENGTH_SHORT).show();
- return true;
- case NO_MEMBERS:
- Toast.makeText(ConversationActivity.this,R.string.this_conference_has_no_members, Toast.LENGTH_SHORT).show();
- return true;
- }
- }
- return false;
- }
-
- protected void selectPresenceToAttachFile(final int attachmentChoice, final int encryption) {
- final Conversation conversation = getSelectedConversation();
- final Account account = conversation.getAccount();
- final OnPresenceSelected callback = new OnPresenceSelected() {
-
- @Override
- public void onPresenceSelected() {
- Intent intent = new Intent();
- boolean chooser = false;
- String fallbackPackageId = null;
- switch (attachmentChoice) {
- case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
- intent.setAction(Intent.ACTION_GET_CONTENT);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
- intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
- }
- intent.setType("image/*");
- chooser = true;
- break;
+ case MISSING_KEYS:
+ Toast.makeText(ConversationActivity.this, single ? getString(R.string.missing_omemo_keys) : getString(R.string.missing_keys_from_x, capabilityJidPair.second.toBareJid().toString()), Toast.LENGTH_SHORT).show();
+ return true;
+ case WRONG_CONFIGURATION:
+ Toast.makeText(ConversationActivity.this, R.string.wrong_conference_configuration, Toast.LENGTH_SHORT).show();
+ return true;
+ case NO_MEMBERS:
+ Toast.makeText(ConversationActivity.this, R.string.this_conference_has_no_members, Toast.LENGTH_SHORT).show();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected void selectPresenceToAttachFile(final int attachmentChoice, final int encryption) {
+ final Conversation conversation = getSelectedConversation();
+ final Account account = conversation.getAccount();
+ final OnPresenceSelected callback = new OnPresenceSelected() {
+
+ @Override
+ public void onPresenceSelected() {
+ Intent intent = new Intent();
+ boolean chooser = false;
+ String fallbackPackageId = null;
+ switch (attachmentChoice) {
+ case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
+ intent.setAction(Intent.ACTION_GET_CONTENT);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
+ }
+ intent.setType("image/*");
+ chooser = true;
+ break;
case ATTACHMENT_CHOICE_CHOOSE_VIDEO:
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("video/*");
chooser = true;
break;
- case ATTACHMENT_CHOICE_TAKE_PHOTO:
- Uri uri = xmppConnectionService.getFileBackend().getTakePhotoUri();
- intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
- intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
- mPendingPhotoUris.clear();
- mPendingPhotoUris.add(uri);
- break;
- case ATTACHMENT_CHOICE_CHOOSE_FILE:
- chooser = true;
- intent.setType("*/*");
- intent.addCategory(Intent.CATEGORY_OPENABLE);
- intent.setAction(Intent.ACTION_GET_CONTENT);
- break;
+ case ATTACHMENT_CHOICE_TAKE_PHOTO:
+ Uri uri = xmppConnectionService.getFileBackend().getTakePhotoUri();
+ intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
+ intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
+ mPendingPhotoUris.clear();
+ mPendingPhotoUris.add(uri);
+ break;
+ case ATTACHMENT_CHOICE_CHOOSE_FILE:
+ chooser = true;
+ intent.setType("*/*");
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setAction(Intent.ACTION_GET_CONTENT);
+ break;
case ATTACHMENT_CHOICE_RECORD_VOICE:
- startActivityForResult(new Intent(getApplicationContext(), RecordingActivity.class),attachmentChoice);
+ startActivityForResult(new Intent(getApplicationContext(), RecordingActivity.class), attachmentChoice);
break;
case ATTACHMENT_CHOICE_LOCATION:
- startActivityForResult(new Intent(getApplicationContext(), ShareLocationActivity.class),attachmentChoice);
- break;
- }
- if (intent.resolveActivity(getPackageManager()) != null) {
+ startActivityForResult(new Intent(getApplicationContext(), ShareLocationActivity.class), attachmentChoice);
+ break;
+ }
+ if (intent.resolveActivity(getPackageManager()) != null) {
Log.d(Config.LOGTAG, "Attachment: " + attachmentChoice);
- if (chooser) {
- startActivityForResult(
- Intent.createChooser(intent, getString(R.string.perform_action_with)),
- attachmentChoice);
- } else {
- startActivityForResult(intent, attachmentChoice);
- }
- } else if (fallbackPackageId != null) {
- startActivity(getInstallApkIntent(fallbackPackageId));
- }
- }
- };
- if ((account.httpUploadAvailable() || attachmentChoice == ATTACHMENT_CHOICE_LOCATION) && encryption != Message.ENCRYPTION_OTR) {
- conversation.setNextCounterpart(null);
- callback.onPresenceSelected();
- } else {
- selectPresence(conversation, callback);
- }
- }
-
- private Intent getInstallApkIntent(final String packageId) {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(Uri.parse("market://details?id=" + packageId));
- if (intent.resolveActivity(getPackageManager()) != null) {
- return intent;
- } else {
- intent.setData(Uri.parse("http://play.google.com/store/apps/details?id=" + packageId));
- return intent;
- }
- }
-
- public void attachFile(final int attachmentChoice) {
- if (attachmentChoice != ATTACHMENT_CHOICE_LOCATION) {
- if (!hasStoragePermission(attachmentChoice)) {
- return;
- }
- }
+ if (chooser) {
+ startActivityForResult(
+ Intent.createChooser(intent, getString(R.string.perform_action_with)),
+ attachmentChoice);
+ } else {
+ startActivityForResult(intent, attachmentChoice);
+ }
+ } else if (fallbackPackageId != null) {
+ startActivity(getInstallApkIntent(fallbackPackageId));
+ }
+ }
+ };
+ if ((account.httpUploadAvailable() || attachmentChoice == ATTACHMENT_CHOICE_LOCATION) && encryption != Message.ENCRYPTION_OTR) {
+ conversation.setNextCounterpart(null);
+ callback.onPresenceSelected();
+ } else {
+ selectPresence(conversation, callback);
+ }
+ }
+
+ private Intent getInstallApkIntent(final String packageId) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse("market://details?id=" + packageId));
+ if (intent.resolveActivity(getPackageManager()) != null) {
+ return intent;
+ } else {
+ intent.setData(Uri.parse("http://play.google.com/store/apps/details?id=" + packageId));
+ return intent;
+ }
+ }
+
+ public void attachFile(final int attachmentChoice) {
+ if (attachmentChoice != ATTACHMENT_CHOICE_LOCATION) {
+ if (!hasStoragePermission(attachmentChoice)) {
+ return;
+ }
+ }
if (attachmentChoice == ATTACHMENT_CHOICE_RECORD_VOICE) {
if (!hasMicPermission(attachmentChoice)) {
return;
@@ -748,128 +748,128 @@ public class ConversationActivity extends XmppActivity
}
}
switch (attachmentChoice) {
- case ATTACHMENT_CHOICE_LOCATION:
- getPreferences().edit().putString("recently_used_quick_action", "location").apply();
- break;
- case ATTACHMENT_CHOICE_RECORD_VOICE:
- getPreferences().edit().putString("recently_used_quick_action", "voice").apply();
- break;
- case ATTACHMENT_CHOICE_TAKE_PHOTO:
- getPreferences().edit().putString("recently_used_quick_action", "photo").apply();
- break;
- case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
- getPreferences().edit().putString("recently_used_quick_action", "picture").apply();
- break;
- }
- final Conversation conversation = getSelectedConversation();
- final int encryption = conversation.getNextEncryption();
- final int mode = conversation.getMode();
- if (encryption == Message.ENCRYPTION_PGP) {
- if (hasPgp()) {
- if (mode == Conversation.MODE_SINGLE && conversation.getContact().getPgpKeyId() != 0) {
- xmppConnectionService.getPgpEngine().hasKey(
- conversation.getContact(),
- new UiCallback<Contact>() {
-
- @Override
- public void userInputRequried(PendingIntent pi, Contact contact) {
- ConversationActivity.this.runIntent(pi, attachmentChoice);
- }
-
- @Override
- public void success(Contact contact) {
- selectPresenceToAttachFile(attachmentChoice, encryption);
- }
-
- @Override
- public void error(int error, Contact contact) {
- replaceToast(getString(error));
- }
- });
- } else if (mode == Conversation.MODE_MULTI && conversation.getMucOptions().pgpKeysInUse()) {
- if (!conversation.getMucOptions().everybodyHasKeys()) {
- Toast warning = Toast
- .makeText(this,
- R.string.missing_public_keys,
- Toast.LENGTH_LONG);
- warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
- warning.show();
- }
- selectPresenceToAttachFile(attachmentChoice, encryption);
- } else {
- final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
- .findFragmentByTag("conversation");
- if (fragment != null) {
- fragment.showNoPGPKeyDialog(false,
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
+ case ATTACHMENT_CHOICE_LOCATION:
+ getPreferences().edit().putString("recently_used_quick_action", "location").apply();
+ break;
+ case ATTACHMENT_CHOICE_RECORD_VOICE:
+ getPreferences().edit().putString("recently_used_quick_action", "voice").apply();
+ break;
+ case ATTACHMENT_CHOICE_TAKE_PHOTO:
+ getPreferences().edit().putString("recently_used_quick_action", "photo").apply();
+ break;
+ case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
+ getPreferences().edit().putString("recently_used_quick_action", "picture").apply();
+ break;
+ }
+ final Conversation conversation = getSelectedConversation();
+ final int encryption = conversation.getNextEncryption();
+ final int mode = conversation.getMode();
+ if (encryption == Message.ENCRYPTION_PGP) {
+ if (hasPgp()) {
+ if (mode == Conversation.MODE_SINGLE && conversation.getContact().getPgpKeyId() != 0) {
+ xmppConnectionService.getPgpEngine().hasKey(
+ conversation.getContact(),
+ new UiCallback<Contact>() {
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Contact contact) {
+ ConversationActivity.this.runIntent(pi, attachmentChoice);
+ }
+
+ @Override
+ public void success(Contact contact) {
+ selectPresenceToAttachFile(attachmentChoice, encryption);
+ }
+
+ @Override
+ public void error(int error, Contact contact) {
+ replaceToast(getString(error));
+ }
+ });
+ } else if (mode == Conversation.MODE_MULTI && conversation.getMucOptions().pgpKeysInUse()) {
+ if (!conversation.getMucOptions().everybodyHasKeys()) {
+ Toast warning = Toast
+ .makeText(this,
+ R.string.missing_public_keys,
+ Toast.LENGTH_LONG);
+ warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
+ warning.show();
+ }
+ selectPresenceToAttachFile(attachmentChoice, encryption);
+ } else {
+ final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
+ .findFragmentByTag("conversation");
+ if (fragment != null) {
+ fragment.showNoPGPKeyDialog(false,
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
conversation.setNextEncryption(Message.ENCRYPTION_NONE);
xmppConnectionService.updateConversation(conversation);
selectPresenceToAttachFile(attachmentChoice, Message.ENCRYPTION_NONE);
- }
- });
- }
- }
- } else {
- showInstallPgpDialog();
- }
- } else {
- if (encryption != Message.ENCRYPTION_AXOLOTL || !trustKeysIfNeeded(REQUEST_TRUST_KEYS_MENU, attachmentChoice)) {
- selectPresenceToAttachFile(attachmentChoice, encryption);
- }
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
- if (grantResults.length > 0)
- if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- if (requestCode == REQUEST_START_DOWNLOAD) {
- if (this.mPendingDownloadableMessage != null) {
- startDownloadable(this.mPendingDownloadableMessage);
- }
- } else {
- attachFile(requestCode);
- }
- } else {
- Toast.makeText(this, R.string.no_permission, Toast.LENGTH_SHORT).show();
- }
- }
-
- public void startDownloadable(Message message) {
- if (!hasStoragePermission(ConversationActivity.REQUEST_START_DOWNLOAD)) {
- this.mPendingDownloadableMessage = message;
- return;
- }
- Transferable transferable = message.getTransferable();
- if (transferable != null) {
- if (!transferable.start()) {
- Toast.makeText(this, R.string.not_connected_try_again, Toast.LENGTH_SHORT).show();
- }
- } else if (message.treatAsDownloadable() != Message.Decision.NEVER) {
- xmppConnectionService.getHttpConnectionManager().createNewDownloadConnection(message, true);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- if (item.getItemId() == android.R.id.home) {
- showConversationsOverview();
- return true;
- } else if (item.getItemId() == R.id.action_add) {
- startActivity(new Intent(this, StartConversationActivity.class));
- return true;
- } else if (getSelectedConversation() != null) {
- switch (item.getItemId()) {
- case R.id.action_attach_file:
- attachFileDialog();
- break;
- case R.id.action_archive_chat:
- this.endConversation(getSelectedConversation());
- break;
+ }
+ });
+ }
+ }
+ } else {
+ showInstallPgpDialog();
+ }
+ } else {
+ if (encryption != Message.ENCRYPTION_AXOLOTL || !trustKeysIfNeeded(REQUEST_TRUST_KEYS_MENU, attachmentChoice)) {
+ selectPresenceToAttachFile(attachmentChoice, encryption);
+ }
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
+ if (grantResults.length > 0)
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ if (requestCode == REQUEST_START_DOWNLOAD) {
+ if (this.mPendingDownloadableMessage != null) {
+ startDownloadable(this.mPendingDownloadableMessage);
+ }
+ } else {
+ attachFile(requestCode);
+ }
+ } else {
+ Toast.makeText(this, R.string.no_permission, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ public void startDownloadable(Message message) {
+ if (!hasStoragePermission(ConversationActivity.REQUEST_START_DOWNLOAD)) {
+ this.mPendingDownloadableMessage = message;
+ return;
+ }
+ Transferable transferable = message.getTransferable();
+ if (transferable != null) {
+ if (!transferable.start()) {
+ Toast.makeText(this, R.string.not_connected_try_again, Toast.LENGTH_SHORT).show();
+ }
+ } else if (message.treatAsDownloadable() != Message.Decision.NEVER) {
+ xmppConnectionService.getHttpConnectionManager().createNewDownloadConnection(message, true);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ showConversationsOverview();
+ return true;
+ } else if (item.getItemId() == R.id.action_add) {
+ startActivity(new Intent(this, StartConversationActivity.class));
+ return true;
+ } else if (getSelectedConversation() != null) {
+ switch (item.getItemId()) {
+ case R.id.action_attach_file:
+ attachFileDialog();
+ break;
+ case R.id.action_archive_chat:
+ this.endConversation(getSelectedConversation());
+ break;
case R.id.action_archive_muc:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.action_end_conversation_muc));
@@ -884,80 +884,80 @@ public class ConversationActivity extends XmppActivity
});
builder.create().show();
break;
- case R.id.action_invite:
- inviteToConversation(getSelectedConversation());
- break;
- case R.id.action_security:
- selectEncryptionDialog(getSelectedConversation());
- break;
- case R.id.action_clear_history:
- clearHistoryDialog(getSelectedConversation());
- break;
- case R.id.action_mute:
- muteConversationDialog(getSelectedConversation());
- break;
- case R.id.action_unmute:
- unmuteConversation(getSelectedConversation());
- break;
- case R.id.action_block:
- BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation());
- break;
- case R.id.action_unblock:
- BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation());
- break;
- default:
- break;
- }
- return super.onOptionsItemSelected(item);
- } else {
- return super.onOptionsItemSelected(item);
- }
- }
-
- public void endConversation(Conversation conversation) {
- endConversation(conversation, true, true);
- }
-
- public void endConversation(Conversation conversation, boolean showOverview, boolean reinit) {
- if (showOverview) {
- showConversationsOverview();
- }
- xmppConnectionService.archiveConversation(conversation);
- if (reinit) {
- if (conversationList.size() > 0) {
- setSelectedConversation(conversationList.get(0));
- this.mConversationFragment.reInit(getSelectedConversation());
- } else {
- setSelectedConversation(null);
- if (mRedirected.compareAndSet(false, true)) {
- Intent intent = new Intent(this, StartConversationActivity.class);
- intent.putExtra("init", true);
- startActivity(intent);
- finish();
- }
- }
- }
- }
-
- @SuppressLint("InflateParams")
- protected void clearHistoryDialog(final Conversation conversation) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(getString(R.string.clear_conversation_history));
- View dialogView = getLayoutInflater().inflate(
- R.layout.dialog_clear_history, null);
+ case R.id.action_invite:
+ inviteToConversation(getSelectedConversation());
+ break;
+ case R.id.action_security:
+ selectEncryptionDialog(getSelectedConversation());
+ break;
+ case R.id.action_clear_history:
+ clearHistoryDialog(getSelectedConversation());
+ break;
+ case R.id.action_mute:
+ muteConversationDialog(getSelectedConversation());
+ break;
+ case R.id.action_unmute:
+ unmuteConversation(getSelectedConversation());
+ break;
+ case R.id.action_block:
+ BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation());
+ break;
+ case R.id.action_unblock:
+ BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation());
+ break;
+ default:
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ public void endConversation(Conversation conversation) {
+ endConversation(conversation, true, true);
+ }
+
+ public void endConversation(Conversation conversation, boolean showOverview, boolean reinit) {
+ if (showOverview) {
+ showConversationsOverview();
+ }
+ xmppConnectionService.archiveConversation(conversation);
+ if (reinit) {
+ if (conversationList.size() > 0) {
+ setSelectedConversation(conversationList.get(0));
+ this.mConversationFragment.reInit(getSelectedConversation());
+ } else {
+ setSelectedConversation(null);
+ if (mRedirected.compareAndSet(false, true)) {
+ Intent intent = new Intent(this, StartConversationActivity.class);
+ intent.putExtra("init", true);
+ startActivity(intent);
+ finish();
+ }
+ }
+ }
+ }
+
+ @SuppressLint("InflateParams")
+ protected void clearHistoryDialog(final Conversation conversation) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(getString(R.string.clear_conversation_history));
+ View dialogView = getLayoutInflater().inflate(
+ R.layout.dialog_clear_history, null);
final CheckBox endConversationCheckBox = (CheckBox) dialogView
.findViewById(R.id.end_conversation_checkbox);
if (conversation.getMode() == Conversation.MODE_SINGLE) {
endConversationCheckBox.setVisibility(View.VISIBLE);
}
- builder.setView(dialogView);
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.setPositiveButton(getString(R.string.delete_messages),
- new OnClickListener() {
+ 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);
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ ConversationActivity.this.xmppConnectionService.clearConversationHistory(conversation);
if (conversation.getMode() == Conversation.MODE_SINGLE) {
if (endConversationCheckBox.isChecked()) {
endConversation(conversation);
@@ -969,415 +969,415 @@ public class ConversationActivity extends XmppActivity
updateConversationList();
ConversationActivity.this.mConversationFragment.updateMessages();
}
- }
- });
- builder.create().show();
- }
-
- protected void attachFileDialog() {
- View menuAttachFile = findViewById(R.id.action_attach_file);
- if (menuAttachFile == null) {
- return;
- }
- PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile);
- attachFilePopup.inflate(R.menu.attachment_choices);
- if (new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION).resolveActivity(getPackageManager()) == null) {
- attachFilePopup.getMenu().findItem(R.id.attach_record_voice).setVisible(false);
- }
- if (new Intent("de.pixart.messenger.location.request").resolveActivity(getPackageManager()) == null) {
- attachFilePopup.getMenu().findItem(R.id.attach_location).setVisible(false);
- }
- attachFilePopup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
-
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.attach_choose_picture:
- attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
- break;
- case R.id.attach_take_picture:
- attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
- break;
+ }
+ });
+ builder.create().show();
+ }
+
+ protected void attachFileDialog() {
+ View menuAttachFile = findViewById(R.id.action_attach_file);
+ if (menuAttachFile == null) {
+ return;
+ }
+ PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile);
+ attachFilePopup.inflate(R.menu.attachment_choices);
+ if (new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION).resolveActivity(getPackageManager()) == null) {
+ attachFilePopup.getMenu().findItem(R.id.attach_record_voice).setVisible(false);
+ }
+ if (new Intent("de.pixart.messenger.location.request").resolveActivity(getPackageManager()) == null) {
+ attachFilePopup.getMenu().findItem(R.id.attach_location).setVisible(false);
+ }
+ attachFilePopup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.attach_choose_picture:
+ attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
+ break;
+ case R.id.attach_take_picture:
+ attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
+ break;
case R.id.attach_choose_video:
attachFile(ATTACHMENT_CHOICE_CHOOSE_VIDEO);
break;
- case R.id.attach_choose_file:
- attachFile(ATTACHMENT_CHOICE_CHOOSE_FILE);
- break;
- case R.id.attach_record_voice:
- attachFile(ATTACHMENT_CHOICE_RECORD_VOICE);
- break;
- case R.id.attach_location:
- attachFile(ATTACHMENT_CHOICE_LOCATION);
- break;
- }
- return false;
- }
- });
- attachFilePopup.show();
- }
-
- public void verifyOtrSessionDialog(final Conversation conversation, View view) {
- if (!conversation.hasValidOtrSession() || conversation.getOtrSession().getSessionStatus() != SessionStatus.ENCRYPTED) {
- Toast.makeText(this, R.string.otr_session_not_started, Toast.LENGTH_LONG).show();
- return;
- }
- if (view == null) {
- return;
- }
- PopupMenu popup = new PopupMenu(this, view);
- popup.inflate(R.menu.verification_choices);
- popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem menuItem) {
- Intent intent = new Intent(ConversationActivity.this, VerifyOTRActivity.class);
- intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
- intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
- intent.putExtra(EXTRA_ACCOUNT, conversation.getAccount().getJid().toBareJid().toString());
- switch (menuItem.getItemId()) {
- case R.id.scan_fingerprint:
- intent.putExtra("mode", VerifyOTRActivity.MODE_SCAN_FINGERPRINT);
- break;
- case R.id.ask_question:
- intent.putExtra("mode", VerifyOTRActivity.MODE_ASK_QUESTION);
- break;
- case R.id.manual_verification:
- intent.putExtra("mode", VerifyOTRActivity.MODE_MANUAL_VERIFICATION);
- break;
- }
- startActivity(intent);
- return true;
- }
- });
- popup.show();
- }
-
- protected void selectEncryptionDialog(final Conversation conversation) {
- View menuItemView = findViewById(R.id.action_security);
- if (menuItemView == null) {
- return;
- }
- PopupMenu popup = new PopupMenu(this, menuItemView);
- final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
- .findFragmentByTag("conversation");
- if (fragment != null) {
- popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
-
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.encryption_choice_none:
- conversation.setNextEncryption(Message.ENCRYPTION_NONE);
- item.setChecked(true);
- break;
- case R.id.encryption_choice_otr:
- conversation.setNextEncryption(Message.ENCRYPTION_OTR);
- item.setChecked(true);
- break;
- case R.id.encryption_choice_pgp:
- if (hasPgp()) {
- if (conversation.getAccount().getPgpSignature() != null) {
- conversation.setNextEncryption(Message.ENCRYPTION_PGP);
- item.setChecked(true);
- } else {
- announcePgp(conversation.getAccount(), conversation, onOpenPGPKeyPublished);
- }
- } else {
- showInstallPgpDialog();
- }
- break;
- case R.id.encryption_choice_axolotl:
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(conversation.getAccount())
- + "Enabled axolotl for Contact " + conversation.getContact().getJid());
- conversation.setNextEncryption(Message.ENCRYPTION_AXOLOTL);
- item.setChecked(true);
- break;
- default:
- conversation.setNextEncryption(Message.ENCRYPTION_NONE);
- break;
- }
+ case R.id.attach_choose_file:
+ attachFile(ATTACHMENT_CHOICE_CHOOSE_FILE);
+ break;
+ case R.id.attach_record_voice:
+ attachFile(ATTACHMENT_CHOICE_RECORD_VOICE);
+ break;
+ case R.id.attach_location:
+ attachFile(ATTACHMENT_CHOICE_LOCATION);
+ break;
+ }
+ return false;
+ }
+ });
+ attachFilePopup.show();
+ }
+
+ public void verifyOtrSessionDialog(final Conversation conversation, View view) {
+ if (!conversation.hasValidOtrSession() || conversation.getOtrSession().getSessionStatus() != SessionStatus.ENCRYPTED) {
+ Toast.makeText(this, R.string.otr_session_not_started, Toast.LENGTH_LONG).show();
+ return;
+ }
+ if (view == null) {
+ return;
+ }
+ PopupMenu popup = new PopupMenu(this, view);
+ popup.inflate(R.menu.verification_choices);
+ popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem) {
+ Intent intent = new Intent(ConversationActivity.this, VerifyOTRActivity.class);
+ intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
+ intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
+ intent.putExtra(EXTRA_ACCOUNT, conversation.getAccount().getJid().toBareJid().toString());
+ switch (menuItem.getItemId()) {
+ case R.id.scan_fingerprint:
+ intent.putExtra("mode", VerifyOTRActivity.MODE_SCAN_FINGERPRINT);
+ break;
+ case R.id.ask_question:
+ intent.putExtra("mode", VerifyOTRActivity.MODE_ASK_QUESTION);
+ break;
+ case R.id.manual_verification:
+ intent.putExtra("mode", VerifyOTRActivity.MODE_MANUAL_VERIFICATION);
+ break;
+ }
+ startActivity(intent);
+ return true;
+ }
+ });
+ popup.show();
+ }
+
+ protected void selectEncryptionDialog(final Conversation conversation) {
+ View menuItemView = findViewById(R.id.action_security);
+ if (menuItemView == null) {
+ return;
+ }
+ PopupMenu popup = new PopupMenu(this, menuItemView);
+ final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
+ .findFragmentByTag("conversation");
+ if (fragment != null) {
+ popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.encryption_choice_none:
+ conversation.setNextEncryption(Message.ENCRYPTION_NONE);
+ item.setChecked(true);
+ break;
+ case R.id.encryption_choice_otr:
+ conversation.setNextEncryption(Message.ENCRYPTION_OTR);
+ item.setChecked(true);
+ break;
+ case R.id.encryption_choice_pgp:
+ if (hasPgp()) {
+ if (conversation.getAccount().getPgpSignature() != null) {
+ conversation.setNextEncryption(Message.ENCRYPTION_PGP);
+ item.setChecked(true);
+ } else {
+ announcePgp(conversation.getAccount(), conversation, onOpenPGPKeyPublished);
+ }
+ } else {
+ showInstallPgpDialog();
+ }
+ break;
+ case R.id.encryption_choice_axolotl:
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(conversation.getAccount())
+ + "Enabled axolotl for Contact " + conversation.getContact().getJid());
+ conversation.setNextEncryption(Message.ENCRYPTION_AXOLOTL);
+ item.setChecked(true);
+ break;
+ default:
+ conversation.setNextEncryption(Message.ENCRYPTION_NONE);
+ break;
+ }
xmppConnectionService.updateConversation(conversation);
- fragment.updateChatMsgHint();
- invalidateOptionsMenu();
- refreshUi();
- return true;
- }
- });
- popup.inflate(R.menu.encryption_choices);
- MenuItem otr = popup.getMenu().findItem(R.id.encryption_choice_otr);
- MenuItem none = popup.getMenu().findItem(R.id.encryption_choice_none);
- MenuItem pgp = popup.getMenu().findItem(R.id.encryption_choice_pgp);
- MenuItem axolotl = popup.getMenu().findItem(R.id.encryption_choice_axolotl);
- pgp.setVisible(Config.supportOpenPgp());
- none.setVisible(Config.supportUnencrypted() || conversation.getMode() == Conversation.MODE_MULTI);
- otr.setVisible(Config.supportOtr());
- axolotl.setVisible(Config.supportOmemo());
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- otr.setVisible(false);
- }
- if (!conversation.getAccount().getAxolotlService().isConversationAxolotlCapable(conversation)) {
- axolotl.setEnabled(false);
- }
- switch (conversation.getNextEncryption()) {
- case Message.ENCRYPTION_NONE:
- none.setChecked(true);
- break;
- case Message.ENCRYPTION_OTR:
- otr.setChecked(true);
- break;
- case Message.ENCRYPTION_PGP:
- pgp.setChecked(true);
- break;
- case Message.ENCRYPTION_AXOLOTL:
- axolotl.setChecked(true);
- break;
- default:
- none.setChecked(true);
- break;
- }
- popup.show();
- }
- }
-
- protected void muteConversationDialog(final Conversation conversation) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- 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);
+ fragment.updateChatMsgHint();
+ invalidateOptionsMenu();
+ refreshUi();
+ return true;
+ }
+ });
+ popup.inflate(R.menu.encryption_choices);
+ MenuItem otr = popup.getMenu().findItem(R.id.encryption_choice_otr);
+ MenuItem none = popup.getMenu().findItem(R.id.encryption_choice_none);
+ MenuItem pgp = popup.getMenu().findItem(R.id.encryption_choice_pgp);
+ MenuItem axolotl = popup.getMenu().findItem(R.id.encryption_choice_axolotl);
+ pgp.setVisible(Config.supportOpenPgp());
+ none.setVisible(Config.supportUnencrypted() || conversation.getMode() == Conversation.MODE_MULTI);
+ otr.setVisible(Config.supportOtr());
+ axolotl.setVisible(Config.supportOmemo());
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ otr.setVisible(false);
+ }
+ if (!conversation.getAccount().getAxolotlService().isConversationAxolotlCapable(conversation)) {
+ axolotl.setEnabled(false);
+ }
+ switch (conversation.getNextEncryption()) {
+ case Message.ENCRYPTION_NONE:
+ none.setChecked(true);
+ break;
+ case Message.ENCRYPTION_OTR:
+ otr.setChecked(true);
+ break;
+ case Message.ENCRYPTION_PGP:
+ pgp.setChecked(true);
+ break;
+ case Message.ENCRYPTION_AXOLOTL:
+ axolotl.setChecked(true);
+ break;
+ default:
+ none.setChecked(true);
+ break;
+ }
+ popup.show();
+ }
+ }
+
+ protected void muteConversationDialog(final Conversation conversation) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ 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.updateConversation(conversation);
- updateConversationList();
- ConversationActivity.this.mConversationFragment.updateMessages();
- invalidateOptionsMenu();
- }
- });
- builder.create().show();
- }
-
- public void unmuteConversation(final Conversation conversation) {
- conversation.setMutedTill(0);
+ updateConversationList();
+ ConversationActivity.this.mConversationFragment.updateMessages();
+ invalidateOptionsMenu();
+ }
+ });
+ builder.create().show();
+ }
+
+ public void unmuteConversation(final Conversation conversation) {
+ conversation.setMutedTill(0);
this.xmppConnectionService.updateConversation(conversation);
- updateConversationList();
- ConversationActivity.this.mConversationFragment.updateMessages();
- invalidateOptionsMenu();
- }
-
- @Override
- public void onBackPressed() {
- if (!isConversationsOverviewVisable()) {
- showConversationsOverview();
- } else {
- moveTaskToBack(true);
- }
- }
-
- @Override
- public boolean onKeyUp(int key, KeyEvent event) {
- int rotation = getWindowManager().getDefaultDisplay().getRotation();
- final int upKey;
- final int downKey;
- switch (rotation) {
- case Surface.ROTATION_90:
- upKey = KeyEvent.KEYCODE_DPAD_LEFT;
- downKey = KeyEvent.KEYCODE_DPAD_RIGHT;
- break;
- case Surface.ROTATION_180:
- upKey = KeyEvent.KEYCODE_DPAD_DOWN;
- downKey = KeyEvent.KEYCODE_DPAD_UP;
- break;
- case Surface.ROTATION_270:
- upKey = KeyEvent.KEYCODE_DPAD_RIGHT;
- downKey = KeyEvent.KEYCODE_DPAD_LEFT;
- break;
- default:
- upKey = KeyEvent.KEYCODE_DPAD_UP;
- downKey = KeyEvent.KEYCODE_DPAD_DOWN;
- }
- final boolean modifier = event.isCtrlPressed() || (event.getMetaState() & KeyEvent.META_ALT_LEFT_ON) != 0;
- if (modifier && key == KeyEvent.KEYCODE_TAB && isConversationsOverviewHideable()) {
- toggleConversationsOverview();
- return true;
- } else if (modifier && key == KeyEvent.KEYCODE_SPACE) {
- startActivity(new Intent(this, StartConversationActivity.class));
- return true;
- } else if (modifier && key == downKey) {
- if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) {
- showConversationsOverview();
- }
- return selectDownConversation();
- } else if (modifier && key == upKey) {
- if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) {
- showConversationsOverview();
- }
- return selectUpConversation();
- } else if (modifier && key == KeyEvent.KEYCODE_1) {
- return openConversationByIndex(0);
- } else if (modifier && key == KeyEvent.KEYCODE_2) {
- return openConversationByIndex(1);
- } else if (modifier && key == KeyEvent.KEYCODE_3) {
- return openConversationByIndex(2);
- } else if (modifier && key == KeyEvent.KEYCODE_4) {
- return openConversationByIndex(3);
- } else if (modifier && key == KeyEvent.KEYCODE_5) {
- return openConversationByIndex(4);
- } else if (modifier && key == KeyEvent.KEYCODE_6) {
- return openConversationByIndex(5);
- } else if (modifier && key == KeyEvent.KEYCODE_7) {
- return openConversationByIndex(6);
- } else if (modifier && key == KeyEvent.KEYCODE_8) {
- return openConversationByIndex(7);
- } else if (modifier && key == KeyEvent.KEYCODE_9) {
- return openConversationByIndex(8);
- } else if (modifier && key == KeyEvent.KEYCODE_0) {
- return openConversationByIndex(9);
- } else {
- return super.onKeyUp(key, event);
- }
- }
-
- private void toggleConversationsOverview() {
- if (isConversationsOverviewVisable()) {
- hideConversationsOverview();
- if (mConversationFragment != null) {
- mConversationFragment.setFocusOnInputField();
- }
- } else {
- showConversationsOverview();
- }
- }
-
- private boolean selectUpConversation() {
- if (this.mSelectedConversation != null) {
- int index = this.conversationList.indexOf(this.mSelectedConversation);
- if (index > 0) {
- return openConversationByIndex(index - 1);
- }
- }
- return false;
- }
-
- private boolean selectDownConversation() {
- if (this.mSelectedConversation != null) {
- int index = this.conversationList.indexOf(this.mSelectedConversation);
- if (index != -1 && index < this.conversationList.size() - 1) {
- return openConversationByIndex(index + 1);
- }
- }
- return false;
- }
-
- private boolean openConversationByIndex(int index) {
- try {
- this.conversationWasSelectedByKeyboard = true;
- setSelectedConversation(this.conversationList.get(index));
- this.mConversationFragment.reInit(getSelectedConversation());
- if (index > listView.getLastVisiblePosition() - 1 || index < listView.getFirstVisiblePosition() + 1) {
- this.listView.setSelection(index);
- }
- openConversation();
- return true;
- } catch (IndexOutOfBoundsException e) {
- return false;
- }
- }
-
- @Override
- protected void onNewIntent(final Intent intent) {
- if (intent != null && ACTION_VIEW_CONVERSATION.equals(intent.getAction())) {
- mOpenConversation = null;
+ updateConversationList();
+ ConversationActivity.this.mConversationFragment.updateMessages();
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (!isConversationsOverviewVisable()) {
+ showConversationsOverview();
+ } else {
+ moveTaskToBack(true);
+ }
+ }
+
+ @Override
+ public boolean onKeyUp(int key, KeyEvent event) {
+ int rotation = getWindowManager().getDefaultDisplay().getRotation();
+ final int upKey;
+ final int downKey;
+ switch (rotation) {
+ case Surface.ROTATION_90:
+ upKey = KeyEvent.KEYCODE_DPAD_LEFT;
+ downKey = KeyEvent.KEYCODE_DPAD_RIGHT;
+ break;
+ case Surface.ROTATION_180:
+ upKey = KeyEvent.KEYCODE_DPAD_DOWN;
+ downKey = KeyEvent.KEYCODE_DPAD_UP;
+ break;
+ case Surface.ROTATION_270:
+ upKey = KeyEvent.KEYCODE_DPAD_RIGHT;
+ downKey = KeyEvent.KEYCODE_DPAD_LEFT;
+ break;
+ default:
+ upKey = KeyEvent.KEYCODE_DPAD_UP;
+ downKey = KeyEvent.KEYCODE_DPAD_DOWN;
+ }
+ final boolean modifier = event.isCtrlPressed() || (event.getMetaState() & KeyEvent.META_ALT_LEFT_ON) != 0;
+ if (modifier && key == KeyEvent.KEYCODE_TAB && isConversationsOverviewHideable()) {
+ toggleConversationsOverview();
+ return true;
+ } else if (modifier && key == KeyEvent.KEYCODE_SPACE) {
+ startActivity(new Intent(this, StartConversationActivity.class));
+ return true;
+ } else if (modifier && key == downKey) {
+ if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) {
+ showConversationsOverview();
+ }
+ return selectDownConversation();
+ } else if (modifier && key == upKey) {
+ if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) {
+ showConversationsOverview();
+ }
+ return selectUpConversation();
+ } else if (modifier && key == KeyEvent.KEYCODE_1) {
+ return openConversationByIndex(0);
+ } else if (modifier && key == KeyEvent.KEYCODE_2) {
+ return openConversationByIndex(1);
+ } else if (modifier && key == KeyEvent.KEYCODE_3) {
+ return openConversationByIndex(2);
+ } else if (modifier && key == KeyEvent.KEYCODE_4) {
+ return openConversationByIndex(3);
+ } else if (modifier && key == KeyEvent.KEYCODE_5) {
+ return openConversationByIndex(4);
+ } else if (modifier && key == KeyEvent.KEYCODE_6) {
+ return openConversationByIndex(5);
+ } else if (modifier && key == KeyEvent.KEYCODE_7) {
+ return openConversationByIndex(6);
+ } else if (modifier && key == KeyEvent.KEYCODE_8) {
+ return openConversationByIndex(7);
+ } else if (modifier && key == KeyEvent.KEYCODE_9) {
+ return openConversationByIndex(8);
+ } else if (modifier && key == KeyEvent.KEYCODE_0) {
+ return openConversationByIndex(9);
+ } else {
+ return super.onKeyUp(key, event);
+ }
+ }
+
+ private void toggleConversationsOverview() {
+ if (isConversationsOverviewVisable()) {
+ hideConversationsOverview();
+ if (mConversationFragment != null) {
+ mConversationFragment.setFocusOnInputField();
+ }
+ } else {
+ showConversationsOverview();
+ }
+ }
+
+ private boolean selectUpConversation() {
+ if (this.mSelectedConversation != null) {
+ int index = this.conversationList.indexOf(this.mSelectedConversation);
+ if (index > 0) {
+ return openConversationByIndex(index - 1);
+ }
+ }
+ return false;
+ }
+
+ private boolean selectDownConversation() {
+ if (this.mSelectedConversation != null) {
+ int index = this.conversationList.indexOf(this.mSelectedConversation);
+ if (index != -1 && index < this.conversationList.size() - 1) {
+ return openConversationByIndex(index + 1);
+ }
+ }
+ return false;
+ }
+
+ private boolean openConversationByIndex(int index) {
+ try {
+ this.conversationWasSelectedByKeyboard = true;
+ setSelectedConversation(this.conversationList.get(index));
+ this.mConversationFragment.reInit(getSelectedConversation());
+ if (index > listView.getLastVisiblePosition() - 1 || index < listView.getFirstVisiblePosition() + 1) {
+ this.listView.setSelection(index);
+ }
+ openConversation();
+ return true;
+ } catch (IndexOutOfBoundsException e) {
+ return false;
+ }
+ }
+
+ @Override
+ protected void onNewIntent(final Intent intent) {
+ if (intent != null && ACTION_VIEW_CONVERSATION.equals(intent.getAction())) {
+ mOpenConversation = null;
mUnprocessedNewIntent = true;
- if (xmppConnectionServiceBound) {
- handleViewConversationIntent(intent);
- intent.setAction(Intent.ACTION_MAIN);
- } else {
- setIntent(intent);
- }
- }
- }
-
- @Override
- public void onStart() {
- super.onStart();
- this.mRedirected.set(false);
- if (this.xmppConnectionServiceBound) {
- this.onBackendConnected();
- }
- if (conversationList.size() >= 1) {
- this.onConversationUpdate();
- }
- }
-
- @Override
- public void onPause() {
- listView.discardUndo();
- super.onPause();
- this.mActivityPaused = true;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- final int theme = findTheme();
- final boolean usingEnterKey = usingEnterKey();
- if (this.mTheme != theme || usingEnterKey != mUsingEnterKey) {
- recreate();
- }
- this.mActivityPaused = false;
-
- if (!isConversationsOverviewVisable() || !isConversationsOverviewHideable()) {
- sendReadMarkerIfNecessary(getSelectedConversation());
- }
- }
-
- @Override
- public void onSaveInstanceState(final Bundle savedInstanceState) {
- Conversation conversation = getSelectedConversation();
- if (conversation != null) {
- savedInstanceState.putString(STATE_OPEN_CONVERSATION, conversation.getUuid());
+ if (xmppConnectionServiceBound) {
+ handleViewConversationIntent(intent);
+ intent.setAction(Intent.ACTION_MAIN);
+ } else {
+ setIntent(intent);
+ }
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ this.mRedirected.set(false);
+ if (this.xmppConnectionServiceBound) {
+ this.onBackendConnected();
+ }
+ if (conversationList.size() >= 1) {
+ this.onConversationUpdate();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ listView.discardUndo();
+ super.onPause();
+ this.mActivityPaused = true;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ final int theme = findTheme();
+ final boolean usingEnterKey = usingEnterKey();
+ if (this.mTheme != theme || usingEnterKey != mUsingEnterKey) {
+ recreate();
+ }
+ this.mActivityPaused = false;
+
+ if (!isConversationsOverviewVisable() || !isConversationsOverviewHideable()) {
+ sendReadMarkerIfNecessary(getSelectedConversation());
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(final Bundle savedInstanceState) {
+ Conversation conversation = getSelectedConversation();
+ if (conversation != null) {
+ savedInstanceState.putString(STATE_OPEN_CONVERSATION, conversation.getUuid());
Pair<Integer, Integer> scrollPosition = mConversationFragment.getScrollPosition();
if (scrollPosition != null) {
savedInstanceState.putInt(STATE_FIRST_VISIBLE, scrollPosition.first);
savedInstanceState.putInt(STATE_OFFSET_FROM_TOP, scrollPosition.second);
}
} else {
- savedInstanceState.remove(STATE_OPEN_CONVERSATION);
- }
- savedInstanceState.putBoolean(STATE_PANEL_OPEN, isConversationsOverviewVisable());
- if (this.mPendingImageUris.size() >= 1) {
- Log.d(Config.LOGTAG,"ConversationActivity.onSaveInstanceState() - saving pending image uri");
+ savedInstanceState.remove(STATE_OPEN_CONVERSATION);
+ }
+ savedInstanceState.putBoolean(STATE_PANEL_OPEN, isConversationsOverviewVisable());
+ if (this.mPendingImageUris.size() >= 1) {
+ Log.d(Config.LOGTAG, "ConversationActivity.onSaveInstanceState() - saving pending image uri");
savedInstanceState.putString(STATE_PENDING_IMAGE_URI, this.mPendingImageUris.get(0).toString());
} else if (this.mPendingPhotoUris.size() >= 1) {
savedInstanceState.putString(STATE_PENDING_PHOTO_URI, this.mPendingPhotoUris.get(0).toString());
} else {
- savedInstanceState.remove(STATE_PENDING_IMAGE_URI);
+ savedInstanceState.remove(STATE_PENDING_IMAGE_URI);
savedInstanceState.remove(STATE_PENDING_PHOTO_URI);
- }
- super.onSaveInstanceState(savedInstanceState);
- }
+ }
+ super.onSaveInstanceState(savedInstanceState);
+ }
- private void clearPending() {
- mPendingImageUris.clear();
- mPendingFileUris.clear();
+ private void clearPending() {
+ mPendingImageUris.clear();
+ mPendingFileUris.clear();
mPendingVideoUris.clear();
mPendingPhotoUris.clear();
- mPendingGeoUri = null;
- mPostponedActivityResult = null;
- }
+ mPendingGeoUri = null;
+ mPostponedActivityResult = null;
+ }
- @Override
- void onBackendConnected() {
- this.xmppConnectionService.getNotificationService().setIsInForeground(true);
- updateConversationList();
+ @Override
+ void onBackendConnected() {
+ this.xmppConnectionService.getNotificationService().setIsInForeground(true);
+ updateConversationList();
Bundle extras = getIntent().getExtras();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
@@ -1390,13 +1390,13 @@ public class ConversationActivity extends XmppActivity
Log.d(Config.LOGTAG, "Device is running Android < SDK 23, no restart required: " + FirstStartTime);
}
- if (mPendingConferenceInvite != null) {
- if (mPendingConferenceInvite.execute(this)) {
- mToast = Toast.makeText(this, R.string.creating_conference, Toast.LENGTH_LONG);
- mToast.show();
- }
- mPendingConferenceInvite = null;
- }
+ if (mPendingConferenceInvite != null) {
+ if (mPendingConferenceInvite.execute(this)) {
+ mToast = Toast.makeText(this, R.string.creating_conference, Toast.LENGTH_LONG);
+ mToast.show();
+ }
+ mPendingConferenceInvite = null;
+ }
if (FirstStartTime == 0) {
Log.d(Config.LOGTAG, "First start time: " + FirstStartTime + ", restarting App");
@@ -1414,72 +1414,72 @@ public class ConversationActivity extends XmppActivity
startActivity(intent);
System.exit(0);
}
- final Intent intent = getIntent();
- if (xmppConnectionService.getAccounts().size() == 0) {
- if (mRedirected.compareAndSet(false, true)) {
- if (Config.X509_VERIFICATION) {
+ final Intent intent = getIntent();
+ if (xmppConnectionService.getAccounts().size() == 0) {
+ if (mRedirected.compareAndSet(false, true)) {
+ if (Config.X509_VERIFICATION) {
startActivity(new Intent(this, ManageAccountActivity.class));
} else if (Config.MAGIC_CREATE_DOMAIN != null) {
Log.d(Config.LOGTAG, "First start time: " + FirstStartTime);
startActivity(new Intent(this, WelcomeActivity.class));
} else {
Intent editAccount = new Intent(this, EditAccountActivity.class);
- editAccount.putExtra("init",true);
+ editAccount.putExtra("init", true);
startActivity(editAccount);
}
finish();
- }
- } else if (conversationList.size() <= 0) {
- if (mRedirected.compareAndSet(false, true)) {
- Account pendingAccount = xmppConnectionService.getPendingAccount();
- if (pendingAccount == null) {
- Intent startConversationActivity = new Intent(this, StartConversationActivity.class);
- intent.putExtra("init", true);
- startActivity(startConversationActivity);
- } else {
- switchToAccount(pendingAccount, true);
- }
- finish();
- }
- } else if (selectConversationByUuid(mOpenConversation)) {
- if (mPanelOpen) {
- showConversationsOverview();
- } else {
- if (isConversationsOverviewHideable()) {
- openConversation();
- updateActionBarTitle(true);
- }
- }
+ }
+ } else if (conversationList.size() <= 0) {
+ if (mRedirected.compareAndSet(false, true)) {
+ Account pendingAccount = xmppConnectionService.getPendingAccount();
+ if (pendingAccount == null) {
+ Intent startConversationActivity = new Intent(this, StartConversationActivity.class);
+ intent.putExtra("init", true);
+ startActivity(startConversationActivity);
+ } else {
+ switchToAccount(pendingAccount, true);
+ }
+ finish();
+ }
+ } else if (selectConversationByUuid(mOpenConversation)) {
+ if (mPanelOpen) {
+ showConversationsOverview();
+ } else {
+ if (isConversationsOverviewHideable()) {
+ openConversation();
+ updateActionBarTitle(true);
+ }
+ }
if (this.mConversationFragment.reInit(getSelectedConversation())) {
Log.d(Config.LOGTAG, "setting scroll position on fragment");
this.mConversationFragment.setScrollPosition(mScrollPosition);
}
mOpenConversation = null;
} else if (intent != null && ACTION_VIEW_CONVERSATION.equals(intent.getAction())) {
- clearPending();
- handleViewConversationIntent(intent);
- intent.setAction(Intent.ACTION_MAIN);
- } else if (getSelectedConversation() == null) {
- showConversationsOverview();
- clearPending();
- setSelectedConversation(conversationList.get(0));
- this.mConversationFragment.reInit(getSelectedConversation());
- } else {
- this.mConversationFragment.messageListAdapter.updatePreferences();
- this.mConversationFragment.messagesView.invalidateViews();
- this.mConversationFragment.setupIme();
- }
+ clearPending();
+ handleViewConversationIntent(intent);
+ intent.setAction(Intent.ACTION_MAIN);
+ } else if (getSelectedConversation() == null) {
+ showConversationsOverview();
+ clearPending();
+ setSelectedConversation(conversationList.get(0));
+ this.mConversationFragment.reInit(getSelectedConversation());
+ } else {
+ this.mConversationFragment.messageListAdapter.updatePreferences();
+ this.mConversationFragment.messagesView.invalidateViews();
+ this.mConversationFragment.setupIme();
+ }
if (xmppConnectionService.getAccounts().size() != 0) {
if (xmppConnectionService.hasInternetConnection()) {
if (xmppConnectionService.isWIFI() || (xmppConnectionService.isMobile() && !xmppConnectionService.isMobileRoaming()))
- AppUpdate();
+ AppUpdate();
}
}
- if (this.mPostponedActivityResult != null) {
- this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
- }
+ if (this.mPostponedActivityResult != null) {
+ this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
+ }
final boolean stopping;
if (Build.VERSION.SDK_INT >= 17) {
@@ -1488,193 +1488,193 @@ public class ConversationActivity extends XmppActivity
stopping = isFinishing();
}
- if (!forbidProcessingPendings) {
+ if (!forbidProcessingPendings) {
int ImageUrisCount = mPendingImageUris.size();
if (ImageUrisCount == 1) {
Uri uri = mPendingImageUris.get(0);
- Log.d(Config.LOGTAG,"ConversationActivity.onBackendConnected() - attaching image to conversations. stopping="+Boolean.toString(stopping));
+ Log.d(Config.LOGTAG, "ConversationActivity.onBackendConnected() - attaching image to conversations. stopping=" + Boolean.toString(stopping));
attachImageToConversation(getSelectedConversation(), uri);
} else {
for (Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) {
Uri foo = i.next();
- Log.d(Config.LOGTAG,"ConversationActivity.onBackendConnected() - attaching images to conversations. stopping="+Boolean.toString(stopping));
+ Log.d(Config.LOGTAG, "ConversationActivity.onBackendConnected() - attaching images to conversations. stopping=" + Boolean.toString(stopping));
attachImagesToConversation(getSelectedConversation(), foo);
}
}
for (Iterator<Uri> i = mPendingPhotoUris.iterator(); i.hasNext(); i.remove()) {
- Log.d(Config.LOGTAG,"ConversationActivity.onBackendConnected() - attaching photo to conversations. stopping="+Boolean.toString(stopping));
+ Log.d(Config.LOGTAG, "ConversationActivity.onBackendConnected() - attaching photo to conversations. stopping=" + Boolean.toString(stopping));
attachPhotoToConversation(getSelectedConversation(), i.next());
}
for (Iterator<Uri> i = mPendingVideoUris.iterator(); i.hasNext(); i.remove()) {
- Log.d(Config.LOGTAG,"ConversationActivity.onBackendConnected() - attaching video to conversations. stopping="+Boolean.toString(stopping));
+ Log.d(Config.LOGTAG, "ConversationActivity.onBackendConnected() - attaching video to conversations. stopping=" + Boolean.toString(stopping));
attachVideoToConversation(getSelectedConversation(), i.next());
}
- for (Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) {
- Log.d(Config.LOGTAG,"ConversationActivity.onBackendConnected() - attaching file to conversations. stopping="+Boolean.toString(stopping));
+ for (Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) {
+ Log.d(Config.LOGTAG, "ConversationActivity.onBackendConnected() - attaching file to conversations. stopping=" + Boolean.toString(stopping));
attachFileToConversation(getSelectedConversation(), i.next());
- }
-
- if (mPendingGeoUri != null) {
- attachLocationToConversation(getSelectedConversation(), mPendingGeoUri);
- mPendingGeoUri = null;
- }
- }
- forbidProcessingPendings = false;
-
- if (!ExceptionHelper.checkForCrash(this, this.xmppConnectionService)) {
- openBatteryOptimizationDialogIfNeeded();
- }
- }
-
- private void handleViewConversationIntent(final Intent intent) {
- final String uuid = intent.getStringExtra(CONVERSATION);
- final String downloadUuid = intent.getStringExtra(EXTRA_DOWNLOAD_UUID);
- final String text = intent.getStringExtra(TEXT);
- final String nick = intent.getStringExtra(NICK);
- final boolean pm = intent.getBooleanExtra(PRIVATE_MESSAGE, false);
- if (selectConversationByUuid(uuid)) {
- this.mConversationFragment.reInit(getSelectedConversation());
- if (nick != null) {
- if (pm) {
- Jid jid = getSelectedConversation().getJid();
- try {
- Jid next = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), nick);
- this.mConversationFragment.privateMessageWith(next);
- } catch (final InvalidJidException ignored) {
- //do nothing
- }
- } else {
- this.mConversationFragment.highlightInConference(nick);
- }
- } else {
- this.mConversationFragment.appendText(text);
- }
- hideConversationsOverview();
+ }
+
+ if (mPendingGeoUri != null) {
+ attachLocationToConversation(getSelectedConversation(), mPendingGeoUri);
+ mPendingGeoUri = null;
+ }
+ }
+ forbidProcessingPendings = false;
+
+ if (!ExceptionHelper.checkForCrash(this, this.xmppConnectionService)) {
+ openBatteryOptimizationDialogIfNeeded();
+ }
+ }
+
+ private void handleViewConversationIntent(final Intent intent) {
+ final String uuid = intent.getStringExtra(CONVERSATION);
+ final String downloadUuid = intent.getStringExtra(EXTRA_DOWNLOAD_UUID);
+ final String text = intent.getStringExtra(TEXT);
+ final String nick = intent.getStringExtra(NICK);
+ final boolean pm = intent.getBooleanExtra(PRIVATE_MESSAGE, false);
+ if (selectConversationByUuid(uuid)) {
+ this.mConversationFragment.reInit(getSelectedConversation());
+ if (nick != null) {
+ if (pm) {
+ Jid jid = getSelectedConversation().getJid();
+ try {
+ Jid next = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), nick);
+ this.mConversationFragment.privateMessageWith(next);
+ } catch (final InvalidJidException ignored) {
+ //do nothing
+ }
+ } else {
+ this.mConversationFragment.highlightInConference(nick);
+ }
+ } else {
+ this.mConversationFragment.appendText(text);
+ }
+ hideConversationsOverview();
mUnprocessedNewIntent = false;
- openConversation();
- if (mContentView instanceof SlidingPaneLayout) {
- updateActionBarTitle(true); //fixes bug where slp isn't properly closed yet
- }
- if (downloadUuid != null) {
- final Message message = mSelectedConversation.findMessageWithFileAndUuid(downloadUuid);
- if (message != null) {
- startDownloadable(message);
- }
- } else {
+ openConversation();
+ if (mContentView instanceof SlidingPaneLayout) {
+ updateActionBarTitle(true); //fixes bug where slp isn't properly closed yet
+ }
+ if (downloadUuid != null) {
+ final Message message = mSelectedConversation.findMessageWithFileAndUuid(downloadUuid);
+ if (message != null) {
+ startDownloadable(message);
+ }
+ } else {
mUnprocessedNewIntent = false;
}
- }
- }
-
- private boolean selectConversationByUuid(String uuid) {
- if (uuid == null) {
- return false;
- }
- for (Conversation aConversationList : conversationList) {
- if (aConversationList.getUuid().equals(uuid)) {
- setSelectedConversation(aConversationList);
- return true;
- }
- }
- return false;
- }
-
- @Override
- protected void unregisterListeners() {
- super.unregisterListeners();
- xmppConnectionService.getNotificationService().setOpenConversation(null);
- }
-
- @SuppressLint("NewApi")
- private static List<Uri> extractUriFromIntent(final Intent intent) {
- List<Uri> uris = new ArrayList<>();
- if (intent == null) {
- return uris;
- }
- Uri uri = intent.getData();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && uri == null) {
- final ClipData clipData = intent.getClipData();
- if (clipData != null) {
- for (int i = 0; i < clipData.getItemCount(); ++i) {
- uris.add(clipData.getItemAt(i).getUri());
- }
- }
- } else {
- uris.add(uri);
- }
- return uris;
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == RESULT_OK) {
- if (requestCode == REQUEST_DECRYPT_PGP) {
- mConversationFragment.onActivityResult(requestCode, resultCode, data);
- } else if (requestCode == REQUEST_CHOOSE_PGP_ID) {
- // the user chose OpenPGP for encryption and selected his key in the PGP provider
- if (xmppConnectionServiceBound) {
- if (data.getExtras().containsKey(OpenPgpApi.EXTRA_SIGN_KEY_ID)) {
- // associate selected PGP keyId with the account
- mSelectedConversation.getAccount().setPgpSignId(data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID));
- // we need to announce the key as described in XEP-027
- announcePgp(mSelectedConversation.getAccount(), null, onOpenPGPKeyPublished);
- } else {
- choosePgpSignId(mSelectedConversation.getAccount());
- }
- this.mPostponedActivityResult = null;
- } else {
- this.mPostponedActivityResult = new Pair<>(requestCode, data);
- }
- } else if (requestCode == REQUEST_ANNOUNCE_PGP) {
- if (xmppConnectionServiceBound) {
- announcePgp(mSelectedConversation.getAccount(), mSelectedConversation, onOpenPGPKeyPublished);
- this.mPostponedActivityResult = null;
- } else {
- this.mPostponedActivityResult = new Pair<>(requestCode, data);
- }
- } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
- mPendingImageUris.clear();
- mPendingImageUris.addAll(extractUriFromIntent(data));
- int ImageUrisCount = mPendingImageUris.size();
- if (xmppConnectionServiceBound) {
+ }
+ }
+
+ private boolean selectConversationByUuid(String uuid) {
+ if (uuid == null) {
+ return false;
+ }
+ for (Conversation aConversationList : conversationList) {
+ if (aConversationList.getUuid().equals(uuid)) {
+ setSelectedConversation(aConversationList);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void unregisterListeners() {
+ super.unregisterListeners();
+ xmppConnectionService.getNotificationService().setOpenConversation(null);
+ }
+
+ @SuppressLint("NewApi")
+ private static List<Uri> extractUriFromIntent(final Intent intent) {
+ List<Uri> uris = new ArrayList<>();
+ if (intent == null) {
+ return uris;
+ }
+ Uri uri = intent.getData();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && uri == null) {
+ final ClipData clipData = intent.getClipData();
+ if (clipData != null) {
+ for (int i = 0; i < clipData.getItemCount(); ++i) {
+ uris.add(clipData.getItemAt(i).getUri());
+ }
+ }
+ } else {
+ uris.add(uri);
+ }
+ return uris;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == RESULT_OK) {
+ if (requestCode == REQUEST_DECRYPT_PGP) {
+ mConversationFragment.onActivityResult(requestCode, resultCode, data);
+ } else if (requestCode == REQUEST_CHOOSE_PGP_ID) {
+ // the user chose OpenPGP for encryption and selected his key in the PGP provider
+ if (xmppConnectionServiceBound) {
+ if (data.getExtras().containsKey(OpenPgpApi.EXTRA_SIGN_KEY_ID)) {
+ // associate selected PGP keyId with the account
+ mSelectedConversation.getAccount().setPgpSignId(data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID));
+ // we need to announce the key as described in XEP-027
+ announcePgp(mSelectedConversation.getAccount(), null, onOpenPGPKeyPublished);
+ } else {
+ choosePgpSignId(mSelectedConversation.getAccount());
+ }
+ this.mPostponedActivityResult = null;
+ } else {
+ this.mPostponedActivityResult = new Pair<>(requestCode, data);
+ }
+ } else if (requestCode == REQUEST_ANNOUNCE_PGP) {
+ if (xmppConnectionServiceBound) {
+ announcePgp(mSelectedConversation.getAccount(), mSelectedConversation, onOpenPGPKeyPublished);
+ this.mPostponedActivityResult = null;
+ } else {
+ this.mPostponedActivityResult = new Pair<>(requestCode, data);
+ }
+ } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
+ mPendingImageUris.clear();
+ mPendingImageUris.addAll(extractUriFromIntent(data));
+ int ImageUrisCount = mPendingImageUris.size();
+ if (xmppConnectionServiceBound) {
if (ImageUrisCount == 1) {
Uri uri = mPendingImageUris.get(0);
- Log.d(Config.LOGTAG,"ConversationActivity.onActivityResult() - attaching image to conversations. CHOOSE_IMAGE");
+ Log.d(Config.LOGTAG, "ConversationActivity.onActivityResult() - attaching image to conversations. CHOOSE_IMAGE");
attachImageToConversation(getSelectedConversation(), uri);
} else {
for (Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) {
- Log.d(Config.LOGTAG,"ConversationActivity.onActivityResult() - attaching images to conversations. CHOOSE_IMAGES");
+ Log.d(Config.LOGTAG, "ConversationActivity.onActivityResult() - attaching images to conversations. CHOOSE_IMAGES");
attachImagesToConversation(getSelectedConversation(), i.next());
}
}
- }
- } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_FILE || requestCode == ATTACHMENT_CHOICE_RECORD_VOICE) {
- final List<Uri> uris = extractUriFromIntent(data);
- final Conversation c = getSelectedConversation();
- final OnPresenceSelected callback = new OnPresenceSelected() {
- @Override
- public void onPresenceSelected() {
- mPendingFileUris.clear();
- mPendingFileUris.addAll(uris);
- if (xmppConnectionServiceBound) {
- for (Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) {
- Log.d(Config.LOGTAG,"ConversationActivity.onActivityResult() - attaching file to conversations. CHOOSE_FILE/RECORD_VOICE");
- attachFileToConversation(c, i.next());
- }
- }
- }
- };
- if (c == null || c.getMode() == Conversation.MODE_MULTI
- || FileBackend.allFilesUnderSize(this, uris, getMaxHttpUploadSize(c))
- || c.getNextEncryption() == Message.ENCRYPTION_OTR) {
- callback.onPresenceSelected();
- } else {
- selectPresence(c, callback);
- }
+ }
+ } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_FILE || requestCode == ATTACHMENT_CHOICE_RECORD_VOICE) {
+ final List<Uri> uris = extractUriFromIntent(data);
+ final Conversation c = getSelectedConversation();
+ final OnPresenceSelected callback = new OnPresenceSelected() {
+ @Override
+ public void onPresenceSelected() {
+ mPendingFileUris.clear();
+ mPendingFileUris.addAll(uris);
+ if (xmppConnectionServiceBound) {
+ for (Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) {
+ Log.d(Config.LOGTAG, "ConversationActivity.onActivityResult() - attaching file to conversations. CHOOSE_FILE/RECORD_VOICE");
+ attachFileToConversation(c, i.next());
+ }
+ }
+ }
+ };
+ if (c == null || c.getMode() == Conversation.MODE_MULTI
+ || FileBackend.allFilesUnderSize(this, uris, getMaxHttpUploadSize(c))
+ || c.getNextEncryption() == Message.ENCRYPTION_OTR) {
+ callback.onPresenceSelected();
+ } else {
+ selectPresence(c, callback);
+ }
} else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_VIDEO) {
final List<Uri> uris = extractUriFromIntent(data);
final Conversation c = getSelectedConversation();
@@ -1685,7 +1685,7 @@ public class ConversationActivity extends XmppActivity
mPendingVideoUris.addAll(uris);
if (xmppConnectionServiceBound) {
for (Iterator<Uri> i = mPendingVideoUris.iterator(); i.hasNext(); i.remove()) {
- Log.d(Config.LOGTAG,"ConversationActivity.onActivityResult() - attaching video to conversations. CHOOSE_VIDEO");
+ Log.d(Config.LOGTAG, "ConversationActivity.onActivityResult() - attaching video to conversations. CHOOSE_VIDEO");
attachVideoToConversation(c, i.next());
}
}
@@ -1698,163 +1698,163 @@ public class ConversationActivity extends XmppActivity
} else {
selectPresence(c, callback);
}
- } else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
- if (mPendingPhotoUris.size() == 1) {
+ } else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
+ if (mPendingPhotoUris.size() == 1) {
Uri uri = FileBackend.getIndexableTakePhotoUri(mPendingPhotoUris.get(0));
- mPendingPhotoUris.set(0, uri);
+ mPendingPhotoUris.set(0, uri);
if (xmppConnectionServiceBound) {
- Log.d(Config.LOGTAG,"ConversationActivity.onActivityResult() - attaching photo to conversations. TAKE_PHOTO");
+ Log.d(Config.LOGTAG, "ConversationActivity.onActivityResult() - attaching photo to conversations. TAKE_PHOTO");
attachPhotoToConversation(getSelectedConversation(), uri);
- mPendingPhotoUris.clear();
- }
- Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ mPendingPhotoUris.clear();
+ }
+ Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(uri);
- sendBroadcast(intent);
- } else {
- mPendingPhotoUris.clear();
- }
+ sendBroadcast(intent);
+ } else {
+ mPendingPhotoUris.clear();
+ }
} else if (requestCode == ATTACHMENT_CHOICE_LOCATION) {
- double latitude = data.getDoubleExtra("latitude", 0);
- double longitude = data.getDoubleExtra("longitude", 0);
- this.mPendingGeoUri = Uri.parse("geo:" + String.valueOf(latitude) + "," + String.valueOf(longitude));
- if (xmppConnectionServiceBound) {
- attachLocationToConversation(getSelectedConversation(), mPendingGeoUri);
- this.mPendingGeoUri = null;
- }
- } else if (requestCode == REQUEST_TRUST_KEYS_TEXT || requestCode == REQUEST_TRUST_KEYS_MENU) {
- this.forbidProcessingPendings = !xmppConnectionServiceBound;
- if (xmppConnectionServiceBound) {
- mConversationFragment.onActivityResult(requestCode, resultCode, data);
- this.mPostponedActivityResult = null;
- } else {
- this.mPostponedActivityResult = new Pair<>(requestCode, data);
- }
-
- }
- } else {
- mPendingImageUris.clear();
- mPendingFileUris.clear();
+ double latitude = data.getDoubleExtra("latitude", 0);
+ double longitude = data.getDoubleExtra("longitude", 0);
+ this.mPendingGeoUri = Uri.parse("geo:" + String.valueOf(latitude) + "," + String.valueOf(longitude));
+ if (xmppConnectionServiceBound) {
+ attachLocationToConversation(getSelectedConversation(), mPendingGeoUri);
+ this.mPendingGeoUri = null;
+ }
+ } else if (requestCode == REQUEST_TRUST_KEYS_TEXT || requestCode == REQUEST_TRUST_KEYS_MENU) {
+ this.forbidProcessingPendings = !xmppConnectionServiceBound;
+ if (xmppConnectionServiceBound) {
+ mConversationFragment.onActivityResult(requestCode, resultCode, data);
+ this.mPostponedActivityResult = null;
+ } else {
+ this.mPostponedActivityResult = new Pair<>(requestCode, data);
+ }
+
+ }
+ } else {
+ mPendingImageUris.clear();
+ mPendingFileUris.clear();
mPendingPhotoUris.clear();
- if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) {
- mConversationFragment.onActivityResult(requestCode, resultCode, data);
- }
- if (requestCode == REQUEST_BATTERY_OP) {
- setNeverAskForBatteryOptimizationsAgain();
- }
- }
- }
-
- private long getMaxHttpUploadSize(Conversation conversation) {
+ if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) {
+ mConversationFragment.onActivityResult(requestCode, resultCode, data);
+ }
+ if (requestCode == REQUEST_BATTERY_OP) {
+ setNeverAskForBatteryOptimizationsAgain();
+ }
+ }
+ }
+
+ private long getMaxHttpUploadSize(Conversation conversation) {
final XmppConnection connection = conversation.getAccount().getXmppConnection();
return connection == null ? -1 : connection.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)) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.battery_optimizations_enabled);
- builder.setMessage(R.string.battery_optimizations_enabled_dialog);
- builder.setPositiveButton(R.string.next, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
- Uri uri = Uri.parse("package:" + getPackageName());
- intent.setData(uri);
- try {
- startActivityForResult(intent, REQUEST_BATTERY_OP);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(ConversationActivity.this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
- }
- }
- });
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
- builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialog) {
- setNeverAskForBatteryOptimizationsAgain();
- }
- });
- }
- builder.create().show();
- }
- }
-
- private boolean hasAccountWithoutPush() {
- for(Account account : xmppConnectionService.getAccounts()) {
- if (account.getStatus() != Account.State.DISABLED
- && !xmppConnectionService.getPushManagementService().available(account)) {
- return true;
- }
- }
- return false;
- }
-
- private void attachLocationToConversation(Conversation conversation, Uri uri) {
- if (conversation == null) {
- return;
- }
- xmppConnectionService.attachLocationToConversation(conversation, uri, new UiCallback<Message>() {
-
- @Override
- public void success(Message message) {
- xmppConnectionService.sendMessage(message);
- }
-
- @Override
- public void error(int errorCode, Message object) {
-
- }
-
- @Override
- public void userInputRequried(PendingIntent pi, Message object) {
-
- }
- });
- }
-
- private void attachFileToConversation(Conversation conversation, Uri uri) {
- if (conversation == null) {
- return;
- }
- final Toast prepareFileToast = Toast.makeText(getApplicationContext(),getText(R.string.preparing_file), Toast.LENGTH_LONG);
- prepareFileToast.show();
- xmppConnectionService.attachFileToConversation(conversation, uri, new UiCallback<Message>() {
- @Override
- public void success(Message message) {
- hidePrepareFileToast(prepareFileToast);
- xmppConnectionService.sendMessage(message);
- }
-
- @Override
- public void error(final int errorCode, Message message) {
- hidePrepareFileToast(prepareFileToast);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- replaceToast(getString(errorCode));
- }
- });
-
- }
-
- @Override
- public void userInputRequried(PendingIntent pi, Message message) {
- hidePrepareFileToast(prepareFileToast);
- }
- });
- }
+ }
+
+ private void setNeverAskForBatteryOptimizationsAgain() {
+ getPreferences().edit().putBoolean("show_battery_optimization", false).commit();
+ }
+
+ private void openBatteryOptimizationDialogIfNeeded() {
+ if (hasAccountWithoutPush()
+ && isOptimizingBattery()
+ && getPreferences().getBoolean("show_battery_optimization", true)) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.battery_optimizations_enabled);
+ builder.setMessage(R.string.battery_optimizations_enabled_dialog);
+ builder.setPositiveButton(R.string.next, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+ Uri uri = Uri.parse("package:" + getPackageName());
+ intent.setData(uri);
+ try {
+ startActivityForResult(intent, REQUEST_BATTERY_OP);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(ConversationActivity.this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ setNeverAskForBatteryOptimizationsAgain();
+ }
+ });
+ }
+ builder.create().show();
+ }
+ }
+
+ private boolean hasAccountWithoutPush() {
+ for (Account account : xmppConnectionService.getAccounts()) {
+ if (account.getStatus() != Account.State.DISABLED
+ && !xmppConnectionService.getPushManagementService().available(account)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void attachLocationToConversation(Conversation conversation, Uri uri) {
+ if (conversation == null) {
+ return;
+ }
+ xmppConnectionService.attachLocationToConversation(conversation, uri, new UiCallback<Message>() {
+
+ @Override
+ public void success(Message message) {
+ xmppConnectionService.sendMessage(message);
+ }
+
+ @Override
+ public void error(int errorCode, Message object) {
+
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Message object) {
+
+ }
+ });
+ }
+
+ private void attachFileToConversation(Conversation conversation, Uri uri) {
+ if (conversation == null) {
+ return;
+ }
+ final Toast prepareFileToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_file), Toast.LENGTH_LONG);
+ prepareFileToast.show();
+ xmppConnectionService.attachFileToConversation(conversation, uri, new UiCallback<Message>() {
+ @Override
+ public void success(Message message) {
+ hidePrepareFileToast(prepareFileToast);
+ xmppConnectionService.sendMessage(message);
+ }
+
+ @Override
+ public void error(final int errorCode, Message message) {
+ hidePrepareFileToast(prepareFileToast);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ replaceToast(getString(errorCode));
+ }
+ });
+
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Message message) {
+ hidePrepareFileToast(prepareFileToast);
+ }
+ });
+ }
private void attachVideoToConversation(Conversation conversation, final Uri uri) {
if (conversation == null) {
return;
}
- final Toast prepareFileToast = Toast.makeText(getApplicationContext(),getText(R.string.preparing_video), Toast.LENGTH_LONG);
+ final Toast prepareFileToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_video), Toast.LENGTH_LONG);
prepareFileToast.show();
xmppConnectionService.attachVideoToConversation(conversation, uri, new UiCallback<Message>() {
@Override
@@ -1886,7 +1886,7 @@ public class ConversationActivity extends XmppActivity
if (conversation == null) {
return;
}
- final Toast prepareFileToast = Toast.makeText(getApplicationContext(),getText(R.string.preparing_image), Toast.LENGTH_LONG);
+ final Toast prepareFileToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_image), Toast.LENGTH_LONG);
prepareFileToast.show();
xmppConnectionService.attachImageToConversation(conversation, uri,
new UiCallback<Message>() {
@@ -1915,327 +1915,327 @@ public class ConversationActivity extends XmppActivity
});
}
- private void attachImagesToConversation(Conversation conversation, Uri uri) {
- 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(final int error, Message message) {
- hidePrepareFileToast(prepareFileToast);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- replaceToast(getString(error));
- }
- });
- }
- });
- }
-
- private void attachImageToConversation(Conversation conversation, Uri uri) {
- if (conversation == null) {
- return;
- }
- final Conversation conversation_preview = conversation;
- final Uri uri_preview = uri;
- Bitmap bitmap = BitmapFactory.decodeFile(FileUtils.getPath(this, uri));
- File file = new File(FileUtils.getPath(this, uri));
- ExifInterface exif = null;
- try {
- exif = new ExifInterface(file.toString());
- } catch (IOException e) {
- e.printStackTrace();
- }
- int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
- Log.d(Config.LOGTAG, "EXIF: " + orientation);
- Bitmap rotated_image = null;
- Log.d(Config.LOGTAG, "Rotate image");
- rotated_image = FileBackend.rotateBitmap(file, bitmap, orientation);
- if (rotated_image != null) {
- int scaleSize = 600;
- int originalWidth = rotated_image.getWidth();
- int originalHeight = rotated_image.getHeight();
- int newWidth = -1;
- int newHeight = -1;
- float multFactor;
- if (originalHeight > originalWidth) {
- newHeight = scaleSize;
- multFactor = (float) originalWidth / (float) originalHeight;
- newWidth = (int) (newHeight * multFactor);
- } else if (originalWidth > originalHeight) {
- newWidth = scaleSize;
- multFactor = (float) originalHeight / (float) originalWidth;
- newHeight = (int) (newWidth * multFactor);
- } else if (originalHeight == originalWidth) {
- newHeight = scaleSize;
- newWidth = scaleSize;
- }
- Log.d(Config.LOGTAG, "Scaling preview image from " + originalHeight + "px x " + originalWidth + "px to " + newHeight + "px x " + newWidth + "px");
- Bitmap preview = Bitmap.createScaledBitmap(rotated_image, newWidth, newHeight, false);
- ImageView ImagePreview = new ImageView(this);
- LinearLayout.LayoutParams vp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
- ImagePreview.setLayoutParams(vp);
- ImagePreview.setMaxWidth(newWidth);
- ImagePreview.setMaxHeight(newHeight);
- //ImagePreview.setScaleType(ImageView.ScaleType.FIT_XY);
- //ImagePreview.setAdjustViewBounds(true);
- ImagePreview.setPadding(5, 5, 5, 5);
- ImagePreview.setImageBitmap(preview);
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setView(ImagePreview);
- builder.setTitle(R.string.send_image);
- builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(final DialogInterface dialog, final int which) {
- final Toast prepareFileToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_image), Toast.LENGTH_LONG);
- prepareFileToast.show();
- xmppConnectionService.attachImageToConversation(conversation_preview, uri_preview,
- 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(final int error, Message message) {
- hidePrepareFileToast(prepareFileToast);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- replaceToast(getString(error));
- }
- });
- }
- });
- }
- });
- builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- mPendingImageUris.clear();
- }
- });
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialog) {
- mPendingImageUris.clear();
- }
- });
- }
- AlertDialog alertDialog = builder.create();
- alertDialog.show();
- } else {
- Toast.makeText(getApplicationContext(), getText(R.string.error_file_not_found), Toast.LENGTH_LONG).show();
- }
- }
-
- private void hidePrepareFileToast(final Toast prepareFileToast) {
- if (prepareFileToast != null) {
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- prepareFileToast.cancel();
- }
- });
- }
- }
-
- public void updateConversationList() {
- xmppConnectionService
- .populateWithOrderedConversations(conversationList);
- if (swipedConversation != null) {
- if (swipedConversation.isRead()) {
- conversationList.remove(swipedConversation);
- } else {
- listView.discardUndo();
- }
- }
- listAdapter.notifyDataSetChanged();
- }
-
- public void runIntent(PendingIntent pi, int requestCode) {
- try {
- this.startIntentSenderForResult(pi.getIntentSender(), requestCode,
- null, 0, 0, 0);
- } catch (final SendIntentException ignored) {
- }
- }
-
- public void encryptTextMessage(Message message) {
- xmppConnectionService.getPgpEngine().encrypt(message,
- new UiCallback<Message>() {
-
- @Override
- public void userInputRequried(PendingIntent pi,Message message) {
- ConversationActivity.this.runIntent(pi,ConversationActivity.REQUEST_SEND_MESSAGE);
- }
-
- @Override
- public void success(Message message) {
- message.setEncryption(Message.ENCRYPTION_DECRYPTED);
- xmppConnectionService.sendMessage(message);
- if (mConversationFragment != null) {
- mConversationFragment.messageSent();
- }
- }
-
- @Override
- public void error(final int error, Message message) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(ConversationActivity.this,
- R.string.unable_to_connect_to_keychain,
- Toast.LENGTH_SHORT
- ).show();
- }
- });
- }
- });
- }
-
- public boolean useSendButtonToIndicateStatus() {
- return getPreferences().getBoolean("send_button_status", true);
- }
-
- public boolean indicateReceived() {
- return getPreferences().getBoolean("indicate_received", true);
- }
-
- public boolean useWhiteBackground() {
- return getPreferences().getBoolean("use_white_background", false);
- }
-
- protected boolean trustKeysIfNeeded(int requestCode) {
- return trustKeysIfNeeded(requestCode, ATTACHMENT_CHOICE_INVALID);
- }
-
- protected boolean trustKeysIfNeeded(int requestCode, int attachmentChoice) {
- AxolotlService axolotlService = mSelectedConversation.getAccount().getAxolotlService();
- final List<Jid> targets = axolotlService.getCryptoTargets(mSelectedConversation);
- boolean hasUnaccepted = !mSelectedConversation.getAcceptedCryptoTargets().containsAll(targets);
- boolean hasUndecidedOwn = !axolotlService.getKeysWithTrust(FingerprintStatus.createActiveUndecided()).isEmpty();
- boolean hasUndecidedContacts = !axolotlService.getKeysWithTrust(FingerprintStatus.createActiveUndecided(), targets).isEmpty();
- boolean hasPendingKeys = !axolotlService.findDevicesWithoutSession(mSelectedConversation).isEmpty();
- boolean hasNoTrustedKeys = axolotlService.anyTargetHasNoTrustedKeys(targets);
- if(hasUndecidedOwn || hasUndecidedContacts || hasPendingKeys || hasNoTrustedKeys || hasUnaccepted) {
- axolotlService.createSessionsIfNeeded(mSelectedConversation);
- Intent intent = new Intent(getApplicationContext(), TrustKeysActivity.class);
- String[] contacts = new String[targets.size()];
- for(int i = 0; i < contacts.length; ++i) {
- contacts[i] = targets.get(i).toString();
- }
- intent.putExtra("contacts", contacts);
- intent.putExtra(EXTRA_ACCOUNT, mSelectedConversation.getAccount().getJid().toBareJid().toString());
- intent.putExtra("choice", attachmentChoice);
- intent.putExtra("conversation",mSelectedConversation.getUuid());
- startActivityForResult(intent, requestCode);
- return true;
- } else {
- return false;
- }
- }
-
- @Override
- protected void refreshUiReal() {
- updateConversationList();
- if (conversationList.size() > 0) {
- if (!this.mConversationFragment.isAdded()) {
- Log.d(Config.LOGTAG,"fragment NOT added to activity. detached="+Boolean.toString(mConversationFragment.isDetached()));
- }
- ConversationActivity.this.mConversationFragment.updateMessages();
- updateActionBarTitle();
- invalidateOptionsMenu();
- } else {
- Log.d(Config.LOGTAG,"not updating conversations fragment because conversations list size was 0");
- }
- }
-
- @Override
- public void onAccountUpdate() {
- this.refreshUi();
- }
-
- @Override
- public void onConversationUpdate() {
- this.refreshUi();
- }
-
- @Override
- public void onRosterUpdate() {
- this.refreshUi();
- }
-
- @Override
- public void OnUpdateBlocklist(Status status) {
- this.refreshUi();
- }
-
- public void unblockConversation(final Blockable conversation) {
- xmppConnectionService.sendUnblockRequest(conversation);
- }
-
- public boolean enterIsSend() {
- return getPreferences().getBoolean("enter_is_send",false);
- }
-
- @Override
- public void onShowErrorToast(final int resId) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(ConversationActivity.this,resId,Toast.LENGTH_SHORT).show();
- }
- });
- }
-
- public boolean highlightSelectedConversations() {
- return !isConversationsOverviewHideable() || this.conversationWasSelectedByKeyboard;
- }
-
- @Override
- public void onClick(View view) {
- final Conversation conversation = getSelectedConversation();
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- switchToContactDetails(getSelectedConversation().getContact());
- } else if (conversation.getMode() == Conversation.MODE_MULTI) {
- Intent intent = new Intent(this,
- ConferenceDetailsActivity.class);
- intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
- intent.putExtra("uuid", getSelectedConversation().getUuid());
- startActivity(intent);
- }
- }
-
- public void setMessagesLoaded() {
- if (mConversationFragment != null) {
- mConversationFragment.setMessagesLoaded();
- mConversationFragment.updateMessages();
- }
- }
+ private void attachImagesToConversation(Conversation conversation, Uri uri) {
+ 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(final int error, Message message) {
+ hidePrepareFileToast(prepareFileToast);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ replaceToast(getString(error));
+ }
+ });
+ }
+ });
+ }
+
+ private void attachImageToConversation(Conversation conversation, Uri uri) {
+ if (conversation == null) {
+ return;
+ }
+ final Conversation conversation_preview = conversation;
+ final Uri uri_preview = uri;
+ Bitmap bitmap = BitmapFactory.decodeFile(FileUtils.getPath(this, uri));
+ File file = new File(FileUtils.getPath(this, uri));
+ ExifInterface exif = null;
+ try {
+ exif = new ExifInterface(file.toString());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
+ Log.d(Config.LOGTAG, "EXIF: " + orientation);
+ Bitmap rotated_image = null;
+ Log.d(Config.LOGTAG, "Rotate image");
+ rotated_image = FileBackend.rotateBitmap(file, bitmap, orientation);
+ if (rotated_image != null) {
+ int scaleSize = 600;
+ int originalWidth = rotated_image.getWidth();
+ int originalHeight = rotated_image.getHeight();
+ int newWidth = -1;
+ int newHeight = -1;
+ float multFactor;
+ if (originalHeight > originalWidth) {
+ newHeight = scaleSize;
+ multFactor = (float) originalWidth / (float) originalHeight;
+ newWidth = (int) (newHeight * multFactor);
+ } else if (originalWidth > originalHeight) {
+ newWidth = scaleSize;
+ multFactor = (float) originalHeight / (float) originalWidth;
+ newHeight = (int) (newWidth * multFactor);
+ } else if (originalHeight == originalWidth) {
+ newHeight = scaleSize;
+ newWidth = scaleSize;
+ }
+ Log.d(Config.LOGTAG, "Scaling preview image from " + originalHeight + "px x " + originalWidth + "px to " + newHeight + "px x " + newWidth + "px");
+ Bitmap preview = Bitmap.createScaledBitmap(rotated_image, newWidth, newHeight, false);
+ ImageView ImagePreview = new ImageView(this);
+ LinearLayout.LayoutParams vp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
+ ImagePreview.setLayoutParams(vp);
+ ImagePreview.setMaxWidth(newWidth);
+ ImagePreview.setMaxHeight(newHeight);
+ //ImagePreview.setScaleType(ImageView.ScaleType.FIT_XY);
+ //ImagePreview.setAdjustViewBounds(true);
+ ImagePreview.setPadding(5, 5, 5, 5);
+ ImagePreview.setImageBitmap(preview);
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setView(ImagePreview);
+ builder.setTitle(R.string.send_image);
+ builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(final DialogInterface dialog, final int which) {
+ final Toast prepareFileToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_image), Toast.LENGTH_LONG);
+ prepareFileToast.show();
+ xmppConnectionService.attachImageToConversation(conversation_preview, uri_preview,
+ 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(final int error, Message message) {
+ hidePrepareFileToast(prepareFileToast);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ replaceToast(getString(error));
+ }
+ });
+ }
+ });
+ }
+ });
+ builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ mPendingImageUris.clear();
+ }
+ });
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ mPendingImageUris.clear();
+ }
+ });
+ }
+ AlertDialog alertDialog = builder.create();
+ alertDialog.show();
+ } else {
+ Toast.makeText(getApplicationContext(), getText(R.string.error_file_not_found), Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private void hidePrepareFileToast(final Toast prepareFileToast) {
+ if (prepareFileToast != null) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ prepareFileToast.cancel();
+ }
+ });
+ }
+ }
+
+ public void updateConversationList() {
+ xmppConnectionService
+ .populateWithOrderedConversations(conversationList);
+ if (swipedConversation != null) {
+ if (swipedConversation.isRead()) {
+ conversationList.remove(swipedConversation);
+ } else {
+ listView.discardUndo();
+ }
+ }
+ listAdapter.notifyDataSetChanged();
+ }
+
+ public void runIntent(PendingIntent pi, int requestCode) {
+ try {
+ this.startIntentSenderForResult(pi.getIntentSender(), requestCode,
+ null, 0, 0, 0);
+ } catch (final SendIntentException ignored) {
+ }
+ }
+
+ public void encryptTextMessage(Message message) {
+ xmppConnectionService.getPgpEngine().encrypt(message,
+ new UiCallback<Message>() {
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Message message) {
+ ConversationActivity.this.runIntent(pi, ConversationActivity.REQUEST_SEND_MESSAGE);
+ }
+
+ @Override
+ public void success(Message message) {
+ message.setEncryption(Message.ENCRYPTION_DECRYPTED);
+ xmppConnectionService.sendMessage(message);
+ if (mConversationFragment != null) {
+ mConversationFragment.messageSent();
+ }
+ }
+
+ @Override
+ public void error(final int error, Message message) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(ConversationActivity.this,
+ R.string.unable_to_connect_to_keychain,
+ Toast.LENGTH_SHORT
+ ).show();
+ }
+ });
+ }
+ });
+ }
+
+ public boolean useSendButtonToIndicateStatus() {
+ return getPreferences().getBoolean("send_button_status", true);
+ }
+
+ public boolean indicateReceived() {
+ return getPreferences().getBoolean("indicate_received", true);
+ }
+
+ public boolean useWhiteBackground() {
+ return getPreferences().getBoolean("use_white_background", false);
+ }
+
+ protected boolean trustKeysIfNeeded(int requestCode) {
+ return trustKeysIfNeeded(requestCode, ATTACHMENT_CHOICE_INVALID);
+ }
+
+ protected boolean trustKeysIfNeeded(int requestCode, int attachmentChoice) {
+ AxolotlService axolotlService = mSelectedConversation.getAccount().getAxolotlService();
+ final List<Jid> targets = axolotlService.getCryptoTargets(mSelectedConversation);
+ boolean hasUnaccepted = !mSelectedConversation.getAcceptedCryptoTargets().containsAll(targets);
+ boolean hasUndecidedOwn = !axolotlService.getKeysWithTrust(FingerprintStatus.createActiveUndecided()).isEmpty();
+ boolean hasUndecidedContacts = !axolotlService.getKeysWithTrust(FingerprintStatus.createActiveUndecided(), targets).isEmpty();
+ boolean hasPendingKeys = !axolotlService.findDevicesWithoutSession(mSelectedConversation).isEmpty();
+ boolean hasNoTrustedKeys = axolotlService.anyTargetHasNoTrustedKeys(targets);
+ if (hasUndecidedOwn || hasUndecidedContacts || hasPendingKeys || hasNoTrustedKeys || hasUnaccepted) {
+ axolotlService.createSessionsIfNeeded(mSelectedConversation);
+ Intent intent = new Intent(getApplicationContext(), TrustKeysActivity.class);
+ String[] contacts = new String[targets.size()];
+ for (int i = 0; i < contacts.length; ++i) {
+ contacts[i] = targets.get(i).toString();
+ }
+ intent.putExtra("contacts", contacts);
+ intent.putExtra(EXTRA_ACCOUNT, mSelectedConversation.getAccount().getJid().toBareJid().toString());
+ intent.putExtra("choice", attachmentChoice);
+ intent.putExtra("conversation", mSelectedConversation.getUuid());
+ startActivityForResult(intent, requestCode);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected void refreshUiReal() {
+ updateConversationList();
+ if (conversationList.size() > 0) {
+ if (!this.mConversationFragment.isAdded()) {
+ Log.d(Config.LOGTAG, "fragment NOT added to activity. detached=" + Boolean.toString(mConversationFragment.isDetached()));
+ }
+ ConversationActivity.this.mConversationFragment.updateMessages();
+ updateActionBarTitle();
+ invalidateOptionsMenu();
+ } else {
+ Log.d(Config.LOGTAG, "not updating conversations fragment because conversations list size was 0");
+ }
+ }
+
+ @Override
+ public void onAccountUpdate() {
+ this.refreshUi();
+ }
+
+ @Override
+ public void onConversationUpdate() {
+ this.refreshUi();
+ }
+
+ @Override
+ public void onRosterUpdate() {
+ this.refreshUi();
+ }
+
+ @Override
+ public void OnUpdateBlocklist(Status status) {
+ this.refreshUi();
+ }
+
+ public void unblockConversation(final Blockable conversation) {
+ xmppConnectionService.sendUnblockRequest(conversation);
+ }
+
+ public boolean enterIsSend() {
+ return getPreferences().getBoolean("enter_is_send", false);
+ }
+
+ @Override
+ public void onShowErrorToast(final int resId) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(ConversationActivity.this, resId, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ public boolean highlightSelectedConversations() {
+ return !isConversationsOverviewHideable() || this.conversationWasSelectedByKeyboard;
+ }
+
+ @Override
+ public void onClick(View view) {
+ final Conversation conversation = getSelectedConversation();
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ switchToContactDetails(getSelectedConversation().getContact());
+ } else if (conversation.getMode() == Conversation.MODE_MULTI) {
+ Intent intent = new Intent(this,
+ ConferenceDetailsActivity.class);
+ intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
+ intent.putExtra("uuid", getSelectedConversation().getUuid());
+ startActivity(intent);
+ }
+ }
+
+ public void setMessagesLoaded() {
+ if (mConversationFragment != null) {
+ mConversationFragment.setMessagesLoaded();
+ mConversationFragment.updateMessages();
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
index c71be325a..01fc47afe 100644
--- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
+++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
@@ -73,152 +73,152 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class ConversationFragment extends Fragment implements EditMessage.KeyboardListener {
- protected Conversation conversation;
- private OnClickListener leaveMuc = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- activity.endConversation(conversation);
- }
- };
- private OnClickListener joinMuc = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- activity.xmppConnectionService.joinMuc(conversation);
- }
- };
- private OnClickListener enterPassword = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- MucOptions muc = conversation.getMucOptions();
- String password = muc.getPassword();
- if (password == null) {
- password = "";
- }
- activity.quickPasswordEdit(password, new OnValueEdited() {
-
- @Override
- public void onValueEdited(String value) {
- activity.xmppConnectionService.providePasswordForMuc(
- conversation, value);
- }
- });
- }
- };
- protected ListView messagesView;
- final protected List<Message> messageList = new ArrayList<>();
- protected MessageAdapter messageListAdapter;
- private EditMessage mEditMessage;
- private ImageButton mSendButton;
- 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
-
- }
-
- @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);
+ protected Conversation conversation;
+ private OnClickListener leaveMuc = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ activity.endConversation(conversation);
+ }
+ };
+ private OnClickListener joinMuc = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ activity.xmppConnectionService.joinMuc(conversation);
+ }
+ };
+ private OnClickListener enterPassword = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ MucOptions muc = conversation.getMucOptions();
+ String password = muc.getPassword();
+ if (password == null) {
+ password = "";
+ }
+ activity.quickPasswordEdit(password, new OnValueEdited() {
+
+ @Override
+ public void onValueEdited(String value) {
+ activity.xmppConnectionService.providePasswordForMuc(
+ conversation, value);
+ }
+ });
+ }
+ };
+ protected ListView messagesView;
+ final protected List<Message> messageList = new ArrayList<>();
+ protected MessageAdapter messageListAdapter;
+ private EditMessage mEditMessage;
+ private ImageButton mSendButton;
+ 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
+
+ }
+
+ @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);
try {
updateStatusMessages();
} catch (IllegalStateException e) {
Log.d(Config.LOGTAG, "caught illegal state exception while updating status messages");
}
messageListAdapter.notifyDataSetChanged();
- int pos = Math.max(getIndexOf(uuid,messageList),0);
- 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 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 -1;
- }
+ int pos = Math.max(getIndexOf(uuid, messageList), 0);
+ 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 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 -1;
+ }
public Pair<Integer, Integer> getScrollPosition() {
if (this.messagesView.getCount() == 0 ||
@@ -243,334 +243,334 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
protected OnClickListener clickToDecryptListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- PendingIntent pendingIntent = conversation.getAccount().getPgpDecryptionService().getPendingIntent();
- if (pendingIntent != null) {
- try {
- activity.startIntentSenderForResult(pendingIntent.getIntentSender(),
+ @Override
+ public void onClick(View v) {
+ PendingIntent pendingIntent = conversation.getAccount().getPgpDecryptionService().getPendingIntent();
+ if (pendingIntent != null) {
+ try {
+ activity.startIntentSenderForResult(pendingIntent.getIntentSender(),
ConversationActivity.REQUEST_DECRYPT_PGP,
null,
0,
0,
0);
- } catch (SendIntentException e) {
- Toast.makeText(activity,R.string.unable_to_connect_to_keychain, Toast.LENGTH_SHORT).show();
- conversation.getAccount().getPgpDecryptionService().continueDecryption(true);
- }
- }
- updateSnackBar(conversation);
- }
- };
- protected OnClickListener clickToVerify = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- activity.verifyOtrSessionDialog(conversation, v);
- }
- };
- private OnEditorActionListener mEditorActionListener = new OnEditorActionListener() {
-
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- if (actionId == EditorInfo.IME_ACTION_SEND) {
- InputMethodManager imm = (InputMethodManager) v.getContext()
- .getSystemService(Context.INPUT_METHOD_SERVICE);
- if (imm.isFullscreenMode()) {
- imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
- }
- sendMessage();
- return true;
- } else {
- return false;
- }
- }
- };
- private OnClickListener mSendButtonListener = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- Object tag = v.getTag();
- if (tag instanceof SendButtonAction) {
- SendButtonAction action = (SendButtonAction) tag;
- switch (action) {
- case TAKE_PHOTO:
- activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_TAKE_PHOTO);
- break;
- case SEND_LOCATION:
- activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_LOCATION);
- break;
- case RECORD_VOICE:
- activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_RECORD_VOICE);
- break;
- case CHOOSE_PICTURE:
- 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);
- }
- updateChatMsgHint();
- updateSendButton();
- }
- break;
- default:
- sendMessage();
- }
- } else {
- sendMessage();
- }
- }
- };
- private View.OnLongClickListener mSendButtonLongListener = new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- final String body = mEditMessage.getText().toString();
- if (body.length() == 0) {
- mEditMessage.getText().insert(0, "/me ");
- }
- return true;
- }
- };
- private OnClickListener clickToMuc = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(getActivity(), ConferenceDetailsActivity.class);
- intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
- intent.putExtra("uuid", conversation.getUuid());
- startActivity(intent);
- }
- };
- private ConversationActivity activity;
- private Message selectedMessage;
-
- public void setMessagesLoaded() {
- this.messagesLoaded = true;
- }
-
- private void sendMessage() {
- final String body = mEditMessage.getText().toString();
- 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);
- }
- }
- } 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:
- sendOtrMessage(message);
- break;
- case Message.ENCRYPTION_PGP:
- sendPgpMessage(message);
- break;
- case Message.ENCRYPTION_AXOLOTL:
- if(!activity.trustKeysIfNeeded(ConversationActivity.REQUEST_TRUST_KEYS_TEXT)) {
- sendAxolotlMessage(message);
- }
- break;
- default:
- sendPlainTextMessage(message);
- }
- }
-
- 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) {
- this.mEditMessage.setHint(getString(
- R.string.send_private_message_to,
- conversation.getNextCounterpart().getResourcepart()));
- } else if (multi && !conversation.getMucOptions().participating()) {
- this.mEditMessage.setHint(R.string.you_are_not_participating);
- } else {
- this.mEditMessage.setHint(UIHelper.getMessageHint(activity,conversation));
- getActivity().invalidateOptionsMenu();
- }
- }
-
- public void setupIme() {
- if (activity == null) {
- return;
- } else if (activity.usingEnterKey() && activity.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()) {
- mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
- mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
- } else {
- mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
- mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
- }
- }
-
- @Override
- public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- final View view = inflater.inflate(R.layout.fragment_conversation, container, false);
- view.setOnClickListener(null);
- mEditMessage = (EditMessage) view.findViewById(R.id.textinput);
- mEditMessage.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (activity != null) {
- activity.hideConversationsOverview();
- }
- }
- });
- mEditMessage.setOnEditorActionListener(mEditorActionListener);
-
- mSendButton = (ImageButton) view.findViewById(R.id.textSendButton);
- mSendButton.setOnClickListener(this.mSendButtonListener);
- mSendButton.setOnLongClickListener(this.mSendButtonLongListener);
-
- snackbar = (RelativeLayout) view.findViewById(R.id.snackbar);
- snackbarMessage = (TextView) view.findViewById(R.id.snackbar_message);
- 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() {
-
- @Override
- public void onContactPictureClicked(Message message) {
- if (message.getStatus() <= Message.STATUS_RECEIVED) {
- if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
- Jid user = message.getCounterpart();
- if (user != null && !user.isBareJid()) {
- if (!message.getConversation().getMucOptions().isUserInRoom(user)) {
- Toast.makeText(activity,activity.getString(R.string.user_has_left_conference,user.getResourcepart()),Toast.LENGTH_SHORT).show();
- }
- highlightInConference(user.getResourcepart());
- }
- } else {
- if (!message.getContact().isSelf()) {
- String fingerprint;
- if (message.getEncryption() == Message.ENCRYPTION_PGP
- || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- fingerprint = "pgp";
- } else {
- fingerprint = message.getFingerprint();
- }
- activity.switchToContactDetails(message.getContact(), fingerprint);
- }
- }
- } else {
- Account account = message.getConversation().getAccount();
- Intent intent;
- if (activity.manuallyChangePresence()) {
- intent = new Intent(activity, SetPresenceActivity.class);
- intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT, account.getJid().toBareJid().toString());
- } else {
- intent = new Intent(activity, EditAccountActivity.class);
- intent.putExtra("jid", account.getJid().toBareJid().toString());
- String fingerprint;
- if (message.getEncryption() == Message.ENCRYPTION_PGP
- || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- fingerprint = "pgp";
- } else {
- fingerprint = message.getFingerprint();
- }
- intent.putExtra("fingerprint", fingerprint);
- }
- startActivity(intent);
- }
- }
- });
- messageListAdapter
- .setOnContactPictureLongClicked(new OnContactPictureLongClicked() {
-
- @Override
- public void onContactPictureLongClicked(Message message) {
- if (message.getStatus() <= Message.STATUS_RECEIVED) {
- if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
- Jid user = message.getCounterpart();
- if (user != null && !user.isBareJid()) {
- if (message.getConversation().getMucOptions().isUserInRoom(user)) {
- privateMessageWith(user);
- } else {
- Toast.makeText(activity, activity.getString(R.string.user_has_left_conference, user.getResourcepart()), Toast.LENGTH_SHORT).show();
- }
- }
- }
- } else {
- activity.showQrCode();
- }
- }
- });
- messagesView.setAdapter(messageListAdapter);
-
- registerForContextMenu(messagesView);
-
- return view;
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
- synchronized (this.messageList) {
- super.onCreateContextMenu(menu, v, menuInfo);
- AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
- this.selectedMessage = this.messageList.get(acmi.position);
- populateContextMenu(menu);
- }
- }
-
- 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
- && t == null;
- activity.getMenuInflater().inflate(R.menu.message_context, menu);
- menu.setHeaderTitle(R.string.message_options);
- MenuItem copyText = menu.findItem(R.id.copy_text);
- MenuItem selectText = menu.findItem(R.id.select_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);
- MenuItem downloadFile = menu.findItem(R.id.download_file);
- MenuItem cancelTransmission = menu.findItem(R.id.cancel_transmission);
- MenuItem deleteFile = menu.findItem(R.id.delete_file);
+ } catch (SendIntentException e) {
+ Toast.makeText(activity, R.string.unable_to_connect_to_keychain, Toast.LENGTH_SHORT).show();
+ conversation.getAccount().getPgpDecryptionService().continueDecryption(true);
+ }
+ }
+ updateSnackBar(conversation);
+ }
+ };
+ protected OnClickListener clickToVerify = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ activity.verifyOtrSessionDialog(conversation, v);
+ }
+ };
+ private OnEditorActionListener mEditorActionListener = new OnEditorActionListener() {
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (actionId == EditorInfo.IME_ACTION_SEND) {
+ InputMethodManager imm = (InputMethodManager) v.getContext()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm.isFullscreenMode()) {
+ imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
+ }
+ sendMessage();
+ return true;
+ } else {
+ return false;
+ }
+ }
+ };
+ private OnClickListener mSendButtonListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Object tag = v.getTag();
+ if (tag instanceof SendButtonAction) {
+ SendButtonAction action = (SendButtonAction) tag;
+ switch (action) {
+ case TAKE_PHOTO:
+ activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_TAKE_PHOTO);
+ break;
+ case SEND_LOCATION:
+ activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_LOCATION);
+ break;
+ case RECORD_VOICE:
+ activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_RECORD_VOICE);
+ break;
+ case CHOOSE_PICTURE:
+ 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);
+ }
+ updateChatMsgHint();
+ updateSendButton();
+ }
+ break;
+ default:
+ sendMessage();
+ }
+ } else {
+ sendMessage();
+ }
+ }
+ };
+ private View.OnLongClickListener mSendButtonLongListener = new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ final String body = mEditMessage.getText().toString();
+ if (body.length() == 0) {
+ mEditMessage.getText().insert(0, "/me ");
+ }
+ return true;
+ }
+ };
+ private OnClickListener clickToMuc = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(getActivity(), ConferenceDetailsActivity.class);
+ intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
+ intent.putExtra("uuid", conversation.getUuid());
+ startActivity(intent);
+ }
+ };
+ private ConversationActivity activity;
+ private Message selectedMessage;
+
+ public void setMessagesLoaded() {
+ this.messagesLoaded = true;
+ }
+
+ private void sendMessage() {
+ final String body = mEditMessage.getText().toString();
+ 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);
+ }
+ }
+ } 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:
+ sendOtrMessage(message);
+ break;
+ case Message.ENCRYPTION_PGP:
+ sendPgpMessage(message);
+ break;
+ case Message.ENCRYPTION_AXOLOTL:
+ if (!activity.trustKeysIfNeeded(ConversationActivity.REQUEST_TRUST_KEYS_TEXT)) {
+ sendAxolotlMessage(message);
+ }
+ break;
+ default:
+ sendPlainTextMessage(message);
+ }
+ }
+
+ 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) {
+ this.mEditMessage.setHint(getString(
+ R.string.send_private_message_to,
+ conversation.getNextCounterpart().getResourcepart()));
+ } else if (multi && !conversation.getMucOptions().participating()) {
+ this.mEditMessage.setHint(R.string.you_are_not_participating);
+ } else {
+ this.mEditMessage.setHint(UIHelper.getMessageHint(activity, conversation));
+ getActivity().invalidateOptionsMenu();
+ }
+ }
+
+ public void setupIme() {
+ if (activity == null) {
+ return;
+ } else if (activity.usingEnterKey() && activity.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()) {
+ mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
+ mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
+ } else {
+ mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
+ mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
+ }
+ }
+
+ @Override
+ public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ final View view = inflater.inflate(R.layout.fragment_conversation, container, false);
+ view.setOnClickListener(null);
+ mEditMessage = (EditMessage) view.findViewById(R.id.textinput);
+ mEditMessage.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (activity != null) {
+ activity.hideConversationsOverview();
+ }
+ }
+ });
+ mEditMessage.setOnEditorActionListener(mEditorActionListener);
+
+ mSendButton = (ImageButton) view.findViewById(R.id.textSendButton);
+ mSendButton.setOnClickListener(this.mSendButtonListener);
+ mSendButton.setOnLongClickListener(this.mSendButtonLongListener);
+
+ snackbar = (RelativeLayout) view.findViewById(R.id.snackbar);
+ snackbarMessage = (TextView) view.findViewById(R.id.snackbar_message);
+ 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() {
+
+ @Override
+ public void onContactPictureClicked(Message message) {
+ if (message.getStatus() <= Message.STATUS_RECEIVED) {
+ if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
+ Jid user = message.getCounterpart();
+ if (user != null && !user.isBareJid()) {
+ if (!message.getConversation().getMucOptions().isUserInRoom(user)) {
+ Toast.makeText(activity, activity.getString(R.string.user_has_left_conference, user.getResourcepart()), Toast.LENGTH_SHORT).show();
+ }
+ highlightInConference(user.getResourcepart());
+ }
+ } else {
+ if (!message.getContact().isSelf()) {
+ String fingerprint;
+ if (message.getEncryption() == Message.ENCRYPTION_PGP
+ || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ fingerprint = "pgp";
+ } else {
+ fingerprint = message.getFingerprint();
+ }
+ activity.switchToContactDetails(message.getContact(), fingerprint);
+ }
+ }
+ } else {
+ Account account = message.getConversation().getAccount();
+ Intent intent;
+ if (activity.manuallyChangePresence()) {
+ intent = new Intent(activity, SetPresenceActivity.class);
+ intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT, account.getJid().toBareJid().toString());
+ } else {
+ intent = new Intent(activity, EditAccountActivity.class);
+ intent.putExtra("jid", account.getJid().toBareJid().toString());
+ String fingerprint;
+ if (message.getEncryption() == Message.ENCRYPTION_PGP
+ || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ fingerprint = "pgp";
+ } else {
+ fingerprint = message.getFingerprint();
+ }
+ intent.putExtra("fingerprint", fingerprint);
+ }
+ startActivity(intent);
+ }
+ }
+ });
+ messageListAdapter
+ .setOnContactPictureLongClicked(new OnContactPictureLongClicked() {
+
+ @Override
+ public void onContactPictureLongClicked(Message message) {
+ if (message.getStatus() <= Message.STATUS_RECEIVED) {
+ if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
+ Jid user = message.getCounterpart();
+ if (user != null && !user.isBareJid()) {
+ if (message.getConversation().getMucOptions().isUserInRoom(user)) {
+ privateMessageWith(user);
+ } else {
+ Toast.makeText(activity, activity.getString(R.string.user_has_left_conference, user.getResourcepart()), Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+ } else {
+ activity.showQrCode();
+ }
+ }
+ });
+ messagesView.setAdapter(messageListAdapter);
+
+ registerForContextMenu(messagesView);
+
+ return view;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ synchronized (this.messageList) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
+ this.selectedMessage = this.messageList.get(acmi.position);
+ populateContextMenu(menu);
+ }
+ }
+
+ 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
+ && t == null;
+ activity.getMenuInflater().inflate(R.menu.message_context, menu);
+ menu.setHeaderTitle(R.string.message_options);
+ MenuItem copyText = menu.findItem(R.id.copy_text);
+ MenuItem selectText = menu.findItem(R.id.select_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);
+ MenuItem downloadFile = menu.findItem(R.id.download_file);
+ MenuItem cancelTransmission = menu.findItem(R.id.cancel_transmission);
+ MenuItem deleteFile = menu.findItem(R.id.delete_file);
MenuItem showErrorMessage = menu.findItem(R.id.show_error_message);
- if (!treatAsFile
- && !GeoHelper.isGeoUri(m.getBody())
+ if (!treatAsFile
+ && !GeoHelper.isGeoUri(m.getBody())
&& !XmppUri.isXmppUri(m.getBody())
- && m.treatAsDownloadable() != Message.Decision.MUST) {
- copyText.setVisible(true);
+ && m.treatAsDownloadable() != Message.Decision.MUST) {
+ copyText.setVisible(true);
selectText.setVisible(ListSelectionManager.isSupported());
- }
- if (m.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
- retryDecryption.setVisible(true);
- }
- if (((relevantForCorrection.getType() == Message.TYPE_TEXT
+ }
+ if (m.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
+ retryDecryption.setVisible(true);
+ }
+ if (((relevantForCorrection.getType() == Message.TYPE_TEXT
&& relevantForCorrection.isLastCorrectableMessage()
&& conversation.getMode() == Conversation.MODE_SINGLE)
|| (relevantForCorrection.getType() == Message.TYPE_TEXT
@@ -578,129 +578,129 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
&& conversation.getMode() == Conversation.MODE_MULTI
&& m.getConversation().getMucOptions().nonanonymous()))
&& !(GeoHelper.isGeoUri(m.getBody()) || XmppUri.isXmppUri(m.getBody()))) {
- correctMessage.setVisible(true);
- }
- if (treatAsFile
+ correctMessage.setVisible(true);
+ }
+ if (treatAsFile
|| GeoHelper.isGeoUri(m.getBody())
|| XmppUri.isXmppUri(m.getBody())) {
- shareWith.setVisible(true);
- }
- if (m.getStatus() == Message.STATUS_SEND_FAILED) {
- sendAgain.setVisible(true);
- }
- if (m.hasFileOnRemoteHost()
- || GeoHelper.isGeoUri(m.getBody())
+ shareWith.setVisible(true);
+ }
+ if (m.getStatus() == Message.STATUS_SEND_FAILED) {
+ sendAgain.setVisible(true);
+ }
+ if (m.hasFileOnRemoteHost()
+ || GeoHelper.isGeoUri(m.getBody())
|| XmppUri.isXmppUri(m.getBody())
- || m.treatAsDownloadable() == Message.Decision.MUST
- || (t != null && t instanceof HttpDownloadConnection)) {
- copyUrl.setVisible(true);
- }
- if ((m.getType() == Message.TYPE_TEXT && t == null && m.treatAsDownloadable() != Message.Decision.NEVER)
- || (m.isFileOrImage() && t instanceof TransferablePlaceholder && m.hasFileOnRemoteHost())){
- downloadFile.setVisible(true);
- downloadFile.setTitle(activity.getString(R.string.download_x_file,UIHelper.getFileDescriptionString(activity, m)));
- }
+ || m.treatAsDownloadable() == Message.Decision.MUST
+ || (t != null && t instanceof HttpDownloadConnection)) {
+ copyUrl.setVisible(true);
+ }
+ if ((m.getType() == Message.TYPE_TEXT && t == null && m.treatAsDownloadable() != Message.Decision.NEVER)
+ || (m.isFileOrImage() && t instanceof TransferablePlaceholder && m.hasFileOnRemoteHost())) {
+ downloadFile.setVisible(true);
+ downloadFile.setTitle(activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, m)));
+ }
boolean waitingOfferedSending = m.getStatus() == Message.STATUS_WAITING
|| m.getStatus() == Message.STATUS_UNSEND
|| m.getStatus() == Message.STATUS_OFFERED;
if ((t != null && !(t instanceof TransferablePlaceholder)) || waitingOfferedSending && m.needsUploading()) {
- cancelTransmission.setVisible(true);
- }
- if (treatAsFile) {
+ cancelTransmission.setVisible(true);
+ }
+ if (treatAsFile) {
String path = m.getRelativeFilePath();
Log.d(Config.LOGTAG, "Path = " + path);
if (path == null || !path.startsWith("/") || path.contains(FileBackend.getConversationsDirectory())) {
deleteFile.setVisible(true);
deleteFile.setTitle(activity.getString(R.string.delete_x_file, UIHelper.getFileDescriptionString(activity, m)));
}
- }
+ }
if (m.getStatus() == Message.STATUS_SEND_FAILED && m.getErrorMessage() != null) {
showErrorMessage.setVisible(true);
}
- }
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.share_with:
- shareWith(selectedMessage);
- return true;
- case R.id.copy_text:
- copyText(selectedMessage);
- return true;
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.share_with:
+ shareWith(selectedMessage);
+ return true;
+ case R.id.copy_text:
+ copyText(selectedMessage);
+ return true;
case R.id.select_text:
selectText(selectedMessage);
return true;
- case R.id.correct_message:
- correctMessage(selectedMessage);
- return true;
- case R.id.send_again:
- resendMessage(selectedMessage);
- return true;
- case R.id.copy_url:
- copyUrl(selectedMessage);
- return true;
- case R.id.download_file:
- downloadFile(selectedMessage);
- return true;
- case R.id.cancel_transmission:
- cancelTransmission(selectedMessage);
- return true;
- case R.id.retry_decryption:
- retryDecryption(selectedMessage);
- return true;
- case R.id.delete_file:
- deleteFile(selectedMessage);
- return true;
+ case R.id.correct_message:
+ correctMessage(selectedMessage);
+ return true;
+ case R.id.send_again:
+ resendMessage(selectedMessage);
+ return true;
+ case R.id.copy_url:
+ copyUrl(selectedMessage);
+ return true;
+ case R.id.download_file:
+ downloadFile(selectedMessage);
+ return true;
+ case R.id.cancel_transmission:
+ cancelTransmission(selectedMessage);
+ return true;
+ case R.id.retry_decryption:
+ retryDecryption(selectedMessage);
+ return true;
+ case R.id.delete_file:
+ deleteFile(selectedMessage);
+ return true;
case R.id.show_error_message:
showErrorMessage(selectedMessage);
return true;
- default:
- return super.onContextItemSelected(item);
- }
- }
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
private void showErrorMessage(final Message message) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(R.string.error_message);
builder.setMessage(message.getErrorMessage());
- builder.setPositiveButton(R.string.ok,null);
+ builder.setPositiveButton(R.string.ok, null);
builder.create().show();
}
- private void shareWith(Message message) {
- Intent shareIntent = new Intent();
- shareIntent.setAction(Intent.ACTION_SEND);
- if (GeoHelper.isGeoUri(message.getBody())) {
- shareIntent.putExtra(Intent.EXTRA_TEXT, message.getBody());
- shareIntent.setType("text/plain");
- } else {
- shareIntent.putExtra(Intent.EXTRA_STREAM,
- activity.xmppConnectionService.getFileBackend()
- .getJingleFileUri(message));
- shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- String mime = message.getMimeType();
- if (mime == null) {
- mime = "*/*";
- }
- shareIntent.setType(mime);
- }
- try {
- activity.startActivity(Intent.createChooser(shareIntent, getText(R.string.share_with)));
- } catch (ActivityNotFoundException e) {
- //This should happen only on faulty androids because normally chooser is always available
- Toast.makeText(activity,R.string.no_application_found_to_open_file,Toast.LENGTH_SHORT).show();
- }
- }
-
- private void copyText(Message message) {
+ private void shareWith(Message message) {
+ Intent shareIntent = new Intent();
+ shareIntent.setAction(Intent.ACTION_SEND);
+ if (GeoHelper.isGeoUri(message.getBody())) {
+ shareIntent.putExtra(Intent.EXTRA_TEXT, message.getBody());
+ shareIntent.setType("text/plain");
+ } else {
+ shareIntent.putExtra(Intent.EXTRA_STREAM,
+ activity.xmppConnectionService.getFileBackend()
+ .getJingleFileUri(message));
+ shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ String mime = message.getMimeType();
+ if (mime == null) {
+ mime = "*/*";
+ }
+ shareIntent.setType(mime);
+ }
+ try {
+ activity.startActivity(Intent.createChooser(shareIntent, getText(R.string.share_with)));
+ } catch (ActivityNotFoundException e) {
+ //This should happen only on faulty androids because normally chooser is always available
+ Toast.makeText(activity, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void copyText(Message message) {
if (activity.copyTextToClipboard(message.getMergedBody().toString(),
- R.string.message_text)) {
- Toast.makeText(activity, R.string.message_copied_to_clipboard,
- Toast.LENGTH_SHORT).show();
- }
- }
+ R.string.message_text)) {
+ Toast.makeText(activity, R.string.message_copied_to_clipboard,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
private void selectText(Message message) {
final int index;
@@ -721,779 +721,779 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
private void deleteFile(Message message) {
- if (activity.xmppConnectionService.getFileBackend().deleteFile(message)) {
- message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
- activity.updateConversationList();
- updateMessages();
- }
- }
-
- private void resendMessage(Message message) {
- if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) {
- DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
- if (!file.exists()) {
- Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show();
- message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
- activity.updateConversationList();
- updateMessages();
- return;
- }
- }
- activity.xmppConnectionService.resendFailedMessages(message);
- }
-
- private void copyUrl(Message message) {
- final String url;
- final int resId;
- if (GeoHelper.isGeoUri(message.getBody())) {
+ if (activity.xmppConnectionService.getFileBackend().deleteFile(message)) {
+ message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
+ activity.updateConversationList();
+ updateMessages();
+ }
+ }
+
+ private void resendMessage(Message message) {
+ if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) {
+ DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
+ if (!file.exists()) {
+ Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show();
+ message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
+ activity.updateConversationList();
+ updateMessages();
+ return;
+ }
+ }
+ activity.xmppConnectionService.resendFailedMessages(message);
+ }
+
+ private void copyUrl(Message message) {
+ final String url;
+ final int resId;
+ if (GeoHelper.isGeoUri(message.getBody())) {
resId = R.string.location;
url = message.getBody();
} else if (XmppUri.isXmppUri(message.getBody())) {
resId = R.string.contact;
url = message.getBody();
- } else if (message.hasFileOnRemoteHost()) {
- resId = R.string.file_url;
- url = message.getFileParams().url.toString();
- } else {
- url = message.getBody().trim();
- resId = R.string.file_url;
- }
- if (activity.copyTextToClipboard(url, resId)) {
- Toast.makeText(activity, R.string.url_copied_to_clipboard,
- Toast.LENGTH_SHORT).show();
- }
- }
-
- private void downloadFile(Message message) {
- activity.xmppConnectionService.getHttpConnectionManager()
- .createNewDownloadConnection(message,true);
- }
-
- private void cancelTransmission(Message message) {
- Transferable transferable = message.getTransferable();
- if (transferable != null) {
- transferable.cancel();
- } else {
- activity.xmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED);
- }
- }
-
- private void retryDecryption(Message message) {
- message.setEncryption(Message.ENCRYPTION_PGP);
- activity.updateConversationList();
- updateMessages();
- conversation.getAccount().getPgpDecryptionService().decrypt(message, false);
- }
-
- protected void privateMessageWith(final Jid counterpart) {
- this.mEditMessage.setText("");
- this.conversation.setNextCounterpart(counterpart);
- updateChatMsgHint();
- 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();
- if (oldString.isEmpty() || mEditMessage.getSelectionStart() == 0) {
- mEditMessage.getText().insert(0, nick + ": ");
- } else {
- if (mEditMessage.getText().charAt(
- mEditMessage.getSelectionStart() - 1) != ' ') {
- nick = " " + nick;
- }
- mEditMessage.getText().insert(mEditMessage.getSelectionStart(),
- nick + " ");
- }
- }
-
- @Override
- public void onStop() {
- super.onStop();
- if (this.conversation != null) {
- final String msg = mEditMessage.getText().toString();
- this.conversation.setNextMessage(msg);
- updateChatState(this.conversation, msg);
- }
- }
-
- private void updateChatState(final Conversation conversation, final String msg) {
- ChatState state = msg.length() == 0 ? Config.DEFAULT_CHATSTATE : ChatState.PAUSED;
- Account.State status = conversation.getAccount().getStatus();
- if (status == Account.State.ONLINE && conversation.setOutgoingChatState(state)) {
- activity.xmppConnectionService.sendChatState(conversation);
- }
- }
-
- public boolean reInit(Conversation conversation) {
- if (conversation == null) {
- return false;
- }
- this.activity = (ConversationActivity) getActivity();
- setupIme();
- if (this.conversation != null) {
- final String msg = mEditMessage.getText().toString();
- this.conversation.setNextMessage(msg);
- if (this.conversation != conversation) {
- updateChatState(this.conversation, msg);
- }
- this.conversation.trim();
- }
-
- this.conversation = conversation;
- boolean canWrite = this.conversation.getMode() == Conversation.MODE_SINGLE || this.conversation.getMucOptions().participating();
- this.mEditMessage.setEnabled(canWrite);
- this.mSendButton.setEnabled(canWrite);
- this.mEditMessage.setKeyboardListener(null);
- this.mEditMessage.setText("");
- this.mEditMessage.append(this.conversation.getNextMessage());
- this.mEditMessage.setKeyboardListener(this);
- messageListAdapter.updatePreferences();
- this.messagesView.setAdapter(messageListAdapter);
- updateMessages();
- this.messagesLoaded = true;
- synchronized (this.messageList) {
- final Message first = conversation.getFirstUnreadMessage();
- final int bottom = Math.max(0, this.messageList.size() - 1);
- final int pos;
- if (first == null) {
- pos = bottom;
- } else {
- int i = getIndexOf(first.getUuid(), this.messageList);
- pos = i < 0 ? bottom : i;
- }
- messagesView.setSelection(pos);
+ } else if (message.hasFileOnRemoteHost()) {
+ resId = R.string.file_url;
+ url = message.getFileParams().url.toString();
+ } else {
+ url = message.getBody().trim();
+ resId = R.string.file_url;
+ }
+ if (activity.copyTextToClipboard(url, resId)) {
+ Toast.makeText(activity, R.string.url_copied_to_clipboard,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void downloadFile(Message message) {
+ activity.xmppConnectionService.getHttpConnectionManager()
+ .createNewDownloadConnection(message, true);
+ }
+
+ private void cancelTransmission(Message message) {
+ Transferable transferable = message.getTransferable();
+ if (transferable != null) {
+ transferable.cancel();
+ } else {
+ activity.xmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED);
+ }
+ }
+
+ private void retryDecryption(Message message) {
+ message.setEncryption(Message.ENCRYPTION_PGP);
+ activity.updateConversationList();
+ updateMessages();
+ conversation.getAccount().getPgpDecryptionService().decrypt(message, false);
+ }
+
+ protected void privateMessageWith(final Jid counterpart) {
+ this.mEditMessage.setText("");
+ this.conversation.setNextCounterpart(counterpart);
+ updateChatMsgHint();
+ 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();
+ if (oldString.isEmpty() || mEditMessage.getSelectionStart() == 0) {
+ mEditMessage.getText().insert(0, nick + ": ");
+ } else {
+ if (mEditMessage.getText().charAt(
+ mEditMessage.getSelectionStart() - 1) != ' ') {
+ nick = " " + nick;
+ }
+ mEditMessage.getText().insert(mEditMessage.getSelectionStart(),
+ nick + " ");
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (this.conversation != null) {
+ final String msg = mEditMessage.getText().toString();
+ this.conversation.setNextMessage(msg);
+ updateChatState(this.conversation, msg);
+ }
+ }
+
+ private void updateChatState(final Conversation conversation, final String msg) {
+ ChatState state = msg.length() == 0 ? Config.DEFAULT_CHATSTATE : ChatState.PAUSED;
+ Account.State status = conversation.getAccount().getStatus();
+ if (status == Account.State.ONLINE && conversation.setOutgoingChatState(state)) {
+ activity.xmppConnectionService.sendChatState(conversation);
+ }
+ }
+
+ public boolean reInit(Conversation conversation) {
+ if (conversation == null) {
+ return false;
+ }
+ this.activity = (ConversationActivity) getActivity();
+ setupIme();
+ if (this.conversation != null) {
+ final String msg = mEditMessage.getText().toString();
+ this.conversation.setNextMessage(msg);
+ if (this.conversation != conversation) {
+ updateChatState(this.conversation, msg);
+ }
+ this.conversation.trim();
+ }
+
+ this.conversation = conversation;
+ boolean canWrite = this.conversation.getMode() == Conversation.MODE_SINGLE || this.conversation.getMucOptions().participating();
+ this.mEditMessage.setEnabled(canWrite);
+ this.mSendButton.setEnabled(canWrite);
+ this.mEditMessage.setKeyboardListener(null);
+ this.mEditMessage.setText("");
+ this.mEditMessage.append(this.conversation.getNextMessage());
+ this.mEditMessage.setKeyboardListener(this);
+ messageListAdapter.updatePreferences();
+ this.messagesView.setAdapter(messageListAdapter);
+ updateMessages();
+ this.messagesLoaded = true;
+ synchronized (this.messageList) {
+ final Message first = conversation.getFirstUnreadMessage();
+ final int bottom = Math.max(0, this.messageList.size() - 1);
+ final int pos;
+ if (first == null) {
+ pos = bottom;
+ } else {
+ int i = getIndexOf(first.getUuid(), this.messageList);
+ pos = i < 0 ? bottom : i;
+ }
+ messagesView.setSelection(pos);
return pos == bottom;
- }
- }
-
- private OnClickListener mEnableAccountListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- final Account account = conversation == null ? null : conversation.getAccount();
- if (account != null) {
- account.setOption(Account.OPTION_DISABLED, false);
- activity.xmppConnectionService.updateAccount(account);
- }
- }
- };
-
- private OnClickListener mUnblockClickListener = new OnClickListener() {
- @Override
- public void onClick(final View v) {
- v.post(new Runnable() {
- @Override
- public void run() {
- v.setVisibility(View.INVISIBLE);
- }
- });
- if (conversation.isDomainBlocked()) {
- BlockContactDialog.show(activity, activity.xmppConnectionService, conversation);
- } else {
- activity.unblockConversation(conversation);
- }
- }
- };
-
- private OnClickListener mAddBackClickListener = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- final Contact contact = conversation == null ? null : conversation.getContact();
- if (contact != null) {
- activity.xmppConnectionService.createContact(contact);
- activity.switchToContactDetails(contact);
- }
- }
- };
-
- private OnClickListener mAllowPresenceSubscription = new OnClickListener() {
- @Override
- public void onClick(View v) {
- final Contact contact = conversation == null ? null : conversation.getContact();
- if (contact != null) {
- activity.xmppConnectionService.sendPresencePacket(contact.getAccount(),
- activity.xmppConnectionService.getPresenceGenerator()
- .sendPresenceUpdatesTo(contact));
- hideSnackbar();
- }
- }
- };
-
- private OnClickListener mAnswerSmpClickListener = new OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent intent = new Intent(activity, VerifyOTRActivity.class);
- intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
- intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
- intent.putExtra(VerifyOTRActivity.EXTRA_ACCOUNT, conversation.getAccount().getJid().toBareJid().toString());
- intent.putExtra("mode", VerifyOTRActivity.MODE_ANSWER_QUESTION);
- startActivity(intent);
- }
- };
-
- private void updateSnackBar(final Conversation conversation) {
- final Account account = conversation.getAccount();
- final Contact contact = conversation.getContact();
- final int mode = conversation.getMode();
- if (account.getStatus() == Account.State.DISABLED) {
- showSnackbar(R.string.this_account_is_disabled, R.string.enable, this.mEnableAccountListener);
- } else if (conversation.isBlocked()) {
- showSnackbar(R.string.contact_blocked, R.string.unblock, this.mUnblockClickListener);
- } else if (!contact.showInRoster() && contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
- showSnackbar(R.string.contact_added_you, R.string.add_back, this.mAddBackClickListener);
- } else if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
- showSnackbar(R.string.contact_asks_for_presence_subscription, R.string.allow, this.mAllowPresenceSubscription);
- } else if (mode == Conversation.MODE_MULTI
- && !conversation.getMucOptions().online()
- && account.getStatus() == Account.State.ONLINE) {
- switch (conversation.getMucOptions().getError()) {
- case NICK_IN_USE:
- showSnackbar(R.string.nick_in_use, R.string.edit, clickToMuc);
- break;
- case NO_RESPONSE:
- showSnackbar(R.string.joining_conference, 0, null);
- break;
- case SERVER_NOT_FOUND:
- showSnackbar(R.string.remote_server_not_found,R.string.leave, leaveMuc);
- break;
- case PASSWORD_REQUIRED:
- showSnackbar(R.string.conference_requires_password, R.string.enter_password, enterPassword);
- break;
- case BANNED:
- showSnackbar(R.string.conference_banned, R.string.leave, leaveMuc);
- break;
- case MEMBERS_ONLY:
- showSnackbar(R.string.conference_members_only, R.string.leave, leaveMuc);
- break;
- case KICKED:
- showSnackbar(R.string.conference_kicked, R.string.join, joinMuc);
- break;
- case UNKNOWN:
- activity.xmppConnectionService.joinMuc(conversation);
- //showSnackbar(R.string.conference_unknown_error, R.string.join, joinMuc);
- break;
- case SHUTDOWN:
- showSnackbar(R.string.conference_shutdown, R.string.join, joinMuc);
- break;
- default:
- break;
- }
- } else if (account.hasPendingPgpIntent(conversation)) {
- showSnackbar(R.string.openpgp_messages_found, R.string.decrypt, clickToDecryptListener);
- } else if (mode == Conversation.MODE_SINGLE
- && conversation.smpRequested()) {
- showSnackbar(R.string.smp_requested, R.string.verify, this.mAnswerSmpClickListener);
- } else if (mode == Conversation.MODE_SINGLE
- && conversation.hasValidOtrSession()
- && (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED)
- && (!conversation.isOtrFingerprintVerified())) {
- showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify, clickToVerify);
- } else {
- hideSnackbar();
- }
- }
-
- public void updateMessages() {
- synchronized (this.messageList) {
- if (getView() == null) {
- return;
- }
- final ConversationActivity activity = (ConversationActivity) getActivity();
- if (this.conversation != null) {
- conversation.populateWithMessages(ConversationFragment.this.messageList);
- updateSnackBar(conversation);
- updateStatusMessages();
- this.messageListAdapter.notifyDataSetChanged();
- updateChatMsgHint();
- if (!activity.isConversationsOverviewVisable() || !activity.isConversationsOverviewHideable()) {
- activity.sendReadMarkerIfNecessary(conversation);
- }
- this.updateSendButton();
- }
- }
- }
-
- protected void messageSent() {
- mEditMessage.setText("");
- updateChatMsgHint();
- new Handler().post(new Runnable() {
- @Override
- public void run() {
- int size = messageList.size();
- messagesView.setSelection(size - 1);
- }
- });
- }
-
- public void setFocusOnInputField() {
- mEditMessage.requestFocus();
- }
-
- enum SendButtonAction {TEXT, TAKE_PHOTO, SEND_LOCATION, RECORD_VOICE, CANCEL, CHOOSE_PICTURE}
-
- private int getSendButtonImageResource(SendButtonAction action, Presence.Status status) {
- switch (action) {
- case TEXT:
- switch (status) {
- case CHAT:
- case ONLINE:
- return R.drawable.ic_send_text_online;
- case AWAY:
- return R.drawable.ic_send_text_away;
- case XA:
- case DND:
- return R.drawable.ic_send_text_dnd;
- default:
- return R.drawable.ic_send_text_offline;
- }
- case TAKE_PHOTO:
- switch (status) {
- case CHAT:
- case ONLINE:
- return R.drawable.ic_send_photo_online;
- case AWAY:
- return R.drawable.ic_send_photo_away;
- case XA:
- case DND:
- return R.drawable.ic_send_photo_dnd;
- default:
- return R.drawable.ic_send_photo_offline;
- }
- case RECORD_VOICE:
- switch (status) {
- case CHAT:
- case ONLINE:
- return R.drawable.ic_send_voice_online;
- case AWAY:
- return R.drawable.ic_send_voice_away;
- case XA:
- case DND:
- return R.drawable.ic_send_voice_dnd;
- default:
- return R.drawable.ic_send_voice_offline;
- }
- case SEND_LOCATION:
- switch (status) {
- case CHAT:
- case ONLINE:
- return R.drawable.ic_send_location_online;
- case AWAY:
- return R.drawable.ic_send_location_away;
- case XA:
- case DND:
- return R.drawable.ic_send_location_dnd;
- default:
- return R.drawable.ic_send_location_offline;
- }
- case CANCEL:
- switch (status) {
- case CHAT:
- case ONLINE:
- return R.drawable.ic_send_cancel_online;
- case AWAY:
- return R.drawable.ic_send_cancel_away;
- case XA:
- case DND:
- return R.drawable.ic_send_cancel_dnd;
- default:
- return R.drawable.ic_send_cancel_offline;
- }
- case CHOOSE_PICTURE:
- switch (status) {
- case CHAT:
- case ONLINE:
- return R.drawable.ic_send_picture_online;
- case AWAY:
- return R.drawable.ic_send_picture_away;
- case XA:
- case DND:
- return R.drawable.ic_send_picture_dnd;
- default:
- return R.drawable.ic_send_picture_offline;
- }
- }
- return R.drawable.ic_send_text_offline;
- }
-
- public void updateSendButton() {
- final Conversation c = this.conversation;
- final SendButtonAction action;
- final Presence.Status status;
- 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 (empty && c.getNextCounterpart() != null) {
- action = SendButtonAction.CANCEL;
- } else {
- action = SendButtonAction.TEXT;
- }
- } else {
- if (empty) {
- if (conference && c.getNextCounterpart() != null) {
- action = SendButtonAction.CANCEL;
- } else {
- String setting = activity.getPreferences().getString("quick_action", "recent");
- if (!setting.equals("none") && UIHelper.receivedLocationQuestion(conversation.getLatestMessage())) {
- setting = "location";
- } else if (setting.equals("recent")) {
- setting = activity.getPreferences().getString("recently_used_quick_action", "text");
- }
- switch (setting) {
- case "photo":
- action = SendButtonAction.TAKE_PHOTO;
- break;
- case "location":
- action = SendButtonAction.SEND_LOCATION;
- break;
- case "voice":
- action = SendButtonAction.RECORD_VOICE;
- break;
- case "picture":
- action = SendButtonAction.CHOOSE_PICTURE;
- break;
- default:
- action = SendButtonAction.TEXT;
- break;
- }
- }
- } else {
- action = SendButtonAction.TEXT;
- }
- }
- if (activity.useSendButtonToIndicateStatus() && c != null
- && c.getAccount().getStatus() == Account.State.ONLINE) {
- if (c.getMode() == Conversation.MODE_SINGLE) {
- status = c.getContact().getShownStatus();
- } else {
- status = c.getMucOptions().online() ? Presence.Status.ONLINE : Presence.Status.OFFLINE;
- }
- } else {
- status = Presence.Status.OFFLINE;
- }
- this.mSendButton.setTag(action);
- this.mSendButton.setImageResource(getSendButtonImageResource(action, status));
- }
-
- protected 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) {
- //this.messageList.add(Message.createStatusMessage(conversation, getString(R.string.contact_is_typing, conversation.getName())));
- } else if (state == ChatState.PAUSED) {
- //this.messageList.add(Message.createStatusMessage(conversation, getString(R.string.contact_has_stopped_typing, conversation.getName())));
- } else {
- for (int i = this.messageList.size() - 1; i >= 0; --i) {
- if (this.messageList.get(i).getStatus() == Message.STATUS_RECEIVED) {
- return;
- } else {
- if (this.messageList.get(i).getStatus() == Message.STATUS_SEND_DISPLAYED) {
+ }
+ }
+
+ private OnClickListener mEnableAccountListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final Account account = conversation == null ? null : conversation.getAccount();
+ if (account != null) {
+ account.setOption(Account.OPTION_DISABLED, false);
+ activity.xmppConnectionService.updateAccount(account);
+ }
+ }
+ };
+
+ private OnClickListener mUnblockClickListener = new OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ v.post(new Runnable() {
+ @Override
+ public void run() {
+ v.setVisibility(View.INVISIBLE);
+ }
+ });
+ if (conversation.isDomainBlocked()) {
+ BlockContactDialog.show(activity, activity.xmppConnectionService, conversation);
+ } else {
+ activity.unblockConversation(conversation);
+ }
+ }
+ };
+
+ private OnClickListener mAddBackClickListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ final Contact contact = conversation == null ? null : conversation.getContact();
+ if (contact != null) {
+ activity.xmppConnectionService.createContact(contact);
+ activity.switchToContactDetails(contact);
+ }
+ }
+ };
+
+ private OnClickListener mAllowPresenceSubscription = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final Contact contact = conversation == null ? null : conversation.getContact();
+ if (contact != null) {
+ activity.xmppConnectionService.sendPresencePacket(contact.getAccount(),
+ activity.xmppConnectionService.getPresenceGenerator()
+ .sendPresenceUpdatesTo(contact));
+ hideSnackbar();
+ }
+ }
+ };
+
+ private OnClickListener mAnswerSmpClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(activity, VerifyOTRActivity.class);
+ intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
+ intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
+ intent.putExtra(VerifyOTRActivity.EXTRA_ACCOUNT, conversation.getAccount().getJid().toBareJid().toString());
+ intent.putExtra("mode", VerifyOTRActivity.MODE_ANSWER_QUESTION);
+ startActivity(intent);
+ }
+ };
+
+ private void updateSnackBar(final Conversation conversation) {
+ final Account account = conversation.getAccount();
+ final Contact contact = conversation.getContact();
+ final int mode = conversation.getMode();
+ if (account.getStatus() == Account.State.DISABLED) {
+ showSnackbar(R.string.this_account_is_disabled, R.string.enable, this.mEnableAccountListener);
+ } else if (conversation.isBlocked()) {
+ showSnackbar(R.string.contact_blocked, R.string.unblock, this.mUnblockClickListener);
+ } else if (!contact.showInRoster() && contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
+ showSnackbar(R.string.contact_added_you, R.string.add_back, this.mAddBackClickListener);
+ } else if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
+ showSnackbar(R.string.contact_asks_for_presence_subscription, R.string.allow, this.mAllowPresenceSubscription);
+ } else if (mode == Conversation.MODE_MULTI
+ && !conversation.getMucOptions().online()
+ && account.getStatus() == Account.State.ONLINE) {
+ switch (conversation.getMucOptions().getError()) {
+ case NICK_IN_USE:
+ showSnackbar(R.string.nick_in_use, R.string.edit, clickToMuc);
+ break;
+ case NO_RESPONSE:
+ showSnackbar(R.string.joining_conference, 0, null);
+ break;
+ case SERVER_NOT_FOUND:
+ showSnackbar(R.string.remote_server_not_found, R.string.leave, leaveMuc);
+ break;
+ case PASSWORD_REQUIRED:
+ showSnackbar(R.string.conference_requires_password, R.string.enter_password, enterPassword);
+ break;
+ case BANNED:
+ showSnackbar(R.string.conference_banned, R.string.leave, leaveMuc);
+ break;
+ case MEMBERS_ONLY:
+ showSnackbar(R.string.conference_members_only, R.string.leave, leaveMuc);
+ break;
+ case KICKED:
+ showSnackbar(R.string.conference_kicked, R.string.join, joinMuc);
+ break;
+ case UNKNOWN:
+ activity.xmppConnectionService.joinMuc(conversation);
+ //showSnackbar(R.string.conference_unknown_error, R.string.join, joinMuc);
+ break;
+ case SHUTDOWN:
+ showSnackbar(R.string.conference_shutdown, R.string.join, joinMuc);
+ break;
+ default:
+ break;
+ }
+ } else if (account.hasPendingPgpIntent(conversation)) {
+ showSnackbar(R.string.openpgp_messages_found, R.string.decrypt, clickToDecryptListener);
+ } else if (mode == Conversation.MODE_SINGLE
+ && conversation.smpRequested()) {
+ showSnackbar(R.string.smp_requested, R.string.verify, this.mAnswerSmpClickListener);
+ } else if (mode == Conversation.MODE_SINGLE
+ && conversation.hasValidOtrSession()
+ && (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED)
+ && (!conversation.isOtrFingerprintVerified())) {
+ showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify, clickToVerify);
+ } else {
+ hideSnackbar();
+ }
+ }
+
+ public void updateMessages() {
+ synchronized (this.messageList) {
+ if (getView() == null) {
+ return;
+ }
+ final ConversationActivity activity = (ConversationActivity) getActivity();
+ if (this.conversation != null) {
+ conversation.populateWithMessages(ConversationFragment.this.messageList);
+ updateSnackBar(conversation);
+ updateStatusMessages();
+ this.messageListAdapter.notifyDataSetChanged();
+ updateChatMsgHint();
+ if (!activity.isConversationsOverviewVisable() || !activity.isConversationsOverviewHideable()) {
+ activity.sendReadMarkerIfNecessary(conversation);
+ }
+ this.updateSendButton();
+ }
+ }
+ }
+
+ protected void messageSent() {
+ mEditMessage.setText("");
+ updateChatMsgHint();
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ int size = messageList.size();
+ messagesView.setSelection(size - 1);
+ }
+ });
+ }
+
+ public void setFocusOnInputField() {
+ mEditMessage.requestFocus();
+ }
+
+ enum SendButtonAction {TEXT, TAKE_PHOTO, SEND_LOCATION, RECORD_VOICE, CANCEL, CHOOSE_PICTURE}
+
+ private int getSendButtonImageResource(SendButtonAction action, Presence.Status status) {
+ switch (action) {
+ case TEXT:
+ switch (status) {
+ case CHAT:
+ case ONLINE:
+ return R.drawable.ic_send_text_online;
+ case AWAY:
+ return R.drawable.ic_send_text_away;
+ case XA:
+ case DND:
+ return R.drawable.ic_send_text_dnd;
+ default:
+ return R.drawable.ic_send_text_offline;
+ }
+ case TAKE_PHOTO:
+ switch (status) {
+ case CHAT:
+ case ONLINE:
+ return R.drawable.ic_send_photo_online;
+ case AWAY:
+ return R.drawable.ic_send_photo_away;
+ case XA:
+ case DND:
+ return R.drawable.ic_send_photo_dnd;
+ default:
+ return R.drawable.ic_send_photo_offline;
+ }
+ case RECORD_VOICE:
+ switch (status) {
+ case CHAT:
+ case ONLINE:
+ return R.drawable.ic_send_voice_online;
+ case AWAY:
+ return R.drawable.ic_send_voice_away;
+ case XA:
+ case DND:
+ return R.drawable.ic_send_voice_dnd;
+ default:
+ return R.drawable.ic_send_voice_offline;
+ }
+ case SEND_LOCATION:
+ switch (status) {
+ case CHAT:
+ case ONLINE:
+ return R.drawable.ic_send_location_online;
+ case AWAY:
+ return R.drawable.ic_send_location_away;
+ case XA:
+ case DND:
+ return R.drawable.ic_send_location_dnd;
+ default:
+ return R.drawable.ic_send_location_offline;
+ }
+ case CANCEL:
+ switch (status) {
+ case CHAT:
+ case ONLINE:
+ return R.drawable.ic_send_cancel_online;
+ case AWAY:
+ return R.drawable.ic_send_cancel_away;
+ case XA:
+ case DND:
+ return R.drawable.ic_send_cancel_dnd;
+ default:
+ return R.drawable.ic_send_cancel_offline;
+ }
+ case CHOOSE_PICTURE:
+ switch (status) {
+ case CHAT:
+ case ONLINE:
+ return R.drawable.ic_send_picture_online;
+ case AWAY:
+ return R.drawable.ic_send_picture_away;
+ case XA:
+ case DND:
+ return R.drawable.ic_send_picture_dnd;
+ default:
+ return R.drawable.ic_send_picture_offline;
+ }
+ }
+ return R.drawable.ic_send_text_offline;
+ }
+
+ public void updateSendButton() {
+ final Conversation c = this.conversation;
+ final SendButtonAction action;
+ final Presence.Status status;
+ 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 (empty && c.getNextCounterpart() != null) {
+ action = SendButtonAction.CANCEL;
+ } else {
+ action = SendButtonAction.TEXT;
+ }
+ } else {
+ if (empty) {
+ if (conference && c.getNextCounterpart() != null) {
+ action = SendButtonAction.CANCEL;
+ } else {
+ String setting = activity.getPreferences().getString("quick_action", "recent");
+ if (!setting.equals("none") && UIHelper.receivedLocationQuestion(conversation.getLatestMessage())) {
+ setting = "location";
+ } else if (setting.equals("recent")) {
+ setting = activity.getPreferences().getString("recently_used_quick_action", "text");
+ }
+ switch (setting) {
+ case "photo":
+ action = SendButtonAction.TAKE_PHOTO;
+ break;
+ case "location":
+ action = SendButtonAction.SEND_LOCATION;
+ break;
+ case "voice":
+ action = SendButtonAction.RECORD_VOICE;
+ break;
+ case "picture":
+ action = SendButtonAction.CHOOSE_PICTURE;
+ break;
+ default:
+ action = SendButtonAction.TEXT;
+ break;
+ }
+ }
+ } else {
+ action = SendButtonAction.TEXT;
+ }
+ }
+ if (activity.useSendButtonToIndicateStatus() && c != null
+ && c.getAccount().getStatus() == Account.State.ONLINE) {
+ if (c.getMode() == Conversation.MODE_SINGLE) {
+ status = c.getContact().getShownStatus();
+ } else {
+ status = c.getMucOptions().online() ? Presence.Status.ONLINE : Presence.Status.OFFLINE;
+ }
+ } else {
+ status = Presence.Status.OFFLINE;
+ }
+ this.mSendButton.setTag(action);
+ this.mSendButton.setImageResource(getSendButtonImageResource(action, status));
+ }
+
+ protected 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) {
+ //this.messageList.add(Message.createStatusMessage(conversation, getString(R.string.contact_is_typing, conversation.getName())));
+ } else if (state == ChatState.PAUSED) {
+ //this.messageList.add(Message.createStatusMessage(conversation, getString(R.string.contact_has_stopped_typing, conversation.getName())));
+ } else {
+ for (int i = this.messageList.size() - 1; i >= 0; --i) {
+ if (this.messageList.get(i).getStatus() == Message.STATUS_RECEIVED) {
+ return;
+ } else {
+ if (this.messageList.get(i).getStatus() == Message.STATUS_SEND_DISPLAYED) {
// this.messageList.add(i + 1,
// Message.createStatusMessage(conversation, getString(R.string.contact_has_read_up_to_this_point, conversation.getName())));
// return;
- }
- }
- }
- }
- }
- }
- }
-
- 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);
- snackbarMessage.setText(message);
- snackbarMessage.setOnClickListener(null);
- snackbarAction.setVisibility(clickListener == null ? View.GONE : View.VISIBLE);
- if (action != 0) {
- snackbarAction.setText(action);
- }
- snackbarAction.setOnClickListener(clickListener);
- }
-
- protected void hideSnackbar() {
- snackbar.setVisibility(View.GONE);
- }
-
- protected void sendPlainTextMessage(Message message) {
- ConversationActivity activity = (ConversationActivity) getActivity();
- activity.xmppConnectionService.sendMessage(message);
- messageSent();
- }
-
- protected void sendPgpMessage(final Message message) {
- final ConversationActivity activity = (ConversationActivity) getActivity();
- final XmppConnectionService xmppService = activity.xmppConnectionService;
- final Contact contact = message.getConversation().getContact();
- if (!activity.hasPgp()) {
- activity.showInstallPgpDialog();
- return;
- }
- if (conversation.getAccount().getPgpSignature() == null) {
- activity.announcePgp(conversation.getAccount(), conversation, activity.onOpenPGPKeyPublished);
- return;
- }
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- if (contact.getPgpKeyId() != 0) {
- xmppService.getPgpEngine().hasKey(contact,
- new UiCallback<Contact>() {
-
- @Override
- public void userInputRequried(PendingIntent pi,
- Contact contact) {
- activity.runIntent(
- pi,
- ConversationActivity.REQUEST_ENCRYPT_MESSAGE);
- }
-
- @Override
- public void success(Contact contact) {
- activity.encryptTextMessage(message);
- }
-
- @Override
- public void error(int error, Contact contact) {
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(activity,
- R.string.unable_to_connect_to_keychain,
- Toast.LENGTH_SHORT
- ).show();
- }
- });
- }
- });
-
- } else {
- showNoPGPKeyDialog(false,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- conversation
- .setNextEncryption(Message.ENCRYPTION_NONE);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ 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);
+ snackbarMessage.setText(message);
+ snackbarMessage.setOnClickListener(null);
+ snackbarAction.setVisibility(clickListener == null ? View.GONE : View.VISIBLE);
+ if (action != 0) {
+ snackbarAction.setText(action);
+ }
+ snackbarAction.setOnClickListener(clickListener);
+ }
+
+ protected void hideSnackbar() {
+ snackbar.setVisibility(View.GONE);
+ }
+
+ protected void sendPlainTextMessage(Message message) {
+ ConversationActivity activity = (ConversationActivity) getActivity();
+ activity.xmppConnectionService.sendMessage(message);
+ messageSent();
+ }
+
+ protected void sendPgpMessage(final Message message) {
+ final ConversationActivity activity = (ConversationActivity) getActivity();
+ final XmppConnectionService xmppService = activity.xmppConnectionService;
+ final Contact contact = message.getConversation().getContact();
+ if (!activity.hasPgp()) {
+ activity.showInstallPgpDialog();
+ return;
+ }
+ if (conversation.getAccount().getPgpSignature() == null) {
+ activity.announcePgp(conversation.getAccount(), conversation, activity.onOpenPGPKeyPublished);
+ return;
+ }
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ if (contact.getPgpKeyId() != 0) {
+ xmppService.getPgpEngine().hasKey(contact,
+ new UiCallback<Contact>() {
+
+ @Override
+ public void userInputRequried(PendingIntent pi,
+ Contact contact) {
+ activity.runIntent(
+ pi,
+ ConversationActivity.REQUEST_ENCRYPT_MESSAGE);
+ }
+
+ @Override
+ public void success(Contact contact) {
+ activity.encryptTextMessage(message);
+ }
+
+ @Override
+ public void error(int error, Contact contact) {
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(activity,
+ R.string.unable_to_connect_to_keychain,
+ Toast.LENGTH_SHORT
+ ).show();
+ }
+ });
+ }
+ });
+
+ } else {
+ showNoPGPKeyDialog(false,
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+ conversation
+ .setNextEncryption(Message.ENCRYPTION_NONE);
xmppService.updateConversation(conversation);
- message.setEncryption(Message.ENCRYPTION_NONE);
- xmppService.sendMessage(message);
- messageSent();
- }
- });
- }
- } else {
- if (conversation.getMucOptions().pgpKeysInUse()) {
- if (!conversation.getMucOptions().everybodyHasKeys()) {
- Toast warning = Toast
- .makeText(getActivity(),
- R.string.missing_public_keys,
- Toast.LENGTH_LONG);
- warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
- warning.show();
- }
- activity.encryptTextMessage(message);
- } else {
- showNoPGPKeyDialog(true,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- conversation
- .setNextEncryption(Message.ENCRYPTION_NONE);
- message.setEncryption(Message.ENCRYPTION_NONE);
+ message.setEncryption(Message.ENCRYPTION_NONE);
+ xmppService.sendMessage(message);
+ messageSent();
+ }
+ });
+ }
+ } else {
+ if (conversation.getMucOptions().pgpKeysInUse()) {
+ if (!conversation.getMucOptions().everybodyHasKeys()) {
+ Toast warning = Toast
+ .makeText(getActivity(),
+ R.string.missing_public_keys,
+ Toast.LENGTH_LONG);
+ warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
+ warning.show();
+ }
+ activity.encryptTextMessage(message);
+ } else {
+ showNoPGPKeyDialog(true,
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+ conversation
+ .setNextEncryption(Message.ENCRYPTION_NONE);
+ message.setEncryption(Message.ENCRYPTION_NONE);
xmppService.updateConversation(conversation);
- xmppService.sendMessage(message);
- messageSent();
- }
- });
- }
- }
- }
-
- public void showNoPGPKeyDialog(boolean plural,
- DialogInterface.OnClickListener listener) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- if (plural) {
- builder.setTitle(getString(R.string.no_pgp_keys));
- builder.setMessage(getText(R.string.contacts_have_no_pgp_keys));
- } else {
- builder.setTitle(getString(R.string.no_pgp_key));
- builder.setMessage(getText(R.string.contact_has_no_pgp_key));
- }
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.setPositiveButton(getString(R.string.send_unencrypted),
- listener);
- builder.create().show();
- }
-
- protected void sendAxolotlMessage(final Message message) {
- final ConversationActivity activity = (ConversationActivity) getActivity();
- final XmppConnectionService xmppService = activity.xmppConnectionService;
- xmppService.sendMessage(message);
- messageSent();
- }
-
- protected void sendOtrMessage(final Message message) {
- final ConversationActivity activity = (ConversationActivity) getActivity();
- final XmppConnectionService xmppService = activity.xmppConnectionService;
- activity.selectPresence(message.getConversation(),
- new OnPresenceSelected() {
-
- @Override
- public void onPresenceSelected() {
- message.setCounterpart(conversation.getNextCounterpart());
- xmppService.sendMessage(message);
- messageSent();
- }
- });
- }
-
- public void appendText(String text) {
- if (text == null) {
- return;
- }
- String previous = this.mEditMessage.getText().toString();
- if (previous.length() != 0 && !previous.endsWith(" ")) {
- text = " " + text;
- }
- this.mEditMessage.append(text);
- }
-
- @Override
- public boolean onEnterPressed() {
- if (activity.enterIsSend()) {
- sendMessage();
- return true;
- } else {
- return false;
- }
- }
-
- @Override
- public void onTypingStarted() {
- Account.State status = conversation.getAccount().getStatus();
- if (status == Account.State.ONLINE && conversation.setOutgoingChatState(ChatState.COMPOSING)) {
- activity.xmppConnectionService.sendChatState(conversation);
- }
- activity.hideConversationsOverview();
- updateSendButton();
- }
-
- @Override
- public void onTypingStopped() {
- Account.State status = conversation.getAccount().getStatus();
- if (status == Account.State.ONLINE && conversation.setOutgoingChatState(ChatState.PAUSED)) {
- activity.xmppConnectionService.sendChatState(conversation);
- }
- }
-
- @Override
- public void onTextDeleted() {
- Account.State status = conversation.getAccount().getStatus();
- if (status == Account.State.ONLINE && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
- activity.xmppConnectionService.sendChatState(conversation);
- }
- updateSendButton();
- }
-
- @Override
- public void onTextChanged() {
- if (conversation != null && conversation.getCorrectingMessage() != null) {
- updateSendButton();
- }
- }
-
- private int completionIndex = 0;
- private int lastCompletionLength = 0;
- private String incomplete;
- private int lastCompletionCursor;
- private boolean firstWord = false;
-
- @Override
- public boolean onTabPressed(boolean repeated) {
- if (conversation == null || conversation.getMode() == Conversation.MODE_SINGLE) {
- return false;
- }
- if (repeated) {
- completionIndex++;
- } else {
- lastCompletionLength = 0;
- completionIndex = 0;
- final String content = mEditMessage.getText().toString();
- lastCompletionCursor = mEditMessage.getSelectionEnd();
- int start = lastCompletionCursor > 0 ? content.lastIndexOf(" ",lastCompletionCursor-1) + 1 : 0;
- firstWord = start == 0;
- incomplete = content.substring(start,lastCompletionCursor);
- }
- List<String> completions = new ArrayList<>();
- for(MucOptions.User user : conversation.getMucOptions().getUsers()) {
- String name = user.getName();
- if (name != null && name.startsWith(incomplete)) {
- completions.add(name+(firstWord ? ": " : " "));
- }
- }
- Collections.sort(completions);
- if (completions.size() > completionIndex) {
- String completion = completions.get(completionIndex).substring(incomplete.length());
- mEditMessage.getEditableText().delete(lastCompletionCursor,lastCompletionCursor + lastCompletionLength);
- mEditMessage.getEditableText().insert(lastCompletionCursor, completion);
- lastCompletionLength = completion.length();
- } else {
- completionIndex = -1;
- mEditMessage.getEditableText().delete(lastCompletionCursor,lastCompletionCursor + lastCompletionLength);
- lastCompletionLength = 0;
- }
- return true;
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode,
- final Intent data) {
- if (resultCode == Activity.RESULT_OK) {
- if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) {
- activity.getSelectedConversation().getAccount().getPgpDecryptionService().continueDecryption(true);
- } else if (requestCode == ConversationActivity.REQUEST_TRUST_KEYS_TEXT) {
- final String body = mEditMessage.getText().toString();
- Message message = new Message(conversation, body, conversation.getNextEncryption());
- sendAxolotlMessage(message);
- } else if (requestCode == ConversationActivity.REQUEST_TRUST_KEYS_MENU) {
- int choice = data.getIntExtra("choice", ConversationActivity.ATTACHMENT_CHOICE_INVALID);
- activity.selectPresenceToAttachFile(choice, conversation.getNextEncryption());
- }
- }
- }
+ xmppService.sendMessage(message);
+ messageSent();
+ }
+ });
+ }
+ }
+ }
+
+ public void showNoPGPKeyDialog(boolean plural,
+ DialogInterface.OnClickListener listener) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ if (plural) {
+ builder.setTitle(getString(R.string.no_pgp_keys));
+ builder.setMessage(getText(R.string.contacts_have_no_pgp_keys));
+ } else {
+ builder.setTitle(getString(R.string.no_pgp_key));
+ builder.setMessage(getText(R.string.contact_has_no_pgp_key));
+ }
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.setPositiveButton(getString(R.string.send_unencrypted),
+ listener);
+ builder.create().show();
+ }
+
+ protected void sendAxolotlMessage(final Message message) {
+ final ConversationActivity activity = (ConversationActivity) getActivity();
+ final XmppConnectionService xmppService = activity.xmppConnectionService;
+ xmppService.sendMessage(message);
+ messageSent();
+ }
+
+ protected void sendOtrMessage(final Message message) {
+ final ConversationActivity activity = (ConversationActivity) getActivity();
+ final XmppConnectionService xmppService = activity.xmppConnectionService;
+ activity.selectPresence(message.getConversation(),
+ new OnPresenceSelected() {
+
+ @Override
+ public void onPresenceSelected() {
+ message.setCounterpart(conversation.getNextCounterpart());
+ xmppService.sendMessage(message);
+ messageSent();
+ }
+ });
+ }
+
+ public void appendText(String text) {
+ if (text == null) {
+ return;
+ }
+ String previous = this.mEditMessage.getText().toString();
+ if (previous.length() != 0 && !previous.endsWith(" ")) {
+ text = " " + text;
+ }
+ this.mEditMessage.append(text);
+ }
+
+ @Override
+ public boolean onEnterPressed() {
+ if (activity.enterIsSend()) {
+ sendMessage();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void onTypingStarted() {
+ Account.State status = conversation.getAccount().getStatus();
+ if (status == Account.State.ONLINE && conversation.setOutgoingChatState(ChatState.COMPOSING)) {
+ activity.xmppConnectionService.sendChatState(conversation);
+ }
+ activity.hideConversationsOverview();
+ updateSendButton();
+ }
+
+ @Override
+ public void onTypingStopped() {
+ Account.State status = conversation.getAccount().getStatus();
+ if (status == Account.State.ONLINE && conversation.setOutgoingChatState(ChatState.PAUSED)) {
+ activity.xmppConnectionService.sendChatState(conversation);
+ }
+ }
+
+ @Override
+ public void onTextDeleted() {
+ Account.State status = conversation.getAccount().getStatus();
+ if (status == Account.State.ONLINE && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
+ activity.xmppConnectionService.sendChatState(conversation);
+ }
+ updateSendButton();
+ }
+
+ @Override
+ public void onTextChanged() {
+ if (conversation != null && conversation.getCorrectingMessage() != null) {
+ updateSendButton();
+ }
+ }
+
+ private int completionIndex = 0;
+ private int lastCompletionLength = 0;
+ private String incomplete;
+ private int lastCompletionCursor;
+ private boolean firstWord = false;
+
+ @Override
+ public boolean onTabPressed(boolean repeated) {
+ if (conversation == null || conversation.getMode() == Conversation.MODE_SINGLE) {
+ return false;
+ }
+ if (repeated) {
+ completionIndex++;
+ } else {
+ lastCompletionLength = 0;
+ completionIndex = 0;
+ final String content = mEditMessage.getText().toString();
+ lastCompletionCursor = mEditMessage.getSelectionEnd();
+ int start = lastCompletionCursor > 0 ? content.lastIndexOf(" ", lastCompletionCursor - 1) + 1 : 0;
+ firstWord = start == 0;
+ incomplete = content.substring(start, lastCompletionCursor);
+ }
+ List<String> completions = new ArrayList<>();
+ for (MucOptions.User user : conversation.getMucOptions().getUsers()) {
+ String name = user.getName();
+ if (name != null && name.startsWith(incomplete)) {
+ completions.add(name + (firstWord ? ": " : " "));
+ }
+ }
+ Collections.sort(completions);
+ if (completions.size() > completionIndex) {
+ String completion = completions.get(completionIndex).substring(incomplete.length());
+ mEditMessage.getEditableText().delete(lastCompletionCursor, lastCompletionCursor + lastCompletionLength);
+ mEditMessage.getEditableText().insert(lastCompletionCursor, completion);
+ lastCompletionLength = completion.length();
+ } else {
+ completionIndex = -1;
+ mEditMessage.getEditableText().delete(lastCompletionCursor, lastCompletionCursor + lastCompletionLength);
+ lastCompletionLength = 0;
+ }
+ return true;
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode,
+ final Intent data) {
+ if (resultCode == Activity.RESULT_OK) {
+ if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) {
+ activity.getSelectedConversation().getAccount().getPgpDecryptionService().continueDecryption(true);
+ } else if (requestCode == ConversationActivity.REQUEST_TRUST_KEYS_TEXT) {
+ final String body = mEditMessage.getText().toString();
+ Message message = new Message(conversation, body, conversation.getNextEncryption());
+ sendAxolotlMessage(message);
+ } else if (requestCode == ConversationActivity.REQUEST_TRUST_KEYS_MENU) {
+ int choice = data.getIntExtra("choice", ConversationActivity.ATTACHMENT_CHOICE_INVALID);
+ activity.selectPresenceToAttachFile(choice, conversation.getNextEncryption());
+ }
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java b/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java
index b5e3c8a1c..58453ba75 100644
--- a/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java
@@ -61,1104 +61,1104 @@ import de.pixart.messenger.xmpp.jid.Jid;
import de.pixart.messenger.xmpp.pep.Avatar;
public class EditAccountActivity extends OmemoActivity implements OnAccountUpdate,
- OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnMamPreferencesFetched {
-
- private static final int REQUEST_DATA_SAVER = 0x37af244;
- private AutoCompleteTextView mAccountJid;
- private EditText mPassword;
- private EditText mPasswordConfirm;
- private CheckBox mRegisterNew;
- private Button mCancelButton;
- private Button mSaveButton;
- private Button mDisableOsOptimizationsButton;
- private TextView mDisableOsOptimizationsHeadline;
- private TextView getmDisableOsOptimizationsBody;
- private TableLayout mMoreTable;
-
- private LinearLayout mStats;
- private RelativeLayout mOsOptimizations;
- private TextView mServerInfoSm;
- private TextView mServerInfoRosterVersion;
- private TextView mServerInfoCarbons;
- private TextView mServerInfoMam;
- private TextView mServerInfoCSI;
- private TextView mServerInfoBlocking;
- private TextView mServerInfoPep;
- private TextView mServerInfoHttpUpload;
- private TextView mServerInfoPush;
- private TextView mSessionEst;
- private TextView mOtrFingerprint;
- private TextView mAxolotlFingerprint;
- private TextView mOwnFingerprintDesc;
- private TextView mAccountJidLabel;
- private ImageView mAvatar;
- private RelativeLayout mOtrFingerprintBox;
- private RelativeLayout mAxolotlFingerprintBox;
- private ImageButton mOtrFingerprintToClipboardButton;
- private ImageButton mAxolotlFingerprintToClipboardButton;
- private ImageButton mRegenerateAxolotlKeyButton;
- private LinearLayout keys;
- private LinearLayout keysCard;
- private LinearLayout mNamePort;
- private EditText mHostname;
- private EditText mPort;
- private AlertDialog mCaptchaDialog = null;
-
- private Jid jidToEdit;
- private boolean mInitMode = false;
- private boolean mUsernameMode = Config.DOMAIN_LOCK != null;
- private boolean mShowOptions = false;
- private Account mAccount;
- private String messageFingerprint;
-
- private boolean mFetchingAvatar = false;
-
- private final OnClickListener mSaveButtonClickListener = new OnClickListener() {
-
- @Override
- public void onClick(final View v) {
- final String password = mPassword.getText().toString();
- final String passwordConfirm = mPasswordConfirm.getText().toString();
-
- if (!mInitMode && passwordChangedInMagicCreateMode()) {
- gotoChangePassword(password);
- return;
- }
- if (mInitMode && mAccount != null) {
- mAccount.setOption(Account.OPTION_DISABLED, false);
- }
- if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !accountInfoEdited()) {
- mAccount.setOption(Account.OPTION_DISABLED, false);
- if (!xmppConnectionService.updateAccount(mAccount)) {
- Toast.makeText(EditAccountActivity.this,R.string.unable_to_update_account,Toast.LENGTH_SHORT).show();
- }
- return;
- }
- final boolean registerNewAccount = mRegisterNew.isChecked() && !Config.DISALLOW_REGISTRATION_IN_UI;
- if (mUsernameMode && mAccountJid.getText().toString().contains("@")) {
- mAccountJid.setError(getString(R.string.invalid_username));
- mAccountJid.requestFocus();
- return;
- }
- final Jid jid;
- try {
- if (mUsernameMode) {
- jid = Jid.fromParts(mAccountJid.getText().toString(), getUserModeDomain(), null);
- } else {
- jid = Jid.fromString(mAccountJid.getText().toString());
- }
- } catch (final InvalidJidException e) {
- if (mUsernameMode) {
- mAccountJid.setError(getString(R.string.invalid_username));
- } else {
- mAccountJid.setError(getString(R.string.invalid_jid));
- }
- mAccountJid.requestFocus();
- return;
- }
- String hostname = null;
- int numericPort = 5222;
- if (mShowOptions) {
- hostname = mHostname.getText().toString().replaceAll("\\s","");
- final String port = mPort.getText().toString().replaceAll("\\s","");
- if (hostname.contains(" ")) {
- mHostname.setError(getString(R.string.not_valid_hostname));
- mHostname.requestFocus();
- return;
- }
- try {
- numericPort = Integer.parseInt(port);
- if (numericPort < 0 || numericPort > 65535) {
- mPort.setError(getString(R.string.not_a_valid_port));
- mPort.requestFocus();
- return;
- }
-
- } catch (NumberFormatException e) {
- mPort.setError(getString(R.string.not_a_valid_port));
- mPort.requestFocus();
- return;
- }
- }
-
- if (jid.isDomainJid()) {
- if (mUsernameMode) {
- mAccountJid.setError(getString(R.string.invalid_username));
- } else {
- mAccountJid.setError(getString(R.string.invalid_jid));
- }
- mAccountJid.requestFocus();
- return;
- }
- if (registerNewAccount) {
- if (XmppConnection.errorMessage != null) {
- Toast.makeText(EditAccountActivity.this,XmppConnection.errorMessage,Toast.LENGTH_LONG).show();
- }
- if (!password.equals(passwordConfirm)) {
- mPasswordConfirm.setError(getString(R.string.passwords_do_not_match));
- mPasswordConfirm.requestFocus();
- return;
- }
- }
- if (mAccount != null) {
- if (mInitMode && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
- mAccount.setOption(Account.OPTION_MAGIC_CREATE, mAccount.getPassword().contains(password));
- }
- mAccount.setJid(jid);
- mAccount.setPort(numericPort);
- mAccount.setHostname(hostname);
- if (XmppConnection.errorMessage != null) {
- mAccountJid.setError(XmppConnection.errorMessage);
- } else {
- mAccountJid.setError(null);
- }
- mPasswordConfirm.setError(null);
- mAccount.setPassword(password);
- mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
- if (!xmppConnectionService.updateAccount(mAccount)) {
- Toast.makeText(EditAccountActivity.this,R.string.unable_to_update_account,Toast.LENGTH_SHORT).show();
- return;
- }
- } else {
- if (xmppConnectionService.findAccountByJid(jid) != null) {
- mAccountJid.setError(getString(R.string.account_already_exists));
- mAccountJid.requestFocus();
- return;
- }
- mAccount = new Account(jid.toBareJid(), password);
- mAccount.setPort(numericPort);
- mAccount.setHostname(hostname);
- mAccount.setOption(Account.OPTION_USETLS, true);
- mAccount.setOption(Account.OPTION_USECOMPRESSION, true);
- mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
- xmppConnectionService.createAccount(mAccount);
- }
- mHostname.setError(null);
- mPort.setError(null);
- if (!mAccount.isOptionSet(Account.OPTION_DISABLED)
- && !registerNewAccount
- && !mInitMode) {
- finish();
- } else {
- updateSaveButton();
- updateAccountInformation(true);
- }
-
- }
- };
- private final OnClickListener mCancelButtonClickListener = new OnClickListener() {
-
- @Override
- public void onClick(final View v) {
- deleteMagicCreatedAccountAndReturnIfNecessary();
- finish();
- }
- };
- private Toast mFetchingMamPrefsToast;
- private TableRow mPushRow;
- private String mSavedInstanceAccount;
- private boolean mSavedInstanceInit = false;
-
- public void refreshUiReal() {
- invalidateOptionsMenu();
- if (mAccount != null
- && mAccount.getStatus() != Account.State.ONLINE
- && mFetchingAvatar) {
- startActivity(new Intent(getApplicationContext(),
- ManageAccountActivity.class));
- finish();
- } else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) {
- if (!mFetchingAvatar) {
- mFetchingAvatar = true;
- xmppConnectionService.checkForAvatar(mAccount, mAvatarFetchCallback);
- }
- }
- if (mAccount != null) {
- updateAccountInformation(false);
- }
- updateSaveButton();
- }
-
- @Override
- public boolean onNavigateUp() {
- deleteMagicCreatedAccountAndReturnIfNecessary();
- return super.onNavigateUp();
- }
-
- @Override
- public void onBackPressed() {
- deleteMagicCreatedAccountAndReturnIfNecessary();
- super.onBackPressed();
- }
-
- private void deleteMagicCreatedAccountAndReturnIfNecessary() {
- if (Config.MAGIC_CREATE_DOMAIN != null
- && mAccount != null
- && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
- && mAccount.isOptionSet(Account.OPTION_REGISTER)
- && xmppConnectionService.getAccounts().size() == 1) {
- xmppConnectionService.deleteAccount(mAccount);
- startActivity(new Intent(EditAccountActivity.this, WelcomeActivity.class));
- }
- }
-
- @Override
- public void onAccountUpdate() {
- refreshUi();
- }
-
- private final UiCallback<Avatar> mAvatarFetchCallback = new UiCallback<Avatar>() {
-
- @Override
- public void userInputRequried(final PendingIntent pi, final Avatar avatar) {
- finishInitialSetup(avatar);
- }
-
- @Override
- public void success(final Avatar avatar) {
- finishInitialSetup(avatar);
- }
-
- @Override
- public void error(final int errorCode, final Avatar avatar) {
- finishInitialSetup(avatar);
- }
- };
- private final TextWatcher mTextWatcher = new TextWatcher() {
-
- @Override
- public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
- updateSaveButton();
- }
-
- @Override
- public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
- }
-
- @Override
- public void afterTextChanged(final Editable s) {
-
- }
- };
-
- private final OnClickListener mAvatarClickListener = new OnClickListener() {
- @Override
- public void onClick(final View view) {
- if (mAccount != null) {
- final Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class);
- intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toBareJid().toString());
- startActivity(intent);
- }
- }
- };
-
- protected void finishInitialSetup(final Avatar avatar) {
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- final Intent intent;
- final XmppConnection connection = mAccount.getXmppConnection();
- final boolean wasFirstAccount = xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1;
- if (avatar != null || (connection != null && !connection.getFeatures().pep())) {
- intent = new Intent(getApplicationContext(), StartConversationActivity.class);
- if (wasFirstAccount) {
- intent.putExtra("init", true);
- }
- } else {
- intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class);
- intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toBareJid().toString());
- intent.putExtra("setup", true);
- }
- if (wasFirstAccount) {
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- }
- startActivity(intent);
- finish();
- }
- });
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == REQUEST_BATTERY_OP || requestCode == REQUEST_DATA_SAVER) {
- updateAccountInformation(mAccount == null);
- }
- }
-
- protected void updateSaveButton() {
- boolean accountInfoEdited = accountInfoEdited();
-
- if (!mInitMode && passwordChangedInMagicCreateMode()) {
- this.mSaveButton.setText(R.string.change_password);
- this.mSaveButton.setEnabled(true);
- this.mSaveButton.setTextColor(getPrimaryTextColor());
- } else if (accountInfoEdited && !mInitMode) {
- this.mSaveButton.setText(R.string.save);
- this.mSaveButton.setEnabled(true);
- this.mSaveButton.setTextColor(getPrimaryTextColor());
- } 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);
- } else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !mInitMode) {
- this.mSaveButton.setEnabled(true);
- this.mSaveButton.setTextColor(getPrimaryTextColor());
- this.mSaveButton.setText(R.string.enable);
- } else {
- this.mSaveButton.setEnabled(true);
- this.mSaveButton.setTextColor(getPrimaryTextColor());
- if (!mInitMode) {
- if (mAccount != null && mAccount.isOnlineAndConnected()) {
- this.mSaveButton.setText(R.string.save);
- if (!accountInfoEdited) {
- this.mSaveButton.setEnabled(false);
- this.mSaveButton.setTextColor(getSecondaryTextColor());
- }
- } else {
- this.mSaveButton.setText(R.string.connect);
- }
- } else {
- this.mSaveButton.setText(R.string.next);
- }
- }
- }
-
- protected boolean accountInfoEdited() {
- if (this.mAccount == null) {
- return false;
- }
- return jidEdited() ||
- !this.mAccount.getPassword().equals(this.mPassword.getText().toString()) ||
- !this.mAccount.getHostname().equals(this.mHostname.getText().toString()) ||
- !String.valueOf(this.mAccount.getPort()).equals(this.mPort.getText().toString());
- }
-
- protected boolean jidEdited() {
- final String unmodified;
- if (mUsernameMode) {
- unmodified = this.mAccount.getJid().getLocalpart();
- } else {
- unmodified = this.mAccount.getJid().toBareJid().toString();
- }
- return !unmodified.equals(this.mAccountJid.getText().toString());
- }
-
- protected boolean passwordChangedInMagicCreateMode() {
- return mAccount != null
- && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
- && !this.mAccount.getPassword().equals(this.mPassword.getText().toString())
- && !this.jidEdited()
- && mAccount.isOnlineAndConnected();
- }
-
- @Override
- protected String getShareableUri() {
- if (mAccount != null) {
- return mAccount.getShareableUri();
- } else {
- return "";
- }
- }
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (savedInstanceState != null) {
- this.mSavedInstanceAccount = savedInstanceState.getString("account");
- this.mSavedInstanceInit = savedInstanceState.getBoolean("initMode", false);
- }
- setContentView(R.layout.activity_edit_account);
- this.mAccountJid = (AutoCompleteTextView) findViewById(R.id.account_jid);
- this.mAccountJid.addTextChangedListener(this.mTextWatcher);
- this.mAccountJidLabel = (TextView) findViewById(R.id.account_jid_label);
- this.mPassword = (EditText) findViewById(R.id.account_password);
- this.mPassword.addTextChangedListener(this.mTextWatcher);
- this.mPasswordConfirm = (EditText) findViewById(R.id.account_password_confirm);
- this.mAvatar = (ImageView) findViewById(R.id.avater);
- this.mAvatar.setOnClickListener(this.mAvatarClickListener);
- this.mRegisterNew = (CheckBox) findViewById(R.id.account_register_new);
- this.mStats = (LinearLayout) findViewById(R.id.stats);
- this.mOsOptimizations = (RelativeLayout) findViewById(R.id.os_optimization);
- this.mDisableOsOptimizationsButton = (Button) findViewById(R.id.os_optimization_disable);
- this.mDisableOsOptimizationsHeadline = (TextView) findViewById(R.id.os_optimization_headline);
- this.getmDisableOsOptimizationsBody = (TextView) findViewById(R.id.os_optimization_body);
- this.mSessionEst = (TextView) findViewById(R.id.session_est);
- this.mServerInfoRosterVersion = (TextView) findViewById(R.id.server_info_roster_version);
- this.mServerInfoCarbons = (TextView) findViewById(R.id.server_info_carbons);
- this.mServerInfoMam = (TextView) findViewById(R.id.server_info_mam);
- this.mServerInfoCSI = (TextView) findViewById(R.id.server_info_csi);
- this.mServerInfoBlocking = (TextView) findViewById(R.id.server_info_blocking);
- this.mServerInfoSm = (TextView) findViewById(R.id.server_info_sm);
- this.mServerInfoPep = (TextView) findViewById(R.id.server_info_pep);
- this.mServerInfoHttpUpload = (TextView) findViewById(R.id.server_info_http_upload);
- this.mPushRow = (TableRow) findViewById(R.id.push_row);
- this.mServerInfoPush = (TextView) findViewById(R.id.server_info_push);
- this.mOtrFingerprint = (TextView) findViewById(R.id.otr_fingerprint);
- this.mOtrFingerprintBox = (RelativeLayout) findViewById(R.id.otr_fingerprint_box);
- this.mOtrFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_to_clipboard);
- this.mAxolotlFingerprint = (TextView) findViewById(R.id.axolotl_fingerprint);
- this.mAxolotlFingerprintBox = (RelativeLayout) findViewById(R.id.axolotl_fingerprint_box);
- this.mAxolotlFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_axolotl_to_clipboard);
- this.mRegenerateAxolotlKeyButton = (ImageButton) findViewById(R.id.action_regenerate_omemo_key);
- this.mOwnFingerprintDesc = (TextView) findViewById(R.id.own_fingerprint_desc);
- this.keysCard = (LinearLayout) findViewById(R.id.other_device_keys_card);
- this.keys = (LinearLayout) findViewById(R.id.other_device_keys);
- this.mNamePort = (LinearLayout) findViewById(R.id.name_port);
- this.mHostname = (EditText) findViewById(R.id.hostname);
- this.mHostname.addTextChangedListener(mTextWatcher);
- this.mPort = (EditText) findViewById(R.id.port);
- this.mPort.setText("5222");
- this.mPort.addTextChangedListener(mTextWatcher);
- this.mSaveButton = (Button) findViewById(R.id.save_button);
- this.mCancelButton = (Button) findViewById(R.id.cancel_button);
- this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener);
- this.mCancelButton.setOnClickListener(this.mCancelButtonClickListener);
- this.mMoreTable = (TableLayout) findViewById(R.id.server_info_more);
- if (savedInstanceState != null && savedInstanceState.getBoolean("showMoreTable")) {
- changeMoreTableVisibility(true);
- }
- final OnCheckedChangeListener OnCheckedShowConfirmPassword = new OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(final CompoundButton buttonView,
- final boolean isChecked) {
- if (isChecked) {
- mPasswordConfirm.setVisibility(View.VISIBLE);
- } else {
- mPasswordConfirm.setVisibility(View.GONE);
- }
- updateSaveButton();
- }
- };
- this.mRegisterNew.setOnCheckedChangeListener(OnCheckedShowConfirmPassword);
- if (Config.DISALLOW_REGISTRATION_IN_UI) {
- this.mRegisterNew.setVisibility(View.GONE);
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(final Menu menu) {
- super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.editaccount, menu);
- final MenuItem showQrCode = menu.findItem(R.id.action_show_qr_code);
- final MenuItem showBlocklist = menu.findItem(R.id.action_show_block_list);
- final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
- final MenuItem changePassword = menu.findItem(R.id.action_change_password_on_server);
- final MenuItem showPassword = menu.findItem(R.id.action_show_password);
- final MenuItem clearDevices = menu.findItem(R.id.action_clear_devices);
- final MenuItem renewCertificate = menu.findItem(R.id.action_renew_certificate);
- final MenuItem mamPrefs = menu.findItem(R.id.action_mam_prefs);
- final MenuItem changePresence = menu.findItem(R.id.action_change_presence);
- renewCertificate.setVisible(mAccount != null && mAccount.getPrivateKeyAlias() != null);
-
- if (mAccount != null && mAccount.isOnlineAndConnected()) {
- if (!mAccount.getXmppConnection().getFeatures().blocking()) {
- showBlocklist.setVisible(false);
- }
- if (!mAccount.getXmppConnection().getFeatures().register()) {
- changePassword.setVisible(false);
- }
- mamPrefs.setVisible(mAccount.getXmppConnection().getFeatures().mam());
- Set<Integer> otherDevices = mAccount.getAxolotlService().getOwnDeviceIds();
- if (otherDevices == null || otherDevices.isEmpty() || !Config.supportOmemo()) {
- clearDevices.setVisible(false);
- }
- changePresence.setVisible(manuallyChangePresence());
- } else {
- showQrCode.setVisible(false);
- showBlocklist.setVisible(false);
- showMoreInfo.setVisible(false);
- changePassword.setVisible(false);
- clearDevices.setVisible(false);
- mamPrefs.setVisible(false);
- changePresence.setVisible(false);
- }
-
- if (mAccount != null) {
- showPassword.setVisible(mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
- && !mAccount.isOptionSet(Account.OPTION_REGISTER));
- } else {
- showPassword.setVisible(false);
- }
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
- if (showMoreInfo.isVisible()) {
- showMoreInfo.setChecked(mMoreTable.getVisibility() == View.VISIBLE);
- }
- return super.onPrepareOptionsMenu(menu);
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- final int theme = findTheme();
- if (this.mTheme != theme) {
- recreate();
- } else if (getIntent() != null) {
- try {
- this.jidToEdit = Jid.fromString(getIntent().getStringExtra("jid"));
- } catch (final InvalidJidException | NullPointerException ignored) {
- this.jidToEdit = null;
- }
- boolean init = getIntent().getBooleanExtra("init", false);
- this.mInitMode = init || this.jidToEdit == null;
- this.messageFingerprint = getIntent().getStringExtra("fingerprint");
- if (!mInitMode) {
- this.mRegisterNew.setVisibility(View.GONE);
- if (getActionBar() != null) {
- getActionBar().setTitle(getString(R.string.account_details));
- }
- } else {
- this.mAvatar.setVisibility(View.GONE);
- ActionBar ab = getActionBar();
- if (ab != null) {
- if (init && Config.MAGIC_CREATE_DOMAIN == null) {
- ab.setDisplayShowHomeEnabled(false);
- ab.setDisplayHomeAsUpEnabled(false);
- }
- ab.setTitle(R.string.action_add_account);
- }
- }
- }
- 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.mNamePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE);
- }
-
- @Override
- public void onSaveInstanceState(final Bundle savedInstanceState) {
- if (mAccount != null) {
- savedInstanceState.putString("account", mAccount.getJid().toBareJid().toString());
- savedInstanceState.putBoolean("initMode", mInitMode);
- savedInstanceState.putBoolean("showMoreTable", mMoreTable.getVisibility() == View.VISIBLE);
- }
- super.onSaveInstanceState(savedInstanceState);
- }
-
- protected void onBackendConnected() {
- boolean init = true;
- if (mSavedInstanceAccount != null) {
- try {
- this.mAccount = xmppConnectionService.findAccountByJid(Jid.fromString(mSavedInstanceAccount));
- this.mInitMode = mSavedInstanceInit;
- init = false;
- } catch (InvalidJidException e) {
- this.mAccount = null;
- }
-
- } else if (this.jidToEdit != null) {
- this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
- }
-
- if (mAccount != null) {
- 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);
- if (this.mInitMode) {
- this.mPassword.requestFocus();
- }
- }
- updateAccountInformation(init);
- }
-
-
- if (Config.MAGIC_CREATE_DOMAIN == null && this.xmppConnectionService.getAccounts().size() == 0) {
- this.mCancelButton.setEnabled(false);
- this.mCancelButton.setTextColor(getSecondaryTextColor());
- }
- if (mUsernameMode) {
- this.mAccountJidLabel.setText(R.string.username);
- this.mAccountJid.setHint(R.string.username_hint);
- } else {
- final KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this,
- R.layout.simple_list_item,
- xmppConnectionService.getKnownHosts());
- this.mAccountJid.setAdapter(mKnownHostsAdapter);
- }
- updateSaveButton();
- invalidateOptionsMenu();
- }
-
- private String getUserModeDomain() {
- if (mAccount != null) {
- return mAccount.getJid().getDomainpart();
- } else {
- return Config.DOMAIN_LOCK;
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- switch (item.getItemId()) {
- case R.id.mgmt_account_reconnect:
- if (xmppConnectionServiceBound) {
- unbindService(mConnection);
- xmppConnectionServiceBound = false;
- }
- stopService(new Intent(EditAccountActivity.this,
- XmppConnectionService.class));
- finish();
- break;
- case R.id.action_show_block_list:
- final Intent showBlocklistIntent = new Intent(this, BlocklistActivity.class);
- showBlocklistIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
- startActivity(showBlocklistIntent);
- break;
- case R.id.action_server_info_show_more:
- changeMoreTableVisibility(!item.isChecked());
- break;
- case R.id.action_change_password_on_server:
- gotoChangePassword(null);
- break;
- case R.id.action_mam_prefs:
- editMamPrefs();
- break;
- case R.id.action_clear_devices:
- showWipePepDialog();
- break;
- case R.id.action_renew_certificate:
- renewCertificate();
- break;
- case R.id.action_change_presence:
- changePresence();
- break;
- case R.id.action_show_password:
- showPassword();
- break;
- }
- return super.onOptionsItemSelected(item);
- }
-
- private void changeMoreTableVisibility(boolean visible) {
- mMoreTable.setVisibility(visible ? View.VISIBLE : View.GONE);
- }
-
- private void gotoChangePassword(String newPassword) {
- final Intent changePasswordIntent = new Intent(this, ChangePasswordActivity.class);
- changePasswordIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
- if (newPassword != null) {
- changePasswordIntent.putExtra("password", newPassword);
- }
- startActivity(changePasswordIntent);
- }
-
- private void renewCertificate() {
- KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
- }
-
- private void changePresence() {
- Intent intent = new Intent(this, SetPresenceActivity.class);
- intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT,mAccount.getJid().toBareJid().toString());
- startActivity(intent);
- }
-
- @Override
- public void alias(String alias) {
- if (alias != null) {
- xmppConnectionService.updateKeyInAccount(mAccount, alias);
- }
- }
-
- private void updateAccountInformation(boolean init) {
- if (init) {
- this.mAccountJid.getEditableText().clear();
- if (mUsernameMode) {
- this.mAccountJid.getEditableText().append(this.mAccount.getJid().getLocalpart());
- } else {
- this.mAccountJid.getEditableText().append(this.mAccount.getJid().toBareJid().toString());
- }
- this.mPassword.setText(this.mAccount.getPassword());
- this.mHostname.setText("");
- this.mHostname.getEditableText().append(this.mAccount.getHostname());
- this.mPort.setText("");
- this.mPort.getEditableText().append(String.valueOf(this.mAccount.getPort()));
- this.mNamePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE);
-
- }
-
- if (!mInitMode) {
- this.mAvatar.setVisibility(View.VISIBLE);
- this.mAvatar.setImageBitmap(avatarService().get(this.mAccount, getPixel(Config.AVATAR_SIZE)));
- this.mAccountJid.setEnabled(false);
- } else {
- this.mAvatar.setVisibility(View.GONE);
- }
- if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) {
- this.mRegisterNew.setVisibility(View.VISIBLE);
- this.mRegisterNew.setChecked(true);
- this.mPasswordConfirm.setText(this.mAccount.getPassword());
- } else {
- this.mRegisterNew.setVisibility(View.GONE);
- this.mRegisterNew.setChecked(false);
- }
- if (this.mAccount.isOnlineAndConnected() && !this.mFetchingAvatar) {
- Features features = this.mAccount.getXmppConnection().getFeatures();
- this.mStats.setVisibility(View.VISIBLE);
- boolean showBatteryWarning = !xmppConnectionService.getPushManagementService().available(mAccount) && isOptimizingBattery();
- boolean showDataSaverWarning = isAffectedByDataSaver();
- showOsOptimizationWarning(showBatteryWarning,showDataSaverWarning);
- this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull(this, this.mAccount.getXmppConnection()
- .getLastSessionEstablished()));
- if (features.rosterVersioning()) {
- this.mServerInfoRosterVersion.setText(R.string.server_info_available);
- } else {
- this.mServerInfoRosterVersion.setText(R.string.server_info_unavailable);
- }
- if (features.carbons()) {
- this.mServerInfoCarbons.setText(R.string.server_info_available);
- } else {
- this.mServerInfoCarbons
- .setText(R.string.server_info_unavailable);
- }
- if (features.mam()) {
- this.mServerInfoMam.setText(R.string.server_info_available);
- } else {
- this.mServerInfoMam.setText(R.string.server_info_unavailable);
- }
- if (features.csi()) {
- this.mServerInfoCSI.setText(R.string.server_info_available);
- } else {
- this.mServerInfoCSI.setText(R.string.server_info_unavailable);
- }
- if (features.blocking()) {
- this.mServerInfoBlocking.setText(R.string.server_info_available);
- } else {
- this.mServerInfoBlocking.setText(R.string.server_info_unavailable);
- }
- if (features.sm()) {
- this.mServerInfoSm.setText(R.string.server_info_available);
- } else {
- this.mServerInfoSm.setText(R.string.server_info_unavailable);
- }
- if (features.pep()) {
- AxolotlService axolotlService = this.mAccount.getAxolotlService();
- if (axolotlService != null && axolotlService.isPepBroken()) {
- this.mServerInfoPep.setText(R.string.server_info_broken);
- } else {
- this.mServerInfoPep.setText(R.string.server_info_available);
- }
- } else {
- this.mServerInfoPep.setText(R.string.server_info_unavailable);
- }
- if (features.httpUpload(0)) {
- this.mServerInfoHttpUpload.setText(R.string.server_info_available);
- } else {
- this.mServerInfoHttpUpload.setText(R.string.server_info_unavailable);
- }
-
- this.mPushRow.setVisibility(xmppConnectionService.getPushManagementService().isStub() ? View.GONE : View.VISIBLE);
-
- if (xmppConnectionService.getPushManagementService().available(mAccount)) {
- this.mServerInfoPush.setText(R.string.server_info_available);
- } else {
- this.mServerInfoPush.setText(R.string.server_info_unavailable);
- }
- final String otrFingerprint = this.mAccount.getOtrFingerprint();
- if (otrFingerprint != null && Config.supportOtr()) {
- this.mOtrFingerprintBox.setVisibility(View.VISIBLE);
- this.mOtrFingerprint.setText(CryptoHelper.prettifyFingerprint(otrFingerprint));
- this.mOtrFingerprintToClipboardButton
- .setVisibility(View.VISIBLE);
- this.mOtrFingerprintToClipboardButton
- .setOnClickListener(new View.OnClickListener() {
-
- @Override
- public void onClick(final View v) {
-
- if (copyTextToClipboard(otrFingerprint, R.string.otr_fingerprint)) {
- Toast.makeText(
- EditAccountActivity.this,
- R.string.toast_message_otr_fingerprint,
- Toast.LENGTH_SHORT).show();
- }
- }
- });
- } else {
- this.mOtrFingerprintBox.setVisibility(View.GONE);
- }
- final String ownAxolotlFingerprint = this.mAccount.getAxolotlService().getOwnFingerprint();
- if (ownAxolotlFingerprint != null && Config.supportOmemo()) {
- this.mAxolotlFingerprintBox.setVisibility(View.VISIBLE);
- if (ownAxolotlFingerprint.equals(messageFingerprint)) {
- this.mOwnFingerprintDesc.setTextColor(getResources().getColor(R.color.accent));
- } else {
- this.mOwnFingerprintDesc.setTextColor(getSecondaryTextColor());
- }
- this.mAxolotlFingerprint.setText(CryptoHelper.prettifyFingerprint(ownAxolotlFingerprint.substring(2)));
- this.mAxolotlFingerprintToClipboardButton
- .setVisibility(View.VISIBLE);
- this.mAxolotlFingerprintToClipboardButton
- .setOnClickListener(new View.OnClickListener() {
-
- @Override
- public void onClick(final View v) {
- copyOmemoFingerprint(ownAxolotlFingerprint);
- }
- });
- if (Config.SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON) {
- this.mRegenerateAxolotlKeyButton
- .setVisibility(View.VISIBLE);
- this.mRegenerateAxolotlKeyButton
- .setOnClickListener(new View.OnClickListener() {
-
- @Override
- public void onClick(final View v) {
- showRegenerateAxolotlKeyDialog();
- }
- });
- }
- } else {
- this.mAxolotlFingerprintBox.setVisibility(View.GONE);
- }
- boolean hasKeys = false;
- keys.removeAllViews();
- for (final String fingerprint : mAccount.getAxolotlService().getFingerprintsForOwnSessions()) {
- if (ownAxolotlFingerprint.equals(fingerprint)) {
- continue;
- }
- boolean highlight = fingerprint.equals(messageFingerprint);
- hasKeys |= addFingerprintRow(keys, mAccount, fingerprint, highlight);
- }
- if (hasKeys && Config.supportOmemo()) {
- keysCard.setVisibility(View.VISIBLE);
- } else {
- keysCard.setVisibility(View.GONE);
- }
- } else {
- if (this.mAccount.errorStatus()) {
- final EditText errorTextField;
- if (this.mAccount.getStatus() == Account.State.UNAUTHORIZED) {
- errorTextField = this.mPassword;
- } else if (mShowOptions
- && this.mAccount.getStatus() == Account.State.SERVER_NOT_FOUND
- && this.mHostname.getText().length() > 0) {
- errorTextField = this.mHostname;
- } else {
- errorTextField = this.mAccountJid;
- }
- errorTextField.setError(getString(this.mAccount.getStatus().getReadableId()));
- if (init || !accountInfoEdited()) {
- errorTextField.requestFocus();
- }
- } else {
- this.mAccountJid.setError(null);
- this.mPassword.setError(null);
- this.mHostname.setError(null);
- }
- this.mStats.setVisibility(View.GONE);
- }
- }
-
- private void showOsOptimizationWarning(boolean showBatteryWarning, boolean showDataSaverWarning) {
- this.mOsOptimizations.setVisibility(showBatteryWarning || showDataSaverWarning ? View.VISIBLE : View.GONE);
- if (showDataSaverWarning) {
- this.mDisableOsOptimizationsHeadline.setText(R.string.data_saver_enabled);
- this.getmDisableOsOptimizationsBody.setText(R.string.data_saver_enabled_explained);
- this.mDisableOsOptimizationsButton.setText(R.string.allow);
- this.mDisableOsOptimizationsButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(Settings.ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS);
- Uri uri = Uri.parse("package:" + getPackageName());
- intent.setData(uri);
- try {
- startActivityForResult(intent, REQUEST_DATA_SAVER);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(EditAccountActivity.this, R.string.device_does_not_support_data_saver, Toast.LENGTH_SHORT).show();
- }
- }
- });
- } else if (showBatteryWarning) {
- this.mDisableOsOptimizationsButton.setText(R.string.disable);
- this.mDisableOsOptimizationsHeadline.setText(R.string.battery_optimizations_enabled);
- this.getmDisableOsOptimizationsBody.setText(R.string.battery_optimizations_enabled_explained);
- this.mDisableOsOptimizationsButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
- Uri uri = Uri.parse("package:" + getPackageName());
- intent.setData(uri);
- try {
- startActivityForResult(intent, REQUEST_BATTERY_OP);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(EditAccountActivity.this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
- }
- }
- });
- }
- }
-
-
- public void showRegenerateAxolotlKeyDialog() {
- Builder builder = new Builder(this);
- builder.setTitle("Regenerate Key");
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- builder.setMessage("Are you sure you want to regenerate your Identity Key? (This will also wipe all established sessions and contact Identity Keys)");
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.setPositiveButton("Yes",
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- mAccount.getAxolotlService().regenerateKeys(false);
- }
- });
- builder.create().show();
- }
-
- public void showWipePepDialog() {
- Builder builder = new Builder(this);
- builder.setTitle(getString(R.string.clear_other_devices));
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- builder.setMessage(getString(R.string.clear_other_devices_desc));
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.setPositiveButton(getString(R.string.accept),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- mAccount.getAxolotlService().wipeOtherPepDevices();
- }
- });
- builder.create().show();
- }
-
- private void editMamPrefs() {
- this.mFetchingMamPrefsToast = Toast.makeText(this, R.string.fetching_mam_prefs, Toast.LENGTH_LONG);
- this.mFetchingMamPrefsToast.show();
- xmppConnectionService.fetchMamPreferences(mAccount, this);
- }
-
- private void showPassword() {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- View view = getLayoutInflater().inflate(R.layout.dialog_show_password, null);
- TextView password = (TextView) view.findViewById(R.id.password);
- password.setText(mAccount.getPassword());
- builder.setTitle(R.string.password);
- builder.setView(view);
- builder.setPositiveButton(R.string.cancel, null);
- builder.create().show();
- }
-
- @Override
- public void onKeyStatusUpdated(AxolotlService.FetchStatus report) {
- refreshUi();
- }
-
- @Override
- public void onCaptchaRequested(final Account account, final String id, final Data data, final Bitmap captcha) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if ((mCaptchaDialog != null) && mCaptchaDialog.isShowing()) {
- mCaptchaDialog.dismiss();
- }
- final AlertDialog.Builder builder = new AlertDialog.Builder(EditAccountActivity.this);
- final View view = getLayoutInflater().inflate(R.layout.captcha, null);
- final ImageView imageView = (ImageView) view.findViewById(R.id.captcha);
- final EditText input = (EditText) view.findViewById(R.id.input);
- imageView.setImageBitmap(captcha);
-
- builder.setTitle(getString(R.string.captcha_required));
- builder.setView(view);
-
- builder.setPositiveButton(getString(R.string.ok),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- String rc = input.getText().toString();
- data.put("username", account.getUsername());
- data.put("password", account.getPassword());
- data.put("ocr", rc);
- data.submit();
-
- if (xmppConnectionServiceBound) {
- xmppConnectionService.sendCreateAccountWithCaptchaPacket(
- account, id, data);
- }
- }
- });
- builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (xmppConnectionService != null) {
- xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
- }
- }
- });
-
- builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- if (xmppConnectionService != null) {
- xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
- }
- }
- });
- mCaptchaDialog = builder.create();
- mCaptchaDialog.show();
- }
- });
- }
-
- public void onShowErrorToast(final int resId) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(EditAccountActivity.this, resId, Toast.LENGTH_SHORT).show();
- }
- });
- }
-
- @Override
- public void onPreferencesFetched(final Element prefs) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (mFetchingMamPrefsToast != null) {
- mFetchingMamPrefsToast.cancel();
- }
- AlertDialog.Builder builder = new Builder(EditAccountActivity.this);
- builder.setTitle(R.string.server_side_mam_prefs);
- String defaultAttr = prefs.getAttribute("default");
- final List<String> defaults = Arrays.asList("never", "roster", "always");
- final AtomicInteger choice = new AtomicInteger(Math.max(0,defaults.indexOf(defaultAttr)));
- builder.setSingleChoiceItems(R.array.mam_prefs, choice.get(), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- choice.set(which);
- }
- });
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- prefs.setAttribute("default",defaults.get(choice.get()));
- xmppConnectionService.pushMamPreferences(mAccount, prefs);
- }
- });
- builder.create().show();
- }
- });
- }
-
- @Override
- public void onPreferencesFetchFailed() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (mFetchingMamPrefsToast != null) {
- mFetchingMamPrefsToast.cancel();
- }
- Toast.makeText(EditAccountActivity.this,R.string.unable_to_fetch_mam_prefs,Toast.LENGTH_LONG).show();
- }
- });
- }
+ OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnMamPreferencesFetched {
+
+ private static final int REQUEST_DATA_SAVER = 0x37af244;
+ private AutoCompleteTextView mAccountJid;
+ private EditText mPassword;
+ private EditText mPasswordConfirm;
+ private CheckBox mRegisterNew;
+ private Button mCancelButton;
+ private Button mSaveButton;
+ private Button mDisableOsOptimizationsButton;
+ private TextView mDisableOsOptimizationsHeadline;
+ private TextView getmDisableOsOptimizationsBody;
+ private TableLayout mMoreTable;
+
+ private LinearLayout mStats;
+ private RelativeLayout mOsOptimizations;
+ private TextView mServerInfoSm;
+ private TextView mServerInfoRosterVersion;
+ private TextView mServerInfoCarbons;
+ private TextView mServerInfoMam;
+ private TextView mServerInfoCSI;
+ private TextView mServerInfoBlocking;
+ private TextView mServerInfoPep;
+ private TextView mServerInfoHttpUpload;
+ private TextView mServerInfoPush;
+ private TextView mSessionEst;
+ private TextView mOtrFingerprint;
+ private TextView mAxolotlFingerprint;
+ private TextView mOwnFingerprintDesc;
+ private TextView mAccountJidLabel;
+ private ImageView mAvatar;
+ private RelativeLayout mOtrFingerprintBox;
+ private RelativeLayout mAxolotlFingerprintBox;
+ private ImageButton mOtrFingerprintToClipboardButton;
+ private ImageButton mAxolotlFingerprintToClipboardButton;
+ private ImageButton mRegenerateAxolotlKeyButton;
+ private LinearLayout keys;
+ private LinearLayout keysCard;
+ private LinearLayout mNamePort;
+ private EditText mHostname;
+ private EditText mPort;
+ private AlertDialog mCaptchaDialog = null;
+
+ private Jid jidToEdit;
+ private boolean mInitMode = false;
+ private boolean mUsernameMode = Config.DOMAIN_LOCK != null;
+ private boolean mShowOptions = false;
+ private Account mAccount;
+ private String messageFingerprint;
+
+ private boolean mFetchingAvatar = false;
+
+ private final OnClickListener mSaveButtonClickListener = new OnClickListener() {
+
+ @Override
+ public void onClick(final View v) {
+ final String password = mPassword.getText().toString();
+ final String passwordConfirm = mPasswordConfirm.getText().toString();
+
+ if (!mInitMode && passwordChangedInMagicCreateMode()) {
+ gotoChangePassword(password);
+ return;
+ }
+ if (mInitMode && mAccount != null) {
+ mAccount.setOption(Account.OPTION_DISABLED, false);
+ }
+ if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !accountInfoEdited()) {
+ mAccount.setOption(Account.OPTION_DISABLED, false);
+ if (!xmppConnectionService.updateAccount(mAccount)) {
+ Toast.makeText(EditAccountActivity.this, R.string.unable_to_update_account, Toast.LENGTH_SHORT).show();
+ }
+ return;
+ }
+ final boolean registerNewAccount = mRegisterNew.isChecked() && !Config.DISALLOW_REGISTRATION_IN_UI;
+ if (mUsernameMode && mAccountJid.getText().toString().contains("@")) {
+ mAccountJid.setError(getString(R.string.invalid_username));
+ mAccountJid.requestFocus();
+ return;
+ }
+ final Jid jid;
+ try {
+ if (mUsernameMode) {
+ jid = Jid.fromParts(mAccountJid.getText().toString(), getUserModeDomain(), null);
+ } else {
+ jid = Jid.fromString(mAccountJid.getText().toString());
+ }
+ } catch (final InvalidJidException e) {
+ if (mUsernameMode) {
+ mAccountJid.setError(getString(R.string.invalid_username));
+ } else {
+ mAccountJid.setError(getString(R.string.invalid_jid));
+ }
+ mAccountJid.requestFocus();
+ return;
+ }
+ String hostname = null;
+ int numericPort = 5222;
+ if (mShowOptions) {
+ hostname = mHostname.getText().toString().replaceAll("\\s", "");
+ final String port = mPort.getText().toString().replaceAll("\\s", "");
+ if (hostname.contains(" ")) {
+ mHostname.setError(getString(R.string.not_valid_hostname));
+ mHostname.requestFocus();
+ return;
+ }
+ try {
+ numericPort = Integer.parseInt(port);
+ if (numericPort < 0 || numericPort > 65535) {
+ mPort.setError(getString(R.string.not_a_valid_port));
+ mPort.requestFocus();
+ return;
+ }
+
+ } catch (NumberFormatException e) {
+ mPort.setError(getString(R.string.not_a_valid_port));
+ mPort.requestFocus();
+ return;
+ }
+ }
+
+ if (jid.isDomainJid()) {
+ if (mUsernameMode) {
+ mAccountJid.setError(getString(R.string.invalid_username));
+ } else {
+ mAccountJid.setError(getString(R.string.invalid_jid));
+ }
+ mAccountJid.requestFocus();
+ return;
+ }
+ if (registerNewAccount) {
+ if (XmppConnection.errorMessage != null) {
+ Toast.makeText(EditAccountActivity.this, XmppConnection.errorMessage, Toast.LENGTH_LONG).show();
+ }
+ if (!password.equals(passwordConfirm)) {
+ mPasswordConfirm.setError(getString(R.string.passwords_do_not_match));
+ mPasswordConfirm.requestFocus();
+ return;
+ }
+ }
+ if (mAccount != null) {
+ if (mInitMode && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
+ mAccount.setOption(Account.OPTION_MAGIC_CREATE, mAccount.getPassword().contains(password));
+ }
+ mAccount.setJid(jid);
+ mAccount.setPort(numericPort);
+ mAccount.setHostname(hostname);
+ if (XmppConnection.errorMessage != null) {
+ mAccountJid.setError(XmppConnection.errorMessage);
+ } else {
+ mAccountJid.setError(null);
+ }
+ mPasswordConfirm.setError(null);
+ mAccount.setPassword(password);
+ mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
+ if (!xmppConnectionService.updateAccount(mAccount)) {
+ Toast.makeText(EditAccountActivity.this, R.string.unable_to_update_account, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ } else {
+ if (xmppConnectionService.findAccountByJid(jid) != null) {
+ mAccountJid.setError(getString(R.string.account_already_exists));
+ mAccountJid.requestFocus();
+ return;
+ }
+ mAccount = new Account(jid.toBareJid(), password);
+ mAccount.setPort(numericPort);
+ mAccount.setHostname(hostname);
+ mAccount.setOption(Account.OPTION_USETLS, true);
+ mAccount.setOption(Account.OPTION_USECOMPRESSION, true);
+ mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
+ xmppConnectionService.createAccount(mAccount);
+ }
+ mHostname.setError(null);
+ mPort.setError(null);
+ if (!mAccount.isOptionSet(Account.OPTION_DISABLED)
+ && !registerNewAccount
+ && !mInitMode) {
+ finish();
+ } else {
+ updateSaveButton();
+ updateAccountInformation(true);
+ }
+
+ }
+ };
+ private final OnClickListener mCancelButtonClickListener = new OnClickListener() {
+
+ @Override
+ public void onClick(final View v) {
+ deleteMagicCreatedAccountAndReturnIfNecessary();
+ finish();
+ }
+ };
+ private Toast mFetchingMamPrefsToast;
+ private TableRow mPushRow;
+ private String mSavedInstanceAccount;
+ private boolean mSavedInstanceInit = false;
+
+ public void refreshUiReal() {
+ invalidateOptionsMenu();
+ if (mAccount != null
+ && mAccount.getStatus() != Account.State.ONLINE
+ && mFetchingAvatar) {
+ startActivity(new Intent(getApplicationContext(),
+ ManageAccountActivity.class));
+ finish();
+ } else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) {
+ if (!mFetchingAvatar) {
+ mFetchingAvatar = true;
+ xmppConnectionService.checkForAvatar(mAccount, mAvatarFetchCallback);
+ }
+ }
+ if (mAccount != null) {
+ updateAccountInformation(false);
+ }
+ updateSaveButton();
+ }
+
+ @Override
+ public boolean onNavigateUp() {
+ deleteMagicCreatedAccountAndReturnIfNecessary();
+ return super.onNavigateUp();
+ }
+
+ @Override
+ public void onBackPressed() {
+ deleteMagicCreatedAccountAndReturnIfNecessary();
+ super.onBackPressed();
+ }
+
+ private void deleteMagicCreatedAccountAndReturnIfNecessary() {
+ if (Config.MAGIC_CREATE_DOMAIN != null
+ && mAccount != null
+ && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
+ && mAccount.isOptionSet(Account.OPTION_REGISTER)
+ && xmppConnectionService.getAccounts().size() == 1) {
+ xmppConnectionService.deleteAccount(mAccount);
+ startActivity(new Intent(EditAccountActivity.this, WelcomeActivity.class));
+ }
+ }
+
+ @Override
+ public void onAccountUpdate() {
+ refreshUi();
+ }
+
+ private final UiCallback<Avatar> mAvatarFetchCallback = new UiCallback<Avatar>() {
+
+ @Override
+ public void userInputRequried(final PendingIntent pi, final Avatar avatar) {
+ finishInitialSetup(avatar);
+ }
+
+ @Override
+ public void success(final Avatar avatar) {
+ finishInitialSetup(avatar);
+ }
+
+ @Override
+ public void error(final int errorCode, final Avatar avatar) {
+ finishInitialSetup(avatar);
+ }
+ };
+ private final TextWatcher mTextWatcher = new TextWatcher() {
+
+ @Override
+ public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
+ updateSaveButton();
+ }
+
+ @Override
+ public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
+ }
+
+ @Override
+ public void afterTextChanged(final Editable s) {
+
+ }
+ };
+
+ private final OnClickListener mAvatarClickListener = new OnClickListener() {
+ @Override
+ public void onClick(final View view) {
+ if (mAccount != null) {
+ final Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class);
+ intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toBareJid().toString());
+ startActivity(intent);
+ }
+ }
+ };
+
+ protected void finishInitialSetup(final Avatar avatar) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ final Intent intent;
+ final XmppConnection connection = mAccount.getXmppConnection();
+ final boolean wasFirstAccount = xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1;
+ if (avatar != null || (connection != null && !connection.getFeatures().pep())) {
+ intent = new Intent(getApplicationContext(), StartConversationActivity.class);
+ if (wasFirstAccount) {
+ intent.putExtra("init", true);
+ }
+ } else {
+ intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class);
+ intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toBareJid().toString());
+ intent.putExtra("setup", true);
+ }
+ if (wasFirstAccount) {
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ }
+ startActivity(intent);
+ finish();
+ }
+ });
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == REQUEST_BATTERY_OP || requestCode == REQUEST_DATA_SAVER) {
+ updateAccountInformation(mAccount == null);
+ }
+ }
+
+ protected void updateSaveButton() {
+ boolean accountInfoEdited = accountInfoEdited();
+
+ if (!mInitMode && passwordChangedInMagicCreateMode()) {
+ this.mSaveButton.setText(R.string.change_password);
+ this.mSaveButton.setEnabled(true);
+ this.mSaveButton.setTextColor(getPrimaryTextColor());
+ } else if (accountInfoEdited && !mInitMode) {
+ this.mSaveButton.setText(R.string.save);
+ this.mSaveButton.setEnabled(true);
+ this.mSaveButton.setTextColor(getPrimaryTextColor());
+ } 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);
+ } else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !mInitMode) {
+ this.mSaveButton.setEnabled(true);
+ this.mSaveButton.setTextColor(getPrimaryTextColor());
+ this.mSaveButton.setText(R.string.enable);
+ } else {
+ this.mSaveButton.setEnabled(true);
+ this.mSaveButton.setTextColor(getPrimaryTextColor());
+ if (!mInitMode) {
+ if (mAccount != null && mAccount.isOnlineAndConnected()) {
+ this.mSaveButton.setText(R.string.save);
+ if (!accountInfoEdited) {
+ this.mSaveButton.setEnabled(false);
+ this.mSaveButton.setTextColor(getSecondaryTextColor());
+ }
+ } else {
+ this.mSaveButton.setText(R.string.connect);
+ }
+ } else {
+ this.mSaveButton.setText(R.string.next);
+ }
+ }
+ }
+
+ protected boolean accountInfoEdited() {
+ if (this.mAccount == null) {
+ return false;
+ }
+ return jidEdited() ||
+ !this.mAccount.getPassword().equals(this.mPassword.getText().toString()) ||
+ !this.mAccount.getHostname().equals(this.mHostname.getText().toString()) ||
+ !String.valueOf(this.mAccount.getPort()).equals(this.mPort.getText().toString());
+ }
+
+ protected boolean jidEdited() {
+ final String unmodified;
+ if (mUsernameMode) {
+ unmodified = this.mAccount.getJid().getLocalpart();
+ } else {
+ unmodified = this.mAccount.getJid().toBareJid().toString();
+ }
+ return !unmodified.equals(this.mAccountJid.getText().toString());
+ }
+
+ protected boolean passwordChangedInMagicCreateMode() {
+ return mAccount != null
+ && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
+ && !this.mAccount.getPassword().equals(this.mPassword.getText().toString())
+ && !this.jidEdited()
+ && mAccount.isOnlineAndConnected();
+ }
+
+ @Override
+ protected String getShareableUri() {
+ if (mAccount != null) {
+ return mAccount.getShareableUri();
+ } else {
+ return "";
+ }
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ this.mSavedInstanceAccount = savedInstanceState.getString("account");
+ this.mSavedInstanceInit = savedInstanceState.getBoolean("initMode", false);
+ }
+ setContentView(R.layout.activity_edit_account);
+ this.mAccountJid = (AutoCompleteTextView) findViewById(R.id.account_jid);
+ this.mAccountJid.addTextChangedListener(this.mTextWatcher);
+ this.mAccountJidLabel = (TextView) findViewById(R.id.account_jid_label);
+ this.mPassword = (EditText) findViewById(R.id.account_password);
+ this.mPassword.addTextChangedListener(this.mTextWatcher);
+ this.mPasswordConfirm = (EditText) findViewById(R.id.account_password_confirm);
+ this.mAvatar = (ImageView) findViewById(R.id.avater);
+ this.mAvatar.setOnClickListener(this.mAvatarClickListener);
+ this.mRegisterNew = (CheckBox) findViewById(R.id.account_register_new);
+ this.mStats = (LinearLayout) findViewById(R.id.stats);
+ this.mOsOptimizations = (RelativeLayout) findViewById(R.id.os_optimization);
+ this.mDisableOsOptimizationsButton = (Button) findViewById(R.id.os_optimization_disable);
+ this.mDisableOsOptimizationsHeadline = (TextView) findViewById(R.id.os_optimization_headline);
+ this.getmDisableOsOptimizationsBody = (TextView) findViewById(R.id.os_optimization_body);
+ this.mSessionEst = (TextView) findViewById(R.id.session_est);
+ this.mServerInfoRosterVersion = (TextView) findViewById(R.id.server_info_roster_version);
+ this.mServerInfoCarbons = (TextView) findViewById(R.id.server_info_carbons);
+ this.mServerInfoMam = (TextView) findViewById(R.id.server_info_mam);
+ this.mServerInfoCSI = (TextView) findViewById(R.id.server_info_csi);
+ this.mServerInfoBlocking = (TextView) findViewById(R.id.server_info_blocking);
+ this.mServerInfoSm = (TextView) findViewById(R.id.server_info_sm);
+ this.mServerInfoPep = (TextView) findViewById(R.id.server_info_pep);
+ this.mServerInfoHttpUpload = (TextView) findViewById(R.id.server_info_http_upload);
+ this.mPushRow = (TableRow) findViewById(R.id.push_row);
+ this.mServerInfoPush = (TextView) findViewById(R.id.server_info_push);
+ this.mOtrFingerprint = (TextView) findViewById(R.id.otr_fingerprint);
+ this.mOtrFingerprintBox = (RelativeLayout) findViewById(R.id.otr_fingerprint_box);
+ this.mOtrFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_to_clipboard);
+ this.mAxolotlFingerprint = (TextView) findViewById(R.id.axolotl_fingerprint);
+ this.mAxolotlFingerprintBox = (RelativeLayout) findViewById(R.id.axolotl_fingerprint_box);
+ this.mAxolotlFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_axolotl_to_clipboard);
+ this.mRegenerateAxolotlKeyButton = (ImageButton) findViewById(R.id.action_regenerate_omemo_key);
+ this.mOwnFingerprintDesc = (TextView) findViewById(R.id.own_fingerprint_desc);
+ this.keysCard = (LinearLayout) findViewById(R.id.other_device_keys_card);
+ this.keys = (LinearLayout) findViewById(R.id.other_device_keys);
+ this.mNamePort = (LinearLayout) findViewById(R.id.name_port);
+ this.mHostname = (EditText) findViewById(R.id.hostname);
+ this.mHostname.addTextChangedListener(mTextWatcher);
+ this.mPort = (EditText) findViewById(R.id.port);
+ this.mPort.setText("5222");
+ this.mPort.addTextChangedListener(mTextWatcher);
+ this.mSaveButton = (Button) findViewById(R.id.save_button);
+ this.mCancelButton = (Button) findViewById(R.id.cancel_button);
+ this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener);
+ this.mCancelButton.setOnClickListener(this.mCancelButtonClickListener);
+ this.mMoreTable = (TableLayout) findViewById(R.id.server_info_more);
+ if (savedInstanceState != null && savedInstanceState.getBoolean("showMoreTable")) {
+ changeMoreTableVisibility(true);
+ }
+ final OnCheckedChangeListener OnCheckedShowConfirmPassword = new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(final CompoundButton buttonView,
+ final boolean isChecked) {
+ if (isChecked) {
+ mPasswordConfirm.setVisibility(View.VISIBLE);
+ } else {
+ mPasswordConfirm.setVisibility(View.GONE);
+ }
+ updateSaveButton();
+ }
+ };
+ this.mRegisterNew.setOnCheckedChangeListener(OnCheckedShowConfirmPassword);
+ if (Config.DISALLOW_REGISTRATION_IN_UI) {
+ this.mRegisterNew.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getMenuInflater().inflate(R.menu.editaccount, menu);
+ final MenuItem showQrCode = menu.findItem(R.id.action_show_qr_code);
+ final MenuItem showBlocklist = menu.findItem(R.id.action_show_block_list);
+ final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
+ final MenuItem changePassword = menu.findItem(R.id.action_change_password_on_server);
+ final MenuItem showPassword = menu.findItem(R.id.action_show_password);
+ final MenuItem clearDevices = menu.findItem(R.id.action_clear_devices);
+ final MenuItem renewCertificate = menu.findItem(R.id.action_renew_certificate);
+ final MenuItem mamPrefs = menu.findItem(R.id.action_mam_prefs);
+ final MenuItem changePresence = menu.findItem(R.id.action_change_presence);
+ renewCertificate.setVisible(mAccount != null && mAccount.getPrivateKeyAlias() != null);
+
+ if (mAccount != null && mAccount.isOnlineAndConnected()) {
+ if (!mAccount.getXmppConnection().getFeatures().blocking()) {
+ showBlocklist.setVisible(false);
+ }
+ if (!mAccount.getXmppConnection().getFeatures().register()) {
+ changePassword.setVisible(false);
+ }
+ mamPrefs.setVisible(mAccount.getXmppConnection().getFeatures().mam());
+ Set<Integer> otherDevices = mAccount.getAxolotlService().getOwnDeviceIds();
+ if (otherDevices == null || otherDevices.isEmpty() || !Config.supportOmemo()) {
+ clearDevices.setVisible(false);
+ }
+ changePresence.setVisible(manuallyChangePresence());
+ } else {
+ showQrCode.setVisible(false);
+ showBlocklist.setVisible(false);
+ showMoreInfo.setVisible(false);
+ changePassword.setVisible(false);
+ clearDevices.setVisible(false);
+ mamPrefs.setVisible(false);
+ changePresence.setVisible(false);
+ }
+
+ if (mAccount != null) {
+ showPassword.setVisible(mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
+ && !mAccount.isOptionSet(Account.OPTION_REGISTER));
+ } else {
+ showPassword.setVisible(false);
+ }
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
+ if (showMoreInfo.isVisible()) {
+ showMoreInfo.setChecked(mMoreTable.getVisibility() == View.VISIBLE);
+ }
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ final int theme = findTheme();
+ if (this.mTheme != theme) {
+ recreate();
+ } else if (getIntent() != null) {
+ try {
+ this.jidToEdit = Jid.fromString(getIntent().getStringExtra("jid"));
+ } catch (final InvalidJidException | NullPointerException ignored) {
+ this.jidToEdit = null;
+ }
+ boolean init = getIntent().getBooleanExtra("init", false);
+ this.mInitMode = init || this.jidToEdit == null;
+ this.messageFingerprint = getIntent().getStringExtra("fingerprint");
+ if (!mInitMode) {
+ this.mRegisterNew.setVisibility(View.GONE);
+ if (getActionBar() != null) {
+ getActionBar().setTitle(getString(R.string.account_details));
+ }
+ } else {
+ this.mAvatar.setVisibility(View.GONE);
+ ActionBar ab = getActionBar();
+ if (ab != null) {
+ if (init && Config.MAGIC_CREATE_DOMAIN == null) {
+ ab.setDisplayShowHomeEnabled(false);
+ ab.setDisplayHomeAsUpEnabled(false);
+ }
+ ab.setTitle(R.string.action_add_account);
+ }
+ }
+ }
+ 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.mNamePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void onSaveInstanceState(final Bundle savedInstanceState) {
+ if (mAccount != null) {
+ savedInstanceState.putString("account", mAccount.getJid().toBareJid().toString());
+ savedInstanceState.putBoolean("initMode", mInitMode);
+ savedInstanceState.putBoolean("showMoreTable", mMoreTable.getVisibility() == View.VISIBLE);
+ }
+ super.onSaveInstanceState(savedInstanceState);
+ }
+
+ protected void onBackendConnected() {
+ boolean init = true;
+ if (mSavedInstanceAccount != null) {
+ try {
+ this.mAccount = xmppConnectionService.findAccountByJid(Jid.fromString(mSavedInstanceAccount));
+ this.mInitMode = mSavedInstanceInit;
+ init = false;
+ } catch (InvalidJidException e) {
+ this.mAccount = null;
+ }
+
+ } else if (this.jidToEdit != null) {
+ this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
+ }
+
+ if (mAccount != null) {
+ 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);
+ if (this.mInitMode) {
+ this.mPassword.requestFocus();
+ }
+ }
+ updateAccountInformation(init);
+ }
+
+
+ if (Config.MAGIC_CREATE_DOMAIN == null && this.xmppConnectionService.getAccounts().size() == 0) {
+ this.mCancelButton.setEnabled(false);
+ this.mCancelButton.setTextColor(getSecondaryTextColor());
+ }
+ if (mUsernameMode) {
+ this.mAccountJidLabel.setText(R.string.username);
+ this.mAccountJid.setHint(R.string.username_hint);
+ } else {
+ final KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this,
+ R.layout.simple_list_item,
+ xmppConnectionService.getKnownHosts());
+ this.mAccountJid.setAdapter(mKnownHostsAdapter);
+ }
+ updateSaveButton();
+ invalidateOptionsMenu();
+ }
+
+ private String getUserModeDomain() {
+ if (mAccount != null) {
+ return mAccount.getJid().getDomainpart();
+ } else {
+ return Config.DOMAIN_LOCK;
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.mgmt_account_reconnect:
+ if (xmppConnectionServiceBound) {
+ unbindService(mConnection);
+ xmppConnectionServiceBound = false;
+ }
+ stopService(new Intent(EditAccountActivity.this,
+ XmppConnectionService.class));
+ finish();
+ break;
+ case R.id.action_show_block_list:
+ final Intent showBlocklistIntent = new Intent(this, BlocklistActivity.class);
+ showBlocklistIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
+ startActivity(showBlocklistIntent);
+ break;
+ case R.id.action_server_info_show_more:
+ changeMoreTableVisibility(!item.isChecked());
+ break;
+ case R.id.action_change_password_on_server:
+ gotoChangePassword(null);
+ break;
+ case R.id.action_mam_prefs:
+ editMamPrefs();
+ break;
+ case R.id.action_clear_devices:
+ showWipePepDialog();
+ break;
+ case R.id.action_renew_certificate:
+ renewCertificate();
+ break;
+ case R.id.action_change_presence:
+ changePresence();
+ break;
+ case R.id.action_show_password:
+ showPassword();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void changeMoreTableVisibility(boolean visible) {
+ mMoreTable.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+
+ private void gotoChangePassword(String newPassword) {
+ final Intent changePasswordIntent = new Intent(this, ChangePasswordActivity.class);
+ changePasswordIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
+ if (newPassword != null) {
+ changePasswordIntent.putExtra("password", newPassword);
+ }
+ startActivity(changePasswordIntent);
+ }
+
+ private void renewCertificate() {
+ KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
+ }
+
+ private void changePresence() {
+ Intent intent = new Intent(this, SetPresenceActivity.class);
+ intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT, mAccount.getJid().toBareJid().toString());
+ startActivity(intent);
+ }
+
+ @Override
+ public void alias(String alias) {
+ if (alias != null) {
+ xmppConnectionService.updateKeyInAccount(mAccount, alias);
+ }
+ }
+
+ private void updateAccountInformation(boolean init) {
+ if (init) {
+ this.mAccountJid.getEditableText().clear();
+ if (mUsernameMode) {
+ this.mAccountJid.getEditableText().append(this.mAccount.getJid().getLocalpart());
+ } else {
+ this.mAccountJid.getEditableText().append(this.mAccount.getJid().toBareJid().toString());
+ }
+ this.mPassword.setText(this.mAccount.getPassword());
+ this.mHostname.setText("");
+ this.mHostname.getEditableText().append(this.mAccount.getHostname());
+ this.mPort.setText("");
+ this.mPort.getEditableText().append(String.valueOf(this.mAccount.getPort()));
+ this.mNamePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE);
+
+ }
+
+ if (!mInitMode) {
+ this.mAvatar.setVisibility(View.VISIBLE);
+ this.mAvatar.setImageBitmap(avatarService().get(this.mAccount, getPixel(Config.AVATAR_SIZE)));
+ this.mAccountJid.setEnabled(false);
+ } else {
+ this.mAvatar.setVisibility(View.GONE);
+ }
+ if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) {
+ this.mRegisterNew.setVisibility(View.VISIBLE);
+ this.mRegisterNew.setChecked(true);
+ this.mPasswordConfirm.setText(this.mAccount.getPassword());
+ } else {
+ this.mRegisterNew.setVisibility(View.GONE);
+ this.mRegisterNew.setChecked(false);
+ }
+ if (this.mAccount.isOnlineAndConnected() && !this.mFetchingAvatar) {
+ Features features = this.mAccount.getXmppConnection().getFeatures();
+ this.mStats.setVisibility(View.VISIBLE);
+ boolean showBatteryWarning = !xmppConnectionService.getPushManagementService().available(mAccount) && isOptimizingBattery();
+ boolean showDataSaverWarning = isAffectedByDataSaver();
+ showOsOptimizationWarning(showBatteryWarning, showDataSaverWarning);
+ this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull(this, this.mAccount.getXmppConnection()
+ .getLastSessionEstablished()));
+ if (features.rosterVersioning()) {
+ this.mServerInfoRosterVersion.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoRosterVersion.setText(R.string.server_info_unavailable);
+ }
+ if (features.carbons()) {
+ this.mServerInfoCarbons.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoCarbons
+ .setText(R.string.server_info_unavailable);
+ }
+ if (features.mam()) {
+ this.mServerInfoMam.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoMam.setText(R.string.server_info_unavailable);
+ }
+ if (features.csi()) {
+ this.mServerInfoCSI.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoCSI.setText(R.string.server_info_unavailable);
+ }
+ if (features.blocking()) {
+ this.mServerInfoBlocking.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoBlocking.setText(R.string.server_info_unavailable);
+ }
+ if (features.sm()) {
+ this.mServerInfoSm.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoSm.setText(R.string.server_info_unavailable);
+ }
+ if (features.pep()) {
+ AxolotlService axolotlService = this.mAccount.getAxolotlService();
+ if (axolotlService != null && axolotlService.isPepBroken()) {
+ this.mServerInfoPep.setText(R.string.server_info_broken);
+ } else {
+ this.mServerInfoPep.setText(R.string.server_info_available);
+ }
+ } else {
+ this.mServerInfoPep.setText(R.string.server_info_unavailable);
+ }
+ if (features.httpUpload(0)) {
+ this.mServerInfoHttpUpload.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoHttpUpload.setText(R.string.server_info_unavailable);
+ }
+
+ this.mPushRow.setVisibility(xmppConnectionService.getPushManagementService().isStub() ? View.GONE : View.VISIBLE);
+
+ if (xmppConnectionService.getPushManagementService().available(mAccount)) {
+ this.mServerInfoPush.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoPush.setText(R.string.server_info_unavailable);
+ }
+ final String otrFingerprint = this.mAccount.getOtrFingerprint();
+ if (otrFingerprint != null && Config.supportOtr()) {
+ this.mOtrFingerprintBox.setVisibility(View.VISIBLE);
+ this.mOtrFingerprint.setText(CryptoHelper.prettifyFingerprint(otrFingerprint));
+ this.mOtrFingerprintToClipboardButton
+ .setVisibility(View.VISIBLE);
+ this.mOtrFingerprintToClipboardButton
+ .setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(final View v) {
+
+ if (copyTextToClipboard(otrFingerprint, R.string.otr_fingerprint)) {
+ Toast.makeText(
+ EditAccountActivity.this,
+ R.string.toast_message_otr_fingerprint,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ } else {
+ this.mOtrFingerprintBox.setVisibility(View.GONE);
+ }
+ final String ownAxolotlFingerprint = this.mAccount.getAxolotlService().getOwnFingerprint();
+ if (ownAxolotlFingerprint != null && Config.supportOmemo()) {
+ this.mAxolotlFingerprintBox.setVisibility(View.VISIBLE);
+ if (ownAxolotlFingerprint.equals(messageFingerprint)) {
+ this.mOwnFingerprintDesc.setTextColor(getResources().getColor(R.color.accent));
+ } else {
+ this.mOwnFingerprintDesc.setTextColor(getSecondaryTextColor());
+ }
+ this.mAxolotlFingerprint.setText(CryptoHelper.prettifyFingerprint(ownAxolotlFingerprint.substring(2)));
+ this.mAxolotlFingerprintToClipboardButton
+ .setVisibility(View.VISIBLE);
+ this.mAxolotlFingerprintToClipboardButton
+ .setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(final View v) {
+ copyOmemoFingerprint(ownAxolotlFingerprint);
+ }
+ });
+ if (Config.SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON) {
+ this.mRegenerateAxolotlKeyButton
+ .setVisibility(View.VISIBLE);
+ this.mRegenerateAxolotlKeyButton
+ .setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(final View v) {
+ showRegenerateAxolotlKeyDialog();
+ }
+ });
+ }
+ } else {
+ this.mAxolotlFingerprintBox.setVisibility(View.GONE);
+ }
+ boolean hasKeys = false;
+ keys.removeAllViews();
+ for (final String fingerprint : mAccount.getAxolotlService().getFingerprintsForOwnSessions()) {
+ if (ownAxolotlFingerprint.equals(fingerprint)) {
+ continue;
+ }
+ boolean highlight = fingerprint.equals(messageFingerprint);
+ hasKeys |= addFingerprintRow(keys, mAccount, fingerprint, highlight);
+ }
+ if (hasKeys && Config.supportOmemo()) {
+ keysCard.setVisibility(View.VISIBLE);
+ } else {
+ keysCard.setVisibility(View.GONE);
+ }
+ } else {
+ if (this.mAccount.errorStatus()) {
+ final EditText errorTextField;
+ if (this.mAccount.getStatus() == Account.State.UNAUTHORIZED) {
+ errorTextField = this.mPassword;
+ } else if (mShowOptions
+ && this.mAccount.getStatus() == Account.State.SERVER_NOT_FOUND
+ && this.mHostname.getText().length() > 0) {
+ errorTextField = this.mHostname;
+ } else {
+ errorTextField = this.mAccountJid;
+ }
+ errorTextField.setError(getString(this.mAccount.getStatus().getReadableId()));
+ if (init || !accountInfoEdited()) {
+ errorTextField.requestFocus();
+ }
+ } else {
+ this.mAccountJid.setError(null);
+ this.mPassword.setError(null);
+ this.mHostname.setError(null);
+ }
+ this.mStats.setVisibility(View.GONE);
+ }
+ }
+
+ private void showOsOptimizationWarning(boolean showBatteryWarning, boolean showDataSaverWarning) {
+ this.mOsOptimizations.setVisibility(showBatteryWarning || showDataSaverWarning ? View.VISIBLE : View.GONE);
+ if (showDataSaverWarning) {
+ this.mDisableOsOptimizationsHeadline.setText(R.string.data_saver_enabled);
+ this.getmDisableOsOptimizationsBody.setText(R.string.data_saver_enabled_explained);
+ this.mDisableOsOptimizationsButton.setText(R.string.allow);
+ this.mDisableOsOptimizationsButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Settings.ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS);
+ Uri uri = Uri.parse("package:" + getPackageName());
+ intent.setData(uri);
+ try {
+ startActivityForResult(intent, REQUEST_DATA_SAVER);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(EditAccountActivity.this, R.string.device_does_not_support_data_saver, Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ } else if (showBatteryWarning) {
+ this.mDisableOsOptimizationsButton.setText(R.string.disable);
+ this.mDisableOsOptimizationsHeadline.setText(R.string.battery_optimizations_enabled);
+ this.getmDisableOsOptimizationsBody.setText(R.string.battery_optimizations_enabled_explained);
+ this.mDisableOsOptimizationsButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+ Uri uri = Uri.parse("package:" + getPackageName());
+ intent.setData(uri);
+ try {
+ startActivityForResult(intent, REQUEST_BATTERY_OP);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(EditAccountActivity.this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ }
+ }
+
+
+ public void showRegenerateAxolotlKeyDialog() {
+ Builder builder = new Builder(this);
+ builder.setTitle("Regenerate Key");
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setMessage("Are you sure you want to regenerate your Identity Key? (This will also wipe all established sessions and contact Identity Keys)");
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.setPositiveButton("Yes",
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mAccount.getAxolotlService().regenerateKeys(false);
+ }
+ });
+ builder.create().show();
+ }
+
+ public void showWipePepDialog() {
+ Builder builder = new Builder(this);
+ builder.setTitle(getString(R.string.clear_other_devices));
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setMessage(getString(R.string.clear_other_devices_desc));
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.setPositiveButton(getString(R.string.accept),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mAccount.getAxolotlService().wipeOtherPepDevices();
+ }
+ });
+ builder.create().show();
+ }
+
+ private void editMamPrefs() {
+ this.mFetchingMamPrefsToast = Toast.makeText(this, R.string.fetching_mam_prefs, Toast.LENGTH_LONG);
+ this.mFetchingMamPrefsToast.show();
+ xmppConnectionService.fetchMamPreferences(mAccount, this);
+ }
+
+ private void showPassword() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ View view = getLayoutInflater().inflate(R.layout.dialog_show_password, null);
+ TextView password = (TextView) view.findViewById(R.id.password);
+ password.setText(mAccount.getPassword());
+ builder.setTitle(R.string.password);
+ builder.setView(view);
+ builder.setPositiveButton(R.string.cancel, null);
+ builder.create().show();
+ }
+
+ @Override
+ public void onKeyStatusUpdated(AxolotlService.FetchStatus report) {
+ refreshUi();
+ }
+
+ @Override
+ public void onCaptchaRequested(final Account account, final String id, final Data data, final Bitmap captcha) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if ((mCaptchaDialog != null) && mCaptchaDialog.isShowing()) {
+ mCaptchaDialog.dismiss();
+ }
+ final AlertDialog.Builder builder = new AlertDialog.Builder(EditAccountActivity.this);
+ final View view = getLayoutInflater().inflate(R.layout.captcha, null);
+ final ImageView imageView = (ImageView) view.findViewById(R.id.captcha);
+ final EditText input = (EditText) view.findViewById(R.id.input);
+ imageView.setImageBitmap(captcha);
+
+ builder.setTitle(getString(R.string.captcha_required));
+ builder.setView(view);
+
+ builder.setPositiveButton(getString(R.string.ok),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String rc = input.getText().toString();
+ data.put("username", account.getUsername());
+ data.put("password", account.getPassword());
+ data.put("ocr", rc);
+ data.submit();
+
+ if (xmppConnectionServiceBound) {
+ xmppConnectionService.sendCreateAccountWithCaptchaPacket(
+ account, id, data);
+ }
+ }
+ });
+ builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (xmppConnectionService != null) {
+ xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
+ }
+ }
+ });
+
+ builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ if (xmppConnectionService != null) {
+ xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
+ }
+ }
+ });
+ mCaptchaDialog = builder.create();
+ mCaptchaDialog.show();
+ }
+ });
+ }
+
+ public void onShowErrorToast(final int resId) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(EditAccountActivity.this, resId, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ @Override
+ public void onPreferencesFetched(final Element prefs) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (mFetchingMamPrefsToast != null) {
+ mFetchingMamPrefsToast.cancel();
+ }
+ AlertDialog.Builder builder = new Builder(EditAccountActivity.this);
+ builder.setTitle(R.string.server_side_mam_prefs);
+ String defaultAttr = prefs.getAttribute("default");
+ final List<String> defaults = Arrays.asList("never", "roster", "always");
+ final AtomicInteger choice = new AtomicInteger(Math.max(0, defaults.indexOf(defaultAttr)));
+ builder.setSingleChoiceItems(R.array.mam_prefs, choice.get(), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ choice.set(which);
+ }
+ });
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ prefs.setAttribute("default", defaults.get(choice.get()));
+ xmppConnectionService.pushMamPreferences(mAccount, prefs);
+ }
+ });
+ builder.create().show();
+ }
+ });
+ }
+
+ @Override
+ public void onPreferencesFetchFailed() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (mFetchingMamPrefsToast != null) {
+ mFetchingMamPrefsToast.cancel();
+ }
+ Toast.makeText(EditAccountActivity.this, R.string.unable_to_fetch_mam_prefs, Toast.LENGTH_LONG).show();
+ }
+ });
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/EditMessage.java b/src/main/java/de/pixart/messenger/ui/EditMessage.java
index ea56e4d56..8c0a7f934 100644
--- a/src/main/java/de/pixart/messenger/ui/EditMessage.java
+++ b/src/main/java/de/pixart/messenger/ui/EditMessage.java
@@ -14,84 +14,89 @@ import github.ankushsachdeva.emojicon.EmojiconEditText;
public class EditMessage extends EmojiconEditText {
- public EditMessage(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public EditMessage(Context context) {
- super(context);
- }
-
- protected Handler mTypingHandler = new Handler();
-
- protected Runnable mTypingTimeout = new Runnable() {
- @Override
- public void run() {
- if (isUserTyping && keyboardListener != null) {
- keyboardListener.onTypingStopped();
- isUserTyping = false;
- }
- }
- };
-
- private boolean isUserTyping = false;
-
- private boolean lastInputWasTab = false;
-
- protected KeyboardListener keyboardListener;
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent e) {
- if (keyCode == KeyEvent.KEYCODE_ENTER && !e.isShiftPressed()) {
- lastInputWasTab = false;
- if (keyboardListener != null && keyboardListener.onEnterPressed()) {
- return true;
- }
- } else if (keyCode == KeyEvent.KEYCODE_TAB && !e.isAltPressed() && !e.isCtrlPressed()) {
- if (keyboardListener != null && keyboardListener.onTabPressed(this.lastInputWasTab)) {
- lastInputWasTab = true;
- return true;
- }
- } else {
- lastInputWasTab = false;
- }
- return super.onKeyDown(keyCode, e);
- }
-
- @Override
- public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
- super.onTextChanged(text,start,lengthBefore,lengthAfter);
- lastInputWasTab = false;
- if (this.mTypingHandler != null && this.keyboardListener != null) {
- this.mTypingHandler.removeCallbacks(mTypingTimeout);
- this.mTypingHandler.postDelayed(mTypingTimeout, Config.TYPING_TIMEOUT * 1000);
- final int length = text.length();
- if (!isUserTyping && length > 0) {
- this.isUserTyping = true;
- this.keyboardListener.onTypingStarted();
- } else if (length == 0) {
- this.isUserTyping = false;
- this.keyboardListener.onTextDeleted();
- }
- this.keyboardListener.onTextChanged();
- }
- }
-
- public void setKeyboardListener(KeyboardListener listener) {
- this.keyboardListener = listener;
- if (listener != null) {
- this.isUserTyping = false;
- }
- }
-
- public interface KeyboardListener {
- boolean onEnterPressed();
- void onTypingStarted();
- void onTypingStopped();
- void onTextDeleted();
- void onTextChanged();
- boolean onTabPressed(boolean repeated);
- }
+ public EditMessage(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public EditMessage(Context context) {
+ super(context);
+ }
+
+ protected Handler mTypingHandler = new Handler();
+
+ protected Runnable mTypingTimeout = new Runnable() {
+ @Override
+ public void run() {
+ if (isUserTyping && keyboardListener != null) {
+ keyboardListener.onTypingStopped();
+ isUserTyping = false;
+ }
+ }
+ };
+
+ private boolean isUserTyping = false;
+
+ private boolean lastInputWasTab = false;
+
+ protected KeyboardListener keyboardListener;
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent e) {
+ if (keyCode == KeyEvent.KEYCODE_ENTER && !e.isShiftPressed()) {
+ lastInputWasTab = false;
+ if (keyboardListener != null && keyboardListener.onEnterPressed()) {
+ return true;
+ }
+ } else if (keyCode == KeyEvent.KEYCODE_TAB && !e.isAltPressed() && !e.isCtrlPressed()) {
+ if (keyboardListener != null && keyboardListener.onTabPressed(this.lastInputWasTab)) {
+ lastInputWasTab = true;
+ return true;
+ }
+ } else {
+ lastInputWasTab = false;
+ }
+ return super.onKeyDown(keyCode, e);
+ }
+
+ @Override
+ public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
+ super.onTextChanged(text, start, lengthBefore, lengthAfter);
+ lastInputWasTab = false;
+ if (this.mTypingHandler != null && this.keyboardListener != null) {
+ this.mTypingHandler.removeCallbacks(mTypingTimeout);
+ this.mTypingHandler.postDelayed(mTypingTimeout, Config.TYPING_TIMEOUT * 1000);
+ final int length = text.length();
+ if (!isUserTyping && length > 0) {
+ this.isUserTyping = true;
+ this.keyboardListener.onTypingStarted();
+ } else if (length == 0) {
+ this.isUserTyping = false;
+ this.keyboardListener.onTextDeleted();
+ }
+ this.keyboardListener.onTextChanged();
+ }
+ }
+
+ public void setKeyboardListener(KeyboardListener listener) {
+ this.keyboardListener = listener;
+ if (listener != null) {
+ this.isUserTyping = false;
+ }
+ }
+
+ public interface KeyboardListener {
+ boolean onEnterPressed();
+
+ void onTypingStarted();
+
+ void onTypingStopped();
+
+ void onTextDeleted();
+
+ void onTextChanged();
+
+ boolean onTabPressed(boolean repeated);
+ }
private static final InputFilter SPAN_FILTER = new InputFilter() {
diff --git a/src/main/java/de/pixart/messenger/ui/EnterJidDialog.java b/src/main/java/de/pixart/messenger/ui/EnterJidDialog.java
index 8f355e348..b26019fac 100644
--- a/src/main/java/de/pixart/messenger/ui/EnterJidDialog.java
+++ b/src/main/java/de/pixart/messenger/ui/EnterJidDialog.java
@@ -19,111 +19,111 @@ import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
public class EnterJidDialog {
- public interface OnEnterJidDialogPositiveListener {
- boolean onEnterJidDialogPositive(Jid account, Jid contact) throws EnterJidDialog.JidError;
- }
-
- public static class JidError extends Exception {
- final String msg;
-
- public JidError(final String msg) {
- this.msg = msg;
- }
-
- public String toString() {
- return msg;
- }
- }
-
- protected final AlertDialog dialog;
- protected View.OnClickListener dialogOnClick;
- protected OnEnterJidDialogPositiveListener listener = null;
-
- public EnterJidDialog(
- final Context context, List<String> knownHosts, final List<String> activatedAccounts,
- final String title, final String positiveButton,
- final String prefilledJid, final String account, boolean allowEditJid
- ) {
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(title);
- View dialogView = LayoutInflater.from(context).inflate(R.layout.enter_jid_dialog, null);
- final TextView jabberIdDesc = (TextView) dialogView.findViewById(R.id.jabber_id);
- jabberIdDesc.setText(R.string.account_settings_jabber_id);
- final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
- final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView.findViewById(R.id.jid);
- jid.setAdapter(new KnownHostsAdapter(context, R.layout.simple_list_item, knownHosts));
- if (prefilledJid != null) {
- jid.append(prefilledJid);
- if (!allowEditJid) {
- jid.setFocusable(false);
- jid.setFocusableInTouchMode(false);
- jid.setClickable(false);
- jid.setCursorVisible(false);
- }
- }
-
- jid.setHint(R.string.account_settings_example_jabber_id);
-
- if (account == null) {
- StartConversationActivity.populateAccountSpinner(context, activatedAccounts, spinner);
- } else {
- ArrayAdapter<String> adapter = new ArrayAdapter<>(context,
- R.layout.simple_list_item,
- new String[] { account });
- spinner.setEnabled(false);
- adapter.setDropDownViewResource(R.layout.simple_list_item);
- spinner.setAdapter(adapter);
- }
-
- builder.setView(dialogView);
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(positiveButton, null);
- this.dialog = builder.create();
-
- this.dialogOnClick = new View.OnClickListener() {
- @Override
- public void onClick(final View v) {
- final Jid accountJid;
- if (!spinner.isEnabled() && account == null) {
- return;
- }
- try {
- if (Config.DOMAIN_LOCK != null) {
- accountJid = Jid.fromParts((String) spinner.getSelectedItem(), Config.DOMAIN_LOCK, null);
- } else {
- accountJid = Jid.fromString((String) spinner.getSelectedItem());
- }
- } catch (final InvalidJidException e) {
- return;
- }
- final Jid contactJid;
- try {
- contactJid = Jid.fromString(jid.getText().toString());
- } catch (final InvalidJidException e) {
- jid.setError(context.getString(R.string.invalid_jid));
- return;
- }
-
- if(listener != null) {
- try {
- if(listener.onEnterJidDialogPositive(accountJid, contactJid)) {
- dialog.dismiss();
- }
- } catch(JidError error) {
- jid.setError(error.toString());
- }
- }
- }
- };
- }
-
- public void setOnEnterJidDialogPositiveListener(OnEnterJidDialogPositiveListener listener) {
- this.listener = listener;
- }
-
- public Dialog show() {
- this.dialog.show();
- this.dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(this.dialogOnClick);
- return this.dialog;
- }
+ public interface OnEnterJidDialogPositiveListener {
+ boolean onEnterJidDialogPositive(Jid account, Jid contact) throws EnterJidDialog.JidError;
+ }
+
+ public static class JidError extends Exception {
+ final String msg;
+
+ public JidError(final String msg) {
+ this.msg = msg;
+ }
+
+ public String toString() {
+ return msg;
+ }
+ }
+
+ protected final AlertDialog dialog;
+ protected View.OnClickListener dialogOnClick;
+ protected OnEnterJidDialogPositiveListener listener = null;
+
+ public EnterJidDialog(
+ final Context context, List<String> knownHosts, final List<String> activatedAccounts,
+ final String title, final String positiveButton,
+ final String prefilledJid, final String account, boolean allowEditJid
+ ) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(title);
+ View dialogView = LayoutInflater.from(context).inflate(R.layout.enter_jid_dialog, null);
+ final TextView jabberIdDesc = (TextView) dialogView.findViewById(R.id.jabber_id);
+ jabberIdDesc.setText(R.string.account_settings_jabber_id);
+ final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
+ final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView.findViewById(R.id.jid);
+ jid.setAdapter(new KnownHostsAdapter(context, R.layout.simple_list_item, knownHosts));
+ if (prefilledJid != null) {
+ jid.append(prefilledJid);
+ if (!allowEditJid) {
+ jid.setFocusable(false);
+ jid.setFocusableInTouchMode(false);
+ jid.setClickable(false);
+ jid.setCursorVisible(false);
+ }
+ }
+
+ jid.setHint(R.string.account_settings_example_jabber_id);
+
+ if (account == null) {
+ StartConversationActivity.populateAccountSpinner(context, activatedAccounts, spinner);
+ } else {
+ ArrayAdapter<String> adapter = new ArrayAdapter<>(context,
+ R.layout.simple_list_item,
+ new String[]{account});
+ spinner.setEnabled(false);
+ adapter.setDropDownViewResource(R.layout.simple_list_item);
+ spinner.setAdapter(adapter);
+ }
+
+ builder.setView(dialogView);
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(positiveButton, null);
+ this.dialog = builder.create();
+
+ this.dialogOnClick = new View.OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ final Jid accountJid;
+ if (!spinner.isEnabled() && account == null) {
+ return;
+ }
+ try {
+ if (Config.DOMAIN_LOCK != null) {
+ accountJid = Jid.fromParts((String) spinner.getSelectedItem(), Config.DOMAIN_LOCK, null);
+ } else {
+ accountJid = Jid.fromString((String) spinner.getSelectedItem());
+ }
+ } catch (final InvalidJidException e) {
+ return;
+ }
+ final Jid contactJid;
+ try {
+ contactJid = Jid.fromString(jid.getText().toString());
+ } catch (final InvalidJidException e) {
+ jid.setError(context.getString(R.string.invalid_jid));
+ return;
+ }
+
+ if (listener != null) {
+ try {
+ if (listener.onEnterJidDialogPositive(accountJid, contactJid)) {
+ dialog.dismiss();
+ }
+ } catch (JidError error) {
+ jid.setError(error.toString());
+ }
+ }
+ }
+ };
+ }
+
+ public void setOnEnterJidDialogPositiveListener(OnEnterJidDialogPositiveListener listener) {
+ this.listener = listener;
+ }
+
+ public Dialog show() {
+ this.dialog.show();
+ this.dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(this.dialogOnClick);
+ return this.dialog;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java b/src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java
index c83318160..5e41ad326 100644
--- a/src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java
@@ -21,100 +21,100 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class MagicCreateActivity extends XmppActivity implements TextWatcher {
- private TextView mFullJidDisplay;
- private EditText mUsername;
- private SecureRandom mRandom;
-
- private static final String CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456780+-/#$!?";
- private static final int PW_LENGTH = 10;
-
- @Override
- protected void refreshUiReal() {
-
- }
-
- @Override
- void onBackendConnected() {
-
- }
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- if (getResources().getBoolean(R.bool.portrait_only)) {
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
- }
- super.onCreate(savedInstanceState);
- setContentView(R.layout.magic_create);
- mFullJidDisplay = (TextView) findViewById(R.id.full_jid);
- mUsername = (EditText) findViewById(R.id.username);
- mRandom = new SecureRandom();
- Button next = (Button) findViewById(R.id.create_account);
- next.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- String username = mUsername.getText().toString();
- if (username.contains("@") || username.length() < 3) {
- mUsername.setError(getString(R.string.invalid_username));
- mUsername.requestFocus();
- } else {
- mUsername.setError(null);
- try {
- Jid jid = Jid.fromParts(username.toLowerCase(), Config.MAGIC_CREATE_DOMAIN, null);
- Account account = xmppConnectionService.findAccountByJid(jid);
- if (account == null) {
- account = new Account(jid, createPassword());
- account.setOption(Account.OPTION_REGISTER, true);
- account.setOption(Account.OPTION_DISABLED, true);
- account.setOption(Account.OPTION_MAGIC_CREATE, true);
- xmppConnectionService.createAccount(account);
- }
- Intent intent = new Intent(MagicCreateActivity.this, EditAccountActivity.class);
- intent.putExtra("jid", account.getJid().toBareJid().toString());
- intent.putExtra("init", true);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- Toast.makeText(MagicCreateActivity.this, R.string.secure_password_generated, Toast.LENGTH_SHORT).show();
- startActivity(intent);
- } catch (InvalidJidException e) {
- mUsername.setError(getString(R.string.invalid_username));
- mUsername.requestFocus();
- }
- }
- }
- });
- mUsername.addTextChangedListener(this);
- }
-
- private String createPassword() {
- StringBuilder builder = new StringBuilder(PW_LENGTH);
- for(int i = 0; i < PW_LENGTH; ++i) {
- builder.append(CHARS.charAt(mRandom.nextInt(CHARS.length() - 1)));
- }
- return builder.toString();
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
-
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- if (s.toString().trim().length() > 0) {
- try {
- mFullJidDisplay.setVisibility(View.VISIBLE);
- Jid jid = Jid.fromParts(s.toString().toLowerCase(), Config.MAGIC_CREATE_DOMAIN, null);
- mFullJidDisplay.setText(getString(R.string.your_full_jid_will_be, jid.toString()));
- } catch (InvalidJidException e) {
- mFullJidDisplay.setVisibility(View.INVISIBLE);
- }
-
- } else {
- mFullJidDisplay.setVisibility(View.INVISIBLE);
- }
- }
+ private TextView mFullJidDisplay;
+ private EditText mUsername;
+ private SecureRandom mRandom;
+
+ private static final String CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456780+-/#$!?";
+ private static final int PW_LENGTH = 10;
+
+ @Override
+ protected void refreshUiReal() {
+
+ }
+
+ @Override
+ void onBackendConnected() {
+
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ if (getResources().getBoolean(R.bool.portrait_only)) {
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ }
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.magic_create);
+ mFullJidDisplay = (TextView) findViewById(R.id.full_jid);
+ mUsername = (EditText) findViewById(R.id.username);
+ mRandom = new SecureRandom();
+ Button next = (Button) findViewById(R.id.create_account);
+ next.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String username = mUsername.getText().toString();
+ if (username.contains("@") || username.length() < 3) {
+ mUsername.setError(getString(R.string.invalid_username));
+ mUsername.requestFocus();
+ } else {
+ mUsername.setError(null);
+ try {
+ Jid jid = Jid.fromParts(username.toLowerCase(), Config.MAGIC_CREATE_DOMAIN, null);
+ Account account = xmppConnectionService.findAccountByJid(jid);
+ if (account == null) {
+ account = new Account(jid, createPassword());
+ account.setOption(Account.OPTION_REGISTER, true);
+ account.setOption(Account.OPTION_DISABLED, true);
+ account.setOption(Account.OPTION_MAGIC_CREATE, true);
+ xmppConnectionService.createAccount(account);
+ }
+ Intent intent = new Intent(MagicCreateActivity.this, EditAccountActivity.class);
+ intent.putExtra("jid", account.getJid().toBareJid().toString());
+ intent.putExtra("init", true);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ Toast.makeText(MagicCreateActivity.this, R.string.secure_password_generated, Toast.LENGTH_SHORT).show();
+ startActivity(intent);
+ } catch (InvalidJidException e) {
+ mUsername.setError(getString(R.string.invalid_username));
+ mUsername.requestFocus();
+ }
+ }
+ }
+ });
+ mUsername.addTextChangedListener(this);
+ }
+
+ private String createPassword() {
+ StringBuilder builder = new StringBuilder(PW_LENGTH);
+ for (int i = 0; i < PW_LENGTH; ++i) {
+ builder.append(CHARS.charAt(mRandom.nextInt(CHARS.length() - 1)));
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.toString().trim().length() > 0) {
+ try {
+ mFullJidDisplay.setVisibility(View.VISIBLE);
+ Jid jid = Jid.fromParts(s.toString().toLowerCase(), Config.MAGIC_CREATE_DOMAIN, null);
+ mFullJidDisplay.setText(getString(R.string.your_full_jid_will_be, jid.toString()));
+ } catch (InvalidJidException e) {
+ mFullJidDisplay.setVisibility(View.INVISIBLE);
+ }
+
+ } else {
+ mFullJidDisplay.setVisibility(View.INVISIBLE);
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ManageAccountActivity.java b/src/main/java/de/pixart/messenger/ui/ManageAccountActivity.java
index 0eb5eda21..66d1330ef 100644
--- a/src/main/java/de/pixart/messenger/ui/ManageAccountActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ManageAccountActivity.java
@@ -38,69 +38,69 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class ManageAccountActivity extends XmppActivity implements OnAccountUpdate, KeyChainAliasCallback, XmppConnectionService.OnAccountCreated {
- private final String STATE_SELECTED_ACCOUNT = "selected_account";
-
- protected Account selectedAccount = null;
- protected Jid selectedAccountJid = null;
-
- protected final List<Account> accountList = new ArrayList<>();
- protected ListView accountListView;
- protected AccountAdapter mAccountAdapter;
- protected AtomicBoolean mInvokedAddAccount = new AtomicBoolean(false);
-
- protected Pair<Integer, Intent> mPostponedActivityResult = null;
-
- @Override
- public void onAccountUpdate() {
- refreshUi();
- }
-
- @Override
- protected void refreshUiReal() {
- synchronized (this.accountList) {
- accountList.clear();
- accountList.addAll(xmppConnectionService.getAccounts());
- }
- ActionBar actionBar = getActionBar();
- if (actionBar != null) {
- actionBar.setHomeButtonEnabled(this.accountList.size() > 0);
- actionBar.setDisplayHomeAsUpEnabled(this.accountList.size() > 0);
- }
- invalidateOptionsMenu();
- mAccountAdapter.notifyDataSetChanged();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
-
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.manage_accounts);
-
- if (savedInstanceState != null) {
- String jid = savedInstanceState.getString(STATE_SELECTED_ACCOUNT);
- if (jid != null) {
- try {
- this.selectedAccountJid = Jid.fromString(jid);
- } catch (InvalidJidException e) {
- this.selectedAccountJid = null;
- }
- }
- }
-
- accountListView = (ListView) findViewById(R.id.account_list);
- this.mAccountAdapter = new AccountAdapter(this, accountList);
- accountListView.setAdapter(this.mAccountAdapter);
- accountListView.setOnItemClickListener(new OnItemClickListener() {
-
- @Override
- public void onItemClick(AdapterView<?> arg0, View view,
- int position, long arg3) {
- switchToAccount(accountList.get(position));
- }
- });
- registerForContextMenu(accountListView);
- }
+ private final String STATE_SELECTED_ACCOUNT = "selected_account";
+
+ protected Account selectedAccount = null;
+ protected Jid selectedAccountJid = null;
+
+ protected final List<Account> accountList = new ArrayList<>();
+ protected ListView accountListView;
+ protected AccountAdapter mAccountAdapter;
+ protected AtomicBoolean mInvokedAddAccount = new AtomicBoolean(false);
+
+ protected Pair<Integer, Intent> mPostponedActivityResult = null;
+
+ @Override
+ public void onAccountUpdate() {
+ refreshUi();
+ }
+
+ @Override
+ protected void refreshUiReal() {
+ synchronized (this.accountList) {
+ accountList.clear();
+ accountList.addAll(xmppConnectionService.getAccounts());
+ }
+ ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ actionBar.setHomeButtonEnabled(this.accountList.size() > 0);
+ actionBar.setDisplayHomeAsUpEnabled(this.accountList.size() > 0);
+ }
+ invalidateOptionsMenu();
+ mAccountAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.manage_accounts);
+
+ if (savedInstanceState != null) {
+ String jid = savedInstanceState.getString(STATE_SELECTED_ACCOUNT);
+ if (jid != null) {
+ try {
+ this.selectedAccountJid = Jid.fromString(jid);
+ } catch (InvalidJidException e) {
+ this.selectedAccountJid = null;
+ }
+ }
+ }
+
+ accountListView = (ListView) findViewById(R.id.account_list);
+ this.mAccountAdapter = new AccountAdapter(this, accountList);
+ accountListView.setAdapter(this.mAccountAdapter);
+ accountListView.setOnItemClickListener(new OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View view,
+ int position, long arg3) {
+ switchToAccount(accountList.get(position));
+ }
+ });
+ registerForContextMenu(accountListView);
+ }
@Override
protected void onStart() {
@@ -111,286 +111,286 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
}
}
- @Override
- public void onSaveInstanceState(final Bundle savedInstanceState) {
- if (selectedAccount != null) {
- savedInstanceState.putString(STATE_SELECTED_ACCOUNT, selectedAccount.getJid().toBareJid().toString());
- }
- super.onSaveInstanceState(savedInstanceState);
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, v, menuInfo);
- ManageAccountActivity.this.getMenuInflater().inflate(
+ @Override
+ public void onSaveInstanceState(final Bundle savedInstanceState) {
+ if (selectedAccount != null) {
+ savedInstanceState.putString(STATE_SELECTED_ACCOUNT, selectedAccount.getJid().toBareJid().toString());
+ }
+ super.onSaveInstanceState(savedInstanceState);
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ ManageAccountActivity.this.getMenuInflater().inflate(
R.menu.manageaccounts_context, menu);
- AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
- this.selectedAccount = accountList.get(acmi.position);
- if (this.selectedAccount.isOptionSet(Account.OPTION_DISABLED)) {
- menu.findItem(R.id.mgmt_account_reconnect).setVisible(false);
- menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(false);
- menu.findItem(R.id.mgmt_account_publish_avatar).setVisible(false);
- menu.findItem(R.id.mgmt_account_change_presence).setVisible(false);
- } else {
- menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(Config.supportOpenPgp());
- menu.findItem(R.id.mgmt_account_change_presence).setVisible(manuallyChangePresence());
- }
- menu.setHeaderTitle(this.selectedAccount.getJid().toBareJid().toString());
- }
-
- @Override
- void onBackendConnected() {
- if (selectedAccountJid != null) {
- this.selectedAccount = xmppConnectionService.findAccountByJid(selectedAccountJid);
- }
- refreshUiReal();
- if (this.mPostponedActivityResult != null) {
- this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
- }
- if (Config.X509_VERIFICATION && this.accountList.size() == 0) {
- if (mInvokedAddAccount.compareAndSet(false, true)) {
- addAccountFromKey();
- }
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.manageaccounts, menu);
- MenuItem addAccount = menu.findItem(R.id.action_add_account);
- MenuItem addAccountWithCertificate = menu.findItem(R.id.action_add_account_with_cert);
-
- if (Config.X509_VERIFICATION) {
- addAccount.setVisible(false);
- addAccountWithCertificate.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- } else {
- addAccount.setVisible(!Config.SINGLE_ACCOUNT);
- }
- addAccountWithCertificate.setVisible(!Config.SINGLE_ACCOUNT);
- return true;
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.mgmt_account_publish_avatar:
- publishAvatar(selectedAccount);
- return true;
- case R.id.mgmt_account_reconnect:
- disableAccount(selectedAccount);
- enableAccount(selectedAccount);
- return true;
- case R.id.mgmt_account_delete:
- deleteAccount(selectedAccount);
- return true;
- case R.id.mgmt_account_announce_pgp:
- publishOpenPGPPublicKey(selectedAccount);
- return true;
- case R.id.mgmt_account_change_presence:
- changePresence(selectedAccount);
- return true;
- default:
- return super.onContextItemSelected(item);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_add_account:
- startActivity(new Intent(getApplicationContext(),
- EditAccountActivity.class));
- break;
- case R.id.action_add_account_with_cert:
- addAccountFromKey();
- break;
- default:
- break;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public boolean onNavigateUp() {
- if (xmppConnectionService.getConversations().size() == 0) {
- Intent contactsIntent = new Intent(this,
- StartConversationActivity.class);
- contactsIntent.setFlags(
- // if activity exists in stack, pop the stack and go back to it
- Intent.FLAG_ACTIVITY_CLEAR_TOP |
- // otherwise, make a new task for it
- Intent.FLAG_ACTIVITY_NEW_TASK |
- // don't use the new activity animation; finish
- // animation runs instead
- Intent.FLAG_ACTIVITY_NO_ANIMATION);
- startActivity(contactsIntent);
- finish();
- return true;
- } else {
- return super.onNavigateUp();
- }
- }
-
- private void changePresence(Account account) {
- Intent intent = new Intent(this, SetPresenceActivity.class);
- intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT,account.getJid().toBareJid().toString());
- startActivity(intent);
- }
-
- public void onClickTglAccountState(Account account, boolean enable) {
- if (enable) {
- enableAccount(account);
- } else {
- disableAccount(account);
- }
- }
-
- private void addAccountFromKey() {
- try {
- KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this, R.string.device_does_not_support_certificates, Toast.LENGTH_LONG).show();
- }
- }
-
- private void publishAvatar(Account account) {
- Intent intent = new Intent(getApplicationContext(),
- PublishProfilePictureActivity.class);
- intent.putExtra(EXTRA_ACCOUNT, account.getJid().toString());
- startActivity(intent);
- }
-
- private void disableAllAccounts() {
- List<Account> list = new ArrayList<>();
- synchronized (this.accountList) {
- for (Account account : this.accountList) {
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- list.add(account);
- }
- }
- }
- for (Account account : list) {
- disableAccount(account);
- }
- }
-
- private boolean accountsLeftToDisable() {
- synchronized (this.accountList) {
- for (Account account : this.accountList) {
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- return true;
- }
- }
- return false;
- }
- }
-
- private boolean accountsLeftToEnable() {
- synchronized (this.accountList) {
- for (Account account : this.accountList) {
- if (account.isOptionSet(Account.OPTION_DISABLED)) {
- return true;
- }
- }
- return false;
- }
- }
-
- private void enableAllAccounts() {
- List<Account> list = new ArrayList<>();
- synchronized (this.accountList) {
- for (Account account : this.accountList) {
- if (account.isOptionSet(Account.OPTION_DISABLED)) {
- list.add(account);
- }
- }
- }
- for (Account account : list) {
- enableAccount(account);
- }
- }
-
- private void disableAccount(Account account) {
- account.setOption(Account.OPTION_DISABLED, true);
- if (!xmppConnectionService.updateAccount(account)) {
- Toast.makeText(this,R.string.unable_to_update_account,Toast.LENGTH_SHORT).show();
- }
- }
-
- private void enableAccount(Account account) {
- account.setOption(Account.OPTION_DISABLED, false);
- if (!xmppConnectionService.updateAccount(account)) {
- Toast.makeText(this,R.string.unable_to_update_account,Toast.LENGTH_SHORT).show();
- }
- }
-
- private void publishOpenPGPPublicKey(Account account) {
- if (ManageAccountActivity.this.hasPgp()) {
- announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
- } else {
- this.showInstallPgpDialog();
- }
- }
-
- private void deleteAccount(final Account account) {
- AlertDialog.Builder builder = new AlertDialog.Builder(
- ManageAccountActivity.this);
- builder.setTitle(getString(R.string.mgmt_account_are_you_sure));
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text));
- builder.setPositiveButton(getString(R.string.delete),
- new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- xmppConnectionService.deleteAccount(account);
- selectedAccount = null;
- }
- });
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.create().show();
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == RESULT_OK) {
- if (xmppConnectionServiceBound) {
- if (requestCode == REQUEST_CHOOSE_PGP_ID) {
- if (data.getExtras().containsKey(OpenPgpApi.EXTRA_SIGN_KEY_ID)) {
- selectedAccount.setPgpSignId(data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID));
- announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
- } else {
- choosePgpSignId(selectedAccount);
- }
- } else if (requestCode == REQUEST_ANNOUNCE_PGP) {
- announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
- }
- this.mPostponedActivityResult = null;
- } else {
- this.mPostponedActivityResult = new Pair<>(requestCode, data);
- }
- }
- }
-
- @Override
- public void alias(String alias) {
- if (alias != null) {
- xmppConnectionService.createAccountFromKey(alias, this);
- }
- }
-
- @Override
- public void onAccountCreated(Account account) {
- switchToAccount(account, true);
- }
-
- @Override
- public void informUser(final int r) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(ManageAccountActivity.this, r, Toast.LENGTH_LONG).show();
- }
- });
- }
+ AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
+ this.selectedAccount = accountList.get(acmi.position);
+ if (this.selectedAccount.isOptionSet(Account.OPTION_DISABLED)) {
+ menu.findItem(R.id.mgmt_account_reconnect).setVisible(false);
+ menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(false);
+ menu.findItem(R.id.mgmt_account_publish_avatar).setVisible(false);
+ menu.findItem(R.id.mgmt_account_change_presence).setVisible(false);
+ } else {
+ menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(Config.supportOpenPgp());
+ menu.findItem(R.id.mgmt_account_change_presence).setVisible(manuallyChangePresence());
+ }
+ menu.setHeaderTitle(this.selectedAccount.getJid().toBareJid().toString());
+ }
+
+ @Override
+ void onBackendConnected() {
+ if (selectedAccountJid != null) {
+ this.selectedAccount = xmppConnectionService.findAccountByJid(selectedAccountJid);
+ }
+ refreshUiReal();
+ if (this.mPostponedActivityResult != null) {
+ this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
+ }
+ if (Config.X509_VERIFICATION && this.accountList.size() == 0) {
+ if (mInvokedAddAccount.compareAndSet(false, true)) {
+ addAccountFromKey();
+ }
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.manageaccounts, menu);
+ MenuItem addAccount = menu.findItem(R.id.action_add_account);
+ MenuItem addAccountWithCertificate = menu.findItem(R.id.action_add_account_with_cert);
+
+ if (Config.X509_VERIFICATION) {
+ addAccount.setVisible(false);
+ addAccountWithCertificate.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ } else {
+ addAccount.setVisible(!Config.SINGLE_ACCOUNT);
+ }
+ addAccountWithCertificate.setVisible(!Config.SINGLE_ACCOUNT);
+ return true;
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.mgmt_account_publish_avatar:
+ publishAvatar(selectedAccount);
+ return true;
+ case R.id.mgmt_account_reconnect:
+ disableAccount(selectedAccount);
+ enableAccount(selectedAccount);
+ return true;
+ case R.id.mgmt_account_delete:
+ deleteAccount(selectedAccount);
+ return true;
+ case R.id.mgmt_account_announce_pgp:
+ publishOpenPGPPublicKey(selectedAccount);
+ return true;
+ case R.id.mgmt_account_change_presence:
+ changePresence(selectedAccount);
+ return true;
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_add_account:
+ startActivity(new Intent(getApplicationContext(),
+ EditAccountActivity.class));
+ break;
+ case R.id.action_add_account_with_cert:
+ addAccountFromKey();
+ break;
+ default:
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public boolean onNavigateUp() {
+ if (xmppConnectionService.getConversations().size() == 0) {
+ Intent contactsIntent = new Intent(this,
+ StartConversationActivity.class);
+ contactsIntent.setFlags(
+ // if activity exists in stack, pop the stack and go back to it
+ Intent.FLAG_ACTIVITY_CLEAR_TOP |
+ // otherwise, make a new task for it
+ Intent.FLAG_ACTIVITY_NEW_TASK |
+ // don't use the new activity animation; finish
+ // animation runs instead
+ Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivity(contactsIntent);
+ finish();
+ return true;
+ } else {
+ return super.onNavigateUp();
+ }
+ }
+
+ private void changePresence(Account account) {
+ Intent intent = new Intent(this, SetPresenceActivity.class);
+ intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT, account.getJid().toBareJid().toString());
+ startActivity(intent);
+ }
+
+ public void onClickTglAccountState(Account account, boolean enable) {
+ if (enable) {
+ enableAccount(account);
+ } else {
+ disableAccount(account);
+ }
+ }
+
+ private void addAccountFromKey() {
+ try {
+ KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(this, R.string.device_does_not_support_certificates, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private void publishAvatar(Account account) {
+ Intent intent = new Intent(getApplicationContext(),
+ PublishProfilePictureActivity.class);
+ intent.putExtra(EXTRA_ACCOUNT, account.getJid().toString());
+ startActivity(intent);
+ }
+
+ private void disableAllAccounts() {
+ List<Account> list = new ArrayList<>();
+ synchronized (this.accountList) {
+ for (Account account : this.accountList) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ list.add(account);
+ }
+ }
+ }
+ for (Account account : list) {
+ disableAccount(account);
+ }
+ }
+
+ private boolean accountsLeftToDisable() {
+ synchronized (this.accountList) {
+ for (Account account : this.accountList) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private boolean accountsLeftToEnable() {
+ synchronized (this.accountList) {
+ for (Account account : this.accountList) {
+ if (account.isOptionSet(Account.OPTION_DISABLED)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private void enableAllAccounts() {
+ List<Account> list = new ArrayList<>();
+ synchronized (this.accountList) {
+ for (Account account : this.accountList) {
+ if (account.isOptionSet(Account.OPTION_DISABLED)) {
+ list.add(account);
+ }
+ }
+ }
+ for (Account account : list) {
+ enableAccount(account);
+ }
+ }
+
+ private void disableAccount(Account account) {
+ account.setOption(Account.OPTION_DISABLED, true);
+ if (!xmppConnectionService.updateAccount(account)) {
+ Toast.makeText(this, R.string.unable_to_update_account, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void enableAccount(Account account) {
+ account.setOption(Account.OPTION_DISABLED, false);
+ if (!xmppConnectionService.updateAccount(account)) {
+ Toast.makeText(this, R.string.unable_to_update_account, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void publishOpenPGPPublicKey(Account account) {
+ if (ManageAccountActivity.this.hasPgp()) {
+ announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
+ } else {
+ this.showInstallPgpDialog();
+ }
+ }
+
+ private void deleteAccount(final Account account) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(
+ ManageAccountActivity.this);
+ builder.setTitle(getString(R.string.mgmt_account_are_you_sure));
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text));
+ builder.setPositiveButton(getString(R.string.delete),
+ new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ xmppConnectionService.deleteAccount(account);
+ selectedAccount = null;
+ }
+ });
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.create().show();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == RESULT_OK) {
+ if (xmppConnectionServiceBound) {
+ if (requestCode == REQUEST_CHOOSE_PGP_ID) {
+ if (data.getExtras().containsKey(OpenPgpApi.EXTRA_SIGN_KEY_ID)) {
+ selectedAccount.setPgpSignId(data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID));
+ announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
+ } else {
+ choosePgpSignId(selectedAccount);
+ }
+ } else if (requestCode == REQUEST_ANNOUNCE_PGP) {
+ announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
+ }
+ this.mPostponedActivityResult = null;
+ } else {
+ this.mPostponedActivityResult = new Pair<>(requestCode, data);
+ }
+ }
+ }
+
+ @Override
+ public void alias(String alias) {
+ if (alias != null) {
+ xmppConnectionService.createAccountFromKey(alias, this);
+ }
+ }
+
+ @Override
+ public void onAccountCreated(Account account) {
+ switchToAccount(account, true);
+ }
+
+ @Override
+ public void informUser(final int r) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(ManageAccountActivity.this, r, Toast.LENGTH_LONG).show();
+ }
+ });
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java b/src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java
index 423ccef9e..ac981f722 100644
--- a/src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java
@@ -1,4 +1,3 @@
-
package de.pixart.messenger.ui;
import android.app.PendingIntent;
@@ -31,311 +30,311 @@ import de.pixart.messenger.xmpp.pep.Avatar;
public class PublishProfilePictureActivity extends XmppActivity {
- private static final int REQUEST_CHOOSE_FILE_AND_CROP = 0xac23;
- private static final int REQUEST_CHOOSE_FILE = 0xac24;
- private ImageView avatar;
- private TextView accountTextView;
- private TextView hintOrWarning;
- private TextView secondaryHint;
- private Button cancelButton;
- private Button publishButton;
- private Uri avatarUri;
- private Uri defaultUri;
- private Account account;
- private boolean support = false;
- private OnLongClickListener backToDefaultListener = new OnLongClickListener() {
+ private static final int REQUEST_CHOOSE_FILE_AND_CROP = 0xac23;
+ private static final int REQUEST_CHOOSE_FILE = 0xac24;
+ private ImageView avatar;
+ private TextView accountTextView;
+ private TextView hintOrWarning;
+ private TextView secondaryHint;
+ private Button cancelButton;
+ private Button publishButton;
+ private Uri avatarUri;
+ private Uri defaultUri;
+ private Account account;
+ private boolean support = false;
+ private OnLongClickListener backToDefaultListener = new OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- avatarUri = defaultUri;
- loadImageIntoPreview(defaultUri);
- return true;
- }
- };
- private boolean mInitialAccountSetup;
- private UiCallback<Avatar> avatarPublication = new UiCallback<Avatar>() {
+ @Override
+ public boolean onLongClick(View v) {
+ avatarUri = defaultUri;
+ loadImageIntoPreview(defaultUri);
+ return true;
+ }
+ };
+ private boolean mInitialAccountSetup;
+ private UiCallback<Avatar> avatarPublication = new UiCallback<Avatar>() {
- @Override
- public void success(Avatar object) {
- runOnUiThread(new Runnable() {
+ @Override
+ public void success(Avatar object) {
+ runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (mInitialAccountSetup) {
- Intent intent = new Intent(getApplicationContext(),
- StartConversationActivity.class);
- intent.putExtra("init", true);
- startActivity(intent);
- }
- Toast.makeText(PublishProfilePictureActivity.this,
- R.string.avatar_has_been_published,
- Toast.LENGTH_SHORT).show();
- finish();
- }
- });
- }
+ @Override
+ public void run() {
+ if (mInitialAccountSetup) {
+ Intent intent = new Intent(getApplicationContext(),
+ StartConversationActivity.class);
+ intent.putExtra("init", true);
+ startActivity(intent);
+ }
+ Toast.makeText(PublishProfilePictureActivity.this,
+ R.string.avatar_has_been_published,
+ Toast.LENGTH_SHORT).show();
+ finish();
+ }
+ });
+ }
- @Override
- public void error(final int errorCode, Avatar object) {
- runOnUiThread(new Runnable() {
+ @Override
+ public void error(final int errorCode, Avatar object) {
+ runOnUiThread(new Runnable() {
- @Override
- public void run() {
- hintOrWarning.setText(errorCode);
- hintOrWarning.setTextColor(getWarningTextColor());
- publishButton.setText(R.string.publish);
- enablePublishButton();
- }
- });
+ @Override
+ public void run() {
+ hintOrWarning.setText(errorCode);
+ hintOrWarning.setTextColor(getWarningTextColor());
+ publishButton.setText(R.string.publish);
+ enablePublishButton();
+ }
+ });
- }
+ }
- @Override
- public void userInputRequried(PendingIntent pi, Avatar object) {
- }
- };
+ @Override
+ public void userInputRequried(PendingIntent pi, Avatar object) {
+ }
+ };
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_publish_profile_picture);
- this.avatar = (ImageView) findViewById(R.id.account_image);
- this.cancelButton = (Button) findViewById(R.id.cancel_button);
- this.publishButton = (Button) findViewById(R.id.publish_button);
- this.accountTextView = (TextView) findViewById(R.id.account);
- this.hintOrWarning = (TextView) findViewById(R.id.hint_or_warning);
- this.secondaryHint = (TextView) findViewById(R.id.secondary_hint);
- this.publishButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_publish_profile_picture);
+ this.avatar = (ImageView) findViewById(R.id.account_image);
+ this.cancelButton = (Button) findViewById(R.id.cancel_button);
+ this.publishButton = (Button) findViewById(R.id.publish_button);
+ this.accountTextView = (TextView) findViewById(R.id.account);
+ this.hintOrWarning = (TextView) findViewById(R.id.hint_or_warning);
+ this.secondaryHint = (TextView) findViewById(R.id.secondary_hint);
+ this.publishButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (avatarUri != null) {
- publishButton.setText(R.string.publishing);
- disablePublishButton();
- xmppConnectionService.publishAvatar(account, avatarUri,
- avatarPublication);
- }
- }
- });
- this.cancelButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (avatarUri != null) {
+ publishButton.setText(R.string.publishing);
+ disablePublishButton();
+ xmppConnectionService.publishAvatar(account, avatarUri,
+ avatarPublication);
+ }
+ }
+ });
+ this.cancelButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mInitialAccountSetup) {
- Intent intent = new Intent(getApplicationContext(),
- StartConversationActivity.class);
- if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) {
- intent.putExtra("init", true);
- }
- startActivity(intent);
- }
- finish();
- }
- });
- this.avatar.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mInitialAccountSetup) {
+ Intent intent = new Intent(getApplicationContext(),
+ StartConversationActivity.class);
+ if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) {
+ intent.putExtra("init", true);
+ }
+ startActivity(intent);
+ }
+ finish();
+ }
+ });
+ this.avatar.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (hasStoragePermission(REQUEST_CHOOSE_FILE)) {
- chooseAvatar(false);
- }
+ @Override
+ public void onClick(View v) {
+ if (hasStoragePermission(REQUEST_CHOOSE_FILE)) {
+ chooseAvatar(false);
+ }
- }
- });
- this.defaultUri = PhoneHelper.getSelfiUri(getApplicationContext());
- }
+ }
+ });
+ this.defaultUri = PhoneHelper.getSelfiUri(getApplicationContext());
+ }
- private void chooseAvatar(boolean crop) {
- Intent attachFileIntent = new Intent();
- attachFileIntent.setType("image/*");
- attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
- Intent chooser = Intent.createChooser(attachFileIntent, getString(R.string.attach_file));
- startActivityForResult(chooser, crop ? REQUEST_CHOOSE_FILE_AND_CROP : REQUEST_CHOOSE_FILE);
- }
+ private void chooseAvatar(boolean crop) {
+ Intent attachFileIntent = new Intent();
+ attachFileIntent.setType("image/*");
+ attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
+ Intent chooser = Intent.createChooser(attachFileIntent, getString(R.string.attach_file));
+ startActivityForResult(chooser, crop ? REQUEST_CHOOSE_FILE_AND_CROP : REQUEST_CHOOSE_FILE);
+ }
- @Override
- public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
- if (grantResults.length > 0)
- if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- if (requestCode == REQUEST_CHOOSE_FILE_AND_CROP) {
- chooseAvatar(true);
- } else if (requestCode == REQUEST_CHOOSE_FILE) {
- chooseAvatar(false);
- }
- } else {
- Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show();
- }
- }
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
+ if (grantResults.length > 0)
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ if (requestCode == REQUEST_CHOOSE_FILE_AND_CROP) {
+ chooseAvatar(true);
+ } else if (requestCode == REQUEST_CHOOSE_FILE) {
+ chooseAvatar(false);
+ }
+ } else {
+ Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show();
+ }
+ }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.publish_avatar, menu);
- return super.onCreateOptionsMenu(menu);
- }
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.publish_avatar, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- if (item.getItemId() == R.id.action_crop_image) {
- if (hasStoragePermission(REQUEST_CHOOSE_FILE_AND_CROP)) {
- chooseAvatar(true);
- }
- return true;
- } else {
- return super.onOptionsItemSelected(item);
- }
- }
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ if (item.getItemId() == R.id.action_crop_image) {
+ if (hasStoragePermission(REQUEST_CHOOSE_FILE_AND_CROP)) {
+ chooseAvatar(true);
+ }
+ return true;
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
- @Override
- 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);
- if (original != null) {
- source = Uri.parse("file://"+original);
- }
- Uri destination = Uri.fromFile(new File(getCacheDir(), "croppedAvatar"));
- final int size = getPixel(Config.AVATAR_SIZE);
- 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;
- if (xmppConnectionServiceBound) {
- loadImageIntoPreview(this.avatarUri);
- }
- break;
- case Crop.REQUEST_CROP:
- this.avatarUri = Uri.fromFile(new File(getCacheDir(), "croppedAvatar"));
- if (xmppConnectionServiceBound) {
- loadImageIntoPreview(this.avatarUri);
- }
- break;
- }
- } else {
- if (requestCode == Crop.REQUEST_CROP && data != null) {
- Throwable throwable = Crop.getError(data);
- if (throwable != null && throwable instanceof OutOfMemoryError) {
- Toast.makeText(this,R.string.selection_too_large, Toast.LENGTH_SHORT).show();
- }
- }
- }
- }
+ @Override
+ 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);
+ if (original != null) {
+ source = Uri.parse("file://" + original);
+ }
+ Uri destination = Uri.fromFile(new File(getCacheDir(), "croppedAvatar"));
+ final int size = getPixel(Config.AVATAR_SIZE);
+ 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;
+ if (xmppConnectionServiceBound) {
+ loadImageIntoPreview(this.avatarUri);
+ }
+ break;
+ case Crop.REQUEST_CROP:
+ this.avatarUri = Uri.fromFile(new File(getCacheDir(), "croppedAvatar"));
+ if (xmppConnectionServiceBound) {
+ loadImageIntoPreview(this.avatarUri);
+ }
+ break;
+ }
+ } else {
+ if (requestCode == Crop.REQUEST_CROP && data != null) {
+ Throwable throwable = Crop.getError(data);
+ if (throwable != null && throwable instanceof OutOfMemoryError) {
+ Toast.makeText(this, R.string.selection_too_large, Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+ }
- @Override
- protected void onBackendConnected() {
- this.account = extractAccount(getIntent());
- if (this.account != null) {
- if (this.account.getXmppConnection() != null) {
- this.support = this.account.getXmppConnection().getFeatures().pep();
- }
- if (this.avatarUri == null) {
- if (this.account.getAvatar() != null
- || this.defaultUri == null) {
- this.avatar.setImageBitmap(avatarService().get(account, getPixel(Config.AVATAR_SIZE)));
- if (this.defaultUri != null) {
- this.avatar
- .setOnLongClickListener(this.backToDefaultListener);
- } else {
- this.secondaryHint.setVisibility(View.INVISIBLE);
- }
- if (!support) {
- this.hintOrWarning
- .setTextColor(getWarningTextColor());
+ @Override
+ protected void onBackendConnected() {
+ this.account = extractAccount(getIntent());
+ if (this.account != null) {
+ if (this.account.getXmppConnection() != null) {
+ this.support = this.account.getXmppConnection().getFeatures().pep();
+ }
+ if (this.avatarUri == null) {
+ if (this.account.getAvatar() != null
+ || this.defaultUri == null) {
+ this.avatar.setImageBitmap(avatarService().get(account, getPixel(Config.AVATAR_SIZE)));
+ if (this.defaultUri != null) {
+ this.avatar
+ .setOnLongClickListener(this.backToDefaultListener);
+ } else {
+ this.secondaryHint.setVisibility(View.INVISIBLE);
+ }
+ if (!support) {
+ this.hintOrWarning
+ .setTextColor(getWarningTextColor());
if (account.getStatus() == Account.State.ONLINE) {
this.hintOrWarning.setText(R.string.error_publish_avatar_no_server_support);
} else {
this.hintOrWarning.setText(R.string.error_publish_avatar_offline);
}
- }
- } else {
- this.avatarUri = this.defaultUri;
- loadImageIntoPreview(this.defaultUri);
- this.secondaryHint.setVisibility(View.INVISIBLE);
- }
- } else {
- loadImageIntoPreview(avatarUri);
- }
- String account;
- if (Config.DOMAIN_LOCK != null) {
- account = this.account.getJid().getLocalpart();
- } else {
- account = this.account.getJid().toBareJid().toString();
- }
- this.accountTextView.setText(account);
- }
- }
+ }
+ } else {
+ this.avatarUri = this.defaultUri;
+ loadImageIntoPreview(this.defaultUri);
+ this.secondaryHint.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ loadImageIntoPreview(avatarUri);
+ }
+ String account;
+ if (Config.DOMAIN_LOCK != null) {
+ account = this.account.getJid().getLocalpart();
+ } else {
+ account = this.account.getJid().toBareJid().toString();
+ }
+ this.accountTextView.setText(account);
+ }
+ }
- @Override
- protected void onStart() {
- super.onStart();
- if (getIntent() != null) {
- this.mInitialAccountSetup = getIntent().getBooleanExtra("setup", false);
- }
- if (this.mInitialAccountSetup) {
- this.cancelButton.setText(R.string.skip);
- }
- }
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (getIntent() != null) {
+ this.mInitialAccountSetup = getIntent().getBooleanExtra("setup", false);
+ }
+ if (this.mInitialAccountSetup) {
+ this.cancelButton.setText(R.string.skip);
+ }
+ }
- protected void loadImageIntoPreview(Uri uri) {
- Bitmap bm = null;
- try {
- bm = xmppConnectionService.getFileBackend().cropCenterSquare(uri, getPixel(Config.AVATAR_SIZE));
- } catch (Exception e) {
- e.printStackTrace();
- }
+ protected void loadImageIntoPreview(Uri uri) {
+ Bitmap bm = null;
+ try {
+ bm = xmppConnectionService.getFileBackend().cropCenterSquare(uri, getPixel(Config.AVATAR_SIZE));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
- if (bm == null) {
- disablePublishButton();
- this.hintOrWarning.setTextColor(getWarningTextColor());
- this.hintOrWarning
- .setText(R.string.error_publish_avatar_converting);
- return;
- }
- this.avatar.setImageBitmap(bm);
- if (support) {
- enablePublishButton();
- this.publishButton.setText(R.string.publish);
- this.hintOrWarning.setText(R.string.publish_avatar_explanation);
- this.hintOrWarning.setTextColor(getPrimaryTextColor());
- } else {
- disablePublishButton();
- this.hintOrWarning.setTextColor(getWarningTextColor());
+ if (bm == null) {
+ disablePublishButton();
+ this.hintOrWarning.setTextColor(getWarningTextColor());
+ this.hintOrWarning
+ .setText(R.string.error_publish_avatar_converting);
+ return;
+ }
+ this.avatar.setImageBitmap(bm);
+ if (support) {
+ enablePublishButton();
+ this.publishButton.setText(R.string.publish);
+ this.hintOrWarning.setText(R.string.publish_avatar_explanation);
+ this.hintOrWarning.setTextColor(getPrimaryTextColor());
+ } else {
+ disablePublishButton();
+ this.hintOrWarning.setTextColor(getWarningTextColor());
if (account.getStatus() == Account.State.ONLINE) {
this.hintOrWarning.setText(R.string.error_publish_avatar_no_server_support);
} else {
this.hintOrWarning.setText(R.string.error_publish_avatar_offline);
}
- }
- if (this.defaultUri != null && uri.equals(this.defaultUri)) {
- this.secondaryHint.setVisibility(View.INVISIBLE);
- this.avatar.setOnLongClickListener(null);
- } else if (this.defaultUri != null) {
- this.secondaryHint.setVisibility(View.VISIBLE);
- this.avatar.setOnLongClickListener(this.backToDefaultListener);
- }
- }
+ }
+ if (this.defaultUri != null && uri.equals(this.defaultUri)) {
+ this.secondaryHint.setVisibility(View.INVISIBLE);
+ this.avatar.setOnLongClickListener(null);
+ } else if (this.defaultUri != null) {
+ this.secondaryHint.setVisibility(View.VISIBLE);
+ this.avatar.setOnLongClickListener(this.backToDefaultListener);
+ }
+ }
- protected void enablePublishButton() {
- this.publishButton.setEnabled(true);
- this.publishButton.setTextColor(getPrimaryTextColor());
- }
+ protected void enablePublishButton() {
+ this.publishButton.setEnabled(true);
+ this.publishButton.setTextColor(getPrimaryTextColor());
+ }
- protected void disablePublishButton() {
- this.publishButton.setEnabled(false);
- this.publishButton.setTextColor(getSecondaryTextColor());
- }
+ protected void disablePublishButton() {
+ this.publishButton.setEnabled(false);
+ this.publishButton.setTextColor(getSecondaryTextColor());
+ }
- public void refreshUiReal() {
- //nothing to do. This Activity doesn't implement any listeners
- }
+ public void refreshUiReal() {
+ //nothing to do. This Activity doesn't implement any listeners
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/RecordingActivity.java b/src/main/java/de/pixart/messenger/ui/RecordingActivity.java
index ed753022b..e5e43f290 100644
--- a/src/main/java/de/pixart/messenger/ui/RecordingActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/RecordingActivity.java
@@ -26,139 +26,139 @@ import de.pixart.messenger.persistance.FileBackend;
public class RecordingActivity extends Activity implements View.OnClickListener {
- private TextView mTimerTextView;
- private Button mCancelButton;
- private Button mStopButton;
-
- private MediaRecorder mRecorder;
- private long mStartTime = 0;
-
- private int[] amplitudes = new int[100];
- private int i = 0;
-
- private Handler mHandler = new Handler();
- private Runnable mTickExecutor = new Runnable() {
- @Override
- public void run() {
- tick();
- mHandler.postDelayed(mTickExecutor,100);
- }
- };
- private File mOutputFile;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_recording);
- this.mTimerTextView = (TextView) this.findViewById(R.id.timer);
- this.mCancelButton = (Button) this.findViewById(R.id.cancel_button);
- this.mCancelButton.setOnClickListener(this);
- this.mStopButton = (Button) this.findViewById(R.id.share_button);
- this.mStopButton.setOnClickListener(this);
+ private TextView mTimerTextView;
+ private Button mCancelButton;
+ private Button mStopButton;
+
+ private MediaRecorder mRecorder;
+ private long mStartTime = 0;
+
+ private int[] amplitudes = new int[100];
+ private int i = 0;
+
+ private Handler mHandler = new Handler();
+ private Runnable mTickExecutor = new Runnable() {
+ @Override
+ public void run() {
+ tick();
+ mHandler.postDelayed(mTickExecutor, 100);
+ }
+ };
+ private File mOutputFile;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_recording);
+ this.mTimerTextView = (TextView) this.findViewById(R.id.timer);
+ this.mCancelButton = (Button) this.findViewById(R.id.cancel_button);
+ this.mCancelButton.setOnClickListener(this);
+ this.mStopButton = (Button) this.findViewById(R.id.share_button);
+ this.mStopButton.setOnClickListener(this);
this.setFinishOnTouchOutside(false);
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
- @Override
- protected void onStart() {
- super.onStart();
- Log.d(Config.LOGTAG, "output: " + getOutputFile());
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Log.d(Config.LOGTAG, "output: " + getOutputFile());
if (!startRecording()) {
mStopButton.setEnabled(false);
mStopButton.setTextColor(0x8a000000);
- Toast.makeText(this,R.string.unable_to_start_recording,Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.unable_to_start_recording, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (mRecorder != null) {
+ stopRecording(false);
+ }
+ }
+
+ private boolean startRecording() {
+ mRecorder = new MediaRecorder();
+ mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+ mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+ mRecorder.setAudioEncodingBitRate(48000);
+ } else {
+ mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+ mRecorder.setAudioEncodingBitRate(48000);
}
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- if (mRecorder != null) {
- stopRecording(false);
- }
- }
-
- private boolean startRecording() {
- mRecorder = new MediaRecorder();
- mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
- mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
- mRecorder.setAudioEncodingBitRate(48000);
- } else {
- mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
- mRecorder.setAudioEncodingBitRate(48000);
- }
- mRecorder.setAudioSamplingRate(48000);
- mOutputFile = getOutputFile();
- mOutputFile.getParentFile().mkdirs();
- mRecorder.setOutputFile(mOutputFile.getAbsolutePath());
-
- try {
- mRecorder.prepare();
- mRecorder.start();
- mStartTime = SystemClock.elapsedRealtime();
- mHandler.postDelayed(mTickExecutor, 100);
- Log.d(Config.LOGTAG,"started recording to "+mOutputFile.getAbsolutePath());
+ mRecorder.setAudioSamplingRate(48000);
+ mOutputFile = getOutputFile();
+ mOutputFile.getParentFile().mkdirs();
+ mRecorder.setOutputFile(mOutputFile.getAbsolutePath());
+
+ try {
+ mRecorder.prepare();
+ mRecorder.start();
+ mStartTime = SystemClock.elapsedRealtime();
+ mHandler.postDelayed(mTickExecutor, 100);
+ Log.d(Config.LOGTAG, "started recording to " + mOutputFile.getAbsolutePath());
return true;
- } catch (Exception e) {
- Log.e(Config.LOGTAG, "prepare() failed "+e.getMessage());
+ } catch (Exception e) {
+ Log.e(Config.LOGTAG, "prepare() failed " + e.getMessage());
return false;
- }
- }
-
- protected void stopRecording(boolean saveFile) {
- mRecorder.stop();
- mRecorder.release();
- mRecorder = null;
- mStartTime = 0;
- mHandler.removeCallbacks(mTickExecutor);
- if (!saveFile && mOutputFile != null) {
- mOutputFile.delete();
- }
- }
-
- private File getOutputFile() {
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
- return new File(FileBackend.getConversationsAudioDirectory() + "/"
- + dateFormat.format(new Date())
- + ".m4a");
- }
-
- private void tick() {
- long time = (mStartTime < 0) ? 0 : (SystemClock.elapsedRealtime() - mStartTime);
- int minutes = (int) (time / 60000);
- int seconds = (int) (time / 1000) % 60;
- int milliseconds = (int) (time / 100) % 10;
- mTimerTextView.setText(minutes+":"+(seconds < 10 ? "0"+seconds : seconds)+"."+milliseconds);
- if (mRecorder != null) {
- amplitudes[i] = mRecorder.getMaxAmplitude();
- //Log.d(Config.LOGTAG,"amplitude: "+(amplitudes[i] * 100 / 32767));
- if (i >= amplitudes.length -1) {
- i = 0;
- } else {
- ++i;
- }
- }
- }
-
- @Override
- public void onClick(View view) {
- switch (view.getId()) {
- case R.id.cancel_button:
- stopRecording(false);
- setResult(RESULT_CANCELED);
- finish();
- break;
- case R.id.share_button:
- stopRecording(true);
- Uri uri = Uri.parse("file://"+mOutputFile.getAbsolutePath());
- Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
- scanIntent.setData(uri);
- sendBroadcast(scanIntent);
- setResult(Activity.RESULT_OK, new Intent().setData(uri));
- finish();
- break;
- }
- }
+ }
+ }
+
+ protected void stopRecording(boolean saveFile) {
+ mRecorder.stop();
+ mRecorder.release();
+ mRecorder = null;
+ mStartTime = 0;
+ mHandler.removeCallbacks(mTickExecutor);
+ if (!saveFile && mOutputFile != null) {
+ mOutputFile.delete();
+ }
+ }
+
+ private File getOutputFile() {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
+ return new File(FileBackend.getConversationsAudioDirectory() + "/"
+ + dateFormat.format(new Date())
+ + ".m4a");
+ }
+
+ private void tick() {
+ long time = (mStartTime < 0) ? 0 : (SystemClock.elapsedRealtime() - mStartTime);
+ int minutes = (int) (time / 60000);
+ int seconds = (int) (time / 1000) % 60;
+ int milliseconds = (int) (time / 100) % 10;
+ mTimerTextView.setText(minutes + ":" + (seconds < 10 ? "0" + seconds : seconds) + "." + milliseconds);
+ if (mRecorder != null) {
+ amplitudes[i] = mRecorder.getMaxAmplitude();
+ //Log.d(Config.LOGTAG,"amplitude: "+(amplitudes[i] * 100 / 32767));
+ if (i >= amplitudes.length - 1) {
+ i = 0;
+ } else {
+ ++i;
+ }
+ }
+ }
+
+ @Override
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.cancel_button:
+ stopRecording(false);
+ setResult(RESULT_CANCELED);
+ finish();
+ break;
+ case R.id.share_button:
+ stopRecording(true);
+ Uri uri = Uri.parse("file://" + mOutputFile.getAbsolutePath());
+ Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ scanIntent.setData(uri);
+ sendBroadcast(scanIntent);
+ setResult(Activity.RESULT_OK, new Intent().setData(uri));
+ finish();
+ break;
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/SetPresenceActivity.java b/src/main/java/de/pixart/messenger/ui/SetPresenceActivity.java
index 7214e560b..b4e88e5cc 100644
--- a/src/main/java/de/pixart/messenger/ui/SetPresenceActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/SetPresenceActivity.java
@@ -29,204 +29,204 @@ import de.pixart.messenger.utils.UIHelper;
public class SetPresenceActivity extends XmppActivity implements View.OnClickListener {
- //data
- protected Account mAccount;
- private List<PresenceTemplate> mTemplates;
-
- //UI Elements
- protected ScrollView mScrollView;
- protected EditText mStatusMessage;
- protected Spinner mShowSpinner;
- protected CheckBox mAllAccounts;
- protected LinearLayout mTemplatesView;
- private Pair<Integer, Intent> mPostponedActivityResult;
-
- private Runnable onPresenceChanged = new Runnable() {
- @Override
- public void run() {
- finish();
- }
- };
-
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_set_presence);
- mScrollView = (ScrollView) findViewById(R.id.scroll_view);
- mShowSpinner = (Spinner) findViewById(R.id.presence_show);
- ArrayAdapter adapter = ArrayAdapter.createFromResource(this,
- R.array.presence_show_options,
- R.layout.simple_list_item);
- mShowSpinner.setAdapter(adapter);
- mShowSpinner.setSelection(1);
- mStatusMessage = (EditText) findViewById(R.id.presence_status_message);
- mAllAccounts = (CheckBox) findViewById(R.id.all_accounts);
- mTemplatesView = (LinearLayout) findViewById(R.id.templates);
- final Button changePresence = (Button) findViewById(R.id.change_presence);
- changePresence.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- executeChangePresence();
- }
- });
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.change_presence, menu);
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- if (item.getItemId() == R.id.action_account_details) {
- if (mAccount != null) {
- switchToAccount(mAccount);
- }
- return true;
- } else {
- return super.onOptionsItemSelected(item);
- }
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == RESULT_OK) {
- if (xmppConnectionServiceBound && mAccount != null) {
- if (requestCode == REQUEST_ANNOUNCE_PGP) {
- announcePgp(mAccount, null, onPresenceChanged);
- }
- this.mPostponedActivityResult = null;
- } else {
- this.mPostponedActivityResult = new Pair<>(requestCode, data);
- }
- }
- }
-
- private void executeChangePresence() {
- Presence.Status status = getStatusFromSpinner();
- boolean allAccounts = mAllAccounts.isChecked();
- String statusMessage = mStatusMessage.getText().toString().trim();
- if (allAccounts && noAccountUsesPgp()) {
- xmppConnectionService.changeStatus(status, statusMessage);
- finish();
- } else if (mAccount != null) {
- if (mAccount.getPgpId() == 0 || !hasPgp()) {
- xmppConnectionService.changeStatus(mAccount, status, statusMessage, true);
- finish();
- } else {
- xmppConnectionService.changeStatus(mAccount, status, statusMessage, false);
- announcePgp(mAccount, null, onPresenceChanged);
- }
- }
- }
-
- private Presence.Status getStatusFromSpinner() {
- switch (mShowSpinner.getSelectedItemPosition()) {
- case 0:
- return Presence.Status.CHAT;
- case 2:
- return Presence.Status.AWAY;
- case 3:
- return Presence.Status.XA;
- case 4:
- return Presence.Status.DND;
- default:
- return Presence.Status.ONLINE;
- }
- }
-
- private void setStatusInSpinner(Presence.Status status) {
- switch(status) {
- case AWAY:
- mShowSpinner.setSelection(2);
- break;
- case XA:
- mShowSpinner.setSelection(3);
- break;
- case CHAT:
- mShowSpinner.setSelection(0);
- break;
- case DND:
- mShowSpinner.setSelection(4);
- break;
- default:
- mShowSpinner.setSelection(1);
- break;
- }
- }
-
- @Override
- protected void refreshUiReal() {
-
- }
-
- @Override
- void onBackendConnected() {
- mAccount = extractAccount(getIntent());
- if (mAccount != null) {
- setStatusInSpinner(mAccount.getPresenceStatus());
- String message = mAccount.getPresenceStatusMessage();
- if (mStatusMessage.getText().length() == 0 && message != null) {
- mStatusMessage.append(message);
- }
- mTemplates = xmppConnectionService.getPresenceTemplates(mAccount);
- if (this.mPostponedActivityResult != null) {
- this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
- }
- boolean e = noAccountUsesPgp();
- mAllAccounts.setEnabled(e);
- mAllAccounts.setTextColor(e ? getPrimaryTextColor() : getSecondaryTextColor());
- }
- redrawTemplates();
- }
-
- private void redrawTemplates() {
- if (mTemplates == null || mTemplates.size() == 0) {
- mTemplatesView.setVisibility(View.GONE);
- } else {
- mTemplatesView.removeAllViews();
- mTemplatesView.setVisibility(View.VISIBLE);
- LayoutInflater inflater = getLayoutInflater();
- for (PresenceTemplate template : mTemplates) {
- View templateLayout = inflater.inflate(R.layout.presence_template, mTemplatesView, false);
- templateLayout.setTag(template);
- setListItemBackgroundOnView(templateLayout);
- templateLayout.setOnClickListener(this);
- TextView message = (TextView) templateLayout.findViewById(R.id.presence_status_message);
- TextView status = (TextView) templateLayout.findViewById(R.id.status);
- ImageButton button = (ImageButton) templateLayout.findViewById(R.id.delete_button);
- button.setTag(template);
- button.setOnClickListener(this);
- ListItem.Tag tag = UIHelper.getTagForStatus(this, template.getStatus());
- status.setText(tag.getName());
- status.setBackgroundColor(tag.getColor());
- message.setText(template.getStatusMessage());
- mTemplatesView.addView(templateLayout);
- }
- }
- }
-
- @Override
- public void onClick(View v) {
- PresenceTemplate template = (PresenceTemplate) v.getTag();
- if (template == null) {
- return;
- }
- if (v.getId() == R.id.presence_template) {
- setStatusInSpinner(template.getStatus());
- mStatusMessage.getEditableText().clear();
- mStatusMessage.getEditableText().append(template.getStatusMessage());
- new Handler().post(new Runnable() {
- @Override
- public void run() {
- mScrollView.smoothScrollTo(0,0);
- }
- });
- } else if (v.getId() == R.id.delete_button) {
- xmppConnectionService.databaseBackend.deletePresenceTemplate(template);
- mTemplates.remove(template);
- redrawTemplates();
- }
- }
+ //data
+ protected Account mAccount;
+ private List<PresenceTemplate> mTemplates;
+
+ //UI Elements
+ protected ScrollView mScrollView;
+ protected EditText mStatusMessage;
+ protected Spinner mShowSpinner;
+ protected CheckBox mAllAccounts;
+ protected LinearLayout mTemplatesView;
+ private Pair<Integer, Intent> mPostponedActivityResult;
+
+ private Runnable onPresenceChanged = new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ }
+ };
+
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_set_presence);
+ mScrollView = (ScrollView) findViewById(R.id.scroll_view);
+ mShowSpinner = (Spinner) findViewById(R.id.presence_show);
+ ArrayAdapter adapter = ArrayAdapter.createFromResource(this,
+ R.array.presence_show_options,
+ R.layout.simple_list_item);
+ mShowSpinner.setAdapter(adapter);
+ mShowSpinner.setSelection(1);
+ mStatusMessage = (EditText) findViewById(R.id.presence_status_message);
+ mAllAccounts = (CheckBox) findViewById(R.id.all_accounts);
+ mTemplatesView = (LinearLayout) findViewById(R.id.templates);
+ final Button changePresence = (Button) findViewById(R.id.change_presence);
+ changePresence.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ executeChangePresence();
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.change_presence, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ if (item.getItemId() == R.id.action_account_details) {
+ if (mAccount != null) {
+ switchToAccount(mAccount);
+ }
+ return true;
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == RESULT_OK) {
+ if (xmppConnectionServiceBound && mAccount != null) {
+ if (requestCode == REQUEST_ANNOUNCE_PGP) {
+ announcePgp(mAccount, null, onPresenceChanged);
+ }
+ this.mPostponedActivityResult = null;
+ } else {
+ this.mPostponedActivityResult = new Pair<>(requestCode, data);
+ }
+ }
+ }
+
+ private void executeChangePresence() {
+ Presence.Status status = getStatusFromSpinner();
+ boolean allAccounts = mAllAccounts.isChecked();
+ String statusMessage = mStatusMessage.getText().toString().trim();
+ if (allAccounts && noAccountUsesPgp()) {
+ xmppConnectionService.changeStatus(status, statusMessage);
+ finish();
+ } else if (mAccount != null) {
+ if (mAccount.getPgpId() == 0 || !hasPgp()) {
+ xmppConnectionService.changeStatus(mAccount, status, statusMessage, true);
+ finish();
+ } else {
+ xmppConnectionService.changeStatus(mAccount, status, statusMessage, false);
+ announcePgp(mAccount, null, onPresenceChanged);
+ }
+ }
+ }
+
+ private Presence.Status getStatusFromSpinner() {
+ switch (mShowSpinner.getSelectedItemPosition()) {
+ case 0:
+ return Presence.Status.CHAT;
+ case 2:
+ return Presence.Status.AWAY;
+ case 3:
+ return Presence.Status.XA;
+ case 4:
+ return Presence.Status.DND;
+ default:
+ return Presence.Status.ONLINE;
+ }
+ }
+
+ private void setStatusInSpinner(Presence.Status status) {
+ switch (status) {
+ case AWAY:
+ mShowSpinner.setSelection(2);
+ break;
+ case XA:
+ mShowSpinner.setSelection(3);
+ break;
+ case CHAT:
+ mShowSpinner.setSelection(0);
+ break;
+ case DND:
+ mShowSpinner.setSelection(4);
+ break;
+ default:
+ mShowSpinner.setSelection(1);
+ break;
+ }
+ }
+
+ @Override
+ protected void refreshUiReal() {
+
+ }
+
+ @Override
+ void onBackendConnected() {
+ mAccount = extractAccount(getIntent());
+ if (mAccount != null) {
+ setStatusInSpinner(mAccount.getPresenceStatus());
+ String message = mAccount.getPresenceStatusMessage();
+ if (mStatusMessage.getText().length() == 0 && message != null) {
+ mStatusMessage.append(message);
+ }
+ mTemplates = xmppConnectionService.getPresenceTemplates(mAccount);
+ if (this.mPostponedActivityResult != null) {
+ this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
+ }
+ boolean e = noAccountUsesPgp();
+ mAllAccounts.setEnabled(e);
+ mAllAccounts.setTextColor(e ? getPrimaryTextColor() : getSecondaryTextColor());
+ }
+ redrawTemplates();
+ }
+
+ private void redrawTemplates() {
+ if (mTemplates == null || mTemplates.size() == 0) {
+ mTemplatesView.setVisibility(View.GONE);
+ } else {
+ mTemplatesView.removeAllViews();
+ mTemplatesView.setVisibility(View.VISIBLE);
+ LayoutInflater inflater = getLayoutInflater();
+ for (PresenceTemplate template : mTemplates) {
+ View templateLayout = inflater.inflate(R.layout.presence_template, mTemplatesView, false);
+ templateLayout.setTag(template);
+ setListItemBackgroundOnView(templateLayout);
+ templateLayout.setOnClickListener(this);
+ TextView message = (TextView) templateLayout.findViewById(R.id.presence_status_message);
+ TextView status = (TextView) templateLayout.findViewById(R.id.status);
+ ImageButton button = (ImageButton) templateLayout.findViewById(R.id.delete_button);
+ button.setTag(template);
+ button.setOnClickListener(this);
+ ListItem.Tag tag = UIHelper.getTagForStatus(this, template.getStatus());
+ status.setText(tag.getName());
+ status.setBackgroundColor(tag.getColor());
+ message.setText(template.getStatusMessage());
+ mTemplatesView.addView(templateLayout);
+ }
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ PresenceTemplate template = (PresenceTemplate) v.getTag();
+ if (template == null) {
+ return;
+ }
+ if (v.getId() == R.id.presence_template) {
+ setStatusInSpinner(template.getStatus());
+ mStatusMessage.getEditableText().clear();
+ mStatusMessage.getEditableText().append(template.getStatusMessage());
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ mScrollView.smoothScrollTo(0, 0);
+ }
+ });
+ } else if (v.getId() == R.id.delete_button) {
+ xmppConnectionService.databaseBackend.deletePresenceTemplate(template);
+ mTemplates.remove(template);
+ redrawTemplates();
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java
index 05b14d215..51c41c2ea 100644
--- a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java
@@ -33,269 +33,269 @@ import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
public class SettingsActivity extends XmppActivity implements
- OnSharedPreferenceChangeListener {
+ OnSharedPreferenceChangeListener {
- public static final int REQUEST_WRITE_LOGS = 0xbf8701;
- private SettingsFragment mSettingsFragment;
+ public static final int REQUEST_WRITE_LOGS = 0xbf8701;
+ private SettingsFragment mSettingsFragment;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- FragmentManager fm = getFragmentManager();
- mSettingsFragment = (SettingsFragment) fm.findFragmentById(android.R.id.content);
- if (mSettingsFragment == null || !mSettingsFragment.getClass().equals(SettingsFragment.class)) {
- mSettingsFragment = new SettingsFragment();
- fm.beginTransaction().replace(android.R.id.content, mSettingsFragment).commit();
- }
- }
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ FragmentManager fm = getFragmentManager();
+ mSettingsFragment = (SettingsFragment) fm.findFragmentById(android.R.id.content);
+ if (mSettingsFragment == null || !mSettingsFragment.getClass().equals(SettingsFragment.class)) {
+ mSettingsFragment = new SettingsFragment();
+ fm.beginTransaction().replace(android.R.id.content, mSettingsFragment).commit();
+ }
+ }
- @Override
- void onBackendConnected() {
+ @Override
+ void onBackendConnected() {
- }
+ }
- @Override
- public void onStart() {
- super.onStart();
- PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);
- ListPreference resources = (ListPreference) mSettingsFragment.findPreference("resource");
- if (resources != null) {
- ArrayList<CharSequence> entries = new ArrayList<>(Arrays.asList(resources.getEntries()));
- if (!entries.contains(Build.MODEL)) {
- entries.add(0, Build.MODEL);
- resources.setEntries(entries.toArray(new CharSequence[entries.size()]));
- resources.setEntryValues(entries.toArray(new CharSequence[entries.size()]));
- }
- }
+ @Override
+ public void onStart() {
+ super.onStart();
+ PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);
+ ListPreference resources = (ListPreference) mSettingsFragment.findPreference("resource");
+ if (resources != null) {
+ ArrayList<CharSequence> entries = new ArrayList<>(Arrays.asList(resources.getEntries()));
+ if (!entries.contains(Build.MODEL)) {
+ entries.add(0, Build.MODEL);
+ resources.setEntries(entries.toArray(new CharSequence[entries.size()]));
+ resources.setEntryValues(entries.toArray(new CharSequence[entries.size()]));
+ }
+ }
- if (Config.FORCE_ORBOT) {
- PreferenceCategory connectionOptions = (PreferenceCategory) mSettingsFragment.findPreference("connection_options");
- PreferenceScreen expert = (PreferenceScreen) mSettingsFragment.findPreference("expert");
- if (connectionOptions != null) {
- expert.removePreference(connectionOptions);
- }
- }
+ 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
- public boolean onPreferenceClick(Preference preference) {
- final MemorizingTrustManager mtm = xmppConnectionService.getMemorizingTrustManager();
- final ArrayList<String> aliases = Collections.list(mtm.getCertificates());
- if (aliases.size() == 0) {
- displayToast(getString(R.string.toast_no_trusted_certs));
- return true;
- }
+ final Preference removeCertsPreference = mSettingsFragment.findPreference("remove_trusted_certificates");
+ removeCertsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ final MemorizingTrustManager mtm = xmppConnectionService.getMemorizingTrustManager();
+ final ArrayList<String> aliases = Collections.list(mtm.getCertificates());
+ if (aliases.size() == 0) {
+ displayToast(getString(R.string.toast_no_trusted_certs));
+ return true;
+ }
final ArrayList selectedItems = new ArrayList<>();
- final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(SettingsActivity.this);
- dialogBuilder.setTitle(getResources().getString(R.string.dialog_manage_certs_title));
- dialogBuilder.setMultiChoiceItems(aliases.toArray(new CharSequence[aliases.size()]), null,
- new DialogInterface.OnMultiChoiceClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int indexSelected,
- boolean isChecked) {
- if (isChecked) {
- selectedItems.add(indexSelected);
- } else if (selectedItems.contains(indexSelected)) {
- selectedItems.remove(Integer.valueOf(indexSelected));
- }
- if (selectedItems.size() > 0)
- ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
- else {
- ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
- }
- }
- });
+ final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(SettingsActivity.this);
+ dialogBuilder.setTitle(getResources().getString(R.string.dialog_manage_certs_title));
+ dialogBuilder.setMultiChoiceItems(aliases.toArray(new CharSequence[aliases.size()]), null,
+ new DialogInterface.OnMultiChoiceClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int indexSelected,
+ boolean isChecked) {
+ if (isChecked) {
+ selectedItems.add(indexSelected);
+ } else if (selectedItems.contains(indexSelected)) {
+ selectedItems.remove(Integer.valueOf(indexSelected));
+ }
+ if (selectedItems.size() > 0)
+ ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
+ else {
+ ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
+ }
+ }
+ });
- 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() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
+ final Preference exportLogsPreference = mSettingsFragment.findPreference("export_logs");
+ exportLogsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
if (hasStoragePermission(REQUEST_WRITE_LOGS)) {
startExport();
}
- return true;
- }
- });
+ return true;
+ }
+ });
final Preference deleteOmemoPreference = mSettingsFragment.findPreference("delete_omemo_identities");
- deleteOmemoPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- deleteOmemoIdentities();
- return true;
- }
- });
- }
+ deleteOmemoPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ deleteOmemoIdentities();
+ return true;
+ }
+ });
+ }
- private void deleteOmemoIdentities() {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.pref_delete_omemo_identities);
- final List<CharSequence> accounts = new ArrayList<>();
- for(Account account : xmppConnectionService.getAccounts()) {
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- accounts.add(account.getJid().toBareJid().toString());
- }
- }
- final boolean[] checkedItems = new boolean[accounts.size()];
- builder.setMultiChoiceItems(accounts.toArray(new CharSequence[accounts.size()]), checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which, boolean isChecked) {
- checkedItems[which] = isChecked;
- final AlertDialog alertDialog = (AlertDialog) dialog;
- for(boolean item : checkedItems) {
- if (item) {
- alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
- return;
- }
- }
- alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
- }
- });
- builder.setNegativeButton(R.string.cancel,null);
- builder.setPositiveButton(R.string.delete_selected_keys, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- for(int i = 0; i < checkedItems.length; ++i) {
- if (checkedItems[i]) {
- try {
- Jid jid = Jid.fromString(accounts.get(i).toString());
- Account account = xmppConnectionService.findAccountByJid(jid);
- if (account != null) {
- account.getAxolotlService().regenerateKeys(true);
- }
- } catch (InvalidJidException e) {
- //
- }
+ private void deleteOmemoIdentities() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.pref_delete_omemo_identities);
+ final List<CharSequence> accounts = new ArrayList<>();
+ for (Account account : xmppConnectionService.getAccounts()) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ accounts.add(account.getJid().toBareJid().toString());
+ }
+ }
+ final boolean[] checkedItems = new boolean[accounts.size()];
+ builder.setMultiChoiceItems(accounts.toArray(new CharSequence[accounts.size()]), checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+ checkedItems[which] = isChecked;
+ final AlertDialog alertDialog = (AlertDialog) dialog;
+ for (boolean item : checkedItems) {
+ if (item) {
+ alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
+ return;
+ }
+ }
+ alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
+ }
+ });
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.delete_selected_keys, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ for (int i = 0; i < checkedItems.length; ++i) {
+ if (checkedItems[i]) {
+ try {
+ Jid jid = Jid.fromString(accounts.get(i).toString());
+ Account account = xmppConnectionService.findAccountByJid(jid);
+ if (account != null) {
+ account.getAxolotlService().regenerateKeys(true);
+ }
+ } catch (InvalidJidException e) {
+ //
+ }
- }
- }
- }
- });
- AlertDialog dialog = builder.create();
- dialog.show();
- dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
- }
+ }
+ }
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
+ }
- @Override
- public void onStop() {
- super.onStop();
- PreferenceManager.getDefaultSharedPreferences(this)
- .unregisterOnSharedPreferenceChangeListener(this);
- }
+ @Override
+ public void onStop() {
+ super.onStop();
+ PreferenceManager.getDefaultSharedPreferences(this)
+ .unregisterOnSharedPreferenceChangeListener(this);
+ }
- @Override
- public void onSharedPreferenceChanged(SharedPreferences preferences, String name) {
- final List<String> resendPresence = Arrays.asList(
- "confirm_messages",
- "xa_on_silent_mode",
- "away_when_screen_off",
- "allow_message_correction",
- "treat_vibrate_as_silent",
- "manually_change_presence",
- "last_activity");
- if (name.equals("resource")) {
- String resource = preferences.getString("resource", "mobile")
- .toLowerCase(Locale.US);
- if (xmppConnectionServiceBound) {
- for (Account account : xmppConnectionService.getAccounts()) {
- if (account.setResource(resource)) {
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- XmppConnection connection = account.getXmppConnection();
- if (connection != null) {
- connection.resetStreamId();
- }
- xmppConnectionService.reconnectAccountInBackground(account);
- }
- }
- }
- }
- } else if (resendPresence.contains(name)) {
- if (xmppConnectionServiceBound) {
- if (name.equals("away_when_screen_off")
- || name.equals("manually_change_presence")) {
- xmppConnectionService.toggleScreenEventReceiver();
- }
- if (name.equals("manually_change_presence") && !noAccountUsesPgp()) {
- Toast.makeText(this, R.string.republish_pgp_keys, Toast.LENGTH_LONG).show();
- }
- xmppConnectionService.refreshAllPresences();
- }
- } else if (name.equals("dont_trust_system_cas")) {
- xmppConnectionService.updateMemorizingTrustmanager();
- reconnectAccounts();
- } else if (name.equals("use_tor")) {
- reconnectAccounts();
- }
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences preferences, String name) {
+ final List<String> resendPresence = Arrays.asList(
+ "confirm_messages",
+ "xa_on_silent_mode",
+ "away_when_screen_off",
+ "allow_message_correction",
+ "treat_vibrate_as_silent",
+ "manually_change_presence",
+ "last_activity");
+ if (name.equals("resource")) {
+ String resource = preferences.getString("resource", "mobile")
+ .toLowerCase(Locale.US);
+ if (xmppConnectionServiceBound) {
+ for (Account account : xmppConnectionService.getAccounts()) {
+ if (account.setResource(resource)) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ connection.resetStreamId();
+ }
+ xmppConnectionService.reconnectAccountInBackground(account);
+ }
+ }
+ }
+ }
+ } else if (resendPresence.contains(name)) {
+ if (xmppConnectionServiceBound) {
+ if (name.equals("away_when_screen_off")
+ || name.equals("manually_change_presence")) {
+ xmppConnectionService.toggleScreenEventReceiver();
+ }
+ if (name.equals("manually_change_presence") && !noAccountUsesPgp()) {
+ Toast.makeText(this, R.string.republish_pgp_keys, Toast.LENGTH_LONG).show();
+ }
+ xmppConnectionService.refreshAllPresences();
+ }
+ } else if (name.equals("dont_trust_system_cas")) {
+ xmppConnectionService.updateMemorizingTrustmanager();
+ reconnectAccounts();
+ } else if (name.equals("use_tor")) {
+ reconnectAccounts();
+ }
- }
+ }
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
- if (grantResults.length > 0)
- if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- if (requestCode == REQUEST_WRITE_LOGS) {
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+ if (grantResults.length > 0)
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ if (requestCode == REQUEST_WRITE_LOGS) {
startExport();
- }
- } else {
- Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show();
- }
- }
+ }
+ } else {
+ Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show();
+ }
+ }
private void startExport() {
startService(new Intent(getApplicationContext(), ExportLogsService.class));
}
private void displayToast(final String msg) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(SettingsActivity.this, msg, Toast.LENGTH_LONG).show();
- }
- });
- }
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(SettingsActivity.this, msg, Toast.LENGTH_LONG).show();
+ }
+ });
+ }
- private void reconnectAccounts() {
- for (Account account : xmppConnectionService.getAccounts()) {
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- xmppConnectionService.reconnectAccountInBackground(account);
- }
- }
- }
+ private void reconnectAccounts() {
+ for (Account account : xmppConnectionService.getAccounts()) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ xmppConnectionService.reconnectAccountInBackground(account);
+ }
+ }
+ }
- public void refreshUiReal() {
- //nothing to do. This Activity doesn't implement any listeners
- }
+ public void refreshUiReal() {
+ //nothing to do. This Activity doesn't implement any listeners
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/SettingsFragment.java b/src/main/java/de/pixart/messenger/ui/SettingsFragment.java
index c9e6dc51a..119e79fb7 100644
--- a/src/main/java/de/pixart/messenger/ui/SettingsFragment.java
+++ b/src/main/java/de/pixart/messenger/ui/SettingsFragment.java
@@ -15,51 +15,51 @@ import de.pixart.messenger.R;
public class SettingsFragment extends PreferenceFragment {
- //http://stackoverflow.com/questions/16374820/action-bar-home-button-not-functional-with-nested-preferencescreen/16800527#16800527
- private void initializeActionBar(PreferenceScreen preferenceScreen) {
- final Dialog dialog = preferenceScreen.getDialog();
+ //http://stackoverflow.com/questions/16374820/action-bar-home-button-not-functional-with-nested-preferencescreen/16800527#16800527
+ private void initializeActionBar(PreferenceScreen preferenceScreen) {
+ final Dialog dialog = preferenceScreen.getDialog();
- if (dialog != null) {
- View homeBtn = dialog.findViewById(android.R.id.home);
+ if (dialog != null) {
+ View homeBtn = dialog.findViewById(android.R.id.home);
- if (homeBtn != null) {
- View.OnClickListener dismissDialogClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- dialog.dismiss();
- }
- };
+ if (homeBtn != null) {
+ View.OnClickListener dismissDialogClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dialog.dismiss();
+ }
+ };
- ViewParent homeBtnContainer = homeBtn.getParent();
+ ViewParent homeBtnContainer = homeBtn.getParent();
- if (homeBtnContainer instanceof FrameLayout) {
- ViewGroup containerParent = (ViewGroup) homeBtnContainer.getParent();
- if (containerParent instanceof LinearLayout) {
- ((LinearLayout) containerParent).setOnClickListener(dismissDialogClickListener);
- } else {
- ((FrameLayout) homeBtnContainer).setOnClickListener(dismissDialogClickListener);
- }
- } else {
- homeBtn.setOnClickListener(dismissDialogClickListener);
- }
- }
- }
- }
+ if (homeBtnContainer instanceof FrameLayout) {
+ ViewGroup containerParent = (ViewGroup) homeBtnContainer.getParent();
+ if (containerParent instanceof LinearLayout) {
+ ((LinearLayout) containerParent).setOnClickListener(dismissDialogClickListener);
+ } else {
+ ((FrameLayout) homeBtnContainer).setOnClickListener(dismissDialogClickListener);
+ }
+ } else {
+ homeBtn.setOnClickListener(dismissDialogClickListener);
+ }
+ }
+ }
+ }
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
- // Load the preferences from an XML resource
- addPreferencesFromResource(R.xml.preferences);
- }
+ // Load the preferences from an XML resource
+ addPreferencesFromResource(R.xml.preferences);
+ }
- @Override
- public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
- super.onPreferenceTreeClick(preferenceScreen, preference);
- if (preference instanceof PreferenceScreen) {
- initializeActionBar((PreferenceScreen) preference);
- }
- return false;
- }
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+ super.onPreferenceTreeClick(preferenceScreen, preference);
+ if (preference instanceof PreferenceScreen) {
+ initializeActionBar((PreferenceScreen) preference);
+ }
+ return false;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ShareLocationActivity.java b/src/main/java/de/pixart/messenger/ui/ShareLocationActivity.java
index 2bb9dfd9d..e742b6590 100644
--- a/src/main/java/de/pixart/messenger/ui/ShareLocationActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ShareLocationActivity.java
@@ -160,62 +160,62 @@ public class ShareLocationActivity extends Activity implements OnMapReadyCallbac
}
- @Override
- public void onConnectionSuspended(int i) {
+ @Override
+ public void onConnectionSuspended(int i) {
- }
+ }
- @Override
- public void onConnectionFailed(ConnectionResult connectionResult) {
+ @Override
+ public void onConnectionFailed(ConnectionResult connectionResult) {
- }
+ }
- @Override
- public void onLocationChanged(Location location) {
+ @Override
+ public void onLocationChanged(Location location) {
double longitude = location.getLongitude();
double latitude = location.getLatitude();
- if (this.mLastLocation == null) {
- centerOnLocation(new LatLng(location.getLatitude(), location.getLongitude()));
- this.mShareButton.setEnabled(true);
- this.mShareButton.setTextColor(0xde000000);
- this.mShareButton.setText(R.string.share);
- }
- this.mLastLocation = location;
+ if (this.mLastLocation == null) {
+ centerOnLocation(new LatLng(location.getLatitude(), location.getLongitude()));
+ this.mShareButton.setEnabled(true);
+ this.mShareButton.setTextColor(0xde000000);
+ this.mShareButton.setText(R.string.share);
+ }
+ this.mLastLocation = location;
if (latitude != 0 && longitude != 0) {
Double[] lat_long = new Double[]{latitude, longitude};
new ReverseGeocodingTask(getBaseContext()).execute(lat_long);
}
- }
-
- @TargetApi(Build.VERSION_CODES.KITKAT)
- private boolean isLocationEnabledKitkat() {
- try {
- int locationMode = Settings.Secure.getInt(getContentResolver(), Settings.Secure.LOCATION_MODE);
- return locationMode != Settings.Secure.LOCATION_MODE_OFF;
- } catch (Settings.SettingNotFoundException e) {
- return false;
- }
- }
-
- @SuppressWarnings("deprecation")
- private boolean isLocationEnabledLegacy() {
- String locationProviders = Settings.Secure.getString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
- return !TextUtils.isEmpty(locationProviders);
- }
-
- private boolean isLocationEnabled() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
- return isLocationEnabledKitkat();
- }else{
- return isLocationEnabledLegacy();
- }
- }
+ }
+
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ private boolean isLocationEnabledKitkat() {
+ try {
+ int locationMode = Settings.Secure.getInt(getContentResolver(), Settings.Secure.LOCATION_MODE);
+ return locationMode != Settings.Secure.LOCATION_MODE_OFF;
+ } catch (Settings.SettingNotFoundException e) {
+ return false;
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private boolean isLocationEnabledLegacy() {
+ String locationProviders = Settings.Secure.getString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
+ return !TextUtils.isEmpty(locationProviders);
+ }
+
+ private boolean isLocationEnabled() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ return isLocationEnabledKitkat();
+ } else {
+ return isLocationEnabledLegacy();
+ }
+ }
private class ReverseGeocodingTask extends AsyncTask<Double, Void, String> {
Context mContext;
- public ReverseGeocodingTask(Context context){
+ public ReverseGeocodingTask(Context context) {
super();
mContext = context;
}
@@ -228,10 +228,10 @@ public class ShareLocationActivity extends Activity implements OnMapReadyCallbac
double longitude = params[1];
List<Address> addresses = null;
- String address="";
+ String address = "";
try {
- addresses = geocoder.getFromLocation(latitude, longitude,1);
+ addresses = geocoder.getFromLocation(latitude, longitude, 1);
} catch (IOException e) {
e.printStackTrace();
}
@@ -244,7 +244,7 @@ public class ShareLocationActivity extends Activity implements OnMapReadyCallbac
strAddress.append(Address.getAddressLine(i)).append("\n");
}
address = strAddress.toString();
- address = address.substring(0, address.length()-1); //trim last \n
+ address = address.substring(0, address.length() - 1); //trim last \n
}
return address;
@@ -256,7 +256,7 @@ public class ShareLocationActivity extends Activity implements OnMapReadyCallbac
// Setting address of the touched Position
mLocationInfo.setVisibility(View.VISIBLE);
mSnackbarLocation.setText(address);
- Log.d(Config.LOGTAG,"Location: Address = "+ address);
+ Log.d(Config.LOGTAG, "Location: Address = " + address);
}
}
}
diff --git a/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java b/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java
index d9b0695ef..5df34457b 100644
--- a/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java
@@ -35,214 +35,214 @@ import static java.lang.String.format;
public class ShareWithActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate {
- private boolean mReturnToPrevious = false;
+ private boolean mReturnToPrevious = false;
private static final String STATE_SHARING_IS_RUNNING = "state_sharing_is_running";
static boolean ContactChosen = false;
static boolean IntentReceived = false;
boolean SharingIsRunning = false;
- @Override
- public void onConversationUpdate() {
- refreshUi();
- }
+ @Override
+ public void onConversationUpdate() {
+ refreshUi();
+ }
- private class Share {
- public List<Uri> uris = new ArrayList<>();
- public boolean image;
+ private class Share {
+ public List<Uri> uris = new ArrayList<>();
+ public boolean image;
public boolean video;
- public String account;
- public String contact;
- public String text;
- public String uuid;
- public boolean multiple = false;
- }
-
- private Share share;
-
- private static final int REQUEST_START_NEW_CONVERSATION = 0x0501;
- private ListView mListView;
- private ConversationAdapter mAdapter;
- private List<Conversation> mConversations = new ArrayList<>();
- private Toast mToast;
- private AtomicInteger attachmentCounter = new AtomicInteger(0);
-
- private UiCallback<Message> attachFileCallback = new UiCallback<Message>() {
-
- @Override
- public void userInputRequried(PendingIntent pi, Message object) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void success(final Message message) {
- xmppConnectionService.sendMessage(message);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (attachmentCounter.decrementAndGet() <=0 ) {
- int resId;
- if (share.image && share.multiple) {
- resId = R.string.shared_images_with_x;
- } else if (share.image) {
- resId = R.string.shared_image_with_x;
+ public String account;
+ public String contact;
+ public String text;
+ public String uuid;
+ public boolean multiple = false;
+ }
+
+ private Share share;
+
+ private static final int REQUEST_START_NEW_CONVERSATION = 0x0501;
+ private ListView mListView;
+ private ConversationAdapter mAdapter;
+ private List<Conversation> mConversations = new ArrayList<>();
+ private Toast mToast;
+ private AtomicInteger attachmentCounter = new AtomicInteger(0);
+
+ private UiCallback<Message> attachFileCallback = new UiCallback<Message>() {
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Message object) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void success(final Message message) {
+ xmppConnectionService.sendMessage(message);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (attachmentCounter.decrementAndGet() <= 0) {
+ int resId;
+ if (share.image && share.multiple) {
+ resId = R.string.shared_images_with_x;
+ } else if (share.image) {
+ resId = R.string.shared_image_with_x;
} else if (share.video) {
resId = R.string.shared_video_with_x;
- } else {
- resId = R.string.shared_file_with_x;
- }
- replaceToast(getString(resId, message.getConversation().getName()));
- if (mReturnToPrevious) {
- finish();
- } else {
- switchToConversation(message.getConversation());
- }
- }
- }
- });
- }
-
- @Override
- public void error(final int errorCode, Message object) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- replaceToast(getString(errorCode));
- if (attachmentCounter.decrementAndGet() <=0 ) {
- finish();
- }
- }
- });
- }
- };
-
- protected void hideToast() {
- if (mToast != null) {
- mToast.cancel();
- }
- }
-
- protected void replaceToast(String msg) {
- hideToast();
- mToast = Toast.makeText(this, msg ,Toast.LENGTH_LONG);
- mToast.show();
- }
-
- protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == REQUEST_START_NEW_CONVERSATION
- && resultCode == RESULT_OK) {
- share.contact = data.getStringExtra("contact");
- share.account = data.getStringExtra(EXTRA_ACCOUNT);
- }
- if (xmppConnectionServiceBound
- && share != null
- && share.contact != null
- && share.account != null) {
- share();
- }
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if (getActionBar() != null) {
- getActionBar().setDisplayHomeAsUpEnabled(false);
- getActionBar().setHomeButtonEnabled(false);
- }
-
- setContentView(R.layout.share_with);
- setTitle(getString(R.string.title_activity_sharewith));
-
- mListView = (ListView) findViewById(R.id.choose_conversation_list);
- mAdapter = new ConversationAdapter(this, this.mConversations);
- mListView.setAdapter(mAdapter);
- mListView.setOnItemClickListener(new OnItemClickListener() {
-
- @Override
- public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
- share(mConversations.get(position));
- }
- });
+ } else {
+ resId = R.string.shared_file_with_x;
+ }
+ replaceToast(getString(resId, message.getConversation().getName()));
+ if (mReturnToPrevious) {
+ finish();
+ } else {
+ switchToConversation(message.getConversation());
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public void error(final int errorCode, Message object) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ replaceToast(getString(errorCode));
+ if (attachmentCounter.decrementAndGet() <= 0) {
+ finish();
+ }
+ }
+ });
+ }
+ };
+
+ protected void hideToast() {
+ if (mToast != null) {
+ mToast.cancel();
+ }
+ }
+
+ protected void replaceToast(String msg) {
+ hideToast();
+ mToast = Toast.makeText(this, msg, Toast.LENGTH_LONG);
+ mToast.show();
+ }
+
+ protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == REQUEST_START_NEW_CONVERSATION
+ && resultCode == RESULT_OK) {
+ share.contact = data.getStringExtra("contact");
+ share.account = data.getStringExtra(EXTRA_ACCOUNT);
+ }
+ if (xmppConnectionServiceBound
+ && share != null
+ && share.contact != null
+ && share.account != null) {
+ share();
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (getActionBar() != null) {
+ getActionBar().setDisplayHomeAsUpEnabled(false);
+ getActionBar().setHomeButtonEnabled(false);
+ }
+
+ setContentView(R.layout.share_with);
+ setTitle(getString(R.string.title_activity_sharewith));
+
+ mListView = (ListView) findViewById(R.id.choose_conversation_list);
+ mAdapter = new ConversationAdapter(this, this.mConversations);
+ mListView.setAdapter(mAdapter);
+ mListView.setOnItemClickListener(new OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
+ share(mConversations.get(position));
+ }
+ });
if (savedInstanceState != null) {
SharingIsRunning = savedInstanceState.getBoolean(STATE_SHARING_IS_RUNNING, false);
}
- if (!SharingIsRunning) {
+ if (!SharingIsRunning) {
Log.d(Config.LOGTAG, "ShareWithActivity onCreate: state restored");
this.share = new Share();
} else {
Log.d(Config.LOGTAG, "ShareWithActivity onCreate: shring running, finish()");
this.finish();
}
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.share_with, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_add:
- final Intent intent = new Intent(getApplicationContext(), ChooseContactActivity.class);
- startActivityForResult(intent, REQUEST_START_NEW_CONVERSATION);
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public void onStart() {
- super.onStart();
- Intent intent = getIntent();
- if (intent == null) {
- return;
- } else {
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.share_with, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_add:
+ final Intent intent = new Intent(getApplicationContext(), ChooseContactActivity.class);
+ startActivityForResult(intent, REQUEST_START_NEW_CONVERSATION);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ Intent intent = getIntent();
+ if (intent == null) {
+ return;
+ } else {
IntentReceived = true;
}
Log.d(Config.LOGTAG, "ShareWithActivity onStart() getIntent " + intent.toString());
- this.mReturnToPrevious = getPreferences().getBoolean("return_to_previous", false);
- final String type = intent.getType();
- final String action = intent.getAction();
- Log.d(Config.LOGTAG, "action: "+action+ ", type:"+type);
- share.uuid = intent.getStringExtra("uuid");
- if (Intent.ACTION_SEND.equals(action)) {
+ this.mReturnToPrevious = getPreferences().getBoolean("return_to_previous", false);
+ final String type = intent.getType();
+ final String action = intent.getAction();
+ Log.d(Config.LOGTAG, "action: " + action + ", type:" + type);
+ share.uuid = intent.getStringExtra("uuid");
+ if (Intent.ACTION_SEND.equals(action)) {
final String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
- final String text = intent.getStringExtra(Intent.EXTRA_TEXT);
- final Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
+ final String text = intent.getStringExtra(Intent.EXTRA_TEXT);
+ final Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
Log.d(Config.LOGTAG, "ShareWithActivity onStart() Uri: " + uri);
- if (type != null && uri != null && (text == null || !type.equals("text/plain"))) {
- this.share.uris.clear();
- this.share.uris.add(uri);
- this.share.image = type.startsWith("image/") || isImage(uri);
+ if (type != null && uri != null && (text == null || !type.equals("text/plain"))) {
+ this.share.uris.clear();
+ this.share.uris.add(uri);
+ this.share.image = type.startsWith("image/") || isImage(uri);
this.share.video = type.startsWith("video/") || isVideo(uri);
- } else {
+ } else {
if (subject != null) {
this.share.text = format("[%s]%n%s", subject, text);
} else {
this.share.text = text;
}
- }
- } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
- this.share.image = type != null && type.startsWith("image/");
- if (!this.share.image) {
- return;
- }
- this.share.uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
- }
- if (xmppConnectionServiceBound) {
- if (share.uuid != null) {
- share();
- } else {
- xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uris.size() == 0);
- }
- }
-
- }
+ }
+ } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
+ this.share.image = type != null && type.startsWith("image/");
+ if (!this.share.image) {
+ return;
+ }
+ this.share.uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
+ }
+ if (xmppConnectionServiceBound) {
+ if (share.uuid != null) {
+ share();
+ } else {
+ xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uris.size() == 0);
+ }
+ }
+
+ }
@Override
public void onSaveInstanceState(final Bundle savedInstanceState) {
@@ -256,14 +256,14 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
super.onSaveInstanceState(savedInstanceState);
}
- protected boolean isImage(Uri uri) {
- try {
- String guess = URLConnection.guessContentTypeFromName(uri.toString());
- return (guess != null && guess.startsWith("image/"));
- } catch (final StringIndexOutOfBoundsException ignored) {
- return false;
- }
- }
+ protected boolean isImage(Uri uri) {
+ try {
+ String guess = URLConnection.guessContentTypeFromName(uri.toString());
+ return (guess != null && guess.startsWith("image/"));
+ } catch (final StringIndexOutOfBoundsException ignored) {
+ return false;
+ }
+ }
protected boolean isVideo(Uri uri) {
try {
@@ -274,65 +274,65 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
}
}
- @Override
- void onBackendConnected() {
- if (xmppConnectionServiceBound && share != null
- && ((share.contact != null && share.account != null) || share.uuid != null)) {
- share();
- return;
- }
- refreshUiReal();
- }
-
- private void share() {
- final Conversation conversation;
- if (share.uuid != null) {
- conversation = xmppConnectionService.findConversationByUuid(share.uuid);
- if (conversation == null) {
- return;
- }
- }else{
- Account account;
- try {
- account = xmppConnectionService.findAccountByJid(Jid.fromString(share.account));
- } catch (final InvalidJidException e) {
- account = null;
- }
- if (account == null) {
- return;
- }
-
- try {
- conversation = xmppConnectionService
- .findOrCreateConversation(account, Jid.fromString(share.contact), false);
- } catch (final InvalidJidException e) {
- return;
- }
- }
+ @Override
+ void onBackendConnected() {
+ if (xmppConnectionServiceBound && share != null
+ && ((share.contact != null && share.account != null) || share.uuid != null)) {
+ share();
+ return;
+ }
+ refreshUiReal();
+ }
+
+ private void share() {
+ final Conversation conversation;
+ if (share.uuid != null) {
+ conversation = xmppConnectionService.findConversationByUuid(share.uuid);
+ if (conversation == null) {
+ return;
+ }
+ } else {
+ Account account;
+ try {
+ account = xmppConnectionService.findAccountByJid(Jid.fromString(share.account));
+ } catch (final InvalidJidException e) {
+ account = null;
+ }
+ if (account == null) {
+ return;
+ }
+
+ try {
+ conversation = xmppConnectionService
+ .findOrCreateConversation(account, Jid.fromString(share.contact), false);
+ } catch (final InvalidJidException e) {
+ return;
+ }
+ }
ContactChosen = true;
- share(conversation);
- }
-
- 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) {
- showInstallPgpDialog();
- } else {
- Toast.makeText(this,R.string.openkeychain_not_installed,Toast.LENGTH_SHORT).show();
- finish();
- }
- return;
- }
- if (share.uris.size() != 0) {
- OnPresenceSelected callback = new OnPresenceSelected() {
- @Override
- public void onPresenceSelected() {
- attachmentCounter.set(share.uris.size());
- if (share.image) {
+ share(conversation);
+ }
+
+ 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) {
+ showInstallPgpDialog();
+ } else {
+ Toast.makeText(this, R.string.openkeychain_not_installed, Toast.LENGTH_SHORT).show();
+ finish();
+ }
+ return;
+ }
+ if (share.uris.size() != 0) {
+ OnPresenceSelected callback = new OnPresenceSelected() {
+ @Override
+ public void onPresenceSelected() {
+ attachmentCounter.set(share.uris.size());
+ if (share.image) {
Log.d(Config.LOGTAG, "ShareWithActivity share() image " + share.uris.size() + " uri(s) " + share.uris.toString());
share.multiple = share.uris.size() > 1;
replaceToast(getString(share.multiple ? R.string.preparing_images : R.string.preparing_image));
@@ -347,49 +347,49 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
ShareWithActivity.this.xmppConnectionService
.attachVideoToConversation(conversation, share.uris.get(0),
attachFileCallback);
- } else {
+ } else {
Log.d(Config.LOGTAG, "ShareWithActivity share() file " + share.uris.size() + " uri(s) " + share.uris.toString());
- replaceToast(getString(R.string.preparing_file));
- ShareWithActivity.this.xmppConnectionService
- .attachFileToConversation(conversation, share.uris.get(0),
- attachFileCallback);
- }
- }
- };
- if (account.httpUploadAvailable()
- && ((share.image && !neverCompressPictures())
- || share.video
- || conversation.getMode() == Conversation.MODE_MULTI
- || FileBackend.allFilesUnderSize(this, share.uris, max))
- && conversation.getNextEncryption() != Message.ENCRYPTION_OTR) {
- callback.onPresenceSelected();
- } else {
- selectPresence(conversation, callback);
- }
- } else {
- if (mReturnToPrevious && this.share.text != null && !this.share.text.isEmpty() ) {
- final OnPresenceSelected callback = new OnPresenceSelected() {
- @Override
- public void onPresenceSelected() {
- Message message = new Message(conversation,share.text, conversation.getNextEncryption());
- if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
- message.setCounterpart(conversation.getNextCounterpart());
- }
- xmppConnectionService.sendMessage(message);
- replaceToast(getString(R.string.shared_text_with_x, conversation.getName()));
- finish();
- }
- };
- if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
- selectPresence(conversation, callback);
- } else {
- callback.onPresenceSelected();
- }
- } else {
+ replaceToast(getString(R.string.preparing_file));
+ ShareWithActivity.this.xmppConnectionService
+ .attachFileToConversation(conversation, share.uris.get(0),
+ attachFileCallback);
+ }
+ }
+ };
+ if (account.httpUploadAvailable()
+ && ((share.image && !neverCompressPictures())
+ || share.video
+ || conversation.getMode() == Conversation.MODE_MULTI
+ || FileBackend.allFilesUnderSize(this, share.uris, max))
+ && conversation.getNextEncryption() != Message.ENCRYPTION_OTR) {
+ callback.onPresenceSelected();
+ } else {
+ selectPresence(conversation, callback);
+ }
+ } else {
+ if (mReturnToPrevious && this.share.text != null && !this.share.text.isEmpty()) {
final OnPresenceSelected callback = new OnPresenceSelected() {
@Override
public void onPresenceSelected() {
- Message message = new Message(conversation,share.text, conversation.getNextEncryption());
+ Message message = new Message(conversation, share.text, conversation.getNextEncryption());
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
+ message.setCounterpart(conversation.getNextCounterpart());
+ }
+ xmppConnectionService.sendMessage(message);
+ replaceToast(getString(R.string.shared_text_with_x, conversation.getName()));
+ finish();
+ }
+ };
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
+ selectPresence(conversation, callback);
+ } else {
+ callback.onPresenceSelected();
+ }
+ } else {
+ final OnPresenceSelected callback = new OnPresenceSelected() {
+ @Override
+ public void onPresenceSelected() {
+ Message message = new Message(conversation, share.text, conversation.getNextEncryption());
if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
message.setCounterpart(conversation.getNextCounterpart());
}
@@ -403,22 +403,22 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
} else {
callback.onPresenceSelected();
}
- }
- }
-
- }
-
- public void refreshUiReal() {
- xmppConnectionService.populateWithOrderedConversations(mConversations, this.share != null && this.share.uris.size() == 0);
- mAdapter.notifyDataSetChanged();
- }
-
- @Override
- public void onBackPressed() {
- if (attachmentCounter.get() >= 1) {
- replaceToast(getString(R.string.sharing_files_please_wait));
- } else {
- super.onBackPressed();
- }
- }
+ }
+ }
+
+ }
+
+ public void refreshUiReal() {
+ xmppConnectionService.populateWithOrderedConversations(mConversations, this.share != null && this.share.uris.size() == 0);
+ mAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (attachmentCounter.get() >= 1) {
+ replaceToast(getString(R.string.sharing_files_please_wait));
+ } else {
+ super.onBackPressed();
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java b/src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java
index ffa9018ec..faddebb86 100644
--- a/src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java
@@ -162,7 +162,7 @@ public class ShowFullscreenMessageActivity extends Activity {
}
private void DisplayVideo(Uri uri) {
- MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(uri.getPath());
height = Integer.valueOf(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
width = Integer.valueOf(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
@@ -217,7 +217,7 @@ public class ShowFullscreenMessageActivity extends Activity {
}
@Override
- public void onStop () {
+ public void onStop() {
mVideo.reset();
WindowManager.LayoutParams layout = getWindow().getAttributes();
layout.screenBrightness = -1;
diff --git a/src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java b/src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java
index 40ff08d80..bb950a21e 100644
--- a/src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java
@@ -35,9 +35,9 @@ import de.pixart.messenger.R;
public class ShowLocationActivity extends Activity implements OnMapReadyCallback {
- private GoogleMap mGoogleMap;
- private LatLng mLocation;
- private String mLocationName;
+ private GoogleMap mGoogleMap;
+ private LatLng mLocation;
+ private String mLocationName;
private MarkerOptions options;
private Marker marker;
@@ -66,26 +66,26 @@ public class ShowLocationActivity extends Activity implements OnMapReadyCallback
}
}
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
if (getActionBar() != null) {
getActionBar().setHomeButtonEnabled(true);
getActionBar().setDisplayHomeAsUpEnabled(true);
}
- setContentView(R.layout.activity_show_locaction);
- MapFragment fragment = (MapFragment) getFragmentManager().findFragmentById(R.id.map_fragment);
- fragment.getMapAsync(this);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- finish();
- return true;
+ setContentView(R.layout.activity_show_locaction);
+ MapFragment fragment = (MapFragment) getFragmentManager().findFragmentById(R.id.map_fragment);
+ fragment.getMapAsync(this);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
case R.id.action_navigate:
double longitude = mLocation.longitude;
double latitude = mLocation.latitude;
@@ -96,9 +96,9 @@ public class ShowLocationActivity extends Activity implements OnMapReadyCallback
Toast.makeText(this, R.string.no_application_found_to_display_location, Toast.LENGTH_SHORT).show();
}
return true;
- }
- return super.onOptionsItemSelected(item);
- }
+ }
+ return super.onOptionsItemSelected(item);
+ }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
@@ -107,31 +107,31 @@ public class ShowLocationActivity extends Activity implements OnMapReadyCallback
return true;
}
- @Override
- protected void onResume() {
- super.onResume();
- Intent intent = getIntent();
-
- this.mLocationName = intent != null ? intent.getStringExtra("name") : null;
-
- if (intent != null && intent.hasExtra("longitude") && intent.hasExtra("latitude")) {
- double longitude = intent.getDoubleExtra("longitude",0);
- double latitude = intent.getDoubleExtra("latitude",0);
- this.mLocation = new LatLng(latitude,longitude);
- if (this.mGoogleMap != null) {
- markAndCenterOnLocation(this.mLocation, this.mLocationName);
- }
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- }
-
- @Override
- public void onMapReady(GoogleMap googleMap) {
- this.mGoogleMap = googleMap;
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Intent intent = getIntent();
+
+ this.mLocationName = intent != null ? intent.getStringExtra("name") : null;
+
+ if (intent != null && intent.hasExtra("longitude") && intent.hasExtra("latitude")) {
+ double longitude = intent.getDoubleExtra("longitude", 0);
+ double latitude = intent.getDoubleExtra("latitude", 0);
+ this.mLocation = new LatLng(latitude, longitude);
+ if (this.mGoogleMap != null) {
+ markAndCenterOnLocation(this.mLocation, this.mLocationName);
+ }
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ public void onMapReady(GoogleMap googleMap) {
+ this.mGoogleMap = googleMap;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|| checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
@@ -142,10 +142,10 @@ public class ShowLocationActivity extends Activity implements OnMapReadyCallback
this.mGoogleMap.setBuildingsEnabled(true);
this.mGoogleMap.setMyLocationEnabled(true);
}
- if (this.mLocation != null) {
- this.markAndCenterOnLocation(this.mLocation,this.mLocationName);
- }
- }
+ if (this.mLocation != null) {
+ this.markAndCenterOnLocation(this.mLocation, this.mLocationName);
+ }
+ }
private static String getAddress(Context context, LatLng location) {
double longitude = location.longitude;
@@ -162,7 +162,7 @@ public class ShowLocationActivity extends Activity implements OnMapReadyCallback
strAddress.append(Address.getAddressLine(i)).append("\n");
}
address = strAddress.toString();
- address = address.substring(0, address.length()-1); //trim last \n
+ address = address.substring(0, address.length() - 1); //trim last \n
}
} catch (Exception e) {
diff --git a/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java b/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java
index cd64149cd..46beddcde 100644
--- a/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java
@@ -843,7 +843,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
private boolean handleJid(Invite invite) {
Account account = xmppConnectionService.findAccountByJid(invite.getJid());
if (account != null && invite.hasFingerprints()) {
- if (xmppConnectionService.verifyFingerprints(account,invite.getFingerprints())) {
+ if (xmppConnectionService.verifyFingerprints(account, invite.getFingerprints())) {
switchToAccount(account);
finish();
return true;
@@ -865,7 +865,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
} else if (contacts.size() == 1) {
Contact contact = contacts.get(0);
if (invite.hasFingerprints()) {
- xmppConnectionService.verifyFingerprints(contact,invite.getFingerprints());
+ xmppConnectionService.verifyFingerprints(contact, invite.getFingerprints());
}
switchToConversation(contact);
return true;
diff --git a/src/main/java/de/pixart/messenger/ui/StartUI.java b/src/main/java/de/pixart/messenger/ui/StartUI.java
index 5c9e4c324..18a5d0f0d 100644
--- a/src/main/java/de/pixart/messenger/ui/StartUI.java
+++ b/src/main/java/de/pixart/messenger/ui/StartUI.java
@@ -43,8 +43,8 @@ public class StartUI extends AppCompatActivity
long FirstStartTime = FirstStart.getLong("FirstStart", 0);
if (EasyPermissions.hasPermissions(this, perms)) {
// Already have permission, start ConversationsActivity
- Log.d(Config.LOGTAG, "All permissions granted, starting "+getString(R.string.app_name) + "(" +FirstStartTime + ")");
- Intent intent = new Intent (this, ConversationActivity.class);
+ Log.d(Config.LOGTAG, "All permissions granted, starting " + getString(R.string.app_name) + "(" + FirstStartTime + ")");
+ Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra("FirstStart", FirstStartTime);
startActivity(intent);
finish();
diff --git a/src/main/java/de/pixart/messenger/ui/TimePreference.java b/src/main/java/de/pixart/messenger/ui/TimePreference.java
index 1c8c8c0f1..731a3bb50 100644
--- a/src/main/java/de/pixart/messenger/ui/TimePreference.java
+++ b/src/main/java/de/pixart/messenger/ui/TimePreference.java
@@ -13,93 +13,93 @@ import java.util.Calendar;
import java.util.Date;
public class TimePreference extends DialogPreference implements Preference.OnPreferenceChangeListener {
- private TimePicker picker = null;
- public final static long DEFAULT_VALUE = 0;
-
- public TimePreference(final Context context, final AttributeSet attrs) {
- super(context, attrs, 0);
- this.setOnPreferenceChangeListener(this);
- }
-
- protected void setTime(final long time) {
- persistLong(time);
- notifyDependencyChange(shouldDisableDependents());
- notifyChanged();
- }
-
- protected void updateSummary(final long time) {
- final DateFormat dateFormat = android.text.format.DateFormat.getTimeFormat(getContext());
- final Date date = new Date(time);
- setSummary(dateFormat.format(date.getTime()));
- }
-
- @Override
- protected View onCreateDialogView() {
- picker = new TimePicker(getContext());
- picker.setIs24HourView(android.text.format.DateFormat.is24HourFormat(getContext()));
- return picker;
- }
-
- protected Calendar getPersistedTime() {
- final Calendar c = Calendar.getInstance();
- c.setTimeInMillis(getPersistedLong(DEFAULT_VALUE));
-
- return c;
- }
-
- @SuppressWarnings("NullableProblems")
- @Override
- protected void onBindDialogView(final View v) {
- super.onBindDialogView(v);
- final Calendar c = getPersistedTime();
-
- picker.setCurrentHour(c.get(Calendar.HOUR_OF_DAY));
- picker.setCurrentMinute(c.get(Calendar.MINUTE));
- }
-
- @Override
- protected void onDialogClosed(final boolean positiveResult) {
- super.onDialogClosed(positiveResult);
-
- if (positiveResult) {
- final Calendar c = Calendar.getInstance();
- c.set(Calendar.MINUTE, picker.getCurrentMinute());
- c.set(Calendar.HOUR_OF_DAY, picker.getCurrentHour());
-
-
- if (!callChangeListener(c.getTimeInMillis())) {
- return;
- }
-
- setTime(c.getTimeInMillis());
- }
- }
-
- @Override
- protected Object onGetDefaultValue(final TypedArray a, final int index) {
- return a.getInteger(index, 0);
- }
-
- @Override
- protected void onSetInitialValue(final boolean restorePersistedValue, final Object defaultValue) {
- long time;
- if (defaultValue == null) {
- time = restorePersistedValue ? getPersistedLong(DEFAULT_VALUE) : DEFAULT_VALUE;
- } else if (defaultValue instanceof Long) {
- time = restorePersistedValue ? getPersistedLong((Long) defaultValue) : (Long) defaultValue;
- } else if (defaultValue instanceof Calendar) {
- time = restorePersistedValue ? getPersistedLong(((Calendar)defaultValue).getTimeInMillis()) : ((Calendar)defaultValue).getTimeInMillis();
- } else {
- time = restorePersistedValue ? getPersistedLong(DEFAULT_VALUE) : DEFAULT_VALUE;
- }
-
- setTime(time);
- updateSummary(time);
- }
-
- @Override
- public boolean onPreferenceChange(final Preference preference, final Object newValue) {
- ((TimePreference) preference).updateSummary((Long)newValue);
- return true;
- }
+ private TimePicker picker = null;
+ public final static long DEFAULT_VALUE = 0;
+
+ public TimePreference(final Context context, final AttributeSet attrs) {
+ super(context, attrs, 0);
+ this.setOnPreferenceChangeListener(this);
+ }
+
+ protected void setTime(final long time) {
+ persistLong(time);
+ notifyDependencyChange(shouldDisableDependents());
+ notifyChanged();
+ }
+
+ protected void updateSummary(final long time) {
+ final DateFormat dateFormat = android.text.format.DateFormat.getTimeFormat(getContext());
+ final Date date = new Date(time);
+ setSummary(dateFormat.format(date.getTime()));
+ }
+
+ @Override
+ protected View onCreateDialogView() {
+ picker = new TimePicker(getContext());
+ picker.setIs24HourView(android.text.format.DateFormat.is24HourFormat(getContext()));
+ return picker;
+ }
+
+ protected Calendar getPersistedTime() {
+ final Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(getPersistedLong(DEFAULT_VALUE));
+
+ return c;
+ }
+
+ @SuppressWarnings("NullableProblems")
+ @Override
+ protected void onBindDialogView(final View v) {
+ super.onBindDialogView(v);
+ final Calendar c = getPersistedTime();
+
+ picker.setCurrentHour(c.get(Calendar.HOUR_OF_DAY));
+ picker.setCurrentMinute(c.get(Calendar.MINUTE));
+ }
+
+ @Override
+ protected void onDialogClosed(final boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+
+ if (positiveResult) {
+ final Calendar c = Calendar.getInstance();
+ c.set(Calendar.MINUTE, picker.getCurrentMinute());
+ c.set(Calendar.HOUR_OF_DAY, picker.getCurrentHour());
+
+
+ if (!callChangeListener(c.getTimeInMillis())) {
+ return;
+ }
+
+ setTime(c.getTimeInMillis());
+ }
+ }
+
+ @Override
+ protected Object onGetDefaultValue(final TypedArray a, final int index) {
+ return a.getInteger(index, 0);
+ }
+
+ @Override
+ protected void onSetInitialValue(final boolean restorePersistedValue, final Object defaultValue) {
+ long time;
+ if (defaultValue == null) {
+ time = restorePersistedValue ? getPersistedLong(DEFAULT_VALUE) : DEFAULT_VALUE;
+ } else if (defaultValue instanceof Long) {
+ time = restorePersistedValue ? getPersistedLong((Long) defaultValue) : (Long) defaultValue;
+ } else if (defaultValue instanceof Calendar) {
+ time = restorePersistedValue ? getPersistedLong(((Calendar) defaultValue).getTimeInMillis()) : ((Calendar) defaultValue).getTimeInMillis();
+ } else {
+ time = restorePersistedValue ? getPersistedLong(DEFAULT_VALUE) : DEFAULT_VALUE;
+ }
+
+ setTime(time);
+ updateSummary(time);
+ }
+
+ @Override
+ public boolean onPreferenceChange(final Preference preference, final Object newValue) {
+ ((TimePreference) preference).updateSummary((Long) newValue);
+ return true;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java b/src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java
index 2f3951c3a..893317970 100644
--- a/src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java
@@ -30,307 +30,307 @@ import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdated {
- private List<Jid> contactJids;
-
- private Account mAccount;
- private Conversation mConversation;
- private TextView keyErrorMessage;
- private LinearLayout keyErrorMessageCard;
- private TextView ownKeysTitle;
- private LinearLayout ownKeys;
- private LinearLayout ownKeysCard;
- private LinearLayout foreignKeys;
- private Button mSaveButton;
- private Button mCancelButton;
-
- private AxolotlService.FetchStatus lastFetchReport = AxolotlService.FetchStatus.SUCCESS;
-
- private final Map<String, Boolean> ownKeysToTrust = new HashMap<>();
- private final Map<Jid,Map<String, Boolean>> foreignKeysToTrust = new HashMap<>();
-
- private final OnClickListener mSaveButtonListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- commitTrusts();
- finishOk();
- }
- };
-
- private final OnClickListener mCancelButtonListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- setResult(RESULT_CANCELED);
- finish();
- }
- };
-
- @Override
- protected void refreshUiReal() {
- invalidateOptionsMenu();
- populateView();
- }
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_trust_keys);
- this.contactJids = new ArrayList<>();
- for(String jid : getIntent().getStringArrayExtra("contacts")) {
- try {
- this.contactJids.add(Jid.fromString(jid));
- } catch (InvalidJidException e) {
- e.printStackTrace();
- }
- }
-
- keyErrorMessageCard = (LinearLayout) findViewById(R.id.key_error_message_card);
- keyErrorMessage = (TextView) findViewById(R.id.key_error_message);
- ownKeysTitle = (TextView) findViewById(R.id.own_keys_title);
- ownKeys = (LinearLayout) findViewById(R.id.own_keys_details);
- ownKeysCard = (LinearLayout) findViewById(R.id.own_keys_card);
- foreignKeys = (LinearLayout) findViewById(R.id.foreign_keys);
- mCancelButton = (Button) findViewById(R.id.cancel_button);
- mCancelButton.setOnClickListener(mCancelButtonListener);
- mSaveButton = (Button) findViewById(R.id.save_button);
- mSaveButton.setOnClickListener(mSaveButtonListener);
-
-
- if (getActionBar() != null) {
- getActionBar().setHomeButtonEnabled(true);
- getActionBar().setDisplayHomeAsUpEnabled(true);
- }
- }
-
- private void populateView() {
- setTitle(getString(R.string.trust_omemo_fingerprints));
- ownKeys.removeAllViews();
- foreignKeys.removeAllViews();
- boolean hasOwnKeys = false;
- boolean hasForeignKeys = false;
- for(final String fingerprint : ownKeysToTrust.keySet()) {
- hasOwnKeys = true;
- addFingerprintRowWithListeners(ownKeys, mAccount, fingerprint, false,
- FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)), false, false,
- new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- ownKeysToTrust.put(fingerprint, isChecked);
- // own fingerprints have no impact on locked status.
- }
- }
- );
- }
-
- synchronized (this.foreignKeysToTrust) {
- for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
- hasForeignKeys = true;
- final LinearLayout layout = (LinearLayout) getLayoutInflater().inflate(R.layout.keys_card, foreignKeys, false);
- final Jid jid = entry.getKey();
- final TextView header = (TextView) layout.findViewById(R.id.foreign_keys_title);
- final LinearLayout keysContainer = (LinearLayout) layout.findViewById(R.id.foreign_keys_details);
- final TextView informNoKeys = (TextView) layout.findViewById(R.id.no_keys_to_accept);
- header.setText(jid.toString());
- final Map<String, Boolean> fingerprints = entry.getValue();
- for (final String fingerprint : fingerprints.keySet()) {
- addFingerprintRowWithListeners(keysContainer, mAccount, fingerprint, false,
- FingerprintStatus.createActive(fingerprints.get(fingerprint)), false, false,
- new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- fingerprints.put(fingerprint, isChecked);
- lockOrUnlockAsNeeded();
- }
- }
- );
- }
- if (fingerprints.size() == 0) {
- informNoKeys.setVisibility(View.VISIBLE);
- informNoKeys.setText(getString(R.string.no_keys_just_confirm,mAccount.getRoster().getContact(jid).getDisplayName()));
- } else {
- informNoKeys.setVisibility(View.GONE);
- }
- foreignKeys.addView(layout);
- }
- }
-
- ownKeysTitle.setText(mAccount.getJid().toBareJid().toString());
- ownKeysCard.setVisibility(hasOwnKeys ? View.VISIBLE : View.GONE);
- foreignKeys.setVisibility(hasForeignKeys ? View.VISIBLE : View.GONE);
- if(hasPendingKeyFetches()) {
- setFetching();
- lock();
- } else {
- if (!hasForeignKeys && hasNoOtherTrustedKeys()) {
- keyErrorMessageCard.setVisibility(View.VISIBLE);
- if (lastFetchReport == AxolotlService.FetchStatus.ERROR
- || mAccount.getAxolotlService().fetchMapHasErrors(contactJids)) {
- keyErrorMessage.setText(R.string.error_no_keys_to_trust_server_error);
- } else {
- keyErrorMessage.setText(R.string.error_no_keys_to_trust);
- }
- ownKeys.removeAllViews();
- ownKeysCard.setVisibility(View.GONE);
- foreignKeys.removeAllViews();
- foreignKeys.setVisibility(View.GONE);
- }
- lockOrUnlockAsNeeded();
- setDone();
- }
- }
-
- private boolean reloadFingerprints() {
- List<Jid> acceptedTargets = mConversation == null ? new ArrayList<Jid>() : mConversation.getAcceptedCryptoTargets();
- ownKeysToTrust.clear();
- AxolotlService service = this.mAccount.getAxolotlService();
- Set<IdentityKey> ownKeysSet = service.getKeysWithTrust(FingerprintStatus.createActiveUndecided());
- for(final IdentityKey identityKey : ownKeysSet) {
- if(!ownKeysToTrust.containsKey(identityKey)) {
- ownKeysToTrust.put(identityKey.getFingerprint().replaceAll("\\s", ""), false);
- }
- }
- synchronized (this.foreignKeysToTrust) {
- foreignKeysToTrust.clear();
- for (Jid jid : contactJids) {
- Set<IdentityKey> foreignKeysSet = service.getKeysWithTrust(FingerprintStatus.createActiveUndecided(), jid);
- if (hasNoOtherTrustedKeys(jid) && ownKeysSet.size() == 0) {
- foreignKeysSet.addAll(service.getKeysWithTrust(FingerprintStatus.createActive(false), jid));
- }
- Map<String, Boolean> foreignFingerprints = new HashMap<>();
- for (final IdentityKey identityKey : foreignKeysSet) {
- if (!foreignFingerprints.containsKey(identityKey)) {
- foreignFingerprints.put(identityKey.getFingerprint().replaceAll("\\s", ""), false);
- }
- }
- if (foreignFingerprints.size() > 0 || !acceptedTargets.contains(jid)) {
- foreignKeysToTrust.put(jid, foreignFingerprints);
- }
- }
- }
- return ownKeysSet.size() + foreignKeysToTrust.size() > 0;
- }
-
- public void onBackendConnected() {
- Intent intent = getIntent();
- this.mAccount = extractAccount(intent);
- if (this.mAccount != null && intent != null) {
- String uuid = intent.getStringExtra("conversation");
- this.mConversation = xmppConnectionService.findConversationByUuid(uuid);
- reloadFingerprints();
- populateView();
- }
- }
-
- private boolean hasNoOtherTrustedKeys() {
- return mAccount == null || mAccount.getAxolotlService().anyTargetHasNoTrustedKeys(contactJids);
- }
-
- private boolean hasNoOtherTrustedKeys(Jid contact) {
- return mAccount == null || mAccount.getAxolotlService().getNumTrustedKeys(contact) == 0;
- }
-
- private boolean hasPendingKeyFetches() {
- return mAccount != null && mAccount.getAxolotlService().hasPendingKeyFetches(mAccount, contactJids);
- }
-
-
- @Override
- public void onKeyStatusUpdated(final AxolotlService.FetchStatus report) {
- if (report != null) {
- lastFetchReport = report;
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- switch (report) {
- case ERROR:
- Toast.makeText(TrustKeysActivity.this,R.string.error_fetching_omemo_key,Toast.LENGTH_SHORT).show();
- break;
- case SUCCESS_VERIFIED:
- Toast.makeText(TrustKeysActivity.this,
- Config.X509_VERIFICATION ? R.string.verified_omemo_key_with_certificate : R.string.all_omemo_keys_have_been_verified,
- Toast.LENGTH_LONG).show();
- break;
- }
- }
- });
-
- }
- boolean keysToTrust = reloadFingerprints();
- if (keysToTrust || hasPendingKeyFetches() || hasNoOtherTrustedKeys()) {
- refreshUi();
- } else {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- finishOk();
- }
- });
-
- }
- }
-
- private void finishOk() {
- Intent data = new Intent();
- data.putExtra("choice", getIntent().getIntExtra("choice", ConversationActivity.ATTACHMENT_CHOICE_INVALID));
- setResult(RESULT_OK, data);
- finish();
- }
-
- private void commitTrusts() {
- for(final String fingerprint :ownKeysToTrust.keySet()) {
- mAccount.getAxolotlService().setFingerprintTrust(
- fingerprint,
- FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)));
- }
- List<Jid> acceptedTargets = mConversation == null ? new ArrayList<Jid>() : mConversation.getAcceptedCryptoTargets();
- synchronized (this.foreignKeysToTrust) {
- for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
- Jid jid = entry.getKey();
- Map<String, Boolean> value = entry.getValue();
- if (!acceptedTargets.contains(jid)) {
- acceptedTargets.add(jid);
- }
- for (final String fingerprint : value.keySet()) {
- mAccount.getAxolotlService().setFingerprintTrust(
- fingerprint,
- FingerprintStatus.createActive(value.get(fingerprint)));
- }
- }
- }
- if (mConversation != null && mConversation.getMode() == Conversation.MODE_MULTI) {
- mConversation.setAcceptedCryptoTargets(acceptedTargets);
- xmppConnectionService.updateConversation(mConversation);
- }
- }
-
- 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();
- return;
- }
- }
- }
- unlock();
-
- }
-
- private void setDone() {
- mSaveButton.setText(getString(R.string.done));
- }
-
- private void setFetching() {
- mSaveButton.setText(getString(R.string.fetching_keys));
- }
+ private List<Jid> contactJids;
+
+ private Account mAccount;
+ private Conversation mConversation;
+ private TextView keyErrorMessage;
+ private LinearLayout keyErrorMessageCard;
+ private TextView ownKeysTitle;
+ private LinearLayout ownKeys;
+ private LinearLayout ownKeysCard;
+ private LinearLayout foreignKeys;
+ private Button mSaveButton;
+ private Button mCancelButton;
+
+ private AxolotlService.FetchStatus lastFetchReport = AxolotlService.FetchStatus.SUCCESS;
+
+ private final Map<String, Boolean> ownKeysToTrust = new HashMap<>();
+ private final Map<Jid, Map<String, Boolean>> foreignKeysToTrust = new HashMap<>();
+
+ private final OnClickListener mSaveButtonListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ commitTrusts();
+ finishOk();
+ }
+ };
+
+ private final OnClickListener mCancelButtonListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ };
+
+ @Override
+ protected void refreshUiReal() {
+ invalidateOptionsMenu();
+ populateView();
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_trust_keys);
+ this.contactJids = new ArrayList<>();
+ for (String jid : getIntent().getStringArrayExtra("contacts")) {
+ try {
+ this.contactJids.add(Jid.fromString(jid));
+ } catch (InvalidJidException e) {
+ e.printStackTrace();
+ }
+ }
+
+ keyErrorMessageCard = (LinearLayout) findViewById(R.id.key_error_message_card);
+ keyErrorMessage = (TextView) findViewById(R.id.key_error_message);
+ ownKeysTitle = (TextView) findViewById(R.id.own_keys_title);
+ ownKeys = (LinearLayout) findViewById(R.id.own_keys_details);
+ ownKeysCard = (LinearLayout) findViewById(R.id.own_keys_card);
+ foreignKeys = (LinearLayout) findViewById(R.id.foreign_keys);
+ mCancelButton = (Button) findViewById(R.id.cancel_button);
+ mCancelButton.setOnClickListener(mCancelButtonListener);
+ mSaveButton = (Button) findViewById(R.id.save_button);
+ mSaveButton.setOnClickListener(mSaveButtonListener);
+
+
+ if (getActionBar() != null) {
+ getActionBar().setHomeButtonEnabled(true);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ private void populateView() {
+ setTitle(getString(R.string.trust_omemo_fingerprints));
+ ownKeys.removeAllViews();
+ foreignKeys.removeAllViews();
+ boolean hasOwnKeys = false;
+ boolean hasForeignKeys = false;
+ for (final String fingerprint : ownKeysToTrust.keySet()) {
+ hasOwnKeys = true;
+ addFingerprintRowWithListeners(ownKeys, mAccount, fingerprint, false,
+ FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)), false, false,
+ new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ ownKeysToTrust.put(fingerprint, isChecked);
+ // own fingerprints have no impact on locked status.
+ }
+ }
+ );
+ }
+
+ synchronized (this.foreignKeysToTrust) {
+ for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
+ hasForeignKeys = true;
+ final LinearLayout layout = (LinearLayout) getLayoutInflater().inflate(R.layout.keys_card, foreignKeys, false);
+ final Jid jid = entry.getKey();
+ final TextView header = (TextView) layout.findViewById(R.id.foreign_keys_title);
+ final LinearLayout keysContainer = (LinearLayout) layout.findViewById(R.id.foreign_keys_details);
+ final TextView informNoKeys = (TextView) layout.findViewById(R.id.no_keys_to_accept);
+ header.setText(jid.toString());
+ final Map<String, Boolean> fingerprints = entry.getValue();
+ for (final String fingerprint : fingerprints.keySet()) {
+ addFingerprintRowWithListeners(keysContainer, mAccount, fingerprint, false,
+ FingerprintStatus.createActive(fingerprints.get(fingerprint)), false, false,
+ new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ fingerprints.put(fingerprint, isChecked);
+ lockOrUnlockAsNeeded();
+ }
+ }
+ );
+ }
+ if (fingerprints.size() == 0) {
+ informNoKeys.setVisibility(View.VISIBLE);
+ informNoKeys.setText(getString(R.string.no_keys_just_confirm, mAccount.getRoster().getContact(jid).getDisplayName()));
+ } else {
+ informNoKeys.setVisibility(View.GONE);
+ }
+ foreignKeys.addView(layout);
+ }
+ }
+
+ ownKeysTitle.setText(mAccount.getJid().toBareJid().toString());
+ ownKeysCard.setVisibility(hasOwnKeys ? View.VISIBLE : View.GONE);
+ foreignKeys.setVisibility(hasForeignKeys ? View.VISIBLE : View.GONE);
+ if (hasPendingKeyFetches()) {
+ setFetching();
+ lock();
+ } else {
+ if (!hasForeignKeys && hasNoOtherTrustedKeys()) {
+ keyErrorMessageCard.setVisibility(View.VISIBLE);
+ if (lastFetchReport == AxolotlService.FetchStatus.ERROR
+ || mAccount.getAxolotlService().fetchMapHasErrors(contactJids)) {
+ keyErrorMessage.setText(R.string.error_no_keys_to_trust_server_error);
+ } else {
+ keyErrorMessage.setText(R.string.error_no_keys_to_trust);
+ }
+ ownKeys.removeAllViews();
+ ownKeysCard.setVisibility(View.GONE);
+ foreignKeys.removeAllViews();
+ foreignKeys.setVisibility(View.GONE);
+ }
+ lockOrUnlockAsNeeded();
+ setDone();
+ }
+ }
+
+ private boolean reloadFingerprints() {
+ List<Jid> acceptedTargets = mConversation == null ? new ArrayList<Jid>() : mConversation.getAcceptedCryptoTargets();
+ ownKeysToTrust.clear();
+ AxolotlService service = this.mAccount.getAxolotlService();
+ Set<IdentityKey> ownKeysSet = service.getKeysWithTrust(FingerprintStatus.createActiveUndecided());
+ for (final IdentityKey identityKey : ownKeysSet) {
+ if (!ownKeysToTrust.containsKey(identityKey)) {
+ ownKeysToTrust.put(identityKey.getFingerprint().replaceAll("\\s", ""), false);
+ }
+ }
+ synchronized (this.foreignKeysToTrust) {
+ foreignKeysToTrust.clear();
+ for (Jid jid : contactJids) {
+ Set<IdentityKey> foreignKeysSet = service.getKeysWithTrust(FingerprintStatus.createActiveUndecided(), jid);
+ if (hasNoOtherTrustedKeys(jid) && ownKeysSet.size() == 0) {
+ foreignKeysSet.addAll(service.getKeysWithTrust(FingerprintStatus.createActive(false), jid));
+ }
+ Map<String, Boolean> foreignFingerprints = new HashMap<>();
+ for (final IdentityKey identityKey : foreignKeysSet) {
+ if (!foreignFingerprints.containsKey(identityKey)) {
+ foreignFingerprints.put(identityKey.getFingerprint().replaceAll("\\s", ""), false);
+ }
+ }
+ if (foreignFingerprints.size() > 0 || !acceptedTargets.contains(jid)) {
+ foreignKeysToTrust.put(jid, foreignFingerprints);
+ }
+ }
+ }
+ return ownKeysSet.size() + foreignKeysToTrust.size() > 0;
+ }
+
+ public void onBackendConnected() {
+ Intent intent = getIntent();
+ this.mAccount = extractAccount(intent);
+ if (this.mAccount != null && intent != null) {
+ String uuid = intent.getStringExtra("conversation");
+ this.mConversation = xmppConnectionService.findConversationByUuid(uuid);
+ reloadFingerprints();
+ populateView();
+ }
+ }
+
+ private boolean hasNoOtherTrustedKeys() {
+ return mAccount == null || mAccount.getAxolotlService().anyTargetHasNoTrustedKeys(contactJids);
+ }
+
+ private boolean hasNoOtherTrustedKeys(Jid contact) {
+ return mAccount == null || mAccount.getAxolotlService().getNumTrustedKeys(contact) == 0;
+ }
+
+ private boolean hasPendingKeyFetches() {
+ return mAccount != null && mAccount.getAxolotlService().hasPendingKeyFetches(mAccount, contactJids);
+ }
+
+
+ @Override
+ public void onKeyStatusUpdated(final AxolotlService.FetchStatus report) {
+ if (report != null) {
+ lastFetchReport = report;
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ switch (report) {
+ case ERROR:
+ Toast.makeText(TrustKeysActivity.this, R.string.error_fetching_omemo_key, Toast.LENGTH_SHORT).show();
+ break;
+ case SUCCESS_VERIFIED:
+ Toast.makeText(TrustKeysActivity.this,
+ Config.X509_VERIFICATION ? R.string.verified_omemo_key_with_certificate : R.string.all_omemo_keys_have_been_verified,
+ Toast.LENGTH_LONG).show();
+ break;
+ }
+ }
+ });
+
+ }
+ boolean keysToTrust = reloadFingerprints();
+ if (keysToTrust || hasPendingKeyFetches() || hasNoOtherTrustedKeys()) {
+ refreshUi();
+ } else {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ finishOk();
+ }
+ });
+
+ }
+ }
+
+ private void finishOk() {
+ Intent data = new Intent();
+ data.putExtra("choice", getIntent().getIntExtra("choice", ConversationActivity.ATTACHMENT_CHOICE_INVALID));
+ setResult(RESULT_OK, data);
+ finish();
+ }
+
+ private void commitTrusts() {
+ for (final String fingerprint : ownKeysToTrust.keySet()) {
+ mAccount.getAxolotlService().setFingerprintTrust(
+ fingerprint,
+ FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)));
+ }
+ List<Jid> acceptedTargets = mConversation == null ? new ArrayList<Jid>() : mConversation.getAcceptedCryptoTargets();
+ synchronized (this.foreignKeysToTrust) {
+ for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
+ Jid jid = entry.getKey();
+ Map<String, Boolean> value = entry.getValue();
+ if (!acceptedTargets.contains(jid)) {
+ acceptedTargets.add(jid);
+ }
+ for (final String fingerprint : value.keySet()) {
+ mAccount.getAxolotlService().setFingerprintTrust(
+ fingerprint,
+ FingerprintStatus.createActive(value.get(fingerprint)));
+ }
+ }
+ }
+ if (mConversation != null && mConversation.getMode() == Conversation.MODE_MULTI) {
+ mConversation.setAcceptedCryptoTargets(acceptedTargets);
+ xmppConnectionService.updateConversation(mConversation);
+ }
+ }
+
+ 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();
+ return;
+ }
+ }
+ }
+ unlock();
+
+ }
+
+ private void setDone() {
+ mSaveButton.setText(getString(R.string.done));
+ }
+
+ private void setFetching() {
+ mSaveButton.setText(getString(R.string.fetching_keys));
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/UiCallback.java b/src/main/java/de/pixart/messenger/ui/UiCallback.java
index 07377c879..310a49255 100644
--- a/src/main/java/de/pixart/messenger/ui/UiCallback.java
+++ b/src/main/java/de/pixart/messenger/ui/UiCallback.java
@@ -3,9 +3,9 @@ package de.pixart.messenger.ui;
import android.app.PendingIntent;
public interface UiCallback<T> {
- void success(T object);
+ void success(T object);
- void error(int errorCode, T object);
+ void error(int errorCode, T object);
- void userInputRequried(PendingIntent pi, T object);
+ void userInputRequried(PendingIntent pi, T object);
}
diff --git a/src/main/java/de/pixart/messenger/ui/VerifyOTRActivity.java b/src/main/java/de/pixart/messenger/ui/VerifyOTRActivity.java
index bee5ab692..1bd8e20f7 100644
--- a/src/main/java/de/pixart/messenger/ui/VerifyOTRActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/VerifyOTRActivity.java
@@ -31,414 +31,414 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class VerifyOTRActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate {
- public static final String ACTION_VERIFY_CONTACT = "verify_contact";
- public static final int MODE_SCAN_FINGERPRINT = - 0x0502;
- public static final int MODE_ASK_QUESTION = 0x0503;
- public static final int MODE_ANSWER_QUESTION = 0x0504;
- public static final int MODE_MANUAL_VERIFICATION = 0x0505;
+ public static final String ACTION_VERIFY_CONTACT = "verify_contact";
+ public static final int MODE_SCAN_FINGERPRINT = -0x0502;
+ public static final int MODE_ASK_QUESTION = 0x0503;
+ public static final int MODE_ANSWER_QUESTION = 0x0504;
+ public static final int MODE_MANUAL_VERIFICATION = 0x0505;
- private LinearLayout mManualVerificationArea;
- private LinearLayout mSmpVerificationArea;
- private TextView mRemoteFingerprint;
- private TextView mYourFingerprint;
- private TextView mVerificationExplain;
- private TextView mStatusMessage;
- private TextView mSharedSecretHint;
- private EditText mSharedSecretHintEditable;
- private EditText mSharedSecretSecret;
- private Button mLeftButton;
- private Button mRightButton;
- private Account mAccount;
- private Conversation mConversation;
- private int mode = MODE_MANUAL_VERIFICATION;
- private XmppUri mPendingUri = null;
+ private LinearLayout mManualVerificationArea;
+ private LinearLayout mSmpVerificationArea;
+ private TextView mRemoteFingerprint;
+ private TextView mYourFingerprint;
+ private TextView mVerificationExplain;
+ private TextView mStatusMessage;
+ private TextView mSharedSecretHint;
+ private EditText mSharedSecretHintEditable;
+ private EditText mSharedSecretSecret;
+ private Button mLeftButton;
+ private Button mRightButton;
+ private Account mAccount;
+ private Conversation mConversation;
+ private int mode = MODE_MANUAL_VERIFICATION;
+ private XmppUri mPendingUri = null;
- private DialogInterface.OnClickListener mVerifyFingerprintListener = new DialogInterface.OnClickListener() {
+ private DialogInterface.OnClickListener mVerifyFingerprintListener = new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int click) {
- mConversation.verifyOtrFingerprint();
- xmppConnectionService.syncRosterToDisk(mConversation.getAccount());
- Toast.makeText(VerifyOTRActivity.this,R.string.verified,Toast.LENGTH_SHORT).show();
- finish();
- }
- };
+ @Override
+ public void onClick(DialogInterface dialogInterface, int click) {
+ mConversation.verifyOtrFingerprint();
+ xmppConnectionService.syncRosterToDisk(mConversation.getAccount());
+ Toast.makeText(VerifyOTRActivity.this, R.string.verified, Toast.LENGTH_SHORT).show();
+ finish();
+ }
+ };
- private View.OnClickListener mCreateSharedSecretListener = new View.OnClickListener() {
- @Override
- public void onClick(final View view) {
- if (isAccountOnline()) {
- final String question = mSharedSecretHintEditable.getText().toString();
- final String secret = mSharedSecretSecret.getText().toString();
- if (question.trim().isEmpty()) {
- mSharedSecretHintEditable.requestFocus();
- mSharedSecretHintEditable.setError(getString(R.string.shared_secret_hint_should_not_be_empty));
- } else if (secret.trim().isEmpty()) {
- mSharedSecretSecret.requestFocus();
- mSharedSecretSecret.setError(getString(R.string.shared_secret_can_not_be_empty));
- } else {
- mSharedSecretSecret.setError(null);
- mSharedSecretHintEditable.setError(null);
- initSmp(question, secret);
- updateView();
- }
- }
- }
- };
- private View.OnClickListener mCancelSharedSecretListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (isAccountOnline()) {
- abortSmp();
- updateView();
- }
- }
- };
- private View.OnClickListener mRespondSharedSecretListener = new View.OnClickListener() {
+ private View.OnClickListener mCreateSharedSecretListener = new View.OnClickListener() {
+ @Override
+ public void onClick(final View view) {
+ if (isAccountOnline()) {
+ final String question = mSharedSecretHintEditable.getText().toString();
+ final String secret = mSharedSecretSecret.getText().toString();
+ if (question.trim().isEmpty()) {
+ mSharedSecretHintEditable.requestFocus();
+ mSharedSecretHintEditable.setError(getString(R.string.shared_secret_hint_should_not_be_empty));
+ } else if (secret.trim().isEmpty()) {
+ mSharedSecretSecret.requestFocus();
+ mSharedSecretSecret.setError(getString(R.string.shared_secret_can_not_be_empty));
+ } else {
+ mSharedSecretSecret.setError(null);
+ mSharedSecretHintEditable.setError(null);
+ initSmp(question, secret);
+ updateView();
+ }
+ }
+ }
+ };
+ private View.OnClickListener mCancelSharedSecretListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (isAccountOnline()) {
+ abortSmp();
+ updateView();
+ }
+ }
+ };
+ private View.OnClickListener mRespondSharedSecretListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (isAccountOnline()) {
- final String question = mSharedSecretHintEditable.getText().toString();
- final String secret = mSharedSecretSecret.getText().toString();
- respondSmp(question, secret);
- updateView();
- }
- }
- };
- private View.OnClickListener mRetrySharedSecretListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mConversation.smp().status = Conversation.Smp.STATUS_NONE;
- mConversation.smp().hint = null;
- mConversation.smp().secret = null;
- updateView();
- }
- };
- private View.OnClickListener mFinishListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mConversation.smp().status = Conversation.Smp.STATUS_NONE;
- finish();
- }
- };
+ @Override
+ public void onClick(View view) {
+ if (isAccountOnline()) {
+ final String question = mSharedSecretHintEditable.getText().toString();
+ final String secret = mSharedSecretSecret.getText().toString();
+ respondSmp(question, secret);
+ updateView();
+ }
+ }
+ };
+ private View.OnClickListener mRetrySharedSecretListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mConversation.smp().status = Conversation.Smp.STATUS_NONE;
+ mConversation.smp().hint = null;
+ mConversation.smp().secret = null;
+ updateView();
+ }
+ };
+ private View.OnClickListener mFinishListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mConversation.smp().status = Conversation.Smp.STATUS_NONE;
+ finish();
+ }
+ };
- protected boolean initSmp(final String question, final String secret) {
- final Session session = mConversation.getOtrSession();
- if (session!=null) {
- try {
- session.initSmp(question, secret);
- mConversation.smp().status = Conversation.Smp.STATUS_WE_REQUESTED;
- mConversation.smp().secret = secret;
- mConversation.smp().hint = question;
- return true;
- } catch (OtrException e) {
- return false;
- }
- } else {
- return false;
- }
- }
+ protected boolean initSmp(final String question, final String secret) {
+ final Session session = mConversation.getOtrSession();
+ if (session != null) {
+ try {
+ session.initSmp(question, secret);
+ mConversation.smp().status = Conversation.Smp.STATUS_WE_REQUESTED;
+ mConversation.smp().secret = secret;
+ mConversation.smp().hint = question;
+ return true;
+ } catch (OtrException e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
- protected boolean abortSmp() {
- final Session session = mConversation.getOtrSession();
- if (session!=null) {
- try {
- session.abortSmp();
- mConversation.smp().status = Conversation.Smp.STATUS_NONE;
- mConversation.smp().hint = null;
- mConversation.smp().secret = null;
- return true;
- } catch (OtrException e) {
- return false;
- }
- } else {
- return false;
- }
- }
+ protected boolean abortSmp() {
+ final Session session = mConversation.getOtrSession();
+ if (session != null) {
+ try {
+ session.abortSmp();
+ mConversation.smp().status = Conversation.Smp.STATUS_NONE;
+ mConversation.smp().hint = null;
+ mConversation.smp().secret = null;
+ return true;
+ } catch (OtrException e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
- protected boolean respondSmp(final String question, final String secret) {
- final Session session = mConversation.getOtrSession();
- if (session!=null) {
- try {
- session.respondSmp(question,secret);
- return true;
- } catch (OtrException e) {
- return false;
- }
- } else {
- return false;
- }
- }
+ protected boolean respondSmp(final String question, final String secret) {
+ final Session session = mConversation.getOtrSession();
+ if (session != null) {
+ try {
+ session.respondSmp(question, secret);
+ return true;
+ } catch (OtrException e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
- protected boolean verifyWithUri(XmppUri uri) {
- Contact contact = mConversation.getContact();
- if (this.mConversation.getContact().getJid().equals(uri.getJid()) && uri.hasFingerprints()) {
- xmppConnectionService.verifyFingerprints(contact,uri.getFingerprints());
- Toast.makeText(this,R.string.verified,Toast.LENGTH_SHORT).show();
- updateView();
- return true;
- } else {
- Toast.makeText(this,R.string.could_not_verify_fingerprint,Toast.LENGTH_SHORT).show();
- return false;
- }
- }
+ protected boolean verifyWithUri(XmppUri uri) {
+ Contact contact = mConversation.getContact();
+ if (this.mConversation.getContact().getJid().equals(uri.getJid()) && uri.hasFingerprints()) {
+ xmppConnectionService.verifyFingerprints(contact, uri.getFingerprints());
+ Toast.makeText(this, R.string.verified, Toast.LENGTH_SHORT).show();
+ updateView();
+ return true;
+ } else {
+ Toast.makeText(this, R.string.could_not_verify_fingerprint, Toast.LENGTH_SHORT).show();
+ return false;
+ }
+ }
- protected boolean isAccountOnline() {
- if (this.mAccount.getStatus() != Account.State.ONLINE) {
- Toast.makeText(this,R.string.not_connected_try_again,Toast.LENGTH_SHORT).show();
- return false;
- } else {
- return true;
- }
- }
+ protected boolean isAccountOnline() {
+ if (this.mAccount.getStatus() != Account.State.ONLINE) {
+ Toast.makeText(this, R.string.not_connected_try_again, Toast.LENGTH_SHORT).show();
+ return false;
+ } else {
+ return true;
+ }
+ }
- protected boolean handleIntent(Intent intent) {
- if (intent != null && intent.getAction().equals(ACTION_VERIFY_CONTACT)) {
- this.mAccount = extractAccount(intent);
- if (this.mAccount == null) {
- return false;
- }
- try {
- this.mConversation = this.xmppConnectionService.find(this.mAccount,Jid.fromString(intent.getExtras().getString("contact")));
- if (this.mConversation == null) {
- return false;
- }
- } catch (final InvalidJidException ignored) {
- return false;
- }
- this.mode = intent.getIntExtra("mode", MODE_MANUAL_VERIFICATION);
- if (this.mode == MODE_SCAN_FINGERPRINT) {
- new IntentIntegrator(this).initiateScan();
- return false;
- }
- return true;
- } else {
- return false;
- }
- }
+ protected boolean handleIntent(Intent intent) {
+ if (intent != null && intent.getAction().equals(ACTION_VERIFY_CONTACT)) {
+ this.mAccount = extractAccount(intent);
+ if (this.mAccount == null) {
+ return false;
+ }
+ try {
+ this.mConversation = this.xmppConnectionService.find(this.mAccount, Jid.fromString(intent.getExtras().getString("contact")));
+ if (this.mConversation == null) {
+ return false;
+ }
+ } catch (final InvalidJidException ignored) {
+ return false;
+ }
+ this.mode = intent.getIntExtra("mode", MODE_MANUAL_VERIFICATION);
+ if (this.mode == MODE_SCAN_FINGERPRINT) {
+ new IntentIntegrator(this).initiateScan();
+ return false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent intent) {
- if ((requestCode & 0xFFFF) == IntentIntegrator.REQUEST_CODE) {
- IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
- if (scanResult != null && scanResult.getFormatName() != null) {
- String data = scanResult.getContents();
- XmppUri uri = new XmppUri(data);
- if (xmppConnectionServiceBound) {
- verifyWithUri(uri);
- finish();
- } else {
- this.mPendingUri = uri;
- }
- } else {
- finish();
- }
- }
- super.onActivityResult(requestCode, requestCode, intent);
- }
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ if ((requestCode & 0xFFFF) == IntentIntegrator.REQUEST_CODE) {
+ IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
+ if (scanResult != null && scanResult.getFormatName() != null) {
+ String data = scanResult.getContents();
+ XmppUri uri = new XmppUri(data);
+ if (xmppConnectionServiceBound) {
+ verifyWithUri(uri);
+ finish();
+ } else {
+ this.mPendingUri = uri;
+ }
+ } else {
+ finish();
+ }
+ }
+ super.onActivityResult(requestCode, requestCode, intent);
+ }
- @Override
- protected void onBackendConnected() {
- if (handleIntent(getIntent())) {
- updateView();
- } else if (mPendingUri!=null) {
- verifyWithUri(mPendingUri);
- finish();
- mPendingUri = null;
- }
- setIntent(null);
- }
+ @Override
+ protected void onBackendConnected() {
+ if (handleIntent(getIntent())) {
+ updateView();
+ } else if (mPendingUri != null) {
+ verifyWithUri(mPendingUri);
+ finish();
+ mPendingUri = null;
+ }
+ setIntent(null);
+ }
- protected void updateView() {
- if (this.mConversation != null && this.mConversation.hasValidOtrSession()) {
- final ActionBar actionBar = getActionBar();
- this.mVerificationExplain.setText(R.string.no_otr_session_found);
- invalidateOptionsMenu();
- switch(this.mode) {
- case MODE_ASK_QUESTION:
- if (actionBar != null ) {
- actionBar.setTitle(R.string.ask_question);
- }
- this.updateViewAskQuestion();
- break;
- case MODE_ANSWER_QUESTION:
- if (actionBar != null ) {
- actionBar.setTitle(R.string.smp_requested);
- }
- this.updateViewAnswerQuestion();
- break;
- case MODE_MANUAL_VERIFICATION:
- default:
- if (actionBar != null ) {
- actionBar.setTitle(R.string.manually_verify);
- }
- this.updateViewManualVerification();
- break;
- }
- } else {
- this.mManualVerificationArea.setVisibility(View.GONE);
- this.mSmpVerificationArea.setVisibility(View.GONE);
- }
- }
+ protected void updateView() {
+ if (this.mConversation != null && this.mConversation.hasValidOtrSession()) {
+ final ActionBar actionBar = getActionBar();
+ this.mVerificationExplain.setText(R.string.no_otr_session_found);
+ invalidateOptionsMenu();
+ switch (this.mode) {
+ case MODE_ASK_QUESTION:
+ if (actionBar != null) {
+ actionBar.setTitle(R.string.ask_question);
+ }
+ this.updateViewAskQuestion();
+ break;
+ case MODE_ANSWER_QUESTION:
+ if (actionBar != null) {
+ actionBar.setTitle(R.string.smp_requested);
+ }
+ this.updateViewAnswerQuestion();
+ break;
+ case MODE_MANUAL_VERIFICATION:
+ default:
+ if (actionBar != null) {
+ actionBar.setTitle(R.string.manually_verify);
+ }
+ this.updateViewManualVerification();
+ break;
+ }
+ } else {
+ this.mManualVerificationArea.setVisibility(View.GONE);
+ this.mSmpVerificationArea.setVisibility(View.GONE);
+ }
+ }
- protected void updateViewManualVerification() {
- this.mVerificationExplain.setText(R.string.manual_verification_explanation);
- this.mManualVerificationArea.setVisibility(View.VISIBLE);
- this.mSmpVerificationArea.setVisibility(View.GONE);
- this.mYourFingerprint.setText(CryptoHelper.prettifyFingerprint(this.mAccount.getOtrFingerprint()));
- this.mRemoteFingerprint.setText(CryptoHelper.prettifyFingerprint(this.mConversation.getOtrFingerprint()));
- if (this.mConversation.isOtrFingerprintVerified()) {
- deactivateButton(this.mRightButton,R.string.verified);
- activateButton(this.mLeftButton,R.string.cancel,this.mFinishListener);
- } else {
- activateButton(this.mLeftButton,R.string.cancel,this.mFinishListener);
- activateButton(this.mRightButton,R.string.verify, new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- showManuallyVerifyDialog();
- }
- });
- }
- }
+ protected void updateViewManualVerification() {
+ this.mVerificationExplain.setText(R.string.manual_verification_explanation);
+ this.mManualVerificationArea.setVisibility(View.VISIBLE);
+ this.mSmpVerificationArea.setVisibility(View.GONE);
+ this.mYourFingerprint.setText(CryptoHelper.prettifyFingerprint(this.mAccount.getOtrFingerprint()));
+ this.mRemoteFingerprint.setText(CryptoHelper.prettifyFingerprint(this.mConversation.getOtrFingerprint()));
+ if (this.mConversation.isOtrFingerprintVerified()) {
+ deactivateButton(this.mRightButton, R.string.verified);
+ activateButton(this.mLeftButton, R.string.cancel, this.mFinishListener);
+ } else {
+ activateButton(this.mLeftButton, R.string.cancel, this.mFinishListener);
+ activateButton(this.mRightButton, R.string.verify, new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ showManuallyVerifyDialog();
+ }
+ });
+ }
+ }
- protected void updateViewAskQuestion() {
- this.mManualVerificationArea.setVisibility(View.GONE);
- this.mSmpVerificationArea.setVisibility(View.VISIBLE);
- this.mVerificationExplain.setText(R.string.smp_explain_question);
- final int smpStatus = this.mConversation.smp().status;
- switch (smpStatus) {
- case Conversation.Smp.STATUS_WE_REQUESTED:
- this.mStatusMessage.setVisibility(View.GONE);
- this.mSharedSecretHintEditable.setVisibility(View.VISIBLE);
- this.mSharedSecretSecret.setVisibility(View.VISIBLE);
- this.mSharedSecretHintEditable.setText(this.mConversation.smp().hint);
- this.mSharedSecretSecret.setText(this.mConversation.smp().secret);
- this.activateButton(this.mLeftButton, R.string.cancel, this.mCancelSharedSecretListener);
- this.deactivateButton(this.mRightButton, R.string.in_progress);
- break;
- case Conversation.Smp.STATUS_FAILED:
- this.mStatusMessage.setVisibility(View.GONE);
- this.mSharedSecretHintEditable.setVisibility(View.VISIBLE);
- this.mSharedSecretSecret.setVisibility(View.VISIBLE);
- this.mSharedSecretSecret.requestFocus();
- this.mSharedSecretSecret.setError(getString(R.string.secrets_do_not_match));
- this.deactivateButton(this.mLeftButton, R.string.cancel);
- this.activateButton(this.mRightButton, R.string.try_again, this.mRetrySharedSecretListener);
- break;
- case Conversation.Smp.STATUS_VERIFIED:
- this.mSharedSecretHintEditable.setText("");
- this.mSharedSecretHintEditable.setVisibility(View.GONE);
- this.mSharedSecretSecret.setText("");
- this.mSharedSecretSecret.setVisibility(View.GONE);
- this.mStatusMessage.setVisibility(View.VISIBLE);
- this.deactivateButton(this.mLeftButton, R.string.cancel);
- this.activateButton(this.mRightButton, R.string.finish, this.mFinishListener);
- break;
- default:
- this.mStatusMessage.setVisibility(View.GONE);
- this.mSharedSecretHintEditable.setVisibility(View.VISIBLE);
- this.mSharedSecretSecret.setVisibility(View.VISIBLE);
- this.activateButton(this.mLeftButton,R.string.cancel,this.mFinishListener);
- this.activateButton(this.mRightButton, R.string.ask_question, this.mCreateSharedSecretListener);
- break;
- }
- }
+ protected void updateViewAskQuestion() {
+ this.mManualVerificationArea.setVisibility(View.GONE);
+ this.mSmpVerificationArea.setVisibility(View.VISIBLE);
+ this.mVerificationExplain.setText(R.string.smp_explain_question);
+ final int smpStatus = this.mConversation.smp().status;
+ switch (smpStatus) {
+ case Conversation.Smp.STATUS_WE_REQUESTED:
+ this.mStatusMessage.setVisibility(View.GONE);
+ this.mSharedSecretHintEditable.setVisibility(View.VISIBLE);
+ this.mSharedSecretSecret.setVisibility(View.VISIBLE);
+ this.mSharedSecretHintEditable.setText(this.mConversation.smp().hint);
+ this.mSharedSecretSecret.setText(this.mConversation.smp().secret);
+ this.activateButton(this.mLeftButton, R.string.cancel, this.mCancelSharedSecretListener);
+ this.deactivateButton(this.mRightButton, R.string.in_progress);
+ break;
+ case Conversation.Smp.STATUS_FAILED:
+ this.mStatusMessage.setVisibility(View.GONE);
+ this.mSharedSecretHintEditable.setVisibility(View.VISIBLE);
+ this.mSharedSecretSecret.setVisibility(View.VISIBLE);
+ this.mSharedSecretSecret.requestFocus();
+ this.mSharedSecretSecret.setError(getString(R.string.secrets_do_not_match));
+ this.deactivateButton(this.mLeftButton, R.string.cancel);
+ this.activateButton(this.mRightButton, R.string.try_again, this.mRetrySharedSecretListener);
+ break;
+ case Conversation.Smp.STATUS_VERIFIED:
+ this.mSharedSecretHintEditable.setText("");
+ this.mSharedSecretHintEditable.setVisibility(View.GONE);
+ this.mSharedSecretSecret.setText("");
+ this.mSharedSecretSecret.setVisibility(View.GONE);
+ this.mStatusMessage.setVisibility(View.VISIBLE);
+ this.deactivateButton(this.mLeftButton, R.string.cancel);
+ this.activateButton(this.mRightButton, R.string.finish, this.mFinishListener);
+ break;
+ default:
+ this.mStatusMessage.setVisibility(View.GONE);
+ this.mSharedSecretHintEditable.setVisibility(View.VISIBLE);
+ this.mSharedSecretSecret.setVisibility(View.VISIBLE);
+ this.activateButton(this.mLeftButton, R.string.cancel, this.mFinishListener);
+ this.activateButton(this.mRightButton, R.string.ask_question, this.mCreateSharedSecretListener);
+ break;
+ }
+ }
- protected void updateViewAnswerQuestion() {
- this.mManualVerificationArea.setVisibility(View.GONE);
- this.mSmpVerificationArea.setVisibility(View.VISIBLE);
- this.mVerificationExplain.setText(R.string.smp_explain_answer);
- this.mSharedSecretHintEditable.setVisibility(View.GONE);
- this.mSharedSecretHint.setVisibility(View.VISIBLE);
- this.deactivateButton(this.mLeftButton, R.string.cancel);
- final int smpStatus = this.mConversation.smp().status;
- switch (smpStatus) {
- case Conversation.Smp.STATUS_CONTACT_REQUESTED:
- this.mStatusMessage.setVisibility(View.GONE);
- this.mSharedSecretHint.setText(this.mConversation.smp().hint);
- this.activateButton(this.mRightButton,R.string.respond,this.mRespondSharedSecretListener);
- break;
- case Conversation.Smp.STATUS_VERIFIED:
- this.mSharedSecretHintEditable.setText("");
- this.mSharedSecretHintEditable.setVisibility(View.GONE);
- this.mSharedSecretHint.setVisibility(View.GONE);
- this.mSharedSecretSecret.setText("");
- this.mSharedSecretSecret.setVisibility(View.GONE);
- this.mStatusMessage.setVisibility(View.VISIBLE);
- this.activateButton(this.mRightButton, R.string.finish, this.mFinishListener);
- break;
- case Conversation.Smp.STATUS_FAILED:
- default:
- this.mSharedSecretSecret.requestFocus();
- this.mSharedSecretSecret.setError(getString(R.string.secrets_do_not_match));
- this.activateButton(this.mRightButton,R.string.finish,this.mFinishListener);
- break;
- }
- }
+ protected void updateViewAnswerQuestion() {
+ this.mManualVerificationArea.setVisibility(View.GONE);
+ this.mSmpVerificationArea.setVisibility(View.VISIBLE);
+ this.mVerificationExplain.setText(R.string.smp_explain_answer);
+ this.mSharedSecretHintEditable.setVisibility(View.GONE);
+ this.mSharedSecretHint.setVisibility(View.VISIBLE);
+ this.deactivateButton(this.mLeftButton, R.string.cancel);
+ final int smpStatus = this.mConversation.smp().status;
+ switch (smpStatus) {
+ case Conversation.Smp.STATUS_CONTACT_REQUESTED:
+ this.mStatusMessage.setVisibility(View.GONE);
+ this.mSharedSecretHint.setText(this.mConversation.smp().hint);
+ this.activateButton(this.mRightButton, R.string.respond, this.mRespondSharedSecretListener);
+ break;
+ case Conversation.Smp.STATUS_VERIFIED:
+ this.mSharedSecretHintEditable.setText("");
+ this.mSharedSecretHintEditable.setVisibility(View.GONE);
+ this.mSharedSecretHint.setVisibility(View.GONE);
+ this.mSharedSecretSecret.setText("");
+ this.mSharedSecretSecret.setVisibility(View.GONE);
+ this.mStatusMessage.setVisibility(View.VISIBLE);
+ this.activateButton(this.mRightButton, R.string.finish, this.mFinishListener);
+ break;
+ case Conversation.Smp.STATUS_FAILED:
+ default:
+ this.mSharedSecretSecret.requestFocus();
+ this.mSharedSecretSecret.setError(getString(R.string.secrets_do_not_match));
+ this.activateButton(this.mRightButton, R.string.finish, this.mFinishListener);
+ break;
+ }
+ }
- protected void activateButton(Button button, int text, View.OnClickListener listener) {
- button.setEnabled(true);
- button.setTextColor(getPrimaryTextColor());
- button.setText(text);
- button.setOnClickListener(listener);
- }
+ protected void activateButton(Button button, int text, View.OnClickListener listener) {
+ button.setEnabled(true);
+ button.setTextColor(getPrimaryTextColor());
+ button.setText(text);
+ button.setOnClickListener(listener);
+ }
- protected void deactivateButton(Button button, int text) {
- button.setEnabled(false);
- button.setTextColor(getSecondaryTextColor());
- button.setText(text);
- button.setOnClickListener(null);
- }
+ protected void deactivateButton(Button button, int text) {
+ button.setEnabled(false);
+ button.setTextColor(getSecondaryTextColor());
+ button.setText(text);
+ button.setOnClickListener(null);
+ }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_verify_otr);
- this.mRemoteFingerprint = (TextView) findViewById(R.id.remote_fingerprint);
- this.mYourFingerprint = (TextView) findViewById(R.id.your_fingerprint);
- this.mLeftButton = (Button) findViewById(R.id.left_button);
- this.mRightButton = (Button) findViewById(R.id.right_button);
- this.mVerificationExplain = (TextView) findViewById(R.id.verification_explanation);
- this.mStatusMessage = (TextView) findViewById(R.id.status_message);
- this.mSharedSecretSecret = (EditText) findViewById(R.id.shared_secret_secret);
- this.mSharedSecretHintEditable = (EditText) findViewById(R.id.shared_secret_hint_editable);
- this.mSharedSecretHint = (TextView) findViewById(R.id.shared_secret_hint);
- this.mManualVerificationArea = (LinearLayout) findViewById(R.id.manual_verification_area);
- this.mSmpVerificationArea = (LinearLayout) findViewById(R.id.smp_verification_area);
- }
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_verify_otr);
+ this.mRemoteFingerprint = (TextView) findViewById(R.id.remote_fingerprint);
+ this.mYourFingerprint = (TextView) findViewById(R.id.your_fingerprint);
+ this.mLeftButton = (Button) findViewById(R.id.left_button);
+ this.mRightButton = (Button) findViewById(R.id.right_button);
+ this.mVerificationExplain = (TextView) findViewById(R.id.verification_explanation);
+ this.mStatusMessage = (TextView) findViewById(R.id.status_message);
+ this.mSharedSecretSecret = (EditText) findViewById(R.id.shared_secret_secret);
+ this.mSharedSecretHintEditable = (EditText) findViewById(R.id.shared_secret_hint_editable);
+ this.mSharedSecretHint = (TextView) findViewById(R.id.shared_secret_hint);
+ this.mManualVerificationArea = (LinearLayout) findViewById(R.id.manual_verification_area);
+ this.mSmpVerificationArea = (LinearLayout) findViewById(R.id.smp_verification_area);
+ }
- @Override
- public boolean onCreateOptionsMenu(final Menu menu) {
- super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.verify_otr, menu);
- return true;
- }
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getMenuInflater().inflate(R.menu.verify_otr, menu);
+ return true;
+ }
- private void showManuallyVerifyDialog() {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.manually_verify);
- builder.setMessage(R.string.are_you_sure_verify_fingerprint);
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(R.string.verify, mVerifyFingerprintListener);
- builder.create().show();
- }
+ private void showManuallyVerifyDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.manually_verify);
+ builder.setMessage(R.string.are_you_sure_verify_fingerprint);
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.verify, mVerifyFingerprintListener);
+ builder.create().show();
+ }
- @Override
- protected String getShareableUri() {
- if (mAccount!=null) {
- return mAccount.getShareableUri();
- } else {
- return "";
- }
- }
+ @Override
+ protected String getShareableUri() {
+ if (mAccount != null) {
+ return mAccount.getShareableUri();
+ } else {
+ return "";
+ }
+ }
- public void onConversationUpdate() {
- refreshUi();
- }
+ public void onConversationUpdate() {
+ refreshUi();
+ }
- @Override
- protected void refreshUiReal() {
- updateView();
- }
+ @Override
+ protected void refreshUiReal() {
+ updateView();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java b/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java
index f0235ce97..7573e40c7 100644
--- a/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java
@@ -101,8 +101,8 @@ public class WelcomeActivity extends XmppActivity {
List<Account> accounts = xmppConnectionService.getAccounts();
Intent intent = new Intent(WelcomeActivity.this, EditAccountActivity.class);
if (accounts.size() == 1) {
- intent.putExtra("jid",accounts.get(0).getJid().toBareJid().toString());
- intent.putExtra("init",true);
+ intent.putExtra("jid", accounts.get(0).getJid().toBareJid().toString());
+ intent.putExtra("init", true);
} else if (accounts.size() >= 1) {
intent = new Intent(WelcomeActivity.this, ManageAccountActivity.class);
}
@@ -125,7 +125,7 @@ public class WelcomeActivity extends XmppActivity {
.setCancelable(false)
.setPositiveButton(R.string.ok,
new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog,int id) {
+ public void onClick(DialogInterface dialog, int id) {
final String password = userInput.getText().toString();
final ProgressDialog pd = ProgressDialog.show(WelcomeActivity.this, getString(R.string.please_wait), getString(R.string.databaseimport_started), true);
if (!password.isEmpty()) {
@@ -157,7 +157,7 @@ public class WelcomeActivity extends XmppActivity {
})
.setNegativeButton(R.string.cancel,
new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog,int id) {
+ public void onClick(DialogInterface dialog, int id) {
Toast.makeText(WelcomeActivity.this, R.string.import_canceled, Toast.LENGTH_LONG).show();
dialog.dismiss();
}
@@ -176,12 +176,12 @@ public class WelcomeActivity extends XmppActivity {
private boolean BackupAvailable() {
// Set the folder on the SDcard
File filePath = new File(FileBackend.getConversationsDirectory() + "/database/database.db.crypt");
- Log.d(Config.LOGTAG,"DB Path: " + filePath.toString());
- if(filePath.exists()) {
- Log.d(Config.LOGTAG,"DB Path existing");
+ Log.d(Config.LOGTAG, "DB Path: " + filePath.toString());
+ if (filePath.exists()) {
+ Log.d(Config.LOGTAG, "DB Path existing");
return true;
} else {
- Log.d(Config.LOGTAG,"DB Path not existing");
+ Log.d(Config.LOGTAG, "DB Path not existing");
return false;
}
}
@@ -198,16 +198,16 @@ public class WelcomeActivity extends XmppActivity {
try {
EncryptDecryptFile.decrypt(InputFile, OutputTemp, DecryptionKey);
} catch (NoSuchAlgorithmException e) {
- Log.d(Config.LOGTAG,"Database importer: decryption failed with " + e);
+ Log.d(Config.LOGTAG, "Database importer: decryption failed with " + e);
e.printStackTrace();
} catch (NoSuchPaddingException e) {
- Log.d(Config.LOGTAG,"Database importer: decryption failed with " + e);
+ Log.d(Config.LOGTAG, "Database importer: decryption failed with " + e);
e.printStackTrace();
} catch (InvalidKeyException e) {
- Log.d(Config.LOGTAG,"Database importer: decryption failed (invalid key) with " + e);
+ Log.d(Config.LOGTAG, "Database importer: decryption failed (invalid key) with " + e);
e.printStackTrace();
} catch (IOException e) {
- Log.d(Config.LOGTAG,"Database importer: decryption failed (IO) with " + e);
+ Log.d(Config.LOGTAG, "Database importer: decryption failed (IO) with " + e);
e.printStackTrace();
}
diff --git a/src/main/java/de/pixart/messenger/ui/XmppActivity.java b/src/main/java/de/pixart/messenger/ui/XmppActivity.java
index 26f185294..8b3d26179 100644
--- a/src/main/java/de/pixart/messenger/ui/XmppActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/XmppActivity.java
@@ -92,1209 +92,1209 @@ import de.pixart.messenger.xmpp.jid.Jid;
public abstract class XmppActivity extends Activity {
- protected static final int REQUEST_ANNOUNCE_PGP = 0x0101;
- protected static final int REQUEST_INVITE_TO_CONVERSATION = 0x0102;
- protected static final int REQUEST_CHOOSE_PGP_ID = 0x0103;
- protected static final int REQUEST_BATTERY_OP = 0x13849ff;
-
- public static final String EXTRA_ACCOUNT = "account";
-
- public XmppConnectionService xmppConnectionService;
- 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 mColorWhite;
- protected int mColorOrange;
- protected int mColorGreen;
- protected int mPrimaryColor;
-
- protected boolean mUseSubject = true;
-
- private DisplayMetrics metrics;
- protected int mTheme;
- protected boolean mUsingEnterKey = false;
-
- protected Toast mToast;
-
- protected void hideToast() {
- if (mToast != null) {
- mToast.cancel();
- }
- }
-
- protected void replaceToast(String msg) {
- replaceToast(msg, true);
- }
-
- protected void replaceToast(String msg, boolean showlong) {
- hideToast();
- mToast = Toast.makeText(this, msg ,showlong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT);
- mToast.show();
- }
-
- public void showProgress() {
-
- }
-
- public void closeProgress() {
-
- }
-
- protected Runnable onOpenPGPKeyPublished = new Runnable() {
- @Override
- public void run() {
- Toast.makeText(XmppActivity.this,R.string.openpgp_has_been_published, Toast.LENGTH_SHORT).show();
- }
- };
-
- private long mLastUiRefresh = 0;
- private Handler mRefreshUiHandler = new Handler();
- private Runnable mRefreshUiRunnable = new Runnable() {
- @Override
- public void run() {
- mLastUiRefresh = SystemClock.elapsedRealtime();
- refreshUiReal();
- }
- };
-
- protected ConferenceInvite mPendingConferenceInvite = null;
-
-
- protected final void refreshUi() {
- final long diff = SystemClock.elapsedRealtime() - mLastUiRefresh;
- if (diff > Config.REFRESH_UI_INTERVAL) {
- mRefreshUiHandler.removeCallbacks(mRefreshUiRunnable);
- runOnUiThread(mRefreshUiRunnable);
- } else {
- final long next = Config.REFRESH_UI_INTERVAL - diff;
- mRefreshUiHandler.removeCallbacks(mRefreshUiRunnable);
- mRefreshUiHandler.postDelayed(mRefreshUiRunnable,next);
- }
- }
-
- abstract protected void refreshUiReal();
-
- protected interface OnValueEdited {
- public void onValueEdited(String value);
- }
-
- public interface OnPresenceSelected {
- public void onPresenceSelected();
- }
-
- protected ServiceConnection mConnection = new ServiceConnection() {
-
- @Override
- public void onServiceConnected(ComponentName className, IBinder service) {
- XmppConnectionBinder binder = (XmppConnectionBinder) service;
- xmppConnectionService = binder.getService();
- xmppConnectionServiceBound = true;
- if (!registeredListeners && shouldRegisterListeners()) {
- registerListeners();
- registeredListeners = true;
- }
- onBackendConnected();
- }
-
- @Override
- public void onServiceDisconnected(ComponentName arg0) {
- xmppConnectionServiceBound = false;
- }
- };
-
- @Override
- protected void onStart() {
- super.onStart();
- if (!xmppConnectionServiceBound) {
- connectToBackend();
- } else {
- if (!registeredListeners) {
- this.registerListeners();
- this.registeredListeners = true;
- }
- this.onBackendConnected();
- }
- }
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
- protected boolean shouldRegisterListeners() {
- if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- return !isDestroyed() && !isFinishing();
- } else {
- return !isFinishing();
- }
- }
-
- public void connectToBackend() {
- Intent intent = new Intent(this, XmppConnectionService.class);
- intent.setAction("ui");
- startService(intent);
- bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- if (xmppConnectionServiceBound) {
- if (registeredListeners) {
- this.unregisterListeners();
- this.registeredListeners = false;
- }
- unbindService(mConnection);
- xmppConnectionServiceBound = false;
- }
- }
-
- protected void hideKeyboard() {
- InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
-
- View focus = getCurrentFocus();
-
- if (focus != null) {
-
- inputManager.hideSoftInputFromWindow(focus.getWindowToken(),
- InputMethodManager.HIDE_NOT_ALWAYS);
- }
- }
-
- public boolean hasPgp() {
- return xmppConnectionService.getPgpEngine() != null;
- }
-
- public void showInstallPgpDialog() {
- Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(getString(R.string.openkeychain_required));
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- builder.setMessage(getText(R.string.openkeychain_required_long));
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.setNeutralButton(getString(R.string.restart),
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (xmppConnectionServiceBound) {
- unbindService(mConnection);
- xmppConnectionServiceBound = false;
- }
- stopService(new Intent(XmppActivity.this,
- XmppConnectionService.class));
- finish();
- }
- });
- builder.setPositiveButton(getString(R.string.install),
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Uri uri = Uri
- .parse("market://details?id=org.sufficientlysecure.keychain");
- Intent marketIntent = new Intent(Intent.ACTION_VIEW,
- uri);
- PackageManager manager = getApplicationContext()
- .getPackageManager();
- List<ResolveInfo> infos = manager
- .queryIntentActivities(marketIntent, 0);
- if (infos.size() > 0) {
- startActivity(marketIntent);
- } else {
- uri = Uri.parse("http://www.openkeychain.org/");
- Intent browserIntent = new Intent(
- Intent.ACTION_VIEW, uri);
- startActivity(browserIntent);
- }
- finish();
- }
- });
- builder.create().show();
- }
-
- abstract void onBackendConnected();
-
- protected void registerListeners() {
- if (this instanceof XmppConnectionService.OnConversationUpdate) {
- this.xmppConnectionService.setOnConversationListChangedListener((XmppConnectionService.OnConversationUpdate) this);
- }
- if (this instanceof XmppConnectionService.OnAccountUpdate) {
- this.xmppConnectionService.setOnAccountListChangedListener((XmppConnectionService.OnAccountUpdate) this);
- }
- if (this instanceof XmppConnectionService.OnCaptchaRequested) {
- this.xmppConnectionService.setOnCaptchaRequestedListener((XmppConnectionService.OnCaptchaRequested) this);
- }
- if (this instanceof XmppConnectionService.OnRosterUpdate) {
- this.xmppConnectionService.setOnRosterUpdateListener((XmppConnectionService.OnRosterUpdate) this);
- }
- if (this instanceof XmppConnectionService.OnMucRosterUpdate) {
- this.xmppConnectionService.setOnMucRosterUpdateListener((XmppConnectionService.OnMucRosterUpdate) this);
- }
- if (this instanceof OnUpdateBlocklist) {
- this.xmppConnectionService.setOnUpdateBlocklistListener((OnUpdateBlocklist) this);
- }
- if (this instanceof XmppConnectionService.OnShowErrorToast) {
- this.xmppConnectionService.setOnShowErrorToastListener((XmppConnectionService.OnShowErrorToast) this);
- }
- if (this instanceof OnKeyStatusUpdated) {
- this.xmppConnectionService.setOnKeyStatusUpdatedListener((OnKeyStatusUpdated) this);
- }
- }
-
- protected void unregisterListeners() {
- if (this instanceof XmppConnectionService.OnConversationUpdate) {
- this.xmppConnectionService.removeOnConversationListChangedListener();
- }
- if (this instanceof XmppConnectionService.OnAccountUpdate) {
- this.xmppConnectionService.removeOnAccountListChangedListener();
- }
- if (this instanceof XmppConnectionService.OnCaptchaRequested) {
- this.xmppConnectionService.removeOnCaptchaRequestedListener();
- }
- if (this instanceof XmppConnectionService.OnRosterUpdate) {
- this.xmppConnectionService.removeOnRosterUpdateListener();
- }
- if (this instanceof XmppConnectionService.OnMucRosterUpdate) {
- this.xmppConnectionService.removeOnMucRosterUpdateListener();
- }
- if (this instanceof OnUpdateBlocklist) {
- this.xmppConnectionService.removeOnUpdateBlocklistListener();
- }
- if (this instanceof XmppConnectionService.OnShowErrorToast) {
- this.xmppConnectionService.removeOnShowErrorToastListener();
- }
- if (this instanceof OnKeyStatusUpdated) {
- this.xmppConnectionService.removeOnNewKeysAvailableListener();
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_invite_user:
- inviteUser();
- break;
- case R.id.action_create_issue:
- createIssue();
- break;
- case R.id.action_settings:
- startActivity(new Intent(this, SettingsActivity.class));
- break;
- case R.id.action_check_updates:
- if (xmppConnectionService.hasInternetConnection()) {
- startActivity(new Intent(this, UpdaterActivity.class));
- } else {
- Toast.makeText(this, R.string.account_status_no_internet, Toast.LENGTH_LONG).show();
- }
- break;
- case R.id.action_accounts:
- final Intent intent = new Intent(getApplicationContext(), EditAccountActivity.class);
- Account mAccount = xmppConnectionService.getAccounts().get(0);
- intent.putExtra("jid", mAccount.getJid().toBareJid().toString());
- intent.putExtra("init", false);
- startActivity(intent);
- break;
- case android.R.id.home:
- finish();
- break;
- case R.id.action_show_qr_code:
- showQrCode();
- break;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- 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);
- mColorWhite = getResources().getColor(R.color.white70);
- mColorOrange = getResources().getColor(R.color.orange500);
- mColorGreen = getResources().getColor(R.color.realgreen);
- 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);
- }
- }
-
- protected boolean isOptimizingBattery() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
- return !pm.isIgnoringBatteryOptimizations(getPackageName());
- } else {
- return false;
- }
- }
-
- protected boolean isAffectedByDataSaver() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
- return cm.isActiveNetworkMetered()
- && cm.getRestrictBackgroundStatus() == ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
- } else {
- return false;
- }
- }
-
- 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);
- }
-
- public void switchToConversation(Conversation conversation, String text,
- boolean newTask) {
- switchToConversation(conversation,text,null,false,newTask);
- }
-
- public void highlightInMuc(Conversation conversation, String nick) {
- switchToConversation(conversation, null, nick, false, false);
- }
-
- public void privateMsgInMuc(Conversation conversation, String nick) {
- switchToConversation(conversation, null, nick, true, false);
- }
-
- private void switchToConversation(Conversation conversation, String text, String nick, boolean pm, boolean newTask) {
- Intent viewConversationIntent = new Intent(this,
- ConversationActivity.class);
- viewConversationIntent.setAction(ConversationActivity.ACTION_VIEW_CONVERSATION);
- viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
- conversation.getUuid());
- if (text != null) {
- viewConversationIntent.putExtra(ConversationActivity.TEXT, text);
- }
- if (nick != null) {
- viewConversationIntent.putExtra(ConversationActivity.NICK, nick);
- viewConversationIntent.putExtra(ConversationActivity.PRIVATE_MESSAGE,pm);
- }
- if (newTask) {
- viewConversationIntent.setFlags(viewConversationIntent.getFlags()
- | Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_SINGLE_TOP);
- } else {
- viewConversationIntent.setFlags(viewConversationIntent.getFlags()
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- }
- startActivity(viewConversationIntent);
- finish();
- }
-
- public void switchToContactDetails(Contact contact) {
- switchToContactDetails(contact, null);
- }
-
- public void switchToContactDetails(Contact contact, String messageFingerprint) {
- Intent intent = new Intent(this, ContactDetailsActivity.class);
- intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT);
- intent.putExtra(EXTRA_ACCOUNT, contact.getAccount().getJid().toBareJid().toString());
- intent.putExtra("contact", contact.getJid().toString());
- intent.putExtra("fingerprint", messageFingerprint);
- startActivity(intent);
- }
-
- public void switchToAccount(Account account) {
- switchToAccount(account, false);
- }
-
- public void switchToAccount(Account account, boolean init) {
- Intent intent = new Intent(this, EditAccountActivity.class);
- intent.putExtra("jid", account.getJid().toBareJid().toString());
- intent.putExtra("init", init);
- startActivity(intent);
- }
-
- protected void inviteToConversation(Conversation conversation) {
- Intent intent = new Intent(getApplicationContext(),
- ChooseContactActivity.class);
- List<String> contacts = new ArrayList<>();
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- for (MucOptions.User user : conversation.getMucOptions().getUsers(false)) {
- Jid jid = user.getRealJid();
- if (jid != null) {
- contacts.add(jid.toBareJid().toString());
- }
- }
- } else {
- contacts.add(conversation.getJid().toBareJid().toString());
- }
- intent.putExtra("filter_contacts", contacts.toArray(new String[contacts.size()]));
- intent.putExtra("conversation", conversation.getUuid());
- intent.putExtra("multiple", true);
- intent.putExtra("show_enter_jid", true);
- intent.putExtra(EXTRA_ACCOUNT, conversation.getAccount().getJid().toBareJid().toString());
- startActivityForResult(intent, REQUEST_INVITE_TO_CONVERSATION);
- }
-
- protected void announcePgp(Account account, final Conversation conversation, final Runnable onSuccess) {
- if (account.getPgpId() == 0) {
- choosePgpSignId(account);
- } else {
- String status = null;
- if (manuallyChangePresence()) {
- status = account.getPresenceStatusMessage();
- }
- if (status == null) {
- status = "";
- }
- xmppConnectionService.getPgpEngine().generateSignature(account, status, new UiCallback<Account>() {
-
- @Override
- public void userInputRequried(PendingIntent pi, Account account) {
- try {
- startIntentSenderForResult(pi.getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
- } catch (final SendIntentException ignored) {
- }
- }
-
- @Override
- public void success(Account account) {
- xmppConnectionService.databaseBackend.updateAccount(account);
- xmppConnectionService.sendPresence(account);
- if (conversation != null) {
- conversation.setNextEncryption(Message.ENCRYPTION_PGP);
- xmppConnectionService.updateConversation(conversation);
- refreshUi();
- }
- if (onSuccess != null) {
- runOnUiThread(onSuccess);
- }
- }
-
- @Override
- public void error(int error, Account account) {
- if (error == 0 && account != null) {
- account.setPgpSignId(0);
- account.unsetPgpSignature();
- xmppConnectionService.databaseBackend.updateAccount(account);
- choosePgpSignId(account);
- } else {
- displayErrorDialog(error);
- }
- }
- });
- }
- }
-
- protected boolean noAccountUsesPgp() {
- if (!hasPgp()) {
- return true;
- }
- for(Account account : xmppConnectionService.getAccounts()) {
- if (account.getPgpId() != 0) {
- return false;
- }
- }
- return true;
- }
-
- @SuppressWarnings("deprecation")
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- protected void setListItemBackgroundOnView(View view) {
- int sdk = android.os.Build.VERSION.SDK_INT;
- if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
- view.setBackgroundDrawable(getResources().getDrawable(R.drawable.greybackground));
- } else {
- view.setBackground(getResources().getDrawable(R.drawable.greybackground));
- }
- }
-
- protected void choosePgpSignId(Account account) {
- xmppConnectionService.getPgpEngine().chooseKey(account, new UiCallback<Account>() {
- @Override
- public void success(Account account1) {
- }
-
- @Override
- public void error(int errorCode, Account object) {
-
- }
-
- @Override
- public void userInputRequried(PendingIntent pi, Account object) {
- try {
- startIntentSenderForResult(pi.getIntentSender(),
- REQUEST_CHOOSE_PGP_ID, null, 0, 0, 0);
- } catch (final SendIntentException ignored) {
- }
- }
- });
- }
-
- protected void displayErrorDialog(final int errorCode) {
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- AlertDialog.Builder builder = new AlertDialog.Builder(
- XmppActivity.this);
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- builder.setTitle(getString(R.string.error));
- builder.setMessage(errorCode);
- builder.setNeutralButton(R.string.accept, null);
- builder.create().show();
- }
- });
-
- }
-
- protected void showAddToRosterDialog(final Conversation conversation) {
- showAddToRosterDialog(conversation.getContact());
- }
-
- protected void showAddToRosterDialog(final Contact contact) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(contact.getJid().toString());
- builder.setMessage(getString(R.string.not_in_roster));
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.setPositiveButton(getString(R.string.add_contact),
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- final Jid jid = contact.getJid();
- Account account = contact.getAccount();
- Contact contact = account.getRoster().getContact(jid);
- xmppConnectionService.createContact(contact);
- }
- });
- builder.create().show();
- }
-
- private void showAskForPresenceDialog(final Contact contact) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(contact.getJid().toString());
- builder.setMessage(R.string.request_presence_updates);
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(R.string.request_now,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (xmppConnectionServiceBound) {
- xmppConnectionService.sendPresencePacket(contact
- .getAccount(), xmppConnectionService
- .getPresenceGenerator()
- .requestPresenceUpdatesFrom(contact));
- }
- }
- });
- builder.create().show();
- }
-
- private void warnMutalPresenceSubscription(final Conversation conversation,
- final OnPresenceSelected listener) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(conversation.getContact().getJid().toString());
- builder.setMessage(R.string.without_mutual_presence_updates);
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(R.string.ignore, new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- conversation.setNextCounterpart(null);
- if (listener != null) {
- listener.onPresenceSelected();
- }
- }
- });
- builder.create().show();
- }
-
- protected void quickEdit(String previousValue, int hint, OnValueEdited callback) {
- quickEdit(previousValue, callback, hint, false);
- }
-
- protected void quickPasswordEdit(String previousValue, OnValueEdited callback) {
- quickEdit(previousValue, callback, R.string.password, true);
- }
-
- @SuppressLint("InflateParams")
- private void quickEdit(final String previousValue,
- final OnValueEdited callback,
- final int hint,
- boolean password) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- View view = getLayoutInflater().inflate(R.layout.quickedit, null);
- final EditText editor = (EditText) view.findViewById(R.id.editor);
- OnClickListener mClickListener = new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- String value = editor.getText().toString();
- if (!value.equals(previousValue) && value.trim().length() > 0) {
- callback.onValueEdited(value);
- }
- }
- };
- if (password) {
- editor.setInputType(InputType.TYPE_CLASS_TEXT
- | InputType.TYPE_TEXT_VARIATION_PASSWORD);
- builder.setPositiveButton(R.string.accept, mClickListener);
- } else {
- builder.setPositiveButton(R.string.edit, mClickListener);
- }
- if (hint != 0) {
- editor.setHint(hint);
- }
- editor.requestFocus();
- editor.setText("");
- if (previousValue != null) {
- editor.getText().append(previousValue);
- }
- builder.setView(view);
- builder.setNegativeButton(R.string.cancel, null);
- builder.create().show();
- }
-
- public boolean hasStoragePermission(int requestCode) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestCode);
- return false;
- } else {
- return true;
- }
- } else {
- return true;
- }
- }
-
- public boolean hasMicPermission(int requestCode) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- if (checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, requestCode);
- return false;
- } else {
- return true;
- }
- } else {
- return true;
- }
- }
-
- public boolean hasLocationPermission(int requestCode) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, requestCode);
- requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, requestCode);
- return false;
- } else {
- return true;
- }
- } else {
- return true;
- }
- }
-
- public void selectPresence(final Conversation conversation,
- final OnPresenceSelected listener) {
- final Contact contact = conversation.getContact();
- if (conversation.hasValidOtrSession()) {
- SessionID id = conversation.getOtrSession().getSessionID();
- Jid jid;
- try {
- jid = Jid.fromString(id.getAccountID() + "/" + id.getUserID());
- } catch (InvalidJidException e) {
- jid = null;
- }
- conversation.setNextCounterpart(jid);
- listener.onPresenceSelected();
- } else if (!contact.showInRoster()) {
- showAddToRosterDialog(conversation);
- } else {
- final Presences presences = contact.getPresences();
- if (presences.size() == 0) {
- if (!contact.getOption(Contact.Options.TO)
- && !contact.getOption(Contact.Options.ASKING)
- && contact.getAccount().getStatus() == Account.State.ONLINE) {
- showAskForPresenceDialog(contact);
- } else if (!contact.getOption(Contact.Options.TO)
- || !contact.getOption(Contact.Options.FROM)) {
- warnMutalPresenceSubscription(conversation, listener);
- } else {
- conversation.setNextCounterpart(null);
- listener.onPresenceSelected();
- }
- } else if (presences.size() == 1) {
- String presence = presences.toResourceArray()[0];
- try {
- conversation.setNextCounterpart(Jid.fromParts(contact.getJid().getLocalpart(),contact.getJid().getDomainpart(),presence));
- } catch (InvalidJidException e) {
- conversation.setNextCounterpart(null);
- }
- listener.onPresenceSelected();
- } else {
- showPresenceSelectionDialog(presences,conversation,listener);
- }
- }
- }
-
- private void showPresenceSelectionDialog(Presences presences, final Conversation conversation, final OnPresenceSelected listener) {
- final Contact contact = conversation.getContact();
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(getString(R.string.choose_presence));
- final String[] resourceArray = presences.toResourceArray();
- Pair<Map<String, String>, Map<String, String>> typeAndName = presences.toTypeAndNameMap();
- final Map<String,String> resourceTypeMap = typeAndName.first;
- final Map<String,String> resourceNameMap = typeAndName.second;
- final String[] readableIdentities = new String[resourceArray.length];
- final AtomicInteger selectedResource = new AtomicInteger(0);
- for (int i = 0; i < resourceArray.length; ++i) {
- String resource = resourceArray[i];
- if (resource.equals(contact.getLastResource())) {
- selectedResource.set(i);
- }
- String type = resourceTypeMap.get(resource);
- String name = resourceNameMap.get(resource);
- if (type != null) {
- if (Collections.frequency(resourceTypeMap.values(),type) == 1) {
- readableIdentities[i] = UIHelper.tranlasteType(this,type);
- } else if (name != null) {
- if (Collections.frequency(resourceNameMap.values(), name) == 1
- || CryptoHelper.UUID_PATTERN.matcher(resource).matches()) {
- readableIdentities[i] = UIHelper.tranlasteType(this,type) + " (" + name+")";
- } else {
- readableIdentities[i] = UIHelper.tranlasteType(this,type) + " (" + name +" / " + resource+")";
- }
- } else {
- readableIdentities[i] = UIHelper.tranlasteType(this,type) + " (" + resource+")";
- }
- } else {
- readableIdentities[i] = resource;
- }
- }
- builder.setSingleChoiceItems(readableIdentities,
- selectedResource.get(),
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- selectedResource.set(which);
- }
- });
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(R.string.ok, new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- try {
- Jid next = Jid.fromParts(contact.getJid().getLocalpart(),contact.getJid().getDomainpart(),resourceArray[selectedResource.get()]);
- conversation.setNextCounterpart(next);
- } catch (InvalidJidException e) {
- conversation.setNextCounterpart(null);
- }
- listener.onPresenceSelected();
- }
- });
- builder.create().show();
- }
-
- protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == REQUEST_INVITE_TO_CONVERSATION && resultCode == RESULT_OK) {
- mPendingConferenceInvite = ConferenceInvite.parse(data);
- if (xmppConnectionServiceBound && mPendingConferenceInvite != null) {
- if (mPendingConferenceInvite.execute(this)) {
- mToast = Toast.makeText(this, R.string.creating_conference, Toast.LENGTH_LONG);
- mToast.show();
- }
- mPendingConferenceInvite = null;
- }
- }
- }
-
-
- private UiCallback<Conversation> adhocCallback = new UiCallback<Conversation>() {
- @Override
- public void success(final Conversation conversation) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- switchToConversation(conversation);
- hideToast();
- }
- });
- }
-
- @Override
- public void error(final int errorCode, Conversation object) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- replaceToast(getString(errorCode));
- }
- });
- }
-
- @Override
- public void userInputRequried(PendingIntent pi, Conversation object) {
-
- }
- };
-
- 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 getUnencryptedTextColor() {
- return this.mColorWhite;
- }
-
- 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));
- }
-
- public boolean copyTextToClipboard(String text, int labelResId) {
- ClipboardManager mClipBoardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
- String label = getResources().getString(labelResId);
- if (mClipBoardManager != null) {
- ClipData mClipData = ClipData.newPlainText(label, text);
- mClipBoardManager.setPrimaryClip(mClipData);
- return true;
- }
- return false;
- }
-
- protected void registerNdefPushMessageCallback() {
- NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
- if (nfcAdapter != null && nfcAdapter.isEnabled()) {
- nfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
- @Override
- public NdefMessage createNdefMessage(NfcEvent nfcEvent) {
- return new NdefMessage(new NdefRecord[]{
- NdefRecord.createUri(getShareableUri()),
- NdefRecord.createApplicationRecord("de.pixart.messenger")
- });
- }
- }, this);
- }
- }
-
- protected boolean neverCompressPictures() {
- return getPreferences().getString("picture_compression", "auto").equals("never");
- }
-
- protected boolean manuallyChangePresence() {
- return getPreferences().getBoolean("manually_change_presence", true);
- }
-
- protected void unregisterNdefPushMessageCallback() {
- NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
- if (nfcAdapter != null && nfcAdapter.isEnabled()) {
- nfcAdapter.setNdefPushMessageCallback(null,this);
- }
- }
-
- protected String getShareableUri() {
- return null;
- }
-
- private void inviteUser() {
- Account mAccount = xmppConnectionService.getAccounts().get(0);
- String user = mAccount.getJid().getLocalpart().toString();
- String domain = mAccount.getJid().getDomainpart().toString();
- String inviteURL = Config.inviteUserURL + user + "/" + domain;
- String inviteText = getString(R.string.InviteText, user);
- Intent intent = new Intent(android.content.Intent.ACTION_SEND);
- intent.setType("text/plain");
- intent.putExtra(Intent.EXTRA_SUBJECT, user + " " + getString(R.string.inviteUser_Subject) + " " + getString(R.string.app_name));
- intent.putExtra(Intent.EXTRA_TEXT, inviteText + "\n\n" + inviteURL);
- startActivity(Intent.createChooser(intent, getString(R.string.invite_contact)));
- }
-
- private void createIssue() {
- String IssueURL = Config.ISSUE_URL;
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(Uri.parse(IssueURL));
- startActivity(intent);
- }
-
- protected void shareUri() {
- String uri = getShareableUri();
- if (uri == null || uri.isEmpty()) {
- return;
- }
- Intent shareIntent = new Intent();
- shareIntent.setAction(Intent.ACTION_SEND);
- shareIntent.putExtra(Intent.EXTRA_TEXT, getShareableUri());
- shareIntent.setType("text/plain");
- try {
- startActivity(Intent.createChooser(shareIntent, getText(R.string.share_uri_with)));
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this, R.string.no_application_to_share_uri, Toast.LENGTH_SHORT).show();
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
- if (this.getShareableUri()!=null) {
- this.registerNdefPushMessageCallback();
- }
- }
-
- protected int findTheme() {
- if (getPreferences().getBoolean("use_larger_font", false)) {
- return R.style.ConversationsTheme_LargerText;
- } else {
- return R.style.ConversationsTheme;
- }
- }
-
- @Override
- public void onPause() {
- super.onPause();
- this.unregisterNdefPushMessageCallback();
- }
-
- protected void showQrCode() {
- String uri = getShareableUri();
- if (uri!=null) {
- Point size = new Point();
- getWindowManager().getDefaultDisplay().getSize(size);
- final int width = (size.x < size.y ? size.x : size.y);
- Bitmap bitmap = createQrCodeBitmap(uri, width);
- ImageView view = new ImageView(this);
- view.setImageBitmap(bitmap);
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setView(view);
- builder.create().show();
- }
- }
-
- protected Bitmap createQrCodeBitmap(String input, int size) {
- Log.d(Config.LOGTAG,"qr code requested size: "+size);
- try {
- final QRCodeWriter QR_CODE_WRITER = new QRCodeWriter();
- final Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
- hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
- final BitMatrix result = QR_CODE_WRITER.encode(input, BarcodeFormat.QR_CODE, size, size, hints);
- final int width = result.getWidth();
- final int height = result.getHeight();
- final int[] pixels = new int[width * height];
- for (int y = 0; y < height; y++) {
- final int offset = y * width;
- for (int x = 0; x < width; x++) {
- pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.TRANSPARENT;
- }
- }
- final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Log.d(Config.LOGTAG,"output size: "+width+"x"+height);
- bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
- return bitmap;
- } catch (final WriterException e) {
- return null;
- }
- }
-
- protected Account extractAccount(Intent intent) {
- String jid = intent != null ? intent.getStringExtra(EXTRA_ACCOUNT) : null;
- try {
- return jid != null ? xmppConnectionService.findAccountByJid(Jid.fromString(jid)) : null;
- } catch (InvalidJidException e) {
- return null;
- }
- }
-
- public static class ConferenceInvite {
- private String uuid;
- private List<Jid> jids = new ArrayList<>();
-
- public static ConferenceInvite parse(Intent data) {
- ConferenceInvite invite = new ConferenceInvite();
- invite.uuid = data.getStringExtra("conversation");
- if (invite.uuid == null) {
- return null;
- }
- try {
- if (data.getBooleanExtra("multiple", false)) {
- String[] toAdd = data.getStringArrayExtra("contacts");
- for (String item : toAdd) {
- invite.jids.add(Jid.fromString(item));
- }
- } else {
- invite.jids.add(Jid.fromString(data.getStringExtra("contact")));
- }
- } catch (final InvalidJidException ignored) {
- return null;
- }
- return invite;
- }
-
- public boolean execute(XmppActivity activity) {
- XmppConnectionService service = activity.xmppConnectionService;
- Conversation conversation = service.findConversationByUuid(this.uuid);
- if (conversation == null) {
- return false;
- }
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- for (Jid jid : jids) {
- service.invite(conversation, jid);
- }
- return false;
- } else {
- jids.add(conversation.getJid().toBareJid());
- service.createAdhocConference(conversation.getAccount(), null, jids, activity.adhocCallback);
- return true;
- }
- }
- }
-
- public AvatarService avatarService() {
- return xmppConnectionService.getAvatarService();
- }
-
- class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
- private final WeakReference<ImageView> imageViewReference;
- private Message message = null;
-
- public BitmapWorkerTask(ImageView imageView) {
- imageViewReference = new WeakReference<>(imageView);
- }
-
- @Override
- protected Bitmap doInBackground(Message... params) {
- if (isCancelled()) {
- return null;
- }
- message = params[0];
- try {
- return xmppConnectionService.getFileBackend().getThumbnail(
- message, (int) (metrics.density * 288), false);
- } catch (FileNotFoundException e) {
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- if (bitmap != null && !isCancelled()) {
- final ImageView imageView = imageViewReference.get();
- if (imageView != null) {
- imageView.setImageBitmap(bitmap);
- imageView.setBackgroundColor(0x00000000);
- }
- }
- }
- }
-
- public void loadBitmap(Message message, ImageView imageView) {
- Bitmap bm;
- try {
- bm = xmppConnectionService.getFileBackend().getThumbnail(message,
- (int) (metrics.density * 288), true);
- } catch (FileNotFoundException e) {
- bm = null;
- }
- if (bm != null) {
- cancelPotentialWork(message, imageView);
- imageView.setImageBitmap(bm);
- imageView.setBackgroundColor(0x00000000);
- } else {
- if (cancelPotentialWork(message, imageView)) {
- imageView.setBackgroundColor(0xff333333);
- imageView.setImageDrawable(null);
- final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
- final AsyncDrawable asyncDrawable = new AsyncDrawable(
- getResources(), null, task);
- imageView.setImageDrawable(asyncDrawable);
- try {
- task.execute(message);
- } catch (final RejectedExecutionException ignored) {
- ignored.printStackTrace();
- }
- }
- }
- }
-
- public static boolean cancelPotentialWork(Message message, ImageView imageView) {
- final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
-
- if (bitmapWorkerTask != null) {
- final Message oldMessage = bitmapWorkerTask.message;
- if (oldMessage == null || message != oldMessage) {
- bitmapWorkerTask.cancel(true);
- } else {
- return false;
- }
- }
- return true;
- }
-
- private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
- if (imageView != null) {
- final Drawable drawable = imageView.getDrawable();
- if (drawable instanceof AsyncDrawable) {
- final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
- return asyncDrawable.getBitmapWorkerTask();
- }
- }
- return null;
- }
-
- static class AsyncDrawable extends BitmapDrawable {
- private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
-
- public AsyncDrawable(Resources res, Bitmap bitmap,
- BitmapWorkerTask bitmapWorkerTask) {
- super(res, bitmap);
- bitmapWorkerTaskReference = new WeakReference<>(
- bitmapWorkerTask);
- }
-
- public BitmapWorkerTask getBitmapWorkerTask() {
- return bitmapWorkerTaskReference.get();
- }
- }
+ protected static final int REQUEST_ANNOUNCE_PGP = 0x0101;
+ protected static final int REQUEST_INVITE_TO_CONVERSATION = 0x0102;
+ protected static final int REQUEST_CHOOSE_PGP_ID = 0x0103;
+ protected static final int REQUEST_BATTERY_OP = 0x13849ff;
+
+ public static final String EXTRA_ACCOUNT = "account";
+
+ public XmppConnectionService xmppConnectionService;
+ 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 mColorWhite;
+ protected int mColorOrange;
+ protected int mColorGreen;
+ protected int mPrimaryColor;
+
+ protected boolean mUseSubject = true;
+
+ private DisplayMetrics metrics;
+ protected int mTheme;
+ protected boolean mUsingEnterKey = false;
+
+ protected Toast mToast;
+
+ protected void hideToast() {
+ if (mToast != null) {
+ mToast.cancel();
+ }
+ }
+
+ protected void replaceToast(String msg) {
+ replaceToast(msg, true);
+ }
+
+ protected void replaceToast(String msg, boolean showlong) {
+ hideToast();
+ mToast = Toast.makeText(this, msg, showlong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT);
+ mToast.show();
+ }
+
+ public void showProgress() {
+
+ }
+
+ public void closeProgress() {
+
+ }
+
+ protected Runnable onOpenPGPKeyPublished = new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(XmppActivity.this, R.string.openpgp_has_been_published, Toast.LENGTH_SHORT).show();
+ }
+ };
+
+ private long mLastUiRefresh = 0;
+ private Handler mRefreshUiHandler = new Handler();
+ private Runnable mRefreshUiRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mLastUiRefresh = SystemClock.elapsedRealtime();
+ refreshUiReal();
+ }
+ };
+
+ protected ConferenceInvite mPendingConferenceInvite = null;
+
+
+ protected final void refreshUi() {
+ final long diff = SystemClock.elapsedRealtime() - mLastUiRefresh;
+ if (diff > Config.REFRESH_UI_INTERVAL) {
+ mRefreshUiHandler.removeCallbacks(mRefreshUiRunnable);
+ runOnUiThread(mRefreshUiRunnable);
+ } else {
+ final long next = Config.REFRESH_UI_INTERVAL - diff;
+ mRefreshUiHandler.removeCallbacks(mRefreshUiRunnable);
+ mRefreshUiHandler.postDelayed(mRefreshUiRunnable, next);
+ }
+ }
+
+ abstract protected void refreshUiReal();
+
+ protected interface OnValueEdited {
+ public void onValueEdited(String value);
+ }
+
+ public interface OnPresenceSelected {
+ public void onPresenceSelected();
+ }
+
+ protected ServiceConnection mConnection = new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ XmppConnectionBinder binder = (XmppConnectionBinder) service;
+ xmppConnectionService = binder.getService();
+ xmppConnectionServiceBound = true;
+ if (!registeredListeners && shouldRegisterListeners()) {
+ registerListeners();
+ registeredListeners = true;
+ }
+ onBackendConnected();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ xmppConnectionServiceBound = false;
+ }
+ };
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (!xmppConnectionServiceBound) {
+ connectToBackend();
+ } else {
+ if (!registeredListeners) {
+ this.registerListeners();
+ this.registeredListeners = true;
+ }
+ this.onBackendConnected();
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+ protected boolean shouldRegisterListeners() {
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ return !isDestroyed() && !isFinishing();
+ } else {
+ return !isFinishing();
+ }
+ }
+
+ public void connectToBackend() {
+ Intent intent = new Intent(this, XmppConnectionService.class);
+ intent.setAction("ui");
+ startService(intent);
+ bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (xmppConnectionServiceBound) {
+ if (registeredListeners) {
+ this.unregisterListeners();
+ this.registeredListeners = false;
+ }
+ unbindService(mConnection);
+ xmppConnectionServiceBound = false;
+ }
+ }
+
+ protected void hideKeyboard() {
+ InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ View focus = getCurrentFocus();
+
+ if (focus != null) {
+
+ inputManager.hideSoftInputFromWindow(focus.getWindowToken(),
+ InputMethodManager.HIDE_NOT_ALWAYS);
+ }
+ }
+
+ public boolean hasPgp() {
+ return xmppConnectionService.getPgpEngine() != null;
+ }
+
+ public void showInstallPgpDialog() {
+ Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(getString(R.string.openkeychain_required));
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setMessage(getText(R.string.openkeychain_required_long));
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.setNeutralButton(getString(R.string.restart),
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (xmppConnectionServiceBound) {
+ unbindService(mConnection);
+ xmppConnectionServiceBound = false;
+ }
+ stopService(new Intent(XmppActivity.this,
+ XmppConnectionService.class));
+ finish();
+ }
+ });
+ builder.setPositiveButton(getString(R.string.install),
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Uri uri = Uri
+ .parse("market://details?id=org.sufficientlysecure.keychain");
+ Intent marketIntent = new Intent(Intent.ACTION_VIEW,
+ uri);
+ PackageManager manager = getApplicationContext()
+ .getPackageManager();
+ List<ResolveInfo> infos = manager
+ .queryIntentActivities(marketIntent, 0);
+ if (infos.size() > 0) {
+ startActivity(marketIntent);
+ } else {
+ uri = Uri.parse("http://www.openkeychain.org/");
+ Intent browserIntent = new Intent(
+ Intent.ACTION_VIEW, uri);
+ startActivity(browserIntent);
+ }
+ finish();
+ }
+ });
+ builder.create().show();
+ }
+
+ abstract void onBackendConnected();
+
+ protected void registerListeners() {
+ if (this instanceof XmppConnectionService.OnConversationUpdate) {
+ this.xmppConnectionService.setOnConversationListChangedListener((XmppConnectionService.OnConversationUpdate) this);
+ }
+ if (this instanceof XmppConnectionService.OnAccountUpdate) {
+ this.xmppConnectionService.setOnAccountListChangedListener((XmppConnectionService.OnAccountUpdate) this);
+ }
+ if (this instanceof XmppConnectionService.OnCaptchaRequested) {
+ this.xmppConnectionService.setOnCaptchaRequestedListener((XmppConnectionService.OnCaptchaRequested) this);
+ }
+ if (this instanceof XmppConnectionService.OnRosterUpdate) {
+ this.xmppConnectionService.setOnRosterUpdateListener((XmppConnectionService.OnRosterUpdate) this);
+ }
+ if (this instanceof XmppConnectionService.OnMucRosterUpdate) {
+ this.xmppConnectionService.setOnMucRosterUpdateListener((XmppConnectionService.OnMucRosterUpdate) this);
+ }
+ if (this instanceof OnUpdateBlocklist) {
+ this.xmppConnectionService.setOnUpdateBlocklistListener((OnUpdateBlocklist) this);
+ }
+ if (this instanceof XmppConnectionService.OnShowErrorToast) {
+ this.xmppConnectionService.setOnShowErrorToastListener((XmppConnectionService.OnShowErrorToast) this);
+ }
+ if (this instanceof OnKeyStatusUpdated) {
+ this.xmppConnectionService.setOnKeyStatusUpdatedListener((OnKeyStatusUpdated) this);
+ }
+ }
+
+ protected void unregisterListeners() {
+ if (this instanceof XmppConnectionService.OnConversationUpdate) {
+ this.xmppConnectionService.removeOnConversationListChangedListener();
+ }
+ if (this instanceof XmppConnectionService.OnAccountUpdate) {
+ this.xmppConnectionService.removeOnAccountListChangedListener();
+ }
+ if (this instanceof XmppConnectionService.OnCaptchaRequested) {
+ this.xmppConnectionService.removeOnCaptchaRequestedListener();
+ }
+ if (this instanceof XmppConnectionService.OnRosterUpdate) {
+ this.xmppConnectionService.removeOnRosterUpdateListener();
+ }
+ if (this instanceof XmppConnectionService.OnMucRosterUpdate) {
+ this.xmppConnectionService.removeOnMucRosterUpdateListener();
+ }
+ if (this instanceof OnUpdateBlocklist) {
+ this.xmppConnectionService.removeOnUpdateBlocklistListener();
+ }
+ if (this instanceof XmppConnectionService.OnShowErrorToast) {
+ this.xmppConnectionService.removeOnShowErrorToastListener();
+ }
+ if (this instanceof OnKeyStatusUpdated) {
+ this.xmppConnectionService.removeOnNewKeysAvailableListener();
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_invite_user:
+ inviteUser();
+ break;
+ case R.id.action_create_issue:
+ createIssue();
+ break;
+ case R.id.action_settings:
+ startActivity(new Intent(this, SettingsActivity.class));
+ break;
+ case R.id.action_check_updates:
+ if (xmppConnectionService.hasInternetConnection()) {
+ startActivity(new Intent(this, UpdaterActivity.class));
+ } else {
+ Toast.makeText(this, R.string.account_status_no_internet, Toast.LENGTH_LONG).show();
+ }
+ break;
+ case R.id.action_accounts:
+ final Intent intent = new Intent(getApplicationContext(), EditAccountActivity.class);
+ Account mAccount = xmppConnectionService.getAccounts().get(0);
+ intent.putExtra("jid", mAccount.getJid().toBareJid().toString());
+ intent.putExtra("init", false);
+ startActivity(intent);
+ break;
+ case android.R.id.home:
+ finish();
+ break;
+ case R.id.action_show_qr_code:
+ showQrCode();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ 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);
+ mColorWhite = getResources().getColor(R.color.white70);
+ mColorOrange = getResources().getColor(R.color.orange500);
+ mColorGreen = getResources().getColor(R.color.realgreen);
+ 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);
+ }
+ }
+
+ protected boolean isOptimizingBattery() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
+ return !pm.isIgnoringBatteryOptimizations(getPackageName());
+ } else {
+ return false;
+ }
+ }
+
+ protected boolean isAffectedByDataSaver() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+ return cm.isActiveNetworkMetered()
+ && cm.getRestrictBackgroundStatus() == ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+ } else {
+ return false;
+ }
+ }
+
+ 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);
+ }
+
+ public void switchToConversation(Conversation conversation, String text,
+ boolean newTask) {
+ switchToConversation(conversation, text, null, false, newTask);
+ }
+
+ public void highlightInMuc(Conversation conversation, String nick) {
+ switchToConversation(conversation, null, nick, false, false);
+ }
+
+ public void privateMsgInMuc(Conversation conversation, String nick) {
+ switchToConversation(conversation, null, nick, true, false);
+ }
+
+ private void switchToConversation(Conversation conversation, String text, String nick, boolean pm, boolean newTask) {
+ Intent viewConversationIntent = new Intent(this,
+ ConversationActivity.class);
+ viewConversationIntent.setAction(ConversationActivity.ACTION_VIEW_CONVERSATION);
+ viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
+ conversation.getUuid());
+ if (text != null) {
+ viewConversationIntent.putExtra(ConversationActivity.TEXT, text);
+ }
+ if (nick != null) {
+ viewConversationIntent.putExtra(ConversationActivity.NICK, nick);
+ viewConversationIntent.putExtra(ConversationActivity.PRIVATE_MESSAGE, pm);
+ }
+ if (newTask) {
+ viewConversationIntent.setFlags(viewConversationIntent.getFlags()
+ | Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ } else {
+ viewConversationIntent.setFlags(viewConversationIntent.getFlags()
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ }
+ startActivity(viewConversationIntent);
+ finish();
+ }
+
+ public void switchToContactDetails(Contact contact) {
+ switchToContactDetails(contact, null);
+ }
+
+ public void switchToContactDetails(Contact contact, String messageFingerprint) {
+ Intent intent = new Intent(this, ContactDetailsActivity.class);
+ intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT);
+ intent.putExtra(EXTRA_ACCOUNT, contact.getAccount().getJid().toBareJid().toString());
+ intent.putExtra("contact", contact.getJid().toString());
+ intent.putExtra("fingerprint", messageFingerprint);
+ startActivity(intent);
+ }
+
+ public void switchToAccount(Account account) {
+ switchToAccount(account, false);
+ }
+
+ public void switchToAccount(Account account, boolean init) {
+ Intent intent = new Intent(this, EditAccountActivity.class);
+ intent.putExtra("jid", account.getJid().toBareJid().toString());
+ intent.putExtra("init", init);
+ startActivity(intent);
+ }
+
+ protected void inviteToConversation(Conversation conversation) {
+ Intent intent = new Intent(getApplicationContext(),
+ ChooseContactActivity.class);
+ List<String> contacts = new ArrayList<>();
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ for (MucOptions.User user : conversation.getMucOptions().getUsers(false)) {
+ Jid jid = user.getRealJid();
+ if (jid != null) {
+ contacts.add(jid.toBareJid().toString());
+ }
+ }
+ } else {
+ contacts.add(conversation.getJid().toBareJid().toString());
+ }
+ intent.putExtra("filter_contacts", contacts.toArray(new String[contacts.size()]));
+ intent.putExtra("conversation", conversation.getUuid());
+ intent.putExtra("multiple", true);
+ intent.putExtra("show_enter_jid", true);
+ intent.putExtra(EXTRA_ACCOUNT, conversation.getAccount().getJid().toBareJid().toString());
+ startActivityForResult(intent, REQUEST_INVITE_TO_CONVERSATION);
+ }
+
+ protected void announcePgp(Account account, final Conversation conversation, final Runnable onSuccess) {
+ if (account.getPgpId() == 0) {
+ choosePgpSignId(account);
+ } else {
+ String status = null;
+ if (manuallyChangePresence()) {
+ status = account.getPresenceStatusMessage();
+ }
+ if (status == null) {
+ status = "";
+ }
+ xmppConnectionService.getPgpEngine().generateSignature(account, status, new UiCallback<Account>() {
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Account account) {
+ try {
+ startIntentSenderForResult(pi.getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
+ } catch (final SendIntentException ignored) {
+ }
+ }
+
+ @Override
+ public void success(Account account) {
+ xmppConnectionService.databaseBackend.updateAccount(account);
+ xmppConnectionService.sendPresence(account);
+ if (conversation != null) {
+ conversation.setNextEncryption(Message.ENCRYPTION_PGP);
+ xmppConnectionService.updateConversation(conversation);
+ refreshUi();
+ }
+ if (onSuccess != null) {
+ runOnUiThread(onSuccess);
+ }
+ }
+
+ @Override
+ public void error(int error, Account account) {
+ if (error == 0 && account != null) {
+ account.setPgpSignId(0);
+ account.unsetPgpSignature();
+ xmppConnectionService.databaseBackend.updateAccount(account);
+ choosePgpSignId(account);
+ } else {
+ displayErrorDialog(error);
+ }
+ }
+ });
+ }
+ }
+
+ protected boolean noAccountUsesPgp() {
+ if (!hasPgp()) {
+ return true;
+ }
+ for (Account account : xmppConnectionService.getAccounts()) {
+ if (account.getPgpId() != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @SuppressWarnings("deprecation")
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ protected void setListItemBackgroundOnView(View view) {
+ int sdk = android.os.Build.VERSION.SDK_INT;
+ if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+ view.setBackgroundDrawable(getResources().getDrawable(R.drawable.greybackground));
+ } else {
+ view.setBackground(getResources().getDrawable(R.drawable.greybackground));
+ }
+ }
+
+ protected void choosePgpSignId(Account account) {
+ xmppConnectionService.getPgpEngine().chooseKey(account, new UiCallback<Account>() {
+ @Override
+ public void success(Account account1) {
+ }
+
+ @Override
+ public void error(int errorCode, Account object) {
+
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Account object) {
+ try {
+ startIntentSenderForResult(pi.getIntentSender(),
+ REQUEST_CHOOSE_PGP_ID, null, 0, 0, 0);
+ } catch (final SendIntentException ignored) {
+ }
+ }
+ });
+ }
+
+ protected void displayErrorDialog(final int errorCode) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(
+ XmppActivity.this);
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setTitle(getString(R.string.error));
+ builder.setMessage(errorCode);
+ builder.setNeutralButton(R.string.accept, null);
+ builder.create().show();
+ }
+ });
+
+ }
+
+ protected void showAddToRosterDialog(final Conversation conversation) {
+ showAddToRosterDialog(conversation.getContact());
+ }
+
+ protected void showAddToRosterDialog(final Contact contact) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(contact.getJid().toString());
+ builder.setMessage(getString(R.string.not_in_roster));
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.setPositiveButton(getString(R.string.add_contact),
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final Jid jid = contact.getJid();
+ Account account = contact.getAccount();
+ Contact contact = account.getRoster().getContact(jid);
+ xmppConnectionService.createContact(contact);
+ }
+ });
+ builder.create().show();
+ }
+
+ private void showAskForPresenceDialog(final Contact contact) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(contact.getJid().toString());
+ builder.setMessage(R.string.request_presence_updates);
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.request_now,
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (xmppConnectionServiceBound) {
+ xmppConnectionService.sendPresencePacket(contact
+ .getAccount(), xmppConnectionService
+ .getPresenceGenerator()
+ .requestPresenceUpdatesFrom(contact));
+ }
+ }
+ });
+ builder.create().show();
+ }
+
+ private void warnMutalPresenceSubscription(final Conversation conversation,
+ final OnPresenceSelected listener) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(conversation.getContact().getJid().toString());
+ builder.setMessage(R.string.without_mutual_presence_updates);
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.ignore, new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ conversation.setNextCounterpart(null);
+ if (listener != null) {
+ listener.onPresenceSelected();
+ }
+ }
+ });
+ builder.create().show();
+ }
+
+ protected void quickEdit(String previousValue, int hint, OnValueEdited callback) {
+ quickEdit(previousValue, callback, hint, false);
+ }
+
+ protected void quickPasswordEdit(String previousValue, OnValueEdited callback) {
+ quickEdit(previousValue, callback, R.string.password, true);
+ }
+
+ @SuppressLint("InflateParams")
+ private void quickEdit(final String previousValue,
+ final OnValueEdited callback,
+ final int hint,
+ boolean password) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ View view = getLayoutInflater().inflate(R.layout.quickedit, null);
+ final EditText editor = (EditText) view.findViewById(R.id.editor);
+ OnClickListener mClickListener = new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String value = editor.getText().toString();
+ if (!value.equals(previousValue) && value.trim().length() > 0) {
+ callback.onValueEdited(value);
+ }
+ }
+ };
+ if (password) {
+ editor.setInputType(InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ builder.setPositiveButton(R.string.accept, mClickListener);
+ } else {
+ builder.setPositiveButton(R.string.edit, mClickListener);
+ }
+ if (hint != 0) {
+ editor.setHint(hint);
+ }
+ editor.requestFocus();
+ editor.setText("");
+ if (previousValue != null) {
+ editor.getText().append(previousValue);
+ }
+ builder.setView(view);
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.create().show();
+ }
+
+ public boolean hasStoragePermission(int requestCode) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestCode);
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ public boolean hasMicPermission(int requestCode) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, requestCode);
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ public boolean hasLocationPermission(int requestCode) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, requestCode);
+ requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, requestCode);
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ public void selectPresence(final Conversation conversation,
+ final OnPresenceSelected listener) {
+ final Contact contact = conversation.getContact();
+ if (conversation.hasValidOtrSession()) {
+ SessionID id = conversation.getOtrSession().getSessionID();
+ Jid jid;
+ try {
+ jid = Jid.fromString(id.getAccountID() + "/" + id.getUserID());
+ } catch (InvalidJidException e) {
+ jid = null;
+ }
+ conversation.setNextCounterpart(jid);
+ listener.onPresenceSelected();
+ } else if (!contact.showInRoster()) {
+ showAddToRosterDialog(conversation);
+ } else {
+ final Presences presences = contact.getPresences();
+ if (presences.size() == 0) {
+ if (!contact.getOption(Contact.Options.TO)
+ && !contact.getOption(Contact.Options.ASKING)
+ && contact.getAccount().getStatus() == Account.State.ONLINE) {
+ showAskForPresenceDialog(contact);
+ } else if (!contact.getOption(Contact.Options.TO)
+ || !contact.getOption(Contact.Options.FROM)) {
+ warnMutalPresenceSubscription(conversation, listener);
+ } else {
+ conversation.setNextCounterpart(null);
+ listener.onPresenceSelected();
+ }
+ } else if (presences.size() == 1) {
+ String presence = presences.toResourceArray()[0];
+ try {
+ conversation.setNextCounterpart(Jid.fromParts(contact.getJid().getLocalpart(), contact.getJid().getDomainpart(), presence));
+ } catch (InvalidJidException e) {
+ conversation.setNextCounterpart(null);
+ }
+ listener.onPresenceSelected();
+ } else {
+ showPresenceSelectionDialog(presences, conversation, listener);
+ }
+ }
+ }
+
+ private void showPresenceSelectionDialog(Presences presences, final Conversation conversation, final OnPresenceSelected listener) {
+ final Contact contact = conversation.getContact();
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(getString(R.string.choose_presence));
+ final String[] resourceArray = presences.toResourceArray();
+ Pair<Map<String, String>, Map<String, String>> typeAndName = presences.toTypeAndNameMap();
+ final Map<String, String> resourceTypeMap = typeAndName.first;
+ final Map<String, String> resourceNameMap = typeAndName.second;
+ final String[] readableIdentities = new String[resourceArray.length];
+ final AtomicInteger selectedResource = new AtomicInteger(0);
+ for (int i = 0; i < resourceArray.length; ++i) {
+ String resource = resourceArray[i];
+ if (resource.equals(contact.getLastResource())) {
+ selectedResource.set(i);
+ }
+ String type = resourceTypeMap.get(resource);
+ String name = resourceNameMap.get(resource);
+ if (type != null) {
+ if (Collections.frequency(resourceTypeMap.values(), type) == 1) {
+ readableIdentities[i] = UIHelper.tranlasteType(this, type);
+ } else if (name != null) {
+ if (Collections.frequency(resourceNameMap.values(), name) == 1
+ || CryptoHelper.UUID_PATTERN.matcher(resource).matches()) {
+ readableIdentities[i] = UIHelper.tranlasteType(this, type) + " (" + name + ")";
+ } else {
+ readableIdentities[i] = UIHelper.tranlasteType(this, type) + " (" + name + " / " + resource + ")";
+ }
+ } else {
+ readableIdentities[i] = UIHelper.tranlasteType(this, type) + " (" + resource + ")";
+ }
+ } else {
+ readableIdentities[i] = resource;
+ }
+ }
+ builder.setSingleChoiceItems(readableIdentities,
+ selectedResource.get(),
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ selectedResource.set(which);
+ }
+ });
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.ok, new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ try {
+ Jid next = Jid.fromParts(contact.getJid().getLocalpart(), contact.getJid().getDomainpart(), resourceArray[selectedResource.get()]);
+ conversation.setNextCounterpart(next);
+ } catch (InvalidJidException e) {
+ conversation.setNextCounterpart(null);
+ }
+ listener.onPresenceSelected();
+ }
+ });
+ builder.create().show();
+ }
+
+ protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == REQUEST_INVITE_TO_CONVERSATION && resultCode == RESULT_OK) {
+ mPendingConferenceInvite = ConferenceInvite.parse(data);
+ if (xmppConnectionServiceBound && mPendingConferenceInvite != null) {
+ if (mPendingConferenceInvite.execute(this)) {
+ mToast = Toast.makeText(this, R.string.creating_conference, Toast.LENGTH_LONG);
+ mToast.show();
+ }
+ mPendingConferenceInvite = null;
+ }
+ }
+ }
+
+
+ private UiCallback<Conversation> adhocCallback = new UiCallback<Conversation>() {
+ @Override
+ public void success(final Conversation conversation) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ switchToConversation(conversation);
+ hideToast();
+ }
+ });
+ }
+
+ @Override
+ public void error(final int errorCode, Conversation object) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ replaceToast(getString(errorCode));
+ }
+ });
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Conversation object) {
+
+ }
+ };
+
+ 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 getUnencryptedTextColor() {
+ return this.mColorWhite;
+ }
+
+ 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));
+ }
+
+ public boolean copyTextToClipboard(String text, int labelResId) {
+ ClipboardManager mClipBoardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+ String label = getResources().getString(labelResId);
+ if (mClipBoardManager != null) {
+ ClipData mClipData = ClipData.newPlainText(label, text);
+ mClipBoardManager.setPrimaryClip(mClipData);
+ return true;
+ }
+ return false;
+ }
+
+ protected void registerNdefPushMessageCallback() {
+ NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ if (nfcAdapter != null && nfcAdapter.isEnabled()) {
+ nfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
+ @Override
+ public NdefMessage createNdefMessage(NfcEvent nfcEvent) {
+ return new NdefMessage(new NdefRecord[]{
+ NdefRecord.createUri(getShareableUri()),
+ NdefRecord.createApplicationRecord("de.pixart.messenger")
+ });
+ }
+ }, this);
+ }
+ }
+
+ protected boolean neverCompressPictures() {
+ return getPreferences().getString("picture_compression", "auto").equals("never");
+ }
+
+ protected boolean manuallyChangePresence() {
+ return getPreferences().getBoolean("manually_change_presence", true);
+ }
+
+ protected void unregisterNdefPushMessageCallback() {
+ NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ if (nfcAdapter != null && nfcAdapter.isEnabled()) {
+ nfcAdapter.setNdefPushMessageCallback(null, this);
+ }
+ }
+
+ protected String getShareableUri() {
+ return null;
+ }
+
+ private void inviteUser() {
+ Account mAccount = xmppConnectionService.getAccounts().get(0);
+ String user = mAccount.getJid().getLocalpart().toString();
+ String domain = mAccount.getJid().getDomainpart().toString();
+ String inviteURL = Config.inviteUserURL + user + "/" + domain;
+ String inviteText = getString(R.string.InviteText, user);
+ Intent intent = new Intent(android.content.Intent.ACTION_SEND);
+ intent.setType("text/plain");
+ intent.putExtra(Intent.EXTRA_SUBJECT, user + " " + getString(R.string.inviteUser_Subject) + " " + getString(R.string.app_name));
+ intent.putExtra(Intent.EXTRA_TEXT, inviteText + "\n\n" + inviteURL);
+ startActivity(Intent.createChooser(intent, getString(R.string.invite_contact)));
+ }
+
+ private void createIssue() {
+ String IssueURL = Config.ISSUE_URL;
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(IssueURL));
+ startActivity(intent);
+ }
+
+ protected void shareUri() {
+ String uri = getShareableUri();
+ if (uri == null || uri.isEmpty()) {
+ return;
+ }
+ Intent shareIntent = new Intent();
+ shareIntent.setAction(Intent.ACTION_SEND);
+ shareIntent.putExtra(Intent.EXTRA_TEXT, getShareableUri());
+ shareIntent.setType("text/plain");
+ try {
+ startActivity(Intent.createChooser(shareIntent, getText(R.string.share_uri_with)));
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(this, R.string.no_application_to_share_uri, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (this.getShareableUri() != null) {
+ this.registerNdefPushMessageCallback();
+ }
+ }
+
+ protected int findTheme() {
+ if (getPreferences().getBoolean("use_larger_font", false)) {
+ return R.style.ConversationsTheme_LargerText;
+ } else {
+ return R.style.ConversationsTheme;
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ this.unregisterNdefPushMessageCallback();
+ }
+
+ protected void showQrCode() {
+ String uri = getShareableUri();
+ if (uri != null) {
+ Point size = new Point();
+ getWindowManager().getDefaultDisplay().getSize(size);
+ final int width = (size.x < size.y ? size.x : size.y);
+ Bitmap bitmap = createQrCodeBitmap(uri, width);
+ ImageView view = new ImageView(this);
+ view.setImageBitmap(bitmap);
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setView(view);
+ builder.create().show();
+ }
+ }
+
+ protected Bitmap createQrCodeBitmap(String input, int size) {
+ Log.d(Config.LOGTAG, "qr code requested size: " + size);
+ try {
+ final QRCodeWriter QR_CODE_WRITER = new QRCodeWriter();
+ final Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
+ hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
+ final BitMatrix result = QR_CODE_WRITER.encode(input, BarcodeFormat.QR_CODE, size, size, hints);
+ final int width = result.getWidth();
+ final int height = result.getHeight();
+ final int[] pixels = new int[width * height];
+ for (int y = 0; y < height; y++) {
+ final int offset = y * width;
+ for (int x = 0; x < width; x++) {
+ pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.TRANSPARENT;
+ }
+ }
+ final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Log.d(Config.LOGTAG, "output size: " + width + "x" + height);
+ bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
+ return bitmap;
+ } catch (final WriterException e) {
+ return null;
+ }
+ }
+
+ protected Account extractAccount(Intent intent) {
+ String jid = intent != null ? intent.getStringExtra(EXTRA_ACCOUNT) : null;
+ try {
+ return jid != null ? xmppConnectionService.findAccountByJid(Jid.fromString(jid)) : null;
+ } catch (InvalidJidException e) {
+ return null;
+ }
+ }
+
+ public static class ConferenceInvite {
+ private String uuid;
+ private List<Jid> jids = new ArrayList<>();
+
+ public static ConferenceInvite parse(Intent data) {
+ ConferenceInvite invite = new ConferenceInvite();
+ invite.uuid = data.getStringExtra("conversation");
+ if (invite.uuid == null) {
+ return null;
+ }
+ try {
+ if (data.getBooleanExtra("multiple", false)) {
+ String[] toAdd = data.getStringArrayExtra("contacts");
+ for (String item : toAdd) {
+ invite.jids.add(Jid.fromString(item));
+ }
+ } else {
+ invite.jids.add(Jid.fromString(data.getStringExtra("contact")));
+ }
+ } catch (final InvalidJidException ignored) {
+ return null;
+ }
+ return invite;
+ }
+
+ public boolean execute(XmppActivity activity) {
+ XmppConnectionService service = activity.xmppConnectionService;
+ Conversation conversation = service.findConversationByUuid(this.uuid);
+ if (conversation == null) {
+ return false;
+ }
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ for (Jid jid : jids) {
+ service.invite(conversation, jid);
+ }
+ return false;
+ } else {
+ jids.add(conversation.getJid().toBareJid());
+ service.createAdhocConference(conversation.getAccount(), null, jids, activity.adhocCallback);
+ return true;
+ }
+ }
+ }
+
+ public AvatarService avatarService() {
+ return xmppConnectionService.getAvatarService();
+ }
+
+ class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
+ private final WeakReference<ImageView> imageViewReference;
+ private Message message = null;
+
+ public BitmapWorkerTask(ImageView imageView) {
+ imageViewReference = new WeakReference<>(imageView);
+ }
+
+ @Override
+ protected Bitmap doInBackground(Message... params) {
+ if (isCancelled()) {
+ return null;
+ }
+ message = params[0];
+ try {
+ return xmppConnectionService.getFileBackend().getThumbnail(
+ message, (int) (metrics.density * 288), false);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ if (bitmap != null && !isCancelled()) {
+ final ImageView imageView = imageViewReference.get();
+ if (imageView != null) {
+ imageView.setImageBitmap(bitmap);
+ imageView.setBackgroundColor(0x00000000);
+ }
+ }
+ }
+ }
+
+ public void loadBitmap(Message message, ImageView imageView) {
+ Bitmap bm;
+ try {
+ bm = xmppConnectionService.getFileBackend().getThumbnail(message,
+ (int) (metrics.density * 288), true);
+ } catch (FileNotFoundException e) {
+ bm = null;
+ }
+ if (bm != null) {
+ cancelPotentialWork(message, imageView);
+ imageView.setImageBitmap(bm);
+ imageView.setBackgroundColor(0x00000000);
+ } else {
+ if (cancelPotentialWork(message, imageView)) {
+ imageView.setBackgroundColor(0xff333333);
+ imageView.setImageDrawable(null);
+ final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+ final AsyncDrawable asyncDrawable = new AsyncDrawable(
+ getResources(), null, task);
+ imageView.setImageDrawable(asyncDrawable);
+ try {
+ task.execute(message);
+ } catch (final RejectedExecutionException ignored) {
+ ignored.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public static boolean cancelPotentialWork(Message message, ImageView imageView) {
+ final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+ if (bitmapWorkerTask != null) {
+ final Message oldMessage = bitmapWorkerTask.message;
+ if (oldMessage == null || message != oldMessage) {
+ bitmapWorkerTask.cancel(true);
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
+ if (imageView != null) {
+ final Drawable drawable = imageView.getDrawable();
+ if (drawable instanceof AsyncDrawable) {
+ final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+ return asyncDrawable.getBitmapWorkerTask();
+ }
+ }
+ return null;
+ }
+
+ static class AsyncDrawable extends BitmapDrawable {
+ private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+ public AsyncDrawable(Resources res, Bitmap bitmap,
+ BitmapWorkerTask bitmapWorkerTask) {
+ super(res, bitmap);
+ bitmapWorkerTaskReference = new WeakReference<>(
+ bitmapWorkerTask);
+ }
+
+ public BitmapWorkerTask getBitmapWorkerTask() {
+ return bitmapWorkerTaskReference.get();
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java
index 812c41ace..f8eb6f4f0 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java
@@ -17,43 +17,43 @@ import de.pixart.messenger.ui.XmppActivity;
public class AccountAdapter extends ArrayAdapter<Account> {
- private XmppActivity activity;
+ private XmppActivity activity;
- public AccountAdapter(XmppActivity activity, List<Account> objects) {
- super(activity, 0, objects);
- this.activity = activity;
- }
+ public AccountAdapter(XmppActivity activity, List<Account> objects) {
+ super(activity, 0, objects);
+ this.activity = activity;
+ }
- @Override
- public View getView(int position, View view, ViewGroup parent) {
- final Account account = getItem(position);
- if (view == null) {
- LayoutInflater inflater = (LayoutInflater) getContext()
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- view = inflater.inflate(R.layout.account_row, parent, false);
- }
- TextView jid = (TextView) view.findViewById(R.id.account_jid);
- if (Config.DOMAIN_LOCK != null) {
- jid.setText(account.getJid().getLocalpart());
- } else {
- jid.setText(account.getJid().toBareJid().toString());
- }
- 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(56)));
- statusView.setText(getContext().getString(account.getStatus().getReadableId()));
- switch (account.getStatus()) {
- case ONLINE:
- statusView.setTextColor(activity.getOnlineColor());
- break;
- case DISABLED:
- case CONNECTING:
- statusView.setTextColor(activity.getSecondaryTextColor());
- break;
- default:
- statusView.setTextColor(activity.getWarningTextColor());
- break;
- }
- return view;
- }
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ final Account account = getItem(position);
+ if (view == null) {
+ LayoutInflater inflater = (LayoutInflater) getContext()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ view = inflater.inflate(R.layout.account_row, parent, false);
+ }
+ TextView jid = (TextView) view.findViewById(R.id.account_jid);
+ if (Config.DOMAIN_LOCK != null) {
+ jid.setText(account.getJid().getLocalpart());
+ } else {
+ jid.setText(account.getJid().toBareJid().toString());
+ }
+ 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(56)));
+ statusView.setText(getContext().getString(account.getStatus().getReadableId()));
+ switch (account.getStatus()) {
+ case ONLINE:
+ statusView.setTextColor(activity.getOnlineColor());
+ break;
+ case DISABLED:
+ case CONNECTING:
+ statusView.setTextColor(activity.getSecondaryTextColor());
+ break;
+ default:
+ statusView.setTextColor(activity.getWarningTextColor());
+ break;
+ }
+ return view;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java
index f19dfa43b..d78c82cb2 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java
@@ -29,13 +29,13 @@ import de.pixart.messenger.xmpp.chatstate.ChatState;
public class ConversationAdapter extends ArrayAdapter<Conversation> {
- private XmppActivity activity;
+ private XmppActivity activity;
- public ConversationAdapter(XmppActivity activity,
- List<Conversation> conversations) {
- super(activity, 0, conversations);
- this.activity = activity;
- }
+ public ConversationAdapter(XmppActivity activity,
+ List<Conversation> conversations) {
+ super(activity, 0, conversations);
+ this.activity = activity;
+ }
public static boolean cancelPotentialWork(Conversation conversation, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
@@ -62,8 +62,8 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
return null;
}
- @Override
- public View getView(int position, View view, ViewGroup parent) {
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.conversation_list_row, parent, false);
@@ -126,23 +126,23 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
}
}
} else {*/
- Pair<String, Boolean> preview = UIHelper.getMessagePreview(activity, message);
- mLastMessage.setVisibility(View.VISIBLE);
- imagePreview.setVisibility(View.GONE);
- mLastMessage.setText(preview.first);
- if (preview.second) {
- if (conversation.isRead()) {
- mLastMessage.setTypeface(null, Typeface.ITALIC);
- } else {
- mLastMessage.setTypeface(null, Typeface.BOLD_ITALIC);
- }
+ Pair<String, Boolean> preview = UIHelper.getMessagePreview(activity, message);
+ mLastMessage.setVisibility(View.VISIBLE);
+ imagePreview.setVisibility(View.GONE);
+ mLastMessage.setText(preview.first);
+ if (preview.second) {
+ if (conversation.isRead()) {
+ mLastMessage.setTypeface(null, Typeface.ITALIC);
} else {
- if (conversation.isRead()) {
- mLastMessage.setTypeface(null, Typeface.NORMAL);
- } else {
- mLastMessage.setTypeface(null, Typeface.BOLD);
- }
+ mLastMessage.setTypeface(null, Typeface.BOLD_ITALIC);
}
+ } else {
+ if (conversation.isRead()) {
+ mLastMessage.setTypeface(null, Typeface.NORMAL);
+ } else {
+ mLastMessage.setTypeface(null, Typeface.BOLD);
+ }
+ }
//}
long muted_till = conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0);
@@ -168,41 +168,41 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
mLastMessage.setTypeface(null, Typeface.BOLD_ITALIC);
}
return view;
- }
-
- public void loadAvatar(Conversation conversation, ImageView imageView) {
- if (cancelPotentialWork(conversation, imageView)) {
- final Bitmap bm = activity.avatarService().get(conversation, activity.getPixel(56), true);
- if (bm != null) {
- cancelPotentialWork(conversation, imageView);
- imageView.setImageBitmap(bm);
- imageView.setBackgroundColor(0x00000000);
- } else {
- imageView.setBackgroundColor(UIHelper.getColorForName(conversation.getName()));
- imageView.setImageDrawable(null);
- final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
- final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
- imageView.setImageDrawable(asyncDrawable);
- try {
- task.execute(conversation);
- } catch (final RejectedExecutionException ignored) {
- }
- }
- }
- }
-
- static class AsyncDrawable extends BitmapDrawable {
- private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
-
- public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
- super(res, bitmap);
- bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
- }
-
- public BitmapWorkerTask getBitmapWorkerTask() {
- return bitmapWorkerTaskReference.get();
- }
- }
+ }
+
+ public void loadAvatar(Conversation conversation, ImageView imageView) {
+ if (cancelPotentialWork(conversation, imageView)) {
+ final Bitmap bm = activity.avatarService().get(conversation, activity.getPixel(56), true);
+ if (bm != null) {
+ cancelPotentialWork(conversation, imageView);
+ imageView.setImageBitmap(bm);
+ imageView.setBackgroundColor(0x00000000);
+ } else {
+ imageView.setBackgroundColor(UIHelper.getColorForName(conversation.getName()));
+ imageView.setImageDrawable(null);
+ final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+ final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
+ imageView.setImageDrawable(asyncDrawable);
+ try {
+ task.execute(conversation);
+ } catch (final RejectedExecutionException ignored) {
+ }
+ }
+ }
+ }
+
+ static class AsyncDrawable extends BitmapDrawable {
+ private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+ public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
+ super(res, bitmap);
+ bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
+ }
+
+ public BitmapWorkerTask getBitmapWorkerTask() {
+ return bitmapWorkerTaskReference.get();
+ }
+ }
class BitmapWorkerTask extends AsyncTask<Conversation, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/KnownHostsAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/KnownHostsAdapter.java
index 2906f16ba..6096a7196 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/KnownHostsAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/KnownHostsAdapter.java
@@ -9,62 +9,62 @@ import java.util.List;
import java.util.Locale;
public class KnownHostsAdapter extends ArrayAdapter<String> {
- private ArrayList<String> domains;
- private Filter domainFilter = new Filter() {
+ private ArrayList<String> domains;
+ private Filter domainFilter = new Filter() {
- @Override
- protected FilterResults performFiltering(CharSequence constraint) {
- if (constraint != null) {
- ArrayList<String> suggestions = new ArrayList<>();
- final String[] split = constraint.toString().split("@");
- if (split.length == 1) {
- for (String domain : domains) {
- suggestions.add(split[0].toLowerCase(Locale
- .getDefault()) + "@" + domain);
- }
- } else if (split.length == 2) {
- for (String domain : domains) {
- if (domain.contentEquals(split[1])) {
- suggestions.clear();
- break;
- } else if (domain.contains(split[1])) {
- suggestions.add(split[0].toLowerCase(Locale
- .getDefault()) + "@" + domain);
- }
- }
- } else {
- return new FilterResults();
- }
- FilterResults filterResults = new FilterResults();
- filterResults.values = suggestions;
- filterResults.count = suggestions.size();
- return filterResults;
- } else {
- return new FilterResults();
- }
- }
+ @Override
+ protected FilterResults performFiltering(CharSequence constraint) {
+ if (constraint != null) {
+ ArrayList<String> suggestions = new ArrayList<>();
+ final String[] split = constraint.toString().split("@");
+ if (split.length == 1) {
+ for (String domain : domains) {
+ suggestions.add(split[0].toLowerCase(Locale
+ .getDefault()) + "@" + domain);
+ }
+ } else if (split.length == 2) {
+ for (String domain : domains) {
+ if (domain.contentEquals(split[1])) {
+ suggestions.clear();
+ break;
+ } else if (domain.contains(split[1])) {
+ suggestions.add(split[0].toLowerCase(Locale
+ .getDefault()) + "@" + domain);
+ }
+ }
+ } else {
+ return new FilterResults();
+ }
+ FilterResults filterResults = new FilterResults();
+ filterResults.values = suggestions;
+ filterResults.count = suggestions.size();
+ return filterResults;
+ } else {
+ return new FilterResults();
+ }
+ }
- @Override
- protected void publishResults(CharSequence constraint,
- FilterResults results) {
- ArrayList filteredList = (ArrayList) results.values;
- if (results != null && results.count > 0) {
- clear();
- for (Object c : filteredList) {
- add((String) c);
- }
- notifyDataSetChanged();
- }
- }
- };
+ @Override
+ protected void publishResults(CharSequence constraint,
+ FilterResults results) {
+ ArrayList filteredList = (ArrayList) results.values;
+ if (results != null && results.count > 0) {
+ clear();
+ for (Object c : filteredList) {
+ add((String) c);
+ }
+ notifyDataSetChanged();
+ }
+ }
+ };
- public KnownHostsAdapter(Context context, int viewResourceId, List<String> mKnownHosts) {
- super(context, viewResourceId, new ArrayList<String>());
- domains = new ArrayList<>(mKnownHosts);
- }
+ public KnownHostsAdapter(Context context, int viewResourceId, List<String> mKnownHosts) {
+ super(context, viewResourceId, new ArrayList<String>());
+ domains = new ArrayList<>(mKnownHosts);
+ }
- @Override
- public Filter getFilter() {
- return domainFilter;
- }
+ @Override
+ public Filter getFilter() {
+ return domainFilter;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java
index 691b901db..1502b187d 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java
@@ -28,156 +28,156 @@ import de.pixart.messenger.utils.UIHelper;
public class ListItemAdapter extends ArrayAdapter<ListItem> {
- protected XmppActivity activity;
- protected boolean showDynamicTags = false;
- private View.OnClickListener onTagTvClick = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (view instanceof TextView && mOnTagClickedListener != null) {
- TextView tv = (TextView) view;
- final String tag = tv.getText().toString();
- mOnTagClickedListener.onTagClicked(tag);
- }
- }
- };
- private OnTagClickedListener mOnTagClickedListener = null;
-
- 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);
- }
-
- @Override
- public View getView(int position, View view, ViewGroup parent) {
- LayoutInflater inflater = (LayoutInflater) getContext()
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- ListItem item = getItem(position);
- if (view == null) {
- view = inflater.inflate(R.layout.contact, parent, false);
- }
- 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);
- FlowLayout tagLayout = (FlowLayout) view.findViewById(R.id.tags);
-
- List<ListItem.Tag> tags = item.getTags(activity);
- if (tags.size() == 0 || !this.showDynamicTags) {
- tagLayout.setVisibility(View.GONE);
- } else {
- tagLayout.setVisibility(View.VISIBLE);
- tagLayout.removeAllViewsInLayout();
- for(ListItem.Tag tag : tags) {
- TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag,tagLayout,false);
- tv.setText(tag.getName());
- tv.setBackgroundColor(tag.getColor());
- tv.setOnClickListener(this.onTagTvClick);
- tagLayout.addView(tv);
- }
- }
- final String jid = item.getDisplayJid();
- if (jid != null) {
- tvJid.setVisibility(View.VISIBLE);
- tvJid.setText(jid);
- } else {
- tvJid.setVisibility(View.GONE);
- }
- tvName.setText(item.getDisplayName());
- loadAvatar(item,picture);
- return view;
- }
-
- public void setOnTagClickedListener(OnTagClickedListener listener) {
- this.mOnTagClickedListener = listener;
- }
-
- public interface OnTagClickedListener {
- void onTagClicked(String tag);
- }
-
- class BitmapWorkerTask extends AsyncTask<ListItem, Void, Bitmap> {
- private final WeakReference<ImageView> imageViewReference;
- private ListItem item = null;
-
- public BitmapWorkerTask(ImageView imageView) {
- imageViewReference = new WeakReference<>(imageView);
- }
-
- @Override
- protected Bitmap doInBackground(ListItem... params) {
- return activity.avatarService().get(params[0], activity.getPixel(48), isCancelled());
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- if (bitmap != null && !isCancelled()) {
- final ImageView imageView = imageViewReference.get();
- if (imageView != null) {
- imageView.setImageBitmap(bitmap);
- imageView.setBackgroundColor(0x00000000);
- }
- }
- }
- }
-
- public void loadAvatar(ListItem item, ImageView imageView) {
- if (cancelPotentialWork(item, imageView)) {
- final Bitmap bm = activity.avatarService().get(item,activity.getPixel(48),true);
- if (bm != null) {
- cancelPotentialWork(item, imageView);
- imageView.setImageBitmap(bm);
- imageView.setBackgroundColor(0x00000000);
- } else {
- imageView.setBackgroundColor(UIHelper.getColorForName(item.getDisplayName()));
- imageView.setImageDrawable(null);
- final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
- final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
- imageView.setImageDrawable(asyncDrawable);
- try {
- task.execute(item);
- } catch (final RejectedExecutionException ignored) {
- }
- }
- }
- }
-
- public static boolean cancelPotentialWork(ListItem item, ImageView imageView) {
- final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
-
- if (bitmapWorkerTask != null) {
- final ListItem oldItem = bitmapWorkerTask.item;
- if (oldItem == null || item != oldItem) {
- bitmapWorkerTask.cancel(true);
- } else {
- return false;
- }
- }
- return true;
- }
-
- private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
- if (imageView != null) {
- final Drawable drawable = imageView.getDrawable();
- if (drawable instanceof AsyncDrawable) {
- final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
- return asyncDrawable.getBitmapWorkerTask();
- }
- }
- return null;
- }
-
- static class AsyncDrawable extends BitmapDrawable {
- private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
-
- public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
- super(res, bitmap);
- bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
- }
-
- public BitmapWorkerTask getBitmapWorkerTask() {
- return bitmapWorkerTaskReference.get();
- }
- }
+ protected XmppActivity activity;
+ protected boolean showDynamicTags = false;
+ private View.OnClickListener onTagTvClick = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (view instanceof TextView && mOnTagClickedListener != null) {
+ TextView tv = (TextView) view;
+ final String tag = tv.getText().toString();
+ mOnTagClickedListener.onTagClicked(tag);
+ }
+ }
+ };
+ private OnTagClickedListener mOnTagClickedListener = null;
+
+ 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);
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ LayoutInflater inflater = (LayoutInflater) getContext()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ ListItem item = getItem(position);
+ if (view == null) {
+ view = inflater.inflate(R.layout.contact, parent, false);
+ }
+ 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);
+ FlowLayout tagLayout = (FlowLayout) view.findViewById(R.id.tags);
+
+ List<ListItem.Tag> tags = item.getTags(activity);
+ if (tags.size() == 0 || !this.showDynamicTags) {
+ tagLayout.setVisibility(View.GONE);
+ } else {
+ tagLayout.setVisibility(View.VISIBLE);
+ tagLayout.removeAllViewsInLayout();
+ for (ListItem.Tag tag : tags) {
+ TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag, tagLayout, false);
+ tv.setText(tag.getName());
+ tv.setBackgroundColor(tag.getColor());
+ tv.setOnClickListener(this.onTagTvClick);
+ tagLayout.addView(tv);
+ }
+ }
+ final String jid = item.getDisplayJid();
+ if (jid != null) {
+ tvJid.setVisibility(View.VISIBLE);
+ tvJid.setText(jid);
+ } else {
+ tvJid.setVisibility(View.GONE);
+ }
+ tvName.setText(item.getDisplayName());
+ loadAvatar(item, picture);
+ return view;
+ }
+
+ public void setOnTagClickedListener(OnTagClickedListener listener) {
+ this.mOnTagClickedListener = listener;
+ }
+
+ public interface OnTagClickedListener {
+ void onTagClicked(String tag);
+ }
+
+ class BitmapWorkerTask extends AsyncTask<ListItem, Void, Bitmap> {
+ private final WeakReference<ImageView> imageViewReference;
+ private ListItem item = null;
+
+ public BitmapWorkerTask(ImageView imageView) {
+ imageViewReference = new WeakReference<>(imageView);
+ }
+
+ @Override
+ protected Bitmap doInBackground(ListItem... params) {
+ return activity.avatarService().get(params[0], activity.getPixel(48), isCancelled());
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ if (bitmap != null && !isCancelled()) {
+ final ImageView imageView = imageViewReference.get();
+ if (imageView != null) {
+ imageView.setImageBitmap(bitmap);
+ imageView.setBackgroundColor(0x00000000);
+ }
+ }
+ }
+ }
+
+ public void loadAvatar(ListItem item, ImageView imageView) {
+ if (cancelPotentialWork(item, imageView)) {
+ final Bitmap bm = activity.avatarService().get(item, activity.getPixel(48), true);
+ if (bm != null) {
+ cancelPotentialWork(item, imageView);
+ imageView.setImageBitmap(bm);
+ imageView.setBackgroundColor(0x00000000);
+ } else {
+ imageView.setBackgroundColor(UIHelper.getColorForName(item.getDisplayName()));
+ imageView.setImageDrawable(null);
+ final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+ final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
+ imageView.setImageDrawable(asyncDrawable);
+ try {
+ task.execute(item);
+ } catch (final RejectedExecutionException ignored) {
+ }
+ }
+ }
+ }
+
+ public static boolean cancelPotentialWork(ListItem item, ImageView imageView) {
+ final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+ if (bitmapWorkerTask != null) {
+ final ListItem oldItem = bitmapWorkerTask.item;
+ if (oldItem == null || item != oldItem) {
+ bitmapWorkerTask.cancel(true);
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
+ if (imageView != null) {
+ final Drawable drawable = imageView.getDrawable();
+ if (drawable instanceof AsyncDrawable) {
+ final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+ return asyncDrawable.getBitmapWorkerTask();
+ }
+ }
+ return null;
+ }
+
+ static class AsyncDrawable extends BitmapDrawable {
+ private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+ public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
+ super(res, bitmap);
+ bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
+ }
+
+ public BitmapWorkerTask getBitmapWorkerTask() {
+ return bitmapWorkerTaskReference.get();
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
index 02cbbdfd0..6cf16fdd5 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
@@ -74,941 +74,941 @@ import nl.changer.audiowife.AudioWife;
public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextView.CopyHandler {
- private static final int SENT = 0;
- private static final int RECEIVED = 1;
- private static final int STATUS = 2;
- private static final Pattern XMPP_PATTERN = Pattern
- .compile("xmpp\\:(?:(?:["
- + Patterns.GOOD_IRI_CHAR
- + "\\;\\/\\?\\@\\&\\=\\#\\~\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])"
- + "|(?:\\%[a-fA-F0-9]{2}))+");
-
- private ConversationActivity activity;
-
- private DisplayMetrics metrics;
-
- private AudioWife audioWife;
-
- private OnContactPictureClicked mOnContactPictureClickedListener;
- private OnContactPictureLongClicked mOnContactPictureLongClickedListener;
-
- private boolean mIndicateReceived = false;
- private final ListSelectionManager listSelectionManager = new ListSelectionManager();
- private HashMap<Integer, AudioWife> audioPlayer;
- 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) {
- this.mOnContactPictureClickedListener = listener;
- }
-
- public void setOnContactPictureLongClicked(
- OnContactPictureLongClicked listener) {
- this.mOnContactPictureLongClickedListener = listener;
- }
-
- @Override
- public int getViewTypeCount() {
- return 3;
- }
-
- public int getItemViewType(Message message) {
- if (message.getType() == Message.TYPE_STATUS) {
- return STATUS;
- } else if (message.getStatus() <= Message.STATUS_RECEIVED) {
- return RECEIVED;
- }
-
- return SENT;
- }
-
- @Override
- public int getItemViewType(int position) {
- return getItemViewType(getItem(position));
- }
-
- private int getMessageTextColor(boolean onDark, boolean primary) {
- if (onDark) {
- return activity.getResources().getColor(primary ? R.color.dark : R.color.primary);
- } else {
- return activity.getResources().getColor(primary ? R.color.dark : R.color.primary);
- }
- }
-
- private void displayStatus(ViewHolder viewHolder, Message message, int type, boolean darkBackground) {
- String filesize = null;
- String info = null;
- boolean error = false;
- if (viewHolder.indicatorReceived != null) {
- viewHolder.indicatorReceived.setVisibility(View.GONE);
- viewHolder.indicatorRead.setVisibility(View.GONE);
- }
-
- if (viewHolder.edit_indicator != null) {
- if (message.edited()) {
- viewHolder.edit_indicator.setVisibility(View.VISIBLE);
- } else {
- viewHolder.edit_indicator.setVisibility(View.GONE);
- }
- }
- boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI
- && message.getMergedStatus() <= 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 * 1024 * 1024)) {
- filesize = params.size / (1024 * 1024) + " MiB";
- } else if (params.size > (1 * 1024)) {
- filesize = params.size / 1024 + " KiB";
- } else if (params.size > 0) {
- filesize = "1 KiB";
- }
- if (message.getTransferable() != null && message.getTransferable().getStatus() == Transferable.STATUS_FAILED) {
- error = true;
- }
- }
- switch (message.getMergedStatus()) {
- case Message.STATUS_WAITING:
- info = getContext().getString(R.string.waiting);
- break;
- case Message.STATUS_UNSEND:
- Transferable d = message.getTransferable();
- if (d != null) {
- info = getContext().getString(R.string.sending_file, d.getProgress());
- } else {
- info = getContext().getString(R.string.sending);
- }
- break;
- case Message.STATUS_OFFERED:
- info = getContext().getString(R.string.offering);
- break;
- case Message.STATUS_SEND_RECEIVED:
- if (mIndicateReceived) {
- viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
- }
- break;
- case Message.STATUS_SEND_DISPLAYED:
- if (mIndicateReceived) {
- viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
- viewHolder.indicatorRead.setVisibility(View.VISIBLE);
- }
- break;
- case Message.STATUS_SEND_FAILED:
- info = getContext().getString(R.string.send_failed);
- error = true;
- break;
- default:
- if (multiReceived) {
- info = UIHelper.getMessageDisplayName(message);
- }
- break;
- }
- if (error && type == SENT) {
- viewHolder.time.setTextColor(activity.getWarningTextColor());
- } else if (!message.isValidInSession() && type == RECEIVED) {
- viewHolder.time.setTextColor(activity.getUnencryptedTextColor());
- } else {
- viewHolder.time.setTextColor(this.getMessageTextColor(darkBackground,false));
- }
- if (message.getEncryption() == Message.ENCRYPTION_NONE) {
- viewHolder.indicator.setVisibility(View.GONE);
- } else {
- viewHolder.indicator.setVisibility(View.VISIBLE);
- if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
- FingerprintStatus status = message.getConversation()
- .getAccount().getAxolotlService().getFingerprintTrust(
- message.getFingerprint());
-
- if(status == null || (!status.isTrustedAndActive())) {
- viewHolder.indicator.setColorFilter(activity.getWarningTextColor());
- viewHolder.indicator.setAlpha(1.0f);
- } else {
- viewHolder.indicator.clearColorFilter();
- if (darkBackground) {
- viewHolder.indicator.setAlpha(0.7f);
- } else {
- viewHolder.indicator.setAlpha(0.57f);
- }
- }
- } else {
- viewHolder.indicator.clearColorFilter();
- if (darkBackground) {
- viewHolder.indicator.setAlpha(0.7f);
- } else {
- viewHolder.indicator.setAlpha(0.57f);
- }
- }
- }
-
- String formatedTime = UIHelper.readableTimeDifferenceFull(getContext(),
- message.getMergedTimeSent());
- if (message.getStatus() <= Message.STATUS_RECEIVED) {
- if ((filesize != null) && (info != null)) {
- viewHolder.time.setText(formatedTime + " \u00B7 " + filesize +" \u00B7 " + info);
- } else if ((filesize == null) && (info != null)) {
- viewHolder.time.setText(formatedTime + " \u00B7 " + info);
- } else if ((filesize != null) && (info == null)) {
- viewHolder.time.setText(formatedTime + " \u00B7 " + filesize);
- } else {
- viewHolder.time.setText(formatedTime);
- }
- } else {
- if ((filesize != null) && (info != null)) {
- viewHolder.time.setText(filesize + " \u00B7 " + info);
- } else if ((filesize == null) && (info != null)) {
- if (error) {
- viewHolder.time.setText(info + " \u00B7 " + formatedTime);
- } else {
- viewHolder.time.setText(info);
- }
- } else if ((filesize != null) && (info == null)) {
- viewHolder.time.setText(filesize + " \u00B7 " + formatedTime);
- } else {
- viewHolder.time.setText(formatedTime);
- }
- }
- }
-
- private void displayInfoMessage(ViewHolder viewHolder, String text, boolean darkBackground) {
- viewHolder.aw_player.setVisibility(View.GONE);
- if (viewHolder.download_button != null) {
- viewHolder.download_button.setVisibility(View.GONE);
- }
- viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.VISIBLE);
- viewHolder.messageBody.setText(text);
- viewHolder.messageBody.setTextColor(getMessageTextColor(darkBackground, false));
- viewHolder.messageBody.setTypeface(null, Typeface.ITALIC);
- viewHolder.messageBody.setTextIsSelectable(false);
- }
-
- private void displayDecryptionFailed(ViewHolder viewHolder, boolean darkBackground) {
- viewHolder.aw_player.setVisibility(View.GONE);
- if (viewHolder.download_button != null) {
- viewHolder.download_button.setVisibility(View.GONE);
- }
- viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.VISIBLE);
- viewHolder.messageBody.setText(getContext().getString(
- R.string.decryption_failed));
- viewHolder.messageBody.setTextColor(getMessageTextColor(darkBackground, false));
- viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
- viewHolder.messageBody.setTextIsSelectable(false);
- }
-
- private void displayHeartMessage(final ViewHolder viewHolder, final String body) {
- viewHolder.aw_player.setVisibility(View.GONE);
- 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 displayXmppMessage(final ViewHolder viewHolder, final String body) {
- String contact = body.toLowerCase();
- contact = contact.split(":")[1];
- contact = contact.split("\\?")[0];
- String add_contact = activity.getString(R.string.add_to_contact_list) + " (" + contact + ")";
- viewHolder.aw_player.setVisibility(View.GONE);
- viewHolder.download_button.setVisibility(View.VISIBLE);
- viewHolder.download_button.setText(add_contact);
- viewHolder.download_button.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(Uri.parse(body));
- activity.startActivity(intent);
- }
- });
- viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.GONE);
-
- }
-
- private void displayTextMessage(final ViewHolder viewHolder, final Message message, boolean darkBackground) {
- if (viewHolder.download_button != null) {
- viewHolder.download_button.setVisibility(View.GONE);
- }
- viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.VISIBLE);
- viewHolder.messageBody.setIncludeFontPadding(true);
- if (message.getBody() != null) {
- final String nick = UIHelper.getMessageDisplayName(message);
- SpannableStringBuilder body = message.getMergedBody();
- boolean hasMeCommand = message.hasMeCommand();
- if (hasMeCommand) {
- body = body.replace(0, Message.ME_COMMAND.length(), nick + " ");
- }
- if (body.length() > Config.MAX_DISPLAY_MESSAGE_CHARS) {
- body = new SpannableStringBuilder(body, 0, Config.MAX_DISPLAY_MESSAGE_CHARS);
- body.append("\u2026");
- }
- Message.MergeSeparator[] mergeSeparators = body.getSpans(0, body.length(), Message.MergeSeparator.class);
- for (Message.MergeSeparator mergeSeparator : mergeSeparators) {
- int start = body.getSpanStart(mergeSeparator);
- int end = body.getSpanEnd(mergeSeparator);
- body.setSpan(new RelativeSizeSpan(0.3f), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- if (message.getType() != Message.TYPE_PRIVATE) {
- if (hasMeCommand) {
- body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 0, nick.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- } else {
- String privateMarker;
- if (message.getStatus() <= Message.STATUS_RECEIVED) {
- privateMarker = activity
- .getString(R.string.private_message);
- } else {
- final String to;
- if (message.getCounterpart() != null) {
- to = message.getCounterpart().getResourcepart();
- } else {
- to = "";
- }
- privateMarker = activity.getString(R.string.private_message_to, to);
- }
- body.insert(0, privateMarker);
- int privateMarkerIndex = privateMarker.length();
- body.insert(privateMarkerIndex, " ");
- body.setSpan(new ForegroundColorSpan(getMessageTextColor(darkBackground, false)), 0, privateMarkerIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- body.setSpan(new StyleSpan(Typeface.BOLD), 0, privateMarkerIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- if (hasMeCommand) {
- body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), privateMarkerIndex + 1, privateMarkerIndex + 1 + nick.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- }
- Linkify.addLinks(body, Linkify.WEB_URLS);
- Linkify.addLinks(body, XMPP_PATTERN, "xmpp");
- Linkify.addLinks(body, GeoHelper.GEO_URI, "geo");
- viewHolder.messageBody.setAutoLinkMask(0);
- viewHolder.messageBody.setText(body);
- viewHolder.messageBody.setTextIsSelectable(true);
- viewHolder.messageBody.setMovementMethod(ClickableMovementMethod.getInstance());
- listSelectionManager.onUpdate(viewHolder.messageBody, message);
-
- } 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);
- }
-
- private void displayDownloadableMessage(ViewHolder viewHolder, final Message message, String text) {
- viewHolder.aw_player.setVisibility(View.GONE);
- viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.GONE);
- viewHolder.download_button.setVisibility(View.VISIBLE);
- viewHolder.download_button.setText(text);
- viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_download_grey600_48dp,0,0,0);
- viewHolder.download_button.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- activity.startDownloadable(message);
- }
- });
- }
-
- private void displayAudioMessage(ViewHolder viewHolder, final Message message, int position) {
- if (audioPlayer == null) audioPlayer = new HashMap<>();
- viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.GONE);
- if (viewHolder.download_button != null) {
- viewHolder.download_button.setVisibility(View.GONE);
- }
- viewHolder.aw_player.setVisibility(View.VISIBLE);
- Uri audioFile = Uri.fromFile(activity.xmppConnectionService.getFileBackend().getFile(message));
-
- audioWife = audioPlayer.get(position);
- if (audioWife == null) {
- audioWife = new AudioWife();
- audioWife.init(getContext(), audioFile);
- audioPlayer.put(position, audioWife);
- RelativeLayout vg = new RelativeLayout(activity);
- LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- audioWife.useDefaultUi(vg, layoutInflater);
- viewHolder.aw_player.addView(audioWife.getPlayerUi());
- } else {
- audioWife.cleanPlayerUi();
- viewHolder.aw_player.addView(audioWife.getPlayerUi());
- }
- }
-
- private void displayOpenableMessage(ViewHolder viewHolder,final Message message) {
- viewHolder.aw_player.setVisibility(View.GONE);
- viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.GONE);
- viewHolder.download_button.setVisibility(View.VISIBLE);
- String mimeType = message.getMimeType();
- String fullName = "";
- if (mimeType != null) {
- if (message.getMimeType().contains("pdf")) {
- viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_file_pdf_grey600_48dp,0,0,0);
- } else if (message.getMimeType().contains("vcard")) {
- File file = new File(activity.xmppConnectionService.getFileBackend().getFile(message).toString());
- VCard vcard = null;
- String name = null;
- String version = null;
- try {
- vcard = Ezvcard.parse(file).first();
- } catch (IOException e) {
- e.printStackTrace();
- }
- if (vcard != null) {
- version = vcard.getVersion().toString();
- Log.d(Config.LOGTAG, "VCard version: " + version);
- name = vcard.getFormattedName().getValue();
- fullName = " (" + name + ")";
- }
- viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_account_card_details_grey600_48dp,0,0,0);
- } else if (message.getMimeType().contains("calendar")) {
- viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_calendar_grey600_48dp,0,0,0);
- } else {
- viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_file_grey600_48dp,0,0,0);
- }
- }
- viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message) + fullName));
- viewHolder.download_button.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- openDownloadable(message);
- }
- });
- }
-
- private void displayLocationMessage(ViewHolder viewHolder, final Message message) {
- viewHolder.aw_player.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.GONE);
- String url = GeoHelper.MapPreviewUri(message);
- viewHolder.image.setVisibility(View.VISIBLE);
- viewHolder.image.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- showLocation(message);
- }
- });
- Glide
- .with(activity)
- .load(Uri.parse(url))
- .asBitmap()
- .diskCacheStrategy(DiskCacheStrategy.ALL)
- .fitCenter()
- .placeholder(R.drawable.ic_map_marker_grey600_48dp)
- .error(R.drawable.ic_map_marker_grey600_48dp)
- .into(viewHolder.image);
- viewHolder.image.setMaxWidth(500);
- viewHolder.image.setAdjustViewBounds(true);
- viewHolder.download_button.setVisibility(View.GONE);
- viewHolder.download_button.setText(R.string.show_location);
- viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_map_marker_grey600_48dp, 0, 0, 0);
- viewHolder.download_button.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- showLocation(message);
- }
- });
-
- }
-
- private void displayImageMessage(ViewHolder viewHolder,
- final Message message) {
- viewHolder.aw_player.setVisibility(View.GONE);
- if (viewHolder.download_button != null) {
- viewHolder.download_button.setVisibility(View.GONE);
- }
- viewHolder.messageBody.setVisibility(View.GONE);
- viewHolder.image.setVisibility(View.VISIBLE);
- FileParams params = message.getFileParams();
- double target = metrics.density * 200;
- int scaledW;
- int scaledH;
- if (Math.max(params.height, params.width) * metrics.density <= target) {
- scaledW = (int) (params.width * metrics.density);
- scaledH = (int) (params.height * metrics.density);
- } else if (Math.max(params.height,params.width) <= target) {
- scaledW = params.width;
- scaledH = params.height;
- } else if (params.width <= params.height) {
- scaledW = (int) (params.width / ((double) params.height / target));
- scaledH = (int) target;
- } else {
- scaledW = (int) target;
- scaledH = (int) (params.height / ((double) params.width / target));
- }
- LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(scaledW, scaledH);
- layoutParams.setMargins(0, (int) (metrics.density * 4), 0, (int) (metrics.density * 4));
- viewHolder.image.setLayoutParams(layoutParams);
- activity.loadBitmap(message, viewHolder.image);
- viewHolder.image.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- openDownloadable(message);
- }
- });
- }
-
- private void loadMoreMessages(Conversation conversation) {
- conversation.setLastClearHistory(0);
- activity.xmppConnectionService.updateConversation(conversation);
- 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 unused, ViewGroup parent) {
- final Message message = getItem(position);
- final boolean isInValidSession = message.isValidInSession();
- final Conversation conversation = message.getConversation();
- final Account account = conversation.getAccount();
- final int type = getItemViewType(position);
- ViewHolder viewHolder;
- View view;
- viewHolder = new ViewHolder();
- switch (type) {
- case SENT:
- view = activity.getLayoutInflater().inflate(
- R.layout.message_sent, parent, false);
- viewHolder.message_box = (LinearLayout) view
- .findViewById(R.id.message_box);
- viewHolder.contact_picture = (ImageView) view
- .findViewById(R.id.message_photo);
- viewHolder.aw_player = (ViewGroup) view.findViewById(R.id.aw_player);
- viewHolder.download_button = (Button) view
- .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 = (CopyTextView) view
- .findViewById(R.id.message_body);
- viewHolder.time = (TextView) view
- .findViewById(R.id.message_time);
- viewHolder.indicatorReceived = (ImageView) view
- .findViewById(R.id.indicator_received);
- viewHolder.indicatorRead = (ImageView) view
- .findViewById(R.id.indicator_read);
- break;
- case RECEIVED:
- view = activity.getLayoutInflater().inflate(
- R.layout.message_received, parent, false);
- viewHolder.message_box = (LinearLayout) view
- .findViewById(R.id.message_box);
- viewHolder.contact_picture = (ImageView) view
- .findViewById(R.id.message_photo);
- viewHolder.aw_player = (ViewGroup) view.findViewById(R.id.aw_player);
- viewHolder.download_button = (Button) view
- .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 = (CopyTextView) view
- .findViewById(R.id.message_body);
- viewHolder.time = (TextView) view
- .findViewById(R.id.message_time);
- viewHolder.indicatorReceived = (ImageView) view
- .findViewById(R.id.indicator_received);
- viewHolder.encryption = (TextView) view.findViewById(R.id.message_encryption);
- break;
- case STATUS:
- 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:
- view = new View(getContext());
- viewHolder = null;
- break;
- }
- if (viewHolder.messageBody != null) {
- listSelectionManager.onCreate(viewHolder.messageBody);
- viewHolder.messageBody.setCopyHandler(this);
- }
- view.setTag(viewHolder);
- if (viewHolder == null) {
- return view;
- }
-
-
- boolean darkBackground = (type == SENT && (!isInValidSession || !mUseWhiteBackground));
-
- 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());
- }
- return view;
- } else {
- loadAvatar(message, viewHolder.contact_picture);
- }
-
- viewHolder.contact_picture
- .setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
- MessageAdapter.this.mOnContactPictureClickedListener
- .onContactPictureClicked(message);
- }
-
- }
- });
- viewHolder.contact_picture
- .setOnLongClickListener(new OnLongClickListener() {
-
- @Override
- public boolean onLongClick(View v) {
- if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
- MessageAdapter.this.mOnContactPictureLongClickedListener
- .onContactPictureLongClicked(message);
- return true;
- } else {
- return false;
- }
- }
- });
-
- final Transferable transferable = message.getTransferable();
- String mimeType = message.getMimeType();
- if (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING) {
- if (transferable.getStatus() == Transferable.STATUS_OFFER) {
- displayDownloadableMessage(viewHolder,message,activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message)));
- } else if (transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) {
- displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)));
- } else {
- displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first,darkBackground);
- }
- } else if (message.getType() == Message.TYPE_IMAGE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
- displayImageMessage(viewHolder, message);
- } else if (message.getType() == Message.TYPE_FILE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
- if (message.getFileParams().width > 0) {
- displayImageMessage(viewHolder,message);
- } else {
- if (mimeType != null) {
- if (message.getMimeType().startsWith("audio/")) {
- displayAudioMessage(viewHolder, message, position);
- } else displayOpenableMessage(viewHolder, message);
- } else displayOpenableMessage(viewHolder, message);
- }
- } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- if (account.isPgpDecryptionServiceConnected()) {
- if (!account.hasPendingPgpIntent(conversation)) {
- displayInfoMessage(viewHolder, activity.getString(R.string.message_decrypting), darkBackground);
- } else {
- displayInfoMessage(viewHolder, activity.getString(R.string.pgp_message), darkBackground);
- }
- } else {
- displayInfoMessage(viewHolder,activity.getString(R.string.install_openkeychain),darkBackground);
- if (viewHolder != null) {
- viewHolder.message_box
- .setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- activity.showInstallPgpDialog();
- }
- });
- }
- }
- } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
- displayDecryptionFailed(viewHolder,darkBackground);
- } else {
- if (GeoHelper.isGeoUri(message.getBody())) {
- displayLocationMessage(viewHolder,message);
- } else if (message.bodyIsHeart()) {
- displayHeartMessage(viewHolder, message.getBody().trim());
- } else if (message.bodyIsXmpp()) {
- displayXmppMessage(viewHolder, message.getBody().trim());
- } else if (message.treatAsDownloadable() == Message.Decision.MUST ||
- message.treatAsDownloadable() == Message.Decision.SHOULD) {
- try {
- URL url = new URL(message.getBody());
- displayDownloadableMessage(viewHolder,
- message,
- activity.getString(R.string.check_x_filesize_on_host,
- UIHelper.getFileDescriptionString(activity, message),
- url.getHost()));
- } catch (Exception e) {
- displayDownloadableMessage(viewHolder,
- message,
- activity.getString(R.string.check_x_filesize,
- UIHelper.getFileDescriptionString(activity, message)));
- }
- } else {
- displayTextMessage(viewHolder, message, darkBackground);
- }
- }
-
- if (type == RECEIVED) {
- if(isInValidSession) {
- 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);
- viewHolder.encryption.setVisibility(View.VISIBLE);
- viewHolder.encryption.setText(CryptoHelper.encryptionTypeToText(message.getEncryption()));
- }
- }
-
- if (type == SENT) {
- if (mUseWhiteBackground) {
- viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_sent_white);
- } else {
- viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_sent);
- }
- }
-
- displayStatus(viewHolder, message, type, darkBackground);
-
- return view;
- }
-
- @Override
- public void notifyDataSetChanged() {
- listSelectionManager.onBeforeNotifyDataSetChanged();
- super.notifyDataSetChanged();
- listSelectionManager.onAfterNotifyDataSetChanged();
- }
-
- @Override
- public String transformTextForCopy(CharSequence text, int start, int end) {
- return text.toString().substring(start, end);
- }
-
- public void openDownloadable(Message message) {
- DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
- if (!file.exists()) {
- Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show();
- return;
- }
- String mime = file.getMimeType();
- if (mime.startsWith("image/")) {
- Intent intent = new Intent(getContext(), ShowFullscreenMessageActivity.class);
- intent.putExtra("image", Uri.fromFile(file));
- try {
- activity.startActivity(intent);
- return;
- } catch (ActivityNotFoundException e) {
- //ignored
- }
- } else if (mime.startsWith("video/")) {
- Intent intent = new Intent(getContext(), ShowFullscreenMessageActivity.class);
- intent.putExtra("video", Uri.fromFile(file));
- try {
- activity.startActivity(intent);
- return;
- } catch (ActivityNotFoundException e) {
- //ignored
- }
- }
- Intent openIntent = new Intent(Intent.ACTION_VIEW);
- if (mime == null) {
- mime = "*/*";
- }
- Uri uri;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- try {
- uri = FileProvider.getUriForFile(activity, FileBackend.CONVERSATIONS_FILE_PROVIDER, file);
- } catch (IllegalArgumentException e) {
- Toast.makeText(activity,activity.getString(R.string.no_permission_to_access_x,file.getAbsolutePath()), Toast.LENGTH_SHORT).show();
- return;
- }
- openIntent.setDataAndType(uri, mime);
- openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- } else {
- uri = Uri.fromFile(file);
- }
- openIntent.setDataAndType(uri, mime);
- PackageManager manager = activity.getPackageManager();
- List<ResolveInfo> info = manager.queryIntentActivities(openIntent, 0);
- if (info.size() == 0) {
- openIntent.setDataAndType(Uri.fromFile(file),"*/*");
- }
- try {
- getContext().startActivity(openIntent);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(activity,R.string.no_application_found_to_open_file,Toast.LENGTH_SHORT).show();
- }
- }
-
- public void showLocation(Message message) {
- for(Intent intent : GeoHelper.createGeoIntentsFromMessage(message)) {
- if (intent.resolveActivity(getContext().getPackageManager()) != null) {
- getContext().startActivity(intent);
- return;
- }
- }
- 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 TextView getMessageBody(View view) {
- final Object tag = view.getTag();
- if (tag instanceof ViewHolder) {
- final ViewHolder viewHolder = (ViewHolder) tag;
- return viewHolder.messageBody;
- }
- return null;
- }
-
- public interface OnContactPictureClicked {
- void onContactPictureClicked(Message message);
- }
-
- public interface OnContactPictureLongClicked {
- void onContactPictureLongClicked(Message message);
- }
-
- private static class ViewHolder {
-
- protected LinearLayout message_box;
- protected Button download_button;
- protected ViewGroup aw_player;
- protected ImageView image;
- protected ImageView indicator;
- protected ImageView indicatorReceived;
- protected ImageView indicatorRead;
- protected TextView time;
- protected CopyTextView messageBody;
- 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> {
- private final WeakReference<ImageView> imageViewReference;
- private Message message = null;
-
- public BitmapWorkerTask(ImageView imageView) {
- imageViewReference = new WeakReference<>(imageView);
- }
-
- @Override
- protected Bitmap doInBackground(Message... params) {
- return activity.avatarService().get(params[0], activity.getPixel(48), isCancelled());
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- if (bitmap != null && !isCancelled()) {
- final ImageView imageView = imageViewReference.get();
- if (imageView != null) {
- imageView.setImageBitmap(bitmap);
- imageView.setBackgroundColor(0x00000000);
- }
- }
- }
- }
-
- public void loadAvatar(Message message, ImageView imageView) {
- if (cancelPotentialWork(message, imageView)) {
- final Bitmap bm = activity.avatarService().get(message, activity.getPixel(48), true);
- if (bm != null) {
- cancelPotentialWork(message, imageView);
- imageView.setImageBitmap(bm);
- imageView.setBackgroundColor(0x00000000);
- } else {
- imageView.setBackgroundColor(UIHelper.getColorForName(UIHelper.getMessageDisplayName(message)));
- imageView.setImageDrawable(null);
- final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
- final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
- imageView.setImageDrawable(asyncDrawable);
- try {
- task.execute(message);
- } catch (final RejectedExecutionException ignored) {
- }
- }
- }
- }
-
- public static boolean cancelPotentialWork(Message message, ImageView imageView) {
- final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
-
- if (bitmapWorkerTask != null) {
- final Message oldMessage = bitmapWorkerTask.message;
- if (oldMessage == null || message != oldMessage) {
- bitmapWorkerTask.cancel(true);
- } else {
- return false;
- }
- }
- return true;
- }
-
- private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
- if (imageView != null) {
- final Drawable drawable = imageView.getDrawable();
- if (drawable instanceof AsyncDrawable) {
- final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
- return asyncDrawable.getBitmapWorkerTask();
- }
- }
- return null;
- }
-
- static class AsyncDrawable extends BitmapDrawable {
- private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
-
- public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
- super(res, bitmap);
- bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
- }
-
- public BitmapWorkerTask getBitmapWorkerTask() {
- return bitmapWorkerTaskReference.get();
- }
- }
+ private static final int SENT = 0;
+ private static final int RECEIVED = 1;
+ private static final int STATUS = 2;
+ private static final Pattern XMPP_PATTERN = Pattern
+ .compile("xmpp\\:(?:(?:["
+ + Patterns.GOOD_IRI_CHAR
+ + "\\;\\/\\?\\@\\&\\=\\#\\~\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])"
+ + "|(?:\\%[a-fA-F0-9]{2}))+");
+
+ private ConversationActivity activity;
+
+ private DisplayMetrics metrics;
+
+ private AudioWife audioWife;
+
+ private OnContactPictureClicked mOnContactPictureClickedListener;
+ private OnContactPictureLongClicked mOnContactPictureLongClickedListener;
+
+ private boolean mIndicateReceived = false;
+ private final ListSelectionManager listSelectionManager = new ListSelectionManager();
+ private HashMap<Integer, AudioWife> audioPlayer;
+ 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) {
+ this.mOnContactPictureClickedListener = listener;
+ }
+
+ public void setOnContactPictureLongClicked(
+ OnContactPictureLongClicked listener) {
+ this.mOnContactPictureLongClickedListener = listener;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 3;
+ }
+
+ public int getItemViewType(Message message) {
+ if (message.getType() == Message.TYPE_STATUS) {
+ return STATUS;
+ } else if (message.getStatus() <= Message.STATUS_RECEIVED) {
+ return RECEIVED;
+ }
+
+ return SENT;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return getItemViewType(getItem(position));
+ }
+
+ private int getMessageTextColor(boolean onDark, boolean primary) {
+ if (onDark) {
+ return activity.getResources().getColor(primary ? R.color.dark : R.color.primary);
+ } else {
+ return activity.getResources().getColor(primary ? R.color.dark : R.color.primary);
+ }
+ }
+
+ private void displayStatus(ViewHolder viewHolder, Message message, int type, boolean darkBackground) {
+ String filesize = null;
+ String info = null;
+ boolean error = false;
+ if (viewHolder.indicatorReceived != null) {
+ viewHolder.indicatorReceived.setVisibility(View.GONE);
+ viewHolder.indicatorRead.setVisibility(View.GONE);
+ }
+
+ if (viewHolder.edit_indicator != null) {
+ if (message.edited()) {
+ viewHolder.edit_indicator.setVisibility(View.VISIBLE);
+ } else {
+ viewHolder.edit_indicator.setVisibility(View.GONE);
+ }
+ }
+ boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI
+ && message.getMergedStatus() <= 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 * 1024 * 1024)) {
+ filesize = params.size / (1024 * 1024) + " MiB";
+ } else if (params.size > (1 * 1024)) {
+ filesize = params.size / 1024 + " KiB";
+ } else if (params.size > 0) {
+ filesize = "1 KiB";
+ }
+ if (message.getTransferable() != null && message.getTransferable().getStatus() == Transferable.STATUS_FAILED) {
+ error = true;
+ }
+ }
+ switch (message.getMergedStatus()) {
+ case Message.STATUS_WAITING:
+ info = getContext().getString(R.string.waiting);
+ break;
+ case Message.STATUS_UNSEND:
+ Transferable d = message.getTransferable();
+ if (d != null) {
+ info = getContext().getString(R.string.sending_file, d.getProgress());
+ } else {
+ info = getContext().getString(R.string.sending);
+ }
+ break;
+ case Message.STATUS_OFFERED:
+ info = getContext().getString(R.string.offering);
+ break;
+ case Message.STATUS_SEND_RECEIVED:
+ if (mIndicateReceived) {
+ viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
+ }
+ break;
+ case Message.STATUS_SEND_DISPLAYED:
+ if (mIndicateReceived) {
+ viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
+ viewHolder.indicatorRead.setVisibility(View.VISIBLE);
+ }
+ break;
+ case Message.STATUS_SEND_FAILED:
+ info = getContext().getString(R.string.send_failed);
+ error = true;
+ break;
+ default:
+ if (multiReceived) {
+ info = UIHelper.getMessageDisplayName(message);
+ }
+ break;
+ }
+ if (error && type == SENT) {
+ viewHolder.time.setTextColor(activity.getWarningTextColor());
+ } else if (!message.isValidInSession() && type == RECEIVED) {
+ viewHolder.time.setTextColor(activity.getUnencryptedTextColor());
+ } else {
+ viewHolder.time.setTextColor(this.getMessageTextColor(darkBackground, false));
+ }
+ if (message.getEncryption() == Message.ENCRYPTION_NONE) {
+ viewHolder.indicator.setVisibility(View.GONE);
+ } else {
+ viewHolder.indicator.setVisibility(View.VISIBLE);
+ if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
+ FingerprintStatus status = message.getConversation()
+ .getAccount().getAxolotlService().getFingerprintTrust(
+ message.getFingerprint());
+
+ if (status == null || (!status.isTrustedAndActive())) {
+ viewHolder.indicator.setColorFilter(activity.getWarningTextColor());
+ viewHolder.indicator.setAlpha(1.0f);
+ } else {
+ viewHolder.indicator.clearColorFilter();
+ if (darkBackground) {
+ viewHolder.indicator.setAlpha(0.7f);
+ } else {
+ viewHolder.indicator.setAlpha(0.57f);
+ }
+ }
+ } else {
+ viewHolder.indicator.clearColorFilter();
+ if (darkBackground) {
+ viewHolder.indicator.setAlpha(0.7f);
+ } else {
+ viewHolder.indicator.setAlpha(0.57f);
+ }
+ }
+ }
+
+ String formatedTime = UIHelper.readableTimeDifferenceFull(getContext(),
+ message.getMergedTimeSent());
+ if (message.getStatus() <= Message.STATUS_RECEIVED) {
+ if ((filesize != null) && (info != null)) {
+ viewHolder.time.setText(formatedTime + " \u00B7 " + filesize + " \u00B7 " + info);
+ } else if ((filesize == null) && (info != null)) {
+ viewHolder.time.setText(formatedTime + " \u00B7 " + info);
+ } else if ((filesize != null) && (info == null)) {
+ viewHolder.time.setText(formatedTime + " \u00B7 " + filesize);
+ } else {
+ viewHolder.time.setText(formatedTime);
+ }
+ } else {
+ if ((filesize != null) && (info != null)) {
+ viewHolder.time.setText(filesize + " \u00B7 " + info);
+ } else if ((filesize == null) && (info != null)) {
+ if (error) {
+ viewHolder.time.setText(info + " \u00B7 " + formatedTime);
+ } else {
+ viewHolder.time.setText(info);
+ }
+ } else if ((filesize != null) && (info == null)) {
+ viewHolder.time.setText(filesize + " \u00B7 " + formatedTime);
+ } else {
+ viewHolder.time.setText(formatedTime);
+ }
+ }
+ }
+
+ private void displayInfoMessage(ViewHolder viewHolder, String text, boolean darkBackground) {
+ viewHolder.aw_player.setVisibility(View.GONE);
+ if (viewHolder.download_button != null) {
+ viewHolder.download_button.setVisibility(View.GONE);
+ }
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.VISIBLE);
+ viewHolder.messageBody.setText(text);
+ viewHolder.messageBody.setTextColor(getMessageTextColor(darkBackground, false));
+ viewHolder.messageBody.setTypeface(null, Typeface.ITALIC);
+ viewHolder.messageBody.setTextIsSelectable(false);
+ }
+
+ private void displayDecryptionFailed(ViewHolder viewHolder, boolean darkBackground) {
+ viewHolder.aw_player.setVisibility(View.GONE);
+ if (viewHolder.download_button != null) {
+ viewHolder.download_button.setVisibility(View.GONE);
+ }
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.VISIBLE);
+ viewHolder.messageBody.setText(getContext().getString(
+ R.string.decryption_failed));
+ viewHolder.messageBody.setTextColor(getMessageTextColor(darkBackground, false));
+ viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
+ viewHolder.messageBody.setTextIsSelectable(false);
+ }
+
+ private void displayHeartMessage(final ViewHolder viewHolder, final String body) {
+ viewHolder.aw_player.setVisibility(View.GONE);
+ 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 displayXmppMessage(final ViewHolder viewHolder, final String body) {
+ String contact = body.toLowerCase();
+ contact = contact.split(":")[1];
+ contact = contact.split("\\?")[0];
+ String add_contact = activity.getString(R.string.add_to_contact_list) + " (" + contact + ")";
+ viewHolder.aw_player.setVisibility(View.GONE);
+ viewHolder.download_button.setVisibility(View.VISIBLE);
+ viewHolder.download_button.setText(add_contact);
+ viewHolder.download_button.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(body));
+ activity.startActivity(intent);
+ }
+ });
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.GONE);
+
+ }
+
+ private void displayTextMessage(final ViewHolder viewHolder, final Message message, boolean darkBackground) {
+ if (viewHolder.download_button != null) {
+ viewHolder.download_button.setVisibility(View.GONE);
+ }
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.VISIBLE);
+ viewHolder.messageBody.setIncludeFontPadding(true);
+ if (message.getBody() != null) {
+ final String nick = UIHelper.getMessageDisplayName(message);
+ SpannableStringBuilder body = message.getMergedBody();
+ boolean hasMeCommand = message.hasMeCommand();
+ if (hasMeCommand) {
+ body = body.replace(0, Message.ME_COMMAND.length(), nick + " ");
+ }
+ if (body.length() > Config.MAX_DISPLAY_MESSAGE_CHARS) {
+ body = new SpannableStringBuilder(body, 0, Config.MAX_DISPLAY_MESSAGE_CHARS);
+ body.append("\u2026");
+ }
+ Message.MergeSeparator[] mergeSeparators = body.getSpans(0, body.length(), Message.MergeSeparator.class);
+ for (Message.MergeSeparator mergeSeparator : mergeSeparators) {
+ int start = body.getSpanStart(mergeSeparator);
+ int end = body.getSpanEnd(mergeSeparator);
+ body.setSpan(new RelativeSizeSpan(0.3f), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ if (message.getType() != Message.TYPE_PRIVATE) {
+ if (hasMeCommand) {
+ body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 0, nick.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ } else {
+ String privateMarker;
+ if (message.getStatus() <= Message.STATUS_RECEIVED) {
+ privateMarker = activity
+ .getString(R.string.private_message);
+ } else {
+ final String to;
+ if (message.getCounterpart() != null) {
+ to = message.getCounterpart().getResourcepart();
+ } else {
+ to = "";
+ }
+ privateMarker = activity.getString(R.string.private_message_to, to);
+ }
+ body.insert(0, privateMarker);
+ int privateMarkerIndex = privateMarker.length();
+ body.insert(privateMarkerIndex, " ");
+ body.setSpan(new ForegroundColorSpan(getMessageTextColor(darkBackground, false)), 0, privateMarkerIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ body.setSpan(new StyleSpan(Typeface.BOLD), 0, privateMarkerIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ if (hasMeCommand) {
+ body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), privateMarkerIndex + 1, privateMarkerIndex + 1 + nick.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+ Linkify.addLinks(body, Linkify.WEB_URLS);
+ Linkify.addLinks(body, XMPP_PATTERN, "xmpp");
+ Linkify.addLinks(body, GeoHelper.GEO_URI, "geo");
+ viewHolder.messageBody.setAutoLinkMask(0);
+ viewHolder.messageBody.setText(body);
+ viewHolder.messageBody.setTextIsSelectable(true);
+ viewHolder.messageBody.setMovementMethod(ClickableMovementMethod.getInstance());
+ listSelectionManager.onUpdate(viewHolder.messageBody, message);
+
+ } 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);
+ }
+
+ private void displayDownloadableMessage(ViewHolder viewHolder, final Message message, String text) {
+ viewHolder.aw_player.setVisibility(View.GONE);
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.GONE);
+ viewHolder.download_button.setVisibility(View.VISIBLE);
+ viewHolder.download_button.setText(text);
+ viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_download_grey600_48dp, 0, 0, 0);
+ viewHolder.download_button.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ activity.startDownloadable(message);
+ }
+ });
+ }
+
+ private void displayAudioMessage(ViewHolder viewHolder, final Message message, int position) {
+ if (audioPlayer == null) audioPlayer = new HashMap<>();
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.GONE);
+ if (viewHolder.download_button != null) {
+ viewHolder.download_button.setVisibility(View.GONE);
+ }
+ viewHolder.aw_player.setVisibility(View.VISIBLE);
+ Uri audioFile = Uri.fromFile(activity.xmppConnectionService.getFileBackend().getFile(message));
+
+ audioWife = audioPlayer.get(position);
+ if (audioWife == null) {
+ audioWife = new AudioWife();
+ audioWife.init(getContext(), audioFile);
+ audioPlayer.put(position, audioWife);
+ RelativeLayout vg = new RelativeLayout(activity);
+ LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ audioWife.useDefaultUi(vg, layoutInflater);
+ viewHolder.aw_player.addView(audioWife.getPlayerUi());
+ } else {
+ audioWife.cleanPlayerUi();
+ viewHolder.aw_player.addView(audioWife.getPlayerUi());
+ }
+ }
+
+ private void displayOpenableMessage(ViewHolder viewHolder, final Message message) {
+ viewHolder.aw_player.setVisibility(View.GONE);
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.GONE);
+ viewHolder.download_button.setVisibility(View.VISIBLE);
+ String mimeType = message.getMimeType();
+ String fullName = "";
+ if (mimeType != null) {
+ if (message.getMimeType().contains("pdf")) {
+ viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_file_pdf_grey600_48dp, 0, 0, 0);
+ } else if (message.getMimeType().contains("vcard")) {
+ File file = new File(activity.xmppConnectionService.getFileBackend().getFile(message).toString());
+ VCard vcard = null;
+ String name = null;
+ String version = null;
+ try {
+ vcard = Ezvcard.parse(file).first();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ if (vcard != null) {
+ version = vcard.getVersion().toString();
+ Log.d(Config.LOGTAG, "VCard version: " + version);
+ name = vcard.getFormattedName().getValue();
+ fullName = " (" + name + ")";
+ }
+ viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_account_card_details_grey600_48dp, 0, 0, 0);
+ } else if (message.getMimeType().contains("calendar")) {
+ viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_calendar_grey600_48dp, 0, 0, 0);
+ } else {
+ viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_file_grey600_48dp, 0, 0, 0);
+ }
+ }
+ viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message) + fullName));
+ viewHolder.download_button.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ openDownloadable(message);
+ }
+ });
+ }
+
+ private void displayLocationMessage(ViewHolder viewHolder, final Message message) {
+ viewHolder.aw_player.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.GONE);
+ String url = GeoHelper.MapPreviewUri(message);
+ viewHolder.image.setVisibility(View.VISIBLE);
+ viewHolder.image.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ showLocation(message);
+ }
+ });
+ Glide
+ .with(activity)
+ .load(Uri.parse(url))
+ .asBitmap()
+ .diskCacheStrategy(DiskCacheStrategy.ALL)
+ .fitCenter()
+ .placeholder(R.drawable.ic_map_marker_grey600_48dp)
+ .error(R.drawable.ic_map_marker_grey600_48dp)
+ .into(viewHolder.image);
+ viewHolder.image.setMaxWidth(500);
+ viewHolder.image.setAdjustViewBounds(true);
+ viewHolder.download_button.setVisibility(View.GONE);
+ viewHolder.download_button.setText(R.string.show_location);
+ viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_map_marker_grey600_48dp, 0, 0, 0);
+ viewHolder.download_button.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ showLocation(message);
+ }
+ });
+
+ }
+
+ private void displayImageMessage(ViewHolder viewHolder,
+ final Message message) {
+ viewHolder.aw_player.setVisibility(View.GONE);
+ if (viewHolder.download_button != null) {
+ viewHolder.download_button.setVisibility(View.GONE);
+ }
+ viewHolder.messageBody.setVisibility(View.GONE);
+ viewHolder.image.setVisibility(View.VISIBLE);
+ FileParams params = message.getFileParams();
+ double target = metrics.density * 200;
+ int scaledW;
+ int scaledH;
+ if (Math.max(params.height, params.width) * metrics.density <= target) {
+ scaledW = (int) (params.width * metrics.density);
+ scaledH = (int) (params.height * metrics.density);
+ } else if (Math.max(params.height, params.width) <= target) {
+ scaledW = params.width;
+ scaledH = params.height;
+ } else if (params.width <= params.height) {
+ scaledW = (int) (params.width / ((double) params.height / target));
+ scaledH = (int) target;
+ } else {
+ scaledW = (int) target;
+ scaledH = (int) (params.height / ((double) params.width / target));
+ }
+ LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(scaledW, scaledH);
+ layoutParams.setMargins(0, (int) (metrics.density * 4), 0, (int) (metrics.density * 4));
+ viewHolder.image.setLayoutParams(layoutParams);
+ activity.loadBitmap(message, viewHolder.image);
+ viewHolder.image.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ openDownloadable(message);
+ }
+ });
+ }
+
+ private void loadMoreMessages(Conversation conversation) {
+ conversation.setLastClearHistory(0);
+ activity.xmppConnectionService.updateConversation(conversation);
+ 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 unused, ViewGroup parent) {
+ final Message message = getItem(position);
+ final boolean isInValidSession = message.isValidInSession();
+ final Conversation conversation = message.getConversation();
+ final Account account = conversation.getAccount();
+ final int type = getItemViewType(position);
+ ViewHolder viewHolder;
+ View view;
+ viewHolder = new ViewHolder();
+ switch (type) {
+ case SENT:
+ view = activity.getLayoutInflater().inflate(
+ R.layout.message_sent, parent, false);
+ viewHolder.message_box = (LinearLayout) view
+ .findViewById(R.id.message_box);
+ viewHolder.contact_picture = (ImageView) view
+ .findViewById(R.id.message_photo);
+ viewHolder.aw_player = (ViewGroup) view.findViewById(R.id.aw_player);
+ viewHolder.download_button = (Button) view
+ .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 = (CopyTextView) view
+ .findViewById(R.id.message_body);
+ viewHolder.time = (TextView) view
+ .findViewById(R.id.message_time);
+ viewHolder.indicatorReceived = (ImageView) view
+ .findViewById(R.id.indicator_received);
+ viewHolder.indicatorRead = (ImageView) view
+ .findViewById(R.id.indicator_read);
+ break;
+ case RECEIVED:
+ view = activity.getLayoutInflater().inflate(
+ R.layout.message_received, parent, false);
+ viewHolder.message_box = (LinearLayout) view
+ .findViewById(R.id.message_box);
+ viewHolder.contact_picture = (ImageView) view
+ .findViewById(R.id.message_photo);
+ viewHolder.aw_player = (ViewGroup) view.findViewById(R.id.aw_player);
+ viewHolder.download_button = (Button) view
+ .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 = (CopyTextView) view
+ .findViewById(R.id.message_body);
+ viewHolder.time = (TextView) view
+ .findViewById(R.id.message_time);
+ viewHolder.indicatorReceived = (ImageView) view
+ .findViewById(R.id.indicator_received);
+ viewHolder.encryption = (TextView) view.findViewById(R.id.message_encryption);
+ break;
+ case STATUS:
+ 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:
+ view = new View(getContext());
+ viewHolder = null;
+ break;
+ }
+ if (viewHolder.messageBody != null) {
+ listSelectionManager.onCreate(viewHolder.messageBody);
+ viewHolder.messageBody.setCopyHandler(this);
+ }
+ view.setTag(viewHolder);
+ if (viewHolder == null) {
+ return view;
+ }
+
+
+ boolean darkBackground = (type == SENT && (!isInValidSession || !mUseWhiteBackground));
+
+ 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());
+ }
+ return view;
+ } else {
+ loadAvatar(message, viewHolder.contact_picture);
+ }
+
+ viewHolder.contact_picture
+ .setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
+ MessageAdapter.this.mOnContactPictureClickedListener
+ .onContactPictureClicked(message);
+ }
+
+ }
+ });
+ viewHolder.contact_picture
+ .setOnLongClickListener(new OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
+ MessageAdapter.this.mOnContactPictureLongClickedListener
+ .onContactPictureLongClicked(message);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ });
+
+ final Transferable transferable = message.getTransferable();
+ String mimeType = message.getMimeType();
+ if (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING) {
+ if (transferable.getStatus() == Transferable.STATUS_OFFER) {
+ displayDownloadableMessage(viewHolder, message, activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message)));
+ } else if (transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) {
+ displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)));
+ } else {
+ displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first, darkBackground);
+ }
+ } else if (message.getType() == Message.TYPE_IMAGE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
+ displayImageMessage(viewHolder, message);
+ } else if (message.getType() == Message.TYPE_FILE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
+ if (message.getFileParams().width > 0) {
+ displayImageMessage(viewHolder, message);
+ } else {
+ if (mimeType != null) {
+ if (message.getMimeType().startsWith("audio/")) {
+ displayAudioMessage(viewHolder, message, position);
+ } else displayOpenableMessage(viewHolder, message);
+ } else displayOpenableMessage(viewHolder, message);
+ }
+ } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ if (account.isPgpDecryptionServiceConnected()) {
+ if (!account.hasPendingPgpIntent(conversation)) {
+ displayInfoMessage(viewHolder, activity.getString(R.string.message_decrypting), darkBackground);
+ } else {
+ displayInfoMessage(viewHolder, activity.getString(R.string.pgp_message), darkBackground);
+ }
+ } else {
+ displayInfoMessage(viewHolder, activity.getString(R.string.install_openkeychain), darkBackground);
+ if (viewHolder != null) {
+ viewHolder.message_box
+ .setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ activity.showInstallPgpDialog();
+ }
+ });
+ }
+ }
+ } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
+ displayDecryptionFailed(viewHolder, darkBackground);
+ } else {
+ if (GeoHelper.isGeoUri(message.getBody())) {
+ displayLocationMessage(viewHolder, message);
+ } else if (message.bodyIsHeart()) {
+ displayHeartMessage(viewHolder, message.getBody().trim());
+ } else if (message.bodyIsXmpp()) {
+ displayXmppMessage(viewHolder, message.getBody().trim());
+ } else if (message.treatAsDownloadable() == Message.Decision.MUST ||
+ message.treatAsDownloadable() == Message.Decision.SHOULD) {
+ try {
+ URL url = new URL(message.getBody());
+ displayDownloadableMessage(viewHolder,
+ message,
+ activity.getString(R.string.check_x_filesize_on_host,
+ UIHelper.getFileDescriptionString(activity, message),
+ url.getHost()));
+ } catch (Exception e) {
+ displayDownloadableMessage(viewHolder,
+ message,
+ activity.getString(R.string.check_x_filesize,
+ UIHelper.getFileDescriptionString(activity, message)));
+ }
+ } else {
+ displayTextMessage(viewHolder, message, darkBackground);
+ }
+ }
+
+ if (type == RECEIVED) {
+ if (isInValidSession) {
+ 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);
+ viewHolder.encryption.setVisibility(View.VISIBLE);
+ viewHolder.encryption.setText(CryptoHelper.encryptionTypeToText(message.getEncryption()));
+ }
+ }
+
+ if (type == SENT) {
+ if (mUseWhiteBackground) {
+ viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_sent_white);
+ } else {
+ viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_sent);
+ }
+ }
+
+ displayStatus(viewHolder, message, type, darkBackground);
+
+ return view;
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ listSelectionManager.onBeforeNotifyDataSetChanged();
+ super.notifyDataSetChanged();
+ listSelectionManager.onAfterNotifyDataSetChanged();
+ }
+
+ @Override
+ public String transformTextForCopy(CharSequence text, int start, int end) {
+ return text.toString().substring(start, end);
+ }
+
+ public void openDownloadable(Message message) {
+ DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
+ if (!file.exists()) {
+ Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ String mime = file.getMimeType();
+ if (mime.startsWith("image/")) {
+ Intent intent = new Intent(getContext(), ShowFullscreenMessageActivity.class);
+ intent.putExtra("image", Uri.fromFile(file));
+ try {
+ activity.startActivity(intent);
+ return;
+ } catch (ActivityNotFoundException e) {
+ //ignored
+ }
+ } else if (mime.startsWith("video/")) {
+ Intent intent = new Intent(getContext(), ShowFullscreenMessageActivity.class);
+ intent.putExtra("video", Uri.fromFile(file));
+ try {
+ activity.startActivity(intent);
+ return;
+ } catch (ActivityNotFoundException e) {
+ //ignored
+ }
+ }
+ Intent openIntent = new Intent(Intent.ACTION_VIEW);
+ if (mime == null) {
+ mime = "*/*";
+ }
+ Uri uri;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ try {
+ uri = FileProvider.getUriForFile(activity, FileBackend.CONVERSATIONS_FILE_PROVIDER, file);
+ } catch (IllegalArgumentException e) {
+ Toast.makeText(activity, activity.getString(R.string.no_permission_to_access_x, file.getAbsolutePath()), Toast.LENGTH_SHORT).show();
+ return;
+ }
+ openIntent.setDataAndType(uri, mime);
+ openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } else {
+ uri = Uri.fromFile(file);
+ }
+ openIntent.setDataAndType(uri, mime);
+ PackageManager manager = activity.getPackageManager();
+ List<ResolveInfo> info = manager.queryIntentActivities(openIntent, 0);
+ if (info.size() == 0) {
+ openIntent.setDataAndType(Uri.fromFile(file), "*/*");
+ }
+ try {
+ getContext().startActivity(openIntent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(activity, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ public void showLocation(Message message) {
+ for (Intent intent : GeoHelper.createGeoIntentsFromMessage(message)) {
+ if (intent.resolveActivity(getContext().getPackageManager()) != null) {
+ getContext().startActivity(intent);
+ return;
+ }
+ }
+ 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 TextView getMessageBody(View view) {
+ final Object tag = view.getTag();
+ if (tag instanceof ViewHolder) {
+ final ViewHolder viewHolder = (ViewHolder) tag;
+ return viewHolder.messageBody;
+ }
+ return null;
+ }
+
+ public interface OnContactPictureClicked {
+ void onContactPictureClicked(Message message);
+ }
+
+ public interface OnContactPictureLongClicked {
+ void onContactPictureLongClicked(Message message);
+ }
+
+ private static class ViewHolder {
+
+ protected LinearLayout message_box;
+ protected Button download_button;
+ protected ViewGroup aw_player;
+ protected ImageView image;
+ protected ImageView indicator;
+ protected ImageView indicatorReceived;
+ protected ImageView indicatorRead;
+ protected TextView time;
+ protected CopyTextView messageBody;
+ 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> {
+ private final WeakReference<ImageView> imageViewReference;
+ private Message message = null;
+
+ public BitmapWorkerTask(ImageView imageView) {
+ imageViewReference = new WeakReference<>(imageView);
+ }
+
+ @Override
+ protected Bitmap doInBackground(Message... params) {
+ return activity.avatarService().get(params[0], activity.getPixel(48), isCancelled());
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ if (bitmap != null && !isCancelled()) {
+ final ImageView imageView = imageViewReference.get();
+ if (imageView != null) {
+ imageView.setImageBitmap(bitmap);
+ imageView.setBackgroundColor(0x00000000);
+ }
+ }
+ }
+ }
+
+ public void loadAvatar(Message message, ImageView imageView) {
+ if (cancelPotentialWork(message, imageView)) {
+ final Bitmap bm = activity.avatarService().get(message, activity.getPixel(48), true);
+ if (bm != null) {
+ cancelPotentialWork(message, imageView);
+ imageView.setImageBitmap(bm);
+ imageView.setBackgroundColor(0x00000000);
+ } else {
+ imageView.setBackgroundColor(UIHelper.getColorForName(UIHelper.getMessageDisplayName(message)));
+ imageView.setImageDrawable(null);
+ final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+ final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
+ imageView.setImageDrawable(asyncDrawable);
+ try {
+ task.execute(message);
+ } catch (final RejectedExecutionException ignored) {
+ }
+ }
+ }
+ }
+
+ public static boolean cancelPotentialWork(Message message, ImageView imageView) {
+ final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+ if (bitmapWorkerTask != null) {
+ final Message oldMessage = bitmapWorkerTask.message;
+ if (oldMessage == null || message != oldMessage) {
+ bitmapWorkerTask.cancel(true);
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
+ if (imageView != null) {
+ final Drawable drawable = imageView.getDrawable();
+ if (drawable instanceof AsyncDrawable) {
+ final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+ return asyncDrawable.getBitmapWorkerTask();
+ }
+ }
+ return null;
+ }
+
+ static class AsyncDrawable extends BitmapDrawable {
+ private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+ public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
+ super(res, bitmap);
+ bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
+ }
+
+ public BitmapWorkerTask getBitmapWorkerTask() {
+ return bitmapWorkerTaskReference.get();
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/forms/FormBooleanFieldWrapper.java b/src/main/java/de/pixart/messenger/ui/forms/FormBooleanFieldWrapper.java
index 2593ac204..51ac79c66 100644
--- a/src/main/java/de/pixart/messenger/ui/forms/FormBooleanFieldWrapper.java
+++ b/src/main/java/de/pixart/messenger/ui/forms/FormBooleanFieldWrapper.java
@@ -12,69 +12,69 @@ import de.pixart.messenger.xmpp.forms.Field;
public class FormBooleanFieldWrapper extends FormFieldWrapper {
- protected CheckBox checkBox;
+ protected CheckBox checkBox;
- protected FormBooleanFieldWrapper(Context context, Field field) {
- super(context, field);
- checkBox = (CheckBox) view.findViewById(R.id.field);
- checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- checkBox.setError(null);
- invokeOnFormFieldValuesEdited();
- }
- });
- }
+ protected FormBooleanFieldWrapper(Context context, Field field) {
+ super(context, field);
+ checkBox = (CheckBox) view.findViewById(R.id.field);
+ checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ checkBox.setError(null);
+ invokeOnFormFieldValuesEdited();
+ }
+ });
+ }
- @Override
- protected void setLabel(String label, boolean required) {
- CheckBox checkBox = (CheckBox) view.findViewById(R.id.field);
- checkBox.setText(createSpannableLabelString(label, required));
- }
+ @Override
+ protected void setLabel(String label, boolean required) {
+ CheckBox checkBox = (CheckBox) view.findViewById(R.id.field);
+ checkBox.setText(createSpannableLabelString(label, required));
+ }
- @Override
- public List<String> getValues() {
- List<String> values = new ArrayList<>();
- values.add(Boolean.toString(checkBox.isChecked()));
- return values;
- }
+ @Override
+ public List<String> getValues() {
+ List<String> values = new ArrayList<>();
+ values.add(Boolean.toString(checkBox.isChecked()));
+ return values;
+ }
- @Override
- protected void setValues(List<String> values) {
- if (values.size() == 0) {
- checkBox.setChecked(false);
- } else {
- checkBox.setChecked(Boolean.parseBoolean(values.get(0)));
- }
- }
+ @Override
+ protected void setValues(List<String> values) {
+ if (values.size() == 0) {
+ checkBox.setChecked(false);
+ } else {
+ checkBox.setChecked(Boolean.parseBoolean(values.get(0)));
+ }
+ }
- @Override
- public boolean validates() {
- if (checkBox.isChecked() || !field.isRequired()) {
- return true;
- } else {
- checkBox.setError(context.getString(R.string.this_field_is_required));
- checkBox.requestFocus();
- return false;
- }
- }
+ @Override
+ public boolean validates() {
+ if (checkBox.isChecked() || !field.isRequired()) {
+ return true;
+ } else {
+ checkBox.setError(context.getString(R.string.this_field_is_required));
+ checkBox.requestFocus();
+ return false;
+ }
+ }
- @Override
- public boolean edited() {
- if (field.getValues().size() == 0) {
- return checkBox.isChecked();
- } else {
- return super.edited();
- }
- }
+ @Override
+ public boolean edited() {
+ if (field.getValues().size() == 0) {
+ return checkBox.isChecked();
+ } else {
+ return super.edited();
+ }
+ }
- @Override
- protected int getLayoutResource() {
- return R.layout.form_boolean;
- }
+ @Override
+ protected int getLayoutResource() {
+ return R.layout.form_boolean;
+ }
- @Override
- void setReadOnly(boolean readOnly) {
- checkBox.setEnabled(!readOnly);
- }
+ @Override
+ void setReadOnly(boolean readOnly) {
+ checkBox.setEnabled(!readOnly);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/forms/FormFieldFactory.java b/src/main/java/de/pixart/messenger/ui/forms/FormFieldFactory.java
index c1318b9f1..5a30e35a5 100644
--- a/src/main/java/de/pixart/messenger/ui/forms/FormFieldFactory.java
+++ b/src/main/java/de/pixart/messenger/ui/forms/FormFieldFactory.java
@@ -7,24 +7,23 @@ import java.util.Hashtable;
import de.pixart.messenger.xmpp.forms.Field;
-
public class FormFieldFactory {
- private static final Hashtable<String, Class> typeTable = new Hashtable<>();
-
- static {
- typeTable.put("text-single", FormTextFieldWrapper.class);
- typeTable.put("text-multi", FormTextFieldWrapper.class);
- typeTable.put("text-private", FormTextFieldWrapper.class);
- typeTable.put("jid-single", FormJidSingleFieldWrapper.class);
- typeTable.put("boolean", FormBooleanFieldWrapper.class);
- }
-
- protected static FormFieldWrapper createFromField(Context context, Field field) {
- Class clazz = typeTable.get(field.getType());
- if (clazz == null) {
- clazz = FormTextFieldWrapper.class;
- }
- return FormFieldWrapper.createFromField(clazz, context, field);
- }
+ private static final Hashtable<String, Class> typeTable = new Hashtable<>();
+
+ static {
+ typeTable.put("text-single", FormTextFieldWrapper.class);
+ typeTable.put("text-multi", FormTextFieldWrapper.class);
+ typeTable.put("text-private", FormTextFieldWrapper.class);
+ typeTable.put("jid-single", FormJidSingleFieldWrapper.class);
+ typeTable.put("boolean", FormBooleanFieldWrapper.class);
+ }
+
+ protected static FormFieldWrapper createFromField(Context context, Field field) {
+ Class clazz = typeTable.get(field.getType());
+ if (clazz == null) {
+ clazz = FormTextFieldWrapper.class;
+ }
+ return FormFieldWrapper.createFromField(clazz, context, field);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java b/src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java
index 5182f3e7a..01b9ea0f9 100644
--- a/src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java
+++ b/src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java
@@ -14,80 +14,80 @@ import de.pixart.messenger.xmpp.forms.Field;
public abstract class FormFieldWrapper {
- protected final Context context;
- protected final Field field;
- protected final View view;
- protected OnFormFieldValuesEdited onFormFieldValuesEditedListener;
-
- protected FormFieldWrapper(Context context, Field field) {
- this.context = context;
- this.field = field;
- LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- this.view = inflater.inflate(getLayoutResource(), null);
- String label = field.getLabel();
- if (label == null) {
- label = field.getFieldName();
- }
- setLabel(label, field.isRequired());
- }
-
- public final void submit() {
- this.field.setValues(getValues());
- }
-
- public final View getView() {
- return view;
- }
-
- protected abstract void setLabel(String label, boolean required);
-
- abstract List<String> getValues();
-
- protected abstract void setValues(List<String> values);
-
- abstract boolean validates();
-
- abstract protected int getLayoutResource();
-
- abstract void setReadOnly(boolean readOnly);
-
- protected SpannableString createSpannableLabelString(String label, boolean required) {
- SpannableString spannableString = new SpannableString(label + (required ? " *" : ""));
- if (required) {
- 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);
- }
- return spannableString;
- }
-
- protected void invokeOnFormFieldValuesEdited() {
- if (this.onFormFieldValuesEditedListener != null) {
- this.onFormFieldValuesEditedListener.onFormFieldValuesEdited();
- }
- }
-
- public boolean edited() {
- return !field.getValues().equals(getValues());
- }
-
- public void setOnFormFieldValuesEditedListener(OnFormFieldValuesEdited listener) {
- this.onFormFieldValuesEditedListener = listener;
- }
-
- protected static <F extends FormFieldWrapper> FormFieldWrapper createFromField(Class<F> c, Context context, Field field) {
- try {
- F fieldWrapper = c.getDeclaredConstructor(Context.class, Field.class).newInstance(context,field);
- fieldWrapper.setValues(field.getValues());
- return fieldWrapper;
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
-
- public interface OnFormFieldValuesEdited {
- void onFormFieldValuesEdited();
- }
+ protected final Context context;
+ protected final Field field;
+ protected final View view;
+ protected OnFormFieldValuesEdited onFormFieldValuesEditedListener;
+
+ protected FormFieldWrapper(Context context, Field field) {
+ this.context = context;
+ this.field = field;
+ LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ this.view = inflater.inflate(getLayoutResource(), null);
+ String label = field.getLabel();
+ if (label == null) {
+ label = field.getFieldName();
+ }
+ setLabel(label, field.isRequired());
+ }
+
+ public final void submit() {
+ this.field.setValues(getValues());
+ }
+
+ public final View getView() {
+ return view;
+ }
+
+ protected abstract void setLabel(String label, boolean required);
+
+ abstract List<String> getValues();
+
+ protected abstract void setValues(List<String> values);
+
+ abstract boolean validates();
+
+ abstract protected int getLayoutResource();
+
+ abstract void setReadOnly(boolean readOnly);
+
+ protected SpannableString createSpannableLabelString(String label, boolean required) {
+ SpannableString spannableString = new SpannableString(label + (required ? " *" : ""));
+ if (required) {
+ 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);
+ }
+ return spannableString;
+ }
+
+ protected void invokeOnFormFieldValuesEdited() {
+ if (this.onFormFieldValuesEditedListener != null) {
+ this.onFormFieldValuesEditedListener.onFormFieldValuesEdited();
+ }
+ }
+
+ public boolean edited() {
+ return !field.getValues().equals(getValues());
+ }
+
+ public void setOnFormFieldValuesEditedListener(OnFormFieldValuesEdited listener) {
+ this.onFormFieldValuesEditedListener = listener;
+ }
+
+ protected static <F extends FormFieldWrapper> FormFieldWrapper createFromField(Class<F> c, Context context, Field field) {
+ try {
+ F fieldWrapper = c.getDeclaredConstructor(Context.class, Field.class).newInstance(context, field);
+ fieldWrapper.setValues(field.getValues());
+ return fieldWrapper;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public interface OnFormFieldValuesEdited {
+ void onFormFieldValuesEdited();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/forms/FormJidSingleFieldWrapper.java b/src/main/java/de/pixart/messenger/ui/forms/FormJidSingleFieldWrapper.java
index 8d37f259f..b6aeaf344 100644
--- a/src/main/java/de/pixart/messenger/ui/forms/FormJidSingleFieldWrapper.java
+++ b/src/main/java/de/pixart/messenger/ui/forms/FormJidSingleFieldWrapper.java
@@ -12,33 +12,33 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class FormJidSingleFieldWrapper extends FormTextFieldWrapper {
- protected FormJidSingleFieldWrapper(Context context, Field field) {
- super(context, field);
- editText.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
- editText.setHint(R.string.account_settings_example_jabber_id);
- }
+ protected FormJidSingleFieldWrapper(Context context, Field field) {
+ super(context, field);
+ editText.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
+ editText.setHint(R.string.account_settings_example_jabber_id);
+ }
- @Override
- public boolean validates() {
- String value = getValue();
- if (!value.isEmpty()) {
- try {
- Jid.fromString(value);
- } catch (InvalidJidException e) {
- editText.setError(context.getString(R.string.invalid_jid));
- editText.requestFocus();
- return false;
- }
- }
- return super.validates();
- }
+ @Override
+ public boolean validates() {
+ String value = getValue();
+ if (!value.isEmpty()) {
+ try {
+ Jid.fromString(value);
+ } catch (InvalidJidException e) {
+ editText.setError(context.getString(R.string.invalid_jid));
+ editText.requestFocus();
+ return false;
+ }
+ }
+ return super.validates();
+ }
- @Override
- protected void setValues(List<String> values) {
- StringBuilder builder = new StringBuilder("");
- for(String value : values) {
- builder.append(value);
- }
- editText.setText(builder.toString());
- }
+ @Override
+ protected void setValues(List<String> values) {
+ StringBuilder builder = new StringBuilder("");
+ for (String value : values) {
+ builder.append(value);
+ }
+ editText.setText(builder.toString());
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/forms/FormTextFieldWrapper.java b/src/main/java/de/pixart/messenger/ui/forms/FormTextFieldWrapper.java
index cccfd1608..5635bc7b4 100644
--- a/src/main/java/de/pixart/messenger/ui/forms/FormTextFieldWrapper.java
+++ b/src/main/java/de/pixart/messenger/ui/forms/FormTextFieldWrapper.java
@@ -15,83 +15,83 @@ import de.pixart.messenger.xmpp.forms.Field;
public class FormTextFieldWrapper extends FormFieldWrapper {
- protected EditText editText;
+ protected EditText editText;
- protected FormTextFieldWrapper(Context context, Field field) {
- super(context, field);
- editText = (EditText) view.findViewById(R.id.field);
- editText.setSingleLine(!"text-multi".equals(field.getType()));
- if ("text-private".equals(field.getType())) {
- editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
- }
- editText.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
+ protected FormTextFieldWrapper(Context context, Field field) {
+ super(context, field);
+ editText = (EditText) view.findViewById(R.id.field);
+ editText.setSingleLine(!"text-multi".equals(field.getType()));
+ if ("text-private".equals(field.getType())) {
+ editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ }
+ editText.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- editText.setError(null);
- invokeOnFormFieldValuesEdited();
- }
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ editText.setError(null);
+ invokeOnFormFieldValuesEdited();
+ }
- @Override
- public void afterTextChanged(Editable s) {
- }
- });
- }
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+ });
+ }
- @Override
- protected void setLabel(String label, boolean required) {
- TextView textView = (TextView) view.findViewById(R.id.label);
- textView.setText(createSpannableLabelString(label, required));
- }
+ @Override
+ protected void setLabel(String label, boolean required) {
+ TextView textView = (TextView) view.findViewById(R.id.label);
+ textView.setText(createSpannableLabelString(label, required));
+ }
- protected String getValue() {
- return editText.getText().toString();
- }
+ protected String getValue() {
+ return editText.getText().toString();
+ }
- @Override
- public List<String> getValues() {
- List<String> values = new ArrayList<>();
- for (String line : getValue().split("\\n")) {
- if (line.length() > 0) {
- values.add(line);
- }
- }
- return values;
- }
+ @Override
+ public List<String> getValues() {
+ List<String> values = new ArrayList<>();
+ for (String line : getValue().split("\\n")) {
+ if (line.length() > 0) {
+ values.add(line);
+ }
+ }
+ return values;
+ }
- @Override
- protected void setValues(List<String> values) {
- StringBuilder builder = new StringBuilder("");
- for(int i = 0; i < values.size(); ++i) {
- builder.append(values.get(i));
- if (i < values.size() - 1 && "text-multi".equals(field.getType())) {
- builder.append("\n");
- }
- }
- editText.setText(builder.toString());
- }
+ @Override
+ protected void setValues(List<String> values) {
+ StringBuilder builder = new StringBuilder("");
+ for (int i = 0; i < values.size(); ++i) {
+ builder.append(values.get(i));
+ if (i < values.size() - 1 && "text-multi".equals(field.getType())) {
+ builder.append("\n");
+ }
+ }
+ editText.setText(builder.toString());
+ }
- @Override
- public boolean validates() {
- if (getValue().trim().length() > 0 || !field.isRequired()) {
- return true;
- } else {
- editText.setError(context.getString(R.string.this_field_is_required));
- editText.requestFocus();
- return false;
- }
- }
+ @Override
+ public boolean validates() {
+ if (getValue().trim().length() > 0 || !field.isRequired()) {
+ return true;
+ } else {
+ editText.setError(context.getString(R.string.this_field_is_required));
+ editText.requestFocus();
+ return false;
+ }
+ }
- @Override
- protected int getLayoutResource() {
- return R.layout.form_text;
- }
+ @Override
+ protected int getLayoutResource() {
+ return R.layout.form_text;
+ }
- @Override
- void setReadOnly(boolean readOnly) {
- editText.setEnabled(!readOnly);
- }
+ @Override
+ void setReadOnly(boolean readOnly) {
+ editText.setEnabled(!readOnly);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/forms/FormWrapper.java b/src/main/java/de/pixart/messenger/ui/forms/FormWrapper.java
index 05fa922b5..f22bef721 100644
--- a/src/main/java/de/pixart/messenger/ui/forms/FormWrapper.java
+++ b/src/main/java/de/pixart/messenger/ui/forms/FormWrapper.java
@@ -11,62 +11,62 @@ import de.pixart.messenger.xmpp.forms.Field;
public class FormWrapper {
- private final LinearLayout layout;
+ private final LinearLayout layout;
- private final Data form;
+ private final Data form;
- private final List<FormFieldWrapper> fieldWrappers = new ArrayList<>();
+ private final List<FormFieldWrapper> fieldWrappers = new ArrayList<>();
- private FormWrapper(Context context, LinearLayout linearLayout, Data form) {
- this.form = form;
- this.layout = linearLayout;
- this.layout.removeAllViews();
- for(Field field : form.getFields()) {
- FormFieldWrapper fieldWrapper = FormFieldFactory.createFromField(context,field);
- if (fieldWrapper != null) {
- layout.addView(fieldWrapper.getView());
- fieldWrappers.add(fieldWrapper);
- }
- }
- }
+ private FormWrapper(Context context, LinearLayout linearLayout, Data form) {
+ this.form = form;
+ this.layout = linearLayout;
+ this.layout.removeAllViews();
+ for (Field field : form.getFields()) {
+ FormFieldWrapper fieldWrapper = FormFieldFactory.createFromField(context, field);
+ if (fieldWrapper != null) {
+ layout.addView(fieldWrapper.getView());
+ fieldWrappers.add(fieldWrapper);
+ }
+ }
+ }
- public Data submit() {
- for(FormFieldWrapper fieldWrapper : fieldWrappers) {
- fieldWrapper.submit();
- }
- this.form.submit();
- return this.form;
- }
+ public Data submit() {
+ for (FormFieldWrapper fieldWrapper : fieldWrappers) {
+ fieldWrapper.submit();
+ }
+ this.form.submit();
+ return this.form;
+ }
- public boolean validates() {
- boolean validates = true;
- for(FormFieldWrapper fieldWrapper : fieldWrappers) {
- validates &= fieldWrapper.validates();
- }
- return validates;
- }
+ public boolean validates() {
+ boolean validates = true;
+ for (FormFieldWrapper fieldWrapper : fieldWrappers) {
+ validates &= fieldWrapper.validates();
+ }
+ return validates;
+ }
- public void setOnFormFieldValuesEditedListener(FormFieldWrapper.OnFormFieldValuesEdited listener) {
- for(FormFieldWrapper fieldWrapper : fieldWrappers) {
- fieldWrapper.setOnFormFieldValuesEditedListener(listener);
- }
- }
+ public void setOnFormFieldValuesEditedListener(FormFieldWrapper.OnFormFieldValuesEdited listener) {
+ for (FormFieldWrapper fieldWrapper : fieldWrappers) {
+ fieldWrapper.setOnFormFieldValuesEditedListener(listener);
+ }
+ }
- public void setReadOnly(boolean b) {
- for(FormFieldWrapper fieldWrapper : fieldWrappers) {
- fieldWrapper.setReadOnly(b);
- }
- }
+ public void setReadOnly(boolean b) {
+ for (FormFieldWrapper fieldWrapper : fieldWrappers) {
+ fieldWrapper.setReadOnly(b);
+ }
+ }
- public boolean edited() {
- boolean edited = false;
- for(FormFieldWrapper fieldWrapper : fieldWrappers) {
- edited |= fieldWrapper.edited();
- }
- return edited;
- }
+ public boolean edited() {
+ boolean edited = false;
+ for (FormFieldWrapper fieldWrapper : fieldWrappers) {
+ edited |= fieldWrapper.edited();
+ }
+ return edited;
+ }
- public static FormWrapper createInLayout(Context context, LinearLayout layout, Data form) {
- return new FormWrapper(context, layout, form);
- }
+ public static FormWrapper createInLayout(Context context, LinearLayout layout, Data form) {
+ return new FormWrapper(context, layout, form);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/widget/ClickableMovementMethod.java b/src/main/java/de/pixart/messenger/ui/widget/ClickableMovementMethod.java
index dbf88f1d7..c399a7864 100644
--- a/src/main/java/de/pixart/messenger/ui/widget/ClickableMovementMethod.java
+++ b/src/main/java/de/pixart/messenger/ui/widget/ClickableMovementMethod.java
@@ -9,34 +9,34 @@ import android.widget.TextView;
public class ClickableMovementMethod extends ArrowKeyMovementMethod {
- @Override
- public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
- // Just copied from android.text.method.LinkMovementMethod
- if (event.getAction() == MotionEvent.ACTION_UP) {
- int x = (int) event.getX();
- int y = (int) event.getY();
- x -= widget.getTotalPaddingLeft();
- y -= widget.getTotalPaddingTop();
- x += widget.getScrollX();
- y += widget.getScrollY();
- Layout layout = widget.getLayout();
- int line = layout.getLineForVertical(y);
- int off = layout.getOffsetForHorizontal(line, x);
- ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
- if (link.length != 0) {
- link[0].onClick(widget);
- return true;
- }
- }
- return super.onTouchEvent(widget, buffer, event);
- }
+ @Override
+ public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
+ // Just copied from android.text.method.LinkMovementMethod
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+ x -= widget.getTotalPaddingLeft();
+ y -= widget.getTotalPaddingTop();
+ x += widget.getScrollX();
+ y += widget.getScrollY();
+ Layout layout = widget.getLayout();
+ int line = layout.getLineForVertical(y);
+ int off = layout.getOffsetForHorizontal(line, x);
+ ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
+ if (link.length != 0) {
+ link[0].onClick(widget);
+ return true;
+ }
+ }
+ return super.onTouchEvent(widget, buffer, event);
+ }
- public static ClickableMovementMethod getInstance() {
- if (sInstance == null) {
- sInstance = new ClickableMovementMethod();
- }
- return sInstance;
- }
+ public static ClickableMovementMethod getInstance() {
+ if (sInstance == null) {
+ sInstance = new ClickableMovementMethod();
+ }
+ return sInstance;
+ }
- private static ClickableMovementMethod sInstance;
+ private static ClickableMovementMethod sInstance;
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/widget/Switch.java b/src/main/java/de/pixart/messenger/ui/widget/Switch.java
index 2bd4eda3d..40d8f4c99 100644
--- a/src/main/java/de/pixart/messenger/ui/widget/Switch.java
+++ b/src/main/java/de/pixart/messenger/ui/widget/Switch.java
@@ -9,60 +9,60 @@ 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;
+ 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) {
+ 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) {
+ 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();
- }
+ 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 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);
- }
+ @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/de/pixart/messenger/utils/ConversationsFileObserver.java b/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java
index fbd4c9708..dd2d673b4 100644
--- a/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java
+++ b/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java
@@ -35,7 +35,7 @@ public abstract class ConversationsFileObserver {
if (files == null) {
continue;
}
- for(File file : files) {
+ for (File file : files) {
if (file.isDirectory() && !file.getName().equals(".") && !file.getName().equals("..")) {
final String currentPath = file.getAbsolutePath();
if (depth(file) <= 8 && !stack.contains(currentPath) && !observing(currentPath)) {
@@ -44,22 +44,22 @@ public abstract class ConversationsFileObserver {
}
}
}
- for(FileObserver observer : mObservers) {
+ for (FileObserver observer : mObservers) {
observer.startWatching();
}
}
private static int depth(File file) {
int depth = 0;
- while((file = file.getParentFile()) != null) {
+ while ((file = file.getParentFile()) != null) {
depth++;
}
return depth;
}
private boolean observing(String path) {
- for(SingleFileObserver observer : mObservers) {
- if(path.equals(observer.path)) {
+ for (SingleFileObserver observer : mObservers) {
+ if (path.equals(observer.path)) {
return true;
}
}
@@ -67,7 +67,7 @@ public abstract class ConversationsFileObserver {
}
public synchronized void stopWatching() {
- for(FileObserver observer : mObservers) {
+ for (FileObserver observer : mObservers) {
observer.stopWatching();
}
mObservers.clear();
@@ -85,7 +85,7 @@ public abstract class ConversationsFileObserver {
@Override
public void onEvent(int event, String filename) {
- ConversationsFileObserver.this.onEvent(event, path+'/'+filename);
+ ConversationsFileObserver.this.onEvent(event, path + '/' + filename);
}
}
diff --git a/src/main/java/de/pixart/messenger/utils/CryptoHelper.java b/src/main/java/de/pixart/messenger/utils/CryptoHelper.java
index 510f60352..ec4f750fd 100644
--- a/src/main/java/de/pixart/messenger/utils/CryptoHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/CryptoHelper.java
@@ -31,179 +31,179 @@ import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
public final class CryptoHelper {
- public static final String FILETRANSFER = "?FILETRANSFERv1:";
- private final static char[] hexArray = "0123456789abcdef".toCharArray();
-
- public static final Pattern UUID_PATTERN = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}");
- final public static byte[] ONE = new byte[] { 0, 0, 0, 1 };
-
- public static String bytesToHex(byte[] bytes) {
- char[] hexChars = new char[bytes.length * 2];
- for (int j = 0; j < bytes.length; j++) {
- int v = bytes[j] & 0xFF;
- hexChars[j * 2] = hexArray[v >>> 4];
- hexChars[j * 2 + 1] = hexArray[v & 0x0F];
- }
- return new String(hexChars);
- }
-
- public static byte[] hexToBytes(String hexString) {
- int len = hexString.length();
- byte[] array = new byte[len / 2];
- for (int i = 0; i < len; i += 2) {
- array[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
- .digit(hexString.charAt(i + 1), 16));
- }
- return array;
- }
-
- public static String hexToString(final String hexString) {
- return new String(hexToBytes(hexString));
- }
-
- public static byte[] concatenateByteArrays(byte[] a, byte[] b) {
- byte[] result = new byte[a.length + b.length];
- System.arraycopy(a, 0, result, 0, a.length);
- System.arraycopy(b, 0, result, a.length, b.length);
- return result;
- }
-
- /**
- * Escapes usernames or passwords for SASL.
- */
- public static String saslEscape(final String s) {
- final StringBuilder sb = new StringBuilder((int) (s.length() * 1.1));
- for (int i = 0; i < s.length(); i++) {
- char c = s.charAt(i);
- switch (c) {
- case ',':
- sb.append("=2C");
- break;
- case '=':
- sb.append("=3D");
- break;
- default:
- sb.append(c);
- break;
- }
- }
- return sb.toString();
- }
-
- public static String saslPrep(final String s) {
- return Normalizer.normalize(s, Normalizer.Form.NFKC);
- }
-
- public static String prettifyFingerprint(String fingerprint) {
- if (fingerprint==null) {
- return "";
- } else if (fingerprint.length() < 40) {
- return fingerprint;
- }
- StringBuilder builder = new StringBuilder(fingerprint.toLowerCase(Locale.US).replaceAll("\\s", ""));
- for(int i=8;i<builder.length();i+=9) {
- builder.insert(i, ' ');
- }
- return builder.toString();
- }
-
- public static String prettifyFingerprintCert(String fingerprint) {
- StringBuilder builder = new StringBuilder(fingerprint);
- for(int i=2;i < builder.length(); i+=3) {
- builder.insert(i,':');
- }
- return builder.toString();
- }
-
- public static String[] getOrderedCipherSuites(final String[] platformSupportedCipherSuites) {
- final Collection<String> cipherSuites = new LinkedHashSet<>(Arrays.asList(Config.ENABLED_CIPHERS));
- final List<String> platformCiphers = Arrays.asList(platformSupportedCipherSuites);
- cipherSuites.retainAll(platformCiphers);
- cipherSuites.addAll(platformCiphers);
- filterWeakCipherSuites(cipherSuites);
- return cipherSuites.toArray(new String[cipherSuites.size()]);
- }
-
- private static void filterWeakCipherSuites(final Collection<String> cipherSuites) {
- final Iterator<String> it = cipherSuites.iterator();
- while (it.hasNext()) {
- String cipherName = it.next();
- // remove all ciphers with no or very weak encryption or no authentication
- for (String weakCipherPattern : Config.WEAK_CIPHER_PATTERNS) {
- if (cipherName.contains(weakCipherPattern)) {
- it.remove();
- break;
- }
- }
- }
- }
-
- public static Pair<Jid,String> extractJidAndName(X509Certificate certificate) throws CertificateEncodingException, InvalidJidException, CertificateParsingException {
- Collection<List<?>> alternativeNames = certificate.getSubjectAlternativeNames();
- List<String> emails = new ArrayList<>();
- if (alternativeNames != null) {
- for(List<?> san : alternativeNames) {
- Integer type = (Integer) san.get(0);
- if (type == 1) {
- emails.add((String) san.get(1));
- }
- }
- }
- X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
- if (emails.size() == 0) {
- emails.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue()));
- }
- String name = IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue());
- if (emails.size() >= 1) {
- return new Pair<>(Jid.fromString(emails.get(0)), name);
- } else {
- return null;
- }
- }
-
- public static Bundle extractCertificateInformation(X509Certificate certificate) {
- Bundle information = new Bundle();
- try {
- JcaX509CertificateHolder holder = new JcaX509CertificateHolder(certificate);
- X500Name subject = holder.getSubject();
- try {
- information.putString("subject_cn", subject.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString());
- } catch (Exception e) {
- //ignored
- }
- try {
- information.putString("subject_o",subject.getRDNs(BCStyle.O)[0].getFirst().getValue().toString());
- } catch (Exception e) {
- //ignored
- }
-
- X500Name issuer = holder.getIssuer();
- try {
- information.putString("issuer_cn", issuer.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString());
- } catch (Exception e) {
- //ignored
- }
- try {
- information.putString("issuer_o", issuer.getRDNs(BCStyle.O)[0].getFirst().getValue().toString());
- } catch (Exception e) {
- //ignored
- }
- try {
- information.putString("sha1", getFingerprintCert(certificate.getEncoded()));
- } catch (Exception e) {
-
- }
- return information;
- } catch (CertificateEncodingException e) {
- return information;
- }
- }
-
- public static String getFingerprintCert(byte[] input) throws NoSuchAlgorithmException {
- MessageDigest md = MessageDigest.getInstance("SHA-1");
- byte[] fingerprint = md.digest(input);
- return prettifyFingerprintCert(bytesToHex(fingerprint));
- }
+ public static final String FILETRANSFER = "?FILETRANSFERv1:";
+ private final static char[] hexArray = "0123456789abcdef".toCharArray();
+
+ public static final Pattern UUID_PATTERN = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}");
+ final public static byte[] ONE = new byte[]{0, 0, 0, 1};
+
+ public static String bytesToHex(byte[] bytes) {
+ char[] hexChars = new char[bytes.length * 2];
+ for (int j = 0; j < bytes.length; j++) {
+ int v = bytes[j] & 0xFF;
+ hexChars[j * 2] = hexArray[v >>> 4];
+ hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+ }
+ return new String(hexChars);
+ }
+
+ public static byte[] hexToBytes(String hexString) {
+ int len = hexString.length();
+ byte[] array = new byte[len / 2];
+ for (int i = 0; i < len; i += 2) {
+ array[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
+ .digit(hexString.charAt(i + 1), 16));
+ }
+ return array;
+ }
+
+ public static String hexToString(final String hexString) {
+ return new String(hexToBytes(hexString));
+ }
+
+ public static byte[] concatenateByteArrays(byte[] a, byte[] b) {
+ byte[] result = new byte[a.length + b.length];
+ System.arraycopy(a, 0, result, 0, a.length);
+ System.arraycopy(b, 0, result, a.length, b.length);
+ return result;
+ }
+
+ /**
+ * Escapes usernames or passwords for SASL.
+ */
+ public static String saslEscape(final String s) {
+ final StringBuilder sb = new StringBuilder((int) (s.length() * 1.1));
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ switch (c) {
+ case ',':
+ sb.append("=2C");
+ break;
+ case '=':
+ sb.append("=3D");
+ break;
+ default:
+ sb.append(c);
+ break;
+ }
+ }
+ return sb.toString();
+ }
+
+ public static String saslPrep(final String s) {
+ return Normalizer.normalize(s, Normalizer.Form.NFKC);
+ }
+
+ public static String prettifyFingerprint(String fingerprint) {
+ if (fingerprint == null) {
+ return "";
+ } else if (fingerprint.length() < 40) {
+ return fingerprint;
+ }
+ StringBuilder builder = new StringBuilder(fingerprint.toLowerCase(Locale.US).replaceAll("\\s", ""));
+ for (int i = 8; i < builder.length(); i += 9) {
+ builder.insert(i, ' ');
+ }
+ return builder.toString();
+ }
+
+ public static String prettifyFingerprintCert(String fingerprint) {
+ StringBuilder builder = new StringBuilder(fingerprint);
+ for (int i = 2; i < builder.length(); i += 3) {
+ builder.insert(i, ':');
+ }
+ return builder.toString();
+ }
+
+ public static String[] getOrderedCipherSuites(final String[] platformSupportedCipherSuites) {
+ final Collection<String> cipherSuites = new LinkedHashSet<>(Arrays.asList(Config.ENABLED_CIPHERS));
+ final List<String> platformCiphers = Arrays.asList(platformSupportedCipherSuites);
+ cipherSuites.retainAll(platformCiphers);
+ cipherSuites.addAll(platformCiphers);
+ filterWeakCipherSuites(cipherSuites);
+ return cipherSuites.toArray(new String[cipherSuites.size()]);
+ }
+
+ private static void filterWeakCipherSuites(final Collection<String> cipherSuites) {
+ final Iterator<String> it = cipherSuites.iterator();
+ while (it.hasNext()) {
+ String cipherName = it.next();
+ // remove all ciphers with no or very weak encryption or no authentication
+ for (String weakCipherPattern : Config.WEAK_CIPHER_PATTERNS) {
+ if (cipherName.contains(weakCipherPattern)) {
+ it.remove();
+ break;
+ }
+ }
+ }
+ }
+
+ public static Pair<Jid, String> extractJidAndName(X509Certificate certificate) throws CertificateEncodingException, InvalidJidException, CertificateParsingException {
+ Collection<List<?>> alternativeNames = certificate.getSubjectAlternativeNames();
+ List<String> emails = new ArrayList<>();
+ if (alternativeNames != null) {
+ for (List<?> san : alternativeNames) {
+ Integer type = (Integer) san.get(0);
+ if (type == 1) {
+ emails.add((String) san.get(1));
+ }
+ }
+ }
+ X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
+ if (emails.size() == 0) {
+ emails.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue()));
+ }
+ String name = IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue());
+ if (emails.size() >= 1) {
+ return new Pair<>(Jid.fromString(emails.get(0)), name);
+ } else {
+ return null;
+ }
+ }
+
+ public static Bundle extractCertificateInformation(X509Certificate certificate) {
+ Bundle information = new Bundle();
+ try {
+ JcaX509CertificateHolder holder = new JcaX509CertificateHolder(certificate);
+ X500Name subject = holder.getSubject();
+ try {
+ information.putString("subject_cn", subject.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString());
+ } catch (Exception e) {
+ //ignored
+ }
+ try {
+ information.putString("subject_o", subject.getRDNs(BCStyle.O)[0].getFirst().getValue().toString());
+ } catch (Exception e) {
+ //ignored
+ }
+
+ X500Name issuer = holder.getIssuer();
+ try {
+ information.putString("issuer_cn", issuer.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString());
+ } catch (Exception e) {
+ //ignored
+ }
+ try {
+ information.putString("issuer_o", issuer.getRDNs(BCStyle.O)[0].getFirst().getValue().toString());
+ } catch (Exception e) {
+ //ignored
+ }
+ try {
+ information.putString("sha1", getFingerprintCert(certificate.getEncoded()));
+ } catch (Exception e) {
+
+ }
+ return information;
+ } catch (CertificateEncodingException e) {
+ return information;
+ }
+ }
+
+ public static String getFingerprintCert(byte[] input) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ byte[] fingerprint = md.digest(input);
+ return prettifyFingerprintCert(bytesToHex(fingerprint));
+ }
public static String getAccountFingerprint(Account account) {
try {
@@ -214,16 +214,16 @@ public final class CryptoHelper {
}
}
- public static int encryptionTypeToText(int encryption) {
- switch (encryption) {
- case Message.ENCRYPTION_OTR:
- return R.string.encryption_choice_otr;
- case Message.ENCRYPTION_AXOLOTL:
- return R.string.encryption_choice_omemo;
- case Message.ENCRYPTION_NONE:
- return R.string.encryption_choice_unencrypted;
- default:
- return R.string.encryption_choice_pgp;
- }
- }
+ public static int encryptionTypeToText(int encryption) {
+ switch (encryption) {
+ case Message.ENCRYPTION_OTR:
+ return R.string.encryption_choice_otr;
+ case Message.ENCRYPTION_AXOLOTL:
+ return R.string.encryption_choice_omemo;
+ case Message.ENCRYPTION_NONE:
+ return R.string.encryption_choice_unencrypted;
+ default:
+ return R.string.encryption_choice_pgp;
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/DNSHelper.java b/src/main/java/de/pixart/messenger/utils/DNSHelper.java
index 4a692c910..70f600375 100644
--- a/src/main/java/de/pixart/messenger/utils/DNSHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/DNSHelper.java
@@ -40,260 +40,260 @@ import de.pixart.messenger.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");
+ 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");
- protected static Client client = new Client();
+ protected static Client client = new Client();
- protected static Context context;
+ protected static Context context;
- public static Bundle getSRVRecord(final Jid jid, Context context) throws IOException {
- DNSHelper.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;
- }
+ 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;
+ }
- @TargetApi(21)
- private static List<InetAddress> getDnsServers(Context context) {
- List<InetAddress> servers = new ArrayList<>();
- ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- Network[] networks = connectivityManager == null ? null : connectivityManager.getAllNetworks();
- if (networks == null) {
- return getDnsServersPreLollipop();
- }
- for(int i = 0; i < networks.length; ++i) {
- LinkProperties linkProperties = connectivityManager.getLinkProperties(networks[i]);
- if (linkProperties != null) {
- if (hasDefaultRoute(linkProperties)) {
- servers.addAll(0, getIPv4First(linkProperties.getDnsServers()));
- } else {
- servers.addAll(getIPv4First(linkProperties.getDnsServers()));
- }
- }
- }
- if (servers.size() > 0) {
- Log.d(Config.LOGTAG, "used lollipop variant to discover dns servers in " + networks.length + " networks");
- }
- return servers.size() > 0 ? servers : getDnsServersPreLollipop();
- }
+ @TargetApi(21)
+ private static List<InetAddress> getDnsServers(Context context) {
+ List<InetAddress> servers = new ArrayList<>();
+ ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ Network[] networks = connectivityManager == null ? null : connectivityManager.getAllNetworks();
+ if (networks == null) {
+ return getDnsServersPreLollipop();
+ }
+ for (int i = 0; i < networks.length; ++i) {
+ LinkProperties linkProperties = connectivityManager.getLinkProperties(networks[i]);
+ if (linkProperties != null) {
+ if (hasDefaultRoute(linkProperties)) {
+ servers.addAll(0, getIPv4First(linkProperties.getDnsServers()));
+ } else {
+ servers.addAll(getIPv4First(linkProperties.getDnsServers()));
+ }
+ }
+ }
+ if (servers.size() > 0) {
+ Log.d(Config.LOGTAG, "used lollipop variant to discover dns servers in " + networks.length + " networks");
+ }
+ return servers.size() > 0 ? servers : getDnsServersPreLollipop();
+ }
- private static List<InetAddress> getIPv4First(List<InetAddress> in) {
- List<InetAddress> out = new ArrayList<>();
- for(InetAddress addr : in) {
- if (addr instanceof Inet4Address) {
- out.add(0, addr);
- } else {
- out.add(addr);
- }
- }
- return out;
- }
+ private static List<InetAddress> getIPv4First(List<InetAddress> in) {
+ List<InetAddress> out = new ArrayList<>();
+ for (InetAddress addr : in) {
+ if (addr instanceof Inet4Address) {
+ out.add(0, addr);
+ } else {
+ out.add(addr);
+ }
+ }
+ return out;
+ }
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- private static boolean hasDefaultRoute(LinkProperties linkProperties) {
- for(RouteInfo route: linkProperties.getRoutes()) {
- if (route.isDefaultRoute()) {
- return true;
- }
- }
- return false;
- }
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ private static boolean hasDefaultRoute(LinkProperties linkProperties) {
+ for (RouteInfo route : linkProperties.getRoutes()) {
+ if (route.isDefaultRoute()) {
+ return true;
+ }
+ }
+ return false;
+ }
- private static List<InetAddress> getDnsServersPreLollipop() {
- List<InetAddress> servers = new ArrayList<>();
- String[] dns = client.findDNS();
- for(int i = 0; i < dns.length; ++i) {
- try {
- servers.add(InetAddress.getByName(dns[i]));
- } catch (UnknownHostException e) {
- //ignore
- }
- }
- return servers;
- }
+ private static List<InetAddress> getDnsServersPreLollipop() {
+ List<InetAddress> servers = new ArrayList<>();
+ String[] dns = client.findDNS();
+ for (int i = 0; i < dns.length; ++i) {
+ try {
+ servers.add(InetAddress.getByName(dns[i]));
+ } catch (UnknownHostException e) {
+ //ignore
+ }
+ }
+ return servers;
+ }
- private static class TlsSrv {
- private final SRV srv;
- private final boolean tls;
+ private static class TlsSrv {
+ private final SRV srv;
+ private final boolean tls;
- public TlsSrv(SRV srv, boolean tls) {
- this.srv = srv;
- this.tls = 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();
+ 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();
final String name = rr.getName() != null ? rr.getName().toLowerCase(Locale.US) : null;
if (d instanceof SRV && NameUtil.idnEquals(qname, name)) {
- 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));
+ 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));
} else if (d instanceof SRV) {
- Log.d(Config.LOGTAG,"found unrecognized SRV record with name: "+name);
- }
- if (d instanceof A) {
- A a = (A) d;
- if (!ips4.containsKey(name)) {
- ips4.put(name, new ArrayList<String>());
- }
- ips4.get(name).add(a.toString());
- }
- if (d instanceof AAAA) {
- AAAA aaaa = (AAAA) d;
- if (!ips6.containsKey(name)) {
- ips6.put(name, new ArrayList<String>());
- }
- ips6.get(name).add("[" + aaaa.toString() + "]");
- }
- }
- }
- }
+ Log.d(Config.LOGTAG, "found unrecognized SRV record with name: " + name);
+ }
+ if (d instanceof A) {
+ A a = (A) d;
+ if (!ips4.containsKey(name)) {
+ ips4.put(name, new ArrayList<String>());
+ }
+ ips4.get(name).add(a.toString());
+ }
+ if (d instanceof AAAA) {
+ AAAA aaaa = (AAAA) d;
+ if (!ips6.containsKey(name)) {
+ ips6.put(name, new ArrayList<String>());
+ }
+ ips6.get(name).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.toLowerCase(Locale.US);
- final String tlsQname = "_xmpps-client._tcp." + host.toLowerCase(Locale.US);
- Log.d(Config.LOGTAG, "using dns server: " + dnsServer.getHostAddress() + " to look up " + host);
+ 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.toLowerCase(Locale.US);
+ final String tlsQname = "_xmpps-client._tcp." + host.toLowerCase(Locale.US);
+ 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<>();
+ 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);
+ 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 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;
+ 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;
final String name = srv.getName() != null ? srv.getName().toLowerCase(Locale.US) : null;
- if (ips6.containsKey(name)) {
- values.add(createNamePortBundle(name,srv.getPort(),ips6, tlsSrv.tls));
- } else {
- try {
- DNSMessage response = client.query(name, TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress());
- for (int i = 0; i < response.getAnswers().length; ++i) {
- values.add(createNamePortBundle(name, 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(name)) {
- values.add(createNamePortBundle(name,srv.getPort(),ips4, tlsSrv.tls));
- } else {
- DNSMessage response = client.query(name, TYPE.A, CLASS.IN, dnsServer.getHostAddress());
- for(int i = 0; i < response.getAnswers().length; ++i) {
- values.add(createNamePortBundle(name,srv.getPort(),response.getAnswers()[i].getPayload(), tlsSrv.tls));
- }
- }
- values.add(createNamePortBundle(name, srv.getPort(), tlsSrv.tls));
- }
- bundle.putParcelableArrayList("values", values);
- } catch (SocketTimeoutException e) {
- bundle.putString("error", "timeout");
- } catch (Exception e) {
- bundle.putString("error", "unhandled");
- }
- return bundle;
- }
+ if (ips6.containsKey(name)) {
+ values.add(createNamePortBundle(name, srv.getPort(), ips6, tlsSrv.tls));
+ } else {
+ try {
+ DNSMessage response = client.query(name, TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress());
+ for (int i = 0; i < response.getAnswers().length; ++i) {
+ values.add(createNamePortBundle(name, 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(name)) {
+ values.add(createNamePortBundle(name, srv.getPort(), ips4, tlsSrv.tls));
+ } else {
+ DNSMessage response = client.query(name, TYPE.A, CLASS.IN, dnsServer.getHostAddress());
+ for (int i = 0; i < response.getAnswers().length; ++i) {
+ values.add(createNamePortBundle(name, srv.getPort(), response.getAnswers()[i].getPayload(), tlsSrv.tls));
+ }
+ }
+ values.add(createNamePortBundle(name, 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, 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, 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;
- }
+ 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;
+ }
- public static boolean isIp(final String server) {
- return server != null && (
- PATTERN_IPV4.matcher(server).matches()
- || PATTERN_IPV6.matcher(server).matches()
- || PATTERN_IPV6_6HEX4DEC.matcher(server).matches()
- || PATTERN_IPV6_HEX4DECCOMPRESSED.matcher(server).matches()
- || PATTERN_IPV6_HEXCOMPRESSED.matcher(server).matches());
- }
+ public static boolean isIp(final String server) {
+ return server != null && (
+ PATTERN_IPV4.matcher(server).matches()
+ || PATTERN_IPV6.matcher(server).matches()
+ || PATTERN_IPV6_6HEX4DEC.matcher(server).matches()
+ || PATTERN_IPV6_HEX4DECCOMPRESSED.matcher(server).matches()
+ || PATTERN_IPV6_HEXCOMPRESSED.matcher(server).matches());
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/ExceptionHandler.java b/src/main/java/de/pixart/messenger/utils/ExceptionHandler.java
index 40648dc04..ec38ea754 100644
--- a/src/main/java/de/pixart/messenger/utils/ExceptionHandler.java
+++ b/src/main/java/de/pixart/messenger/utils/ExceptionHandler.java
@@ -9,23 +9,23 @@ import java.lang.Thread.UncaughtExceptionHandler;
public class ExceptionHandler implements UncaughtExceptionHandler {
- private UncaughtExceptionHandler defaultHandler;
- private Context context;
+ private UncaughtExceptionHandler defaultHandler;
+ private Context context;
- public ExceptionHandler(Context context) {
- this.context = context;
- this.defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
- }
+ public ExceptionHandler(Context context) {
+ this.context = context;
+ this.defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+ }
- @Override
- public void uncaughtException(Thread thread, Throwable ex) {
- Writer result = new StringWriter();
- PrintWriter printWriter = new PrintWriter(result);
- ex.printStackTrace(printWriter);
- String stacktrace = result.toString();
- printWriter.close();
- ExceptionHelper.writeToStacktraceFile(context, stacktrace);
- this.defaultHandler.uncaughtException(thread, ex);
- }
+ @Override
+ public void uncaughtException(Thread thread, Throwable ex) {
+ Writer result = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(result);
+ ex.printStackTrace(printWriter);
+ String stacktrace = result.toString();
+ printWriter.close();
+ ExceptionHelper.writeToStacktraceFile(context, stacktrace);
+ this.defaultHandler.uncaughtException(thread, ex);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/ExceptionHelper.java b/src/main/java/de/pixart/messenger/utils/ExceptionHelper.java
index b405f24d4..5cf46c151 100644
--- a/src/main/java/de/pixart/messenger/utils/ExceptionHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/ExceptionHelper.java
@@ -31,107 +31,108 @@ import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
public class ExceptionHelper {
- private static SimpleDateFormat DATE_FORMATs = new SimpleDateFormat("yyyy-MM-dd");
- public static void init(Context context) {
- if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof ExceptionHandler)) {
- Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(
- context));
- }
- }
+ private static SimpleDateFormat DATE_FORMATs = new SimpleDateFormat("yyyy-MM-dd");
- public static boolean checkForCrash(ConversationActivity activity, final XmppConnectionService service) {
- try {
- final SharedPreferences preferences = PreferenceManager
- .getDefaultSharedPreferences(activity);
- boolean crashreport = preferences.getBoolean("crashreport", true);
- if (!crashreport || Config.BUG_REPORTS == null) {
- return false;
- }
- List<Account> accounts = service.getAccounts();
- Account account = null;
- for (int i = 0; i < accounts.size(); ++i) {
- if (!accounts.get(i).isOptionSet(Account.OPTION_DISABLED)) {
- account = accounts.get(i);
- break;
- }
- }
- if (account == null) {
- return false;
- }
- final Account finalAccount = account;
- FileInputStream file = activity.openFileInput("stacktrace.txt");
- InputStreamReader inputStreamReader = new InputStreamReader(file);
- BufferedReader stacktrace = new BufferedReader(inputStreamReader);
- final StringBuilder report = new StringBuilder();
- PackageManager pm = activity.getPackageManager();
- PackageInfo packageInfo;
- try {
- packageInfo = pm.getPackageInfo(activity.getPackageName(), PackageManager.GET_SIGNATURES);
- report.append("Version: " + packageInfo.versionName + '\n');
- report.append("Last Update: " + DATE_FORMATs.format(new Date(packageInfo.lastUpdateTime)) + '\n');
- Signature[] signatures = packageInfo.signatures;
- if (signatures != null && signatures.length >= 1) {
- report.append("SHA-1: " + CryptoHelper.getFingerprintCert(packageInfo.signatures[0].toByteArray()) + "\n");
- }
- report.append('\n');
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- String line;
- while ((line = stacktrace.readLine()) != null) {
- report.append(line);
- report.append('\n');
- }
- file.close();
- activity.deleteFile("stacktrace.txt");
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
- builder.setTitle(activity.getString(R.string.crash_report_title));
- builder.setMessage(activity.getText(R.string.crash_report_message));
- builder.setPositiveButton(activity.getText(R.string.send_now),
- new OnClickListener() {
+ public static void init(Context context) {
+ if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof ExceptionHandler)) {
+ Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(
+ context));
+ }
+ }
- @Override
- public void onClick(DialogInterface dialog, int which) {
+ public static boolean checkForCrash(ConversationActivity activity, final XmppConnectionService service) {
+ try {
+ final SharedPreferences preferences = PreferenceManager
+ .getDefaultSharedPreferences(activity);
+ boolean crashreport = preferences.getBoolean("crashreport", true);
+ if (!crashreport || Config.BUG_REPORTS == null) {
+ return false;
+ }
+ List<Account> accounts = service.getAccounts();
+ Account account = null;
+ for (int i = 0; i < accounts.size(); ++i) {
+ if (!accounts.get(i).isOptionSet(Account.OPTION_DISABLED)) {
+ account = accounts.get(i);
+ break;
+ }
+ }
+ if (account == null) {
+ return false;
+ }
+ final Account finalAccount = account;
+ FileInputStream file = activity.openFileInput("stacktrace.txt");
+ InputStreamReader inputStreamReader = new InputStreamReader(file);
+ BufferedReader stacktrace = new BufferedReader(inputStreamReader);
+ final StringBuilder report = new StringBuilder();
+ PackageManager pm = activity.getPackageManager();
+ PackageInfo packageInfo;
+ try {
+ packageInfo = pm.getPackageInfo(activity.getPackageName(), PackageManager.GET_SIGNATURES);
+ report.append("Version: " + packageInfo.versionName + '\n');
+ report.append("Last Update: " + DATE_FORMATs.format(new Date(packageInfo.lastUpdateTime)) + '\n');
+ Signature[] signatures = packageInfo.signatures;
+ if (signatures != null && signatures.length >= 1) {
+ report.append("SHA-1: " + CryptoHelper.getFingerprintCert(packageInfo.signatures[0].toByteArray()) + "\n");
+ }
+ report.append('\n');
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ String line;
+ while ((line = stacktrace.readLine()) != null) {
+ report.append(line);
+ report.append('\n');
+ }
+ file.close();
+ activity.deleteFile("stacktrace.txt");
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setTitle(activity.getString(R.string.crash_report_title));
+ builder.setMessage(activity.getText(R.string.crash_report_message));
+ builder.setPositiveButton(activity.getText(R.string.send_now),
+ new OnClickListener() {
- Log.d(Config.LOGTAG, "using account="
- + finalAccount.getJid().toBareJid()
- + " to send in stack trace");
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
- Conversation conversation = null;
- try {
- conversation = service.findOrCreateConversation(finalAccount,
- Jid.fromString(Config.BUG_REPORTS), false);
- } catch (final InvalidJidException ignored) {
- }
- Message message = new Message(conversation, report
- .toString(), Message.ENCRYPTION_NONE);
- service.sendMessage(message);
- }
- });
- builder.setNegativeButton(activity.getText(R.string.send_never),
- new OnClickListener() {
+ Log.d(Config.LOGTAG, "using account="
+ + finalAccount.getJid().toBareJid()
+ + " to send in stack trace");
- @Override
- public void onClick(DialogInterface dialog, int which) {
- preferences.edit().putBoolean("crash_report", false)
- .apply();
- }
- });
- builder.create().show();
- return true;
- } catch (final IOException ignored) {
- return false;
- }
- }
+ Conversation conversation = null;
+ try {
+ conversation = service.findOrCreateConversation(finalAccount,
+ Jid.fromString(Config.BUG_REPORTS), false);
+ } catch (final InvalidJidException ignored) {
+ }
+ Message message = new Message(conversation, report
+ .toString(), Message.ENCRYPTION_NONE);
+ service.sendMessage(message);
+ }
+ });
+ builder.setNegativeButton(activity.getText(R.string.send_never),
+ new OnClickListener() {
- public static void writeToStacktraceFile(Context context, String msg) {
- try {
- OutputStream os = context.openFileOutput("stacktrace.txt", Context.MODE_PRIVATE);
- os.write(msg.getBytes());
- os.flush();
- os.close();
- } catch (IOException ignored) {
- }
- }
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ preferences.edit().putBoolean("crash_report", false)
+ .apply();
+ }
+ });
+ builder.create().show();
+ return true;
+ } catch (final IOException ignored) {
+ return false;
+ }
+ }
+
+ public static void writeToStacktraceFile(Context context, String msg) {
+ try {
+ OutputStream os = context.openFileOutput("stacktrace.txt", Context.MODE_PRIVATE);
+ os.write(msg.getBytes());
+ os.flush();
+ os.close();
+ } catch (IOException ignored) {
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/ExifHelper.java b/src/main/java/de/pixart/messenger/utils/ExifHelper.java
index 0ac4d6e4d..230af54d1 100644
--- a/src/main/java/de/pixart/messenger/utils/ExifHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/ExifHelper.java
@@ -66,7 +66,7 @@ public class ExifHelper {
if (!read(is, buf, 6)) return 0;
length -= 6;
if (pack(buf, 0, 4, false) == 0x45786966 &&
- pack(buf, 4, 2, false) == 0) {
+ pack(buf, 4, 2, false) == 0) {
break;
}
}
@@ -136,7 +136,7 @@ public class ExifHelper {
}
private static int pack(byte[] bytes, int offset, int length,
- boolean littleEndian) {
+ boolean littleEndian) {
int step = 1;
if (littleEndian) {
offset += length - 1;
diff --git a/src/main/java/de/pixart/messenger/utils/FileUtils.java b/src/main/java/de/pixart/messenger/utils/FileUtils.java
index fa69de49b..1500b6246 100644
--- a/src/main/java/de/pixart/messenger/utils/FileUtils.java
+++ b/src/main/java/de/pixart/messenger/utils/FileUtils.java
@@ -14,145 +14,145 @@ import java.io.File;
public 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) {
- if (uri == null) {
- return null;
- }
-
- final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
-
- // DocumentProvider
- if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
- // ExternalStorageProvider
- if (isExternalStorageDocument(uri)) {
- final String docId = DocumentsContract.getDocumentId(uri);
- final String[] split = docId.split(":");
- final String type = split[0];
-
- if ("primary".equalsIgnoreCase(type)) {
- return Environment.getExternalStorageDirectory() + "/" + split[1];
- }
-
- // TODO handle non-primary volumes
- }
- // DownloadsProvider
- else if (isDownloadsDocument(uri)) {
-
- final String id = DocumentsContract.getDocumentId(uri);
- final Uri contentUri = ContentUris.withAppendedId(
- Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
-
- return getDataColumn(context, contentUri, null, null);
- }
- // MediaProvider
- else if (isMediaDocument(uri)) {
- final String docId = DocumentsContract.getDocumentId(uri);
- final String[] split = docId.split(":");
- final String type = split[0];
-
- Uri contentUri = null;
- if ("image".equals(type)) {
- contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
- } else if ("video".equals(type)) {
- contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
- } else if ("audio".equals(type)) {
- contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
- }
-
- final String selection = "_id=?";
- final String[] selectionArgs = new String[]{
- split[1]
- };
-
- return getDataColumn(context, contentUri, selection, selectionArgs);
- }
- }
- // MediaStore (and general)
- else if ("content".equalsIgnoreCase(uri.getScheme())) {
- String path = getDataColumn(context, uri, null, null);
- if (path != null) {
- File file = new File(path);
- if (!file.canRead()) {
- return null;
- }
- }
- return path;
- }
- // File
- else if ("file".equalsIgnoreCase(uri.getScheme())) {
- return uri.getPath();
- }
-
- return null;
- }
-
- /**
- * Get the value of the data column for this Uri. This is useful for
- * MediaStore Uris, and other file-based ContentProviders.
- *
- * @param context The context.
- * @param uri The Uri to query.
- * @param selection (Optional) Filter used in the query.
- * @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,
- String[] selectionArgs) {
-
- Cursor cursor = null;
- final String column = "_data";
- final String[] projection = {
- column
- };
-
- try {
- cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,null);
- if (cursor != null && cursor.moveToFirst()) {
- final int column_index = cursor.getColumnIndexOrThrow(column);
- return cursor.getString(column_index);
- }
- } catch(Exception e) {
- return null;
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- return null;
- }
-
-
- /**
- * @param uri The Uri to check.
- * @return Whether the Uri authority is ExternalStorageProvider.
- */
- public static boolean isExternalStorageDocument(Uri uri) {
- return "com.android.externalstorage.documents".equals(uri.getAuthority());
- }
-
- /**
- * @param uri The Uri to check.
- * @return Whether the Uri authority is DownloadsProvider.
- */
- public static boolean isDownloadsDocument(Uri uri) {
- return "com.android.providers.downloads.documents".equals(uri.getAuthority());
- }
-
- /**
- * @param uri The Uri to check.
- * @return Whether the Uri authority is MediaProvider.
- */
- public static boolean isMediaDocument(Uri uri) {
- return "com.android.providers.media.documents".equals(uri.getAuthority());
- }
+ /**
+ * 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) {
+ if (uri == null) {
+ return null;
+ }
+
+ final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
+
+ // DocumentProvider
+ if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
+ // ExternalStorageProvider
+ if (isExternalStorageDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+
+ if ("primary".equalsIgnoreCase(type)) {
+ return Environment.getExternalStorageDirectory() + "/" + split[1];
+ }
+
+ // TODO handle non-primary volumes
+ }
+ // DownloadsProvider
+ else if (isDownloadsDocument(uri)) {
+
+ final String id = DocumentsContract.getDocumentId(uri);
+ final Uri contentUri = ContentUris.withAppendedId(
+ Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
+
+ return getDataColumn(context, contentUri, null, null);
+ }
+ // MediaProvider
+ else if (isMediaDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+
+ Uri contentUri = null;
+ if ("image".equals(type)) {
+ contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ } else if ("video".equals(type)) {
+ contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+ } else if ("audio".equals(type)) {
+ contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+ }
+
+ final String selection = "_id=?";
+ final String[] selectionArgs = new String[]{
+ split[1]
+ };
+
+ return getDataColumn(context, contentUri, selection, selectionArgs);
+ }
+ }
+ // MediaStore (and general)
+ else if ("content".equalsIgnoreCase(uri.getScheme())) {
+ String path = getDataColumn(context, uri, null, null);
+ if (path != null) {
+ File file = new File(path);
+ if (!file.canRead()) {
+ return null;
+ }
+ }
+ return path;
+ }
+ // File
+ else if ("file".equalsIgnoreCase(uri.getScheme())) {
+ return uri.getPath();
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the value of the data column for this Uri. This is useful for
+ * MediaStore Uris, and other file-based ContentProviders.
+ *
+ * @param context The context.
+ * @param uri The Uri to query.
+ * @param selection (Optional) Filter used in the query.
+ * @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,
+ String[] selectionArgs) {
+
+ Cursor cursor = null;
+ final String column = "_data";
+ final String[] projection = {
+ column
+ };
+
+ try {
+ cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ final int column_index = cursor.getColumnIndexOrThrow(column);
+ return cursor.getString(column_index);
+ }
+ } catch (Exception e) {
+ return null;
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is ExternalStorageProvider.
+ */
+ public static boolean isExternalStorageDocument(Uri uri) {
+ return "com.android.externalstorage.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is DownloadsProvider.
+ */
+ public static boolean isDownloadsDocument(Uri uri) {
+ return "com.android.providers.downloads.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is MediaProvider.
+ */
+ public static boolean isMediaDocument(Uri uri) {
+ return "com.android.providers.media.documents".equals(uri.getAuthority());
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/GeoHelper.java b/src/main/java/de/pixart/messenger/utils/GeoHelper.java
index 0fe63e095..8f263c593 100644
--- a/src/main/java/de/pixart/messenger/utils/GeoHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/GeoHelper.java
@@ -14,13 +14,13 @@ import de.pixart.messenger.entities.Conversation;
import de.pixart.messenger.entities.Message;
public class GeoHelper {
- public static Pattern GEO_URI = Pattern.compile("geo:([\\-0-9.]+),([\\-0-9.]+)(?:,([\\-0-9.]+))?(?:\\?(.*))?", Pattern.CASE_INSENSITIVE);
+ public static Pattern GEO_URI = Pattern.compile("geo:([\\-0-9.]+),([\\-0-9.]+)(?:,([\\-0-9.]+))?(?:\\?(.*))?", Pattern.CASE_INSENSITIVE);
- public static boolean isGeoUri(String body) {
- return body != null && GEO_URI.matcher(body).matches();
- }
+ public static boolean isGeoUri(String body) {
+ return body != null && GEO_URI.matcher(body).matches();
+ }
- public static String MapPreviewUri (Message message) {
+ public static String MapPreviewUri(Message message) {
Matcher matcher = GEO_URI.matcher(message.getBody());
if (!matcher.matches()) {
return null;
@@ -42,62 +42,62 @@ public class GeoHelper {
return "https://maps.google.com/maps/api/staticmap?center=" + latitude + "," + longitude + "&size=500x500&scale=2&format=jpg&markers=" + latitude + "," + longitude + "&sensor=false";
}
- public static ArrayList<Intent> createGeoIntentsFromMessage(Message message) {
- final ArrayList<Intent> intents = new ArrayList<>();
- Matcher matcher = GEO_URI.matcher(message.getBody());
- if (!matcher.matches()) {
- return intents;
- }
- double latitude;
- double longitude;
- try {
- latitude = Double.parseDouble(matcher.group(1));
- if (latitude > 90.0 || latitude < -90.0) {
- return intents;
- }
- longitude = Double.parseDouble(matcher.group(2));
- if (longitude > 180.0 || longitude < -180.0) {
- return intents;
- }
- } catch (NumberFormatException nfe) {
- return intents;
- }
- final Conversation conversation = message.getConversation();
- String label;
- if (conversation.getMode() == Conversation.MODE_SINGLE && message.getStatus() == Message.STATUS_RECEIVED) {
- try {
- label = "(" + URLEncoder.encode(message.getConversation().getName(), "UTF-8") + ")";
- } catch (UnsupportedEncodingException e) {
- label = "";
- }
- } else {
- label = "";
- }
+ public static ArrayList<Intent> createGeoIntentsFromMessage(Message message) {
+ final ArrayList<Intent> intents = new ArrayList<>();
+ Matcher matcher = GEO_URI.matcher(message.getBody());
+ if (!matcher.matches()) {
+ return intents;
+ }
+ double latitude;
+ double longitude;
+ try {
+ latitude = Double.parseDouble(matcher.group(1));
+ if (latitude > 90.0 || latitude < -90.0) {
+ return intents;
+ }
+ longitude = Double.parseDouble(matcher.group(2));
+ if (longitude > 180.0 || longitude < -180.0) {
+ return intents;
+ }
+ } catch (NumberFormatException nfe) {
+ return intents;
+ }
+ final Conversation conversation = message.getConversation();
+ String label;
+ if (conversation.getMode() == Conversation.MODE_SINGLE && message.getStatus() == Message.STATUS_RECEIVED) {
+ try {
+ label = "(" + URLEncoder.encode(message.getConversation().getName(), "UTF-8") + ")";
+ } catch (UnsupportedEncodingException e) {
+ label = "";
+ }
+ } else {
+ label = "";
+ }
- Intent locationPluginIntent = new Intent("de.pixart.messenger.location.show");
- locationPluginIntent.putExtra("latitude",latitude);
- locationPluginIntent.putExtra("longitude",longitude);
- if (message.getStatus() != Message.STATUS_RECEIVED) {
- locationPluginIntent.putExtra("jid",conversation.getAccount().getJid().toString());
- locationPluginIntent.putExtra("name",conversation.getAccount().getJid().getLocalpart());
- } else {
- Contact contact = message.getContact();
- if (contact != null) {
- locationPluginIntent.putExtra("name", contact.getDisplayName());
- locationPluginIntent.putExtra("jid", contact.getJid().toString());
- } else {
- locationPluginIntent.putExtra("name", UIHelper.getDisplayedMucCounterpart(message.getCounterpart()));
- }
- }
- intents.add(locationPluginIntent);
+ Intent locationPluginIntent = new Intent("de.pixart.messenger.location.show");
+ locationPluginIntent.putExtra("latitude", latitude);
+ locationPluginIntent.putExtra("longitude", longitude);
+ if (message.getStatus() != Message.STATUS_RECEIVED) {
+ locationPluginIntent.putExtra("jid", conversation.getAccount().getJid().toString());
+ locationPluginIntent.putExtra("name", conversation.getAccount().getJid().getLocalpart());
+ } else {
+ Contact contact = message.getContact();
+ if (contact != null) {
+ locationPluginIntent.putExtra("name", contact.getDisplayName());
+ locationPluginIntent.putExtra("jid", contact.getJid().toString());
+ } else {
+ locationPluginIntent.putExtra("name", UIHelper.getDisplayedMucCounterpart(message.getCounterpart()));
+ }
+ }
+ intents.add(locationPluginIntent);
- Intent geoIntent = new Intent(Intent.ACTION_VIEW);
- geoIntent.setData(Uri.parse("geo:" + String.valueOf(latitude) + "," + String.valueOf(longitude) + "?q=" + String.valueOf(latitude) + "," + String.valueOf(longitude) + label));
- intents.add(geoIntent);
+ Intent geoIntent = new Intent(Intent.ACTION_VIEW);
+ geoIntent.setData(Uri.parse("geo:" + String.valueOf(latitude) + "," + String.valueOf(longitude) + "?q=" + String.valueOf(latitude) + "," + String.valueOf(longitude) + label));
+ intents.add(geoIntent);
- Intent httpIntent = new Intent(Intent.ACTION_VIEW);
- httpIntent.setData(Uri.parse("https://maps.google.com/maps?q=loc:"+String.valueOf(latitude) + "," + String.valueOf(longitude) +label));
- intents.add(httpIntent);
- return intents;
- }
+ Intent httpIntent = new Intent(Intent.ACTION_VIEW);
+ httpIntent.setData(Uri.parse("https://maps.google.com/maps?q=loc:" + String.valueOf(latitude) + "," + String.valueOf(longitude) + label));
+ intents.add(httpIntent);
+ return intents;
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/utils/MimeUtils.java b/src/main/java/de/pixart/messenger/utils/MimeUtils.java
index f90822c2e..366ed308b 100644
--- a/src/main/java/de/pixart/messenger/utils/MimeUtils.java
+++ b/src/main/java/de/pixart/messenger/utils/MimeUtils.java
@@ -14,6 +14,7 @@
* limitations under the License.
*/
package de.pixart.messenger.utils;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -21,6 +22,7 @@ import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
+
/**
* Utilities for dealing with MIME types.
* Used to implement java.net.URLConnection and android.webkit.MimeTypeMap.
@@ -28,6 +30,7 @@ import java.util.Properties;
public final class MimeUtils {
private static final Map<String, String> mimeTypeToExtensionMap = new HashMap<String, String>();
private static final Map<String, String> extensionToMimeTypeMap = new HashMap<String, String>();
+
static {
// The following table is based on /etc/mime.types data minus
// chemical/* MIME types and MIME types that don't map to any
@@ -373,6 +376,7 @@ public final class MimeUtils {
add("x-epoc/x-sisx-app", "sisx");
applyOverrides();
}
+
private static void add(String mimeType, String extension) {
// If we have an existing x -> y mapping, we do not want to
// override it with another mapping x -> y2.
@@ -386,6 +390,7 @@ public final class MimeUtils {
extensionToMimeTypeMap.put(extension, mimeType);
}
}
+
private static InputStream getContentTypesPropertiesStream() {
// User override?
String userTable = System.getProperty("content.types.user.table");
@@ -408,6 +413,7 @@ public final class MimeUtils {
}
return null;
}
+
/**
* This isn't what the RI does. The RI doesn't have hard-coded defaults, so supplying your
* own "content.types.user.table" means you don't get any of the built-ins, and the built-ins
@@ -436,10 +442,13 @@ public final class MimeUtils {
} catch (IOException ignored) {
}
}
+
private MimeUtils() {
}
+
/**
* Returns true if the given MIME type has an entry in the map.
+ *
* @param mimeType A MIME type (i.e. text/plain)
* @return True iff there is a mimeType entry in the map.
*/
@@ -449,8 +458,10 @@ public final class MimeUtils {
}
return mimeTypeToExtensionMap.containsKey(mimeType);
}
+
/**
* Returns the MIME type for the given extension.
+ *
* @param extension A file extension without the leading '.'
* @return The MIME type for the given extension or null iff there is none.
*/
@@ -460,8 +471,10 @@ public final class MimeUtils {
}
return extensionToMimeTypeMap.get(extension.toLowerCase());
}
+
/**
* Returns true if the given extension has a registered MIME type.
+ *
* @param extension A file extension without the leading '.'
* @return True iff there is an extension entry in the map.
*/
@@ -471,10 +484,12 @@ public final class MimeUtils {
}
return extensionToMimeTypeMap.containsKey(extension);
}
+
/**
* Returns the registered extension for the given MIME type. Note that some
* MIME types map to multiple extensions. This call will return the most
* common extension for the given MIME type.
+ *
* @param mimeType A MIME type (i.e. text/plain)
* @return The extension for the given MIME type or null iff there is none.
*/
diff --git a/src/main/java/de/pixart/messenger/utils/OnPhoneContactsLoadedListener.java b/src/main/java/de/pixart/messenger/utils/OnPhoneContactsLoadedListener.java
index 049154745..f19258dda 100644
--- a/src/main/java/de/pixart/messenger/utils/OnPhoneContactsLoadedListener.java
+++ b/src/main/java/de/pixart/messenger/utils/OnPhoneContactsLoadedListener.java
@@ -5,5 +5,5 @@ import android.os.Bundle;
import java.util.List;
public interface OnPhoneContactsLoadedListener {
- public void onPhoneContactsLoaded(List<Bundle> phoneContacts);
+ public void onPhoneContactsLoaded(List<Bundle> phoneContacts);
}
diff --git a/src/main/java/de/pixart/messenger/utils/PRNGFixes.java b/src/main/java/de/pixart/messenger/utils/PRNGFixes.java
index be456a30c..211ebca8e 100644
--- a/src/main/java/de/pixart/messenger/utils/PRNGFixes.java
+++ b/src/main/java/de/pixart/messenger/utils/PRNGFixes.java
@@ -21,145 +21,144 @@ import java.security.Security;
/**
* Fixes for the output of the default PRNG having low entropy.
- *
+ * <p>
* The fixes need to be applied via {@link #apply()} before any use of Java
* Cryptography Architecture primitives. A good place to invoke them is in the
* application's {@code onCreate}.
*/
public final class PRNGFixes {
- private static final int VERSION_CODE_JELLY_BEAN = 16;
- private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18;
- private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL = getBuildFingerprintAndDeviceSerial();
+ private static final int VERSION_CODE_JELLY_BEAN = 16;
+ private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18;
+ private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL = getBuildFingerprintAndDeviceSerial();
- /** Hidden constructor to prevent instantiation. */
- private PRNGFixes() {
- }
+ /**
+ * Hidden constructor to prevent instantiation.
+ */
+ private PRNGFixes() {
+ }
- /**
- * Applies all fixes.
- *
- * @throws SecurityException
- * if a fix is needed but could not be applied.
- */
- public static void apply() {
- applyOpenSSLFix();
- installLinuxPRNGSecureRandom();
- }
+ /**
+ * Applies all fixes.
+ *
+ * @throws SecurityException if a fix is needed but could not be applied.
+ */
+ public static void apply() {
+ applyOpenSSLFix();
+ installLinuxPRNGSecureRandom();
+ }
- /**
- * Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the
- * fix is not needed.
- *
- * @throws SecurityException
- * if the fix is needed but could not be applied.
- */
- private static void applyOpenSSLFix() throws SecurityException {
- if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN)
- || (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) {
- // No need to apply the fix
- return;
- }
+ /**
+ * Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the
+ * fix is not needed.
+ *
+ * @throws SecurityException if the fix is needed but could not be applied.
+ */
+ private static void applyOpenSSLFix() throws SecurityException {
+ if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN)
+ || (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) {
+ // No need to apply the fix
+ return;
+ }
- try {
- // Mix in the device- and invocation-specific seed.
- Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
- .getMethod("RAND_seed", byte[].class)
- .invoke(null, generateSeed());
+ try {
+ // Mix in the device- and invocation-specific seed.
+ Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
+ .getMethod("RAND_seed", byte[].class)
+ .invoke(null, generateSeed());
- // Mix output of Linux PRNG into OpenSSL's PRNG
- int bytesRead = (Integer) Class
- .forName(
- "org.apache.harmony.xnet.provider.jsse.NativeCrypto")
- .getMethod("RAND_load_file", String.class, long.class)
- .invoke(null, "/dev/urandom", 1024);
- if (bytesRead != 1024) {
- throw new IOException(
- "Unexpected number of bytes read from Linux PRNG: "
- + bytesRead);
- }
- } catch (Exception e) {
- throw new SecurityException("Failed to seed OpenSSL PRNG", e);
- }
- }
+ // Mix output of Linux PRNG into OpenSSL's PRNG
+ int bytesRead = (Integer) Class
+ .forName(
+ "org.apache.harmony.xnet.provider.jsse.NativeCrypto")
+ .getMethod("RAND_load_file", String.class, long.class)
+ .invoke(null, "/dev/urandom", 1024);
+ if (bytesRead != 1024) {
+ throw new IOException(
+ "Unexpected number of bytes read from Linux PRNG: "
+ + bytesRead);
+ }
+ } catch (Exception e) {
+ throw new SecurityException("Failed to seed OpenSSL PRNG", e);
+ }
+ }
- /**
- * Installs a Linux PRNG-backed {@code SecureRandom} implementation as the
- * default. Does nothing if the implementation is already the default or if
- * there is not need to install the implementation.
- *
- * @throws SecurityException
- * if the fix is needed but could not be applied.
- */
- private static void installLinuxPRNGSecureRandom() throws SecurityException {
- if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) {
- // No need to apply the fix
- return;
- }
+ /**
+ * Installs a Linux PRNG-backed {@code SecureRandom} implementation as the
+ * default. Does nothing if the implementation is already the default or if
+ * there is not need to install the implementation.
+ *
+ * @throws SecurityException if the fix is needed but could not be applied.
+ */
+ private static void installLinuxPRNGSecureRandom() throws SecurityException {
+ if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) {
+ // No need to apply the fix
+ return;
+ }
- // Install a Linux PRNG-based SecureRandom implementation as the
- // default, if not yet installed.
- Provider[] secureRandomProviders = Security
- .getProviders("SecureRandom.SHA1PRNG");
- if ((secureRandomProviders == null)
- || (secureRandomProviders.length < 1)
- || (!LinuxPRNGSecureRandomProvider.class
- .equals(secureRandomProviders[0].getClass()))) {
- Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
- }
+ // Install a Linux PRNG-based SecureRandom implementation as the
+ // default, if not yet installed.
+ Provider[] secureRandomProviders = Security
+ .getProviders("SecureRandom.SHA1PRNG");
+ if ((secureRandomProviders == null)
+ || (secureRandomProviders.length < 1)
+ || (!LinuxPRNGSecureRandomProvider.class
+ .equals(secureRandomProviders[0].getClass()))) {
+ Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
+ }
- // Assert that new SecureRandom() and
- // SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed
- // by the Linux PRNG-based SecureRandom implementation.
- SecureRandom rng1 = new SecureRandom();
- if (!LinuxPRNGSecureRandomProvider.class.equals(rng1.getProvider()
- .getClass())) {
- throw new SecurityException(
- "new SecureRandom() backed by wrong Provider: "
- + rng1.getProvider().getClass());
- }
+ // Assert that new SecureRandom() and
+ // SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed
+ // by the Linux PRNG-based SecureRandom implementation.
+ SecureRandom rng1 = new SecureRandom();
+ if (!LinuxPRNGSecureRandomProvider.class.equals(rng1.getProvider()
+ .getClass())) {
+ throw new SecurityException(
+ "new SecureRandom() backed by wrong Provider: "
+ + rng1.getProvider().getClass());
+ }
- SecureRandom rng2;
- try {
- rng2 = SecureRandom.getInstance("SHA1PRNG");
- } catch (NoSuchAlgorithmException e) {
- throw new SecurityException("SHA1PRNG not available", e);
- }
- if (!LinuxPRNGSecureRandomProvider.class.equals(rng2.getProvider()
- .getClass())) {
- throw new SecurityException(
- "SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
- + " Provider: " + rng2.getProvider().getClass());
- }
- }
+ SecureRandom rng2;
+ try {
+ rng2 = SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ throw new SecurityException("SHA1PRNG not available", e);
+ }
+ if (!LinuxPRNGSecureRandomProvider.class.equals(rng2.getProvider()
+ .getClass())) {
+ throw new SecurityException(
+ "SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
+ + " Provider: " + rng2.getProvider().getClass());
+ }
+ }
- /**
- * {@code Provider} of {@code SecureRandom} engines which pass through all
- * requests to the Linux PRNG.
- */
- private static class LinuxPRNGSecureRandomProvider extends Provider {
+ /**
+ * {@code Provider} of {@code SecureRandom} engines which pass through all
+ * requests to the Linux PRNG.
+ */
+ private static class LinuxPRNGSecureRandomProvider extends Provider {
- public LinuxPRNGSecureRandomProvider() {
- super("LinuxPRNG", 1.0,
- "A Linux-specific random number provider that uses"
- + " /dev/urandom");
- // Although /dev/urandom is not a SHA-1 PRNG, some apps
- // explicitly request a SHA1PRNG SecureRandom and we thus need to
- // prevent them from getting the default implementation whose output
- // may have low entropy.
- put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName());
- put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
- }
- }
+ public LinuxPRNGSecureRandomProvider() {
+ super("LinuxPRNG", 1.0,
+ "A Linux-specific random number provider that uses"
+ + " /dev/urandom");
+ // Although /dev/urandom is not a SHA-1 PRNG, some apps
+ // explicitly request a SHA1PRNG SecureRandom and we thus need to
+ // prevent them from getting the default implementation whose output
+ // may have low entropy.
+ put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName());
+ put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
+ }
+ }
- /**
- * {@link SecureRandomSpi} which passes all requests to the Linux PRNG (
- * {@code /dev/urandom}).
- */
- public static class LinuxPRNGSecureRandom extends SecureRandomSpi {
+ /**
+ * {@link SecureRandomSpi} which passes all requests to the Linux PRNG (
+ * {@code /dev/urandom}).
+ */
+ public static class LinuxPRNGSecureRandom extends SecureRandomSpi {
/*
- * IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed
+ * IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed
* are passed through to the Linux PRNG (/dev/urandom). Instances of
* this class seed themselves by mixing in the current time, PID, UID,
* build fingerprint, and hardware serial number (where available) into
@@ -170,158 +169,158 @@ public final class PRNGFixes {
* duplicated PRNG output.
*/
- private static final File URANDOM_FILE = new File("/dev/urandom");
+ private static final File URANDOM_FILE = new File("/dev/urandom");
- private static final Object sLock = new Object();
+ private static final Object sLock = new Object();
- /**
- * Input stream for reading from Linux PRNG or {@code null} if not yet
- * opened.
- *
- * @GuardedBy("sLock")
- */
- private static DataInputStream sUrandomIn;
+ /**
+ * Input stream for reading from Linux PRNG or {@code null} if not yet
+ * opened.
+ *
+ * @GuardedBy("sLock")
+ */
+ private static DataInputStream sUrandomIn;
- /**
- * Output stream for writing to Linux PRNG or {@code null} if not yet
- * opened.
- *
- * @GuardedBy("sLock")
- */
- private static OutputStream sUrandomOut;
+ /**
+ * Output stream for writing to Linux PRNG or {@code null} if not yet
+ * opened.
+ *
+ * @GuardedBy("sLock")
+ */
+ private static OutputStream sUrandomOut;
- /**
- * Whether this engine instance has been seeded. This is needed because
- * each instance needs to seed itself if the client does not explicitly
- * seed it.
- */
- private boolean mSeeded;
+ /**
+ * Whether this engine instance has been seeded. This is needed because
+ * each instance needs to seed itself if the client does not explicitly
+ * seed it.
+ */
+ private boolean mSeeded;
- @Override
- protected void engineSetSeed(byte[] bytes) {
- try {
- OutputStream out;
- synchronized (sLock) {
- out = getUrandomOutputStream();
- }
- out.write(bytes);
- out.flush();
- } catch (IOException e) {
- // On a small fraction of devices /dev/urandom is not writable.
- // Log and ignore.
- Log.w(PRNGFixes.class.getSimpleName(),
- "Failed to mix seed into " + URANDOM_FILE);
- } finally {
- mSeeded = true;
- }
- }
+ @Override
+ protected void engineSetSeed(byte[] bytes) {
+ try {
+ OutputStream out;
+ synchronized (sLock) {
+ out = getUrandomOutputStream();
+ }
+ out.write(bytes);
+ out.flush();
+ } catch (IOException e) {
+ // On a small fraction of devices /dev/urandom is not writable.
+ // Log and ignore.
+ Log.w(PRNGFixes.class.getSimpleName(),
+ "Failed to mix seed into " + URANDOM_FILE);
+ } finally {
+ mSeeded = true;
+ }
+ }
- @Override
- protected void engineNextBytes(byte[] bytes) {
- if (!mSeeded) {
- // Mix in the device- and invocation-specific seed.
- engineSetSeed(generateSeed());
- }
+ @Override
+ protected void engineNextBytes(byte[] bytes) {
+ if (!mSeeded) {
+ // Mix in the device- and invocation-specific seed.
+ engineSetSeed(generateSeed());
+ }
- try {
- DataInputStream in;
- synchronized (sLock) {
- in = getUrandomInputStream();
- }
- synchronized (in) {
- in.readFully(bytes);
- }
- } catch (IOException e) {
- throw new SecurityException("Failed to read from "
- + URANDOM_FILE, e);
- }
- }
+ try {
+ DataInputStream in;
+ synchronized (sLock) {
+ in = getUrandomInputStream();
+ }
+ synchronized (in) {
+ in.readFully(bytes);
+ }
+ } catch (IOException e) {
+ throw new SecurityException("Failed to read from "
+ + URANDOM_FILE, e);
+ }
+ }
- @Override
- protected byte[] engineGenerateSeed(int size) {
- byte[] seed = new byte[size];
- engineNextBytes(seed);
- return seed;
- }
+ @Override
+ protected byte[] engineGenerateSeed(int size) {
+ byte[] seed = new byte[size];
+ engineNextBytes(seed);
+ return seed;
+ }
- private DataInputStream getUrandomInputStream() {
- synchronized (sLock) {
- if (sUrandomIn == null) {
- // NOTE: Consider inserting a BufferedInputStream between
- // DataInputStream and FileInputStream if you need higher
- // PRNG output performance and can live with future PRNG
- // output being pulled into this process prematurely.
- try {
- sUrandomIn = new DataInputStream(new FileInputStream(
- URANDOM_FILE));
- } catch (IOException e) {
- throw new SecurityException("Failed to open "
- + URANDOM_FILE + " for reading", e);
- }
- }
- return sUrandomIn;
- }
- }
+ private DataInputStream getUrandomInputStream() {
+ synchronized (sLock) {
+ if (sUrandomIn == null) {
+ // NOTE: Consider inserting a BufferedInputStream between
+ // DataInputStream and FileInputStream if you need higher
+ // PRNG output performance and can live with future PRNG
+ // output being pulled into this process prematurely.
+ try {
+ sUrandomIn = new DataInputStream(new FileInputStream(
+ URANDOM_FILE));
+ } catch (IOException e) {
+ throw new SecurityException("Failed to open "
+ + URANDOM_FILE + " for reading", e);
+ }
+ }
+ return sUrandomIn;
+ }
+ }
- private OutputStream getUrandomOutputStream() throws IOException {
- synchronized (sLock) {
- if (sUrandomOut == null) {
- sUrandomOut = new FileOutputStream(URANDOM_FILE);
- }
- return sUrandomOut;
- }
- }
- }
+ private OutputStream getUrandomOutputStream() throws IOException {
+ synchronized (sLock) {
+ if (sUrandomOut == null) {
+ sUrandomOut = new FileOutputStream(URANDOM_FILE);
+ }
+ return sUrandomOut;
+ }
+ }
+ }
- /**
- * Generates a device- and invocation-specific seed to be mixed into the
- * Linux PRNG.
- */
- private static byte[] generateSeed() {
- try {
- ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
- DataOutputStream seedBufferOut = new DataOutputStream(seedBuffer);
- seedBufferOut.writeLong(System.currentTimeMillis());
- seedBufferOut.writeLong(System.nanoTime());
- seedBufferOut.writeInt(Process.myPid());
- seedBufferOut.writeInt(Process.myUid());
- seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL);
- seedBufferOut.close();
- return seedBuffer.toByteArray();
- } catch (IOException e) {
- throw new SecurityException("Failed to generate seed", e);
- }
- }
+ /**
+ * Generates a device- and invocation-specific seed to be mixed into the
+ * Linux PRNG.
+ */
+ private static byte[] generateSeed() {
+ try {
+ ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
+ DataOutputStream seedBufferOut = new DataOutputStream(seedBuffer);
+ seedBufferOut.writeLong(System.currentTimeMillis());
+ seedBufferOut.writeLong(System.nanoTime());
+ seedBufferOut.writeInt(Process.myPid());
+ seedBufferOut.writeInt(Process.myUid());
+ seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL);
+ seedBufferOut.close();
+ return seedBuffer.toByteArray();
+ } catch (IOException e) {
+ throw new SecurityException("Failed to generate seed", e);
+ }
+ }
- /**
- * Gets the hardware serial number of this device.
- *
- * @return serial number or {@code null} if not available.
- */
- private static String getDeviceSerialNumber() {
- // We're using the Reflection API because Build.SERIAL is only available
- // since API Level 9 (Gingerbread, Android 2.3).
- try {
- return (String) Build.class.getField("SERIAL").get(null);
- } catch (Exception ignored) {
- return null;
- }
- }
+ /**
+ * Gets the hardware serial number of this device.
+ *
+ * @return serial number or {@code null} if not available.
+ */
+ private static String getDeviceSerialNumber() {
+ // We're using the Reflection API because Build.SERIAL is only available
+ // since API Level 9 (Gingerbread, Android 2.3).
+ try {
+ return (String) Build.class.getField("SERIAL").get(null);
+ } catch (Exception ignored) {
+ return null;
+ }
+ }
- private static byte[] getBuildFingerprintAndDeviceSerial() {
- StringBuilder result = new StringBuilder();
- String fingerprint = Build.FINGERPRINT;
- if (fingerprint != null) {
- result.append(fingerprint);
- }
- String serial = getDeviceSerialNumber();
- if (serial != null) {
- result.append(serial);
- }
- try {
- return result.toString().getBytes("UTF-8");
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("UTF-8 encoding not supported");
- }
- }
+ private static byte[] getBuildFingerprintAndDeviceSerial() {
+ StringBuilder result = new StringBuilder();
+ String fingerprint = Build.FINGERPRINT;
+ if (fingerprint != null) {
+ result.append(fingerprint);
+ }
+ String serial = getDeviceSerialNumber();
+ if (serial != null) {
+ result.append(serial);
+ }
+ try {
+ return result.toString().getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("UTF-8 encoding not supported");
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/utils/PhoneHelper.java b/src/main/java/de/pixart/messenger/utils/PhoneHelper.java
index 805b76673..cdc51f729 100644
--- a/src/main/java/de/pixart/messenger/utils/PhoneHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/PhoneHelper.java
@@ -19,124 +19,124 @@ import java.util.concurrent.RejectedExecutionException;
public class PhoneHelper {
- public static void loadPhoneContacts(Context context, final OnPhoneContactsLoadedListener listener) {
- final List<Bundle> phoneContacts = new ArrayList<>();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
- && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
- listener.onPhoneContactsLoaded(phoneContacts);
- return;
- }
- final String[] PROJECTION = new String[]{ContactsContract.Data._ID,
- ContactsContract.Data.DISPLAY_NAME,
- ContactsContract.Data.PHOTO_URI,
- ContactsContract.Data.LOOKUP_KEY,
- ContactsContract.CommonDataKinds.Im.DATA};
-
- final String SELECTION = "(" + ContactsContract.Data.MIMETYPE + "=\""
- + ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE
- + "\") AND (" + ContactsContract.CommonDataKinds.Im.PROTOCOL
- + "=\"" + ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER
- + "\")";
-
- CursorLoader mCursorLoader = new NotThrowCursorLoader(context,
- ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, null,
- null);
- mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
-
- @Override
- public void onLoadComplete(Loader<Cursor> arg0, Cursor cursor) {
- if (cursor != null) {
- while (cursor.moveToNext()) {
- Bundle contact = new Bundle();
- contact.putInt("phoneid", cursor.getInt(cursor
- .getColumnIndex(ContactsContract.Data._ID)));
- contact.putString(
- "displayname",
- cursor.getString(cursor
- .getColumnIndex(ContactsContract.Data.DISPLAY_NAME)));
- contact.putString("photouri", cursor.getString(cursor
- .getColumnIndex(ContactsContract.Data.PHOTO_URI)));
- contact.putString("lookup", cursor.getString(cursor
- .getColumnIndex(ContactsContract.Data.LOOKUP_KEY)));
-
- contact.putString(
- "jid",
- cursor.getString(cursor
- .getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)));
- phoneContacts.add(contact);
- }
- cursor.close();
- }
-
- if (listener != null) {
- listener.onPhoneContactsLoaded(phoneContacts);
- }
- }
- });
- try {
- mCursorLoader.startLoading();
- } catch (RejectedExecutionException e) {
- if (listener != null) {
- listener.onPhoneContactsLoaded(phoneContacts);
- }
- }
- }
-
- private static class NotThrowCursorLoader extends CursorLoader {
-
- public NotThrowCursorLoader(Context c, Uri u, String[] p, String s, String[] sa, String so) {
- super(c, u, p, s, sa, so);
- }
-
- @Override
- public Cursor loadInBackground() {
-
- try {
- return (super.loadInBackground());
- } catch (Throwable e) {
- return(null);
- }
- }
-
- }
-
- public static Uri getSelfiUri(Context context) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
- && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
- return null;
- }
- String[] mProjection = new String[]{Profile._ID, Profile.PHOTO_URI};
- Cursor mProfileCursor = context.getContentResolver().query(
- Profile.CONTENT_URI, mProjection, null, null, null);
-
- if (mProfileCursor == null || mProfileCursor.getCount() == 0) {
- return null;
- } else {
- mProfileCursor.moveToFirst();
- String uri = mProfileCursor.getString(1);
- mProfileCursor.close();
- if (uri == null) {
- return null;
- } else {
- return Uri.parse(uri);
- }
- }
- }
-
- public static String getVersionName(Context context) {
- final String packageName = context == null ? null : context.getPackageName();
- if (packageName != null) {
- try {
- return context.getPackageManager().getPackageInfo(packageName, 0).versionName;
- } catch (final PackageManager.NameNotFoundException | RuntimeException e) {
- return "unknown";
- }
- } else {
- return "unknown";
- }
- }
-
- public static String getOSVersion(Context context) {
- return "Android/" + android.os.Build.MODEL + "/" + android.os.Build.VERSION.RELEASE;
+ public static void loadPhoneContacts(Context context, final OnPhoneContactsLoadedListener listener) {
+ final List<Bundle> phoneContacts = new ArrayList<>();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+ && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
+ listener.onPhoneContactsLoaded(phoneContacts);
+ return;
+ }
+ final String[] PROJECTION = new String[]{ContactsContract.Data._ID,
+ ContactsContract.Data.DISPLAY_NAME,
+ ContactsContract.Data.PHOTO_URI,
+ ContactsContract.Data.LOOKUP_KEY,
+ ContactsContract.CommonDataKinds.Im.DATA};
+
+ final String SELECTION = "(" + ContactsContract.Data.MIMETYPE + "=\""
+ + ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE
+ + "\") AND (" + ContactsContract.CommonDataKinds.Im.PROTOCOL
+ + "=\"" + ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER
+ + "\")";
+
+ CursorLoader mCursorLoader = new NotThrowCursorLoader(context,
+ ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, null,
+ null);
+ mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
+
+ @Override
+ public void onLoadComplete(Loader<Cursor> arg0, Cursor cursor) {
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ Bundle contact = new Bundle();
+ contact.putInt("phoneid", cursor.getInt(cursor
+ .getColumnIndex(ContactsContract.Data._ID)));
+ contact.putString(
+ "displayname",
+ cursor.getString(cursor
+ .getColumnIndex(ContactsContract.Data.DISPLAY_NAME)));
+ contact.putString("photouri", cursor.getString(cursor
+ .getColumnIndex(ContactsContract.Data.PHOTO_URI)));
+ contact.putString("lookup", cursor.getString(cursor
+ .getColumnIndex(ContactsContract.Data.LOOKUP_KEY)));
+
+ contact.putString(
+ "jid",
+ cursor.getString(cursor
+ .getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)));
+ phoneContacts.add(contact);
+ }
+ cursor.close();
+ }
+
+ if (listener != null) {
+ listener.onPhoneContactsLoaded(phoneContacts);
+ }
+ }
+ });
+ try {
+ mCursorLoader.startLoading();
+ } catch (RejectedExecutionException e) {
+ if (listener != null) {
+ listener.onPhoneContactsLoaded(phoneContacts);
+ }
+ }
+ }
+
+ private static class NotThrowCursorLoader extends CursorLoader {
+
+ public NotThrowCursorLoader(Context c, Uri u, String[] p, String s, String[] sa, String so) {
+ super(c, u, p, s, sa, so);
+ }
+
+ @Override
+ public Cursor loadInBackground() {
+
+ try {
+ return (super.loadInBackground());
+ } catch (Throwable e) {
+ return (null);
+ }
+ }
+
+ }
+
+ public static Uri getSelfiUri(Context context) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+ && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
+ return null;
+ }
+ String[] mProjection = new String[]{Profile._ID, Profile.PHOTO_URI};
+ Cursor mProfileCursor = context.getContentResolver().query(
+ Profile.CONTENT_URI, mProjection, null, null, null);
+
+ if (mProfileCursor == null || mProfileCursor.getCount() == 0) {
+ return null;
+ } else {
+ mProfileCursor.moveToFirst();
+ String uri = mProfileCursor.getString(1);
+ mProfileCursor.close();
+ if (uri == null) {
+ return null;
+ } else {
+ return Uri.parse(uri);
+ }
+ }
+ }
+
+ public static String getVersionName(Context context) {
+ final String packageName = context == null ? null : context.getPackageName();
+ if (packageName != null) {
+ try {
+ return context.getPackageManager().getPackageInfo(packageName, 0).versionName;
+ } catch (final PackageManager.NameNotFoundException | RuntimeException e) {
+ return "unknown";
+ }
+ } else {
+ return "unknown";
+ }
+ }
+
+ public static String getOSVersion(Context context) {
+ return "Android/" + android.os.Build.MODEL + "/" + android.os.Build.VERSION.RELEASE;
}
}
diff --git a/src/main/java/de/pixart/messenger/utils/SSLSocketHelper.java b/src/main/java/de/pixart/messenger/utils/SSLSocketHelper.java
index 661fda807..ad3629354 100644
--- a/src/main/java/de/pixart/messenger/utils/SSLSocketHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/SSLSocketHelper.java
@@ -14,60 +14,60 @@ import javax.net.ssl.SSLSocketFactory;
public class SSLSocketHelper {
- public static void setSecurity(final SSLSocket sslSocket) throws NoSuchAlgorithmException {
- final String[] supportProtocols;
- final Collection<String> supportedProtocols = new LinkedList<>(
- Arrays.asList(sslSocket.getSupportedProtocols()));
- supportedProtocols.remove("SSLv3");
- supportProtocols = supportedProtocols.toArray(new String[supportedProtocols.size()]);
+ public static void setSecurity(final SSLSocket sslSocket) throws NoSuchAlgorithmException {
+ final String[] supportProtocols;
+ final Collection<String> supportedProtocols = new LinkedList<>(
+ Arrays.asList(sslSocket.getSupportedProtocols()));
+ supportedProtocols.remove("SSLv3");
+ supportProtocols = supportedProtocols.toArray(new String[supportedProtocols.size()]);
- sslSocket.setEnabledProtocols(supportProtocols);
+ sslSocket.setEnabledProtocols(supportProtocols);
- final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites(
- sslSocket.getSupportedCipherSuites());
- if (cipherSuites.length > 0) {
- sslSocket.setEnabledCipherSuites(cipherSuites);
- }
- }
+ final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites(
+ sslSocket.getSupportedCipherSuites());
+ if (cipherSuites.length > 0) {
+ sslSocket.setEnabledCipherSuites(cipherSuites);
+ }
+ }
- public static void setSNIHost(final SSLSocketFactory factory, final SSLSocket socket, final String hostname) {
- if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
- ((android.net.SSLCertificateSocketFactory) factory).setHostname(socket, hostname);
- } else {
- try {
- socket.getClass().getMethod("setHostname", String.class).invoke(socket, hostname);
- } catch (Throwable e) {
- // ignore any error, we just can't set the hostname...
- }
- }
- }
+ public static void setSNIHost(final SSLSocketFactory factory, final SSLSocket socket, final String hostname) {
+ if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ ((android.net.SSLCertificateSocketFactory) factory).setHostname(socket, hostname);
+ } else {
+ try {
+ socket.getClass().getMethod("setHostname", String.class).invoke(socket, hostname);
+ } catch (Throwable e) {
+ // ignore any error, we just can't set the hostname...
+ }
+ }
+ }
- public static void setAlpnProtocol(final SSLSocketFactory factory, final SSLSocket socket, final String protocol) {
- try {
- if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
- // can't call directly because of @hide?
- //((android.net.SSLCertificateSocketFactory)factory).setAlpnProtocols(new byte[][]{protocol.getBytes("UTF-8")});
- android.net.SSLCertificateSocketFactory.class.getMethod("setAlpnProtocols", byte[][].class).invoke(socket, new Object[]{new byte[][]{protocol.getBytes("UTF-8")}});
- } else {
- final Method method = socket.getClass().getMethod("setAlpnProtocols", byte[].class);
- // the concatenation of 8-bit, length prefixed protocol names, just one in our case...
- // http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4
- final byte[] protocolUTF8Bytes = protocol.getBytes("UTF-8");
- final byte[] lengthPrefixedProtocols = new byte[protocolUTF8Bytes.length + 1];
- lengthPrefixedProtocols[0] = (byte) protocol.length(); // cannot be over 255 anyhow
- System.arraycopy(protocolUTF8Bytes, 0, lengthPrefixedProtocols, 1, protocolUTF8Bytes.length);
- method.invoke(socket, new Object[]{lengthPrefixedProtocols});
- }
- } catch (Throwable e) {
- // ignore any error, we just can't set the alpn protocol...
- }
- }
+ public static void setAlpnProtocol(final SSLSocketFactory factory, final SSLSocket socket, final String protocol) {
+ try {
+ if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
+ // can't call directly because of @hide?
+ //((android.net.SSLCertificateSocketFactory)factory).setAlpnProtocols(new byte[][]{protocol.getBytes("UTF-8")});
+ android.net.SSLCertificateSocketFactory.class.getMethod("setAlpnProtocols", byte[][].class).invoke(socket, new Object[]{new byte[][]{protocol.getBytes("UTF-8")}});
+ } else {
+ final Method method = socket.getClass().getMethod("setAlpnProtocols", byte[].class);
+ // the concatenation of 8-bit, length prefixed protocol names, just one in our case...
+ // http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4
+ final byte[] protocolUTF8Bytes = protocol.getBytes("UTF-8");
+ final byte[] lengthPrefixedProtocols = new byte[protocolUTF8Bytes.length + 1];
+ lengthPrefixedProtocols[0] = (byte) protocol.length(); // cannot be over 255 anyhow
+ System.arraycopy(protocolUTF8Bytes, 0, lengthPrefixedProtocols, 1, protocolUTF8Bytes.length);
+ method.invoke(socket, new Object[]{lengthPrefixedProtocols});
+ }
+ } catch (Throwable e) {
+ // ignore any error, we just can't set the alpn protocol...
+ }
+ }
- public static SSLContext getSSLContext() throws NoSuchAlgorithmException {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- return SSLContext.getInstance("TLSv1.2");
- } else {
- return SSLContext.getInstance("TLS");
- }
- }
+ public static SSLContext getSSLContext() throws NoSuchAlgorithmException {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ return SSLContext.getInstance("TLSv1.2");
+ } else {
+ return SSLContext.getInstance("TLS");
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java b/src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java
index 116149ec1..6b60a424f 100644
--- a/src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java
+++ b/src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java
@@ -9,43 +9,43 @@ import java.util.concurrent.Executors;
public class SerialSingleThreadExecutor implements Executor {
- final Executor executor = Executors.newSingleThreadExecutor();
- protected final Queue<Runnable> tasks = new ArrayDeque();
- Runnable active;
-
- public SerialSingleThreadExecutor() {
- this(false);
- }
-
- public SerialSingleThreadExecutor(boolean prepareLooper) {
- if (prepareLooper) {
- execute(new Runnable() {
- @Override
- public void run() {
- Looper.prepare();
- }
- });
- }
- }
-
- public synchronized void execute(final Runnable r) {
- tasks.offer(new Runnable() {
- public void run() {
- try {
- r.run();
- } finally {
- scheduleNext();
- }
- }
- });
- if (active == null) {
- scheduleNext();
- }
- }
-
- protected synchronized void scheduleNext() {
- if ((active = tasks.poll()) != null) {
- executor.execute(active);
- }
- }
+ final Executor executor = Executors.newSingleThreadExecutor();
+ protected final Queue<Runnable> tasks = new ArrayDeque();
+ Runnable active;
+
+ public SerialSingleThreadExecutor() {
+ this(false);
+ }
+
+ public SerialSingleThreadExecutor(boolean prepareLooper) {
+ if (prepareLooper) {
+ execute(new Runnable() {
+ @Override
+ public void run() {
+ Looper.prepare();
+ }
+ });
+ }
+ }
+
+ public synchronized void execute(final Runnable r) {
+ tasks.offer(new Runnable() {
+ public void run() {
+ try {
+ r.run();
+ } finally {
+ scheduleNext();
+ }
+ }
+ });
+ if (active == null) {
+ scheduleNext();
+ }
+ }
+
+ protected synchronized void scheduleNext() {
+ if ((active = tasks.poll()) != null) {
+ executor.execute(active);
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/utils/SocksSocketFactory.java b/src/main/java/de/pixart/messenger/utils/SocksSocketFactory.java
index ee7be8e9c..e3465fb9b 100644
--- a/src/main/java/de/pixart/messenger/utils/SocksSocketFactory.java
+++ b/src/main/java/de/pixart/messenger/utils/SocksSocketFactory.java
@@ -12,46 +12,46 @@ import de.pixart.messenger.Config;
public class SocksSocketFactory {
- public static void createSocksConnection(Socket socket, String destination, int port) throws IOException {
- InputStream proxyIs = socket.getInputStream();
- OutputStream proxyOs = socket.getOutputStream();
- proxyOs.write(new byte[]{0x05, 0x01, 0x00});
- byte[] response = new byte[2];
- proxyIs.read(response);
- byte[] dest = destination.getBytes();
- ByteBuffer request = ByteBuffer.allocate(7 + dest.length);
- request.put(new byte[]{0x05, 0x01, 0x00, 0x03});
- request.put((byte) dest.length);
- request.put(dest);
- request.putShort((short) port);
- proxyOs.write(request.array());
- response = new byte[7 + dest.length];
- proxyIs.read(response);
- if (response[1] != 0x00) {
- throw new SocksConnectionException();
- }
- }
-
- public static Socket createSocket(InetSocketAddress address, String destination, int port) throws IOException {
- Socket socket = new Socket();
- try {
- socket.connect(address, Config.CONNECT_TIMEOUT * 1000);
- } catch (IOException e) {
- throw new SocksProxyNotFoundException();
- }
- createSocksConnection(socket, destination, port);
- 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 {
-
- }
-
- public static class SocksProxyNotFoundException extends IOException {
-
- }
+ public static void createSocksConnection(Socket socket, String destination, int port) throws IOException {
+ InputStream proxyIs = socket.getInputStream();
+ OutputStream proxyOs = socket.getOutputStream();
+ proxyOs.write(new byte[]{0x05, 0x01, 0x00});
+ byte[] response = new byte[2];
+ proxyIs.read(response);
+ byte[] dest = destination.getBytes();
+ ByteBuffer request = ByteBuffer.allocate(7 + dest.length);
+ request.put(new byte[]{0x05, 0x01, 0x00, 0x03});
+ request.put((byte) dest.length);
+ request.put(dest);
+ request.putShort((short) port);
+ proxyOs.write(request.array());
+ response = new byte[7 + dest.length];
+ proxyIs.read(response);
+ if (response[1] != 0x00) {
+ throw new SocksConnectionException();
+ }
+ }
+
+ public static Socket createSocket(InetSocketAddress address, String destination, int port) throws IOException {
+ Socket socket = new Socket();
+ try {
+ socket.connect(address, Config.CONNECT_TIMEOUT * 1000);
+ } catch (IOException e) {
+ throw new SocksProxyNotFoundException();
+ }
+ createSocksConnection(socket, destination, port);
+ 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 {
+
+ }
+
+ public static class SocksProxyNotFoundException extends IOException {
+
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/UIHelper.java b/src/main/java/de/pixart/messenger/utils/UIHelper.java
index 100eff4ea..0bb87e6c7 100644
--- a/src/main/java/de/pixart/messenger/utils/UIHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/UIHelper.java
@@ -24,310 +24,310 @@ import de.pixart.messenger.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";
+ 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));
+ public static final ArrayList<String> HEARTS = new ArrayList<>(Arrays.asList(BLACK_HEART_SUIT, HEAVY_BLACK_HEART_SUIT, WHITE_HEART_SUIT));
- private static final ArrayList<String> LOCATION_QUESTIONS = new ArrayList<>(Arrays.asList(
- "where are you", //en
- "where are you now", //en
- "where are you right now", //en
- "whats your 20", //en
- "what is your 20", //en
- "what's your 20", //en
- "whats your twenty", //en
- "what is your twenty", //en
- "what's your twenty", //en
- "wo bist du", //de
- "wo bist du jetzt", //de
- "wo bist du gerade", //de
- "wo seid ihr", //de
- "wo seid ihr jetzt", //de
- "wo seid ihr gerade", //de
- "dónde estás", //es
- "donde estas" //es
- ));
+ private static final ArrayList<String> LOCATION_QUESTIONS = new ArrayList<>(Arrays.asList(
+ "where are you", //en
+ "where are you now", //en
+ "where are you right now", //en
+ "whats your 20", //en
+ "what is your 20", //en
+ "what's your 20", //en
+ "whats your twenty", //en
+ "what is your twenty", //en
+ "what's your twenty", //en
+ "wo bist du", //de
+ "wo bist du jetzt", //de
+ "wo bist du gerade", //de
+ "wo seid ihr", //de
+ "wo seid ihr jetzt", //de
+ "wo seid ihr gerade", //de
+ "dónde estás", //es
+ "donde estas" //es
+ ));
- private static final int SHORT_DATE_FLAGS = DateUtils.FORMAT_SHOW_DATE
- | DateUtils.FORMAT_NO_YEAR | DateUtils.FORMAT_ABBREV_ALL;
- private static final int FULL_DATE_FLAGS = DateUtils.FORMAT_SHOW_TIME
- | DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE;
+ private static final int SHORT_DATE_FLAGS = DateUtils.FORMAT_SHOW_DATE
+ | DateUtils.FORMAT_NO_YEAR | DateUtils.FORMAT_ABBREV_ALL;
+ private static final int FULL_DATE_FLAGS = DateUtils.FORMAT_SHOW_TIME
+ | DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE;
- public static String readableTimeDifference(Context context, long time) {
- return readableTimeDifference(context, time, false);
- }
+ public static String readableTimeDifference(Context context, long time) {
+ return readableTimeDifference(context, time, false);
+ }
- public static String readableTimeDifferenceFull(Context context, long time) {
- return readableTimeDifference(context, time, true);
- }
+ public static String readableTimeDifferenceFull(Context context, long time) {
+ return readableTimeDifference(context, time, true);
+ }
- private static String readableTimeDifference(Context context, long time,
- boolean fullDate) {
- if (time == 0) {
- return context.getString(R.string.just_now);
- }
- Date date = new Date(time);
- long difference = (System.currentTimeMillis() - time) / 1000;
- if (difference < 60) {
- return context.getString(R.string.just_now);
- } else if (difference < 60 * 2) {
- return context.getString(R.string.minute_ago);
- } else if (difference < 60 * 15) {
- return context.getString(R.string.minutes_ago,Math.round(difference / 60.0));
- } else if (today(date)) {
- java.text.DateFormat df = DateFormat.getTimeFormat(context);
- return df.format(date);
- } else {
- if (fullDate) {
- return DateUtils.formatDateTime(context, date.getTime(),
- FULL_DATE_FLAGS);
- } else {
- return DateUtils.formatDateTime(context, date.getTime(),
- SHORT_DATE_FLAGS);
- }
- }
- }
+ private static String readableTimeDifference(Context context, long time,
+ boolean fullDate) {
+ if (time == 0) {
+ return context.getString(R.string.just_now);
+ }
+ Date date = new Date(time);
+ long difference = (System.currentTimeMillis() - time) / 1000;
+ if (difference < 60) {
+ return context.getString(R.string.just_now);
+ } else if (difference < 60 * 2) {
+ return context.getString(R.string.minute_ago);
+ } else if (difference < 60 * 15) {
+ return context.getString(R.string.minutes_ago, Math.round(difference / 60.0));
+ } else if (today(date)) {
+ java.text.DateFormat df = DateFormat.getTimeFormat(context);
+ return df.format(date);
+ } else {
+ if (fullDate) {
+ return DateUtils.formatDateTime(context, date.getTime(),
+ FULL_DATE_FLAGS);
+ } else {
+ return DateUtils.formatDateTime(context, date.getTime(),
+ SHORT_DATE_FLAGS);
+ }
+ }
+ }
- private static boolean today(Date date) {
- return sameDay(date,new Date(System.currentTimeMillis()));
- }
+ private static boolean today(Date date) {
+ return sameDay(date, new Date(System.currentTimeMillis()));
+ }
- private static boolean sameDay(Date a, Date b) {
- Calendar cal1 = Calendar.getInstance();
- Calendar cal2 = Calendar.getInstance();
- cal1.setTime(a);
- cal2.setTime(b);
- return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)
- && cal1.get(Calendar.DAY_OF_YEAR) == cal2
- .get(Calendar.DAY_OF_YEAR);
- }
+ private static boolean sameDay(Date a, Date b) {
+ Calendar cal1 = Calendar.getInstance();
+ Calendar cal2 = Calendar.getInstance();
+ cal1.setTime(a);
+ cal2.setTime(b);
+ return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)
+ && cal1.get(Calendar.DAY_OF_YEAR) == cal2
+ .get(Calendar.DAY_OF_YEAR);
+ }
- public static String lastseen(Context context, boolean active, long time) {
- long difference = (System.currentTimeMillis() - time) / 1000;
- active = active && difference <= 300;
- if (active || difference < 60) {
- return context.getString(R.string.last_seen_now);
- } else if (difference < 60 * 2) {
- return context.getString(R.string.last_seen_min);
- } else if (difference < 60 * 60) {
- return context.getString(R.string.last_seen_mins,
- Math.round(difference / 60.0));
- } else if (difference < 60 * 60 * 2) {
- return context.getString(R.string.last_seen_hour);
- } else if (difference < 60 * 60 * 24) {
- return context.getString(R.string.last_seen_hours,
- Math.round(difference / (60.0 * 60.0)));
- } else if (difference < 60 * 60 * 48) {
- return context.getString(R.string.last_seen_day);
- } else {
- return context.getString(R.string.last_seen_days,
- Math.round(difference / (60.0 * 60.0 * 24.0)));
- }
- }
+ public static String lastseen(Context context, boolean active, long time) {
+ long difference = (System.currentTimeMillis() - time) / 1000;
+ active = active && difference <= 300;
+ if (active || difference < 60) {
+ return context.getString(R.string.last_seen_now);
+ } else if (difference < 60 * 2) {
+ return context.getString(R.string.last_seen_min);
+ } else if (difference < 60 * 60) {
+ return context.getString(R.string.last_seen_mins,
+ Math.round(difference / 60.0));
+ } else if (difference < 60 * 60 * 2) {
+ return context.getString(R.string.last_seen_hour);
+ } else if (difference < 60 * 60 * 24) {
+ return context.getString(R.string.last_seen_hours,
+ Math.round(difference / (60.0 * 60.0)));
+ } else if (difference < 60 * 60 * 48) {
+ return context.getString(R.string.last_seen_day);
+ } else {
+ return context.getString(R.string.last_seen_days,
+ Math.round(difference / (60.0 * 60.0 * 24.0)));
+ }
+ }
- public static int getColorForName(String name) {
- if (name == null || name.isEmpty()) {
- return 0xFF202020;
- }
- int colors[] = {0xFFe91e63, 0xFF9c27b0, 0xFF673ab7, 0xFF3f51b5,
- 0xFF5677fc, 0xFF03a9f4, 0xFF00bcd4, 0xFF009688, 0xFFff5722,
- 0xFF795548, 0xFF607d8b};
- return colors[(int) ((name.hashCode() & 0xffffffffl) % colors.length)];
- }
+ public static int getColorForName(String name) {
+ if (name == null || name.isEmpty()) {
+ return 0xFF202020;
+ }
+ int colors[] = {0xFFe91e63, 0xFF9c27b0, 0xFF673ab7, 0xFF3f51b5,
+ 0xFF5677fc, 0xFF03a9f4, 0xFF00bcd4, 0xFF009688, 0xFFff5722,
+ 0xFF795548, 0xFF607d8b};
+ return colors[(int) ((name.hashCode() & 0xffffffffl) % colors.length)];
+ }
- public static Pair<String,Boolean> getMessagePreview(final Context context, final Message message) {
- final Transferable d = message.getTransferable();
- if (d != null ) {
- switch (d.getStatus()) {
- case Transferable.STATUS_CHECKING:
- return new Pair<>(context.getString(R.string.checking_x,
- getFileDescriptionString(context,message)),true);
- case Transferable.STATUS_DOWNLOADING:
- return new Pair<>(context.getString(R.string.receiving_x_file,
- getFileDescriptionString(context,message),
- d.getProgress()),true);
- case Transferable.STATUS_OFFER:
- case Transferable.STATUS_OFFER_CHECK_FILESIZE:
- return new Pair<>(context.getString(R.string.x_file_offered_for_download,
- getFileDescriptionString(context,message)),true);
- case Transferable.STATUS_DELETED:
- return new Pair<>(context.getString(R.string.file_deleted),true);
- case Transferable.STATUS_FAILED:
- return new Pair<>(context.getString(R.string.file_transmission_failed),true);
- case Transferable.STATUS_UPLOADING:
- if (message.getStatus() == Message.STATUS_OFFERED) {
- return new Pair<>(context.getString(R.string.offering_x_file,
- getFileDescriptionString(context, message)), true);
- } else {
- return new Pair<>(context.getString(R.string.sending_x_file,
- getFileDescriptionString(context, message)), true);
- }
- default:
- return new Pair<>("",false);
- }
- } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- return new Pair<>(context.getString(R.string.pgp_message),true);
- } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
- return new Pair<>(context.getString(R.string.decryption_failed), true);
- } else if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) {
- if (message.getStatus() == Message.STATUS_RECEIVED) {
- return new Pair<>(context.getString(R.string.received_x_file,
- getFileDescriptionString(context, message)), true);
- } else {
- return new Pair<>(getFileDescriptionString(context,message),true);
- }
- } else {
- String body = message.getBody();
+ public static Pair<String, Boolean> getMessagePreview(final Context context, final Message message) {
+ final Transferable d = message.getTransferable();
+ if (d != null) {
+ switch (d.getStatus()) {
+ case Transferable.STATUS_CHECKING:
+ return new Pair<>(context.getString(R.string.checking_x,
+ getFileDescriptionString(context, message)), true);
+ case Transferable.STATUS_DOWNLOADING:
+ return new Pair<>(context.getString(R.string.receiving_x_file,
+ getFileDescriptionString(context, message),
+ d.getProgress()), true);
+ case Transferable.STATUS_OFFER:
+ case Transferable.STATUS_OFFER_CHECK_FILESIZE:
+ return new Pair<>(context.getString(R.string.x_file_offered_for_download,
+ getFileDescriptionString(context, message)), true);
+ case Transferable.STATUS_DELETED:
+ return new Pair<>(context.getString(R.string.file_deleted), true);
+ case Transferable.STATUS_FAILED:
+ return new Pair<>(context.getString(R.string.file_transmission_failed), true);
+ case Transferable.STATUS_UPLOADING:
+ if (message.getStatus() == Message.STATUS_OFFERED) {
+ return new Pair<>(context.getString(R.string.offering_x_file,
+ getFileDescriptionString(context, message)), true);
+ } else {
+ return new Pair<>(context.getString(R.string.sending_x_file,
+ getFileDescriptionString(context, message)), true);
+ }
+ default:
+ return new Pair<>("", false);
+ }
+ } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ return new Pair<>(context.getString(R.string.pgp_message), true);
+ } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
+ return new Pair<>(context.getString(R.string.decryption_failed), true);
+ } else if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) {
+ if (message.getStatus() == Message.STATUS_RECEIVED) {
+ return new Pair<>(context.getString(R.string.received_x_file,
+ getFileDescriptionString(context, message)), true);
+ } else {
+ return new Pair<>(getFileDescriptionString(context, message), true);
+ }
+ } else {
+ String body = message.getBody();
if (body.length() > 256) {
body = body.substring(0, 256);
}
if (body.startsWith(Message.ME_COMMAND)) {
- return new Pair<>(body.replaceAll("^" + Message.ME_COMMAND,
- 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);
- } else {
- return new Pair<>(context.getString(R.string.location), true);
- }
+ return new Pair<>(body.replaceAll("^" + Message.ME_COMMAND,
+ 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);
+ } else {
+ return new Pair<>(context.getString(R.string.location), true);
+ }
} else if (message.bodyIsXmpp()) {
if (message.getStatus() == Message.STATUS_RECEIVED) {
return new Pair<>(context.getString(R.string.received_contact), true);
} else {
return new Pair<>(context.getString(R.string.contact), true);
}
- } else if (message.treatAsDownloadable() == Message.Decision.MUST) {
- return new Pair<>(context.getString(R.string.x_file_offered_for_download,
- getFileDescriptionString(context,message)),true);
- } else{
- return new Pair<>(body.trim(), false);
- }
- }
- }
+ } else if (message.treatAsDownloadable() == Message.Decision.MUST) {
+ return new Pair<>(context.getString(R.string.x_file_offered_for_download,
+ getFileDescriptionString(context, message)), true);
+ } else {
+ return new Pair<>(body.trim(), false);
+ }
+ }
+ }
- public static String getFileDescriptionString(final Context context, final Message message) {
- if (message.getType() == Message.TYPE_IMAGE) {
- return context.getString(R.string.image);
- }
- final String mime = message.getMimeType();
- if (mime == null) {
- return context.getString(R.string.file);
- } else if (mime.startsWith("audio/")) {
- return context.getString(R.string.audio);
- } else if(mime.startsWith("video/")) {
- return context.getString(R.string.video);
- } else if (mime.startsWith("image/")) {
- return context.getString(R.string.image);
- } else if (mime.contains("pdf")) {
- return context.getString(R.string.pdf_document) ;
- } else if (mime.contains("application/vnd.android.package-archive")) {
- return context.getString(R.string.apk) ;
- } else if (mime.contains("vcard")) {
- return context.getString(R.string.vcard) ;
- } else {
- return mime;
- }
- }
+ public static String getFileDescriptionString(final Context context, final Message message) {
+ if (message.getType() == Message.TYPE_IMAGE) {
+ return context.getString(R.string.image);
+ }
+ final String mime = message.getMimeType();
+ if (mime == null) {
+ return context.getString(R.string.file);
+ } else if (mime.startsWith("audio/")) {
+ return context.getString(R.string.audio);
+ } else if (mime.startsWith("video/")) {
+ return context.getString(R.string.video);
+ } else if (mime.startsWith("image/")) {
+ return context.getString(R.string.image);
+ } else if (mime.contains("pdf")) {
+ return context.getString(R.string.pdf_document);
+ } else if (mime.contains("application/vnd.android.package-archive")) {
+ return context.getString(R.string.apk);
+ } else if (mime.contains("vcard")) {
+ return context.getString(R.string.vcard);
+ } else {
+ return mime;
+ }
+ }
- public static String getMessageDisplayName(final Message message) {
- final Conversation conversation = message.getConversation();
- if (message.getStatus() == Message.STATUS_RECEIVED) {
- final Contact contact = message.getContact();
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- if (contact != null) {
- return contact.getDisplayName();
- } else {
- return getDisplayedMucCounterpart(message.getCounterpart());
- }
- } else {
- return contact != null ? contact.getDisplayName() : "";
- }
- } else {
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- return conversation.getMucOptions().getSelf().getName();
- } else {
- final Jid jid = conversation.getAccount().getJid();
- return jid.hasLocalpart() ? jid.getLocalpart() : jid.toDomainJid().toString();
- }
- }
- }
+ public static String getMessageDisplayName(final Message message) {
+ final Conversation conversation = message.getConversation();
+ if (message.getStatus() == Message.STATUS_RECEIVED) {
+ final Contact contact = message.getContact();
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ if (contact != null) {
+ return contact.getDisplayName();
+ } else {
+ return getDisplayedMucCounterpart(message.getCounterpart());
+ }
+ } else {
+ return contact != null ? contact.getDisplayName() : "";
+ }
+ } else {
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ return conversation.getMucOptions().getSelf().getName();
+ } else {
+ final Jid jid = conversation.getAccount().getJid();
+ return jid.hasLocalpart() ? jid.getLocalpart() : jid.toDomainJid().toString();
+ }
+ }
+ }
- public static String getMessageHint(Context context, Conversation conversation) {
- switch (conversation.getNextEncryption()) {
- case Message.ENCRYPTION_NONE:
- if (Config.multipleEncryptionChoices()) {
- return context.getString(R.string.send_unencrypted_message);
- } else {
- return context.getString(R.string.send_message_to_x,conversation.getName());
- }
- case Message.ENCRYPTION_OTR:
- return context.getString(R.string.send_otr_message);
- case Message.ENCRYPTION_AXOLOTL:
- AxolotlService axolotlService = conversation.getAccount().getAxolotlService();
- if (axolotlService != null && axolotlService.trustedSessionVerified(conversation)) {
- return context.getString(R.string.send_omemo_x509_message);
- } else {
- return context.getString(R.string.send_omemo_message);
- }
- case Message.ENCRYPTION_PGP:
- return context.getString(R.string.send_pgp_message);
- default:
- return "";
- }
- }
+ public static String getMessageHint(Context context, Conversation conversation) {
+ switch (conversation.getNextEncryption()) {
+ case Message.ENCRYPTION_NONE:
+ if (Config.multipleEncryptionChoices()) {
+ return context.getString(R.string.send_unencrypted_message);
+ } else {
+ return context.getString(R.string.send_message_to_x, conversation.getName());
+ }
+ case Message.ENCRYPTION_OTR:
+ return context.getString(R.string.send_otr_message);
+ case Message.ENCRYPTION_AXOLOTL:
+ AxolotlService axolotlService = conversation.getAccount().getAxolotlService();
+ if (axolotlService != null && axolotlService.trustedSessionVerified(conversation)) {
+ return context.getString(R.string.send_omemo_x509_message);
+ } else {
+ return context.getString(R.string.send_omemo_message);
+ }
+ case Message.ENCRYPTION_PGP:
+ return context.getString(R.string.send_pgp_message);
+ default:
+ return "";
+ }
+ }
- public static String getDisplayedMucCounterpart(final Jid counterpart) {
- if (counterpart==null) {
- return "";
- } else if (!counterpart.isBareJid()) {
- return counterpart.getResourcepart().trim();
- } else {
- return counterpart.toString().trim();
- }
- }
+ public static String getDisplayedMucCounterpart(final Jid counterpart) {
+ if (counterpart == null) {
+ return "";
+ } else if (!counterpart.isBareJid()) {
+ return counterpart.getResourcepart().trim();
+ } else {
+ return counterpart.toString().trim();
+ }
+ }
- public static boolean receivedLocationQuestion(Message message) {
- if (message == null
- || message.getStatus() != Message.STATUS_RECEIVED
- || message.getType() != Message.TYPE_TEXT) {
- return false;
- }
- String body = message.getBody() == null ? null : message.getBody().trim().toLowerCase(Locale.getDefault());
- body = body.replace("?","").replace("¿","");
- return LOCATION_QUESTIONS.contains(body);
- }
+ public static boolean receivedLocationQuestion(Message message) {
+ if (message == null
+ || message.getStatus() != Message.STATUS_RECEIVED
+ || message.getType() != Message.TYPE_TEXT) {
+ return false;
+ }
+ String body = message.getBody() == null ? null : message.getBody().trim().toLowerCase(Locale.getDefault());
+ body = body.replace("?", "").replace("¿", "");
+ return LOCATION_QUESTIONS.contains(body);
+ }
- public static ListItem.Tag getTagForStatus(Context context, Presence.Status status) {
- switch (status) {
- case CHAT:
- return new ListItem.Tag(context.getString(R.string.presence_chat), 0xff259b24);
- case AWAY:
- return new ListItem.Tag(context.getString(R.string.presence_away), 0xffff9800);
- case XA:
- return new ListItem.Tag(context.getString(R.string.presence_xa), 0xfff44336);
- case DND:
- return new ListItem.Tag(context.getString(R.string.presence_dnd), 0xfff44336);
- default:
- return new ListItem.Tag(context.getString(R.string.presence_online), 0xff259b24);
- }
- }
+ public static ListItem.Tag getTagForStatus(Context context, Presence.Status status) {
+ switch (status) {
+ case CHAT:
+ return new ListItem.Tag(context.getString(R.string.presence_chat), 0xff259b24);
+ case AWAY:
+ return new ListItem.Tag(context.getString(R.string.presence_away), 0xffff9800);
+ case XA:
+ return new ListItem.Tag(context.getString(R.string.presence_xa), 0xfff44336);
+ case DND:
+ return new ListItem.Tag(context.getString(R.string.presence_dnd), 0xfff44336);
+ default:
+ return new ListItem.Tag(context.getString(R.string.presence_online), 0xff259b24);
+ }
+ }
- public static String tranlasteType(Context context, String type) {
- switch (type.toLowerCase()) {
- case "pc":
- return context.getString(R.string.type_pc);
- case "phone":
- return context.getString(R.string.type_phone);
- case "tablet":
- return context.getString(R.string.type_tablet);
- case "web":
- return context.getString(R.string.type_web);
- case "console":
- return context.getString(R.string.type_console);
- default:
- return type;
- }
- }
+ public static String tranlasteType(Context context, String type) {
+ switch (type.toLowerCase()) {
+ case "pc":
+ return context.getString(R.string.type_pc);
+ case "phone":
+ return context.getString(R.string.type_phone);
+ case "tablet":
+ return context.getString(R.string.type_tablet);
+ case "web":
+ return context.getString(R.string.type_web);
+ case "console":
+ return context.getString(R.string.type_console);
+ default:
+ return type;
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/XmlHelper.java b/src/main/java/de/pixart/messenger/utils/XmlHelper.java
index 29729f9d1..299deed28 100644
--- a/src/main/java/de/pixart/messenger/utils/XmlHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/XmlHelper.java
@@ -1,13 +1,13 @@
package de.pixart.messenger.utils;
public class XmlHelper {
- public static String encodeEntities(String content) {
- content = content.replace("&", "&amp;");
- content = content.replace("<", "&lt;");
- content = content.replace(">", "&gt;");
- content = content.replace("\"", "&quot;");
- content = content.replace("'", "&apos;");
- content = content.replaceAll("[\\p{Cntrl}&&[^\n\t\r]]", "");
- return content;
- }
+ public static String encodeEntities(String content) {
+ content = content.replace("&", "&amp;");
+ content = content.replace("<", "&lt;");
+ content = content.replace(">", "&gt;");
+ content = content.replace("\"", "&quot;");
+ content = content.replace("'", "&apos;");
+ content = content.replaceAll("[\\p{Cntrl}&&[^\n\t\r]]", "");
+ return content;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/Xmlns.java b/src/main/java/de/pixart/messenger/utils/Xmlns.java
index a4022e03e..675d2db80 100644
--- a/src/main/java/de/pixart/messenger/utils/Xmlns.java
+++ b/src/main/java/de/pixart/messenger/utils/Xmlns.java
@@ -1,10 +1,10 @@
package de.pixart.messenger.utils;
public final class Xmlns {
- public static final String BLOCKING = "urn:xmpp:blocking";
- public static final String ROSTER = "jabber:iq:roster";
- public static final String REGISTER = "jabber:iq:register";
- public static final String BYTE_STREAMS = "http://jabber.org/protocol/bytestreams";
- public static final String HTTP_UPLOAD = "urn:xmpp:http:upload";
+ public static final String BLOCKING = "urn:xmpp:blocking";
+ public static final String ROSTER = "jabber:iq:roster";
+ public static final String REGISTER = "jabber:iq:register";
+ public static final String BYTE_STREAMS = "http://jabber.org/protocol/bytestreams";
+ public static final String HTTP_UPLOAD = "urn:xmpp:http:upload";
public static final String STANZA_IDS = "urn:xmpp:sid:0";
}
diff --git a/src/main/java/de/pixart/messenger/utils/XmppUri.java b/src/main/java/de/pixart/messenger/utils/XmppUri.java
index dc52f6637..51f2b808a 100644
--- a/src/main/java/de/pixart/messenger/utils/XmppUri.java
+++ b/src/main/java/de/pixart/messenger/utils/XmppUri.java
@@ -13,139 +13,140 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class XmppUri {
- protected String jid;
- protected boolean muc;
- protected String fingerprint;
- protected List<Fingerprint> fingerprints = new ArrayList<>();
- public static final String OMEMO_URI_PARAM = "omemo-sid-";
- public static final String OTR_URI_PARAM = "otr-fingerprint";
-
- public XmppUri(String uri) {
- try {
- parse(Uri.parse(uri));
- } catch (IllegalArgumentException e) {
- try {
- jid = Jid.fromString(uri).toBareJid().toString();
- } catch (InvalidJidException e2) {
- jid = null;
- }
- }
- }
-
- public XmppUri(Uri uri) {
- parse(uri);
- }
-
- public static boolean isXmppUri(String uri) {
- String scheme = Uri.parse(uri).getScheme();
- return "xmpp".equalsIgnoreCase(scheme);
- }
-
- protected void parse(Uri uri) {
- String scheme = uri.getScheme();
- String host = uri.getHost();
- List<String> segments = uri.getPathSegments();
- if ("https".equalsIgnoreCase(scheme) && "jabber.pix-art.de".equalsIgnoreCase(host)) {
- if (segments.size() >= 2 && segments.get(1).contains("@")) {
- // sample : https://conversations.im/i/foo@bar.com
- try {
- jid = Jid.fromString(segments.get(1)).toString();
- } catch (Exception e) {
- jid = null;
- }
- } else if (segments.size() >= 3) {
- // sample : https://conversations.im/i/foo/bar.com
- jid = segments.get(1) + "@" + segments.get(2);
- }
- muc = segments.size() > 1 && "j".equalsIgnoreCase(segments.get(0));
- } else if ("xmpp".equalsIgnoreCase(scheme)) {
- // sample: xmpp:foo@bar.com
- muc = "join".equalsIgnoreCase(uri.getQuery());
- if (uri.getAuthority() != null) {
- jid = uri.getAuthority();
- } else {
- jid = uri.getSchemeSpecificPart().split("\\?")[0];
- }
- this.fingerprints = parseFingerprints(uri.getQuery());
- } else if ("imto".equalsIgnoreCase(scheme)) {
- // sample: imto://xmpp/foo@bar.com
- try {
- jid = URLDecoder.decode(uri.getEncodedPath(), "UTF-8").split("/")[1];
- } catch (final UnsupportedEncodingException ignored) {
- jid = null;
- }
- } else {
- try {
- jid = Jid.fromString(uri.toString()).toBareJid().toString();
- } catch (final InvalidJidException ignored) {
- jid = null;
- }
- }
- }
-
- protected List<Fingerprint> parseFingerprints(String query) {
- List<Fingerprint> fingerprints = new ArrayList<>();
- String[] pairs = query == null ? new String[0] : query.split(";");
- for(String pair : pairs) {
- String[] parts = pair.split("=",2);
- if (parts.length == 2) {
- String key = parts[0].toLowerCase(Locale.US);
- String value = parts[1];
- if (OTR_URI_PARAM.equals(key)) {
- fingerprints.add(new Fingerprint(FingerprintType.OTR,value));
- }
- if (key.startsWith(OMEMO_URI_PARAM)) {
- try {
- int id = Integer.parseInt(key.substring(OMEMO_URI_PARAM.length()));
- fingerprints.add(new Fingerprint(FingerprintType.OMEMO,value,id));
- } catch (Exception e) {
- //ignoring invalid device id
- }
- }
- return null;
- }
- }
- return fingerprints;
- }
-
- public Jid getJid() {
- try {
- return this.jid == null ? null :Jid.fromString(this.jid.toLowerCase());
- } catch (InvalidJidException e) {
- return null;
- }
- }
-
- public List<Fingerprint> getFingerprints() {
- return this.fingerprints;
- }
-
- public boolean hasFingerprints() {
- return fingerprints.size() > 0;
- }
- public enum FingerprintType {
- OMEMO,
- OTR
- }
-
- public static class Fingerprint {
- public final FingerprintType type;
- public final String fingerprint;
- public final int deviceId;
-
- public Fingerprint(FingerprintType type, String fingerprint) {
- this(type, fingerprint, 0);
- }
-
- public Fingerprint(FingerprintType type, String fingerprint, int deviceId) {
- this.type = type;
- this.fingerprint = fingerprint;
- this.deviceId = deviceId;
- }
-
- @Override
- public String toString() {
- return type.toString()+": "+fingerprint+(deviceId != 0 ? " "+String.valueOf(deviceId) : "");
- }
- }
+ protected String jid;
+ protected boolean muc;
+ protected String fingerprint;
+ protected List<Fingerprint> fingerprints = new ArrayList<>();
+ public static final String OMEMO_URI_PARAM = "omemo-sid-";
+ public static final String OTR_URI_PARAM = "otr-fingerprint";
+
+ public XmppUri(String uri) {
+ try {
+ parse(Uri.parse(uri));
+ } catch (IllegalArgumentException e) {
+ try {
+ jid = Jid.fromString(uri).toBareJid().toString();
+ } catch (InvalidJidException e2) {
+ jid = null;
+ }
+ }
+ }
+
+ public XmppUri(Uri uri) {
+ parse(uri);
+ }
+
+ public static boolean isXmppUri(String uri) {
+ String scheme = Uri.parse(uri).getScheme();
+ return "xmpp".equalsIgnoreCase(scheme);
+ }
+
+ protected void parse(Uri uri) {
+ String scheme = uri.getScheme();
+ String host = uri.getHost();
+ List<String> segments = uri.getPathSegments();
+ if ("https".equalsIgnoreCase(scheme) && "jabber.pix-art.de".equalsIgnoreCase(host)) {
+ if (segments.size() >= 2 && segments.get(1).contains("@")) {
+ // sample : https://conversations.im/i/foo@bar.com
+ try {
+ jid = Jid.fromString(segments.get(1)).toString();
+ } catch (Exception e) {
+ jid = null;
+ }
+ } else if (segments.size() >= 3) {
+ // sample : https://conversations.im/i/foo/bar.com
+ jid = segments.get(1) + "@" + segments.get(2);
+ }
+ muc = segments.size() > 1 && "j".equalsIgnoreCase(segments.get(0));
+ } else if ("xmpp".equalsIgnoreCase(scheme)) {
+ // sample: xmpp:foo@bar.com
+ muc = "join".equalsIgnoreCase(uri.getQuery());
+ if (uri.getAuthority() != null) {
+ jid = uri.getAuthority();
+ } else {
+ jid = uri.getSchemeSpecificPart().split("\\?")[0];
+ }
+ this.fingerprints = parseFingerprints(uri.getQuery());
+ } else if ("imto".equalsIgnoreCase(scheme)) {
+ // sample: imto://xmpp/foo@bar.com
+ try {
+ jid = URLDecoder.decode(uri.getEncodedPath(), "UTF-8").split("/")[1];
+ } catch (final UnsupportedEncodingException ignored) {
+ jid = null;
+ }
+ } else {
+ try {
+ jid = Jid.fromString(uri.toString()).toBareJid().toString();
+ } catch (final InvalidJidException ignored) {
+ jid = null;
+ }
+ }
+ }
+
+ protected List<Fingerprint> parseFingerprints(String query) {
+ List<Fingerprint> fingerprints = new ArrayList<>();
+ String[] pairs = query == null ? new String[0] : query.split(";");
+ for (String pair : pairs) {
+ String[] parts = pair.split("=", 2);
+ if (parts.length == 2) {
+ String key = parts[0].toLowerCase(Locale.US);
+ String value = parts[1];
+ if (OTR_URI_PARAM.equals(key)) {
+ fingerprints.add(new Fingerprint(FingerprintType.OTR, value));
+ }
+ if (key.startsWith(OMEMO_URI_PARAM)) {
+ try {
+ int id = Integer.parseInt(key.substring(OMEMO_URI_PARAM.length()));
+ fingerprints.add(new Fingerprint(FingerprintType.OMEMO, value, id));
+ } catch (Exception e) {
+ //ignoring invalid device id
+ }
+ }
+ return null;
+ }
+ }
+ return fingerprints;
+ }
+
+ public Jid getJid() {
+ try {
+ return this.jid == null ? null : Jid.fromString(this.jid.toLowerCase());
+ } catch (InvalidJidException e) {
+ return null;
+ }
+ }
+
+ public List<Fingerprint> getFingerprints() {
+ return this.fingerprints;
+ }
+
+ public boolean hasFingerprints() {
+ return fingerprints.size() > 0;
+ }
+
+ public enum FingerprintType {
+ OMEMO,
+ OTR
+ }
+
+ public static class Fingerprint {
+ public final FingerprintType type;
+ public final String fingerprint;
+ public final int deviceId;
+
+ public Fingerprint(FingerprintType type, String fingerprint) {
+ this(type, fingerprint, 0);
+ }
+
+ public Fingerprint(FingerprintType type, String fingerprint, int deviceId) {
+ this.type = type;
+ this.fingerprint = fingerprint;
+ this.deviceId = deviceId;
+ }
+
+ @Override
+ public String toString() {
+ return type.toString() + ": " + fingerprint + (deviceId != 0 ? " " + String.valueOf(deviceId) : "");
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/video/MediaController.java b/src/main/java/de/pixart/messenger/utils/video/MediaController.java
index 7af113931..2b47afade 100644
--- a/src/main/java/de/pixart/messenger/utils/video/MediaController.java
+++ b/src/main/java/de/pixart/messenger/utils/video/MediaController.java
@@ -199,7 +199,7 @@ public class MediaController {
int rotationValue = Integer.valueOf(rotation);
int originalWidth = Integer.valueOf(width);
int originalHeight = Integer.valueOf(height);
- float ratio = (float)video_width / (float)video_height; // 16:9 = 1,7778, 4:3 = 1,3333
+ float ratio = (float) video_width / (float) video_height; // 16:9 = 1,7778, 4:3 = 1,3333
if (video_height > video_width) {
resultHeight = Config.VIDEO_SIZE;
diff --git a/src/main/java/de/pixart/messenger/utils/video/TextureRenderer.java b/src/main/java/de/pixart/messenger/utils/video/TextureRenderer.java
index aa4542fa3..3c53941ea 100644
--- a/src/main/java/de/pixart/messenger/utils/video/TextureRenderer.java
+++ b/src/main/java/de/pixart/messenger/utils/video/TextureRenderer.java
@@ -43,23 +43,23 @@ public class TextureRenderer {
private static final String VERTEX_SHADER =
"uniform mat4 uMVPMatrix;\n" +
- "uniform mat4 uSTMatrix;\n" +
- "attribute vec4 aPosition;\n" +
- "attribute vec4 aTextureCoord;\n" +
- "varying vec2 vTextureCoord;\n" +
- "void main() {\n" +
- " gl_Position = uMVPMatrix * aPosition;\n" +
- " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
- "}\n";
+ "uniform mat4 uSTMatrix;\n" +
+ "attribute vec4 aPosition;\n" +
+ "attribute vec4 aTextureCoord;\n" +
+ "varying vec2 vTextureCoord;\n" +
+ "void main() {\n" +
+ " gl_Position = uMVPMatrix * aPosition;\n" +
+ " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
+ "}\n";
private static final String FRAGMENT_SHADER =
"#extension GL_OES_EGL_image_external : require\n" +
- "precision mediump float;\n" +
- "varying vec2 vTextureCoord;\n" +
- "uniform samplerExternalOES sTexture;\n" +
- "void main() {\n" +
- " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
- "}\n";
+ "precision mediump float;\n" +
+ "varying vec2 vTextureCoord;\n" +
+ "uniform samplerExternalOES sTexture;\n" +
+ "void main() {\n" +
+ " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
+ "}\n";
private float[] mMVPMatrix = new float[16];
private float[] mSTMatrix = new float[16];
diff --git a/src/main/java/de/pixart/messenger/utils/video/Track.java b/src/main/java/de/pixart/messenger/utils/video/Track.java
index 347c65490..18111a675 100644
--- a/src/main/java/de/pixart/messenger/utils/video/Track.java
+++ b/src/main/java/de/pixart/messenger/utils/video/Track.java
@@ -70,7 +70,7 @@ public class Track {
public Track(int id, MediaFormat format, boolean isAudio) throws Exception {
trackId = id;
if (!isAudio) {
- sampleDurations.add((long)3015);
+ sampleDurations.add((long) 3015);
duration = 3015;
width = format.getInteger(MediaFormat.KEY_WIDTH);
height = format.getInteger(MediaFormat.KEY_HEIGHT);
@@ -136,7 +136,7 @@ public class Track {
sampleDescriptionBox.addBox(visualSampleEntry);
}
} else {
- sampleDurations.add((long)1024);
+ sampleDurations.add((long) 1024);
duration = 1024;
isAudio = true;
volume = 1;
@@ -167,7 +167,7 @@ public class Track {
AudioSpecificConfig audioSpecificConfig = new AudioSpecificConfig();
audioSpecificConfig.setAudioObjectType(2);
- audioSpecificConfig.setSamplingFrequencyIndex(samplingFrequencyIndexMap.get((int)audioSampleEntry.getSampleRate()));
+ audioSpecificConfig.setSamplingFrequencyIndex(samplingFrequencyIndexMap.get((int) audioSampleEntry.getSampleRate()));
audioSpecificConfig.setChannelConfiguration(audioSampleEntry.getChannelCount());
decoderConfigDescriptor.setAudioSpecificInfo(audioSpecificConfig);
diff --git a/src/main/java/de/pixart/messenger/xml/Element.java b/src/main/java/de/pixart/messenger/xml/Element.java
index ca8cdf0a7..7ed08a483 100644
--- a/src/main/java/de/pixart/messenger/xml/Element.java
+++ b/src/main/java/de/pixart/messenger/xml/Element.java
@@ -12,178 +12,178 @@ import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
public class Element {
- private final String name;
- private Hashtable<String, String> attributes = new Hashtable<>();
- private String content;
- protected List<Element> children = new ArrayList<>();
-
- public Element(String name) {
- this.name = name;
- }
-
- public Element(String name, String xmlns) {
- this.name = name;
- this.setAttribute("xmlns", xmlns);
- }
-
- public Element addChild(Element child) {
- this.content = null;
- children.add(child);
- return child;
- }
-
- public Element addChild(String name) {
- this.content = null;
- Element child = new Element(name);
- children.add(child);
- return child;
- }
-
- public Element addChild(String name, String xmlns) {
- this.content = null;
- Element child = new Element(name);
- child.setAttribute("xmlns", xmlns);
- children.add(child);
- return child;
- }
-
- public Element setContent(String content) {
- this.content = content;
- this.children.clear();
- return this;
- }
-
- public Element findChild(String name) {
- for (Element child : this.children) {
- if (child.getName().equals(name)) {
- return child;
- }
- }
- return null;
- }
-
- public String findChildContent(String name) {
- Element element = findChild(name);
- return element == null ? null : element.getContent();
- }
-
- public Element findChild(String name, String xmlns) {
- for (Element child : this.children) {
- if (name.equals(child.getName()) && xmlns.equals(child.getAttribute("xmlns"))) {
- return child;
- }
- }
- return null;
- }
-
- public String findChildContent(String name, String xmlns) {
- Element element = findChild(name,xmlns);
- return element == null ? null : element.getContent();
- }
-
- public boolean hasChild(final String name) {
- return findChild(name) != null;
- }
-
- public boolean hasChild(final String name, final String xmlns) {
- return findChild(name, xmlns) != null;
- }
-
- public List<Element> getChildren() {
- return this.children;
- }
-
- public Element setChildren(List<Element> children) {
- this.children = children;
- return this;
- }
-
- public final String getContent() {
- return content;
- }
-
- public Element setAttribute(String name, String value) {
- if (name != null && value != null) {
- this.attributes.put(name, value);
- }
- return this;
- }
-
- public Element setAttributes(Hashtable<String, String> attributes) {
- this.attributes = attributes;
- return this;
- }
-
- public String getAttribute(String name) {
- if (this.attributes.containsKey(name)) {
- return this.attributes.get(name);
- } else {
- return null;
- }
- }
-
- public Jid getAttributeAsJid(String name) {
- final String jid = this.getAttribute(name);
- if (jid != null && !jid.isEmpty()) {
- try {
- return Jid.fromString(jid);
- } catch (final InvalidJidException e) {
- Log.e(Config.LOGTAG, "could not parse jid " + jid);
- return null;
- }
- }
- return null;
- }
-
- public Hashtable<String, String> getAttributes() {
- return this.attributes;
- }
-
- public String toString() {
- StringBuilder elementOutput = new StringBuilder();
- if ((content == null) && (children.size() == 0)) {
- Tag emptyTag = Tag.empty(name);
- emptyTag.setAtttributes(this.attributes);
- elementOutput.append(emptyTag.toString());
- } else {
- Tag startTag = Tag.start(name);
- startTag.setAtttributes(this.attributes);
- elementOutput.append(startTag);
- if (content != null) {
- elementOutput.append(XmlHelper.encodeEntities(content));
- } else {
- for (Element child : children) {
- elementOutput.append(child.toString());
- }
- }
- Tag endTag = Tag.end(name);
- elementOutput.append(endTag);
- }
- return elementOutput.toString();
- }
-
- public final String getName() {
- return name;
- }
-
- public void clearChildren() {
- this.children.clear();
- }
-
- public void setAttribute(String name, long value) {
- this.setAttribute(name, Long.toString(value));
- }
-
- public void setAttribute(String name, int value) {
- this.setAttribute(name, Integer.toString(value));
- }
-
- public boolean getAttributeAsBoolean(String name) {
- String attr = getAttribute(name);
- return (attr != null && (attr.equalsIgnoreCase("true") || attr.equalsIgnoreCase("1")));
- }
-
- public String getNamespace() {
- return getAttribute("xmlns");
- }
+ private final String name;
+ private Hashtable<String, String> attributes = new Hashtable<>();
+ private String content;
+ protected List<Element> children = new ArrayList<>();
+
+ public Element(String name) {
+ this.name = name;
+ }
+
+ public Element(String name, String xmlns) {
+ this.name = name;
+ this.setAttribute("xmlns", xmlns);
+ }
+
+ public Element addChild(Element child) {
+ this.content = null;
+ children.add(child);
+ return child;
+ }
+
+ public Element addChild(String name) {
+ this.content = null;
+ Element child = new Element(name);
+ children.add(child);
+ return child;
+ }
+
+ public Element addChild(String name, String xmlns) {
+ this.content = null;
+ Element child = new Element(name);
+ child.setAttribute("xmlns", xmlns);
+ children.add(child);
+ return child;
+ }
+
+ public Element setContent(String content) {
+ this.content = content;
+ this.children.clear();
+ return this;
+ }
+
+ public Element findChild(String name) {
+ for (Element child : this.children) {
+ if (child.getName().equals(name)) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ public String findChildContent(String name) {
+ Element element = findChild(name);
+ return element == null ? null : element.getContent();
+ }
+
+ public Element findChild(String name, String xmlns) {
+ for (Element child : this.children) {
+ if (name.equals(child.getName()) && xmlns.equals(child.getAttribute("xmlns"))) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ public String findChildContent(String name, String xmlns) {
+ Element element = findChild(name, xmlns);
+ return element == null ? null : element.getContent();
+ }
+
+ public boolean hasChild(final String name) {
+ return findChild(name) != null;
+ }
+
+ public boolean hasChild(final String name, final String xmlns) {
+ return findChild(name, xmlns) != null;
+ }
+
+ public List<Element> getChildren() {
+ return this.children;
+ }
+
+ public Element setChildren(List<Element> children) {
+ this.children = children;
+ return this;
+ }
+
+ public final String getContent() {
+ return content;
+ }
+
+ public Element setAttribute(String name, String value) {
+ if (name != null && value != null) {
+ this.attributes.put(name, value);
+ }
+ return this;
+ }
+
+ public Element setAttributes(Hashtable<String, String> attributes) {
+ this.attributes = attributes;
+ return this;
+ }
+
+ public String getAttribute(String name) {
+ if (this.attributes.containsKey(name)) {
+ return this.attributes.get(name);
+ } else {
+ return null;
+ }
+ }
+
+ public Jid getAttributeAsJid(String name) {
+ final String jid = this.getAttribute(name);
+ if (jid != null && !jid.isEmpty()) {
+ try {
+ return Jid.fromString(jid);
+ } catch (final InvalidJidException e) {
+ Log.e(Config.LOGTAG, "could not parse jid " + jid);
+ return null;
+ }
+ }
+ return null;
+ }
+
+ public Hashtable<String, String> getAttributes() {
+ return this.attributes;
+ }
+
+ public String toString() {
+ StringBuilder elementOutput = new StringBuilder();
+ if ((content == null) && (children.size() == 0)) {
+ Tag emptyTag = Tag.empty(name);
+ emptyTag.setAtttributes(this.attributes);
+ elementOutput.append(emptyTag.toString());
+ } else {
+ Tag startTag = Tag.start(name);
+ startTag.setAtttributes(this.attributes);
+ elementOutput.append(startTag);
+ if (content != null) {
+ elementOutput.append(XmlHelper.encodeEntities(content));
+ } else {
+ for (Element child : children) {
+ elementOutput.append(child.toString());
+ }
+ }
+ Tag endTag = Tag.end(name);
+ elementOutput.append(endTag);
+ }
+ return elementOutput.toString();
+ }
+
+ public final String getName() {
+ return name;
+ }
+
+ public void clearChildren() {
+ this.children.clear();
+ }
+
+ public void setAttribute(String name, long value) {
+ this.setAttribute(name, Long.toString(value));
+ }
+
+ public void setAttribute(String name, int value) {
+ this.setAttribute(name, Integer.toString(value));
+ }
+
+ public boolean getAttributeAsBoolean(String name) {
+ String attr = getAttribute(name);
+ return (attr != null && (attr.equalsIgnoreCase("true") || attr.equalsIgnoreCase("1")));
+ }
+
+ public String getNamespace() {
+ return getAttribute("xmlns");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xml/Tag.java b/src/main/java/de/pixart/messenger/xml/Tag.java
index b604140f0..8e6944a40 100644
--- a/src/main/java/de/pixart/messenger/xml/Tag.java
+++ b/src/main/java/de/pixart/messenger/xml/Tag.java
@@ -8,97 +8,97 @@ import java.util.Set;
import de.pixart.messenger.utils.XmlHelper;
public class Tag {
- public static final int NO = -1;
- public static final int START = 0;
- public static final int END = 1;
- public static final int EMPTY = 2;
-
- protected int type;
- protected String name;
- protected Hashtable<String, String> attributes = new Hashtable<String, String>();
-
- protected Tag(int type, String name) {
- this.type = type;
- this.name = name;
- }
-
- public static Tag no(String text) {
- return new Tag(NO, text);
- }
-
- public static Tag start(String name) {
- return new Tag(START, name);
- }
-
- public static Tag end(String name) {
- return new Tag(END, name);
- }
-
- public static Tag empty(String name) {
- return new Tag(EMPTY, name);
- }
-
- public String getName() {
- return name;
- }
-
- public String getAttribute(String attrName) {
- return this.attributes.get(attrName);
- }
-
- public Tag setAttribute(String attrName, String attrValue) {
- this.attributes.put(attrName, attrValue);
- return this;
- }
-
- public Tag setAtttributes(Hashtable<String, String> attributes) {
- this.attributes = attributes;
- return this;
- }
-
- public boolean isStart(String needle) {
- if (needle == null)
- return false;
- return (this.type == START) && (needle.equals(this.name));
- }
-
- public boolean isEnd(String needle) {
- if (needle == null)
- return false;
- return (this.type == END) && (needle.equals(this.name));
- }
-
- public boolean isNo() {
- return (this.type == NO);
- }
-
- public String toString() {
- StringBuilder tagOutput = new StringBuilder();
- tagOutput.append('<');
- if (type == END) {
- tagOutput.append('/');
- }
- tagOutput.append(name);
- if (type != END) {
- Set<Entry<String, String>> attributeSet = attributes.entrySet();
- Iterator<Entry<String, String>> it = attributeSet.iterator();
- while (it.hasNext()) {
- Entry<String, String> entry = it.next();
- tagOutput.append(' ');
- tagOutput.append(entry.getKey());
- tagOutput.append("=\"");
- tagOutput.append(XmlHelper.encodeEntities(entry.getValue()));
- tagOutput.append('"');
- }
- }
- if (type == EMPTY) {
- tagOutput.append('/');
- }
- tagOutput.append('>');
- return tagOutput.toString();
- }
-
- public Hashtable<String, String> getAttributes() {
- return this.attributes;
- }
+ public static final int NO = -1;
+ public static final int START = 0;
+ public static final int END = 1;
+ public static final int EMPTY = 2;
+
+ protected int type;
+ protected String name;
+ protected Hashtable<String, String> attributes = new Hashtable<String, String>();
+
+ protected Tag(int type, String name) {
+ this.type = type;
+ this.name = name;
+ }
+
+ public static Tag no(String text) {
+ return new Tag(NO, text);
+ }
+
+ public static Tag start(String name) {
+ return new Tag(START, name);
+ }
+
+ public static Tag end(String name) {
+ return new Tag(END, name);
+ }
+
+ public static Tag empty(String name) {
+ return new Tag(EMPTY, name);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getAttribute(String attrName) {
+ return this.attributes.get(attrName);
+ }
+
+ public Tag setAttribute(String attrName, String attrValue) {
+ this.attributes.put(attrName, attrValue);
+ return this;
+ }
+
+ public Tag setAtttributes(Hashtable<String, String> attributes) {
+ this.attributes = attributes;
+ return this;
+ }
+
+ public boolean isStart(String needle) {
+ if (needle == null)
+ return false;
+ return (this.type == START) && (needle.equals(this.name));
+ }
+
+ public boolean isEnd(String needle) {
+ if (needle == null)
+ return false;
+ return (this.type == END) && (needle.equals(this.name));
+ }
+
+ public boolean isNo() {
+ return (this.type == NO);
+ }
+
+ public String toString() {
+ StringBuilder tagOutput = new StringBuilder();
+ tagOutput.append('<');
+ if (type == END) {
+ tagOutput.append('/');
+ }
+ tagOutput.append(name);
+ if (type != END) {
+ Set<Entry<String, String>> attributeSet = attributes.entrySet();
+ Iterator<Entry<String, String>> it = attributeSet.iterator();
+ while (it.hasNext()) {
+ Entry<String, String> entry = it.next();
+ tagOutput.append(' ');
+ tagOutput.append(entry.getKey());
+ tagOutput.append("=\"");
+ tagOutput.append(XmlHelper.encodeEntities(entry.getValue()));
+ tagOutput.append('"');
+ }
+ }
+ if (type == EMPTY) {
+ tagOutput.append('/');
+ }
+ tagOutput.append('>');
+ return tagOutput.toString();
+ }
+
+ public Hashtable<String, String> getAttributes() {
+ return this.attributes;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xml/TagWriter.java b/src/main/java/de/pixart/messenger/xml/TagWriter.java
index 61220773a..0a663ce1b 100644
--- a/src/main/java/de/pixart/messenger/xml/TagWriter.java
+++ b/src/main/java/de/pixart/messenger/xml/TagWriter.java
@@ -9,96 +9,96 @@ import de.pixart.messenger.xmpp.stanzas.AbstractStanza;
public class TagWriter {
- private OutputStreamWriter outputStream;
- private boolean finshed = false;
- private LinkedBlockingQueue<AbstractStanza> writeQueue = new LinkedBlockingQueue<AbstractStanza>();
- private Thread asyncStanzaWriter = new Thread() {
- private boolean shouldStop = false;
+ private OutputStreamWriter outputStream;
+ private boolean finshed = false;
+ private LinkedBlockingQueue<AbstractStanza> writeQueue = new LinkedBlockingQueue<AbstractStanza>();
+ private Thread asyncStanzaWriter = new Thread() {
+ private boolean shouldStop = false;
- @Override
- public void run() {
- while (!shouldStop) {
- if ((finshed) && (writeQueue.size() == 0)) {
- return;
- }
- try {
- AbstractStanza output = writeQueue.take();
- outputStream.write(output.toString());
- outputStream.flush();
- } catch (Exception e) {
- shouldStop = true;
- }
- }
- }
- };
+ @Override
+ public void run() {
+ while (!shouldStop) {
+ if ((finshed) && (writeQueue.size() == 0)) {
+ return;
+ }
+ try {
+ AbstractStanza output = writeQueue.take();
+ outputStream.write(output.toString());
+ outputStream.flush();
+ } catch (Exception e) {
+ shouldStop = true;
+ }
+ }
+ }
+ };
- public TagWriter() {
- }
+ public TagWriter() {
+ }
- public void setOutputStream(OutputStream out) throws IOException {
- if (out == null) {
- throw new IOException();
- }
- this.outputStream = new OutputStreamWriter(out);
- }
+ public void setOutputStream(OutputStream out) throws IOException {
+ if (out == null) {
+ throw new IOException();
+ }
+ this.outputStream = new OutputStreamWriter(out);
+ }
- public TagWriter beginDocument() throws IOException {
- if (outputStream == null) {
- throw new IOException("output stream was null");
- }
- outputStream.write("<?xml version='1.0'?>");
- outputStream.flush();
- return this;
- }
+ public TagWriter beginDocument() throws IOException {
+ if (outputStream == null) {
+ throw new IOException("output stream was null");
+ }
+ outputStream.write("<?xml version='1.0'?>");
+ outputStream.flush();
+ return this;
+ }
- public TagWriter writeTag(Tag tag) throws IOException {
- if (outputStream == null) {
- throw new IOException("output stream was null");
- }
- outputStream.write(tag.toString());
- outputStream.flush();
- return this;
- }
+ public TagWriter writeTag(Tag tag) throws IOException {
+ if (outputStream == null) {
+ throw new IOException("output stream was null");
+ }
+ outputStream.write(tag.toString());
+ outputStream.flush();
+ return this;
+ }
- public TagWriter writeElement(Element element) throws IOException {
- if (outputStream == null) {
- throw new IOException("output stream was null");
- }
- outputStream.write(element.toString());
- outputStream.flush();
- return this;
- }
+ public TagWriter writeElement(Element element) throws IOException {
+ if (outputStream == null) {
+ throw new IOException("output stream was null");
+ }
+ outputStream.write(element.toString());
+ outputStream.flush();
+ return this;
+ }
- public TagWriter writeStanzaAsync(AbstractStanza stanza) {
- if (finshed) {
- return this;
- } else {
- if (!asyncStanzaWriter.isAlive()) {
- try {
- asyncStanzaWriter.start();
- } catch (IllegalThreadStateException e) {
- // already started
- }
- }
- writeQueue.add(stanza);
- return this;
- }
- }
+ public TagWriter writeStanzaAsync(AbstractStanza stanza) {
+ if (finshed) {
+ return this;
+ } else {
+ if (!asyncStanzaWriter.isAlive()) {
+ try {
+ asyncStanzaWriter.start();
+ } catch (IllegalThreadStateException e) {
+ // already started
+ }
+ }
+ writeQueue.add(stanza);
+ return this;
+ }
+ }
- public void finish() {
- this.finshed = true;
- }
+ public void finish() {
+ this.finshed = true;
+ }
- public boolean finished() {
- return (this.writeQueue.size() == 0);
- }
+ public boolean finished() {
+ return (this.writeQueue.size() == 0);
+ }
- public boolean isActive() {
- return outputStream != null;
- }
+ public boolean isActive() {
+ return outputStream != null;
+ }
- public void forceClose() {
- finish();
- outputStream = null;
- }
+ public void forceClose() {
+ finish();
+ outputStream = null;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xml/XmlReader.java b/src/main/java/de/pixart/messenger/xml/XmlReader.java
index 0b7796c6c..3c8cfa1f2 100644
--- a/src/main/java/de/pixart/messenger/xml/XmlReader.java
+++ b/src/main/java/de/pixart/messenger/xml/XmlReader.java
@@ -15,111 +15,111 @@ import java.io.InputStreamReader;
import de.pixart.messenger.Config;
public class XmlReader {
- private XmlPullParser parser;
- private PowerManager.WakeLock wakeLock;
- private InputStream is;
+ private XmlPullParser parser;
+ private PowerManager.WakeLock wakeLock;
+ private InputStream is;
- public XmlReader(WakeLock wakeLock) {
- this.parser = Xml.newPullParser();
- try {
- this.parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- } catch (XmlPullParserException e) {
- Log.d(Config.LOGTAG, "error setting namespace feature on parser");
- }
- this.wakeLock = wakeLock;
- }
+ public XmlReader(WakeLock wakeLock) {
+ this.parser = Xml.newPullParser();
+ try {
+ this.parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ } catch (XmlPullParserException e) {
+ Log.d(Config.LOGTAG, "error setting namespace feature on parser");
+ }
+ this.wakeLock = wakeLock;
+ }
- public void setInputStream(InputStream inputStream) throws IOException {
- if (inputStream == null) {
- throw new IOException();
- }
- this.is = inputStream;
- try {
- parser.setInput(new InputStreamReader(this.is));
- } catch (XmlPullParserException e) {
- throw new IOException("error resetting parser");
- }
- }
+ public void setInputStream(InputStream inputStream) throws IOException {
+ if (inputStream == null) {
+ throw new IOException();
+ }
+ this.is = inputStream;
+ try {
+ parser.setInput(new InputStreamReader(this.is));
+ } catch (XmlPullParserException e) {
+ throw new IOException("error resetting parser");
+ }
+ }
- public void reset() throws IOException {
- if (this.is == null) {
- throw new IOException();
- }
- try {
- parser.setInput(new InputStreamReader(this.is));
- } catch (XmlPullParserException e) {
- throw new IOException("error resetting parser");
- }
- }
+ public void reset() throws IOException {
+ if (this.is == null) {
+ throw new IOException();
+ }
+ try {
+ parser.setInput(new InputStreamReader(this.is));
+ } catch (XmlPullParserException e) {
+ throw new IOException("error resetting parser");
+ }
+ }
- public Tag readTag() throws XmlPullParserException, IOException {
- if (wakeLock.isHeld()) {
- try {
- wakeLock.release();
- } catch (RuntimeException re) {
- Log.d(Config.LOGTAG,"runtime exception releasing wakelock before reading tag "+re.getMessage());
- }
- }
- try {
- while (this.is != null && parser.next() != XmlPullParser.END_DOCUMENT) {
- wakeLock.acquire();
- if (parser.getEventType() == XmlPullParser.START_TAG) {
- Tag tag = Tag.start(parser.getName());
- for (int i = 0; i < parser.getAttributeCount(); ++i) {
- tag.setAttribute(parser.getAttributeName(i),
- parser.getAttributeValue(i));
- }
- String xmlns = parser.getNamespace();
- if (xmlns != null) {
- tag.setAttribute("xmlns", xmlns);
- }
- return tag;
- } else if (parser.getEventType() == XmlPullParser.END_TAG) {
- return Tag.end(parser.getName());
- } else if (parser.getEventType() == XmlPullParser.TEXT) {
- return Tag.no(parser.getText());
- }
- }
+ public Tag readTag() throws XmlPullParserException, IOException {
+ if (wakeLock.isHeld()) {
+ try {
+ wakeLock.release();
+ } catch (RuntimeException re) {
+ Log.d(Config.LOGTAG, "runtime exception releasing wakelock before reading tag " + re.getMessage());
+ }
+ }
+ try {
+ while (this.is != null && parser.next() != XmlPullParser.END_DOCUMENT) {
+ wakeLock.acquire();
+ if (parser.getEventType() == XmlPullParser.START_TAG) {
+ Tag tag = Tag.start(parser.getName());
+ for (int i = 0; i < parser.getAttributeCount(); ++i) {
+ tag.setAttribute(parser.getAttributeName(i),
+ parser.getAttributeValue(i));
+ }
+ String xmlns = parser.getNamespace();
+ if (xmlns != null) {
+ tag.setAttribute("xmlns", xmlns);
+ }
+ return tag;
+ } else if (parser.getEventType() == XmlPullParser.END_TAG) {
+ return Tag.end(parser.getName());
+ } else if (parser.getEventType() == XmlPullParser.TEXT) {
+ return Tag.no(parser.getText());
+ }
+ }
- } catch (Throwable throwable) {
- throw new IOException("xml parser mishandled "+throwable.getClass().getName(), throwable);
- } finally {
- if (wakeLock.isHeld()) {
- try {
- wakeLock.release();
- } catch (RuntimeException re) {
- Log.d(Config.LOGTAG,"runtime exception releasing wakelock after exception "+re.getMessage());
- }
- }
- }
- return null;
- }
+ } catch (Throwable throwable) {
+ throw new IOException("xml parser mishandled " + throwable.getClass().getName(), throwable);
+ } finally {
+ if (wakeLock.isHeld()) {
+ try {
+ wakeLock.release();
+ } catch (RuntimeException re) {
+ Log.d(Config.LOGTAG, "runtime exception releasing wakelock after exception " + re.getMessage());
+ }
+ }
+ }
+ return null;
+ }
- public Element readElement(Tag currentTag) throws XmlPullParserException,
- IOException {
- Element element = new Element(currentTag.getName());
- element.setAttributes(currentTag.getAttributes());
- Tag nextTag = this.readTag();
- if (nextTag == null) {
- throw new IOException("interrupted mid tag");
- }
- if (nextTag.isNo()) {
- element.setContent(nextTag.getName());
- nextTag = this.readTag();
- if (nextTag == null) {
- throw new IOException("interrupted mid tag");
- }
- }
- while (!nextTag.isEnd(element.getName())) {
- if (!nextTag.isNo()) {
- Element child = this.readElement(nextTag);
- element.addChild(child);
- }
- nextTag = this.readTag();
- if (nextTag == null) {
- throw new IOException("interrupted mid tag");
- }
- }
- return element;
- }
+ public Element readElement(Tag currentTag) throws XmlPullParserException,
+ IOException {
+ Element element = new Element(currentTag.getName());
+ element.setAttributes(currentTag.getAttributes());
+ Tag nextTag = this.readTag();
+ if (nextTag == null) {
+ throw new IOException("interrupted mid tag");
+ }
+ if (nextTag.isNo()) {
+ element.setContent(nextTag.getName());
+ nextTag = this.readTag();
+ if (nextTag == null) {
+ throw new IOException("interrupted mid tag");
+ }
+ }
+ while (!nextTag.isEnd(element.getName())) {
+ if (!nextTag.isNo()) {
+ Element child = this.readElement(nextTag);
+ element.addChild(child);
+ }
+ nextTag = this.readTag();
+ if (nextTag == null) {
+ throw new IOException("interrupted mid tag");
+ }
+ }
+ return element;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnAdvancedStreamFeaturesLoaded.java b/src/main/java/de/pixart/messenger/xmpp/OnAdvancedStreamFeaturesLoaded.java
index 572b61bc1..3ca11c068 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnAdvancedStreamFeaturesLoaded.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnAdvancedStreamFeaturesLoaded.java
@@ -3,5 +3,5 @@ package de.pixart.messenger.xmpp;
import de.pixart.messenger.entities.Account;
public interface OnAdvancedStreamFeaturesLoaded {
- public void onAdvancedStreamFeaturesAvailable(final Account account);
+ public void onAdvancedStreamFeaturesAvailable(final Account account);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnBindListener.java b/src/main/java/de/pixart/messenger/xmpp/OnBindListener.java
index cafc3fadb..710a807dd 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnBindListener.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnBindListener.java
@@ -3,5 +3,5 @@ package de.pixart.messenger.xmpp;
import de.pixart.messenger.entities.Account;
public interface OnBindListener {
- public void onBind(Account account);
+ public void onBind(Account account);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnContactStatusChanged.java b/src/main/java/de/pixart/messenger/xmpp/OnContactStatusChanged.java
index be1c51b72..66cd24858 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnContactStatusChanged.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnContactStatusChanged.java
@@ -3,5 +3,5 @@ package de.pixart.messenger.xmpp;
import de.pixart.messenger.entities.Contact;
public interface OnContactStatusChanged {
- public void onContactStatusChanged(final Contact contact, final boolean online);
+ public void onContactStatusChanged(final Contact contact, final boolean online);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnIqPacketReceived.java b/src/main/java/de/pixart/messenger/xmpp/OnIqPacketReceived.java
index b07b6258e..797eba155 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnIqPacketReceived.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnIqPacketReceived.java
@@ -4,5 +4,5 @@ import de.pixart.messenger.entities.Account;
import de.pixart.messenger.xmpp.stanzas.IqPacket;
public interface OnIqPacketReceived extends PacketReceived {
- public void onIqPacketReceived(Account account, IqPacket packet);
+ public void onIqPacketReceived(Account account, IqPacket packet);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnKeyStatusUpdated.java b/src/main/java/de/pixart/messenger/xmpp/OnKeyStatusUpdated.java
index 3727d37d7..925a2544d 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnKeyStatusUpdated.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnKeyStatusUpdated.java
@@ -3,5 +3,5 @@ package de.pixart.messenger.xmpp;
import de.pixart.messenger.crypto.axolotl.AxolotlService;
public interface OnKeyStatusUpdated {
- public void onKeyStatusUpdated(AxolotlService.FetchStatus report);
+ public void onKeyStatusUpdated(AxolotlService.FetchStatus report);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnMessageAcknowledged.java b/src/main/java/de/pixart/messenger/xmpp/OnMessageAcknowledged.java
index 3ab3bd75a..7ff5658a8 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnMessageAcknowledged.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnMessageAcknowledged.java
@@ -3,5 +3,5 @@ package de.pixart.messenger.xmpp;
import de.pixart.messenger.entities.Account;
public interface OnMessageAcknowledged {
- public void onMessageAcknowledged(Account account, String id);
+ public void onMessageAcknowledged(Account account, String id);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnMessagePacketReceived.java b/src/main/java/de/pixart/messenger/xmpp/OnMessagePacketReceived.java
index 9a0f3bb2b..d4d54f8df 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnMessagePacketReceived.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnMessagePacketReceived.java
@@ -4,5 +4,5 @@ import de.pixart.messenger.entities.Account;
import de.pixart.messenger.xmpp.stanzas.MessagePacket;
public interface OnMessagePacketReceived extends PacketReceived {
- public void onMessagePacketReceived(Account account, MessagePacket packet);
+ public void onMessagePacketReceived(Account account, MessagePacket packet);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnPresencePacketReceived.java b/src/main/java/de/pixart/messenger/xmpp/OnPresencePacketReceived.java
index 5419443a2..2df725ff7 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnPresencePacketReceived.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnPresencePacketReceived.java
@@ -4,5 +4,5 @@ import de.pixart.messenger.entities.Account;
import de.pixart.messenger.xmpp.stanzas.PresencePacket;
public interface OnPresencePacketReceived extends PacketReceived {
- public void onPresencePacketReceived(Account account, PresencePacket packet);
+ public void onPresencePacketReceived(Account account, PresencePacket packet);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnStatusChanged.java b/src/main/java/de/pixart/messenger/xmpp/OnStatusChanged.java
index 7f1e07b9c..40b7b313a 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnStatusChanged.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnStatusChanged.java
@@ -3,5 +3,5 @@ package de.pixart.messenger.xmpp;
import de.pixart.messenger.entities.Account;
public interface OnStatusChanged {
- public void onStatusChanged(Account account);
+ public void onStatusChanged(Account account);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnUpdateBlocklist.java b/src/main/java/de/pixart/messenger/xmpp/OnUpdateBlocklist.java
index 4b8d90ec7..bf02e2f29 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnUpdateBlocklist.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnUpdateBlocklist.java
@@ -1,13 +1,13 @@
package de.pixart.messenger.xmpp;
public interface OnUpdateBlocklist {
- // Use an enum instead of a boolean to make sure we don't run into the boolean trap
- // (`onUpdateBlocklist(true)' doesn't read well, and could be confusing).
- public static enum Status {
- BLOCKED,
- UNBLOCKED
- }
+ // Use an enum instead of a boolean to make sure we don't run into the boolean trap
+ // (`onUpdateBlocklist(true)' doesn't read well, and could be confusing).
+ public static enum Status {
+ BLOCKED,
+ UNBLOCKED
+ }
- @SuppressWarnings("MethodNameSameAsClassName")
- public void OnUpdateBlocklist(final Status status);
+ @SuppressWarnings("MethodNameSameAsClassName")
+ public void OnUpdateBlocklist(final Status status);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java
index 2642ee84a..bcb5904a7 100644
--- a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java
+++ b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java
@@ -94,784 +94,783 @@ import de.pixart.messenger.xmpp.stanzas.streammgmt.ResumePacket;
public class XmppConnection implements Runnable {
- private static final int PACKET_IQ = 0;
- private static final int PACKET_MESSAGE = 1;
- private static final int PACKET_PRESENCE = 2;
- protected Account account;
- private final WakeLock wakeLock;
- private Socket socket;
- private XmlReader tagReader;
- private TagWriter tagWriter;
- private final Features features = new Features(this);
- private boolean needsBinding = true;
- private boolean shouldAuthenticate = true;
- private Element streamFeatures;
- private final HashMap<Jid, ServiceDiscoveryResult> disco = new HashMap<>();
-
- private String streamId = null;
- private int smVersion = 3;
- private final SparseArray<AbstractAcknowledgeableStanza> mStanzaQueue = new SparseArray<>();
-
- private int stanzasReceived = 0;
- private int stanzasSent = 0;
- private long lastPacketReceived = 0;
- private long lastPingSent = 0;
- private long lastConnect = 0;
- private long lastSessionStarted = 0;
- private long lastDiscoStarted = 0;
- private AtomicInteger mPendingServiceDiscoveries = new AtomicInteger(0);
- private AtomicBoolean mWaitForDisco = new AtomicBoolean(true);
- private boolean mInteractive = false;
- private int attempt = 0;
+ private static final int PACKET_IQ = 0;
+ private static final int PACKET_MESSAGE = 1;
+ private static final int PACKET_PRESENCE = 2;
+ protected Account account;
+ private final WakeLock wakeLock;
+ private Socket socket;
+ private XmlReader tagReader;
+ private TagWriter tagWriter;
+ private final Features features = new Features(this);
+ private boolean needsBinding = true;
+ private boolean shouldAuthenticate = true;
+ private Element streamFeatures;
+ private final HashMap<Jid, ServiceDiscoveryResult> disco = new HashMap<>();
+
+ private String streamId = null;
+ private int smVersion = 3;
+ private final SparseArray<AbstractAcknowledgeableStanza> mStanzaQueue = new SparseArray<>();
+
+ private int stanzasReceived = 0;
+ private int stanzasSent = 0;
+ private long lastPacketReceived = 0;
+ private long lastPingSent = 0;
+ private long lastConnect = 0;
+ private long lastSessionStarted = 0;
+ private long lastDiscoStarted = 0;
+ private AtomicInteger mPendingServiceDiscoveries = new AtomicInteger(0);
+ private AtomicBoolean mWaitForDisco = new AtomicBoolean(true);
+ private boolean mInteractive = false;
+ private int attempt = 0;
public static String errorMessage = null;
- private final Hashtable<String, Pair<IqPacket, OnIqPacketReceived>> packetCallbacks = new Hashtable<>();
- private OnPresencePacketReceived presenceListener = null;
- private OnJinglePacketReceived jingleListener = null;
- private OnIqPacketReceived unregisteredIqListener = null;
- private OnMessagePacketReceived messageListener = null;
- private OnStatusChanged statusListener = null;
- private OnBindListener bindListener = null;
- private final ArrayList<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners = new ArrayList<>();
- private OnMessageAcknowledged acknowledgedListener = null;
- private XmppConnectionService mXmppConnectionService = null;
+ private final Hashtable<String, Pair<IqPacket, OnIqPacketReceived>> packetCallbacks = new Hashtable<>();
+ private OnPresencePacketReceived presenceListener = null;
+ private OnJinglePacketReceived jingleListener = null;
+ private OnIqPacketReceived unregisteredIqListener = null;
+ private OnMessagePacketReceived messageListener = null;
+ private OnStatusChanged statusListener = null;
+ private OnBindListener bindListener = null;
+ private final ArrayList<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners = new ArrayList<>();
+ private OnMessageAcknowledged acknowledgedListener = null;
+ private XmppConnectionService mXmppConnectionService = null;
private EditAccountActivity mEditAccountActivity = null;
- private SaslMechanism saslMechanism;
+ private SaslMechanism saslMechanism;
private class MyKeyManager implements X509KeyManager {
- @Override
- public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
- return account.getPrivateKeyAlias();
- }
-
- @Override
- public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
- return null;
- }
-
- @Override
- public X509Certificate[] getCertificateChain(String alias) {
- Log.d(Config.LOGTAG,"getting certificate chain");
- try {
- return KeyChain.getCertificateChain(mXmppConnectionService, alias);
- } catch (Exception e) {
- Log.d(Config.LOGTAG,e.getMessage());
- return new X509Certificate[0];
- }
- }
-
- @Override
- public String[] getClientAliases(String s, Principal[] principals) {
- return new String[0];
- }
-
- @Override
- public String[] getServerAliases(String s, Principal[] principals) {
- return new String[0];
- }
-
- @Override
- public PrivateKey getPrivateKey(String alias) {
- try {
- return KeyChain.getPrivateKey(mXmppConnectionService, alias);
- } catch (Exception e) {
- return null;
- }
- }
- }
-
- 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) {
- account.setOption(Account.OPTION_REGISTER, false);
- forceCloseSocket();
- changeStatus(Account.State.REGISTRATION_SUCCESSFUL);
- } else {
- final List<String> PASSWORD_TOO_WEAK_MSGS = Arrays.asList(
- "The password is too weak",
- "Please use a longer password.");
- final Element error = packet.findChild("error");
- Account.State state = Account.State.REGISTRATION_FAILED;
+ @Override
+ public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
+ return account.getPrivateKeyAlias();
+ }
+
+ @Override
+ public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
+ return null;
+ }
+
+ @Override
+ public X509Certificate[] getCertificateChain(String alias) {
+ Log.d(Config.LOGTAG, "getting certificate chain");
+ try {
+ return KeyChain.getCertificateChain(mXmppConnectionService, alias);
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, e.getMessage());
+ return new X509Certificate[0];
+ }
+ }
+
+ @Override
+ public String[] getClientAliases(String s, Principal[] principals) {
+ return new String[0];
+ }
+
+ @Override
+ public String[] getServerAliases(String s, Principal[] principals) {
+ return new String[0];
+ }
+
+ @Override
+ public PrivateKey getPrivateKey(String alias) {
+ try {
+ return KeyChain.getPrivateKey(mXmppConnectionService, alias);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ }
+
+ 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) {
+ account.setOption(Account.OPTION_REGISTER, false);
+ forceCloseSocket();
+ changeStatus(Account.State.REGISTRATION_SUCCESSFUL);
+ } else {
+ final List<String> PASSWORD_TOO_WEAK_MSGS = Arrays.asList(
+ "The password is too weak",
+ "Please use a longer password.");
+ final Element error = packet.findChild("error");
+ Account.State state = Account.State.REGISTRATION_FAILED;
mXmppConnectionService.deleteAccount(account);
- if (error != null) {
+ if (error != null) {
if (error.hasChild("text")) {
errorMessage = error.findChildContent("text");
Log.d(Config.LOGTAG, "Error creating account : " + error.findChildContent("text"));
}
- if (error.hasChild("conflict")) {
- state = Account.State.REGISTRATION_CONFLICT;
- } else if (error.hasChild("resource-constraint")
- && "wait".equals(error.getAttribute("type"))) {
- state = Account.State.REGISTRATION_PLEASE_WAIT;
- } else if (error.hasChild("not-acceptable")
- && PASSWORD_TOO_WEAK_MSGS.contains(error.findChildContent("text"))) {
- state = Account.State.REGISTRATION_PASSWORD_TOO_WEAK;
- }
- }
+ if (error.hasChild("conflict")) {
+ state = Account.State.REGISTRATION_CONFLICT;
+ } else if (error.hasChild("resource-constraint")
+ && "wait".equals(error.getAttribute("type"))) {
+ state = Account.State.REGISTRATION_PLEASE_WAIT;
+ } else if (error.hasChild("not-acceptable")
+ && PASSWORD_TOO_WEAK_MSGS.contains(error.findChildContent("text"))) {
+ state = Account.State.REGISTRATION_PASSWORD_TOO_WEAK;
+ }
+ }
Log.d(Config.LOGTAG, "Delete account because of error " + error);
- changeStatus(state);
- forceCloseSocket();
- }
- }
- };
-
- public XmppConnection(final Account account, final XmppConnectionService service) {
- this.account = account;
- this.wakeLock = service.getPowerManager().newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, account.getJid().toBareJid().toString());
- tagWriter = new TagWriter();
- mXmppConnectionService = service;
- }
-
- protected void changeStatus(final Account.State nextStatus) {
- if (account.getStatus() != nextStatus) {
- if ((nextStatus == Account.State.OFFLINE)
- && (account.getStatus() != Account.State.CONNECTING)
- && (account.getStatus() != Account.State.ONLINE)
- && (account.getStatus() != Account.State.DISABLED)) {
- return;
- }
- if (nextStatus == Account.State.ONLINE) {
- this.attempt = 0;
- }
- account.setStatus(nextStatus);
- if (statusListener != null) {
- statusListener.onStatusChanged(account);
- }
- }
- }
-
- public void prepareNewConnection() {
- this.lastConnect = SystemClock.elapsedRealtime();
- this.lastPingSent = SystemClock.elapsedRealtime();
- this.lastDiscoStarted = Long.MAX_VALUE;
- this.changeStatus(Account.State.CONNECTING);
- }
-
- protected void connect() {
+ changeStatus(state);
+ forceCloseSocket();
+ }
+ }
+ };
+
+ public XmppConnection(final Account account, final XmppConnectionService service) {
+ this.account = account;
+ this.wakeLock = service.getPowerManager().newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, account.getJid().toBareJid().toString());
+ tagWriter = new TagWriter();
+ mXmppConnectionService = service;
+ }
+
+ protected void changeStatus(final Account.State nextStatus) {
+ if (account.getStatus() != nextStatus) {
+ if ((nextStatus == Account.State.OFFLINE)
+ && (account.getStatus() != Account.State.CONNECTING)
+ && (account.getStatus() != Account.State.ONLINE)
+ && (account.getStatus() != Account.State.DISABLED)) {
+ return;
+ }
+ if (nextStatus == Account.State.ONLINE) {
+ this.attempt = 0;
+ }
+ account.setStatus(nextStatus);
+ if (statusListener != null) {
+ statusListener.onStatusChanged(account);
+ }
+ }
+ }
+
+ public void prepareNewConnection() {
+ this.lastConnect = SystemClock.elapsedRealtime();
+ this.lastPingSent = SystemClock.elapsedRealtime();
+ this.lastDiscoStarted = Long.MAX_VALUE;
+ this.changeStatus(Account.State.CONNECTING);
+ }
+
+ protected void connect() {
if (mXmppConnectionService.areMessagesInitialized()) {
mXmppConnectionService.resetSendingToWaiting(account);
}
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": connecting");
- features.encryptionEnabled = false;
- this.attempt++;
- switch (account.getJid().getDomainpart()) {
- case "chat.facebook.com":
- mServerIdentity = Identity.FACEBOOK;
- break;
- case "nimbuzz.com":
- mServerIdentity = Identity.NIMBUZZ;
- break;
- default:
- mServerIdentity = Identity.UNKNOWN;
- break;
- }
- try {
- shouldAuthenticate = needsBinding = !account.isOptionSet(Account.OPTION_REGISTER);
- 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 (Config.XMPP_IP != null && Config.XMPP_Ports != null) {
- Integer[] XMPP_Port = Config.XMPP_Ports;
- Integer Port = XMPP_Port[new Random().nextInt(XMPP_Port.length)];
- socket = new Socket();
- try {
- socket.connect(new InetSocketAddress(Config.XMPP_IP, Port), Config.SOCKET_TIMEOUT * 1000);
- } catch (IOException e) {
- throw new IOException();
- }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": connecting");
+ features.encryptionEnabled = false;
+ this.attempt++;
+ switch (account.getJid().getDomainpart()) {
+ case "chat.facebook.com":
+ mServerIdentity = Identity.FACEBOOK;
+ break;
+ case "nimbuzz.com":
+ mServerIdentity = Identity.NIMBUZZ;
+ break;
+ default:
+ mServerIdentity = Identity.UNKNOWN;
+ break;
+ }
+ try {
+ shouldAuthenticate = needsBinding = !account.isOptionSet(Account.OPTION_REGISTER);
+ 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 (Config.XMPP_IP != null && Config.XMPP_Ports != null) {
+ Integer[] XMPP_Port = Config.XMPP_Ports;
+ Integer Port = XMPP_Port[new Random().nextInt(XMPP_Port.length)];
+ socket = new Socket();
+ try {
+ socket.connect(new InetSocketAddress(Config.XMPP_IP, Port), Config.SOCKET_TIMEOUT * 1000);
+ } catch (IOException e) {
+ throw new IOException();
+ }
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": connect to " + Config.XMPP_IP + ":" + Port);
- startXmpp();
- }
- else 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()) {
-
- InetSocketAddress address = new InetSocketAddress(account.getHostname(), account.getPort());
-
- features.encryptionEnabled = account.getPort() == 5223;
-
- try {
- if (features.encryptionEnabled) {
- try {
- final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
- socket = tlsFactoryVerifier.factory.createSocket();
- socket.connect(address, Config.SOCKET_TIMEOUT * 1000);
- final SSLSession session = ((SSLSocket) socket).getSession();
- if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), session)) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
- throw new SecurityException();
- }
- } catch (KeyManagementException e) {
- features.encryptionEnabled = false;
- socket = new Socket();
- }
- } else {
- socket = new Socket();
- socket.connect(address, Config.SOCKET_TIMEOUT * 1000);
- }
- } catch (IOException e) {
- throw new UnknownHostException();
- }
- startXmpp();
- } else if (DNSHelper.isIp(account.getServer().toString())) {
- socket = new Socket();
- try {
- socket.connect(new InetSocketAddress(account.getServer().toString(), 5222), 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();
- }
- }
- }
- }
- processStream();
- } catch (final java.lang.SecurityException e) {
- this.changeStatus(Account.State.MISSING_INTERNET_PERMISSION);
- } catch (final IncompatibleServerException e) {
- this.changeStatus(Account.State.INCOMPATIBLE_SERVER);
- } catch (final SecurityException e) {
- this.changeStatus(Account.State.SECURITY_ERROR);
- } catch (final UnauthorizedException e) {
- this.changeStatus(Account.State.UNAUTHORIZED);
- } catch (final PaymentRequiredException e) {
- this.changeStatus(Account.State.PAYMENT_REQUIRED);
- } 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 StreamErrorHostUnknown e) {
- this.changeStatus(Account.State.HOST_UNKNOWN);
- } catch(final StreamErrorPolicyViolation e) {
- this.changeStatus(Account.State.POLICY_VIOLATION);
- } catch(final StreamError e) {
- this.changeStatus(Account.State.STREAM_ERROR);
- } catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
- this.changeStatus(Account.State.OFFLINE);
- this.attempt = Math.max(0, this.attempt - 1);
- } finally {
- forceCloseSocket();
- if (wakeLock.isHeld()) {
- try {
- wakeLock.release();
- } catch (final RuntimeException ignored) {
- }
- }
- }
- }
-
- /**
- * Starts xmpp protocol, call after connecting to socket
- * @return true if server returns with valid xmpp, false otherwise
- * @throws IOException Unknown tag on connect
- * @throws XmlPullParserException Bad Xml
- * @throws NoSuchAlgorithmException Other error
+ startXmpp();
+ } else 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()) {
+
+ InetSocketAddress address = new InetSocketAddress(account.getHostname(), account.getPort());
+
+ features.encryptionEnabled = account.getPort() == 5223;
+
+ try {
+ if (features.encryptionEnabled) {
+ try {
+ final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
+ socket = tlsFactoryVerifier.factory.createSocket();
+ socket.connect(address, Config.SOCKET_TIMEOUT * 1000);
+ final SSLSession session = ((SSLSocket) socket).getSession();
+ if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), session)) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
+ throw new SecurityException();
+ }
+ } catch (KeyManagementException e) {
+ features.encryptionEnabled = false;
+ socket = new Socket();
+ }
+ } else {
+ socket = new Socket();
+ socket.connect(address, Config.SOCKET_TIMEOUT * 1000);
+ }
+ } catch (IOException e) {
+ throw new UnknownHostException();
+ }
+ startXmpp();
+ } else if (DNSHelper.isIp(account.getServer().toString())) {
+ socket = new Socket();
+ try {
+ socket.connect(new InetSocketAddress(account.getServer().toString(), 5222), 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();
+ }
+ }
+ }
+ }
+ processStream();
+ } catch (final java.lang.SecurityException e) {
+ this.changeStatus(Account.State.MISSING_INTERNET_PERMISSION);
+ } catch (final IncompatibleServerException e) {
+ this.changeStatus(Account.State.INCOMPATIBLE_SERVER);
+ } catch (final SecurityException e) {
+ this.changeStatus(Account.State.SECURITY_ERROR);
+ } catch (final UnauthorizedException e) {
+ this.changeStatus(Account.State.UNAUTHORIZED);
+ } catch (final PaymentRequiredException e) {
+ this.changeStatus(Account.State.PAYMENT_REQUIRED);
+ } 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 StreamErrorHostUnknown e) {
+ this.changeStatus(Account.State.HOST_UNKNOWN);
+ } catch (final StreamErrorPolicyViolation e) {
+ this.changeStatus(Account.State.POLICY_VIOLATION);
+ } catch (final StreamError e) {
+ this.changeStatus(Account.State.STREAM_ERROR);
+ } catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
+ this.changeStatus(Account.State.OFFLINE);
+ this.attempt = Math.max(0, this.attempt - 1);
+ } finally {
+ forceCloseSocket();
+ if (wakeLock.isHeld()) {
+ try {
+ wakeLock.release();
+ } catch (final RuntimeException ignored) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Starts xmpp protocol, call after connecting to socket
+ *
+ * @return true if server returns with valid xmpp, false otherwise
+ * @throws IOException Unknown tag on connect
+ * @throws XmlPullParserException Bad Xml
+ * @throws NoSuchAlgorithmException Other error
*/
- private boolean startXmpp() throws IOException, XmlPullParserException, NoSuchAlgorithmException {
- tagWriter.setOutputStream(socket.getOutputStream());
- tagReader.setInputStream(socket.getInputStream());
- tagWriter.beginDocument();
- sendStartStream();
- Tag nextTag;
- while ((nextTag = tagReader.readTag()) != null) {
- if (nextTag.isStart("stream")) {
- return true;
- } else {
- throw new IOException("unknown tag on connect");
- }
- }
- if (socket.isConnected()) {
- socket.close();
- }
- return false;
- }
-
- private static class TlsFactoryVerifier {
- private final SSLSocketFactory factory;
- private final HostnameVerifier verifier;
-
- public TlsFactoryVerifier(final SSLSocketFactory factory, final HostnameVerifier verifier) throws IOException {
- this.factory = factory;
- this.verifier = verifier;
- if (factory == null || verifier == null) {
- throw new IOException("could not setup ssl");
- }
- }
- }
-
- private TlsFactoryVerifier getTlsFactoryVerifier() throws NoSuchAlgorithmException, KeyManagementException, IOException {
- final SSLContext sc = SSLSocketHelper.getSSLContext();
- MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager();
- KeyManager[] keyManager;
- if (account.getPrivateKeyAlias() != null && account.getPassword().isEmpty()) {
- keyManager = new KeyManager[]{new MyKeyManager()};
- } else {
- keyManager = null;
- }
- sc.init(keyManager, new X509TrustManager[]{mInteractive ? trustManager : trustManager.getNonInteractive()}, mXmppConnectionService.getRNG());
- final SSLSocketFactory factory = sc.getSocketFactory();
- final HostnameVerifier verifier;
- if (mInteractive) {
- verifier = trustManager.wrapHostnameVerifier(new XmppDomainVerifier());
- } else {
- verifier = trustManager.wrapHostnameVerifierNonInteractive(new XmppDomainVerifier());
- }
-
- return new TlsFactoryVerifier(factory, verifier);
- }
-
- @Override
- public void run() {
- forceCloseSocket();
- connect();
- }
-
- private void processStream() throws XmlPullParserException, IOException, NoSuchAlgorithmException {
- Tag nextTag = tagReader.readTag();
- while (nextTag != null && !nextTag.isEnd("stream")) {
- if (nextTag.isStart("error")) {
- processStreamError(nextTag);
- } else if (nextTag.isStart("features")) {
- processStreamFeatures(nextTag);
- } else if (nextTag.isStart("proceed")) {
- switchOverToTls(nextTag);
- } else if (nextTag.isStart("success")) {
- final String challenge = tagReader.readElement(nextTag).getContent();
- try {
- saslMechanism.getResponse(challenge);
- } catch (final SaslMechanism.AuthenticationException e) {
- disconnect(true);
- Log.e(Config.LOGTAG, String.valueOf(e));
- }
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": logged in");
- account.setKey(Account.PINNED_MECHANISM_KEY,
- String.valueOf(saslMechanism.getPriority()));
- tagReader.reset();
- sendStartStream();
- final Tag tag = tagReader.readTag();
- if (tag != null && tag.isStart("stream")) {
- processStream();
- } else {
- throw new IOException("server didn't restart stream after successful auth");
- }
- break;
- } else if (nextTag.isStart("failure")) {
- final Element failure = tagReader.readElement(nextTag);
- final String text = failure.findChildContent("text");
- if (failure.hasChild("account-disabled")
- && text != null
- && text.contains("renew")
- && Config.MAGIC_CREATE_DOMAIN != null
- && text.contains(Config.MAGIC_CREATE_DOMAIN)) {
- throw new PaymentRequiredException();
- } else {
- throw new UnauthorizedException();
- }
- } else if (nextTag.isStart("challenge")) {
- final String challenge = tagReader.readElement(nextTag).getContent();
- final Element response = new Element("response");
- response.setAttribute("xmlns",
- "urn:ietf:params:xml:ns:xmpp-sasl");
- try {
- response.setContent(saslMechanism.getResponse(challenge));
- } catch (final SaslMechanism.AuthenticationException e) {
- // TODO: Send auth abort tag.
- Log.e(Config.LOGTAG, e.toString());
- }
- tagWriter.writeElement(response);
- } else if (nextTag.isStart("enabled")) {
- final Element enabled = tagReader.readElement(nextTag);
- if ("true".equals(enabled.getAttribute("resume"))) {
- this.streamId = enabled.getAttribute("id");
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
- + ": stream management(" + smVersion
- + ") enabled (resumable)");
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
- + ": stream management(" + smVersion + ") enabled");
- }
- this.stanzasReceived = 0;
- final RequestPacket r = new RequestPacket(smVersion);
- tagWriter.writeStanzaAsync(r);
- } else if (nextTag.isStart("resumed")) {
- lastPacketReceived = SystemClock.elapsedRealtime();
- final Element resumed = tagReader.readElement(nextTag);
- final String h = resumed.getAttribute("h");
- try {
- ArrayList<AbstractAcknowledgeableStanza> failedStanzas = new ArrayList<>();
- synchronized (this.mStanzaQueue) {
- final int serverCount = Integer.parseInt(h);
- if (serverCount != stanzasSent) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
- + ": session resumed with lost packages");
- stanzasSent = serverCount;
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": session resumed");
- }
- acknowledgeStanzaUpTo(serverCount);
- for (int i = 0; i < this.mStanzaQueue.size(); ++i) {
- failedStanzas.add(mStanzaQueue.valueAt(i));
- }
- mStanzaQueue.clear();
- }
- Log.d(Config.LOGTAG, "resending " + failedStanzas.size() + " stanzas");
- for (AbstractAcknowledgeableStanza packet : failedStanzas) {
- if (packet instanceof MessagePacket) {
- MessagePacket message = (MessagePacket) packet;
- mXmppConnectionService.markMessage(account,
- message.getTo().toBareJid(),
- message.getId(),
- Message.STATUS_UNSEND);
- }
- sendPacket(packet);
- }
- } catch (final NumberFormatException ignored) {
- }
- Log.d(Config.LOGTAG, account.getJid().toBareJid()+ ": online with resource " + account.getResource());
- changeStatus(Account.State.ONLINE);
- } else if (nextTag.isStart("r")) {
- tagReader.readElement(nextTag);
- if (Config.EXTENDED_SM_LOGGING) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": acknowledging stanza #" + this.stanzasReceived);
- }
- final AckPacket ack = new AckPacket(this.stanzasReceived, smVersion);
- tagWriter.writeStanzaAsync(ack);
- } else if (nextTag.isStart("a")) {
- final Element ack = tagReader.readElement(nextTag);
- lastPacketReceived = SystemClock.elapsedRealtime();
- try {
- synchronized (this.mStanzaQueue) {
- final int serverSequence = Integer.parseInt(ack.getAttribute("h"));
- acknowledgeStanzaUpTo(serverSequence);
- }
- } catch (NumberFormatException | NullPointerException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": server send ack without sequence number");
- }
- } else if (nextTag.isStart("failed")) {
- Element failed = tagReader.readElement(nextTag);
- try {
- final int serverCount = Integer.parseInt(failed.getAttribute("h"));
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": resumption failed but server acknowledged stanza #"+serverCount);
- synchronized (this.mStanzaQueue) {
- acknowledgeStanzaUpTo(serverCount);
- }
- } catch (NumberFormatException | NullPointerException e) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": resumption failed");
- }
- resetStreamId();
- if (account.getStatus() != Account.State.ONLINE) {
- sendBindRequest();
- }
- } else if (nextTag.isStart("iq")) {
- processIq(nextTag);
- } else if (nextTag.isStart("message")) {
- processMessage(nextTag);
- } else if (nextTag.isStart("presence")) {
- processPresence(nextTag);
- }
- nextTag = tagReader.readTag();
- }
- }
-
- private void acknowledgeStanzaUpTo(int serverCount) {
- for (int i = 0; i < mStanzaQueue.size(); ++i) {
- if (serverCount >= mStanzaQueue.keyAt(i)) {
- if (Config.EXTENDED_SM_LOGGING) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server acknowledged stanza #" + mStanzaQueue.keyAt(i));
- }
- AbstractAcknowledgeableStanza stanza = mStanzaQueue.valueAt(i);
- if (stanza instanceof MessagePacket && acknowledgedListener != null) {
- MessagePacket packet = (MessagePacket) stanza;
- acknowledgedListener.onMessageAcknowledged(account, packet.getId());
- }
- mStanzaQueue.removeAt(i);
- i--;
- }
- }
- }
-
- private Element processPacket(final Tag currentTag, final int packetType)
- throws XmlPullParserException, IOException {
- Element element;
- switch (packetType) {
- case PACKET_IQ:
- element = new IqPacket();
- break;
- case PACKET_MESSAGE:
- element = new MessagePacket();
- break;
- case PACKET_PRESENCE:
- element = new PresencePacket();
- break;
- default:
- return null;
- }
- element.setAttributes(currentTag.getAttributes());
- Tag nextTag = tagReader.readTag();
- if (nextTag == null) {
- throw new IOException("interrupted mid tag");
- }
- while (!nextTag.isEnd(element.getName())) {
- if (!nextTag.isNo()) {
- final Element child = tagReader.readElement(nextTag);
- final String type = currentTag.getAttribute("type");
- if (packetType == PACKET_IQ
- && "jingle".equals(child.getName())
- && ("set".equalsIgnoreCase(type) || "get"
- .equalsIgnoreCase(type))) {
- element = new JinglePacket();
- element.setAttributes(currentTag.getAttributes());
- }
- element.addChild(child);
- }
- nextTag = tagReader.readTag();
- if (nextTag == null) {
- throw new IOException("interrupted mid tag");
- }
- }
- if (stanzasReceived == Integer.MAX_VALUE) {
- resetStreamId();
- throw new IOException("time to restart the session. cant handle >2 billion pcks");
- }
- ++stanzasReceived;
- lastPacketReceived = SystemClock.elapsedRealtime();
- if (Config.BACKGROUND_STANZA_LOGGING && mXmppConnectionService.checkListeners()) {
- Log.d(Config.LOGTAG,"[background stanza] "+element);
- }
- return element;
- }
-
- private void processIq(final Tag currentTag) throws XmlPullParserException, IOException {
- final IqPacket packet = (IqPacket) processPacket(currentTag, PACKET_IQ);
-
- if (packet.getId() == null) {
- return; // an iq packet without id is definitely invalid
- }
-
- if (packet instanceof JinglePacket) {
- if (this.jingleListener != null) {
- this.jingleListener.onJinglePacketReceived(account,(JinglePacket) packet);
- }
- } else {
- OnIqPacketReceived callback = null;
- synchronized (this.packetCallbacks) {
- if (packetCallbacks.containsKey(packet.getId())) {
- final Pair<IqPacket, OnIqPacketReceived> packetCallbackDuple = packetCallbacks.get(packet.getId());
- // Packets to the server should have responses from the server
- if (packetCallbackDuple.first.toServer(account)) {
- if (packet.fromServer(account) || mServerIdentity == Identity.FACEBOOK) {
- callback = packetCallbackDuple.second;
- packetCallbacks.remove(packet.getId());
- } else {
- Log.e(Config.LOGTAG, account.getJid().toBareJid().toString() + ": ignoring spoofed iq packet");
- }
- } else {
- if (packet.getFrom().equals(packetCallbackDuple.first.getTo())) {
- callback = packetCallbackDuple.second;
- packetCallbacks.remove(packet.getId());
- } else {
- Log.e(Config.LOGTAG, account.getJid().toBareJid().toString() + ": ignoring spoofed iq packet");
- }
- }
- } else if (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET) {
- callback = this.unregisteredIqListener;
- }
- }
- if (callback != null) {
- callback.onIqPacketReceived(account,packet);
- }
- }
- }
-
- private void processMessage(final Tag currentTag) throws XmlPullParserException, IOException {
- final MessagePacket packet = (MessagePacket) processPacket(currentTag,PACKET_MESSAGE);
- this.messageListener.onMessagePacketReceived(account, packet);
- }
-
- private void processPresence(final Tag currentTag) throws XmlPullParserException, IOException {
- PresencePacket packet = (PresencePacket) processPacket(currentTag, PACKET_PRESENCE);
- this.presenceListener.onPresencePacketReceived(account, packet);
- }
-
- private void sendStartTLS() throws IOException {
- final Tag startTLS = Tag.empty("starttls");
- startTLS.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-tls");
- tagWriter.writeTag(startTLS);
- }
-
-
-
- private void switchOverToTls(final Tag currentTag) throws XmlPullParserException, IOException {
- tagReader.readTag();
- try {
- final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
- final InetAddress address = socket == null ? null : socket.getInetAddress();
-
- if (address == null) {
- throw new IOException("could not setup ssl");
- }
-
- final SSLSocket sslSocket = (SSLSocket) tlsFactoryVerifier.factory.createSocket(socket, address.getHostAddress(), socket.getPort(), true);
-
- if (sslSocket == null) {
- throw new IOException("could not initialize ssl socket");
- }
-
- SSLSocketHelper.setSecurity(sslSocket);
-
- if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), sslSocket.getSession())) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
- throw new SecurityException();
- }
- tagReader.setInputStream(sslSocket.getInputStream());
- tagWriter.setOutputStream(sslSocket.getOutputStream());
- sendStartStream();
- Log.d(Config.LOGTAG, account.getJid().toBareJid()+ ": TLS connection established");
- features.encryptionEnabled = true;
- final Tag tag = tagReader.readTag();
- if (tag != null && tag.isStart("stream")) {
- processStream();
- } else {
- throw new IOException("server didn't restart stream after STARTTLS");
- }
- sslSocket.close();
- } catch (final NoSuchAlgorithmException | KeyManagementException e1) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
- throw new SecurityException();
- }
- }
-
- private void processStreamFeatures(final Tag currentTag)
- throws XmlPullParserException, IOException {
- this.streamFeatures = tagReader.readElement(currentTag);
- if (this.streamFeatures.hasChild("starttls") && !features.encryptionEnabled) {
- sendStartTLS();
- } else if (this.streamFeatures.hasChild("register") && account.isOptionSet(Account.OPTION_REGISTER)) {
- if (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS) {
- sendRegistryRequest();
- } else {
- throw new IncompatibleServerException();
- }
- } else if (!this.streamFeatures.hasChild("register")
- && account.isOptionSet(Account.OPTION_REGISTER)) {
- forceCloseSocket();
- changeStatus(Account.State.REGISTRATION_NOT_SUPPORTED);
- } else if (this.streamFeatures.hasChild("mechanisms")
- && shouldAuthenticate
- && (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS)) {
- 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);
- }
- final ResumePacket resume = new ResumePacket(this.streamId, stanzasReceived, smVersion);
- this.tagWriter.writeStanzaAsync(resume);
- } else if (needsBinding) {
- if (this.streamFeatures.hasChild("bind")) {
- sendBindRequest();
- } else {
- throw new IncompatibleServerException();
- }
- }
- }
-
- private void authenticate() throws IOException {
- final List<String> mechanisms = extractMechanisms(streamFeatures
- .findChild("mechanisms"));
- final Element auth = new Element("auth");
- auth.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
- if (mechanisms.contains("EXTERNAL") && account.getPrivateKeyAlias() != null) {
- saslMechanism = new External(tagWriter, account, mXmppConnectionService.getRNG());
- } else if (mechanisms.contains("SCRAM-SHA-1")) {
- saslMechanism = new ScramSha1(tagWriter, account, mXmppConnectionService.getRNG());
- } else if (mechanisms.contains("PLAIN")) {
- saslMechanism = new Plain(tagWriter, account);
- } else if (mechanisms.contains("DIGEST-MD5")) {
- saslMechanism = new DigestMd5(tagWriter, account, mXmppConnectionService.getRNG());
- } else if (mechanisms.contains("ANONYMOUS")) {
- saslMechanism = new Anonymous(tagWriter, account, mXmppConnectionService.getRNG());
- }
- if (saslMechanism != null) {
+ private boolean startXmpp() throws IOException, XmlPullParserException, NoSuchAlgorithmException {
+ tagWriter.setOutputStream(socket.getOutputStream());
+ tagReader.setInputStream(socket.getInputStream());
+ tagWriter.beginDocument();
+ sendStartStream();
+ Tag nextTag;
+ while ((nextTag = tagReader.readTag()) != null) {
+ if (nextTag.isStart("stream")) {
+ return true;
+ } else {
+ throw new IOException("unknown tag on connect");
+ }
+ }
+ if (socket.isConnected()) {
+ socket.close();
+ }
+ return false;
+ }
+
+ private static class TlsFactoryVerifier {
+ private final SSLSocketFactory factory;
+ private final HostnameVerifier verifier;
+
+ public TlsFactoryVerifier(final SSLSocketFactory factory, final HostnameVerifier verifier) throws IOException {
+ this.factory = factory;
+ this.verifier = verifier;
+ if (factory == null || verifier == null) {
+ throw new IOException("could not setup ssl");
+ }
+ }
+ }
+
+ private TlsFactoryVerifier getTlsFactoryVerifier() throws NoSuchAlgorithmException, KeyManagementException, IOException {
+ final SSLContext sc = SSLSocketHelper.getSSLContext();
+ MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager();
+ KeyManager[] keyManager;
+ if (account.getPrivateKeyAlias() != null && account.getPassword().isEmpty()) {
+ keyManager = new KeyManager[]{new MyKeyManager()};
+ } else {
+ keyManager = null;
+ }
+ sc.init(keyManager, new X509TrustManager[]{mInteractive ? trustManager : trustManager.getNonInteractive()}, mXmppConnectionService.getRNG());
+ final SSLSocketFactory factory = sc.getSocketFactory();
+ final HostnameVerifier verifier;
+ if (mInteractive) {
+ verifier = trustManager.wrapHostnameVerifier(new XmppDomainVerifier());
+ } else {
+ verifier = trustManager.wrapHostnameVerifierNonInteractive(new XmppDomainVerifier());
+ }
+
+ return new TlsFactoryVerifier(factory, verifier);
+ }
+
+ @Override
+ public void run() {
+ forceCloseSocket();
+ connect();
+ }
+
+ private void processStream() throws XmlPullParserException, IOException, NoSuchAlgorithmException {
+ Tag nextTag = tagReader.readTag();
+ while (nextTag != null && !nextTag.isEnd("stream")) {
+ if (nextTag.isStart("error")) {
+ processStreamError(nextTag);
+ } else if (nextTag.isStart("features")) {
+ processStreamFeatures(nextTag);
+ } else if (nextTag.isStart("proceed")) {
+ switchOverToTls(nextTag);
+ } else if (nextTag.isStart("success")) {
+ final String challenge = tagReader.readElement(nextTag).getContent();
+ try {
+ saslMechanism.getResponse(challenge);
+ } catch (final SaslMechanism.AuthenticationException e) {
+ disconnect(true);
+ Log.e(Config.LOGTAG, String.valueOf(e));
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": logged in");
+ account.setKey(Account.PINNED_MECHANISM_KEY,
+ String.valueOf(saslMechanism.getPriority()));
+ tagReader.reset();
+ sendStartStream();
+ final Tag tag = tagReader.readTag();
+ if (tag != null && tag.isStart("stream")) {
+ processStream();
+ } else {
+ throw new IOException("server didn't restart stream after successful auth");
+ }
+ break;
+ } else if (nextTag.isStart("failure")) {
+ final Element failure = tagReader.readElement(nextTag);
+ final String text = failure.findChildContent("text");
+ if (failure.hasChild("account-disabled")
+ && text != null
+ && text.contains("renew")
+ && Config.MAGIC_CREATE_DOMAIN != null
+ && text.contains(Config.MAGIC_CREATE_DOMAIN)) {
+ throw new PaymentRequiredException();
+ } else {
+ throw new UnauthorizedException();
+ }
+ } else if (nextTag.isStart("challenge")) {
+ final String challenge = tagReader.readElement(nextTag).getContent();
+ final Element response = new Element("response");
+ response.setAttribute("xmlns",
+ "urn:ietf:params:xml:ns:xmpp-sasl");
+ try {
+ response.setContent(saslMechanism.getResponse(challenge));
+ } catch (final SaslMechanism.AuthenticationException e) {
+ // TODO: Send auth abort tag.
+ Log.e(Config.LOGTAG, e.toString());
+ }
+ tagWriter.writeElement(response);
+ } else if (nextTag.isStart("enabled")) {
+ final Element enabled = tagReader.readElement(nextTag);
+ if ("true".equals(enabled.getAttribute("resume"))) {
+ this.streamId = enabled.getAttribute("id");
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ + ": stream management(" + smVersion
+ + ") enabled (resumable)");
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ + ": stream management(" + smVersion + ") enabled");
+ }
+ this.stanzasReceived = 0;
+ final RequestPacket r = new RequestPacket(smVersion);
+ tagWriter.writeStanzaAsync(r);
+ } else if (nextTag.isStart("resumed")) {
+ lastPacketReceived = SystemClock.elapsedRealtime();
+ final Element resumed = tagReader.readElement(nextTag);
+ final String h = resumed.getAttribute("h");
+ try {
+ ArrayList<AbstractAcknowledgeableStanza> failedStanzas = new ArrayList<>();
+ synchronized (this.mStanzaQueue) {
+ final int serverCount = Integer.parseInt(h);
+ if (serverCount != stanzasSent) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ + ": session resumed with lost packages");
+ stanzasSent = serverCount;
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": session resumed");
+ }
+ acknowledgeStanzaUpTo(serverCount);
+ for (int i = 0; i < this.mStanzaQueue.size(); ++i) {
+ failedStanzas.add(mStanzaQueue.valueAt(i));
+ }
+ mStanzaQueue.clear();
+ }
+ Log.d(Config.LOGTAG, "resending " + failedStanzas.size() + " stanzas");
+ for (AbstractAcknowledgeableStanza packet : failedStanzas) {
+ if (packet instanceof MessagePacket) {
+ MessagePacket message = (MessagePacket) packet;
+ mXmppConnectionService.markMessage(account,
+ message.getTo().toBareJid(),
+ message.getId(),
+ Message.STATUS_UNSEND);
+ }
+ sendPacket(packet);
+ }
+ } catch (final NumberFormatException ignored) {
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource());
+ changeStatus(Account.State.ONLINE);
+ } else if (nextTag.isStart("r")) {
+ tagReader.readElement(nextTag);
+ if (Config.EXTENDED_SM_LOGGING) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": acknowledging stanza #" + this.stanzasReceived);
+ }
+ final AckPacket ack = new AckPacket(this.stanzasReceived, smVersion);
+ tagWriter.writeStanzaAsync(ack);
+ } else if (nextTag.isStart("a")) {
+ final Element ack = tagReader.readElement(nextTag);
+ lastPacketReceived = SystemClock.elapsedRealtime();
+ try {
+ synchronized (this.mStanzaQueue) {
+ final int serverSequence = Integer.parseInt(ack.getAttribute("h"));
+ acknowledgeStanzaUpTo(serverSequence);
+ }
+ } catch (NumberFormatException | NullPointerException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server send ack without sequence number");
+ }
+ } else if (nextTag.isStart("failed")) {
+ Element failed = tagReader.readElement(nextTag);
+ try {
+ final int serverCount = Integer.parseInt(failed.getAttribute("h"));
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": resumption failed but server acknowledged stanza #" + serverCount);
+ synchronized (this.mStanzaQueue) {
+ acknowledgeStanzaUpTo(serverCount);
+ }
+ } catch (NumberFormatException | NullPointerException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": resumption failed");
+ }
+ resetStreamId();
+ if (account.getStatus() != Account.State.ONLINE) {
+ sendBindRequest();
+ }
+ } else if (nextTag.isStart("iq")) {
+ processIq(nextTag);
+ } else if (nextTag.isStart("message")) {
+ processMessage(nextTag);
+ } else if (nextTag.isStart("presence")) {
+ processPresence(nextTag);
+ }
+ nextTag = tagReader.readTag();
+ }
+ }
+
+ private void acknowledgeStanzaUpTo(int serverCount) {
+ for (int i = 0; i < mStanzaQueue.size(); ++i) {
+ if (serverCount >= mStanzaQueue.keyAt(i)) {
+ if (Config.EXTENDED_SM_LOGGING) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server acknowledged stanza #" + mStanzaQueue.keyAt(i));
+ }
+ AbstractAcknowledgeableStanza stanza = mStanzaQueue.valueAt(i);
+ if (stanza instanceof MessagePacket && acknowledgedListener != null) {
+ MessagePacket packet = (MessagePacket) stanza;
+ acknowledgedListener.onMessageAcknowledged(account, packet.getId());
+ }
+ mStanzaQueue.removeAt(i);
+ i--;
+ }
+ }
+ }
+
+ private Element processPacket(final Tag currentTag, final int packetType)
+ throws XmlPullParserException, IOException {
+ Element element;
+ switch (packetType) {
+ case PACKET_IQ:
+ element = new IqPacket();
+ break;
+ case PACKET_MESSAGE:
+ element = new MessagePacket();
+ break;
+ case PACKET_PRESENCE:
+ element = new PresencePacket();
+ break;
+ default:
+ return null;
+ }
+ element.setAttributes(currentTag.getAttributes());
+ Tag nextTag = tagReader.readTag();
+ if (nextTag == null) {
+ throw new IOException("interrupted mid tag");
+ }
+ while (!nextTag.isEnd(element.getName())) {
+ if (!nextTag.isNo()) {
+ final Element child = tagReader.readElement(nextTag);
+ final String type = currentTag.getAttribute("type");
+ if (packetType == PACKET_IQ
+ && "jingle".equals(child.getName())
+ && ("set".equalsIgnoreCase(type) || "get"
+ .equalsIgnoreCase(type))) {
+ element = new JinglePacket();
+ element.setAttributes(currentTag.getAttributes());
+ }
+ element.addChild(child);
+ }
+ nextTag = tagReader.readTag();
+ if (nextTag == null) {
+ throw new IOException("interrupted mid tag");
+ }
+ }
+ if (stanzasReceived == Integer.MAX_VALUE) {
+ resetStreamId();
+ throw new IOException("time to restart the session. cant handle >2 billion pcks");
+ }
+ ++stanzasReceived;
+ lastPacketReceived = SystemClock.elapsedRealtime();
+ if (Config.BACKGROUND_STANZA_LOGGING && mXmppConnectionService.checkListeners()) {
+ Log.d(Config.LOGTAG, "[background stanza] " + element);
+ }
+ return element;
+ }
+
+ private void processIq(final Tag currentTag) throws XmlPullParserException, IOException {
+ final IqPacket packet = (IqPacket) processPacket(currentTag, PACKET_IQ);
+
+ if (packet.getId() == null) {
+ return; // an iq packet without id is definitely invalid
+ }
+
+ if (packet instanceof JinglePacket) {
+ if (this.jingleListener != null) {
+ this.jingleListener.onJinglePacketReceived(account, (JinglePacket) packet);
+ }
+ } else {
+ OnIqPacketReceived callback = null;
+ synchronized (this.packetCallbacks) {
+ if (packetCallbacks.containsKey(packet.getId())) {
+ final Pair<IqPacket, OnIqPacketReceived> packetCallbackDuple = packetCallbacks.get(packet.getId());
+ // Packets to the server should have responses from the server
+ if (packetCallbackDuple.first.toServer(account)) {
+ if (packet.fromServer(account) || mServerIdentity == Identity.FACEBOOK) {
+ callback = packetCallbackDuple.second;
+ packetCallbacks.remove(packet.getId());
+ } else {
+ Log.e(Config.LOGTAG, account.getJid().toBareJid().toString() + ": ignoring spoofed iq packet");
+ }
+ } else {
+ if (packet.getFrom().equals(packetCallbackDuple.first.getTo())) {
+ callback = packetCallbackDuple.second;
+ packetCallbacks.remove(packet.getId());
+ } else {
+ Log.e(Config.LOGTAG, account.getJid().toBareJid().toString() + ": ignoring spoofed iq packet");
+ }
+ }
+ } else if (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET) {
+ callback = this.unregisteredIqListener;
+ }
+ }
+ if (callback != null) {
+ callback.onIqPacketReceived(account, packet);
+ }
+ }
+ }
+
+ private void processMessage(final Tag currentTag) throws XmlPullParserException, IOException {
+ final MessagePacket packet = (MessagePacket) processPacket(currentTag, PACKET_MESSAGE);
+ this.messageListener.onMessagePacketReceived(account, packet);
+ }
+
+ private void processPresence(final Tag currentTag) throws XmlPullParserException, IOException {
+ PresencePacket packet = (PresencePacket) processPacket(currentTag, PACKET_PRESENCE);
+ this.presenceListener.onPresencePacketReceived(account, packet);
+ }
+
+ private void sendStartTLS() throws IOException {
+ final Tag startTLS = Tag.empty("starttls");
+ startTLS.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-tls");
+ tagWriter.writeTag(startTLS);
+ }
+
+
+ private void switchOverToTls(final Tag currentTag) throws XmlPullParserException, IOException {
+ tagReader.readTag();
+ try {
+ final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
+ final InetAddress address = socket == null ? null : socket.getInetAddress();
+
+ if (address == null) {
+ throw new IOException("could not setup ssl");
+ }
+
+ final SSLSocket sslSocket = (SSLSocket) tlsFactoryVerifier.factory.createSocket(socket, address.getHostAddress(), socket.getPort(), true);
+
+ if (sslSocket == null) {
+ throw new IOException("could not initialize ssl socket");
+ }
+
+ SSLSocketHelper.setSecurity(sslSocket);
+
+ if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), sslSocket.getSession())) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
+ throw new SecurityException();
+ }
+ tagReader.setInputStream(sslSocket.getInputStream());
+ tagWriter.setOutputStream(sslSocket.getOutputStream());
+ sendStartStream();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS connection established");
+ features.encryptionEnabled = true;
+ final Tag tag = tagReader.readTag();
+ if (tag != null && tag.isStart("stream")) {
+ processStream();
+ } else {
+ throw new IOException("server didn't restart stream after STARTTLS");
+ }
+ sslSocket.close();
+ } catch (final NoSuchAlgorithmException | KeyManagementException e1) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
+ throw new SecurityException();
+ }
+ }
+
+ private void processStreamFeatures(final Tag currentTag)
+ throws XmlPullParserException, IOException {
+ this.streamFeatures = tagReader.readElement(currentTag);
+ if (this.streamFeatures.hasChild("starttls") && !features.encryptionEnabled) {
+ sendStartTLS();
+ } else if (this.streamFeatures.hasChild("register") && account.isOptionSet(Account.OPTION_REGISTER)) {
+ if (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS) {
+ sendRegistryRequest();
+ } else {
+ throw new IncompatibleServerException();
+ }
+ } else if (!this.streamFeatures.hasChild("register")
+ && account.isOptionSet(Account.OPTION_REGISTER)) {
+ forceCloseSocket();
+ changeStatus(Account.State.REGISTRATION_NOT_SUPPORTED);
+ } else if (this.streamFeatures.hasChild("mechanisms")
+ && shouldAuthenticate
+ && (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS)) {
+ 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);
+ }
+ final ResumePacket resume = new ResumePacket(this.streamId, stanzasReceived, smVersion);
+ this.tagWriter.writeStanzaAsync(resume);
+ } else if (needsBinding) {
+ if (this.streamFeatures.hasChild("bind")) {
+ sendBindRequest();
+ } else {
+ throw new IncompatibleServerException();
+ }
+ }
+ }
+
+ private void authenticate() throws IOException {
+ final List<String> mechanisms = extractMechanisms(streamFeatures
+ .findChild("mechanisms"));
+ final Element auth = new Element("auth");
+ auth.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
+ if (mechanisms.contains("EXTERNAL") && account.getPrivateKeyAlias() != null) {
+ saslMechanism = new External(tagWriter, account, mXmppConnectionService.getRNG());
+ } else if (mechanisms.contains("SCRAM-SHA-1")) {
+ saslMechanism = new ScramSha1(tagWriter, account, mXmppConnectionService.getRNG());
+ } else if (mechanisms.contains("PLAIN")) {
+ saslMechanism = new Plain(tagWriter, account);
+ } else if (mechanisms.contains("DIGEST-MD5")) {
+ saslMechanism = new DigestMd5(tagWriter, account, mXmppConnectionService.getRNG());
+ } else if (mechanisms.contains("ANONYMOUS")) {
+ saslMechanism = new Anonymous(tagWriter, account, mXmppConnectionService.getRNG());
+ }
+ if (saslMechanism != null) {
final int pinnedMechanism = account.getKeyAsInt(Account.PINNED_MECHANISM_KEY, -1);
if (pinnedMechanism > saslMechanism.getPriority()) {
Log.e(Config.LOGTAG, "Auth failed. Authentication mechanism " + saslMechanism.getMechanism() +
@@ -879,839 +878,840 @@ public class XmppConnection implements Runnable {
") than pinned priority (" + pinnedMechanism +
"). Possible downgrade attack?");
throw new SecurityException();
- }
- Log.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism());
- auth.setAttribute("mechanism", saslMechanism.getMechanism());
- if (!saslMechanism.getClientFirstMessage().isEmpty()) {
- auth.setContent(saslMechanism.getClientFirstMessage());
- }
- tagWriter.writeElement(auth);
- } else {
- throw new IncompatibleServerException();
- }
- }
-
- private List<String> extractMechanisms(final Element stream) {
- final ArrayList<String> mechanisms = new ArrayList<>(stream
- .getChildren().size());
- for (final Element child : stream.getChildren()) {
- mechanisms.add(child.getContent());
- }
- return mechanisms;
- }
-
- private void sendRegistryRequest() {
- final IqPacket register = new IqPacket(IqPacket.TYPE.GET);
- register.query("jabber:iq:register");
- register.setTo(account.getServer());
- sendUnmodifiedIqPacket(register, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- boolean failed = false;
- if (packet.getType() == IqPacket.TYPE.RESULT
- && packet.query().hasChild("username")
- && (packet.query().hasChild("password"))) {
- final IqPacket register = new IqPacket(IqPacket.TYPE.SET);
- final Element username = new Element("username").setContent(account.getUsername());
- final Element password = new Element("password").setContent(account.getPassword());
- register.query("jabber:iq:register").addChild(username);
- register.query().addChild(password);
- register.setFrom(account.getJid().toBareJid());
- sendUnmodifiedIqPacket(register, registrationResponseListener);
- } 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"));
- final Element blob = packet.query().findChild("data", "urn:xmpp:bob");
- final String id = packet.getId();
-
- Bitmap captcha = null;
- if (blob != null) {
- try {
- final String base64Blob = blob.getContent();
- final byte[] strBlob = Base64.decode(base64Blob, Base64.DEFAULT);
- InputStream stream = new ByteArrayInputStream(strBlob);
- captcha = BitmapFactory.decodeStream(stream);
- } catch (Exception e) {
- //ignored
- }
- } else {
- try {
- Field url = data.getFieldByName("url");
- String urlString = url.findChildContent("value");
- URL uri = new URL(urlString);
- captcha = BitmapFactory.decodeStream(uri.openConnection().getInputStream());
- } catch (IOException e) {
- Log.e(Config.LOGTAG, e.toString());
- }
- }
-
- if (captcha != null) {
- failed = !mXmppConnectionService.displayCaptchaRequest(account, id, data, captcha);
- }
- } else {
- failed = true;
- }
-
- if (failed) {
- final Element instructions = packet.query().findChild("instructions");
- setAccountCreationFailed((instructions != null) ? instructions.getContent() : "");
- }
- }
- });
- }
-
- private void setAccountCreationFailed(String instructions) {
- changeStatus(Account.State.REGISTRATION_FAILED);
- disconnect(true);
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
- + ": could not register. instructions are"
- + instructions);
- }
-
- public void resetEverything() {
- resetAttemptCount();
- resetStreamId();
- clearIqCallbacks();
- mStanzaQueue.clear();
- synchronized (this.disco) {
- disco.clear();
- }
- }
-
- private void sendBindRequest() {
- while(!mXmppConnectionService.areMessagesInitialized() && socket != null && !socket.isClosed()) {
- try {
- Thread.sleep(500);
- } catch (final InterruptedException ignored) {
- }
- }
- needsBinding = false;
- clearIqCallbacks();
- final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind")
- .addChild("resource").setContent(account.getResource());
- this.sendUnmodifiedIqPacket(iq, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
- return;
- }
- final Element bind = packet.findChild("bind");
- if (bind != null && packet.getType() == IqPacket.TYPE.RESULT) {
- final Element jid = bind.findChild("jid");
- if (jid != null && jid.getContent() != null) {
- try {
+ }
+ Log.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism());
+ auth.setAttribute("mechanism", saslMechanism.getMechanism());
+ if (!saslMechanism.getClientFirstMessage().isEmpty()) {
+ auth.setContent(saslMechanism.getClientFirstMessage());
+ }
+ tagWriter.writeElement(auth);
+ } else {
+ throw new IncompatibleServerException();
+ }
+ }
+
+ private List<String> extractMechanisms(final Element stream) {
+ final ArrayList<String> mechanisms = new ArrayList<>(stream
+ .getChildren().size());
+ for (final Element child : stream.getChildren()) {
+ mechanisms.add(child.getContent());
+ }
+ return mechanisms;
+ }
+
+ private void sendRegistryRequest() {
+ final IqPacket register = new IqPacket(IqPacket.TYPE.GET);
+ register.query("jabber:iq:register");
+ register.setTo(account.getServer());
+ sendUnmodifiedIqPacket(register, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ boolean failed = false;
+ if (packet.getType() == IqPacket.TYPE.RESULT
+ && packet.query().hasChild("username")
+ && (packet.query().hasChild("password"))) {
+ final IqPacket register = new IqPacket(IqPacket.TYPE.SET);
+ final Element username = new Element("username").setContent(account.getUsername());
+ final Element password = new Element("password").setContent(account.getPassword());
+ register.query("jabber:iq:register").addChild(username);
+ register.query().addChild(password);
+ register.setFrom(account.getJid().toBareJid());
+ sendUnmodifiedIqPacket(register, registrationResponseListener);
+ } 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"));
+ final Element blob = packet.query().findChild("data", "urn:xmpp:bob");
+ final String id = packet.getId();
+
+ Bitmap captcha = null;
+ if (blob != null) {
+ try {
+ final String base64Blob = blob.getContent();
+ final byte[] strBlob = Base64.decode(base64Blob, Base64.DEFAULT);
+ InputStream stream = new ByteArrayInputStream(strBlob);
+ captcha = BitmapFactory.decodeStream(stream);
+ } catch (Exception e) {
+ //ignored
+ }
+ } else {
+ try {
+ Field url = data.getFieldByName("url");
+ String urlString = url.findChildContent("value");
+ URL uri = new URL(urlString);
+ captcha = BitmapFactory.decodeStream(uri.openConnection().getInputStream());
+ } catch (IOException e) {
+ Log.e(Config.LOGTAG, e.toString());
+ }
+ }
+
+ if (captcha != null) {
+ failed = !mXmppConnectionService.displayCaptchaRequest(account, id, data, captcha);
+ }
+ } else {
+ failed = true;
+ }
+
+ if (failed) {
+ final Element instructions = packet.query().findChild("instructions");
+ setAccountCreationFailed((instructions != null) ? instructions.getContent() : "");
+ }
+ }
+ });
+ }
+
+ private void setAccountCreationFailed(String instructions) {
+ changeStatus(Account.State.REGISTRATION_FAILED);
+ disconnect(true);
+ Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ + ": could not register. instructions are"
+ + instructions);
+ }
+
+ public void resetEverything() {
+ resetAttemptCount();
+ resetStreamId();
+ clearIqCallbacks();
+ mStanzaQueue.clear();
+ synchronized (this.disco) {
+ disco.clear();
+ }
+ }
+
+ private void sendBindRequest() {
+ while (!mXmppConnectionService.areMessagesInitialized() && socket != null && !socket.isClosed()) {
+ try {
+ Thread.sleep(500);
+ } catch (final InterruptedException ignored) {
+ }
+ }
+ needsBinding = false;
+ clearIqCallbacks();
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind")
+ .addChild("resource").setContent(account.getResource());
+ this.sendUnmodifiedIqPacket(iq, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
+ return;
+ }
+ final Element bind = packet.findChild("bind");
+ if (bind != null && packet.getType() == IqPacket.TYPE.RESULT) {
+ final Element jid = bind.findChild("jid");
+ if (jid != null && jid.getContent() != null) {
+ try {
if (account.setJid(Jid.fromString(jid.getContent()))) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": bare jid changed during bind. updating database");
mXmppConnectionService.databaseBackend.updateAccount(account);
}
if (streamFeatures.hasChild("session")
- && !streamFeatures.findChild("session").hasChild("optional")) {
- 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)");
- }
- } else {
- Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure (" + packet.toString());
- }
- forceCloseSocket();
- changeStatus(Account.State.BIND_FAILURE);
- }
- });
- }
-
- private void clearIqCallbacks() {
- final IqPacket failurePacket = new IqPacket(IqPacket.TYPE.TIMEOUT);
- final ArrayList<OnIqPacketReceived> callbacks = new ArrayList<>();
- synchronized (this.packetCallbacks) {
- if (this.packetCallbacks.size() == 0) {
- return;
- }
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": clearing "+this.packetCallbacks.size()+" iq callbacks");
- final Iterator<Pair<IqPacket, OnIqPacketReceived>> iterator = this.packetCallbacks.values().iterator();
- while (iterator.hasNext()) {
- Pair<IqPacket, OnIqPacketReceived> entry = iterator.next();
- callbacks.add(entry.second);
- iterator.remove();
- }
- }
- for(OnIqPacketReceived callback : callbacks) {
- callback.onIqPacketReceived(account,failurePacket);
- }
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": done clearing iq callbacks. " + this.packetCallbacks.size() + " left");
- }
-
- public void sendDiscoTimeout() {
- if (mWaitForDisco.compareAndSet(true, false)) {
- finalizeBind();
- }
- }
-
- private void sendStartSession() {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": sending legacy session to outdated server");
- final IqPacket startSession = new IqPacket(IqPacket.TYPE.SET);
- startSession.addChild("session", "urn:ietf:params:xml:ns:xmpp-session");
- this.sendUnmodifiedIqPacket(startSession, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- 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");
- disconnect(true);
- }
- }
- });
- }
-
- private void sendPostBindInitialization() {
- smVersion = 0;
- if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) {
- smVersion = 3;
- } else if (streamFeatures.hasChild("sm", "urn:xmpp:sm:2")) {
- smVersion = 2;
- }
- if (smVersion != 0) {
- synchronized (this.mStanzaQueue) {
- final EnablePacket enable = new EnablePacket(smVersion);
- tagWriter.writeStanzaAsync(enable);
- stanzasSent = 0;
- mStanzaQueue.clear();
- }
- }
+ && !streamFeatures.findChild("session").hasChild("optional")) {
+ 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)");
+ }
+ } else {
+ Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure (" + packet.toString());
+ }
+ forceCloseSocket();
+ changeStatus(Account.State.BIND_FAILURE);
+ }
+ });
+ }
+
+ private void clearIqCallbacks() {
+ final IqPacket failurePacket = new IqPacket(IqPacket.TYPE.TIMEOUT);
+ final ArrayList<OnIqPacketReceived> callbacks = new ArrayList<>();
+ synchronized (this.packetCallbacks) {
+ if (this.packetCallbacks.size() == 0) {
+ return;
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": clearing " + this.packetCallbacks.size() + " iq callbacks");
+ final Iterator<Pair<IqPacket, OnIqPacketReceived>> iterator = this.packetCallbacks.values().iterator();
+ while (iterator.hasNext()) {
+ Pair<IqPacket, OnIqPacketReceived> entry = iterator.next();
+ callbacks.add(entry.second);
+ iterator.remove();
+ }
+ }
+ for (OnIqPacketReceived callback : callbacks) {
+ callback.onIqPacketReceived(account, failurePacket);
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": done clearing iq callbacks. " + this.packetCallbacks.size() + " left");
+ }
+
+ public void sendDiscoTimeout() {
+ if (mWaitForDisco.compareAndSet(true, false)) {
+ finalizeBind();
+ }
+ }
+
+ private void sendStartSession() {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": sending legacy session to outdated server");
+ final IqPacket startSession = new IqPacket(IqPacket.TYPE.SET);
+ startSession.addChild("session", "urn:ietf:params:xml:ns:xmpp-session");
+ this.sendUnmodifiedIqPacket(startSession, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ 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");
+ disconnect(true);
+ }
+ }
+ });
+ }
+
+ private void sendPostBindInitialization() {
+ smVersion = 0;
+ if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) {
+ smVersion = 3;
+ } else if (streamFeatures.hasChild("sm", "urn:xmpp:sm:2")) {
+ smVersion = 2;
+ }
+ if (smVersion != 0) {
+ synchronized (this.mStanzaQueue) {
+ final EnablePacket enable = new EnablePacket(smVersion);
+ tagWriter.writeStanzaAsync(enable);
+ stanzasSent = 0;
+ mStanzaQueue.clear();
+ }
+ }
features.carbonsEnabled = false;
- features.blockListRequested = false;
- synchronized (this.disco) {
- this.disco.clear();
- }
- mPendingServiceDiscoveries.set(0);
+ features.blockListRequested = false;
+ synchronized (this.disco) {
+ this.disco.clear();
+ }
+ mPendingServiceDiscoveries.set(0);
mWaitForDisco.set(mServerIdentity != Identity.NIMBUZZ && smVersion != 0);
- lastDiscoStarted = SystemClock.elapsedRealtime();
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": starting service discovery");
- mXmppConnectionService.scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode());
- Element caps = streamFeatures.findChild("c");
- final String hash = caps == null ? null : caps.getAttribute("hash");
- final String ver = caps == null ? null : caps.getAttribute("ver");
- ServiceDiscoveryResult discoveryResult = null;
- if (hash != null && ver != null) {
- discoveryResult = mXmppConnectionService.getCachedServiceDiscoveryResult(new Pair<>(hash, ver));
- }
- if (discoveryResult == null) {
- sendServiceDiscoveryInfo(account.getServer());
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": server caps came from cache");
- disco.put(account.getServer(), discoveryResult);
- }
- sendServiceDiscoveryInfo(account.getJid().toBareJid());
- sendServiceDiscoveryItems(account.getServer());
-
- if (!mWaitForDisco.get()) {
- finalizeBind();
- }
- this.lastSessionStarted = SystemClock.elapsedRealtime();
- }
-
- private void sendServiceDiscoveryInfo(final Jid jid) {
- mPendingServiceDiscoveries.incrementAndGet();
- final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
- iq.setTo(jid);
- iq.query("http://jabber.org/protocol/disco#info");
- this.sendIqPacket(iq, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- boolean advancedStreamFeaturesLoaded;
- synchronized (XmppConnection.this.disco) {
- ServiceDiscoveryResult result = new ServiceDiscoveryResult(packet);
- for (final ServiceDiscoveryResult.Identity id : result.getIdentities()) {
- if (mServerIdentity == Identity.UNKNOWN && id.getType().equals("im") &&
- id.getCategory().equals("server") && id.getName() != null &&
- jid.equals(account.getServer())) {
- switch (id.getName()) {
- case "Prosody":
- mServerIdentity = Identity.PROSODY;
- break;
- case "ejabberd":
- mServerIdentity = Identity.EJABBERD;
- break;
- case "Slack-XMPP":
- mServerIdentity = Identity.SLACK;
- break;
- }
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server name: " + id.getName());
- }
- }
- if (jid.equals(account.getServer())) {
- mXmppConnectionService.databaseBackend.insertDiscoveryResult(result);
- }
- disco.put(jid, result);
- advancedStreamFeaturesLoaded = disco.containsKey(account.getServer())
- && disco.containsKey(account.getJid().toBareJid());
- }
- if (advancedStreamFeaturesLoaded && (jid.equals(account.getServer()) || jid.equals(account.getJid().toBareJid()))) {
- enableAdvancedStreamFeatures();
- }
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco info for " + jid.toString());
- }
- if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
- if (mPendingServiceDiscoveries.decrementAndGet() == 0
- && mWaitForDisco.compareAndSet(true, false)) {
- finalizeBind();
- }
- }
- }
- });
- }
-
- private void finalizeBind() {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource());
- if (bindListener != null) {
- bindListener.onBind(account);
- }
- changeStatus(Account.State.ONLINE);
- }
-
- private void enableAdvancedStreamFeatures() {
- if (getFeatures().carbons() && !features.carbonsEnabled) {
- sendEnableCarbons();
- }
- if (getFeatures().blocking() && !features.blockListRequested) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": Requesting block list");
- this.sendIqPacket(getIqGenerator().generateGetBlockList(), mXmppConnectionService.getIqParser());
- }
- for (final OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) {
- listener.onAdvancedStreamFeaturesAvailable(account);
- }
- }
-
- private void sendServiceDiscoveryItems(final Jid server) {
- mPendingServiceDiscoveries.incrementAndGet();
- final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
- iq.setTo(server.toDomainJid());
- iq.query("http://jabber.org/protocol/disco#items");
- this.sendIqPacket(iq, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- final List<Element> elements = packet.query().getChildren();
- for (final Element element : elements) {
- if (element.getName().equals("item")) {
- final Jid jid = element.getAttributeAsJid("jid");
- if (jid != null && !jid.equals(account.getServer())) {
- sendServiceDiscoveryInfo(jid);
- }
- }
- }
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco items of " + server);
- }
- if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
- if (mPendingServiceDiscoveries.decrementAndGet() == 0
- && mWaitForDisco.compareAndSet(true, false)) {
- finalizeBind();
- }
- }
- }
- });
- }
-
- private void sendEnableCarbons() {
- final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- iq.addChild("enable", "urn:xmpp:carbons:2");
- this.sendIqPacket(iq, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- if (!packet.hasChild("error")) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
- + ": successfully enabled carbons");
- features.carbonsEnabled = true;
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
- + ": error enableing carbons " + packet.toString());
- }
- }
- });
- }
-
- private void processStreamError(final Tag currentTag)
- throws XmlPullParserException, IOException {
- final Element streamError = tagReader.readElement(currentTag);
- if (streamError == null) {
- return;
- }
- if (streamError.hasChild("conflict")) {
- final String resource = account.getResource().split("\\.")[0];
- account.setResource(resource + "." + nextRandomId());
- Log.d(Config.LOGTAG,
- account.getJid().toBareJid() + ": switching resource due to conflict ("
- + account.getResource() + ")");
- throw new IOException();
- } else if (streamError.hasChild("host-unknown")) {
- throw new StreamErrorHostUnknown();
- } else if (streamError.hasChild("policy-violation")) {
- throw new StreamErrorPolicyViolation();
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": stream error "+streamError.toString());
- throw new StreamError();
- }
- }
-
- private void sendStartStream() throws IOException {
- final Tag stream = Tag.start("stream:stream");
- stream.setAttribute("to", account.getServer().toString());
- stream.setAttribute("version", "1.0");
- stream.setAttribute("xml:lang", "en");
- stream.setAttribute("xmlns", "jabber:client");
- stream.setAttribute("xmlns:stream", "http://etherx.jabber.org/streams");
- tagWriter.writeTag(stream);
- }
-
- private String nextRandomId() {
- return new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
- }
-
- public String sendIqPacket(final IqPacket packet, final OnIqPacketReceived callback) {
- packet.setFrom(account.getJid());
- return this.sendUnmodifiedIqPacket(packet, callback);
- }
-
- public synchronized String sendUnmodifiedIqPacket(final IqPacket packet, final OnIqPacketReceived callback) {
- if (packet.getId() == null) {
- final String id = nextRandomId();
- packet.setAttribute("id", id);
- }
- if (callback != null) {
- synchronized (this.packetCallbacks) {
- packetCallbacks.put(packet.getId(), new Pair<>(packet, callback));
- }
- }
- this.sendPacket(packet);
- return packet.getId();
- }
-
- public void sendMessagePacket(final MessagePacket packet) {
- this.sendPacket(packet);
- }
-
- public void sendPresencePacket(final PresencePacket packet) {
- this.sendPacket(packet);
- }
-
- private synchronized void sendPacket(final AbstractStanza packet) {
- if (stanzasSent == Integer.MAX_VALUE) {
- resetStreamId();
- disconnect(true);
- return;
- }
- synchronized (this.mStanzaQueue) {
- tagWriter.writeStanzaAsync(packet);
- if (packet instanceof AbstractAcknowledgeableStanza) {
- AbstractAcknowledgeableStanza stanza = (AbstractAcknowledgeableStanza) packet;
- ++stanzasSent;
- this.mStanzaQueue.append(stanzasSent, stanza);
- if (stanza instanceof MessagePacket && stanza.getId() != null && getFeatures().sm()) {
- if (Config.EXTENDED_SM_LOGGING) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": requesting ack for message stanza #" + stanzasSent);
- }
- tagWriter.writeStanzaAsync(new RequestPacket(this.smVersion));
- }
- }
- }
- }
-
- public void sendPing() {
- if (!r()) {
- final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
- iq.setFrom(account.getJid());
- iq.addChild("ping", "urn:xmpp:ping");
- this.sendIqPacket(iq, null);
- }
- this.lastPingSent = SystemClock.elapsedRealtime();
- }
-
- public void setOnMessagePacketReceivedListener(
- final OnMessagePacketReceived listener) {
- this.messageListener = listener;
- }
-
- public void setOnUnregisteredIqPacketReceivedListener(
- final OnIqPacketReceived listener) {
- this.unregisteredIqListener = listener;
- }
-
- public void setOnPresencePacketReceivedListener(
- final OnPresencePacketReceived listener) {
- this.presenceListener = listener;
- }
-
- public void setOnJinglePacketReceivedListener(
- final OnJinglePacketReceived listener) {
- this.jingleListener = listener;
- }
-
- public void setOnStatusChangedListener(final OnStatusChanged listener) {
- this.statusListener = listener;
- }
-
- public void setOnBindListener(final OnBindListener listener) {
- this.bindListener = listener;
- }
-
- public void setOnMessageAcknowledgeListener(final OnMessageAcknowledged listener) {
- this.acknowledgedListener = listener;
- }
-
- public void addOnAdvancedStreamFeaturesAvailableListener(final OnAdvancedStreamFeaturesLoaded listener) {
- if (!this.advancedStreamFeaturesLoadedListeners.contains(listener)) {
- this.advancedStreamFeaturesLoadedListeners.add(listener);
- }
- }
-
- public void waitForPush() {
- if (tagWriter.isActive()) {
- tagWriter.finish();
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- while(!tagWriter.finished()) {
- Thread.sleep(10);
- }
- socket.close();
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": closed tcp without closing stream");
- changeStatus(Account.State.OFFLINE);
- } catch (IOException | InterruptedException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": error while closing socket for waitForPush()");
- }
- }
- }).start();
- } else {
- forceCloseSocket();
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": closed tcp without closing stream (no waiting)");
- }
- }
-
- private void forceCloseSocket() {
- if (socket != null) {
- try {
- socket.close();
- } catch (IOException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": io exception "+e.getMessage()+" during force close");
- }
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": socket was null during force close");
- }
- }
-
- public void interrupt() {
- Thread.currentThread().interrupt();
- }
-
- public void disconnect(final boolean force) {
- interrupt();
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting force="+Boolean.valueOf(force));
- if (force) {
- tagWriter.forceClose();
- forceCloseSocket();
- } else {
- if (tagWriter.isActive()) {
- tagWriter.finish();
- try {
- int i = 0;
- boolean warned = false;
- while (!tagWriter.finished() && socket.isConnected() && i <= 10) {
- if (!warned) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()+": waiting for tag writer to finish");
- warned = true;
- }
- try {
- Thread.sleep(200);
- } catch(InterruptedException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": sleep interrupted");
- }
- i++;
- }
- if (warned) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": tag writer has finished");
- }
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": closing stream");
- tagWriter.writeTag(Tag.end("stream:stream"));
- } catch (final IOException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": io exception during disconnect ("+e.getMessage()+")");
- } finally {
- forceCloseSocket();
- }
- }
- }
- }
-
- public void resetStreamId() {
- this.streamId = null;
- }
-
- private List<Entry<Jid, ServiceDiscoveryResult>> findDiscoItemsByFeature(final String feature) {
- synchronized (this.disco) {
- final List<Entry<Jid, ServiceDiscoveryResult>> items = new ArrayList<>();
- for (final Entry<Jid, ServiceDiscoveryResult> cursor : this.disco.entrySet()) {
- if (cursor.getValue().getFeatures().contains(feature)) {
- items.add(cursor);
- }
- }
- return items;
- }
- }
-
- public Jid findDiscoItemByFeature(final String feature) {
- final List<Entry<Jid, ServiceDiscoveryResult>> items = findDiscoItemsByFeature(feature);
- if (items.size() >= 1) {
- return items.get(0).getKey();
- }
- return null;
- }
-
- public boolean r() {
- if (getFeatures().sm()) {
- this.tagWriter.writeStanzaAsync(new RequestPacket(smVersion));
- return true;
- } else {
- return false;
- }
- }
-
- public String getMucServer() {
- synchronized (this.disco) {
- for (final Entry<Jid, ServiceDiscoveryResult> cursor : disco.entrySet()) {
- final ServiceDiscoveryResult value = cursor.getValue();
- if (value.getFeatures().contains("http://jabber.org/protocol/muc")
- && !value.getFeatures().contains("jabber:iq:gateway")
- && !value.hasIdentity("conference", "irc")) {
- return cursor.getKey().toString();
- }
- }
- }
- return null;
- }
-
- public int getTimeToNextAttempt() {
- final int interval = Math.min((int) (25 * Math.pow(1.3, attempt)), 300);
- final int secondsSinceLast = (int) ((SystemClock.elapsedRealtime() - this.lastConnect) / 1000);
- return interval - secondsSinceLast;
- }
-
- public int getAttempt() {
- return this.attempt;
- }
-
- public Features getFeatures() {
- return this.features;
- }
-
- public long getLastSessionEstablished() {
- final long diff = SystemClock.elapsedRealtime() - this.lastSessionStarted;
- return System.currentTimeMillis() - diff;
- }
-
- public long getLastConnect() {
- return this.lastConnect;
- }
-
- public long getLastPingSent() {
- return this.lastPingSent;
- }
-
- public long getLastDiscoStarted() {
- return this.lastDiscoStarted;
- }
- public long getLastPacketReceived() {
- return this.lastPacketReceived;
- }
-
- public void sendActive() {
- this.sendPacket(new ActivePacket());
- }
-
- public void sendInactive() {
- this.sendPacket(new InactivePacket());
- }
-
- public void resetAttemptCount() {
- this.attempt = 0;
- this.lastConnect = 0;
- }
-
- public void setInteractive(boolean interactive) {
- this.mInteractive = interactive;
- }
-
- public Identity getServerIdentity() {
- return mServerIdentity;
- }
-
- private class UnauthorizedException extends IOException {
-
- }
-
- private class SecurityException extends IOException {
-
- }
-
- private class IncompatibleServerException extends IOException {
-
- }
-
- private class StreamErrorHostUnknown extends StreamError {
-
- }
-
- private class StreamErrorPolicyViolation extends StreamError {
-
- }
+ lastDiscoStarted = SystemClock.elapsedRealtime();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": starting service discovery");
+ mXmppConnectionService.scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode());
+ Element caps = streamFeatures.findChild("c");
+ final String hash = caps == null ? null : caps.getAttribute("hash");
+ final String ver = caps == null ? null : caps.getAttribute("ver");
+ ServiceDiscoveryResult discoveryResult = null;
+ if (hash != null && ver != null) {
+ discoveryResult = mXmppConnectionService.getCachedServiceDiscoveryResult(new Pair<>(hash, ver));
+ }
+ if (discoveryResult == null) {
+ sendServiceDiscoveryInfo(account.getServer());
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server caps came from cache");
+ disco.put(account.getServer(), discoveryResult);
+ }
+ sendServiceDiscoveryInfo(account.getJid().toBareJid());
+ sendServiceDiscoveryItems(account.getServer());
- private class StreamError extends IOException {
+ if (!mWaitForDisco.get()) {
+ finalizeBind();
+ }
+ this.lastSessionStarted = SystemClock.elapsedRealtime();
+ }
+
+ private void sendServiceDiscoveryInfo(final Jid jid) {
+ mPendingServiceDiscoveries.incrementAndGet();
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
+ iq.setTo(jid);
+ iq.query("http://jabber.org/protocol/disco#info");
+ this.sendIqPacket(iq, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ boolean advancedStreamFeaturesLoaded;
+ synchronized (XmppConnection.this.disco) {
+ ServiceDiscoveryResult result = new ServiceDiscoveryResult(packet);
+ for (final ServiceDiscoveryResult.Identity id : result.getIdentities()) {
+ if (mServerIdentity == Identity.UNKNOWN && id.getType().equals("im") &&
+ id.getCategory().equals("server") && id.getName() != null &&
+ jid.equals(account.getServer())) {
+ switch (id.getName()) {
+ case "Prosody":
+ mServerIdentity = Identity.PROSODY;
+ break;
+ case "ejabberd":
+ mServerIdentity = Identity.EJABBERD;
+ break;
+ case "Slack-XMPP":
+ mServerIdentity = Identity.SLACK;
+ break;
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server name: " + id.getName());
+ }
+ }
+ if (jid.equals(account.getServer())) {
+ mXmppConnectionService.databaseBackend.insertDiscoveryResult(result);
+ }
+ disco.put(jid, result);
+ advancedStreamFeaturesLoaded = disco.containsKey(account.getServer())
+ && disco.containsKey(account.getJid().toBareJid());
+ }
+ if (advancedStreamFeaturesLoaded && (jid.equals(account.getServer()) || jid.equals(account.getJid().toBareJid()))) {
+ enableAdvancedStreamFeatures();
+ }
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco info for " + jid.toString());
+ }
+ if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
+ if (mPendingServiceDiscoveries.decrementAndGet() == 0
+ && mWaitForDisco.compareAndSet(true, false)) {
+ finalizeBind();
+ }
+ }
+ }
+ });
+ }
+
+ private void finalizeBind() {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource());
+ if (bindListener != null) {
+ bindListener.onBind(account);
+ }
+ changeStatus(Account.State.ONLINE);
+ }
+
+ private void enableAdvancedStreamFeatures() {
+ if (getFeatures().carbons() && !features.carbonsEnabled) {
+ sendEnableCarbons();
+ }
+ if (getFeatures().blocking() && !features.blockListRequested) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": Requesting block list");
+ this.sendIqPacket(getIqGenerator().generateGetBlockList(), mXmppConnectionService.getIqParser());
+ }
+ for (final OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) {
+ listener.onAdvancedStreamFeaturesAvailable(account);
+ }
+ }
+
+ private void sendServiceDiscoveryItems(final Jid server) {
+ mPendingServiceDiscoveries.incrementAndGet();
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
+ iq.setTo(server.toDomainJid());
+ iq.query("http://jabber.org/protocol/disco#items");
+ this.sendIqPacket(iq, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ final List<Element> elements = packet.query().getChildren();
+ for (final Element element : elements) {
+ if (element.getName().equals("item")) {
+ final Jid jid = element.getAttributeAsJid("jid");
+ if (jid != null && !jid.equals(account.getServer())) {
+ sendServiceDiscoveryInfo(jid);
+ }
+ }
+ }
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco items of " + server);
+ }
+ if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
+ if (mPendingServiceDiscoveries.decrementAndGet() == 0
+ && mWaitForDisco.compareAndSet(true, false)) {
+ finalizeBind();
+ }
+ }
+ }
+ });
+ }
+
+ private void sendEnableCarbons() {
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ iq.addChild("enable", "urn:xmpp:carbons:2");
+ this.sendIqPacket(iq, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (!packet.hasChild("error")) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ + ": successfully enabled carbons");
+ features.carbonsEnabled = true;
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ + ": error enableing carbons " + packet.toString());
+ }
+ }
+ });
+ }
+
+ private void processStreamError(final Tag currentTag)
+ throws XmlPullParserException, IOException {
+ final Element streamError = tagReader.readElement(currentTag);
+ if (streamError == null) {
+ return;
+ }
+ if (streamError.hasChild("conflict")) {
+ final String resource = account.getResource().split("\\.")[0];
+ account.setResource(resource + "." + nextRandomId());
+ Log.d(Config.LOGTAG,
+ account.getJid().toBareJid() + ": switching resource due to conflict ("
+ + account.getResource() + ")");
+ throw new IOException();
+ } else if (streamError.hasChild("host-unknown")) {
+ throw new StreamErrorHostUnknown();
+ } else if (streamError.hasChild("policy-violation")) {
+ throw new StreamErrorPolicyViolation();
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": stream error " + streamError.toString());
+ throw new StreamError();
+ }
+ }
+
+ private void sendStartStream() throws IOException {
+ final Tag stream = Tag.start("stream:stream");
+ stream.setAttribute("to", account.getServer().toString());
+ stream.setAttribute("version", "1.0");
+ stream.setAttribute("xml:lang", "en");
+ stream.setAttribute("xmlns", "jabber:client");
+ stream.setAttribute("xmlns:stream", "http://etherx.jabber.org/streams");
+ tagWriter.writeTag(stream);
+ }
+
+ private String nextRandomId() {
+ return new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
+ }
+
+ public String sendIqPacket(final IqPacket packet, final OnIqPacketReceived callback) {
+ packet.setFrom(account.getJid());
+ return this.sendUnmodifiedIqPacket(packet, callback);
+ }
+
+ public synchronized String sendUnmodifiedIqPacket(final IqPacket packet, final OnIqPacketReceived callback) {
+ if (packet.getId() == null) {
+ final String id = nextRandomId();
+ packet.setAttribute("id", id);
+ }
+ if (callback != null) {
+ synchronized (this.packetCallbacks) {
+ packetCallbacks.put(packet.getId(), new Pair<>(packet, callback));
+ }
+ }
+ this.sendPacket(packet);
+ return packet.getId();
+ }
+
+ public void sendMessagePacket(final MessagePacket packet) {
+ this.sendPacket(packet);
+ }
+
+ public void sendPresencePacket(final PresencePacket packet) {
+ this.sendPacket(packet);
+ }
+
+ private synchronized void sendPacket(final AbstractStanza packet) {
+ if (stanzasSent == Integer.MAX_VALUE) {
+ resetStreamId();
+ disconnect(true);
+ return;
+ }
+ synchronized (this.mStanzaQueue) {
+ tagWriter.writeStanzaAsync(packet);
+ if (packet instanceof AbstractAcknowledgeableStanza) {
+ AbstractAcknowledgeableStanza stanza = (AbstractAcknowledgeableStanza) packet;
+ ++stanzasSent;
+ this.mStanzaQueue.append(stanzasSent, stanza);
+ if (stanza instanceof MessagePacket && stanza.getId() != null && getFeatures().sm()) {
+ if (Config.EXTENDED_SM_LOGGING) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": requesting ack for message stanza #" + stanzasSent);
+ }
+ tagWriter.writeStanzaAsync(new RequestPacket(this.smVersion));
+ }
+ }
+ }
+ }
+
+ public void sendPing() {
+ if (!r()) {
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
+ iq.setFrom(account.getJid());
+ iq.addChild("ping", "urn:xmpp:ping");
+ this.sendIqPacket(iq, null);
+ }
+ this.lastPingSent = SystemClock.elapsedRealtime();
+ }
+
+ public void setOnMessagePacketReceivedListener(
+ final OnMessagePacketReceived listener) {
+ this.messageListener = listener;
+ }
+
+ public void setOnUnregisteredIqPacketReceivedListener(
+ final OnIqPacketReceived listener) {
+ this.unregisteredIqListener = listener;
+ }
+
+ public void setOnPresencePacketReceivedListener(
+ final OnPresencePacketReceived listener) {
+ this.presenceListener = listener;
+ }
+
+ public void setOnJinglePacketReceivedListener(
+ final OnJinglePacketReceived listener) {
+ this.jingleListener = listener;
+ }
+
+ public void setOnStatusChangedListener(final OnStatusChanged listener) {
+ this.statusListener = listener;
+ }
+
+ public void setOnBindListener(final OnBindListener listener) {
+ this.bindListener = listener;
+ }
+
+ public void setOnMessageAcknowledgeListener(final OnMessageAcknowledged listener) {
+ this.acknowledgedListener = listener;
+ }
+
+ public void addOnAdvancedStreamFeaturesAvailableListener(final OnAdvancedStreamFeaturesLoaded listener) {
+ if (!this.advancedStreamFeaturesLoadedListeners.contains(listener)) {
+ this.advancedStreamFeaturesLoadedListeners.add(listener);
+ }
+ }
+
+ public void waitForPush() {
+ if (tagWriter.isActive()) {
+ tagWriter.finish();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ while (!tagWriter.finished()) {
+ Thread.sleep(10);
+ }
+ socket.close();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": closed tcp without closing stream");
+ changeStatus(Account.State.OFFLINE);
+ } catch (IOException | InterruptedException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": error while closing socket for waitForPush()");
+ }
+ }
+ }).start();
+ } else {
+ forceCloseSocket();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": closed tcp without closing stream (no waiting)");
+ }
+ }
+
+ private void forceCloseSocket() {
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch (IOException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": io exception " + e.getMessage() + " during force close");
+ }
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": socket was null during force close");
+ }
+ }
+
+ public void interrupt() {
+ Thread.currentThread().interrupt();
+ }
+
+ public void disconnect(final boolean force) {
+ interrupt();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting force=" + Boolean.valueOf(force));
+ if (force) {
+ tagWriter.forceClose();
+ forceCloseSocket();
+ } else {
+ if (tagWriter.isActive()) {
+ tagWriter.finish();
+ try {
+ int i = 0;
+ boolean warned = false;
+ while (!tagWriter.finished() && socket.isConnected() && i <= 10) {
+ if (!warned) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": waiting for tag writer to finish");
+ warned = true;
+ }
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": sleep interrupted");
+ }
+ i++;
+ }
+ if (warned) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": tag writer has finished");
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": closing stream");
+ tagWriter.writeTag(Tag.end("stream:stream"));
+ } catch (final IOException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": io exception during disconnect (" + e.getMessage() + ")");
+ } finally {
+ forceCloseSocket();
+ }
+ }
+ }
+ }
+
+ public void resetStreamId() {
+ this.streamId = null;
+ }
+
+ private List<Entry<Jid, ServiceDiscoveryResult>> findDiscoItemsByFeature(final String feature) {
+ synchronized (this.disco) {
+ final List<Entry<Jid, ServiceDiscoveryResult>> items = new ArrayList<>();
+ for (final Entry<Jid, ServiceDiscoveryResult> cursor : this.disco.entrySet()) {
+ if (cursor.getValue().getFeatures().contains(feature)) {
+ items.add(cursor);
+ }
+ }
+ return items;
+ }
+ }
+
+ public Jid findDiscoItemByFeature(final String feature) {
+ final List<Entry<Jid, ServiceDiscoveryResult>> items = findDiscoItemsByFeature(feature);
+ if (items.size() >= 1) {
+ return items.get(0).getKey();
+ }
+ return null;
+ }
+
+ public boolean r() {
+ if (getFeatures().sm()) {
+ this.tagWriter.writeStanzaAsync(new RequestPacket(smVersion));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public String getMucServer() {
+ synchronized (this.disco) {
+ for (final Entry<Jid, ServiceDiscoveryResult> cursor : disco.entrySet()) {
+ final ServiceDiscoveryResult value = cursor.getValue();
+ if (value.getFeatures().contains("http://jabber.org/protocol/muc")
+ && !value.getFeatures().contains("jabber:iq:gateway")
+ && !value.hasIdentity("conference", "irc")) {
+ return cursor.getKey().toString();
+ }
+ }
+ }
+ return null;
+ }
+
+ public int getTimeToNextAttempt() {
+ final int interval = Math.min((int) (25 * Math.pow(1.3, attempt)), 300);
+ final int secondsSinceLast = (int) ((SystemClock.elapsedRealtime() - this.lastConnect) / 1000);
+ return interval - secondsSinceLast;
+ }
+
+ public int getAttempt() {
+ return this.attempt;
+ }
+
+ public Features getFeatures() {
+ return this.features;
+ }
+
+ public long getLastSessionEstablished() {
+ final long diff = SystemClock.elapsedRealtime() - this.lastSessionStarted;
+ return System.currentTimeMillis() - diff;
+ }
+
+ public long getLastConnect() {
+ return this.lastConnect;
+ }
+
+ public long getLastPingSent() {
+ return this.lastPingSent;
+ }
+
+ public long getLastDiscoStarted() {
+ return this.lastDiscoStarted;
+ }
+
+ public long getLastPacketReceived() {
+ return this.lastPacketReceived;
+ }
+
+ public void sendActive() {
+ this.sendPacket(new ActivePacket());
+ }
- }
+ public void sendInactive() {
+ this.sendPacket(new InactivePacket());
+ }
- private class PaymentRequiredException extends IOException {
+ public void resetAttemptCount() {
+ this.attempt = 0;
+ this.lastConnect = 0;
+ }
- }
+ public void setInteractive(boolean interactive) {
+ this.mInteractive = interactive;
+ }
- public enum Identity {
- FACEBOOK,
- SLACK,
- EJABBERD,
- PROSODY,
- NIMBUZZ,
- UNKNOWN
- }
+ public Identity getServerIdentity() {
+ return mServerIdentity;
+ }
- public class Features {
- XmppConnection connection;
- private boolean carbonsEnabled = false;
- private boolean encryptionEnabled = false;
- private boolean blockListRequested = false;
+ private class UnauthorizedException extends IOException {
- public Features(final XmppConnection connection) {
- this.connection = connection;
- }
+ }
- private boolean hasDiscoFeature(final Jid server, final String feature) {
- synchronized (XmppConnection.this.disco) {
- return connection.disco.containsKey(server) &&
- connection.disco.get(server).getFeatures().contains(feature);
- }
- }
+ private class SecurityException extends IOException {
- public boolean carbons() {
- return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2");
- }
+ }
- public boolean blocking() {
- return hasDiscoFeature(account.getServer(), Xmlns.BLOCKING);
- }
+ private class IncompatibleServerException extends IOException {
+
+ }
+
+ private class StreamErrorHostUnknown extends StreamError {
+
+ }
+
+ private class StreamErrorPolicyViolation extends StreamError {
+
+ }
+
+ private class StreamError extends IOException {
+
+ }
+
+ private class PaymentRequiredException extends IOException {
+
+ }
+
+ public enum Identity {
+ FACEBOOK,
+ SLACK,
+ EJABBERD,
+ PROSODY,
+ NIMBUZZ,
+ UNKNOWN
+ }
+
+ public class Features {
+ XmppConnection connection;
+ private boolean carbonsEnabled = false;
+ private boolean encryptionEnabled = false;
+ private boolean blockListRequested = false;
+
+ public Features(final XmppConnection connection) {
+ this.connection = connection;
+ }
+
+ private boolean hasDiscoFeature(final Jid server, final String feature) {
+ synchronized (XmppConnection.this.disco) {
+ return connection.disco.containsKey(server) &&
+ connection.disco.get(server).getFeatures().contains(feature);
+ }
+ }
+
+ public boolean carbons() {
+ return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2");
+ }
+
+ public boolean blocking() {
+ return hasDiscoFeature(account.getServer(), Xmlns.BLOCKING);
+ }
public boolean spamReporting() {
return hasDiscoFeature(account.getServer(), "urn:xmpp:reporting:reason:spam:0");
}
- public boolean register() {
- return hasDiscoFeature(account.getServer(), Xmlns.REGISTER);
- }
-
- public boolean sm() {
- return streamId != null
- || (connection.streamFeatures != null && connection.streamFeatures.hasChild("sm"));
- }
-
- public boolean csi() {
- return connection.streamFeatures != null && connection.streamFeatures.hasChild("csi", "urn:xmpp:csi:0");
- }
-
- public boolean pep() {
- synchronized (XmppConnection.this.disco) {
- ServiceDiscoveryResult info = disco.get(account.getJid().toBareJid());
- return info != null && info.hasIdentity("pubsub", "pep");
- }
- }
-
- public boolean pepPersistent() {
- synchronized (XmppConnection.this.disco) {
- ServiceDiscoveryResult info = disco.get(account.getJid().toBareJid());
- return info != null && info.getFeatures().contains("http://jabber.org/protocol/pubsub#persistent-items");
- }
- }
-
- public boolean mam() {
- return hasDiscoFeature(account.getJid().toBareJid(), "urn:xmpp:mam:0")
- || hasDiscoFeature(account.getServer(), "urn:xmpp:mam:0");
- }
-
- public boolean push() {
- return hasDiscoFeature(account.getJid().toBareJid(), "urn:xmpp:push:0")
- || hasDiscoFeature(account.getServer(), "urn:xmpp:push:0");
- }
-
- public boolean rosterVersioning() {
- return connection.streamFeatures != null && connection.streamFeatures.hasChild("ver");
- }
-
- public void setBlockListRequested(boolean value) {
- this.blockListRequested = value;
- }
-
- public boolean httpUpload(long filesize) {
- if (Config.DISABLE_HTTP_UPLOAD) {
- return false;
- } else {
- List<Entry<Jid, ServiceDiscoveryResult>> items = findDiscoItemsByFeature(Xmlns.HTTP_UPLOAD);
- if (items.size() > 0) {
- try {
- long maxsize = Long.parseLong(items.get(0).getValue().getExtendedDiscoInformation(Xmlns.HTTP_UPLOAD, "max-file-size"));
- if(filesize <= maxsize) {
- return true;
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": http upload is not available for files with size "+filesize+" (max is "+maxsize+")");
- return false;
- }
- } catch (Exception e) {
- return true;
- }
- } else {
- return false;
- }
- }
- }
-
- public long getMaxHttpUploadSize() {
- List<Entry<Jid, ServiceDiscoveryResult>> items = findDiscoItemsByFeature(Xmlns.HTTP_UPLOAD);
- if (items.size() > 0) {
- try {
- return Long.parseLong(items.get(0).getValue().getExtendedDiscoInformation(Xmlns.HTTP_UPLOAD, "max-file-size"));
- } catch (Exception e) {
- return -1;
- }
- } else {
- return -1;
- }
- }
+ public boolean register() {
+ return hasDiscoFeature(account.getServer(), Xmlns.REGISTER);
+ }
+
+ public boolean sm() {
+ return streamId != null
+ || (connection.streamFeatures != null && connection.streamFeatures.hasChild("sm"));
+ }
+
+ public boolean csi() {
+ return connection.streamFeatures != null && connection.streamFeatures.hasChild("csi", "urn:xmpp:csi:0");
+ }
+
+ public boolean pep() {
+ synchronized (XmppConnection.this.disco) {
+ ServiceDiscoveryResult info = disco.get(account.getJid().toBareJid());
+ return info != null && info.hasIdentity("pubsub", "pep");
+ }
+ }
+
+ public boolean pepPersistent() {
+ synchronized (XmppConnection.this.disco) {
+ ServiceDiscoveryResult info = disco.get(account.getJid().toBareJid());
+ return info != null && info.getFeatures().contains("http://jabber.org/protocol/pubsub#persistent-items");
+ }
+ }
+
+ public boolean mam() {
+ return hasDiscoFeature(account.getJid().toBareJid(), "urn:xmpp:mam:0")
+ || hasDiscoFeature(account.getServer(), "urn:xmpp:mam:0");
+ }
+
+ public boolean push() {
+ return hasDiscoFeature(account.getJid().toBareJid(), "urn:xmpp:push:0")
+ || hasDiscoFeature(account.getServer(), "urn:xmpp:push:0");
+ }
+
+ public boolean rosterVersioning() {
+ return connection.streamFeatures != null && connection.streamFeatures.hasChild("ver");
+ }
+
+ public void setBlockListRequested(boolean value) {
+ this.blockListRequested = value;
+ }
+
+ public boolean httpUpload(long filesize) {
+ if (Config.DISABLE_HTTP_UPLOAD) {
+ return false;
+ } else {
+ List<Entry<Jid, ServiceDiscoveryResult>> items = findDiscoItemsByFeature(Xmlns.HTTP_UPLOAD);
+ if (items.size() > 0) {
+ try {
+ long maxsize = Long.parseLong(items.get(0).getValue().getExtendedDiscoInformation(Xmlns.HTTP_UPLOAD, "max-file-size"));
+ if (filesize <= maxsize) {
+ return true;
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": http upload is not available for files with size " + filesize + " (max is " + maxsize + ")");
+ return false;
+ }
+ } catch (Exception e) {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+
+ public long getMaxHttpUploadSize() {
+ List<Entry<Jid, ServiceDiscoveryResult>> items = findDiscoItemsByFeature(Xmlns.HTTP_UPLOAD);
+ if (items.size() > 0) {
+ try {
+ return Long.parseLong(items.get(0).getValue().getExtendedDiscoInformation(Xmlns.HTTP_UPLOAD, "max-file-size"));
+ } catch (Exception e) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ }
public boolean stanzaIds() {
- return hasDiscoFeature(account.getJid().toBareJid(),Xmlns.STANZA_IDS);
+ return hasDiscoFeature(account.getJid().toBareJid(), Xmlns.STANZA_IDS);
}
- }
+ }
- private IqGenerator getIqGenerator() {
- return mXmppConnectionService.getIqGenerator();
- }
+ private IqGenerator getIqGenerator() {
+ return mXmppConnectionService.getIqGenerator();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/chatstate/ChatState.java b/src/main/java/de/pixart/messenger/xmpp/chatstate/ChatState.java
index c3117455c..169d0bb66 100644
--- a/src/main/java/de/pixart/messenger/xmpp/chatstate/ChatState.java
+++ b/src/main/java/de/pixart/messenger/xmpp/chatstate/ChatState.java
@@ -4,29 +4,29 @@ import de.pixart.messenger.xml.Element;
public enum ChatState {
- ACTIVE, INACTIVE, GONE, COMPOSING, PAUSED;
+ ACTIVE, INACTIVE, GONE, COMPOSING, PAUSED;
- public static ChatState parse(Element element) {
- final String NAMESPACE = "http://jabber.org/protocol/chatstates";
- if (element.hasChild("active",NAMESPACE)) {
- return ACTIVE;
- } else if (element.hasChild("inactive",NAMESPACE)) {
- return INACTIVE;
- } else if (element.hasChild("composing",NAMESPACE)) {
- return COMPOSING;
- } else if (element.hasChild("gone",NAMESPACE)) {
- return GONE;
- } else if (element.hasChild("paused",NAMESPACE)) {
- return PAUSED;
- } else {
- return null;
- }
- }
+ public static ChatState parse(Element element) {
+ final String NAMESPACE = "http://jabber.org/protocol/chatstates";
+ if (element.hasChild("active", NAMESPACE)) {
+ return ACTIVE;
+ } else if (element.hasChild("inactive", NAMESPACE)) {
+ return INACTIVE;
+ } else if (element.hasChild("composing", NAMESPACE)) {
+ return COMPOSING;
+ } else if (element.hasChild("gone", NAMESPACE)) {
+ return GONE;
+ } else if (element.hasChild("paused", NAMESPACE)) {
+ return PAUSED;
+ } else {
+ return null;
+ }
+ }
- public static Element toElement(ChatState state) {
- final String NAMESPACE = "http://jabber.org/protocol/chatstates";
- final Element element = new Element(state.toString().toLowerCase());
- element.setAttribute("xmlns",NAMESPACE);
- return element;
- }
+ public static Element toElement(ChatState state) {
+ final String NAMESPACE = "http://jabber.org/protocol/chatstates";
+ final Element element = new Element(state.toString().toLowerCase());
+ element.setAttribute("xmlns", NAMESPACE);
+ return element;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/forms/Data.java b/src/main/java/de/pixart/messenger/xmpp/forms/Data.java
index 8ae70f9c7..f92e468e5 100644
--- a/src/main/java/de/pixart/messenger/xmpp/forms/Data.java
+++ b/src/main/java/de/pixart/messenger/xmpp/forms/Data.java
@@ -9,91 +9,91 @@ import de.pixart.messenger.xml.Element;
public class Data extends Element {
- public static final String FORM_TYPE = "FORM_TYPE";
-
- public Data() {
- super("x");
- this.setAttribute("xmlns","jabber:x:data");
- }
-
- public List<Field> getFields() {
- ArrayList<Field> fields = new ArrayList<Field>();
- for(Element child : getChildren()) {
- if (child.getName().equals("field")
- && !FORM_TYPE.equals(child.getAttribute("var"))) {
- fields.add(Field.parse(child));
- }
- }
- return fields;
- }
-
- public Field getFieldByName(String needle) {
- for(Element child : getChildren()) {
- if (child.getName().equals("field")
- && needle.equals(child.getAttribute("var"))) {
- return Field.parse(child);
- }
- }
- return null;
- }
-
- public void put(String name, String value) {
- Field field = getFieldByName(name);
- if (field == null) {
- field = new Field(name);
- this.addChild(field);
- }
- field.setValue(value);
- }
-
- public void put(String name, Collection<String> values) {
- Field field = getFieldByName(name);
- if (field == null) {
- field = new Field(name);
- this.addChild(field);
- }
- field.setValues(values);
- }
-
- public void submit() {
- this.setAttribute("type","submit");
- removeUnnecessaryChildren();
- for(Field field : getFields()) {
- field.removeNonValueChildren();
- }
- }
-
- private void removeUnnecessaryChildren() {
- for(Iterator<Element> iterator = this.children.iterator(); iterator.hasNext();) {
- Element element = iterator.next();
- if (!element.getName().equals("field") && !element.getName().equals("title")) {
- iterator.remove();
- }
- }
- }
-
- public static Data parse(Element element) {
- Data data = new Data();
- data.setAttributes(element.getAttributes());
- data.setChildren(element.getChildren());
- return data;
- }
-
- public void setFormType(String formType) {
- this.put(FORM_TYPE, formType);
- }
-
- public String getFormType() {
- String type = getValue(FORM_TYPE);
- return type == null ? "" : type;
- }
-
- public String getValue(String name) {
- Field field = this.getFieldByName(name);
- return field == null ? null : field.getValue();
- }
-
- public String getTitle() {
- return findChildContent("title");
- }
+ public static final String FORM_TYPE = "FORM_TYPE";
+
+ public Data() {
+ super("x");
+ this.setAttribute("xmlns", "jabber:x:data");
+ }
+
+ public List<Field> getFields() {
+ ArrayList<Field> fields = new ArrayList<Field>();
+ for (Element child : getChildren()) {
+ if (child.getName().equals("field")
+ && !FORM_TYPE.equals(child.getAttribute("var"))) {
+ fields.add(Field.parse(child));
+ }
+ }
+ return fields;
+ }
+
+ public Field getFieldByName(String needle) {
+ for (Element child : getChildren()) {
+ if (child.getName().equals("field")
+ && needle.equals(child.getAttribute("var"))) {
+ return Field.parse(child);
+ }
+ }
+ return null;
+ }
+
+ public void put(String name, String value) {
+ Field field = getFieldByName(name);
+ if (field == null) {
+ field = new Field(name);
+ this.addChild(field);
+ }
+ field.setValue(value);
+ }
+
+ public void put(String name, Collection<String> values) {
+ Field field = getFieldByName(name);
+ if (field == null) {
+ field = new Field(name);
+ this.addChild(field);
+ }
+ field.setValues(values);
+ }
+
+ public void submit() {
+ this.setAttribute("type", "submit");
+ removeUnnecessaryChildren();
+ for (Field field : getFields()) {
+ field.removeNonValueChildren();
+ }
+ }
+
+ private void removeUnnecessaryChildren() {
+ for (Iterator<Element> iterator = this.children.iterator(); iterator.hasNext(); ) {
+ Element element = iterator.next();
+ if (!element.getName().equals("field") && !element.getName().equals("title")) {
+ iterator.remove();
+ }
+ }
+ }
+
+ public static Data parse(Element element) {
+ Data data = new Data();
+ data.setAttributes(element.getAttributes());
+ data.setChildren(element.getChildren());
+ return data;
+ }
+
+ public void setFormType(String formType) {
+ this.put(FORM_TYPE, formType);
+ }
+
+ public String getFormType() {
+ String type = getValue(FORM_TYPE);
+ return type == null ? "" : type;
+ }
+
+ public String getValue(String name) {
+ Field field = this.getFieldByName(name);
+ return field == null ? null : field.getValue();
+ }
+
+ public String getTitle() {
+ return findChildContent("title");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/forms/Field.java b/src/main/java/de/pixart/messenger/xmpp/forms/Field.java
index 4cf5fc6b7..a05f9538e 100644
--- a/src/main/java/de/pixart/messenger/xmpp/forms/Field.java
+++ b/src/main/java/de/pixart/messenger/xmpp/forms/Field.java
@@ -9,73 +9,73 @@ import de.pixart.messenger.xml.Element;
public class Field extends Element {
- public Field(String name) {
- super("field");
- this.setAttribute("var",name);
- }
+ public Field(String name) {
+ super("field");
+ this.setAttribute("var", name);
+ }
- private Field() {
- super("field");
- }
+ private Field() {
+ super("field");
+ }
- public String getFieldName() {
- return this.getAttribute("var");
- }
+ public String getFieldName() {
+ return this.getAttribute("var");
+ }
- public void setValue(String value) {
- this.children.clear();
- this.addChild("value").setContent(value);
- }
+ public void setValue(String value) {
+ this.children.clear();
+ this.addChild("value").setContent(value);
+ }
- public void setValues(Collection<String> values) {
- this.children.clear();
- for(String value : values) {
- this.addChild("value").setContent(value);
- }
- }
+ public void setValues(Collection<String> values) {
+ this.children.clear();
+ for (String value : values) {
+ this.addChild("value").setContent(value);
+ }
+ }
- public void removeNonValueChildren() {
- for(Iterator<Element> iterator = this.children.iterator(); iterator.hasNext();) {
- Element element = iterator.next();
- if (!element.getName().equals("value")) {
- iterator.remove();
- }
- }
- }
+ public void removeNonValueChildren() {
+ for (Iterator<Element> iterator = this.children.iterator(); iterator.hasNext(); ) {
+ Element element = iterator.next();
+ if (!element.getName().equals("value")) {
+ iterator.remove();
+ }
+ }
+ }
- public static Field parse(Element element) {
- Field field = new Field();
- field.setAttributes(element.getAttributes());
- field.setChildren(element.getChildren());
- return field;
- }
+ public static Field parse(Element element) {
+ Field field = new Field();
+ field.setAttributes(element.getAttributes());
+ field.setChildren(element.getChildren());
+ return field;
+ }
- public String getValue() {
- return findChildContent("value");
- }
+ public String getValue() {
+ return findChildContent("value");
+ }
- public List<String> getValues() {
- List<String> values = new ArrayList<>();
- for(Element child : getChildren()) {
- if ("value".equals(child.getName())) {
- String content = child.getContent();
- if (content != null) {
- values.add(content);
- }
- }
- }
- return values;
- }
+ public List<String> getValues() {
+ List<String> values = new ArrayList<>();
+ for (Element child : getChildren()) {
+ if ("value".equals(child.getName())) {
+ String content = child.getContent();
+ if (content != null) {
+ values.add(content);
+ }
+ }
+ }
+ return values;
+ }
- public String getLabel() {
- return getAttribute("label");
- }
+ public String getLabel() {
+ return getAttribute("label");
+ }
- public String getType() {
- return getAttribute("type");
- }
+ public String getType() {
+ return getAttribute("type");
+ }
- public boolean isRequired() {
- return hasChild("required");
- }
+ public boolean isRequired() {
+ return hasChild("required");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jid/InvalidJidException.java b/src/main/java/de/pixart/messenger/xmpp/jid/InvalidJidException.java
index 9f091063a..fa1b396d4 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jid/InvalidJidException.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jid/InvalidJidException.java
@@ -8,7 +8,7 @@ public class InvalidJidException extends Exception {
public final static String INVALID_PART_LENGTH = "JID part must be between 0 and 1023 characters";
public final static String INVALID_CHARACTER = "JID contains an invalid character";
public final static String STRINGPREP_FAIL = "The STRINGPREP operation has failed for the given JID";
- public final static String IS_NULL = "JID can not be NULL";
+ public final static String IS_NULL = "JID can not be NULL";
/**
* Constructs a new {@code Exception} that includes the current stack trace.
@@ -31,7 +31,7 @@ public class InvalidJidException extends Exception {
* specified detail message and the specified cause.
*
* @param detailMessage the detail message for this exception.
- * @param throwable the cause of this exception.
+ * @param throwable the cause of this exception.
*/
public InvalidJidException(final String detailMessage, final Throwable throwable) {
super(detailMessage, throwable);
diff --git a/src/main/java/de/pixart/messenger/xmpp/jid/Jid.java b/src/main/java/de/pixart/messenger/xmpp/jid/Jid.java
index c596367ca..123e6ee8f 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jid/Jid.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jid/Jid.java
@@ -15,225 +15,225 @@ import gnu.inet.encoding.StringprepException;
*/
public final class Jid {
- private static LruCache<String,Jid> cache = new LruCache<>(1024);
+ private static LruCache<String, Jid> cache = new LruCache<>(1024);
- private final String localpart;
- private final String domainpart;
- private final String resourcepart;
+ private final String localpart;
+ private final String domainpart;
+ private final String resourcepart;
// It's much more efficient to store the full JID as well as the parts instead of figuring them
// all out every time (since some characters are displayed but aren't used for comparisons).
private final String displayjid;
- public String getLocalpart() {
- return localpart;
- }
-
- public String getDomainpart() {
- return IDN.toUnicode(domainpart);
- }
-
- public String getResourcepart() {
- return resourcepart;
- }
-
- public static Jid fromSessionID(final SessionID id) throws InvalidJidException{
- if (id.getUserID().isEmpty()) {
- return Jid.fromString(id.getAccountID());
- } else {
- return Jid.fromString(id.getAccountID()+"/"+id.getUserID());
- }
- }
-
- public static Jid fromString(final String jid) throws InvalidJidException {
- return Jid.fromString(jid, false);
- }
-
- public static Jid fromString(final String jid, final boolean safe) throws InvalidJidException {
- return new Jid(jid, safe);
- }
-
- public static Jid fromParts(final String localpart,
- final String domainpart,
- final String resourcepart) throws InvalidJidException {
- String out;
- if (localpart == null || localpart.isEmpty()) {
- out = domainpart;
- } else {
- out = localpart + "@" + domainpart;
- }
- if (resourcepart != null && !resourcepart.isEmpty()) {
- out = out + "/" + resourcepart;
- }
- return new Jid(out, false);
- }
-
- private Jid(final String jid, final boolean safe) throws InvalidJidException {
- if (jid == null) throw new InvalidJidException(InvalidJidException.IS_NULL);
-
- Jid fromCache = Jid.cache.get(jid);
- if (fromCache != null) {
+ public String getLocalpart() {
+ return localpart;
+ }
+
+ public String getDomainpart() {
+ return IDN.toUnicode(domainpart);
+ }
+
+ public String getResourcepart() {
+ return resourcepart;
+ }
+
+ public static Jid fromSessionID(final SessionID id) throws InvalidJidException {
+ if (id.getUserID().isEmpty()) {
+ return Jid.fromString(id.getAccountID());
+ } else {
+ return Jid.fromString(id.getAccountID() + "/" + id.getUserID());
+ }
+ }
+
+ public static Jid fromString(final String jid) throws InvalidJidException {
+ return Jid.fromString(jid, false);
+ }
+
+ public static Jid fromString(final String jid, final boolean safe) throws InvalidJidException {
+ return new Jid(jid, safe);
+ }
+
+ public static Jid fromParts(final String localpart,
+ final String domainpart,
+ final String resourcepart) throws InvalidJidException {
+ String out;
+ if (localpart == null || localpart.isEmpty()) {
+ out = domainpart;
+ } else {
+ out = localpart + "@" + domainpart;
+ }
+ if (resourcepart != null && !resourcepart.isEmpty()) {
+ out = out + "/" + resourcepart;
+ }
+ return new Jid(out, false);
+ }
+
+ private Jid(final String jid, final boolean safe) throws InvalidJidException {
+ if (jid == null) throw new InvalidJidException(InvalidJidException.IS_NULL);
+
+ Jid fromCache = Jid.cache.get(jid);
+ if (fromCache != null) {
displayjid = fromCache.displayjid;
- localpart = fromCache.localpart;
- domainpart = fromCache.domainpart;
- resourcepart = fromCache.resourcepart;
- return;
- }
-
- // Hackish Android way to count the number of chars in a string... should work everywhere.
- final int atCount = jid.length() - jid.replace("@", "").length();
- final int slashCount = jid.length() - jid.replace("/", "").length();
-
- // Throw an error if there's anything obvious wrong with the JID...
- if (jid.isEmpty() || jid.length() > 3071) {
- throw new InvalidJidException(InvalidJidException.INVALID_LENGTH);
- }
-
- // Go ahead and check if the localpart or resourcepart is empty.
- if (jid.startsWith("@") || (jid.endsWith("@") && slashCount == 0) || jid.startsWith("/") || (jid.endsWith("/") && slashCount < 2)) {
- throw new InvalidJidException(InvalidJidException.INVALID_CHARACTER);
- }
+ localpart = fromCache.localpart;
+ domainpart = fromCache.domainpart;
+ resourcepart = fromCache.resourcepart;
+ return;
+ }
+
+ // Hackish Android way to count the number of chars in a string... should work everywhere.
+ final int atCount = jid.length() - jid.replace("@", "").length();
+ final int slashCount = jid.length() - jid.replace("/", "").length();
+
+ // Throw an error if there's anything obvious wrong with the JID...
+ if (jid.isEmpty() || jid.length() > 3071) {
+ throw new InvalidJidException(InvalidJidException.INVALID_LENGTH);
+ }
+
+ // Go ahead and check if the localpart or resourcepart is empty.
+ if (jid.startsWith("@") || (jid.endsWith("@") && slashCount == 0) || jid.startsWith("/") || (jid.endsWith("/") && slashCount < 2)) {
+ throw new InvalidJidException(InvalidJidException.INVALID_CHARACTER);
+ }
String finaljid;
- final int domainpartStart;
- final int atLoc = jid.indexOf("@");
- final int slashLoc = jid.indexOf("/");
- // If there is no "@" in the JID (eg. "example.net" or "example.net/resource")
- // or there are one or more "@" signs but they're all in the resourcepart (eg. "example.net/@/rp@"):
- if (atCount == 0 || (atCount > 0 && slashLoc != -1 && atLoc > slashLoc)) {
- localpart = "";
+ final int domainpartStart;
+ final int atLoc = jid.indexOf("@");
+ final int slashLoc = jid.indexOf("/");
+ // If there is no "@" in the JID (eg. "example.net" or "example.net/resource")
+ // or there are one or more "@" signs but they're all in the resourcepart (eg. "example.net/@/rp@"):
+ if (atCount == 0 || (atCount > 0 && slashLoc != -1 && atLoc > slashLoc)) {
+ localpart = "";
finaljid = "";
- domainpartStart = 0;
- } else {
- final String lp = jid.substring(0, atLoc);
- try {
- localpart = Config.DISABLE_STRING_PREP || safe ? lp : Stringprep.nodeprep(lp);
- } catch (final StringprepException e) {
- throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
- }
- if (localpart.isEmpty() || localpart.length() > 1023) {
- throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
- }
- domainpartStart = atLoc + 1;
+ domainpartStart = 0;
+ } else {
+ final String lp = jid.substring(0, atLoc);
+ try {
+ localpart = Config.DISABLE_STRING_PREP || safe ? lp : Stringprep.nodeprep(lp);
+ } catch (final StringprepException e) {
+ throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
+ }
+ if (localpart.isEmpty() || localpart.length() > 1023) {
+ throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
+ }
+ domainpartStart = atLoc + 1;
finaljid = lp + "@";
- }
-
- final String dp;
- if (slashCount > 0) {
- final String rp = jid.substring(slashLoc + 1, jid.length());
- try {
- resourcepart = Config.DISABLE_STRING_PREP || safe ? rp : Stringprep.resourceprep(rp);
- } catch (final StringprepException e) {
- throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
- }
- if (resourcepart.isEmpty() || resourcepart.length() > 1023) {
- throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
- }
- try {
- dp = IDN.toUnicode(Stringprep.nameprep(jid.substring(domainpartStart, slashLoc)), IDN.USE_STD3_ASCII_RULES);
- } catch (final StringprepException e) {
- throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
- }
+ }
+
+ final String dp;
+ if (slashCount > 0) {
+ final String rp = jid.substring(slashLoc + 1, jid.length());
+ try {
+ resourcepart = Config.DISABLE_STRING_PREP || safe ? rp : Stringprep.resourceprep(rp);
+ } catch (final StringprepException e) {
+ throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
+ }
+ if (resourcepart.isEmpty() || resourcepart.length() > 1023) {
+ throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
+ }
+ try {
+ dp = IDN.toUnicode(Stringprep.nameprep(jid.substring(domainpartStart, slashLoc)), IDN.USE_STD3_ASCII_RULES);
+ } catch (final StringprepException e) {
+ throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
+ }
finaljid = finaljid + dp + "/" + rp;
- } else {
- resourcepart = "";
- try{
- dp = IDN.toUnicode(Stringprep.nameprep(jid.substring(domainpartStart, jid.length())), IDN.USE_STD3_ASCII_RULES);
- } catch (final StringprepException e) {
- throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
- }
+ } else {
+ resourcepart = "";
+ try {
+ dp = IDN.toUnicode(Stringprep.nameprep(jid.substring(domainpartStart, jid.length())), IDN.USE_STD3_ASCII_RULES);
+ } catch (final StringprepException e) {
+ throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
+ }
finaljid = finaljid + dp;
}
- // Remove trailing "." before storing the domain part.
- if (dp.endsWith(".")) {
- try {
- domainpart = IDN.toASCII(dp.substring(0, dp.length() - 1), IDN.USE_STD3_ASCII_RULES);
- } catch (final IllegalArgumentException e) {
- throw new InvalidJidException(e);
- }
- } else {
- try {
- domainpart = IDN.toASCII(dp, IDN.USE_STD3_ASCII_RULES);
- } catch (final IllegalArgumentException e) {
- throw new InvalidJidException(e);
- }
- }
-
- // TODO: Find a proper domain validation library; validate individual parts, separators, etc.
- if (domainpart.isEmpty() || domainpart.length() > 1023) {
- throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
- }
-
- Jid.cache.put(jid, this);
+ // Remove trailing "." before storing the domain part.
+ if (dp.endsWith(".")) {
+ try {
+ domainpart = IDN.toASCII(dp.substring(0, dp.length() - 1), IDN.USE_STD3_ASCII_RULES);
+ } catch (final IllegalArgumentException e) {
+ throw new InvalidJidException(e);
+ }
+ } else {
+ try {
+ domainpart = IDN.toASCII(dp, IDN.USE_STD3_ASCII_RULES);
+ } catch (final IllegalArgumentException e) {
+ throw new InvalidJidException(e);
+ }
+ }
+
+ // TODO: Find a proper domain validation library; validate individual parts, separators, etc.
+ if (domainpart.isEmpty() || domainpart.length() > 1023) {
+ throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
+ }
+
+ Jid.cache.put(jid, this);
this.displayjid = finaljid;
- }
-
- public Jid toBareJid() {
- try {
- return resourcepart.isEmpty() ? this : fromParts(localpart, domainpart, "");
- } catch (final InvalidJidException e) {
- // This should never happen.
- throw new AssertionError("Jid " + this.toString() + " invalid");
- }
- }
-
- public Jid toDomainJid() {
- try {
- return resourcepart.isEmpty() && localpart.isEmpty() ? this : fromString(getDomainpart());
- } catch (final InvalidJidException e) {
- // This should never happen.
- throw new AssertionError("Jid " + this.toString() + " invalid");
- }
- }
-
- @Override
- public String toString() {
+ }
+
+ public Jid toBareJid() {
+ try {
+ return resourcepart.isEmpty() ? this : fromParts(localpart, domainpart, "");
+ } catch (final InvalidJidException e) {
+ // This should never happen.
+ throw new AssertionError("Jid " + this.toString() + " invalid");
+ }
+ }
+
+ public Jid toDomainJid() {
+ try {
+ return resourcepart.isEmpty() && localpart.isEmpty() ? this : fromString(getDomainpart());
+ } catch (final InvalidJidException e) {
+ // This should never happen.
+ throw new AssertionError("Jid " + this.toString() + " invalid");
+ }
+ }
+
+ @Override
+ public String toString() {
return displayjid;
}
public String toPreppedString() {
- String out;
- if (hasLocalpart()) {
- out = localpart + '@' + domainpart;
- } else {
- out = domainpart;
- }
- if (!resourcepart.isEmpty()) {
- out += '/'+resourcepart;
- }
- return out;
- }
-
- @Override
- public boolean equals(final Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- final Jid jid = (Jid) o;
-
- return jid.hashCode() == this.hashCode();
- }
-
- @Override
- public int hashCode() {
- int result = localpart.hashCode();
- result = 31 * result + domainpart.hashCode();
- result = 31 * result + resourcepart.hashCode();
- return result;
- }
-
- public boolean hasLocalpart() {
- return !localpart.isEmpty();
- }
-
- public boolean isBareJid() {
- return this.resourcepart.isEmpty();
- }
-
- public boolean isDomainJid() {
- return !this.hasLocalpart();
- }
+ String out;
+ if (hasLocalpart()) {
+ out = localpart + '@' + domainpart;
+ } else {
+ out = domainpart;
+ }
+ if (!resourcepart.isEmpty()) {
+ out += '/' + resourcepart;
+ }
+ return out;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final Jid jid = (Jid) o;
+
+ return jid.hashCode() == this.hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ int result = localpart.hashCode();
+ result = 31 * result + domainpart.hashCode();
+ result = 31 * result + resourcepart.hashCode();
+ return result;
+ }
+
+ public boolean hasLocalpart() {
+ return !localpart.isEmpty();
+ }
+
+ public boolean isBareJid() {
+ return this.resourcepart.isEmpty();
+ }
+
+ public boolean isDomainJid() {
+ return !this.hasLocalpart();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleCandidate.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleCandidate.java
index 166fa6c69..6f87db473 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleCandidate.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleCandidate.java
@@ -8,57 +8,57 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class JingleCandidate {
- public static int TYPE_UNKNOWN;
- public static int TYPE_DIRECT = 0;
- public static int TYPE_PROXY = 1;
-
- private boolean ours;
- private boolean usedByCounterpart = false;
- private String cid;
- private String host;
- private int port;
- private int type;
- private Jid jid;
- private int priority;
-
- public JingleCandidate(String cid, boolean ours) {
- this.ours = ours;
- this.cid = cid;
- }
-
- public String getCid() {
- return cid;
- }
-
- public void setHost(String host) {
- this.host = host;
- }
-
- public String getHost() {
- return this.host;
- }
-
- public void setJid(final Jid jid) {
- this.jid = jid;
- }
-
- public Jid getJid() {
- return this.jid;
- }
-
- public void setPort(int port) {
- this.port = port;
- }
-
- public int getPort() {
- return this.port;
- }
-
- public void setType(int type) {
- this.type = type;
- }
-
- public void setType(String type) {
+ public static int TYPE_UNKNOWN;
+ public static int TYPE_DIRECT = 0;
+ public static int TYPE_PROXY = 1;
+
+ private boolean ours;
+ private boolean usedByCounterpart = false;
+ private String cid;
+ private String host;
+ private int port;
+ private int type;
+ private Jid jid;
+ private int priority;
+
+ public JingleCandidate(String cid, boolean ours) {
+ this.ours = ours;
+ this.cid = cid;
+ }
+
+ public String getCid() {
+ return cid;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public String getHost() {
+ return this.host;
+ }
+
+ public void setJid(final Jid jid) {
+ this.jid = jid;
+ }
+
+ public Jid getJid() {
+ return this.jid;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public int getPort() {
+ return this.port;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ public void setType(String type) {
switch (type) {
case "proxy":
this.type = TYPE_PROXY;
@@ -70,78 +70,78 @@ public class JingleCandidate {
this.type = TYPE_UNKNOWN;
break;
}
- }
-
- public void setPriority(int i) {
- this.priority = i;
- }
-
- public int getPriority() {
- return this.priority;
- }
-
- public boolean equals(JingleCandidate other) {
- return this.getCid().equals(other.getCid());
- }
-
- public boolean equalValues(JingleCandidate other) {
- return other != null && other.getHost().equals(this.getHost()) && (other.getPort() == this.getPort());
- }
-
- public boolean isOurs() {
- return ours;
- }
-
- public int getType() {
- return this.type;
- }
-
- public static List<JingleCandidate> parse(List<Element> canditates) {
- List<JingleCandidate> parsedCandidates = new ArrayList<>();
- for (Element c : canditates) {
- parsedCandidates.add(JingleCandidate.parse(c));
- }
- return parsedCandidates;
- }
-
- public static JingleCandidate parse(Element candidate) {
- JingleCandidate parsedCandidate = new JingleCandidate(
- candidate.getAttribute("cid"), false);
- parsedCandidate.setHost(candidate.getAttribute("host"));
- parsedCandidate.setJid(candidate.getAttributeAsJid("jid"));
- parsedCandidate.setType(candidate.getAttribute("type"));
- parsedCandidate.setPriority(Integer.parseInt(candidate
- .getAttribute("priority")));
- parsedCandidate
- .setPort(Integer.parseInt(candidate.getAttribute("port")));
- return parsedCandidate;
- }
-
- public Element toElement() {
- Element element = new Element("candidate");
- element.setAttribute("cid", this.getCid());
- element.setAttribute("host", this.getHost());
- element.setAttribute("port", Integer.toString(this.getPort()));
- element.setAttribute("jid", this.getJid().toString());
- element.setAttribute("priority", Integer.toString(this.getPriority()));
- if (this.getType() == TYPE_DIRECT) {
- element.setAttribute("type", "direct");
- } else if (this.getType() == TYPE_PROXY) {
- element.setAttribute("type", "proxy");
- }
- return element;
- }
-
- public void flagAsUsedByCounterpart() {
- this.usedByCounterpart = true;
- }
-
- public boolean isUsedByCounterpart() {
- return this.usedByCounterpart;
- }
-
- public String toString() {
- return this.getHost() + ":" + this.getPort() + " (prio="
- + this.getPriority() + ")";
- }
+ }
+
+ public void setPriority(int i) {
+ this.priority = i;
+ }
+
+ public int getPriority() {
+ return this.priority;
+ }
+
+ public boolean equals(JingleCandidate other) {
+ return this.getCid().equals(other.getCid());
+ }
+
+ public boolean equalValues(JingleCandidate other) {
+ return other != null && other.getHost().equals(this.getHost()) && (other.getPort() == this.getPort());
+ }
+
+ public boolean isOurs() {
+ return ours;
+ }
+
+ public int getType() {
+ return this.type;
+ }
+
+ public static List<JingleCandidate> parse(List<Element> canditates) {
+ List<JingleCandidate> parsedCandidates = new ArrayList<>();
+ for (Element c : canditates) {
+ parsedCandidates.add(JingleCandidate.parse(c));
+ }
+ return parsedCandidates;
+ }
+
+ public static JingleCandidate parse(Element candidate) {
+ JingleCandidate parsedCandidate = new JingleCandidate(
+ candidate.getAttribute("cid"), false);
+ parsedCandidate.setHost(candidate.getAttribute("host"));
+ parsedCandidate.setJid(candidate.getAttributeAsJid("jid"));
+ parsedCandidate.setType(candidate.getAttribute("type"));
+ parsedCandidate.setPriority(Integer.parseInt(candidate
+ .getAttribute("priority")));
+ parsedCandidate
+ .setPort(Integer.parseInt(candidate.getAttribute("port")));
+ return parsedCandidate;
+ }
+
+ public Element toElement() {
+ Element element = new Element("candidate");
+ element.setAttribute("cid", this.getCid());
+ element.setAttribute("host", this.getHost());
+ element.setAttribute("port", Integer.toString(this.getPort()));
+ element.setAttribute("jid", this.getJid().toString());
+ element.setAttribute("priority", Integer.toString(this.getPriority()));
+ if (this.getType() == TYPE_DIRECT) {
+ element.setAttribute("type", "direct");
+ } else if (this.getType() == TYPE_PROXY) {
+ element.setAttribute("type", "proxy");
+ }
+ return element;
+ }
+
+ public void flagAsUsedByCounterpart() {
+ this.usedByCounterpart = true;
+ }
+
+ public boolean isUsedByCounterpart() {
+ return this.usedByCounterpart;
+ }
+
+ public String toString() {
+ return this.getHost() + ":" + this.getPort() + " (prio="
+ + this.getPriority() + ")";
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java
index 5417b8deb..29b9db382 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java
@@ -40,1048 +40,1048 @@ import de.pixart.messenger.xmpp.jingle.stanzas.Reason;
import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class JingleConnection implements Transferable {
- private final SimpleDateFormat fileDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
+ private final SimpleDateFormat fileDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
- private JingleConnectionManager mJingleConnectionManager;
- private XmppConnectionService mXmppConnectionService;
+ private JingleConnectionManager mJingleConnectionManager;
+ private XmppConnectionService mXmppConnectionService;
- protected static final int JINGLE_STATUS_INITIATED = 0;
- protected static final int JINGLE_STATUS_ACCEPTED = 1;
- protected static final int JINGLE_STATUS_FINISHED = 4;
- protected static final int JINGLE_STATUS_TRANSMITTING = 5;
- protected static final int JINGLE_STATUS_FAILED = 99;
+ protected static final int JINGLE_STATUS_INITIATED = 0;
+ protected static final int JINGLE_STATUS_ACCEPTED = 1;
+ protected static final int JINGLE_STATUS_FINISHED = 4;
+ protected static final int JINGLE_STATUS_TRANSMITTING = 5;
+ protected static final int JINGLE_STATUS_FAILED = 99;
- private Content.Version ftVersion = Content.Version.FT_3;
+ private Content.Version ftVersion = Content.Version.FT_3;
- private int ibbBlockSize = 8192;
+ private int ibbBlockSize = 8192;
- private int mJingleStatus = -1;
- private int mStatus = Transferable.STATUS_UNKNOWN;
- private Message message;
- private String sessionId;
- private Account account;
- private Jid initiator;
- private Jid responder;
- private List<JingleCandidate> candidates = new ArrayList<>();
- private ConcurrentHashMap<String, JingleSocks5Transport> connections = new ConcurrentHashMap<>();
+ private int mJingleStatus = -1;
+ private int mStatus = Transferable.STATUS_UNKNOWN;
+ private Message message;
+ private String sessionId;
+ private Account account;
+ private Jid initiator;
+ private Jid responder;
+ private List<JingleCandidate> candidates = new ArrayList<>();
+ private ConcurrentHashMap<String, JingleSocks5Transport> connections = new ConcurrentHashMap<>();
- private String transportId;
- private Element fileOffer;
- private DownloadableFile file = null;
+ private String transportId;
+ private Element fileOffer;
+ private DownloadableFile file = null;
- private String contentName;
- private String contentCreator;
+ private String contentName;
+ private String contentCreator;
- private int mProgress = 0;
+ private int mProgress = 0;
- private boolean receivedCandidate = false;
- private boolean sentCandidate = false;
+ private boolean receivedCandidate = false;
+ private boolean sentCandidate = false;
- private boolean acceptedAutomatically = false;
+ private boolean acceptedAutomatically = false;
- private XmppAxolotlMessage mXmppAxolotlMessage;
+ private XmppAxolotlMessage mXmppAxolotlMessage;
- private JingleTransport transport = null;
+ private JingleTransport transport = null;
- private OutputStream mFileOutputStream;
- private InputStream mFileInputStream;
+ private OutputStream mFileOutputStream;
+ private InputStream mFileInputStream;
- private OnIqPacketReceived responseListener = new OnIqPacketReceived() {
+ private OnIqPacketReceived responseListener = new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() != IqPacket.TYPE.RESULT) {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() != IqPacket.TYPE.RESULT) {
fail(IqParser.extractErrorMessage(packet));
- }
- }
- };
-
- final OnFileTransmissionStatusChanged onFileTransmissionSatusChanged = new OnFileTransmissionStatusChanged() {
-
- @Override
- public void onFileTransmitted(DownloadableFile file) {
- if (responder.equals(account.getJid())) {
- sendSuccess();
- mXmppConnectionService.getFileBackend().updateFileParams(message);
- mXmppConnectionService.databaseBackend.createMessage(message);
- mXmppConnectionService.markMessage(message,Message.STATUS_RECEIVED);
- if (acceptedAutomatically) {
- message.markUnread();
- if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- account.getPgpDecryptionService().decrypt(message, true);
- } else {
- JingleConnection.this.mXmppConnectionService.getNotificationService().push(message);
- }
- }
- } else {
- if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- account.getPgpDecryptionService().decrypt(message, false);
- }
- if (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- file.delete();
- }
- }
- Log.d(Config.LOGTAG,"successfully transmitted file:" + file.getAbsolutePath()+" ("+file.getSha1Sum()+")");
- if (message.getEncryption() != Message.ENCRYPTION_PGP) {
- mXmppConnectionService.getFileBackend().updateMediaScanner(file);
- }
- }
-
- @Override
- public void onFileTransferAborted() {
- JingleConnection.this.sendCancel();
- JingleConnection.this.fail();
- }
- };
-
- public InputStream getFileInputStream() {
- return this.mFileInputStream;
- }
-
- public OutputStream getFileOutputStream() {
- return this.mFileOutputStream;
- }
-
- private OnProxyActivated onProxyActivated = new OnProxyActivated() {
-
- @Override
- public void success() {
- if (initiator.equals(account.getJid())) {
- Log.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");
- }
- }
-
- @Override
- public void failed() {
- Log.d(Config.LOGTAG, "proxy activation failed");
- }
- };
-
- public JingleConnection(JingleConnectionManager mJingleConnectionManager) {
- this.mJingleConnectionManager = mJingleConnectionManager;
- this.mXmppConnectionService = mJingleConnectionManager
- .getXmppConnectionService();
- }
-
- public String getSessionId() {
- return this.sessionId;
- }
-
- public Account getAccount() {
- return this.account;
- }
-
- public Jid getCounterPart() {
- return this.message.getCounterpart();
- }
-
- public void deliverPacket(JinglePacket packet) {
- boolean returnResult = true;
- if (packet.isAction("session-terminate")) {
- Reason reason = packet.getReason();
- if (reason != null) {
- if (reason.hasChild("cancel")) {
- this.fail();
- } else if (reason.hasChild("success")) {
- this.receiveSuccess();
- } else {
- this.fail();
- }
- } else {
- this.fail();
- }
- } else if (packet.isAction("session-accept")) {
- returnResult = receiveAccept(packet);
- } else if (packet.isAction("transport-info")) {
- returnResult = receiveTransportInfo(packet);
- } else if (packet.isAction("transport-replace")) {
- if (packet.getJingleContent().hasIbbTransport()) {
- returnResult = this.receiveFallbackToIbb(packet);
- } else {
- returnResult = false;
- Log.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 "
- + packet.getAction());
- returnResult = false;
- }
- IqPacket response;
- if (returnResult) {
- response = packet.generateResponse(IqPacket.TYPE.RESULT);
-
- } else {
- response = packet.generateResponse(IqPacket.TYPE.ERROR);
- }
- mXmppConnectionService.sendIqPacket(account,response,null);
- }
-
- public void init(final Message message) {
- if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
- Conversation conversation = message.getConversation();
- conversation.getAccount().getAxolotlService().prepareKeyTransportMessage(conversation, new OnMessageCreatedCallback() {
- @Override
- public void run(XmppAxolotlMessage xmppAxolotlMessage) {
- if (xmppAxolotlMessage != null) {
- init(message, xmppAxolotlMessage);
- } else {
- fail();
- }
- }
- });
- } else {
- init(message, null);
- }
- }
-
- private void init(Message message, XmppAxolotlMessage xmppAxolotlMessage) {
- this.mXmppAxolotlMessage = xmppAxolotlMessage;
- this.contentCreator = "initiator";
- this.contentName = this.mJingleConnectionManager.nextRandomId();
- this.message = message;
- this.account = message.getConversation().getAccount();
- upgradeNamespace();
- this.message.setTransferable(this);
- this.mStatus = Transferable.STATUS_UPLOADING;
- this.initiator = this.account.getJid();
- this.responder = this.message.getCounterpart();
- this.sessionId = this.mJingleConnectionManager.nextRandomId();
- this.transportId = this.mJingleConnectionManager.nextRandomId();
- if (this.candidates.size() > 0) {
- this.sendInitRequest();
- } else {
- this.mJingleConnectionManager.getPrimaryCandidate(account,
- new OnPrimaryCandidateFound() {
-
- @Override
- public void onPrimaryCandidateFound(boolean success,
- final JingleCandidate candidate) {
- if (success) {
- final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
- JingleConnection.this, candidate);
- connections.put(candidate.getCid(),
- socksConnection);
- socksConnection
- .connect(new OnTransportConnected() {
-
- @Override
- public void failed() {
- Log.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");
- mergeCandidate(candidate);
- sendInitRequest();
- }
- });
- mergeCandidate(candidate);
- } else {
- Log.d(Config.LOGTAG, "no primary candidate of our own was found");
- sendInitRequest();
- }
- }
- });
- }
-
- }
-
- private void upgradeNamespace() {
- Jid jid = this.message.getCounterpart();
- String resource = jid != null ?jid.getResourcepart() : null;
- if (resource != null) {
- Presence presence = this.account.getRoster().getContact(jid).getPresences().getPresences().get(resource);
- ServiceDiscoveryResult result = presence != null ? presence.getServiceDiscoveryResult() : null;
- if (result != null) {
- List<String> features = result.getFeatures();
- if (features.contains(Content.Version.FT_4.getNamespace())) {
- this.ftVersion = Content.Version.FT_4;
- }
- }
- }
- }
-
- public void init(Account account, JinglePacket packet) {
- this.mJingleStatus = JINGLE_STATUS_INITIATED;
- Conversation conversation = this.mXmppConnectionService
- .findOrCreateConversation(account,
- packet.getFrom().toBareJid(), false);
- this.message = new Message(conversation, "", Message.ENCRYPTION_NONE);
- this.message.setStatus(Message.STATUS_RECEIVED);
- this.mStatus = Transferable.STATUS_OFFER;
- this.message.setTransferable(this);
+ }
+ }
+ };
+
+ final OnFileTransmissionStatusChanged onFileTransmissionSatusChanged = new OnFileTransmissionStatusChanged() {
+
+ @Override
+ public void onFileTransmitted(DownloadableFile file) {
+ if (responder.equals(account.getJid())) {
+ sendSuccess();
+ mXmppConnectionService.getFileBackend().updateFileParams(message);
+ mXmppConnectionService.databaseBackend.createMessage(message);
+ mXmppConnectionService.markMessage(message, Message.STATUS_RECEIVED);
+ if (acceptedAutomatically) {
+ message.markUnread();
+ if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ account.getPgpDecryptionService().decrypt(message, true);
+ } else {
+ JingleConnection.this.mXmppConnectionService.getNotificationService().push(message);
+ }
+ }
+ } else {
+ if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ account.getPgpDecryptionService().decrypt(message, false);
+ }
+ if (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ file.delete();
+ }
+ }
+ Log.d(Config.LOGTAG, "successfully transmitted file:" + file.getAbsolutePath() + " (" + file.getSha1Sum() + ")");
+ if (message.getEncryption() != Message.ENCRYPTION_PGP) {
+ mXmppConnectionService.getFileBackend().updateMediaScanner(file);
+ }
+ }
+
+ @Override
+ public void onFileTransferAborted() {
+ JingleConnection.this.sendCancel();
+ JingleConnection.this.fail();
+ }
+ };
+
+ public InputStream getFileInputStream() {
+ return this.mFileInputStream;
+ }
+
+ public OutputStream getFileOutputStream() {
+ return this.mFileOutputStream;
+ }
+
+ private OnProxyActivated onProxyActivated = new OnProxyActivated() {
+
+ @Override
+ public void success() {
+ if (initiator.equals(account.getJid())) {
+ Log.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");
+ }
+ }
+
+ @Override
+ public void failed() {
+ Log.d(Config.LOGTAG, "proxy activation failed");
+ }
+ };
+
+ public JingleConnection(JingleConnectionManager mJingleConnectionManager) {
+ this.mJingleConnectionManager = mJingleConnectionManager;
+ this.mXmppConnectionService = mJingleConnectionManager
+ .getXmppConnectionService();
+ }
+
+ public String getSessionId() {
+ return this.sessionId;
+ }
+
+ public Account getAccount() {
+ return this.account;
+ }
+
+ public Jid getCounterPart() {
+ return this.message.getCounterpart();
+ }
+
+ public void deliverPacket(JinglePacket packet) {
+ boolean returnResult = true;
+ if (packet.isAction("session-terminate")) {
+ Reason reason = packet.getReason();
+ if (reason != null) {
+ if (reason.hasChild("cancel")) {
+ this.fail();
+ } else if (reason.hasChild("success")) {
+ this.receiveSuccess();
+ } else {
+ this.fail();
+ }
+ } else {
+ this.fail();
+ }
+ } else if (packet.isAction("session-accept")) {
+ returnResult = receiveAccept(packet);
+ } else if (packet.isAction("transport-info")) {
+ returnResult = receiveTransportInfo(packet);
+ } else if (packet.isAction("transport-replace")) {
+ if (packet.getJingleContent().hasIbbTransport()) {
+ returnResult = this.receiveFallbackToIbb(packet);
+ } else {
+ returnResult = false;
+ Log.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 "
+ + packet.getAction());
+ returnResult = false;
+ }
+ IqPacket response;
+ if (returnResult) {
+ response = packet.generateResponse(IqPacket.TYPE.RESULT);
+
+ } else {
+ response = packet.generateResponse(IqPacket.TYPE.ERROR);
+ }
+ mXmppConnectionService.sendIqPacket(account, response, null);
+ }
+
+ public void init(final Message message) {
+ if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
+ Conversation conversation = message.getConversation();
+ conversation.getAccount().getAxolotlService().prepareKeyTransportMessage(conversation, new OnMessageCreatedCallback() {
+ @Override
+ public void run(XmppAxolotlMessage xmppAxolotlMessage) {
+ if (xmppAxolotlMessage != null) {
+ init(message, xmppAxolotlMessage);
+ } else {
+ fail();
+ }
+ }
+ });
+ } else {
+ init(message, null);
+ }
+ }
+
+ private void init(Message message, XmppAxolotlMessage xmppAxolotlMessage) {
+ this.mXmppAxolotlMessage = xmppAxolotlMessage;
+ this.contentCreator = "initiator";
+ this.contentName = this.mJingleConnectionManager.nextRandomId();
+ this.message = message;
+ this.account = message.getConversation().getAccount();
+ upgradeNamespace();
+ this.message.setTransferable(this);
+ this.mStatus = Transferable.STATUS_UPLOADING;
+ this.initiator = this.account.getJid();
+ this.responder = this.message.getCounterpart();
+ this.sessionId = this.mJingleConnectionManager.nextRandomId();
+ this.transportId = this.mJingleConnectionManager.nextRandomId();
+ if (this.candidates.size() > 0) {
+ this.sendInitRequest();
+ } else {
+ this.mJingleConnectionManager.getPrimaryCandidate(account,
+ new OnPrimaryCandidateFound() {
+
+ @Override
+ public void onPrimaryCandidateFound(boolean success,
+ final JingleCandidate candidate) {
+ if (success) {
+ final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
+ JingleConnection.this, candidate);
+ connections.put(candidate.getCid(),
+ socksConnection);
+ socksConnection
+ .connect(new OnTransportConnected() {
+
+ @Override
+ public void failed() {
+ Log.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");
+ mergeCandidate(candidate);
+ sendInitRequest();
+ }
+ });
+ mergeCandidate(candidate);
+ } else {
+ Log.d(Config.LOGTAG, "no primary candidate of our own was found");
+ sendInitRequest();
+ }
+ }
+ });
+ }
+
+ }
+
+ private void upgradeNamespace() {
+ Jid jid = this.message.getCounterpart();
+ String resource = jid != null ? jid.getResourcepart() : null;
+ if (resource != null) {
+ Presence presence = this.account.getRoster().getContact(jid).getPresences().getPresences().get(resource);
+ ServiceDiscoveryResult result = presence != null ? presence.getServiceDiscoveryResult() : null;
+ if (result != null) {
+ List<String> features = result.getFeatures();
+ if (features.contains(Content.Version.FT_4.getNamespace())) {
+ this.ftVersion = Content.Version.FT_4;
+ }
+ }
+ }
+ }
+
+ public void init(Account account, JinglePacket packet) {
+ this.mJingleStatus = JINGLE_STATUS_INITIATED;
+ Conversation conversation = this.mXmppConnectionService
+ .findOrCreateConversation(account,
+ packet.getFrom().toBareJid(), false);
+ this.message = new Message(conversation, "", Message.ENCRYPTION_NONE);
+ this.message.setStatus(Message.STATUS_RECEIVED);
+ this.mStatus = Transferable.STATUS_OFFER;
+ this.message.setTransferable(this);
final Jid from = packet.getFrom();
- this.message.setCounterpart(from);
- this.account = account;
- this.initiator = packet.getFrom();
- this.responder = this.account.getJid();
- this.sessionId = packet.getSessionId();
- Content content = packet.getJingleContent();
- this.contentCreator = content.getAttribute("creator");
- this.contentName = content.getAttribute("name");
- this.transportId = content.getTransportId();
- this.mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren()));
- this.ftVersion = content.getVersion();
- if (ftVersion == null) {
- this.sendCancel();
- this.fail();
- return;
- }
- this.fileOffer = content.getFileOffer(this.ftVersion);
-
- mXmppConnectionService.sendIqPacket(account,packet.generateResponse(IqPacket.TYPE.RESULT),null);
-
- if (fileOffer != null) {
- Element encrypted = fileOffer.findChild("encrypted", AxolotlService.PEP_PREFIX);
- if (encrypted != null) {
- this.mXmppAxolotlMessage = XmppAxolotlMessage.fromElement(encrypted, packet.getFrom().toBareJid());
- }
- Element fileSize = fileOffer.findChild("size");
- Element fileNameElement = fileOffer.findChild("name");
- if (fileNameElement != null) {
- String[] filename = fileNameElement.getContent()
- .toLowerCase(Locale.US).toLowerCase().split("\\.");
- String filename_new = fileDateFormat.format(new Date(message.getTimeSent()))+"_"+message.getUuid().substring(0,4);
- String extension = filename[filename.length - 1];
- if (VALID_IMAGE_EXTENSIONS.contains(extension)) {
- message.setType(Message.TYPE_IMAGE);
- message.setRelativeFilePath(filename_new+"."+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(filename_new+"."+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);
- }
- }
- } else {
- message.setType(Message.TYPE_FILE);
- }
- 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(filename_new+"_"+suffix);
- }
- long size = Long.parseLong(fileSize.getContent());
- message.setBody(Long.toString(size));
- conversation.add(message);
- mXmppConnectionService.updateConversationUi();
- if (mJingleConnectionManager.hasStoragePermission()
- && size < this.mJingleConnectionManager.getAutoAcceptFileSize()
- && mXmppConnectionService.isDataSaverDisabled()) {
- Log.d(Config.LOGTAG, "auto accepting file from "+ packet.getFrom());
- this.acceptedAutomatically = true;
- this.sendAccept();
- } else {
- message.markUnread();
- Log.d(Config.LOGTAG,
- "not auto accepting new file offer with size: "
- + size
- + " allowed size:"
- + this.mJingleConnectionManager
- .getAutoAcceptFileSize());
- this.mXmppConnectionService.getNotificationService().push(message);
- }
- this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false);
- if (mXmppAxolotlMessage != null) {
- XmppAxolotlMessage.XmppAxolotlKeyTransportMessage transportMessage = account.getAxolotlService().processReceivingKeyTransportMessage(mXmppAxolotlMessage);
- if (transportMessage != null) {
- message.setEncryption(Message.ENCRYPTION_AXOLOTL);
- this.file.setKey(transportMessage.getKey());
- this.file.setIv(transportMessage.getIv());
- message.setFingerprint(transportMessage.getFingerprint());
- } else {
- Log.d(Config.LOGTAG,"could not process KeyTransportMessage");
- }
- } else if (message.getEncryption() == Message.ENCRYPTION_OTR) {
- byte[] key = conversation.getSymmetricKey();
- if (key == null) {
- this.sendCancel();
- this.fail();
- return;
- } else {
- this.file.setKeyAndIv(key);
- }
- }
- this.mFileOutputStream = AbstractConnectionManager.createOutputStream(this.file,message.getEncryption() == Message.ENCRYPTION_AXOLOTL);
- if (message.getEncryption() == Message.ENCRYPTION_OTR && Config.REPORT_WRONG_FILESIZE_IN_OTR_JINGLE) {
- this.file.setExpectedSize((size / 16 + 1) * 16);
- } else {
- this.file.setExpectedSize(size);
- }
- Log.d(Config.LOGTAG, "receiving file: expecting size of " + this.file.getExpectedSize());
- } else {
- this.sendCancel();
- this.fail();
- }
- } else {
- this.sendCancel();
- this.fail();
- }
- }
-
- private void sendInitRequest() {
- JinglePacket packet = this.bootstrapPacket("session-initiate");
- 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);
- 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");
- cancel();
- }
- this.file.setKeyAndIv(conversation.getSymmetricKey());
- pair = AbstractConnectionManager.createInputStream(this.file, false);
- this.file.setExpectedSize(pair.second);
- content.setFileOffer(this.file, true, this.ftVersion);
- } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
- this.file.setKey(mXmppAxolotlMessage.getInnerKey());
- this.file.setIv(mXmppAxolotlMessage.getIV());
- pair = AbstractConnectionManager.createInputStream(this.file, true);
- this.file.setExpectedSize(pair.second);
- content.setFileOffer(this.file, false, this.ftVersion).addChild(mXmppAxolotlMessage.toElement());
- } else {
- pair = AbstractConnectionManager.createInputStream(this.file, false);
- this.file.setExpectedSize(pair.second);
- content.setFileOffer(this.file, false, this.ftVersion);
- }
- } catch (FileNotFoundException e) {
- cancel();
- return;
- }
- this.mFileInputStream = pair.first;
- content.setTransportId(this.transportId);
- content.socks5transport().setChildren(getCandidatesAsElements());
- packet.setContent(content);
- this.sendJinglePacket(packet,new OnIqPacketReceived() {
-
- @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");
- mJingleStatus = JINGLE_STATUS_INITIATED;
- mXmppConnectionService.markMessage(message, Message.STATUS_OFFERED);
- } else {
- fail(IqParser.extractErrorMessage(packet));
- }
- }
- });
-
- }
- }
-
- private List<Element> getCandidatesAsElements() {
- List<Element> elements = new ArrayList<>();
- for (JingleCandidate c : this.candidates) {
- if (c.isOurs()) {
- elements.add(c.toElement());
- }
- }
- return elements;
- }
-
- private void sendAccept() {
- mJingleStatus = JINGLE_STATUS_ACCEPTED;
- this.mStatus = Transferable.STATUS_DOWNLOADING;
- mXmppConnectionService.updateConversationUi();
- this.mJingleConnectionManager.getPrimaryCandidate(this.account, new OnPrimaryCandidateFound() {
- @Override
- public void onPrimaryCandidateFound(boolean success, final JingleCandidate candidate) {
- final JinglePacket packet = bootstrapPacket("session-accept");
- final Content content = new Content(contentCreator,contentName);
- content.setFileOffer(fileOffer, ftVersion);
- content.setTransportId(transportId);
- if (success && candidate != null && !equalCandidateExists(candidate)) {
- final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
- JingleConnection.this,
- candidate);
- connections.put(candidate.getCid(), socksConnection);
- socksConnection.connect(new OnTransportConnected() {
-
- @Override
- public void failed() {
- Log.d(Config.LOGTAG,"connection to our own primary candidate failed");
- content.socks5transport().setChildren(getCandidatesAsElements());
- packet.setContent(content);
- sendJinglePacket(packet);
- connectNextCandidate();
- }
-
- @Override
- public void established() {
- Log.d(Config.LOGTAG, "connected to primary candidate");
- mergeCandidate(candidate);
- content.socks5transport().setChildren(getCandidatesAsElements());
- packet.setContent(content);
- sendJinglePacket(packet);
- connectNextCandidate();
- }
- });
- } else {
- Log.d(Config.LOGTAG,"did not find a primary candidate for ourself");
- content.socks5transport().setChildren(getCandidatesAsElements());
- packet.setContent(content);
- sendJinglePacket(packet);
- connectNextCandidate();
- }
- }
- });
- }
-
- private JinglePacket bootstrapPacket(String action) {
- JinglePacket packet = new JinglePacket();
- packet.setAction(action);
- packet.setFrom(account.getJid());
- packet.setTo(this.message.getCounterpart());
- packet.setSessionId(this.sessionId);
- packet.setInitiator(this.initiator);
- return packet;
- }
-
- private void sendJinglePacket(JinglePacket packet) {
- mXmppConnectionService.sendIqPacket(account,packet,responseListener);
- }
-
- private void sendJinglePacket(JinglePacket packet, OnIqPacketReceived callback) {
- mXmppConnectionService.sendIqPacket(account,packet,callback);
- }
-
- private boolean receiveAccept(JinglePacket packet) {
- Content content = packet.getJingleContent();
- mergeCandidates(JingleCandidate.parse(content.socks5transport()
- .getChildren()));
- this.mJingleStatus = JINGLE_STATUS_ACCEPTED;
- mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
- this.connectNextCandidate();
- return true;
- }
-
- private boolean receiveTransportInfo(JinglePacket packet) {
- Content content = packet.getJingleContent();
- if (content.hasSocks5Transport()) {
- if (content.socks5transport().hasChild("activated")) {
- if ((this.transport != null) && (this.transport instanceof JingleSocks5Transport)) {
- onProxyActivated.success();
- } else {
- String cid = content.socks5transport().findChild("activated").getAttribute("cid");
- Log.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");
- this.sendCancel();
- this.fail();
- }
- }
- return true;
- } else if (content.socks5transport().hasChild("proxy-error")) {
- onProxyActivated.failed();
- return true;
- } else if (content.socks5transport().hasChild("candidate-error")) {
- Log.d(Config.LOGTAG, "received candidate error");
- this.receivedCandidate = true;
- if ((mJingleStatus == JINGLE_STATUS_ACCEPTED)
- && (this.sentCandidate)) {
- this.connect();
- }
- return true;
- } else if (content.socks5transport().hasChild("candidate-used")) {
- String cid = content.socks5transport()
- .findChild("candidate-used").getAttribute("cid");
- if (cid != null) {
- Log.d(Config.LOGTAG, "candidate used by counterpart:" + cid);
- JingleCandidate candidate = getCandidate(cid);
+ this.message.setCounterpart(from);
+ this.account = account;
+ this.initiator = packet.getFrom();
+ this.responder = this.account.getJid();
+ this.sessionId = packet.getSessionId();
+ Content content = packet.getJingleContent();
+ this.contentCreator = content.getAttribute("creator");
+ this.contentName = content.getAttribute("name");
+ this.transportId = content.getTransportId();
+ this.mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren()));
+ this.ftVersion = content.getVersion();
+ if (ftVersion == null) {
+ this.sendCancel();
+ this.fail();
+ return;
+ }
+ this.fileOffer = content.getFileOffer(this.ftVersion);
+
+ mXmppConnectionService.sendIqPacket(account, packet.generateResponse(IqPacket.TYPE.RESULT), null);
+
+ if (fileOffer != null) {
+ Element encrypted = fileOffer.findChild("encrypted", AxolotlService.PEP_PREFIX);
+ if (encrypted != null) {
+ this.mXmppAxolotlMessage = XmppAxolotlMessage.fromElement(encrypted, packet.getFrom().toBareJid());
+ }
+ Element fileSize = fileOffer.findChild("size");
+ Element fileNameElement = fileOffer.findChild("name");
+ if (fileNameElement != null) {
+ String[] filename = fileNameElement.getContent()
+ .toLowerCase(Locale.US).toLowerCase().split("\\.");
+ String filename_new = fileDateFormat.format(new Date(message.getTimeSent())) + "_" + message.getUuid().substring(0, 4);
+ String extension = filename[filename.length - 1];
+ if (VALID_IMAGE_EXTENSIONS.contains(extension)) {
+ message.setType(Message.TYPE_IMAGE);
+ message.setRelativeFilePath(filename_new + "." + 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(filename_new + "." + 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);
+ }
+ }
+ } else {
+ message.setType(Message.TYPE_FILE);
+ }
+ 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(filename_new + "_" + suffix);
+ }
+ long size = Long.parseLong(fileSize.getContent());
+ message.setBody(Long.toString(size));
+ conversation.add(message);
+ mXmppConnectionService.updateConversationUi();
+ if (mJingleConnectionManager.hasStoragePermission()
+ && size < this.mJingleConnectionManager.getAutoAcceptFileSize()
+ && mXmppConnectionService.isDataSaverDisabled()) {
+ Log.d(Config.LOGTAG, "auto accepting file from " + packet.getFrom());
+ this.acceptedAutomatically = true;
+ this.sendAccept();
+ } else {
+ message.markUnread();
+ Log.d(Config.LOGTAG,
+ "not auto accepting new file offer with size: "
+ + size
+ + " allowed size:"
+ + this.mJingleConnectionManager
+ .getAutoAcceptFileSize());
+ this.mXmppConnectionService.getNotificationService().push(message);
+ }
+ this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false);
+ if (mXmppAxolotlMessage != null) {
+ XmppAxolotlMessage.XmppAxolotlKeyTransportMessage transportMessage = account.getAxolotlService().processReceivingKeyTransportMessage(mXmppAxolotlMessage);
+ if (transportMessage != null) {
+ message.setEncryption(Message.ENCRYPTION_AXOLOTL);
+ this.file.setKey(transportMessage.getKey());
+ this.file.setIv(transportMessage.getIv());
+ message.setFingerprint(transportMessage.getFingerprint());
+ } else {
+ Log.d(Config.LOGTAG, "could not process KeyTransportMessage");
+ }
+ } else if (message.getEncryption() == Message.ENCRYPTION_OTR) {
+ byte[] key = conversation.getSymmetricKey();
+ if (key == null) {
+ this.sendCancel();
+ this.fail();
+ return;
+ } else {
+ this.file.setKeyAndIv(key);
+ }
+ }
+ this.mFileOutputStream = AbstractConnectionManager.createOutputStream(this.file, message.getEncryption() == Message.ENCRYPTION_AXOLOTL);
+ if (message.getEncryption() == Message.ENCRYPTION_OTR && Config.REPORT_WRONG_FILESIZE_IN_OTR_JINGLE) {
+ this.file.setExpectedSize((size / 16 + 1) * 16);
+ } else {
+ this.file.setExpectedSize(size);
+ }
+ Log.d(Config.LOGTAG, "receiving file: expecting size of " + this.file.getExpectedSize());
+ } else {
+ this.sendCancel();
+ this.fail();
+ }
+ } else {
+ this.sendCancel();
+ this.fail();
+ }
+ }
+
+ private void sendInitRequest() {
+ JinglePacket packet = this.bootstrapPacket("session-initiate");
+ 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);
+ 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");
+ cancel();
+ }
+ this.file.setKeyAndIv(conversation.getSymmetricKey());
+ pair = AbstractConnectionManager.createInputStream(this.file, false);
+ this.file.setExpectedSize(pair.second);
+ content.setFileOffer(this.file, true, this.ftVersion);
+ } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
+ this.file.setKey(mXmppAxolotlMessage.getInnerKey());
+ this.file.setIv(mXmppAxolotlMessage.getIV());
+ pair = AbstractConnectionManager.createInputStream(this.file, true);
+ this.file.setExpectedSize(pair.second);
+ content.setFileOffer(this.file, false, this.ftVersion).addChild(mXmppAxolotlMessage.toElement());
+ } else {
+ pair = AbstractConnectionManager.createInputStream(this.file, false);
+ this.file.setExpectedSize(pair.second);
+ content.setFileOffer(this.file, false, this.ftVersion);
+ }
+ } catch (FileNotFoundException e) {
+ cancel();
+ return;
+ }
+ this.mFileInputStream = pair.first;
+ content.setTransportId(this.transportId);
+ content.socks5transport().setChildren(getCandidatesAsElements());
+ packet.setContent(content);
+ this.sendJinglePacket(packet, new OnIqPacketReceived() {
+
+ @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");
+ mJingleStatus = JINGLE_STATUS_INITIATED;
+ mXmppConnectionService.markMessage(message, Message.STATUS_OFFERED);
+ } else {
+ fail(IqParser.extractErrorMessage(packet));
+ }
+ }
+ });
+
+ }
+ }
+
+ private List<Element> getCandidatesAsElements() {
+ List<Element> elements = new ArrayList<>();
+ for (JingleCandidate c : this.candidates) {
+ if (c.isOurs()) {
+ elements.add(c.toElement());
+ }
+ }
+ return elements;
+ }
+
+ private void sendAccept() {
+ mJingleStatus = JINGLE_STATUS_ACCEPTED;
+ this.mStatus = Transferable.STATUS_DOWNLOADING;
+ mXmppConnectionService.updateConversationUi();
+ this.mJingleConnectionManager.getPrimaryCandidate(this.account, new OnPrimaryCandidateFound() {
+ @Override
+ public void onPrimaryCandidateFound(boolean success, final JingleCandidate candidate) {
+ final JinglePacket packet = bootstrapPacket("session-accept");
+ final Content content = new Content(contentCreator, contentName);
+ content.setFileOffer(fileOffer, ftVersion);
+ content.setTransportId(transportId);
+ if (success && candidate != null && !equalCandidateExists(candidate)) {
+ final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
+ JingleConnection.this,
+ candidate);
+ connections.put(candidate.getCid(), socksConnection);
+ socksConnection.connect(new OnTransportConnected() {
+
+ @Override
+ public void failed() {
+ Log.d(Config.LOGTAG, "connection to our own primary candidate failed");
+ content.socks5transport().setChildren(getCandidatesAsElements());
+ packet.setContent(content);
+ sendJinglePacket(packet);
+ connectNextCandidate();
+ }
+
+ @Override
+ public void established() {
+ Log.d(Config.LOGTAG, "connected to primary candidate");
+ mergeCandidate(candidate);
+ content.socks5transport().setChildren(getCandidatesAsElements());
+ packet.setContent(content);
+ sendJinglePacket(packet);
+ connectNextCandidate();
+ }
+ });
+ } else {
+ Log.d(Config.LOGTAG, "did not find a primary candidate for ourself");
+ content.socks5transport().setChildren(getCandidatesAsElements());
+ packet.setContent(content);
+ sendJinglePacket(packet);
+ connectNextCandidate();
+ }
+ }
+ });
+ }
+
+ private JinglePacket bootstrapPacket(String action) {
+ JinglePacket packet = new JinglePacket();
+ packet.setAction(action);
+ packet.setFrom(account.getJid());
+ packet.setTo(this.message.getCounterpart());
+ packet.setSessionId(this.sessionId);
+ packet.setInitiator(this.initiator);
+ return packet;
+ }
+
+ private void sendJinglePacket(JinglePacket packet) {
+ mXmppConnectionService.sendIqPacket(account, packet, responseListener);
+ }
+
+ private void sendJinglePacket(JinglePacket packet, OnIqPacketReceived callback) {
+ mXmppConnectionService.sendIqPacket(account, packet, callback);
+ }
+
+ private boolean receiveAccept(JinglePacket packet) {
+ Content content = packet.getJingleContent();
+ mergeCandidates(JingleCandidate.parse(content.socks5transport()
+ .getChildren()));
+ this.mJingleStatus = JINGLE_STATUS_ACCEPTED;
+ mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
+ this.connectNextCandidate();
+ return true;
+ }
+
+ private boolean receiveTransportInfo(JinglePacket packet) {
+ Content content = packet.getJingleContent();
+ if (content.hasSocks5Transport()) {
+ if (content.socks5transport().hasChild("activated")) {
+ if ((this.transport != null) && (this.transport instanceof JingleSocks5Transport)) {
+ onProxyActivated.success();
+ } else {
+ String cid = content.socks5transport().findChild("activated").getAttribute("cid");
+ Log.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");
+ this.sendCancel();
+ this.fail();
+ }
+ }
+ return true;
+ } else if (content.socks5transport().hasChild("proxy-error")) {
+ onProxyActivated.failed();
+ return true;
+ } else if (content.socks5transport().hasChild("candidate-error")) {
+ Log.d(Config.LOGTAG, "received candidate error");
+ this.receivedCandidate = true;
+ if ((mJingleStatus == JINGLE_STATUS_ACCEPTED)
+ && (this.sentCandidate)) {
+ this.connect();
+ }
+ return true;
+ } else if (content.socks5transport().hasChild("candidate-used")) {
+ String cid = content.socks5transport()
+ .findChild("candidate-used").getAttribute("cid");
+ if (cid != null) {
+ Log.d(Config.LOGTAG, "candidate used by counterpart:" + cid);
+ JingleCandidate candidate = getCandidate(cid);
if (candidate == null) {
- Log.d(Config.LOGTAG,"could not find candidate with cid="+cid);
+ Log.d(Config.LOGTAG, "could not find candidate with cid=" + cid);
return false;
}
- candidate.flagAsUsedByCounterpart();
- this.receivedCandidate = true;
- if ((mJingleStatus == JINGLE_STATUS_ACCEPTED)
- && (this.sentCandidate)) {
- this.connect();
- } else {
- Log.d(Config.LOGTAG,
- "ignoring because file is already in transmission or we haven't sent our candidate yet");
- }
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- } else {
- return true;
- }
- }
-
- private void connect() {
- final JingleSocks5Transport connection = chooseConnection();
- this.transport = connection;
- if (connection == null) {
- Log.d(Config.LOGTAG, "could not find suitable candidate");
- this.disconnectSocks5Connections();
- if (this.initiator.equals(account.getJid())) {
- this.sendFallbackToIbb();
- }
- } else {
- this.mJingleStatus = JINGLE_STATUS_TRANSMITTING;
- if (connection.needsActivation()) {
- if (connection.getCandidate().isOurs()) {
- final String sid;
- if (ftVersion == Content.Version.FT_3) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": use session ID instead of transport ID to activate proxy");
- sid = getSessionId();
- } else {
- sid = getTransportId();
- }
- Log.d(Config.LOGTAG, "candidate "
- + connection.getCandidate().getCid()
- + " was our proxy. going to activate");
- IqPacket activation = new IqPacket(IqPacket.TYPE.SET);
- activation.setTo(connection.getCandidate().getJid());
- activation.query("http://jabber.org/protocol/bytestreams")
- .setAttribute("sid", sid);
- activation.query().addChild("activate")
- .setContent(this.getCounterPart().toString());
- mXmppConnectionService.sendIqPacket(account,activation,
- new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account,
- IqPacket packet) {
- if (packet.getType() != IqPacket.TYPE.RESULT) {
- onProxyActivated.failed();
- } else {
- onProxyActivated.success();
- sendProxyActivated(connection.getCandidate().getCid());
- }
- }
- });
- } else {
- Log.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");
- connection.send(file, onFileTransmissionSatusChanged);
- } else {
- Log.d(Config.LOGTAG, "we were responding. receiving file");
- connection.receive(file, onFileTransmissionSatusChanged);
- }
- }
- }
- }
-
- private JingleSocks5Transport chooseConnection() {
- JingleSocks5Transport connection = null;
- for (Entry<String, JingleSocks5Transport> cursor : connections
- .entrySet()) {
- JingleSocks5Transport currentConnection = cursor.getValue();
- // Log.d(Config.LOGTAG,"comparing candidate: "+currentConnection.getCandidate().toString());
- if (currentConnection.isEstablished()
- && (currentConnection.getCandidate().isUsedByCounterpart() || (!currentConnection
- .getCandidate().isOurs()))) {
- // Log.d(Config.LOGTAG,"is usable");
- if (connection == null) {
- connection = currentConnection;
- } else {
- if (connection.getCandidate().getPriority() < currentConnection
- .getCandidate().getPriority()) {
- connection = currentConnection;
- } else if (connection.getCandidate().getPriority() == currentConnection
- .getCandidate().getPriority()) {
- // Log.d(Config.LOGTAG,"found two candidates with same priority");
- if (initiator.equals(account.getJid())) {
- if (currentConnection.getCandidate().isOurs()) {
- connection = currentConnection;
- }
- } else {
- if (!currentConnection.getCandidate().isOurs()) {
- connection = currentConnection;
- }
- }
- }
- }
- }
- }
- return connection;
- }
-
- private void sendSuccess() {
- JinglePacket packet = bootstrapPacket("session-terminate");
- Reason reason = new Reason();
- reason.addChild("success");
- packet.setReason(reason);
- this.sendJinglePacket(packet);
- this.disconnectSocks5Connections();
- this.mJingleStatus = JINGLE_STATUS_FINISHED;
- this.message.setStatus(Message.STATUS_RECEIVED);
- this.message.setTransferable(null);
- this.mXmppConnectionService.updateMessage(message);
- this.mJingleConnectionManager.finishConnection(this);
- }
-
- private void sendFallbackToIbb() {
- Log.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();
- content.setTransportId(this.transportId);
- content.ibbTransport().setAttribute("block-size",
- Integer.toString(this.ibbBlockSize));
- packet.setContent(content);
- this.sendJinglePacket(packet);
- }
-
- private boolean receiveFallbackToIbb(JinglePacket packet) {
- Log.d(Config.LOGTAG, "receiving fallack to ibb");
- String receivedBlockSize = packet.getJingleContent().ibbTransport()
- .getAttribute("block-size");
- if (receivedBlockSize != null) {
- int bs = Integer.parseInt(receivedBlockSize);
- if (bs > this.ibbBlockSize) {
- this.ibbBlockSize = bs;
- }
- }
- this.transportId = packet.getJingleContent().getTransportId();
- this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize);
- this.transport.receive(file, onFileTransmissionSatusChanged);
- JinglePacket answer = bootstrapPacket("transport-accept");
- Content content = new Content("initiator", "a-file-offer");
- content.setTransportId(this.transportId);
- content.ibbTransport().setAttribute("block-size",this.ibbBlockSize);
- answer.setContent(content);
- this.sendJinglePacket(answer);
- return true;
- }
-
- private boolean receiveTransportAccept(JinglePacket packet) {
- if (packet.getJingleContent().hasIbbTransport()) {
- String receivedBlockSize = packet.getJingleContent().ibbTransport()
- .getAttribute("block-size");
- if (receivedBlockSize != null) {
- int bs = Integer.parseInt(receivedBlockSize);
- if (bs > this.ibbBlockSize) {
- this.ibbBlockSize = bs;
- }
- }
- this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize);
- this.transport.connect(new OnTransportConnected() {
-
- @Override
- public void failed() {
- Log.d(Config.LOGTAG, "ibb open failed");
- }
-
- @Override
- public void established() {
- JingleConnection.this.transport.send(file,
- onFileTransmissionSatusChanged);
- }
- });
- return true;
- } else {
- return false;
- }
- }
-
- private void receiveSuccess() {
- this.mJingleStatus = JINGLE_STATUS_FINISHED;
- this.mXmppConnectionService.markMessage(this.message,Message.STATUS_SEND_RECEIVED);
- this.disconnectSocks5Connections();
- if (this.transport != null && this.transport instanceof JingleInbandTransport) {
- this.transport.disconnect();
- }
- this.message.setTransferable(null);
- this.mJingleConnectionManager.finishConnection(this);
- }
-
- public void cancel() {
- this.disconnectSocks5Connections();
- if (this.transport != null && this.transport instanceof JingleInbandTransport) {
- this.transport.disconnect();
- }
- this.sendCancel();
- this.mJingleConnectionManager.finishConnection(this);
- if (this.responder.equals(account.getJid())) {
- this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED));
- if (this.file!=null) {
- file.delete();
- }
- this.mXmppConnectionService.updateConversationUi();
- } else {
- this.mXmppConnectionService.markMessage(this.message,
- Message.STATUS_SEND_FAILED);
- this.message.setTransferable(null);
- }
- }
-
- private void fail() {
+ candidate.flagAsUsedByCounterpart();
+ this.receivedCandidate = true;
+ if ((mJingleStatus == JINGLE_STATUS_ACCEPTED)
+ && (this.sentCandidate)) {
+ this.connect();
+ } else {
+ Log.d(Config.LOGTAG,
+ "ignoring because file is already in transmission or we haven't sent our candidate yet");
+ }
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ private void connect() {
+ final JingleSocks5Transport connection = chooseConnection();
+ this.transport = connection;
+ if (connection == null) {
+ Log.d(Config.LOGTAG, "could not find suitable candidate");
+ this.disconnectSocks5Connections();
+ if (this.initiator.equals(account.getJid())) {
+ this.sendFallbackToIbb();
+ }
+ } else {
+ this.mJingleStatus = JINGLE_STATUS_TRANSMITTING;
+ if (connection.needsActivation()) {
+ if (connection.getCandidate().isOurs()) {
+ final String sid;
+ if (ftVersion == Content.Version.FT_3) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": use session ID instead of transport ID to activate proxy");
+ sid = getSessionId();
+ } else {
+ sid = getTransportId();
+ }
+ Log.d(Config.LOGTAG, "candidate "
+ + connection.getCandidate().getCid()
+ + " was our proxy. going to activate");
+ IqPacket activation = new IqPacket(IqPacket.TYPE.SET);
+ activation.setTo(connection.getCandidate().getJid());
+ activation.query("http://jabber.org/protocol/bytestreams")
+ .setAttribute("sid", sid);
+ activation.query().addChild("activate")
+ .setContent(this.getCounterPart().toString());
+ mXmppConnectionService.sendIqPacket(account, activation,
+ new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account,
+ IqPacket packet) {
+ if (packet.getType() != IqPacket.TYPE.RESULT) {
+ onProxyActivated.failed();
+ } else {
+ onProxyActivated.success();
+ sendProxyActivated(connection.getCandidate().getCid());
+ }
+ }
+ });
+ } else {
+ Log.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");
+ connection.send(file, onFileTransmissionSatusChanged);
+ } else {
+ Log.d(Config.LOGTAG, "we were responding. receiving file");
+ connection.receive(file, onFileTransmissionSatusChanged);
+ }
+ }
+ }
+ }
+
+ private JingleSocks5Transport chooseConnection() {
+ JingleSocks5Transport connection = null;
+ for (Entry<String, JingleSocks5Transport> cursor : connections
+ .entrySet()) {
+ JingleSocks5Transport currentConnection = cursor.getValue();
+ // Log.d(Config.LOGTAG,"comparing candidate: "+currentConnection.getCandidate().toString());
+ if (currentConnection.isEstablished()
+ && (currentConnection.getCandidate().isUsedByCounterpart() || (!currentConnection
+ .getCandidate().isOurs()))) {
+ // Log.d(Config.LOGTAG,"is usable");
+ if (connection == null) {
+ connection = currentConnection;
+ } else {
+ if (connection.getCandidate().getPriority() < currentConnection
+ .getCandidate().getPriority()) {
+ connection = currentConnection;
+ } else if (connection.getCandidate().getPriority() == currentConnection
+ .getCandidate().getPriority()) {
+ // Log.d(Config.LOGTAG,"found two candidates with same priority");
+ if (initiator.equals(account.getJid())) {
+ if (currentConnection.getCandidate().isOurs()) {
+ connection = currentConnection;
+ }
+ } else {
+ if (!currentConnection.getCandidate().isOurs()) {
+ connection = currentConnection;
+ }
+ }
+ }
+ }
+ }
+ }
+ return connection;
+ }
+
+ private void sendSuccess() {
+ JinglePacket packet = bootstrapPacket("session-terminate");
+ Reason reason = new Reason();
+ reason.addChild("success");
+ packet.setReason(reason);
+ this.sendJinglePacket(packet);
+ this.disconnectSocks5Connections();
+ this.mJingleStatus = JINGLE_STATUS_FINISHED;
+ this.message.setStatus(Message.STATUS_RECEIVED);
+ this.message.setTransferable(null);
+ this.mXmppConnectionService.updateMessage(message);
+ this.mJingleConnectionManager.finishConnection(this);
+ }
+
+ private void sendFallbackToIbb() {
+ Log.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();
+ content.setTransportId(this.transportId);
+ content.ibbTransport().setAttribute("block-size",
+ Integer.toString(this.ibbBlockSize));
+ packet.setContent(content);
+ this.sendJinglePacket(packet);
+ }
+
+ private boolean receiveFallbackToIbb(JinglePacket packet) {
+ Log.d(Config.LOGTAG, "receiving fallack to ibb");
+ String receivedBlockSize = packet.getJingleContent().ibbTransport()
+ .getAttribute("block-size");
+ if (receivedBlockSize != null) {
+ int bs = Integer.parseInt(receivedBlockSize);
+ if (bs > this.ibbBlockSize) {
+ this.ibbBlockSize = bs;
+ }
+ }
+ this.transportId = packet.getJingleContent().getTransportId();
+ this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize);
+ this.transport.receive(file, onFileTransmissionSatusChanged);
+ JinglePacket answer = bootstrapPacket("transport-accept");
+ Content content = new Content("initiator", "a-file-offer");
+ content.setTransportId(this.transportId);
+ content.ibbTransport().setAttribute("block-size", this.ibbBlockSize);
+ answer.setContent(content);
+ this.sendJinglePacket(answer);
+ return true;
+ }
+
+ private boolean receiveTransportAccept(JinglePacket packet) {
+ if (packet.getJingleContent().hasIbbTransport()) {
+ String receivedBlockSize = packet.getJingleContent().ibbTransport()
+ .getAttribute("block-size");
+ if (receivedBlockSize != null) {
+ int bs = Integer.parseInt(receivedBlockSize);
+ if (bs > this.ibbBlockSize) {
+ this.ibbBlockSize = bs;
+ }
+ }
+ this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize);
+ this.transport.connect(new OnTransportConnected() {
+
+ @Override
+ public void failed() {
+ Log.d(Config.LOGTAG, "ibb open failed");
+ }
+
+ @Override
+ public void established() {
+ JingleConnection.this.transport.send(file,
+ onFileTransmissionSatusChanged);
+ }
+ });
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private void receiveSuccess() {
+ this.mJingleStatus = JINGLE_STATUS_FINISHED;
+ this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_RECEIVED);
+ this.disconnectSocks5Connections();
+ if (this.transport != null && this.transport instanceof JingleInbandTransport) {
+ this.transport.disconnect();
+ }
+ this.message.setTransferable(null);
+ this.mJingleConnectionManager.finishConnection(this);
+ }
+
+ public void cancel() {
+ this.disconnectSocks5Connections();
+ if (this.transport != null && this.transport instanceof JingleInbandTransport) {
+ this.transport.disconnect();
+ }
+ this.sendCancel();
+ this.mJingleConnectionManager.finishConnection(this);
+ if (this.responder.equals(account.getJid())) {
+ this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED));
+ if (this.file != null) {
+ file.delete();
+ }
+ this.mXmppConnectionService.updateConversationUi();
+ } else {
+ this.mXmppConnectionService.markMessage(this.message,
+ Message.STATUS_SEND_FAILED);
+ this.message.setTransferable(null);
+ }
+ }
+
+ private void fail() {
fail(null);
}
private void fail(String errorMessage) {
- this.mJingleStatus = JINGLE_STATUS_FAILED;
- this.disconnectSocks5Connections();
- if (this.transport != null && this.transport instanceof JingleInbandTransport) {
- this.transport.disconnect();
- }
- FileBackend.close(mFileInputStream);
- FileBackend.close(mFileOutputStream);
- if (this.message != null) {
- if (this.responder.equals(account.getJid())) {
- this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED));
- if (this.file!=null) {
- file.delete();
- }
- this.mXmppConnectionService.updateConversationUi();
- } else {
- this.mXmppConnectionService.markMessage(this.message,
- Message.STATUS_SEND_FAILED,
+ this.mJingleStatus = JINGLE_STATUS_FAILED;
+ this.disconnectSocks5Connections();
+ if (this.transport != null && this.transport instanceof JingleInbandTransport) {
+ this.transport.disconnect();
+ }
+ FileBackend.close(mFileInputStream);
+ FileBackend.close(mFileOutputStream);
+ if (this.message != null) {
+ if (this.responder.equals(account.getJid())) {
+ this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED));
+ if (this.file != null) {
+ file.delete();
+ }
+ this.mXmppConnectionService.updateConversationUi();
+ } else {
+ this.mXmppConnectionService.markMessage(this.message,
+ Message.STATUS_SEND_FAILED,
errorMessage);
- this.message.setTransferable(null);
- }
- }
- this.mJingleConnectionManager.finishConnection(this);
- }
-
- private void sendCancel() {
- JinglePacket packet = bootstrapPacket("session-terminate");
- Reason reason = new Reason();
- reason.addChild("cancel");
- packet.setReason(reason);
- this.sendJinglePacket(packet);
- }
-
- private void connectNextCandidate() {
- for (JingleCandidate candidate : this.candidates) {
- if ((!connections.containsKey(candidate.getCid()) && (!candidate
- .isOurs()))) {
- this.connectWithCandidate(candidate);
- return;
- }
- }
- this.sendCandidateError();
- }
-
- private void connectWithCandidate(final JingleCandidate candidate) {
- final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
- this, candidate);
- connections.put(candidate.getCid(), socksConnection);
- socksConnection.connect(new OnTransportConnected() {
-
- @Override
- public void failed() {
- Log.d(Config.LOGTAG,
- "connection failed with " + candidate.getHost() + ":"
- + candidate.getPort());
- connectNextCandidate();
- }
-
- @Override
- public void established() {
- Log.d(Config.LOGTAG,
- "established connection with " + candidate.getHost()
- + ":" + candidate.getPort());
- sendCandidateUsed(candidate.getCid());
- }
- });
- }
-
- private void disconnectSocks5Connections() {
- Iterator<Entry<String, JingleSocks5Transport>> it = this.connections
- .entrySet().iterator();
- while (it.hasNext()) {
- Entry<String, JingleSocks5Transport> pairs = it.next();
- pairs.getValue().disconnect();
- it.remove();
- }
- }
-
- private void sendProxyActivated(String cid) {
- JinglePacket packet = bootstrapPacket("transport-info");
- Content content = new Content(this.contentCreator, this.contentName);
- content.setTransportId(this.transportId);
- content.socks5transport().addChild("activated")
- .setAttribute("cid", cid);
- packet.setContent(content);
- this.sendJinglePacket(packet);
- }
-
- private void sendCandidateUsed(final String cid) {
- JinglePacket packet = bootstrapPacket("transport-info");
- Content content = new Content(this.contentCreator, this.contentName);
- content.setTransportId(this.transportId);
- content.socks5transport().addChild("candidate-used")
- .setAttribute("cid", cid);
- packet.setContent(content);
- this.sentCandidate = true;
- if ((receivedCandidate) && (mJingleStatus == JINGLE_STATUS_ACCEPTED)) {
- connect();
- }
- this.sendJinglePacket(packet);
- }
-
- private void sendCandidateError() {
- JinglePacket packet = bootstrapPacket("transport-info");
- Content content = new Content(this.contentCreator, this.contentName);
- content.setTransportId(this.transportId);
- content.socks5transport().addChild("candidate-error");
- packet.setContent(content);
- this.sentCandidate = true;
- if ((receivedCandidate) && (mJingleStatus == JINGLE_STATUS_ACCEPTED)) {
- connect();
- }
- this.sendJinglePacket(packet);
- }
-
- public Jid getInitiator() {
- return this.initiator;
- }
-
- public Jid getResponder() {
- return this.responder;
- }
-
- public int getJingleStatus() {
- return this.mJingleStatus;
- }
-
- private boolean equalCandidateExists(JingleCandidate candidate) {
- for (JingleCandidate c : this.candidates) {
- if (c.equalValues(candidate)) {
- return true;
- }
- }
- return false;
- }
-
- private void mergeCandidate(JingleCandidate candidate) {
- for (JingleCandidate c : this.candidates) {
- if (c.equals(candidate)) {
- return;
- }
- }
- this.candidates.add(candidate);
- }
-
- private void mergeCandidates(List<JingleCandidate> candidates) {
- for (JingleCandidate c : candidates) {
- mergeCandidate(c);
- }
- }
-
- private JingleCandidate getCandidate(String cid) {
- for (JingleCandidate c : this.candidates) {
- if (c.getCid().equals(cid)) {
- return c;
- }
- }
- return null;
- }
-
- public void updateProgress(int i) {
- this.mProgress = i;
- mXmppConnectionService.updateConversationUi();
- }
-
- public String getTransportId() {
- return this.transportId;
- }
-
- public Content.Version getFtVersion() {
- return this.ftVersion;
- }
-
- interface OnProxyActivated {
- public void success();
-
- public void failed();
- }
-
- public boolean hasTransportId(String sid) {
- return sid.equals(this.transportId);
- }
-
- public JingleTransport getTransport() {
- return this.transport;
- }
-
- public boolean start() {
- if (account.getStatus() == Account.State.ONLINE) {
- if (mJingleStatus == JINGLE_STATUS_INITIATED) {
- new Thread(new Runnable() {
-
- @Override
- public void run() {
- sendAccept();
- }
- }).start();
- }
- return true;
- } else {
- return false;
- }
- }
-
- @Override
- public int getStatus() {
- return this.mStatus;
- }
-
- @Override
- public long getFileSize() {
- if (this.file != null) {
- return this.file.getExpectedSize();
- } else {
- return 0;
- }
- }
-
- @Override
- public int getProgress() {
- return this.mProgress;
- }
-
- public AbstractConnectionManager getConnectionManager() {
- return this.mJingleConnectionManager;
- }
+ this.message.setTransferable(null);
+ }
+ }
+ this.mJingleConnectionManager.finishConnection(this);
+ }
+
+ private void sendCancel() {
+ JinglePacket packet = bootstrapPacket("session-terminate");
+ Reason reason = new Reason();
+ reason.addChild("cancel");
+ packet.setReason(reason);
+ this.sendJinglePacket(packet);
+ }
+
+ private void connectNextCandidate() {
+ for (JingleCandidate candidate : this.candidates) {
+ if ((!connections.containsKey(candidate.getCid()) && (!candidate
+ .isOurs()))) {
+ this.connectWithCandidate(candidate);
+ return;
+ }
+ }
+ this.sendCandidateError();
+ }
+
+ private void connectWithCandidate(final JingleCandidate candidate) {
+ final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
+ this, candidate);
+ connections.put(candidate.getCid(), socksConnection);
+ socksConnection.connect(new OnTransportConnected() {
+
+ @Override
+ public void failed() {
+ Log.d(Config.LOGTAG,
+ "connection failed with " + candidate.getHost() + ":"
+ + candidate.getPort());
+ connectNextCandidate();
+ }
+
+ @Override
+ public void established() {
+ Log.d(Config.LOGTAG,
+ "established connection with " + candidate.getHost()
+ + ":" + candidate.getPort());
+ sendCandidateUsed(candidate.getCid());
+ }
+ });
+ }
+
+ private void disconnectSocks5Connections() {
+ Iterator<Entry<String, JingleSocks5Transport>> it = this.connections
+ .entrySet().iterator();
+ while (it.hasNext()) {
+ Entry<String, JingleSocks5Transport> pairs = it.next();
+ pairs.getValue().disconnect();
+ it.remove();
+ }
+ }
+
+ private void sendProxyActivated(String cid) {
+ JinglePacket packet = bootstrapPacket("transport-info");
+ Content content = new Content(this.contentCreator, this.contentName);
+ content.setTransportId(this.transportId);
+ content.socks5transport().addChild("activated")
+ .setAttribute("cid", cid);
+ packet.setContent(content);
+ this.sendJinglePacket(packet);
+ }
+
+ private void sendCandidateUsed(final String cid) {
+ JinglePacket packet = bootstrapPacket("transport-info");
+ Content content = new Content(this.contentCreator, this.contentName);
+ content.setTransportId(this.transportId);
+ content.socks5transport().addChild("candidate-used")
+ .setAttribute("cid", cid);
+ packet.setContent(content);
+ this.sentCandidate = true;
+ if ((receivedCandidate) && (mJingleStatus == JINGLE_STATUS_ACCEPTED)) {
+ connect();
+ }
+ this.sendJinglePacket(packet);
+ }
+
+ private void sendCandidateError() {
+ JinglePacket packet = bootstrapPacket("transport-info");
+ Content content = new Content(this.contentCreator, this.contentName);
+ content.setTransportId(this.transportId);
+ content.socks5transport().addChild("candidate-error");
+ packet.setContent(content);
+ this.sentCandidate = true;
+ if ((receivedCandidate) && (mJingleStatus == JINGLE_STATUS_ACCEPTED)) {
+ connect();
+ }
+ this.sendJinglePacket(packet);
+ }
+
+ public Jid getInitiator() {
+ return this.initiator;
+ }
+
+ public Jid getResponder() {
+ return this.responder;
+ }
+
+ public int getJingleStatus() {
+ return this.mJingleStatus;
+ }
+
+ private boolean equalCandidateExists(JingleCandidate candidate) {
+ for (JingleCandidate c : this.candidates) {
+ if (c.equalValues(candidate)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void mergeCandidate(JingleCandidate candidate) {
+ for (JingleCandidate c : this.candidates) {
+ if (c.equals(candidate)) {
+ return;
+ }
+ }
+ this.candidates.add(candidate);
+ }
+
+ private void mergeCandidates(List<JingleCandidate> candidates) {
+ for (JingleCandidate c : candidates) {
+ mergeCandidate(c);
+ }
+ }
+
+ private JingleCandidate getCandidate(String cid) {
+ for (JingleCandidate c : this.candidates) {
+ if (c.getCid().equals(cid)) {
+ return c;
+ }
+ }
+ return null;
+ }
+
+ public void updateProgress(int i) {
+ this.mProgress = i;
+ mXmppConnectionService.updateConversationUi();
+ }
+
+ public String getTransportId() {
+ return this.transportId;
+ }
+
+ public Content.Version getFtVersion() {
+ return this.ftVersion;
+ }
+
+ interface OnProxyActivated {
+ public void success();
+
+ public void failed();
+ }
+
+ public boolean hasTransportId(String sid) {
+ return sid.equals(this.transportId);
+ }
+
+ public JingleTransport getTransport() {
+ return this.transport;
+ }
+
+ public boolean start() {
+ if (account.getStatus() == Account.State.ONLINE) {
+ if (mJingleStatus == JINGLE_STATUS_INITIATED) {
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ sendAccept();
+ }
+ }).start();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int getStatus() {
+ return this.mStatus;
+ }
+
+ @Override
+ public long getFileSize() {
+ if (this.file != null) {
+ return this.file.getExpectedSize();
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public int getProgress() {
+ return this.mProgress;
+ }
+
+ public AbstractConnectionManager getConnectionManager() {
+ return this.mJingleConnectionManager;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnectionManager.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnectionManager.java
index b8f96bded..d36bc52b0 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnectionManager.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnectionManager.java
@@ -23,149 +23,149 @@ import de.pixart.messenger.xmpp.jingle.stanzas.JinglePacket;
import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class JingleConnectionManager extends AbstractConnectionManager {
- private List<JingleConnection> connections = new CopyOnWriteArrayList<>();
-
- private HashMap<Jid, JingleCandidate> primaryCandidates = new HashMap<>();
-
- @SuppressLint("TrulyRandom")
- private SecureRandom random = new SecureRandom();
-
- public JingleConnectionManager(XmppConnectionService service) {
- super(service);
- }
-
- public void deliverPacket(Account account, JinglePacket packet) {
- if (packet.isAction("session-initiate")) {
- JingleConnection connection = new JingleConnection(this);
- connection.init(account, packet);
- connections.add(connection);
- } else {
- for (JingleConnection connection : connections) {
- if (connection.getAccount() == account
- && connection.getSessionId().equals(
- packet.getSessionId())
- && connection.getCounterPart().equals(packet.getFrom())) {
- connection.deliverPacket(packet);
- return;
- }
- }
- IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR);
- Element error = response.addChild("error");
- error.setAttribute("type", "cancel");
- error.addChild("item-not-found",
- "urn:ietf:params:xml:ns:xmpp-stanzas");
- error.addChild("unknown-session", "urn:xmpp:jingle:errors:1");
- account.getXmppConnection().sendIqPacket(response, null);
- }
- }
-
- public JingleConnection createNewConnection(Message message) {
- Transferable old = message.getTransferable();
- if (old != null) {
- old.cancel();
- }
- JingleConnection connection = new JingleConnection(this);
- mXmppConnectionService.markMessage(message,Message.STATUS_WAITING);
- connection.init(message);
- this.connections.add(connection);
- return connection;
- }
-
- public JingleConnection createNewConnection(final JinglePacket packet) {
- JingleConnection connection = new JingleConnection(this);
- this.connections.add(connection);
- return connection;
- }
-
- public void finishConnection(JingleConnection connection) {
- this.connections.remove(connection);
- }
-
- public void getPrimaryCandidate(Account account,
- final OnPrimaryCandidateFound listener) {
- if (Config.DISABLE_PROXY_LOOKUP) {
- listener.onPrimaryCandidateFound(false, null);
- return;
- }
- if (!this.primaryCandidates.containsKey(account.getJid().toBareJid())) {
- final Jid proxy = account.getXmppConnection().findDiscoItemByFeature(Xmlns.BYTE_STREAMS);
- if (proxy != null) {
- IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
- iq.setTo(proxy);
- iq.query(Xmlns.BYTE_STREAMS);
- account.getXmppConnection().sendIqPacket(iq,new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- Element streamhost = packet.query().findChild("streamhost",Xmlns.BYTE_STREAMS);
- final String host = streamhost == null ? null : streamhost.getAttribute("host");
- final String port = streamhost == null ? null : streamhost.getAttribute("port");
- if (host != null && port != null) {
- try {
- JingleCandidate candidate = new JingleCandidate(nextRandomId(), true);
- candidate.setHost(host);
- candidate.setPort(Integer.parseInt(port));
- candidate.setType(JingleCandidate.TYPE_PROXY);
- candidate.setJid(proxy);
- candidate.setPriority(655360 + 65535);
- primaryCandidates.put(account.getJid().toBareJid(),candidate);
- listener.onPrimaryCandidateFound(true,candidate);
- } catch (final NumberFormatException e) {
- listener.onPrimaryCandidateFound(false,null);
- return;
- }
- } else {
- listener.onPrimaryCandidateFound(false,null);
- }
- }
- });
- } else {
- listener.onPrimaryCandidateFound(false, null);
- }
-
- } else {
- listener.onPrimaryCandidateFound(true,
- this.primaryCandidates.get(account.getJid().toBareJid()));
- }
- }
-
- public String nextRandomId() {
- return new BigInteger(50, random).toString(32);
- }
-
- public void deliverIbbPacket(Account account, IqPacket packet) {
- String sid = null;
- Element payload = null;
- if (packet.hasChild("open", "http://jabber.org/protocol/ibb")) {
- payload = packet.findChild("open", "http://jabber.org/protocol/ibb");
- sid = payload.getAttribute("sid");
- } else if (packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
- payload = packet.findChild("data", "http://jabber.org/protocol/ibb");
- sid = payload.getAttribute("sid");
- }
- if (sid != null) {
- for (JingleConnection connection : connections) {
- if (connection.getAccount() == account
- && connection.hasTransportId(sid)) {
- JingleTransport transport = connection.getTransport();
- if (transport instanceof JingleInbandTransport) {
- JingleInbandTransport inbandTransport = (JingleInbandTransport) transport;
- inbandTransport.deliverPayload(packet, payload);
- return;
- }
- }
- }
- Log.d(Config.LOGTAG,"couldn't deliver payload: " + payload.toString());
- } else {
- Log.d(Config.LOGTAG, "no sid found in incoming ibb packet");
- }
- }
-
- public void cancelInTransmission() {
- for (JingleConnection connection : this.connections) {
- if (connection.getJingleStatus() == JingleConnection.JINGLE_STATUS_TRANSMITTING) {
- connection.cancel();
- }
- }
- }
+ private List<JingleConnection> connections = new CopyOnWriteArrayList<>();
+
+ private HashMap<Jid, JingleCandidate> primaryCandidates = new HashMap<>();
+
+ @SuppressLint("TrulyRandom")
+ private SecureRandom random = new SecureRandom();
+
+ public JingleConnectionManager(XmppConnectionService service) {
+ super(service);
+ }
+
+ public void deliverPacket(Account account, JinglePacket packet) {
+ if (packet.isAction("session-initiate")) {
+ JingleConnection connection = new JingleConnection(this);
+ connection.init(account, packet);
+ connections.add(connection);
+ } else {
+ for (JingleConnection connection : connections) {
+ if (connection.getAccount() == account
+ && connection.getSessionId().equals(
+ packet.getSessionId())
+ && connection.getCounterPart().equals(packet.getFrom())) {
+ connection.deliverPacket(packet);
+ return;
+ }
+ }
+ IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR);
+ Element error = response.addChild("error");
+ error.setAttribute("type", "cancel");
+ error.addChild("item-not-found",
+ "urn:ietf:params:xml:ns:xmpp-stanzas");
+ error.addChild("unknown-session", "urn:xmpp:jingle:errors:1");
+ account.getXmppConnection().sendIqPacket(response, null);
+ }
+ }
+
+ public JingleConnection createNewConnection(Message message) {
+ Transferable old = message.getTransferable();
+ if (old != null) {
+ old.cancel();
+ }
+ JingleConnection connection = new JingleConnection(this);
+ mXmppConnectionService.markMessage(message, Message.STATUS_WAITING);
+ connection.init(message);
+ this.connections.add(connection);
+ return connection;
+ }
+
+ public JingleConnection createNewConnection(final JinglePacket packet) {
+ JingleConnection connection = new JingleConnection(this);
+ this.connections.add(connection);
+ return connection;
+ }
+
+ public void finishConnection(JingleConnection connection) {
+ this.connections.remove(connection);
+ }
+
+ public void getPrimaryCandidate(Account account,
+ final OnPrimaryCandidateFound listener) {
+ if (Config.DISABLE_PROXY_LOOKUP) {
+ listener.onPrimaryCandidateFound(false, null);
+ return;
+ }
+ if (!this.primaryCandidates.containsKey(account.getJid().toBareJid())) {
+ final Jid proxy = account.getXmppConnection().findDiscoItemByFeature(Xmlns.BYTE_STREAMS);
+ if (proxy != null) {
+ IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
+ iq.setTo(proxy);
+ iq.query(Xmlns.BYTE_STREAMS);
+ account.getXmppConnection().sendIqPacket(iq, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Element streamhost = packet.query().findChild("streamhost", Xmlns.BYTE_STREAMS);
+ final String host = streamhost == null ? null : streamhost.getAttribute("host");
+ final String port = streamhost == null ? null : streamhost.getAttribute("port");
+ if (host != null && port != null) {
+ try {
+ JingleCandidate candidate = new JingleCandidate(nextRandomId(), true);
+ candidate.setHost(host);
+ candidate.setPort(Integer.parseInt(port));
+ candidate.setType(JingleCandidate.TYPE_PROXY);
+ candidate.setJid(proxy);
+ candidate.setPriority(655360 + 65535);
+ primaryCandidates.put(account.getJid().toBareJid(), candidate);
+ listener.onPrimaryCandidateFound(true, candidate);
+ } catch (final NumberFormatException e) {
+ listener.onPrimaryCandidateFound(false, null);
+ return;
+ }
+ } else {
+ listener.onPrimaryCandidateFound(false, null);
+ }
+ }
+ });
+ } else {
+ listener.onPrimaryCandidateFound(false, null);
+ }
+
+ } else {
+ listener.onPrimaryCandidateFound(true,
+ this.primaryCandidates.get(account.getJid().toBareJid()));
+ }
+ }
+
+ public String nextRandomId() {
+ return new BigInteger(50, random).toString(32);
+ }
+
+ public void deliverIbbPacket(Account account, IqPacket packet) {
+ String sid = null;
+ Element payload = null;
+ if (packet.hasChild("open", "http://jabber.org/protocol/ibb")) {
+ payload = packet.findChild("open", "http://jabber.org/protocol/ibb");
+ sid = payload.getAttribute("sid");
+ } else if (packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
+ payload = packet.findChild("data", "http://jabber.org/protocol/ibb");
+ sid = payload.getAttribute("sid");
+ }
+ if (sid != null) {
+ for (JingleConnection connection : connections) {
+ if (connection.getAccount() == account
+ && connection.hasTransportId(sid)) {
+ JingleTransport transport = connection.getTransport();
+ if (transport instanceof JingleInbandTransport) {
+ JingleInbandTransport inbandTransport = (JingleInbandTransport) transport;
+ inbandTransport.deliverPayload(packet, payload);
+ return;
+ }
+ }
+ }
+ Log.d(Config.LOGTAG, "couldn't deliver payload: " + payload.toString());
+ } else {
+ Log.d(Config.LOGTAG, "no sid found in incoming ibb packet");
+ }
+ }
+
+ public void cancelInTransmission() {
+ for (JingleConnection connection : this.connections) {
+ if (connection.getJingleStatus() == JingleConnection.JINGLE_STATUS_TRANSMITTING) {
+ connection.cancel();
+ }
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleInbandTransport.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleInbandTransport.java
index 4e45c2a03..b19910c9f 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleInbandTransport.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleInbandTransport.java
@@ -22,218 +22,218 @@ import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class JingleInbandTransport extends JingleTransport {
- private Account account;
- private Jid counterpart;
- private int blockSize;
- private int seq = 0;
- private String sessionId;
-
- private boolean established = false;
-
- private boolean connected = true;
-
- private DownloadableFile file;
- private JingleConnection connection;
-
- private InputStream fileInputStream = null;
- private OutputStream fileOutputStream = null;
- private long remainingSize = 0;
- private long fileSize = 0;
- private MessageDigest digest;
-
- private OnFileTransmissionStatusChanged onFileTransmissionStatusChanged;
-
- private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (connected && packet.getType() == IqPacket.TYPE.RESULT) {
- sendNextBlock();
- }
- }
- };
-
- public JingleInbandTransport(final JingleConnection connection, final String sid, final int blocksize) {
- this.connection = connection;
- this.account = connection.getAccount();
- this.counterpart = connection.getCounterPart();
- this.blockSize = blocksize;
- this.sessionId = sid;
- }
-
- public void connect(final OnTransportConnected callback) {
- IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- iq.setTo(this.counterpart);
- Element open = iq.addChild("open", "http://jabber.org/protocol/ibb");
- open.setAttribute("sid", this.sessionId);
- open.setAttribute("stanza", "iq");
- open.setAttribute("block-size", Integer.toString(this.blockSize));
- this.connected = true;
- this.account.getXmppConnection().sendIqPacket(iq,
- new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account,
- IqPacket packet) {
- if (packet.getType() != IqPacket.TYPE.RESULT) {
- callback.failed();
- } else {
- callback.established();
- }
- }
- });
- }
-
- @Override
- public void receive(DownloadableFile file,
- OnFileTransmissionStatusChanged callback) {
- this.onFileTransmissionStatusChanged = callback;
- this.file = file;
- try {
- this.digest = MessageDigest.getInstance("SHA-1");
- digest.reset();
- file.getParentFile().mkdirs();
- file.createNewFile();
- this.fileOutputStream = connection.getFileOutputStream();
- if (this.fileOutputStream == null) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not create output stream");
- callback.onFileTransferAborted();
- return;
- }
- this.remainingSize = this.fileSize = file.getExpectedSize();
- } catch (final NoSuchAlgorithmException | IOException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+" "+e.getMessage());
- callback.onFileTransferAborted();
- }
+ private Account account;
+ private Jid counterpart;
+ private int blockSize;
+ private int seq = 0;
+ private String sessionId;
+
+ private boolean established = false;
+
+ private boolean connected = true;
+
+ private DownloadableFile file;
+ private JingleConnection connection;
+
+ private InputStream fileInputStream = null;
+ private OutputStream fileOutputStream = null;
+ private long remainingSize = 0;
+ private long fileSize = 0;
+ private MessageDigest digest;
+
+ private OnFileTransmissionStatusChanged onFileTransmissionStatusChanged;
+
+ private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (connected && packet.getType() == IqPacket.TYPE.RESULT) {
+ sendNextBlock();
+ }
+ }
+ };
+
+ public JingleInbandTransport(final JingleConnection connection, final String sid, final int blocksize) {
+ this.connection = connection;
+ this.account = connection.getAccount();
+ this.counterpart = connection.getCounterPart();
+ this.blockSize = blocksize;
+ this.sessionId = sid;
}
- @Override
- public void send(DownloadableFile file,
- OnFileTransmissionStatusChanged callback) {
- this.onFileTransmissionStatusChanged = callback;
- this.file = file;
- try {
- this.remainingSize = this.file.getExpectedSize();
- this.fileSize = this.remainingSize;
- this.digest = MessageDigest.getInstance("SHA-1");
- this.digest.reset();
- fileInputStream = connection.getFileInputStream();
- if (fileInputStream == null) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could no create input stream");
- callback.onFileTransferAborted();
- return;
- }
- if (this.connected) {
- this.sendNextBlock();
- }
- } catch (NoSuchAlgorithmException e) {
- callback.onFileTransferAborted();
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
- }
- }
-
- @Override
- public void disconnect() {
- this.connected = false;
- if (this.fileOutputStream != null) {
- try {
- this.fileOutputStream.close();
- } catch (IOException e) {
-
- }
- }
- if (this.fileInputStream != null) {
- try {
- this.fileInputStream.close();
- } catch (IOException e) {
-
- }
- }
- }
-
- private void sendNextBlock() {
- byte[] buffer = new byte[this.blockSize];
- try {
- int count = fileInputStream.read(buffer);
- if (count == -1) {
- file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
- this.onFileTransmissionStatusChanged.onFileTransmitted(file);
- fileInputStream.close();
- return;
- } else if (count != buffer.length) {
- int rem = fileInputStream.read(buffer,count,buffer.length-count);
- if (rem > 0) {
- count += rem;
- }
- }
- this.remainingSize -= count;
- this.digest.update(buffer,0,count);
- String base64 = Base64.encodeToString(buffer,0,count, Base64.NO_WRAP);
- IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- iq.setTo(this.counterpart);
- Element data = iq.addChild("data", "http://jabber.org/protocol/ibb");
- data.setAttribute("seq", Integer.toString(this.seq));
- data.setAttribute("block-size", Integer.toString(this.blockSize));
- data.setAttribute("sid", this.sessionId);
- data.setContent(base64);
- this.account.getXmppConnection().sendIqPacket(iq, this.onAckReceived);
- this.account.getXmppConnection().r(); //don't fill up stanza queue too much
- this.seq++;
- if (this.remainingSize > 0) {
- connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
- } else {
- file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
- this.onFileTransmissionStatusChanged.onFileTransmitted(file);
- fileInputStream.close();
- }
- } catch (IOException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
- FileBackend.close(fileInputStream);
- this.onFileTransmissionStatusChanged.onFileTransferAborted();
- }
- }
-
- private void receiveNextBlock(String data) {
- try {
- byte[] buffer = Base64.decode(data, Base64.NO_WRAP);
- if (this.remainingSize < buffer.length) {
- buffer = Arrays.copyOfRange(buffer, 0, (int) this.remainingSize);
- }
- this.remainingSize -= buffer.length;
- this.fileOutputStream.write(buffer);
- this.digest.update(buffer);
- if (this.remainingSize <= 0) {
- file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
- fileOutputStream.flush();
- fileOutputStream.close();
- this.onFileTransmissionStatusChanged.onFileTransmitted(file);
- } else {
- connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
- }
- } catch (Exception e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
- FileBackend.close(fileOutputStream);
- this.onFileTransmissionStatusChanged.onFileTransferAborted();
- }
- }
-
- public void deliverPayload(IqPacket packet, Element payload) {
- if (payload.getName().equals("open")) {
- if (!established) {
- established = true;
- connected = true;
- this.receiveNextBlock("");
- this.account.getXmppConnection().sendIqPacket(
- packet.generateResponse(IqPacket.TYPE.RESULT), null);
- } else {
- this.account.getXmppConnection().sendIqPacket(
- packet.generateResponse(IqPacket.TYPE.ERROR), null);
- }
- } else if (connected && payload.getName().equals("data")) {
- this.receiveNextBlock(payload.getContent());
- this.account.getXmppConnection().sendIqPacket(
- packet.generateResponse(IqPacket.TYPE.RESULT), null);
- } else {
- // TODO some sort of exception
- }
- }
+ public void connect(final OnTransportConnected callback) {
+ IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ iq.setTo(this.counterpart);
+ Element open = iq.addChild("open", "http://jabber.org/protocol/ibb");
+ open.setAttribute("sid", this.sessionId);
+ open.setAttribute("stanza", "iq");
+ open.setAttribute("block-size", Integer.toString(this.blockSize));
+ this.connected = true;
+ this.account.getXmppConnection().sendIqPacket(iq,
+ new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account,
+ IqPacket packet) {
+ if (packet.getType() != IqPacket.TYPE.RESULT) {
+ callback.failed();
+ } else {
+ callback.established();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void receive(DownloadableFile file,
+ OnFileTransmissionStatusChanged callback) {
+ this.onFileTransmissionStatusChanged = callback;
+ this.file = file;
+ try {
+ this.digest = MessageDigest.getInstance("SHA-1");
+ digest.reset();
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ this.fileOutputStream = connection.getFileOutputStream();
+ if (this.fileOutputStream == null) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not create output stream");
+ callback.onFileTransferAborted();
+ return;
+ }
+ this.remainingSize = this.fileSize = file.getExpectedSize();
+ } catch (final NoSuchAlgorithmException | IOException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + " " + e.getMessage());
+ callback.onFileTransferAborted();
+ }
+ }
+
+ @Override
+ public void send(DownloadableFile file,
+ OnFileTransmissionStatusChanged callback) {
+ this.onFileTransmissionStatusChanged = callback;
+ this.file = file;
+ try {
+ this.remainingSize = this.file.getExpectedSize();
+ this.fileSize = this.remainingSize;
+ this.digest = MessageDigest.getInstance("SHA-1");
+ this.digest.reset();
+ fileInputStream = connection.getFileInputStream();
+ if (fileInputStream == null) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could no create input stream");
+ callback.onFileTransferAborted();
+ return;
+ }
+ if (this.connected) {
+ this.sendNextBlock();
+ }
+ } catch (NoSuchAlgorithmException e) {
+ callback.onFileTransferAborted();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": " + e.getMessage());
+ }
+ }
+
+ @Override
+ public void disconnect() {
+ this.connected = false;
+ if (this.fileOutputStream != null) {
+ try {
+ this.fileOutputStream.close();
+ } catch (IOException e) {
+
+ }
+ }
+ if (this.fileInputStream != null) {
+ try {
+ this.fileInputStream.close();
+ } catch (IOException e) {
+
+ }
+ }
+ }
+
+ private void sendNextBlock() {
+ byte[] buffer = new byte[this.blockSize];
+ try {
+ int count = fileInputStream.read(buffer);
+ if (count == -1) {
+ file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
+ this.onFileTransmissionStatusChanged.onFileTransmitted(file);
+ fileInputStream.close();
+ return;
+ } else if (count != buffer.length) {
+ int rem = fileInputStream.read(buffer, count, buffer.length - count);
+ if (rem > 0) {
+ count += rem;
+ }
+ }
+ this.remainingSize -= count;
+ this.digest.update(buffer, 0, count);
+ String base64 = Base64.encodeToString(buffer, 0, count, Base64.NO_WRAP);
+ IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ iq.setTo(this.counterpart);
+ Element data = iq.addChild("data", "http://jabber.org/protocol/ibb");
+ data.setAttribute("seq", Integer.toString(this.seq));
+ data.setAttribute("block-size", Integer.toString(this.blockSize));
+ data.setAttribute("sid", this.sessionId);
+ data.setContent(base64);
+ this.account.getXmppConnection().sendIqPacket(iq, this.onAckReceived);
+ this.account.getXmppConnection().r(); //don't fill up stanza queue too much
+ this.seq++;
+ if (this.remainingSize > 0) {
+ connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
+ } else {
+ file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
+ this.onFileTransmissionStatusChanged.onFileTransmitted(file);
+ fileInputStream.close();
+ }
+ } catch (IOException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": " + e.getMessage());
+ FileBackend.close(fileInputStream);
+ this.onFileTransmissionStatusChanged.onFileTransferAborted();
+ }
+ }
+
+ private void receiveNextBlock(String data) {
+ try {
+ byte[] buffer = Base64.decode(data, Base64.NO_WRAP);
+ if (this.remainingSize < buffer.length) {
+ buffer = Arrays.copyOfRange(buffer, 0, (int) this.remainingSize);
+ }
+ this.remainingSize -= buffer.length;
+ this.fileOutputStream.write(buffer);
+ this.digest.update(buffer);
+ if (this.remainingSize <= 0) {
+ file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
+ fileOutputStream.flush();
+ fileOutputStream.close();
+ this.onFileTransmissionStatusChanged.onFileTransmitted(file);
+ } else {
+ connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
+ }
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": " + e.getMessage());
+ FileBackend.close(fileOutputStream);
+ this.onFileTransmissionStatusChanged.onFileTransferAborted();
+ }
+ }
+
+ public void deliverPayload(IqPacket packet, Element payload) {
+ if (payload.getName().equals("open")) {
+ if (!established) {
+ established = true;
+ connected = true;
+ this.receiveNextBlock("");
+ this.account.getXmppConnection().sendIqPacket(
+ packet.generateResponse(IqPacket.TYPE.RESULT), null);
+ } else {
+ this.account.getXmppConnection().sendIqPacket(
+ packet.generateResponse(IqPacket.TYPE.ERROR), null);
+ }
+ } else if (connected && payload.getName().equals("data")) {
+ this.receiveNextBlock(payload.getContent());
+ this.account.getXmppConnection().sendIqPacket(
+ packet.generateResponse(IqPacket.TYPE.RESULT), null);
+ } else {
+ // TODO some sort of exception
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java
index 4f3e78394..57d1afeba 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java
@@ -20,191 +20,191 @@ import de.pixart.messenger.utils.SocksSocketFactory;
import de.pixart.messenger.xmpp.jingle.stanzas.Content;
public class JingleSocks5Transport extends JingleTransport {
- private JingleCandidate candidate;
- private JingleConnection connection;
- private String destination;
- private OutputStream outputStream;
- private InputStream inputStream;
- private boolean isEstablished = false;
- private boolean activated = false;
- protected Socket socket;
-
- public JingleSocks5Transport(JingleConnection jingleConnection,
- JingleCandidate candidate) {
- this.candidate = candidate;
- this.connection = jingleConnection;
- try {
- MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
- StringBuilder destBuilder = new StringBuilder();
- if (jingleConnection.getFtVersion() == Content.Version.FT_3) {
- Log.d(Config.LOGTAG,this.connection.getAccount().getJid().toBareJid()+": using session Id instead of transport Id for proxy destination");
- destBuilder.append(jingleConnection.getSessionId());
- } else {
- destBuilder.append(jingleConnection.getTransportId());
- }
- if (candidate.isOurs()) {
- destBuilder.append(jingleConnection.getAccount().getJid());
- destBuilder.append(jingleConnection.getCounterPart());
- } else {
- destBuilder.append(jingleConnection.getCounterPart());
- destBuilder.append(jingleConnection.getAccount().getJid());
- }
- mDigest.reset();
- this.destination = CryptoHelper.bytesToHex(mDigest
- .digest(destBuilder.toString().getBytes()));
- } catch (NoSuchAlgorithmException e) {
-
- }
- }
-
- public void connect(final OnTransportConnected callback) {
- new Thread(new Runnable() {
-
- @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);
- }
- inputStream = socket.getInputStream();
- outputStream = socket.getOutputStream();
- SocksSocketFactory.createSocksConnection(socket,destination,0);
- isEstablished = true;
- callback.established();
- } catch (IOException e) {
- callback.failed();
- }
- }
- }).start();
-
- }
-
- public void send(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) {
- new Thread(new Runnable() {
-
- @Override
- public void run() {
- InputStream fileInputStream = null;
- final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_"+connection.getSessionId());
- try {
- wakeLock.acquire();
- MessageDigest digest = MessageDigest.getInstance("SHA-1");
- digest.reset();
- fileInputStream = connection.getFileInputStream();
- if (fileInputStream == null) {
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create input stream");
- callback.onFileTransferAborted();
- return;
- }
- long size = file.getExpectedSize();
- long transmitted = 0;
- int count;
- byte[] buffer = new byte[8192];
- while ((count = fileInputStream.read(buffer)) > 0) {
- outputStream.write(buffer, 0, count);
- digest.update(buffer, 0, count);
- transmitted += count;
- connection.updateProgress((int) ((((double) transmitted) / size) * 100));
- }
- outputStream.flush();
- file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
- if (callback != null) {
- callback.onFileTransmitted(file);
- }
- } catch (Exception e) {
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
- callback.onFileTransferAborted();
- } finally {
- FileBackend.close(fileInputStream);
- wakeLock.release();
- }
- }
- }).start();
-
- }
-
- public void receive(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) {
- new Thread(new Runnable() {
-
- @Override
- public void run() {
- OutputStream fileOutputStream = null;
- final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_receive_"+connection.getSessionId());
- try {
- wakeLock.acquire();
- MessageDigest digest = MessageDigest.getInstance("SHA-1");
- digest.reset();
- //inputStream.skip(45);
- socket.setSoTimeout(30000);
- file.getParentFile().mkdirs();
- file.createNewFile();
- fileOutputStream = connection.getFileOutputStream();
- if (fileOutputStream == null) {
- callback.onFileTransferAborted();
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create output stream");
- return;
- }
- double size = file.getExpectedSize();
- long remainingSize = file.getExpectedSize();
- byte[] buffer = new byte[8192];
- int count;
- while (remainingSize > 0) {
- count = inputStream.read(buffer);
- if (count == -1) {
- callback.onFileTransferAborted();
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": file ended prematurely with "+remainingSize+" bytes remaining");
- return;
- } else {
- fileOutputStream.write(buffer, 0, count);
- digest.update(buffer, 0, count);
- remainingSize -= count;
- }
- connection.updateProgress((int) (((size - remainingSize) / size) * 100));
- }
- fileOutputStream.flush();
- fileOutputStream.close();
- file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
- callback.onFileTransmitted(file);
- } catch (Exception e) {
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
- callback.onFileTransferAborted();
- } finally {
- wakeLock.release();
- FileBackend.close(fileOutputStream);
- FileBackend.close(inputStream);
- }
- }
- }).start();
- }
-
- public boolean isProxy() {
- return this.candidate.getType() == JingleCandidate.TYPE_PROXY;
- }
-
- public boolean needsActivation() {
- return (this.isProxy() && !this.activated);
- }
-
- public void disconnect() {
- FileBackend.close(inputStream);
- FileBackend.close(outputStream);
- FileBackend.close(socket);
- }
-
- public boolean isEstablished() {
- return this.isEstablished;
- }
-
- public JingleCandidate getCandidate() {
- return this.candidate;
- }
-
- public void setActivated(boolean activated) {
- this.activated = activated;
- }
+ private JingleCandidate candidate;
+ private JingleConnection connection;
+ private String destination;
+ private OutputStream outputStream;
+ private InputStream inputStream;
+ private boolean isEstablished = false;
+ private boolean activated = false;
+ protected Socket socket;
+
+ public JingleSocks5Transport(JingleConnection jingleConnection,
+ JingleCandidate candidate) {
+ this.candidate = candidate;
+ this.connection = jingleConnection;
+ try {
+ MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
+ StringBuilder destBuilder = new StringBuilder();
+ if (jingleConnection.getFtVersion() == Content.Version.FT_3) {
+ Log.d(Config.LOGTAG, this.connection.getAccount().getJid().toBareJid() + ": using session Id instead of transport Id for proxy destination");
+ destBuilder.append(jingleConnection.getSessionId());
+ } else {
+ destBuilder.append(jingleConnection.getTransportId());
+ }
+ if (candidate.isOurs()) {
+ destBuilder.append(jingleConnection.getAccount().getJid());
+ destBuilder.append(jingleConnection.getCounterPart());
+ } else {
+ destBuilder.append(jingleConnection.getCounterPart());
+ destBuilder.append(jingleConnection.getAccount().getJid());
+ }
+ mDigest.reset();
+ this.destination = CryptoHelper.bytesToHex(mDigest
+ .digest(destBuilder.toString().getBytes()));
+ } catch (NoSuchAlgorithmException e) {
+
+ }
+ }
+
+ public void connect(final OnTransportConnected callback) {
+ new Thread(new Runnable() {
+
+ @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);
+ }
+ inputStream = socket.getInputStream();
+ outputStream = socket.getOutputStream();
+ SocksSocketFactory.createSocksConnection(socket, destination, 0);
+ isEstablished = true;
+ callback.established();
+ } catch (IOException e) {
+ callback.failed();
+ }
+ }
+ }).start();
+
+ }
+
+ public void send(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) {
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ InputStream fileInputStream = null;
+ final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_" + connection.getSessionId());
+ try {
+ wakeLock.acquire();
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ digest.reset();
+ fileInputStream = connection.getFileInputStream();
+ if (fileInputStream == null) {
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create input stream");
+ callback.onFileTransferAborted();
+ return;
+ }
+ long size = file.getExpectedSize();
+ long transmitted = 0;
+ int count;
+ byte[] buffer = new byte[8192];
+ while ((count = fileInputStream.read(buffer)) > 0) {
+ outputStream.write(buffer, 0, count);
+ digest.update(buffer, 0, count);
+ transmitted += count;
+ connection.updateProgress((int) ((((double) transmitted) / size) * 100));
+ }
+ outputStream.flush();
+ file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
+ if (callback != null) {
+ callback.onFileTransmitted(file);
+ }
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": " + e.getMessage());
+ callback.onFileTransferAborted();
+ } finally {
+ FileBackend.close(fileInputStream);
+ wakeLock.release();
+ }
+ }
+ }).start();
+
+ }
+
+ public void receive(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) {
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ OutputStream fileOutputStream = null;
+ final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_receive_" + connection.getSessionId());
+ try {
+ wakeLock.acquire();
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ digest.reset();
+ //inputStream.skip(45);
+ socket.setSoTimeout(30000);
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ fileOutputStream = connection.getFileOutputStream();
+ if (fileOutputStream == null) {
+ callback.onFileTransferAborted();
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create output stream");
+ return;
+ }
+ double size = file.getExpectedSize();
+ long remainingSize = file.getExpectedSize();
+ byte[] buffer = new byte[8192];
+ int count;
+ while (remainingSize > 0) {
+ count = inputStream.read(buffer);
+ if (count == -1) {
+ callback.onFileTransferAborted();
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": file ended prematurely with " + remainingSize + " bytes remaining");
+ return;
+ } else {
+ fileOutputStream.write(buffer, 0, count);
+ digest.update(buffer, 0, count);
+ remainingSize -= count;
+ }
+ connection.updateProgress((int) (((size - remainingSize) / size) * 100));
+ }
+ fileOutputStream.flush();
+ fileOutputStream.close();
+ file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
+ callback.onFileTransmitted(file);
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": " + e.getMessage());
+ callback.onFileTransferAborted();
+ } finally {
+ wakeLock.release();
+ FileBackend.close(fileOutputStream);
+ FileBackend.close(inputStream);
+ }
+ }
+ }).start();
+ }
+
+ public boolean isProxy() {
+ return this.candidate.getType() == JingleCandidate.TYPE_PROXY;
+ }
+
+ public boolean needsActivation() {
+ return (this.isProxy() && !this.activated);
+ }
+
+ public void disconnect() {
+ FileBackend.close(inputStream);
+ FileBackend.close(outputStream);
+ FileBackend.close(socket);
+ }
+
+ public boolean isEstablished() {
+ return this.isEstablished;
+ }
+
+ public JingleCandidate getCandidate() {
+ return this.candidate;
+ }
+
+ public void setActivated(boolean activated) {
+ this.activated = activated;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleTransport.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleTransport.java
index 1ee97b415..b9adb4632 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleTransport.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleTransport.java
@@ -3,13 +3,13 @@ package de.pixart.messenger.xmpp.jingle;
import de.pixart.messenger.entities.DownloadableFile;
public abstract class JingleTransport {
- public abstract void connect(final OnTransportConnected callback);
+ public abstract void connect(final OnTransportConnected callback);
- public abstract void receive(final DownloadableFile file,
- final OnFileTransmissionStatusChanged callback);
+ public abstract void receive(final DownloadableFile file,
+ final OnFileTransmissionStatusChanged callback);
- public abstract void send(final DownloadableFile file,
- final OnFileTransmissionStatusChanged callback);
+ public abstract void send(final DownloadableFile file,
+ final OnFileTransmissionStatusChanged callback);
- public abstract void disconnect();
+ public abstract void disconnect();
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/OnFileTransmissionStatusChanged.java b/src/main/java/de/pixart/messenger/xmpp/jingle/OnFileTransmissionStatusChanged.java
index 6bbf7111a..65f9726d8 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/OnFileTransmissionStatusChanged.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/OnFileTransmissionStatusChanged.java
@@ -3,7 +3,7 @@ package de.pixart.messenger.xmpp.jingle;
import de.pixart.messenger.entities.DownloadableFile;
public interface OnFileTransmissionStatusChanged {
- void onFileTransmitted(DownloadableFile file);
+ void onFileTransmitted(DownloadableFile file);
- void onFileTransferAborted();
+ void onFileTransferAborted();
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/OnJinglePacketReceived.java b/src/main/java/de/pixart/messenger/xmpp/jingle/OnJinglePacketReceived.java
index 82dfefa5e..4cacac74c 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/OnJinglePacketReceived.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/OnJinglePacketReceived.java
@@ -5,5 +5,5 @@ import de.pixart.messenger.xmpp.PacketReceived;
import de.pixart.messenger.xmpp.jingle.stanzas.JinglePacket;
public interface OnJinglePacketReceived extends PacketReceived {
- void onJinglePacketReceived(Account account, JinglePacket packet);
+ void onJinglePacketReceived(Account account, JinglePacket packet);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/OnPrimaryCandidateFound.java b/src/main/java/de/pixart/messenger/xmpp/jingle/OnPrimaryCandidateFound.java
index fad7bb4d4..b1fb0b02a 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/OnPrimaryCandidateFound.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/OnPrimaryCandidateFound.java
@@ -1,5 +1,5 @@
package de.pixart.messenger.xmpp.jingle;
public interface OnPrimaryCandidateFound {
- void onPrimaryCandidateFound(boolean success, JingleCandidate canditate);
+ void onPrimaryCandidateFound(boolean success, JingleCandidate canditate);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/OnTransportConnected.java b/src/main/java/de/pixart/messenger/xmpp/jingle/OnTransportConnected.java
index 10efd76d7..11a3eed5e 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/OnTransportConnected.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/OnTransportConnected.java
@@ -1,7 +1,7 @@
package de.pixart.messenger.xmpp.jingle;
public interface OnTransportConnected {
- public void failed();
+ public void failed();
- public void established();
+ public void established();
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Content.java b/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Content.java
index bef32926f..fc7231e30 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Content.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Content.java
@@ -5,125 +5,125 @@ import de.pixart.messenger.xml.Element;
public class Content extends Element {
- public enum Version {
- FT_3("urn:xmpp:jingle:apps:file-transfer:3"),
- FT_4("urn:xmpp:jingle:apps:file-transfer:4");
-
- private final String namespace;
-
- Version(String namespace) {
- this.namespace = namespace;
- }
-
- public String getNamespace() {
- return namespace;
- }
- }
-
- private String transportId;
-
- public Content() {
- super("content");
- }
-
- public Content(String creator, String name) {
- super("content");
- this.setAttribute("creator", creator);
- this.setAttribute("name", name);
- }
-
- public Version getVersion() {
- if (hasChild("description", Version.FT_3.namespace)) {
- return Version.FT_3;
- } else if (hasChild("description" , Version.FT_4.namespace)) {
- return Version.FT_4;
- }
- return null;
- }
-
- public void setTransportId(String sid) {
- this.transportId = sid;
- }
-
- public Element setFileOffer(DownloadableFile actualFile, boolean otr, Version version) {
- Element description = this.addChild("description", version.namespace);
- Element file;
- if (version == Version.FT_3) {
- Element offer = description.addChild("offer");
- file = offer.addChild("file");
- } else {
- file = description.addChild("file");
- }
- file.addChild("size").setContent(Long.toString(actualFile.getExpectedSize()));
- if (otr) {
- file.addChild("name").setContent(actualFile.getName() + ".otr");
- } else {
- file.addChild("name").setContent(actualFile.getName());
- }
- return file;
- }
-
- public Element getFileOffer(Version version) {
- Element description = this.findChild("description", version.namespace);
- if (description == null) {
- return null;
- }
- if (version == Version.FT_3) {
- Element offer = description.findChild("offer");
- if (offer == null) {
- return null;
- }
- return offer.findChild("file");
- } else {
- return description.findChild("file");
- }
- }
-
- public void setFileOffer(Element fileOffer, Version version) {
- Element description = this.addChild("description", version.namespace);
- if (version == Version.FT_3) {
- description.addChild("offer").addChild(fileOffer);
- } else {
- description.addChild(fileOffer);
- }
- }
-
- public String getTransportId() {
- if (hasSocks5Transport()) {
- this.transportId = socks5transport().getAttribute("sid");
- } else if (hasIbbTransport()) {
- this.transportId = ibbTransport().getAttribute("sid");
- }
- return this.transportId;
- }
-
- public Element socks5transport() {
- Element transport = this.findChild("transport",
- "urn:xmpp:jingle:transports:s5b:1");
- if (transport == null) {
- transport = this.addChild("transport",
- "urn:xmpp:jingle:transports:s5b:1");
- transport.setAttribute("sid", this.transportId);
- }
- return transport;
- }
-
- public Element ibbTransport() {
- Element transport = this.findChild("transport",
- "urn:xmpp:jingle:transports:ibb:1");
- if (transport == null) {
- transport = this.addChild("transport",
- "urn:xmpp:jingle:transports:ibb:1");
- transport.setAttribute("sid", this.transportId);
- }
- return transport;
- }
-
- public boolean hasSocks5Transport() {
- return this.hasChild("transport", "urn:xmpp:jingle:transports:s5b:1");
- }
-
- public boolean hasIbbTransport() {
- return this.hasChild("transport", "urn:xmpp:jingle:transports:ibb:1");
- }
+ public enum Version {
+ FT_3("urn:xmpp:jingle:apps:file-transfer:3"),
+ FT_4("urn:xmpp:jingle:apps:file-transfer:4");
+
+ private final String namespace;
+
+ Version(String namespace) {
+ this.namespace = namespace;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+ }
+
+ private String transportId;
+
+ public Content() {
+ super("content");
+ }
+
+ public Content(String creator, String name) {
+ super("content");
+ this.setAttribute("creator", creator);
+ this.setAttribute("name", name);
+ }
+
+ public Version getVersion() {
+ if (hasChild("description", Version.FT_3.namespace)) {
+ return Version.FT_3;
+ } else if (hasChild("description", Version.FT_4.namespace)) {
+ return Version.FT_4;
+ }
+ return null;
+ }
+
+ public void setTransportId(String sid) {
+ this.transportId = sid;
+ }
+
+ public Element setFileOffer(DownloadableFile actualFile, boolean otr, Version version) {
+ Element description = this.addChild("description", version.namespace);
+ Element file;
+ if (version == Version.FT_3) {
+ Element offer = description.addChild("offer");
+ file = offer.addChild("file");
+ } else {
+ file = description.addChild("file");
+ }
+ file.addChild("size").setContent(Long.toString(actualFile.getExpectedSize()));
+ if (otr) {
+ file.addChild("name").setContent(actualFile.getName() + ".otr");
+ } else {
+ file.addChild("name").setContent(actualFile.getName());
+ }
+ return file;
+ }
+
+ public Element getFileOffer(Version version) {
+ Element description = this.findChild("description", version.namespace);
+ if (description == null) {
+ return null;
+ }
+ if (version == Version.FT_3) {
+ Element offer = description.findChild("offer");
+ if (offer == null) {
+ return null;
+ }
+ return offer.findChild("file");
+ } else {
+ return description.findChild("file");
+ }
+ }
+
+ public void setFileOffer(Element fileOffer, Version version) {
+ Element description = this.addChild("description", version.namespace);
+ if (version == Version.FT_3) {
+ description.addChild("offer").addChild(fileOffer);
+ } else {
+ description.addChild(fileOffer);
+ }
+ }
+
+ public String getTransportId() {
+ if (hasSocks5Transport()) {
+ this.transportId = socks5transport().getAttribute("sid");
+ } else if (hasIbbTransport()) {
+ this.transportId = ibbTransport().getAttribute("sid");
+ }
+ return this.transportId;
+ }
+
+ public Element socks5transport() {
+ Element transport = this.findChild("transport",
+ "urn:xmpp:jingle:transports:s5b:1");
+ if (transport == null) {
+ transport = this.addChild("transport",
+ "urn:xmpp:jingle:transports:s5b:1");
+ transport.setAttribute("sid", this.transportId);
+ }
+ return transport;
+ }
+
+ public Element ibbTransport() {
+ Element transport = this.findChild("transport",
+ "urn:xmpp:jingle:transports:ibb:1");
+ if (transport == null) {
+ transport = this.addChild("transport",
+ "urn:xmpp:jingle:transports:ibb:1");
+ transport.setAttribute("sid", this.transportId);
+ }
+ return transport;
+ }
+
+ public boolean hasSocks5Transport() {
+ return this.hasChild("transport", "urn:xmpp:jingle:transports:s5b:1");
+ }
+
+ public boolean hasIbbTransport() {
+ return this.hasChild("transport", "urn:xmpp:jingle:transports:ibb:1");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/JinglePacket.java b/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/JinglePacket.java
index 971ab8e1b..de856772a 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/JinglePacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/JinglePacket.java
@@ -5,92 +5,92 @@ import de.pixart.messenger.xmpp.jid.Jid;
import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class JinglePacket extends IqPacket {
- Content content = null;
- Reason reason = null;
- Element jingle = new Element("jingle");
+ Content content = null;
+ Reason reason = null;
+ Element jingle = new Element("jingle");
- @Override
- public Element addChild(Element child) {
- if ("jingle".equals(child.getName())) {
- Element contentElement = child.findChild("content");
- if (contentElement != null) {
- this.content = new Content();
- this.content.setChildren(contentElement.getChildren());
- this.content.setAttributes(contentElement.getAttributes());
- }
- Element reasonElement = child.findChild("reason");
- if (reasonElement != null) {
- this.reason = new Reason();
- this.reason.setChildren(reasonElement.getChildren());
- this.reason.setAttributes(reasonElement.getAttributes());
- }
- this.jingle.setAttributes(child.getAttributes());
- }
- return child;
- }
+ @Override
+ public Element addChild(Element child) {
+ if ("jingle".equals(child.getName())) {
+ Element contentElement = child.findChild("content");
+ if (contentElement != null) {
+ this.content = new Content();
+ this.content.setChildren(contentElement.getChildren());
+ this.content.setAttributes(contentElement.getAttributes());
+ }
+ Element reasonElement = child.findChild("reason");
+ if (reasonElement != null) {
+ this.reason = new Reason();
+ this.reason.setChildren(reasonElement.getChildren());
+ this.reason.setAttributes(reasonElement.getAttributes());
+ }
+ this.jingle.setAttributes(child.getAttributes());
+ }
+ return child;
+ }
- public JinglePacket setContent(Content content) {
- this.content = content;
- return this;
- }
+ public JinglePacket setContent(Content content) {
+ this.content = content;
+ return this;
+ }
- public Content getJingleContent() {
- if (this.content == null) {
- this.content = new Content();
- }
- return this.content;
- }
+ public Content getJingleContent() {
+ if (this.content == null) {
+ this.content = new Content();
+ }
+ return this.content;
+ }
- public JinglePacket setReason(Reason reason) {
- this.reason = reason;
- return this;
- }
+ public JinglePacket setReason(Reason reason) {
+ this.reason = reason;
+ return this;
+ }
- public Reason getReason() {
- return this.reason;
- }
+ public Reason getReason() {
+ return this.reason;
+ }
- private void build() {
- this.children.clear();
- this.jingle.clearChildren();
- this.jingle.setAttribute("xmlns", "urn:xmpp:jingle:1");
- if (this.content != null) {
- jingle.addChild(this.content);
- }
- if (this.reason != null) {
- jingle.addChild(this.reason);
- }
- this.children.add(jingle);
- this.setAttribute("type", "set");
- }
+ private void build() {
+ this.children.clear();
+ this.jingle.clearChildren();
+ this.jingle.setAttribute("xmlns", "urn:xmpp:jingle:1");
+ if (this.content != null) {
+ jingle.addChild(this.content);
+ }
+ if (this.reason != null) {
+ jingle.addChild(this.reason);
+ }
+ this.children.add(jingle);
+ this.setAttribute("type", "set");
+ }
- public String getSessionId() {
- return this.jingle.getAttribute("sid");
- }
+ public String getSessionId() {
+ return this.jingle.getAttribute("sid");
+ }
- public void setSessionId(String sid) {
- this.jingle.setAttribute("sid", sid);
- }
+ public void setSessionId(String sid) {
+ this.jingle.setAttribute("sid", sid);
+ }
- @Override
- public String toString() {
- this.build();
- return super.toString();
- }
+ @Override
+ public String toString() {
+ this.build();
+ return super.toString();
+ }
- public void setAction(String action) {
- this.jingle.setAttribute("action", action);
- }
+ public void setAction(String action) {
+ this.jingle.setAttribute("action", action);
+ }
- public String getAction() {
- return this.jingle.getAttribute("action");
- }
+ public String getAction() {
+ return this.jingle.getAttribute("action");
+ }
- public void setInitiator(final Jid initiator) {
- this.jingle.setAttribute("initiator", initiator.toString());
- }
+ public void setInitiator(final Jid initiator) {
+ this.jingle.setAttribute("initiator", initiator.toString());
+ }
- public boolean isAction(String action) {
- return action.equalsIgnoreCase(this.getAction());
- }
+ public boolean isAction(String action) {
+ return action.equalsIgnoreCase(this.getAction());
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Reason.java b/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Reason.java
index 3c77a91a6..19f27bc40 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Reason.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Reason.java
@@ -3,11 +3,11 @@ package de.pixart.messenger.xmpp.jingle.stanzas;
import de.pixart.messenger.xml.Element;
public class Reason extends Element {
- private Reason(String name) {
- super(name);
- }
+ private Reason(String name) {
+ super(name);
+ }
- public Reason() {
- super("reason");
- }
+ public Reason() {
+ super("reason");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/pep/Avatar.java b/src/main/java/de/pixart/messenger/xmpp/pep/Avatar.java
index 1f28cd92d..655eabfc8 100644
--- a/src/main/java/de/pixart/messenger/xmpp/pep/Avatar.java
+++ b/src/main/java/de/pixart/messenger/xmpp/pep/Avatar.java
@@ -7,96 +7,98 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class Avatar {
- public enum Origin { PEP, VCARD };
+ public enum Origin {PEP, VCARD}
- public String type;
- public String sha1sum;
- public String image;
- public int height;
- public int width;
- public long size;
- public Jid owner;
- public Origin origin = Origin.PEP; //default to maintain compat
+ ;
- public byte[] getImageAsBytes() {
- return Base64.decode(image, Base64.DEFAULT);
- }
+ public String type;
+ public String sha1sum;
+ public String image;
+ public int height;
+ public int width;
+ public long size;
+ public Jid owner;
+ public Origin origin = Origin.PEP; //default to maintain compat
- public String getFilename() {
- return sha1sum;
- }
+ public byte[] getImageAsBytes() {
+ return Base64.decode(image, Base64.DEFAULT);
+ }
- public static Avatar parseMetadata(Element items) {
- Element item = items.findChild("item");
- if (item == null) {
- return null;
- }
- Element metadata = item.findChild("metadata");
- if (metadata == null) {
- return null;
- }
- String primaryId = item.getAttribute("id");
- if (primaryId == null) {
- return null;
- }
- for (Element child : metadata.getChildren()) {
- if (child.getName().equals("info")
- && primaryId.equals(child.getAttribute("id"))) {
- Avatar avatar = new Avatar();
- String height = child.getAttribute("height");
- String width = child.getAttribute("width");
- String size = child.getAttribute("bytes");
- try {
- if (height != null) {
- avatar.height = Integer.parseInt(height);
- }
- if (width != null) {
- avatar.width = Integer.parseInt(width);
- }
- if (size != null) {
- avatar.size = Long.parseLong(size);
- }
- } catch (NumberFormatException e) {
- return null;
- }
- avatar.type = child.getAttribute("type");
- String hash = child.getAttribute("id");
- if (!isValidSHA1(hash)) {
- return null;
- }
- avatar.sha1sum = hash;
- avatar.origin = Origin.PEP;
- return avatar;
- }
- }
- return null;
- }
+ public String getFilename() {
+ return sha1sum;
+ }
- @Override
- public boolean equals(Object object) {
- if (object != null && object instanceof Avatar) {
- Avatar other = (Avatar) object;
- return other.getFilename().equals(this.getFilename());
- } else {
- return false;
- }
- }
+ public static Avatar parseMetadata(Element items) {
+ Element item = items.findChild("item");
+ if (item == null) {
+ return null;
+ }
+ Element metadata = item.findChild("metadata");
+ if (metadata == null) {
+ return null;
+ }
+ String primaryId = item.getAttribute("id");
+ if (primaryId == null) {
+ return null;
+ }
+ for (Element child : metadata.getChildren()) {
+ if (child.getName().equals("info")
+ && primaryId.equals(child.getAttribute("id"))) {
+ Avatar avatar = new Avatar();
+ String height = child.getAttribute("height");
+ String width = child.getAttribute("width");
+ String size = child.getAttribute("bytes");
+ try {
+ if (height != null) {
+ avatar.height = Integer.parseInt(height);
+ }
+ if (width != null) {
+ avatar.width = Integer.parseInt(width);
+ }
+ if (size != null) {
+ avatar.size = Long.parseLong(size);
+ }
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ avatar.type = child.getAttribute("type");
+ String hash = child.getAttribute("id");
+ if (!isValidSHA1(hash)) {
+ return null;
+ }
+ avatar.sha1sum = hash;
+ avatar.origin = Origin.PEP;
+ return avatar;
+ }
+ }
+ return null;
+ }
- public static Avatar parsePresence(Element x) {
- String hash = x == null ? null : x.findChildContent("photo");
- if (hash == null) {
- return null;
- }
- if (!isValidSHA1(hash)) {
- return null;
- }
- Avatar avatar = new Avatar();
- avatar.sha1sum = hash;
- avatar.origin = Origin.VCARD;
- return avatar;
- }
+ @Override
+ public boolean equals(Object object) {
+ if (object != null && object instanceof Avatar) {
+ Avatar other = (Avatar) object;
+ return other.getFilename().equals(this.getFilename());
+ } else {
+ return false;
+ }
+ }
- private static boolean isValidSHA1(String s) {
- return s != null && s.matches("[a-fA-F0-9]{40}");
- }
+ public static Avatar parsePresence(Element x) {
+ String hash = x == null ? null : x.findChildContent("photo");
+ if (hash == null) {
+ return null;
+ }
+ if (!isValidSHA1(hash)) {
+ return null;
+ }
+ Avatar avatar = new Avatar();
+ avatar.sha1sum = hash;
+ avatar.origin = Origin.VCARD;
+ return avatar;
+ }
+
+ private static boolean isValidSHA1(String s) {
+ return s != null && s.matches("[a-fA-F0-9]{40}");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractAcknowledgeableStanza.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractAcknowledgeableStanza.java
index 615c4dfbe..22d035e6e 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractAcknowledgeableStanza.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractAcknowledgeableStanza.java
@@ -4,28 +4,28 @@ import de.pixart.messenger.xml.Element;
abstract public class AbstractAcknowledgeableStanza extends AbstractStanza {
- protected AbstractAcknowledgeableStanza(String name) {
- super(name);
- }
+ protected AbstractAcknowledgeableStanza(String name) {
+ super(name);
+ }
- public String getId() {
- return this.getAttribute("id");
- }
+ public String getId() {
+ return this.getAttribute("id");
+ }
- public void setId(final String id) {
- setAttribute("id", id);
- }
+ public void setId(final String id) {
+ setAttribute("id", id);
+ }
- public Element getError() {
- Element error = findChild("error");
- if (error != null) {
- for(Element element : error.getChildren()) {
- if (!element.getName().equals("text")) {
- return element;
- }
- }
- }
- return null;
- }
+ public Element getError() {
+ Element error = findChild("error");
+ if (error != null) {
+ for (Element element : error.getChildren()) {
+ if (!element.getName().equals("text")) {
+ return element;
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractStanza.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractStanza.java
index a21d8b56d..8d1b24ef8 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractStanza.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractStanza.java
@@ -6,45 +6,45 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class AbstractStanza extends Element {
- protected AbstractStanza(final String name) {
- super(name);
- }
-
- public Jid getTo() {
- return getAttributeAsJid("to");
- }
-
- public Jid getFrom() {
- return getAttributeAsJid("from");
- }
-
- public void setTo(final Jid to) {
- if (to != null) {
- setAttribute("to", to.toString());
- }
- }
-
- public void setFrom(final Jid from) {
- if (from != null) {
- setAttribute("from", from.toString());
- }
- }
-
- public boolean fromServer(final Account account) {
- return getFrom() == null
- || getFrom().equals(account.getServer())
- || getFrom().equals(account.getJid().toBareJid())
- || getFrom().equals(account.getJid());
- }
-
- public boolean toServer(final Account account) {
- return getTo() == null
- || getTo().equals(account.getServer())
- || getTo().equals(account.getJid().toBareJid())
- || getTo().equals(account.getJid());
- }
-
- public boolean fromAccount(final Account account) {
- return getFrom() != null && getFrom().toBareJid().equals(account.getJid().toBareJid());
- }
+ protected AbstractStanza(final String name) {
+ super(name);
+ }
+
+ public Jid getTo() {
+ return getAttributeAsJid("to");
+ }
+
+ public Jid getFrom() {
+ return getAttributeAsJid("from");
+ }
+
+ public void setTo(final Jid to) {
+ if (to != null) {
+ setAttribute("to", to.toString());
+ }
+ }
+
+ public void setFrom(final Jid from) {
+ if (from != null) {
+ setAttribute("from", from.toString());
+ }
+ }
+
+ public boolean fromServer(final Account account) {
+ return getFrom() == null
+ || getFrom().equals(account.getServer())
+ || getFrom().equals(account.getJid().toBareJid())
+ || getFrom().equals(account.getJid());
+ }
+
+ public boolean toServer(final Account account) {
+ return getTo() == null
+ || getTo().equals(account.getServer())
+ || getTo().equals(account.getJid().toBareJid())
+ || getTo().equals(account.getJid());
+ }
+
+ public boolean fromAccount(final Account account) {
+ return getFrom() != null && getFrom().toBareJid().equals(account.getJid().toBareJid());
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/IqPacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/IqPacket.java
index b0a22c314..2efc948f4 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/IqPacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/IqPacket.java
@@ -4,66 +4,66 @@ import de.pixart.messenger.xml.Element;
public class IqPacket extends AbstractAcknowledgeableStanza {
- public enum TYPE {
- ERROR,
- SET,
- RESULT,
- GET,
- INVALID,
- TIMEOUT
- }
+ public enum TYPE {
+ ERROR,
+ SET,
+ RESULT,
+ GET,
+ INVALID,
+ TIMEOUT
+ }
- public IqPacket(final TYPE type) {
- super("iq");
- if (type != TYPE.INVALID) {
- this.setAttribute("type", type.toString().toLowerCase());
- }
- }
+ public IqPacket(final TYPE type) {
+ super("iq");
+ if (type != TYPE.INVALID) {
+ this.setAttribute("type", type.toString().toLowerCase());
+ }
+ }
- public IqPacket() {
- super("iq");
- }
+ public IqPacket() {
+ super("iq");
+ }
- public Element query() {
- Element query = findChild("query");
- if (query == null) {
- query = addChild("query");
- }
- return query;
- }
+ public Element query() {
+ Element query = findChild("query");
+ if (query == null) {
+ query = addChild("query");
+ }
+ return query;
+ }
- public Element query(final String xmlns) {
- final Element query = query();
- query.setAttribute("xmlns", xmlns);
- return query();
- }
+ public Element query(final String xmlns) {
+ final Element query = query();
+ query.setAttribute("xmlns", xmlns);
+ return query();
+ }
- public TYPE getType() {
- final String type = getAttribute("type");
- if (type == null) {
- return TYPE.INVALID;
- }
- switch (type) {
- case "error":
- return TYPE.ERROR;
- case "result":
- return TYPE.RESULT;
- case "set":
- return TYPE.SET;
- case "get":
- return TYPE.GET;
- case "timeout":
- return TYPE.TIMEOUT;
- default:
- return TYPE.INVALID;
- }
- }
+ public TYPE getType() {
+ final String type = getAttribute("type");
+ if (type == null) {
+ return TYPE.INVALID;
+ }
+ switch (type) {
+ case "error":
+ return TYPE.ERROR;
+ case "result":
+ return TYPE.RESULT;
+ case "set":
+ return TYPE.SET;
+ case "get":
+ return TYPE.GET;
+ case "timeout":
+ return TYPE.TIMEOUT;
+ default:
+ return TYPE.INVALID;
+ }
+ }
- public IqPacket generateResponse(final TYPE type) {
- final IqPacket packet = new IqPacket(type);
- packet.setTo(this.getFrom());
- packet.setId(this.getId());
- return packet;
- }
+ public IqPacket generateResponse(final TYPE type) {
+ final IqPacket packet = new IqPacket(type);
+ packet.setTo(this.getFrom());
+ packet.setId(this.getId());
+ return packet;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/MessagePacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/MessagePacket.java
index 5af2d701b..2f7d7e071 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/MessagePacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/MessagePacket.java
@@ -6,94 +6,94 @@ import de.pixart.messenger.parser.AbstractParser;
import de.pixart.messenger.xml.Element;
public class MessagePacket extends AbstractAcknowledgeableStanza {
- public static final int TYPE_CHAT = 0;
- public static final int TYPE_NORMAL = 2;
- public static final int TYPE_GROUPCHAT = 3;
- public static final int TYPE_ERROR = 4;
- public static final int TYPE_HEADLINE = 5;
+ public static final int TYPE_CHAT = 0;
+ public static final int TYPE_NORMAL = 2;
+ public static final int TYPE_GROUPCHAT = 3;
+ public static final int TYPE_ERROR = 4;
+ public static final int TYPE_HEADLINE = 5;
- public MessagePacket() {
- super("message");
- }
+ public MessagePacket() {
+ super("message");
+ }
- public String getBody() {
- return findChildContent("body");
- }
+ public String getBody() {
+ return findChildContent("body");
+ }
- public void setBody(String text) {
- this.children.remove(findChild("body"));
- Element body = new Element("body");
- body.setContent(text);
- this.children.add(0, body);
- }
+ public void setBody(String text) {
+ this.children.remove(findChild("body"));
+ Element body = new Element("body");
+ body.setContent(text);
+ this.children.add(0, body);
+ }
- public void setAxolotlMessage(Element axolotlMessage) {
- this.children.remove(findChild("body"));
- this.children.add(0, axolotlMessage);
- }
+ public void setAxolotlMessage(Element axolotlMessage) {
+ this.children.remove(findChild("body"));
+ this.children.add(0, axolotlMessage);
+ }
- public void setType(int type) {
- switch (type) {
- case TYPE_CHAT:
- this.setAttribute("type", "chat");
- break;
- case TYPE_GROUPCHAT:
- this.setAttribute("type", "groupchat");
- break;
- case TYPE_NORMAL:
- break;
- case TYPE_ERROR:
- this.setAttribute("type","error");
- break;
- default:
- this.setAttribute("type", "chat");
- break;
- }
- }
+ public void setType(int type) {
+ switch (type) {
+ case TYPE_CHAT:
+ this.setAttribute("type", "chat");
+ break;
+ case TYPE_GROUPCHAT:
+ this.setAttribute("type", "groupchat");
+ break;
+ case TYPE_NORMAL:
+ break;
+ case TYPE_ERROR:
+ this.setAttribute("type", "error");
+ break;
+ default:
+ this.setAttribute("type", "chat");
+ break;
+ }
+ }
- public int getType() {
- String type = getAttribute("type");
- if (type == null) {
- return TYPE_NORMAL;
- } else if (type.equals("normal")) {
- return TYPE_NORMAL;
- } else if (type.equals("chat")) {
- return TYPE_CHAT;
- } else if (type.equals("groupchat")) {
- return TYPE_GROUPCHAT;
- } else if (type.equals("error")) {
- return TYPE_ERROR;
- } else if (type.equals("headline")) {
- return TYPE_HEADLINE;
- } else {
- return TYPE_NORMAL;
- }
- }
+ public int getType() {
+ String type = getAttribute("type");
+ if (type == null) {
+ return TYPE_NORMAL;
+ } else if (type.equals("normal")) {
+ return TYPE_NORMAL;
+ } else if (type.equals("chat")) {
+ return TYPE_CHAT;
+ } else if (type.equals("groupchat")) {
+ return TYPE_GROUPCHAT;
+ } else if (type.equals("error")) {
+ return TYPE_ERROR;
+ } else if (type.equals("headline")) {
+ return TYPE_HEADLINE;
+ } else {
+ return TYPE_NORMAL;
+ }
+ }
- public Pair<MessagePacket,Long> getForwardedMessagePacket(String name, String namespace) {
- Element wrapper = findChild(name, namespace);
- if (wrapper == null) {
- return null;
- }
- Element forwarded = wrapper.findChild("forwarded", "urn:xmpp:forward:0");
- if (forwarded == null) {
- return null;
- }
- MessagePacket packet = create(forwarded.findChild("message"));
- if (packet == null) {
- return null;
- }
- Long timestamp = AbstractParser.parseTimestamp(forwarded, null);
- return new Pair(packet,timestamp);
- }
+ public Pair<MessagePacket, Long> getForwardedMessagePacket(String name, String namespace) {
+ Element wrapper = findChild(name, namespace);
+ if (wrapper == null) {
+ return null;
+ }
+ Element forwarded = wrapper.findChild("forwarded", "urn:xmpp:forward:0");
+ if (forwarded == null) {
+ return null;
+ }
+ MessagePacket packet = create(forwarded.findChild("message"));
+ if (packet == null) {
+ return null;
+ }
+ Long timestamp = AbstractParser.parseTimestamp(forwarded, null);
+ return new Pair(packet, timestamp);
+ }
- public static MessagePacket create(Element element) {
- if (element == null) {
- return null;
- }
- MessagePacket packet = new MessagePacket();
- packet.setAttributes(element.getAttributes());
- packet.setChildren(element.getChildren());
- return packet;
- }
+ public static MessagePacket create(Element element) {
+ if (element == null) {
+ return null;
+ }
+ MessagePacket packet = new MessagePacket();
+ packet.setAttributes(element.getAttributes());
+ packet.setChildren(element.getChildren());
+ return packet;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/PresencePacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/PresencePacket.java
index 6c816cac3..6c57de807 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/PresencePacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/PresencePacket.java
@@ -2,7 +2,7 @@ package de.pixart.messenger.xmpp.stanzas;
public class PresencePacket extends AbstractAcknowledgeableStanza {
- public PresencePacket() {
- super("presence");
- }
+ public PresencePacket() {
+ super("presence");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/csi/ActivePacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/csi/ActivePacket.java
index 10abb3499..5d1cb61c3 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/csi/ActivePacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/csi/ActivePacket.java
@@ -3,8 +3,8 @@ package de.pixart.messenger.xmpp.stanzas.csi;
import de.pixart.messenger.xmpp.stanzas.AbstractStanza;
public class ActivePacket extends AbstractStanza {
- public ActivePacket() {
- super("active");
- setAttribute("xmlns", "urn:xmpp:csi:0");
- }
+ public ActivePacket() {
+ super("active");
+ setAttribute("xmlns", "urn:xmpp:csi:0");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/csi/InactivePacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/csi/InactivePacket.java
index ebaad8205..9e840190d 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/csi/InactivePacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/csi/InactivePacket.java
@@ -3,8 +3,8 @@ package de.pixart.messenger.xmpp.stanzas.csi;
import de.pixart.messenger.xmpp.stanzas.AbstractStanza;
public class InactivePacket extends AbstractStanza {
- public InactivePacket() {
- super("inactive");
- setAttribute("xmlns", "urn:xmpp:csi:0");
- }
+ public InactivePacket() {
+ super("inactive");
+ setAttribute("xmlns", "urn:xmpp:csi:0");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/AckPacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/AckPacket.java
index 5c8131878..f98707fbe 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/AckPacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/AckPacket.java
@@ -4,10 +4,10 @@ import de.pixart.messenger.xmpp.stanzas.AbstractStanza;
public class AckPacket extends AbstractStanza {
- public AckPacket(int sequence, int smVersion) {
- super("a");
- this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
- this.setAttribute("h", Integer.toString(sequence));
- }
+ public AckPacket(int sequence, int smVersion) {
+ super("a");
+ this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
+ this.setAttribute("h", Integer.toString(sequence));
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/EnablePacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/EnablePacket.java
index 2beca0f20..d4c8ab242 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/EnablePacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/EnablePacket.java
@@ -4,10 +4,10 @@ import de.pixart.messenger.xmpp.stanzas.AbstractStanza;
public class EnablePacket extends AbstractStanza {
- public EnablePacket(int smVersion) {
- super("enable");
- this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
- this.setAttribute("resume", "true");
- }
+ public EnablePacket(int smVersion) {
+ super("enable");
+ this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
+ this.setAttribute("resume", "true");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/RequestPacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/RequestPacket.java
index 19880f3c6..cbd82eed6 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/RequestPacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/RequestPacket.java
@@ -4,9 +4,9 @@ import de.pixart.messenger.xmpp.stanzas.AbstractStanza;
public class RequestPacket extends AbstractStanza {
- public RequestPacket(int smVersion) {
- super("r");
- this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
- }
+ public RequestPacket(int smVersion) {
+ super("r");
+ this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/ResumePacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/ResumePacket.java
index 1c7f8d08c..e47fc1b95 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/ResumePacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/ResumePacket.java
@@ -4,11 +4,11 @@ import de.pixart.messenger.xmpp.stanzas.AbstractStanza;
public class ResumePacket extends AbstractStanza {
- public ResumePacket(String id, int sequence, int smVersion) {
- super("resume");
- this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
- this.setAttribute("previd", id);
- this.setAttribute("h", Integer.toString(sequence));
- }
+ public ResumePacket(String id, int sequence, int smVersion) {
+ super("resume");
+ this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
+ this.setAttribute("previd", id);
+ this.setAttribute("h", Integer.toString(sequence));
+ }
}
diff --git a/src/main/res/drawable/actionbar_tab_indicator.xml b/src/main/res/drawable/actionbar_tab_indicator.xml
index 5598ee424..ffd36f6fe 100644
--- a/src/main/res/drawable/actionbar_tab_indicator.xml
+++ b/src/main/res/drawable/actionbar_tab_indicator.xml
@@ -2,20 +2,20 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Non focused states -->
- <item android:drawable="@android:color/transparent" android:state_focused="false" android:state_pressed="false" android:state_selected="false"/>
- <item android:drawable="@drawable/tab_selected_conversations" android:state_focused="false" android:state_pressed="false" android:state_selected="true"/>
+ <item android:drawable="@android:color/transparent" android:state_focused="false" android:state_pressed="false" android:state_selected="false" />
+ <item android:drawable="@drawable/tab_selected_conversations" android:state_focused="false" android:state_pressed="false" android:state_selected="true" />
<!-- Focused states -->
- <item android:drawable="@drawable/tab_unselected_focused_conversations" android:state_focused="true" android:state_pressed="false" android:state_selected="false"/>
- <item android:drawable="@drawable/tab_selected_focused_conversations" android:state_focused="true" android:state_pressed="false" android:state_selected="true"/>
+ <item android:drawable="@drawable/tab_unselected_focused_conversations" android:state_focused="true" android:state_pressed="false" android:state_selected="false" />
+ <item android:drawable="@drawable/tab_selected_focused_conversations" android:state_focused="true" android:state_pressed="false" android:state_selected="true" />
<!-- Pressed -->
<!-- Non focused states -->
- <item android:drawable="@drawable/tab_unselected_pressed_conversations" android:state_focused="false" android:state_pressed="true" android:state_selected="false"/>
- <item android:drawable="@drawable/tab_selected_pressed_conversations" android:state_focused="false" android:state_pressed="true" android:state_selected="true"/>
+ <item android:drawable="@drawable/tab_unselected_pressed_conversations" android:state_focused="false" android:state_pressed="true" android:state_selected="false" />
+ <item android:drawable="@drawable/tab_selected_pressed_conversations" android:state_focused="false" android:state_pressed="true" android:state_selected="true" />
<!-- Focused states -->
- <item android:drawable="@drawable/tab_unselected_pressed_conversations" android:state_focused="true" android:state_pressed="true" android:state_selected="false"/>
- <item android:drawable="@drawable/tab_selected_pressed_conversations" android:state_focused="true" android:state_pressed="true" android:state_selected="true"/>
+ <item android:drawable="@drawable/tab_unselected_pressed_conversations" android:state_focused="true" android:state_pressed="true" android:state_selected="false" />
+ <item android:drawable="@drawable/tab_selected_pressed_conversations" android:state_focused="true" android:state_pressed="true" android:state_selected="true" />
</selector> \ No newline at end of file
diff --git a/src/main/res/drawable/es_slidingpane_shadow.xml b/src/main/res/drawable/es_slidingpane_shadow.xml
index de96e08f4..b9aaf908b 100644
--- a/src/main/res/drawable/es_slidingpane_shadow.xml
+++ b/src/main/res/drawable/es_slidingpane_shadow.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android" >
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:endColor="@color/black12"
diff --git a/src/main/res/drawable/grey.xml b/src/main/res/drawable/grey.xml
index 26986dbe2..9354f095d 100644
--- a/src/main/res/drawable/grey.xml
+++ b/src/main/res/drawable/grey.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle" >
+ android:shape="rectangle">
<solid android:color="@color/black12" />
diff --git a/src/main/res/drawable/greybackground.xml b/src/main/res/drawable/greybackground.xml
index bedc4b17a..f820cf318 100644
--- a/src/main/res/drawable/greybackground.xml
+++ b/src/main/res/drawable/greybackground.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/grey" android:state_pressed="true"/>
+ <item android:drawable="@drawable/grey" android:state_pressed="true" />
</selector> \ No newline at end of file
diff --git a/src/main/res/drawable/infocard_border.xml b/src/main/res/drawable/infocard_border.xml
index baf60602f..ff3ec366a 100644
--- a/src/main/res/drawable/infocard_border.xml
+++ b/src/main/res/drawable/infocard_border.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android" >
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/grey50" />
@@ -7,9 +7,8 @@
<stroke
android:width="0.5dp"
- android:color="@color/black12" >
- </stroke>
-
+ android:color="@color/black12"></stroke>
+
<padding
android:bottom="0dp"
android:left="0dp"
diff --git a/src/main/res/drawable/message_border.xml b/src/main/res/drawable/message_border.xml
index e80f50cd5..73c7a6716 100644
--- a/src/main/res/drawable/message_border.xml
+++ b/src/main/res/drawable/message_border.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle" >
+ android:shape="rectangle">
<corners android:radius="5dp" />
diff --git a/src/main/res/drawable/snackbar.xml b/src/main/res/drawable/snackbar.xml
index 2645b1362..a250a1bd6 100644
--- a/src/main/res/drawable/snackbar.xml
+++ b/src/main/res/drawable/snackbar.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android" >
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/grey800" />
diff --git a/src/main/res/drawable/switch_back_off.xml b/src/main/res/drawable/switch_back_off.xml
index 20d2fb146..9082347b2 100644
--- a/src/main/res/drawable/switch_back_off.xml
+++ b/src/main/res/drawable/switch_back_off.xml
@@ -1,15 +1,19 @@
<?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">
+ <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">
+ </shape>
+ </item>
+ <item android:state_enabled="true">
+ <shape android:shape="rectangle">
<solid android:color="#939393" />
<corners android:radius="99dp" />
- </shape></item>
+ </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
index 45117a98e..ae78edecc 100644
--- a/src/main/res/drawable/switch_back_on.xml
+++ b/src/main/res/drawable/switch_back_on.xml
@@ -2,15 +2,15 @@
<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"/>
+ <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"/>
+ <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
index ba3d1c456..da33e46c3 100644
--- a/src/main/res/drawable/switch_thumb.xml
+++ b/src/main/res/drawable/switch_thumb.xml
@@ -1,12 +1,12 @@
<?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"/>
+ <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-w945dp/fragment_conversations_overview.xml b/src/main/res/layout-w945dp/fragment_conversations_overview.xml
index 7ae1788d7..c6cc7d7c2 100644
--- a/src/main/res/layout-w945dp/fragment_conversations_overview.xml
+++ b/src/main/res/layout-w945dp/fragment_conversations_overview.xml
@@ -10,7 +10,7 @@
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@color/grey50"
- android:orientation="vertical" >
+ android:orientation="vertical">
<de.timroes.android.listview.EnhancedListView
android:id="@+id/list"
@@ -26,7 +26,6 @@
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="match_parent"
- android:orientation="vertical" >
- </LinearLayout>
+ android:orientation="vertical"></LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/layout/ab_title.xml b/src/main/res/layout/ab_title.xml
index 1143ad798..2886a4716 100644
--- a/src/main/res/layout/ab_title.xml
+++ b/src/main/res/layout/ab_title.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical" android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="start|center_vertical">
@@ -17,6 +18,7 @@
android:clickable="true"
android:onClick="onClick"
android:paddingTop="1dp" />
+
<github.ankushsachdeva.emojicon.EmojiconTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text2"
style="@style/Base.TextAppearance.AppCompat.Widget.ActionBar.Subtitle"
diff --git a/src/main/res/layout/account_row.xml b/src/main/res/layout/account_row.xml
index fbb05a274..b953b6d31 100644
--- a/src/main/res/layout/account_row.xml
+++ b/src/main/res/layout/account_row.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?android:attr/activatedBackgroundIndicator"
- android:paddingLeft="8dp"
- android:paddingBottom="8dp"
- android:paddingTop="8dp">
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:paddingLeft="8dp"
+ android:paddingBottom="8dp"
+ android:paddingTop="8dp">
<com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/account_image"
diff --git a/src/main/res/layout/actionview_search.xml b/src/main/res/layout/actionview_search.xml
index 50becfc68..64588959c 100644
--- a/src/main/res/layout/actionview_search.xml
+++ b/src/main/res/layout/actionview_search.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:addStatesFromChildren="true"
android:focusable="true"
android:gravity="center"
android:paddingLeft="5dp"
- android:paddingRight="5dp" >
+ android:paddingRight="5dp">
<EditText
android:id="@+id/search_field"
@@ -17,6 +17,6 @@
android:imeOptions="actionSearch"
android:textColor="@color/white"
android:textColorHint="@color/white70"
- android:hint="@string/search_for_contacts_or_groups"/>
+ android:hint="@string/search_for_contacts_or_groups" />
</RelativeLayout> \ No newline at end of file
diff --git a/src/main/res/layout/activity_about.xml b/src/main/res/layout/activity_about.xml
index 47204b430..2d9f9a691 100644
--- a/src/main/res/layout/activity_about.xml
+++ b/src/main/res/layout/activity_about.xml
@@ -18,5 +18,5 @@
android:textColor="@color/black87"
android:textSize="?attr/TextSizeBody"
android:typeface="monospace"
- android:fontFamily="monospace"/>
+ android:fontFamily="monospace" />
</ScrollView>
diff --git a/src/main/res/layout/activity_change_password.xml b/src/main/res/layout/activity_change_password.xml
index 6fb1d0131..8808227e3 100644
--- a/src/main/res/layout/activity_change_password.xml
+++ b/src/main/res/layout/activity_change_password.xml
@@ -1,110 +1,111 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@color/grey200">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/grey200">
- <ScrollView
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_above="@+id/button_bar">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
- android:layout_marginTop="@dimen/activity_vertical_margin"
- android:layout_marginBottom="@dimen/activity_vertical_margin"
- android:background="@drawable/infocard_border"
- android:padding="@dimen/infocard_padding"
- android:orientation="vertical">
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_above="@+id/button_bar">
- <TextView
- android:id="@+id/current_password_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/current_password"
- android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"/>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
+ android:background="@drawable/infocard_border"
+ android:padding="@dimen/infocard_padding"
+ android:orientation="vertical">
- <EditText
- android:id="@+id/current_password"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp"
- android:hint="@string/password"
- android:inputType="textPassword"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
- android:textSize="?attr/TextSizeBody"/>
+ <TextView
+ android:id="@+id/current_password_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/current_password"
+ android:textColor="@color/black87"
+ 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:textSize="?attr/TextSizeBody"/>
+ <EditText
+ android:id="@+id/current_password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:hint="@string/password"
+ android:inputType="textPassword"
+ android:textColor="@color/black87"
+ android:textColorHint="@color/black54"
+ android:textSize="?attr/TextSizeBody" />
- <EditText
- android:id="@+id/new_password"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp"
- android:hint="@string/password"
- android:inputType="textPassword"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
- 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: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:textSize="?attr/TextSizeBody"/>
+ <EditText
+ android:id="@+id/new_password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:hint="@string/password"
+ android:inputType="textPassword"
+ android:textColor="@color/black87"
+ android:textColorHint="@color/black54"
+ android:textSize="?attr/TextSizeBody" />
- <EditText
- android:id="@+id/new_password_confirm"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="@string/password"
- android:inputType="textPassword"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
- android:textSize="?attr/TextSizeBody"/>
- </LinearLayout>
- </ScrollView>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/account_settings_confirm_password"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody" />
+
+ <EditText
+ android:id="@+id/new_password_confirm"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/password"
+ android:inputType="textPassword"
+ android:textColor="@color/black87"
+ android:textColorHint="@color/black54"
+ android:textSize="?attr/TextSizeBody" />
+ </LinearLayout>
+ </ScrollView>
- <LinearLayout
- android:id="@+id/button_bar"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true">
+ <LinearLayout
+ android:id="@+id/button_bar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true">
- <Button
- android:id="@+id/left_button"
- style="?android:attr/borderlessButtonStyle"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/cancel"/>
+ <Button
+ android:id="@+id/left_button"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/cancel" />
- <View
- android:layout_width="1dp"
- android:layout_height="fill_parent"
- android:layout_marginBottom="7dp"
- android:layout_marginTop="7dp"
- android:background="@color/black12"/>
+ <View
+ android:layout_width="1dp"
+ android:layout_height="fill_parent"
+ android:layout_marginBottom="7dp"
+ android:layout_marginTop="7dp"
+ android:background="@color/black12" />
- <Button
- android:id="@+id/right_button"
- style="?android:attr/borderlessButtonStyle"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/change_password"/>
- </LinearLayout>
+ <Button
+ android:id="@+id/right_button"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/change_password" />
+ </LinearLayout>
</RelativeLayout> \ No newline at end of file
diff --git a/src/main/res/layout/activity_choose_contact.xml b/src/main/res/layout/activity_choose_contact.xml
index 248a7822c..66e416de5 100644
--- a/src/main/res/layout/activity_choose_contact.xml
+++ b/src/main/res/layout/activity_choose_contact.xml
@@ -2,7 +2,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_height="match_parent">
<ListView
android:id="@+id/choose_contact_list"
diff --git a/src/main/res/layout/activity_contact_details.xml b/src/main/res/layout/activity_contact_details.xml
index eb479b96b..ca641886e 100644
--- a/src/main/res/layout/activity_contact_details.xml
+++ b/src/main/res/layout/activity_contact_details.xml
@@ -2,13 +2,13 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:background="@color/grey200" >
+ android:background="@color/grey200">
<LinearLayout
android:id="@+id/details_main_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:orientation="vertical" >
+ android:orientation="vertical">
<RelativeLayout
android:layout_width="fill_parent"
@@ -18,7 +18,7 @@
android:layout_marginTop="@dimen/activity_vertical_margin"
android:layout_marginBottom="@dimen/activity_vertical_margin"
android:background="@drawable/infocard_border"
- android:padding="@dimen/infocard_padding" >
+ android:padding="@dimen/infocard_padding">
<QuickContactBadge
android:id="@+id/details_contact_badge"
@@ -64,8 +64,7 @@
android:layout_marginLeft="-2dp"
android:layout_marginBottom="4dp"
android:orientation="horizontal"
- android:layout_gravity="center_horizontal">
- </com.wefika.flowlayout.FlowLayout>
+ android:layout_gravity="center_horizontal"></com.wefika.flowlayout.FlowLayout>
<TextView
android:id="@+id/details_lastseen"
@@ -109,7 +108,7 @@
android:layout_marginTop="@dimen/activity_vertical_margin"
android:layout_marginBottom="@dimen/activity_vertical_margin"
android:background="@drawable/infocard_border"
- android:padding="@dimen/infocard_padding" >
+ android:padding="@dimen/infocard_padding">
<Button
android:id="@+id/add_contact_button"
@@ -156,8 +155,7 @@
android:divider="?android:dividerHorizontal"
android:orientation="vertical"
android:padding="@dimen/infocard_padding"
- android:showDividers="middle" >
- </LinearLayout>
+ android:showDividers="middle"></LinearLayout>
</LinearLayout>
</ScrollView>
diff --git a/src/main/res/layout/activity_edit_account.xml b/src/main/res/layout/activity_edit_account.xml
index 808834d22..7747055ec 100644
--- a/src/main/res/layout/activity_edit_account.xml
+++ b/src/main/res/layout/activity_edit_account.xml
@@ -189,6 +189,7 @@
android:orientation="vertical"
android:padding="@dimen/infocard_padding"
android:visibility="gone">
+
<TextView
android:id="@+id/os_optimization_headline"
android:layout_width="wrap_content"
@@ -196,7 +197,8 @@
android:text="@string/battery_optimizations_enabled"
android:textColor="@color/black87"
android:textSize="?attr/TextSizeHeadline"
- android:textStyle="bold"/>
+ android:textStyle="bold" />
+
<TextView
android:id="@+id/os_optimization_body"
android:layout_width="wrap_content"
@@ -206,7 +208,8 @@
android:layout_marginTop="8dp"
android:text="@string/battery_optimizations_enabled_explained"
android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
+
<Button
android:id="@+id/os_optimization_disable"
style="?android:attr/borderlessButtonStyle"
@@ -215,9 +218,10 @@
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true" android:layout_below="@+id/os_optimization_body"
+ android:layout_alignParentRight="true"
+ android:layout_below="@+id/os_optimization_body"
android:text="@string/disable"
- android:textColor="@color/accent"/>
+ android:textColor="@color/accent" />
</RelativeLayout>
<LinearLayout
@@ -251,7 +255,7 @@
android:textColor="@color/black87"
android:textSize="?attr/TextSizeBody"
android:singleLine="true"
- android:ellipsize="end"/>
+ android:ellipsize="end" />
<TextView
android:id="@+id/session_est"
@@ -285,7 +289,7 @@
android:textColor="@color/black87"
android:textSize="?attr/TextSizeBody"
android:singleLine="true"
- android:ellipsize="end"/>
+ android:ellipsize="end" />
<TextView
android:id="@+id/server_info_pep"
@@ -309,7 +313,7 @@
android:textColor="@color/black87"
android:textSize="?attr/TextSizeBody"
android:singleLine="true"
- android:ellipsize="end"/>
+ android:ellipsize="end" />
<TextView
android:id="@+id/server_info_blocking"
@@ -333,7 +337,7 @@
android:textColor="@color/black87"
android:textSize="?attr/TextSizeBody"
android:singleLine="true"
- android:ellipsize="end"/>
+ android:ellipsize="end" />
<TextView
android:id="@+id/server_info_sm"
@@ -357,7 +361,7 @@
android:textColor="@color/black87"
android:textSize="?attr/TextSizeBody"
android:singleLine="true"
- android:ellipsize="end"/>
+ android:ellipsize="end" />
<TextView
android:id="@+id/server_info_roster_version"
@@ -381,7 +385,7 @@
android:textColor="@color/black87"
android:textSize="?attr/TextSizeBody"
android:singleLine="true"
- android:ellipsize="end"/>
+ android:ellipsize="end" />
<TextView
android:id="@+id/server_info_carbons"
@@ -405,7 +409,7 @@
android:textColor="@color/black87"
android:textSize="?attr/TextSizeBody"
android:singleLine="true"
- android:ellipsize="end"/>
+ android:ellipsize="end" />
<TextView
android:id="@+id/server_info_mam"
@@ -429,7 +433,7 @@
android:textColor="@color/black87"
android:textSize="?attr/TextSizeBody"
android:singleLine="true"
- android:ellipsize="end"/>
+ android:ellipsize="end" />
<TextView
android:id="@+id/server_info_csi"
@@ -454,7 +458,7 @@
android:textColor="@color/black87"
android:textSize="?attr/TextSizeBody"
android:singleLine="true"
- android:ellipsize="end"/>
+ android:ellipsize="end" />
<TextView
android:id="@+id/server_info_push"
@@ -463,8 +467,9 @@
android:layout_gravity="right"
android:textColor="@color/black87"
android:paddingLeft="4dp"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
</TableRow>
+
<TableRow
android:layout_width="fill_parent"
android:layout_height="wrap_content">
@@ -476,7 +481,7 @@
android:textColor="@color/black87"
android:textSize="?attr/TextSizeBody"
android:singleLine="true"
- android:ellipsize="end"/>
+ android:ellipsize="end" />
<TextView
android:id="@+id/server_info_http_upload"
@@ -485,7 +490,7 @@
android:layout_gravity="right"
android:textColor="@color/black87"
android:paddingLeft="4dp"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
</TableRow>
</TableLayout>
@@ -624,8 +629,7 @@
android:layout_height="wrap_content"
android:divider="?android:dividerHorizontal"
android:orientation="vertical"
- android:showDividers="middle">
- </LinearLayout>
+ android:showDividers="middle"></LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
@@ -647,14 +651,14 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/cancel"
- android:textColor="@color/black87"/>
+ android:textColor="@color/black87" />
<View
android:layout_width="1dp"
android:layout_height="fill_parent"
android:layout_marginBottom="7dp"
android:layout_marginTop="7dp"
- android:background="@color/black12"/>
+ android:background="@color/black12" />
<Button
android:id="@+id/save_button"
@@ -664,7 +668,7 @@
android:layout_weight="1"
android:enabled="false"
android:text="@string/save"
- android:textColor="@color/black54"/>
+ android:textColor="@color/black54" />
</LinearLayout>
</RelativeLayout> \ No newline at end of file
diff --git a/src/main/res/layout/activity_fullscreen_message.xml b/src/main/res/layout/activity_fullscreen_message.xml
index 69e6f3d6a..a4d30f902 100644
--- a/src/main/res/layout/activity_fullscreen_message.xml
+++ b/src/main/res/layout/activity_fullscreen_message.xml
@@ -7,8 +7,7 @@
<include
android:id="@+id/toolbar"
- layout="@layout/tool_bar">
- </include>
+ layout="@layout/tool_bar"></include>
<uk.co.senab.photoview.PhotoView
android:id="@id/message_image_view"
diff --git a/src/main/res/layout/activity_muc_details.xml b/src/main/res/layout/activity_muc_details.xml
index df7737ea0..1fce6b608 100644
--- a/src/main/res/layout/activity_muc_details.xml
+++ b/src/main/res/layout/activity_muc_details.xml
@@ -51,30 +51,31 @@
</RelativeLayout>
<RelativeLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
- <TextView
- android:id="@+id/notification_status_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/notify_on_all_messages"
- android:layout_centerVertical="true"
- android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"
- android:layout_alignParentLeft="true"
- android:layout_toLeftOf="@+id/notification_status_button"
- />
- <ImageButton
- android:id="@+id/notification_status_button"
- style="?android:attr/buttonStyleSmall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_alignParentRight="true"
- android:layout_centerVertical="true"
- android:background="?android:selectableItemBackground"
- android:padding="@dimen/image_button_padding"
- android:src="@drawable/ic_notifications_grey600_24dp"/>
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/notification_status_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/notify_on_all_messages"
+ android:layout_centerVertical="true"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"
+ android:layout_alignParentLeft="true"
+ android:layout_toLeftOf="@+id/notification_status_button" />
+
+ <ImageButton
+ android:id="@+id/notification_status_button"
+ style="?android:attr/buttonStyleSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:background="?android:selectableItemBackground"
+ android:padding="@dimen/image_button_padding"
+ android:src="@drawable/ic_notifications_grey600_24dp" />
</RelativeLayout>
<TextView
@@ -129,7 +130,7 @@
android:padding="1dp"
android:src="@drawable/ic_profile"
app:riv_corner_radius="5dp"
- android:layout_alignParentRight="false"/>
+ android:layout_alignParentRight="false" />
<LinearLayout
android:layout_width="fill_parent"
@@ -174,8 +175,7 @@
android:layout_weight="1"
android:divider="?android:dividerHorizontal"
android:orientation="vertical"
- android:showDividers="middle">
- </LinearLayout>
+ android:showDividers="middle"></LinearLayout>
<Button
android:id="@+id/invite"
@@ -188,52 +188,52 @@
</LinearLayout>
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/activity_vertical_margin"
- android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
- android:layout_marginTop="@dimen/activity_vertical_margin"
- android:background="@drawable/infocard_border"
- android:orientation="vertical"
- android:padding="@dimen/infocard_padding"
- android:id="@+id/muc_info_more"
- android:shrinkColumns="0"
- android:stretchColumns="1"
- android:visibility="gone">
-
-
- <TableLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:stretchColumns="1">
-
- <TableRow
- android:layout_width="fill_parent"
- android:layout_height="match_parent">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/server_info_mam"
- android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"
- android:singleLine="true"
- android:ellipsize="end" />
-
- <TextView
- android:id="@+id/muc_info_mam"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:paddingLeft="4dp"
- android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody" />
- </TableRow>
-
- </TableLayout>
- </LinearLayout>
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:background="@drawable/infocard_border"
+ android:orientation="vertical"
+ android:padding="@dimen/infocard_padding"
+ android:id="@+id/muc_info_more"
+ android:shrinkColumns="0"
+ android:stretchColumns="1"
+ android:visibility="gone">
+
+
+ <TableLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:stretchColumns="1">
+
+ <TableRow
+ android:layout_width="fill_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/server_info_mam"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"
+ android:singleLine="true"
+ android:ellipsize="end" />
+
+ <TextView
+ android:id="@+id/muc_info_mam"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:paddingLeft="4dp"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody" />
+ </TableRow>
+
+ </TableLayout>
+ </LinearLayout>
</LinearLayout>
</ScrollView> \ No newline at end of file
diff --git a/src/main/res/layout/activity_publish_profile_picture.xml b/src/main/res/layout/activity_publish_profile_picture.xml
index 8690509e9..7051233b5 100644
--- a/src/main/res/layout/activity_publish_profile_picture.xml
+++ b/src/main/res/layout/activity_publish_profile_picture.xml
@@ -4,116 +4,119 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/grey200">
+
<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/button_bar"
- android:layout_alignParentTop="true" >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="center_horizontal">
+ android:layout_alignParentTop="true">
<LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_above="@+id/button_bar"
- android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true"
- android:layout_below="@+id/secondary_hint"
- android:gravity="center_vertical"
- android:orientation="vertical"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
- android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginTop="@dimen/activity_vertical_margin"
- android:layout_marginBottom="@dimen/activity_vertical_margin"
- android:background="@drawable/infocard_border"
- android:padding="@dimen/infocard_padding">
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center_horizontal">
<LinearLayout
- android:id="@+id/account_image_wrapper"
- android:layout_width="wrap_content"
+ android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- android:layout_gravity="center_horizontal">
+ android:layout_above="@+id/button_bar"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true"
+ android:layout_below="@+id/secondary_hint"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
+ android:background="@drawable/infocard_border"
+ android:padding="@dimen/infocard_padding">
- <com.makeramen.roundedimageview.RoundedImageView
- android:id="@+id/account_image"
+ <LinearLayout
+ android:id="@+id/account_image_wrapper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
- android:layout_marginBottom="16dp"
- android:adjustViewBounds="true"
- android:background="@drawable/message_border"
- android:contentDescription="@string/account_image_description"
- android:maxHeight="384dp"
- android:maxWidth="384dp"
- android:padding="1dp"
- app:riv_corner_radius="5dp" />
- </LinearLayout>
+ android:layout_gravity="center_horizontal">
- <TextView
- android:id="@+id/hint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/account_image_wrapper"
- android:layout_centerHorizontal="true"
- android:text="@string/touch_to_choose_picture"
- android:textColor="@color/black54"
- android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
- android:layout_marginTop="@dimen/activity_vertical_margin"
- android:layout_marginBottom="@dimen/activity_vertical_margin"
- android:layout_gravity="center_horizontal"
- android:textAlignment="center" />
+ <com.makeramen.roundedimageview.RoundedImageView
+ android:id="@+id/account_image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="16dp"
+ android:adjustViewBounds="true"
+ android:background="@drawable/message_border"
+ android:contentDescription="@string/account_image_description"
+ android:maxHeight="384dp"
+ android:maxWidth="384dp"
+ android:padding="1dp"
+ app:riv_corner_radius="5dp" />
+ </LinearLayout>
- <TextView
- android:id="@+id/secondary_hint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/hint"
- android:layout_centerHorizontal="true"
- android:text="@string/or_long_press_for_default"
- android:textColor="@color/black54"
- android:layout_marginBottom="@dimen/activity_vertical_margin"
- android:layout_marginTop="@dimen/activity_vertical_margin"
- android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
- android:layout_gravity="center_horizontal"
- android:textAlignment="center" />
+ <TextView
+ android:id="@+id/hint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/account_image_wrapper"
+ android:layout_centerHorizontal="true"
+ android:text="@string/touch_to_choose_picture"
+ android:textColor="@color/black54"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
+ android:layout_gravity="center_horizontal"
+ android:textAlignment="center" />
- <TextView
- android:id="@+id/account"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="@color/black87"
- android:textSize="?attr/TextSizeHeadline"
- android:visibility="gone" />
+ <TextView
+ android:id="@+id/secondary_hint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/hint"
+ android:layout_centerHorizontal="true"
+ android:text="@string/or_long_press_for_default"
+ android:textColor="@color/black54"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
+ android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_gravity="center_horizontal"
+ android:textAlignment="center" />
- <TextView
- android:id="@+id/hint_or_warning"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:minLines="3"
- android:text="@string/publish_avatar_explanation"
- android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"
- android:textAlignment="center"
- android:layout_gravity="center_horizontal" />
- </LinearLayout>
-</LinearLayout>
+ <TextView
+ android:id="@+id/account"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeHeadline"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/hint_or_warning"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:minLines="3"
+ android:text="@string/publish_avatar_explanation"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"
+ android:textAlignment="center"
+ android:layout_gravity="center_horizontal" />
+ </LinearLayout>
+ </LinearLayout>
</ScrollView>
+
<LinearLayout
android:id="@+id/button_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true" >
+ android:layout_alignParentRight="true">
<Button
android:id="@+id/cancel_button"
diff --git a/src/main/res/layout/activity_recording.xml b/src/main/res/layout/activity_recording.xml
index 2e5ea7307..b1bcb2a65 100644
--- a/src/main/res/layout/activity_recording.xml
+++ b/src/main/res/layout/activity_recording.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/grey50">
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/grey50">
<LinearLayout
android:id="@+id/button_bar"
@@ -48,5 +48,5 @@
android:textStyle="bold"
android:id="@+id/timer"
android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"/>
+ android:layout_centerHorizontal="true" />
</RelativeLayout> \ No newline at end of file
diff --git a/src/main/res/layout/activity_set_presence.xml b/src/main/res/layout/activity_set_presence.xml
index d7ada8539..517654093 100644
--- a/src/main/res/layout/activity_set_presence.xml
+++ b/src/main/res/layout/activity_set_presence.xml
@@ -20,6 +20,7 @@
android:background="@drawable/infocard_border"
android:padding="@dimen/infocard_padding"
android:orientation="vertical">
+
<github.ankushsachdeva.emojicon.EmojiconEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -28,12 +29,14 @@
android:id="@+id/presence_status_message"
android:textColor="@color/black87"
android:layout_marginBottom="8dp"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
+
<Spinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/presence_show"
- android:layout_gravity="center_horizontal"/>
+ android:layout_gravity="center_horizontal" />
+
<CheckBox
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
@@ -43,7 +46,8 @@
android:id="@+id/all_accounts"
android:textColor="@color/black87"
android:textSize="?attr/TextSizeBody"
- android:visibility="gone"/>
+ android:visibility="gone" />
+
<Button
android:id="@+id/change_presence"
style="?android:attr/borderlessButtonStyle"
@@ -51,8 +55,9 @@
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="@string/change_presence"
- android:textColor="@color/accent"/>
+ android:textColor="@color/accent" />
</LinearLayout>
+
<LinearLayout
android:id="@+id/templates"
android:layout_width="match_parent"
@@ -65,7 +70,6 @@
android:padding="@dimen/infocard_padding"
android:orientation="vertical"
android:divider="?android:dividerHorizontal"
- android:showDividers="middle">
- </LinearLayout>
+ android:showDividers="middle"></LinearLayout>
</LinearLayout>
</ScrollView> \ No newline at end of file
diff --git a/src/main/res/layout/activity_share_locaction.xml b/src/main/res/layout/activity_share_locaction.xml
index df3e08a95..7505e9b99 100644
--- a/src/main/res/layout/activity_share_locaction.xml
+++ b/src/main/res/layout/activity_share_locaction.xml
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:map="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@color/ripple_material_dark">
+ xmlns:map="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/ripple_material_dark">
+
<fragment
android:name="com.google.android.gms.maps.MapFragment"
android:id="@+id/map_fragment"
@@ -18,7 +19,7 @@
map:uiTiltGestures="false"
map:uiZoomControls="false"
map:uiZoomGestures="true"
- tools:ignore="MissingPrefix"/>
+ tools:ignore="MissingPrefix" />
<RelativeLayout
android:id="@+id/snackbar"
@@ -31,7 +32,7 @@
android:layout_above="@+id/button_bar"
android:background="@drawable/snackbar"
android:minHeight="48dp"
- android:visibility="visible" >
+ android:visibility="visible">
<TextView
android:layout_width="wrap_content"
@@ -42,7 +43,7 @@
android:paddingLeft="24dp"
android:textColor="@color/grey50"
android:textSize="?attr/TextSizeBody"
- android:text="@string/location_sharing_disabled"/>
+ android:text="@string/location_sharing_disabled" />
<TextView
android:id="@+id/snackbar_action"
@@ -59,7 +60,7 @@
android:text="@string/enable"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
- android:layout_alignParentEnd="true"/>
+ android:layout_alignParentEnd="true" />
</RelativeLayout>
<RelativeLayout
@@ -73,7 +74,7 @@
android:layout_above="@+id/button_bar"
android:background="@drawable/snackbar"
android:minHeight="24dp"
- android:visibility="gone" >
+ android:visibility="gone">
<TextView
android:id="@id/snackbar_location_message"
@@ -93,7 +94,7 @@
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true" >
+ android:layout_alignParentRight="true">
<Button
android:id="@+id/cancel_button"
diff --git a/src/main/res/layout/activity_show_locaction.xml b/src/main/res/layout/activity_show_locaction.xml
index 95c22e09d..32e177b33 100644
--- a/src/main/res/layout/activity_show_locaction.xml
+++ b/src/main/res/layout/activity_show_locaction.xml
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:map="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ xmlns:map="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
<fragment
@@ -18,6 +18,6 @@
map:uiTiltGestures="false"
map:uiZoomControls="false"
map:uiZoomGestures="true"
- tools:ignore="MissingPrefix"/>
+ tools:ignore="MissingPrefix" />
</RelativeLayout> \ No newline at end of file
diff --git a/src/main/res/layout/activity_start_conversation.xml b/src/main/res/layout/activity_start_conversation.xml
index d55ec196f..28e954b99 100644
--- a/src/main/res/layout/activity_start_conversation.xml
+++ b/src/main/res/layout/activity_start_conversation.xml
@@ -3,6 +3,6 @@
android:id="@+id/start_conversation_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/grey50" >
+ android:background="@color/grey50">
</android.support.v4.view.ViewPager> \ No newline at end of file
diff --git a/src/main/res/layout/activity_trust_keys.xml b/src/main/res/layout/activity_trust_keys.xml
index 1e95099f9..2ebffb5be 100644
--- a/src/main/res/layout/activity_trust_keys.xml
+++ b/src/main/res/layout/activity_trust_keys.xml
@@ -3,11 +3,12 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/grey200">
+
<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/button_bar"
- android:layout_alignParentTop="true" >
+ android:layout_alignParentTop="true">
<LinearLayout
android:layout_width="match_parent"
@@ -34,7 +35,7 @@
android:textColor="@color/black87"
android:textSize="?attr/TextSizeHeadline"
android:textStyle="bold"
- android:text="@string/error_trustkeys_title"/>
+ android:text="@string/error_trustkeys_title" />
<TextView
android:id="@+id/key_error_message"
@@ -42,7 +43,7 @@
android:layout_height="wrap_content"
android:textColor="@color/black87"
android:textSize="?attr/TextSizeBody"
- android:padding="8dp"/>
+ android:padding="8dp" />
</LinearLayout>
@@ -65,7 +66,7 @@
android:layout_height="wrap_content"
android:textColor="@color/black87"
android:textSize="?attr/TextSizeHeadline"
- android:textStyle="bold"/>
+ android:textStyle="bold" />
<LinearLayout
android:id="@+id/own_keys_details"
@@ -73,8 +74,7 @@
android:layout_height="wrap_content"
android:divider="?android:dividerHorizontal"
android:showDividers="middle"
- android:orientation="vertical">
- </LinearLayout>
+ android:orientation="vertical"></LinearLayout>
</LinearLayout>
@@ -89,6 +89,7 @@
</LinearLayout>
</ScrollView>
+
<LinearLayout
android:id="@+id/button_bar"
android:layout_width="wrap_content"
@@ -97,7 +98,7 @@
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true"
android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true" >
+ android:layout_alignParentRight="true">
<Button
android:id="@+id/cancel_button"
@@ -123,6 +124,6 @@
android:layout_weight="1"
android:enabled="true"
android:textColor="@color/black54"
- android:text="@string/done"/>
+ android:text="@string/done" />
</LinearLayout>
</RelativeLayout>
diff --git a/src/main/res/layout/activity_updater.xml b/src/main/res/layout/activity_updater.xml
index ccc147361..8613b978a 100644
--- a/src/main/res/layout/activity_updater.xml
+++ b/src/main/res/layout/activity_updater.xml
@@ -1,13 +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" android:layout_width="match_parent"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="de.pixart.messenger.ui.UpdaterActivity">
- <TextView android:id="@+id/updater" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true" android:layout_marginTop="6dp"
- android:text="Current" android:textAppearance="?android:attr/textAppearanceMedium"
+ <TextView
+ android:id="@+id/updater"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginTop="6dp"
+ android:text="Current"
+ android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="normal"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp" />
diff --git a/src/main/res/layout/activity_verify_otr.xml b/src/main/res/layout/activity_verify_otr.xml
index c15f19d5e..1617b8e26 100644
--- a/src/main/res/layout/activity_verify_otr.xml
+++ b/src/main/res/layout/activity_verify_otr.xml
@@ -1,152 +1,152 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@color/grey50">
-
- <ScrollView
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_above="@+id/button_bar">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
- android:layout_marginTop="@dimen/activity_vertical_margin"
- android:layout_marginBottom="@dimen/activity_vertical_margin">
-
- <TextView
- android:id="@+id/verification_explanation"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <LinearLayout
- android:id="@+id/manual_verification_area"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/your_fingerprint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"
- android:typeface="monospace"
- android:fontFamily="monospace"/>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/your_fingerprint"
- android:textColor="@color/black54"
- android:textSize="?attr/TextSizeInfo"/>
-
- <TextView
- android:id="@+id/remote_fingerprint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
- android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"
- android:typeface="monospace"
- android:fontFamily="monospace"/>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="20dp"
- android:text="@string/remote_fingerprint"
- android:textColor="@color/black54"
- android:textSize="?attr/TextSizeInfo"/>
-
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/smp_verification_area"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_marginTop="16dp"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/status_message"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:text="@string/verified"
- android:textColor="@color/black87"
- android:textSize="?attr/TextSizeHeadline"
- android:textStyle="bold"
- android:visibility="gone"/>
-
- <TextView
- android:id="@+id/shared_secret_hint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp"
- android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"
- android:textStyle="bold"
- android:visibility="gone"/>
-
- <EditText
- android:id="@+id/shared_secret_hint_editable"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp"
- android:hint="@string/shared_secret_hint"
- android:inputType="textAutoComplete"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
- android:textSize="?attr/TextSizeBody"/>
-
- <EditText
- android:id="@+id/shared_secret_secret"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:hint="@string/shared_secret_secret"
- android:inputType="textPassword"
- android:textColor="@color/black87"
- android:textColorHint="@color/black54"
- android:textSize="?attr/TextSizeBody"/>
- </LinearLayout>
- </LinearLayout>
- </ScrollView>
-
- <LinearLayout
- android:id="@+id/button_bar"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true">
-
- <Button
- android:id="@+id/left_button"
- style="?android:attr/borderlessButtonStyle"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"/>
-
- <View
- android:layout_width="1dp"
- android:layout_height="fill_parent"
- android:layout_marginBottom="7dp"
- android:layout_marginTop="7dp"
- android:background="@color/black12"/>
-
- <Button
- android:id="@+id/right_button"
- style="?android:attr/borderlessButtonStyle"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"/>
- </LinearLayout>
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/grey50">
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_above="@+id/button_bar">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin">
+
+ <TextView
+ android:id="@+id/verification_explanation"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <LinearLayout
+ android:id="@+id/manual_verification_area"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/your_fingerprint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"
+ android:typeface="monospace"
+ android:fontFamily="monospace" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/your_fingerprint"
+ android:textColor="@color/black54"
+ android:textSize="?attr/TextSizeInfo" />
+
+ <TextView
+ android:id="@+id/remote_fingerprint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="20dp"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"
+ android:typeface="monospace"
+ android:fontFamily="monospace" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="20dp"
+ android:text="@string/remote_fingerprint"
+ android:textColor="@color/black54"
+ android:textSize="?attr/TextSizeInfo" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/smp_verification_area"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_marginTop="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/status_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:text="@string/verified"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeHeadline"
+ android:textStyle="bold"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/shared_secret_hint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"
+ android:textStyle="bold"
+ android:visibility="gone" />
+
+ <EditText
+ android:id="@+id/shared_secret_hint_editable"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:hint="@string/shared_secret_hint"
+ android:inputType="textAutoComplete"
+ android:textColor="@color/black87"
+ android:textColorHint="@color/black54"
+ android:textSize="?attr/TextSizeBody" />
+
+ <EditText
+ android:id="@+id/shared_secret_secret"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:hint="@string/shared_secret_secret"
+ android:inputType="textPassword"
+ android:textColor="@color/black87"
+ android:textColorHint="@color/black54"
+ android:textSize="?attr/TextSizeBody" />
+ </LinearLayout>
+ </LinearLayout>
+ </ScrollView>
+
+ <LinearLayout
+ android:id="@+id/button_bar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true">
+
+ <Button
+ android:id="@+id/left_button"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+
+ <View
+ android:layout_width="1dp"
+ android:layout_height="fill_parent"
+ android:layout_marginBottom="7dp"
+ android:layout_marginTop="7dp"
+ android:background="@color/black12" />
+
+ <Button
+ android:id="@+id/right_button"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+ </LinearLayout>
</RelativeLayout> \ No newline at end of file
diff --git a/src/main/res/layout/captcha.xml b/src/main/res/layout/captcha.xml
index ea77b8354..08e685db4 100644
--- a/src/main/res/layout/captcha.xml
+++ b/src/main/res/layout/captcha.xml
@@ -3,13 +3,14 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:padding="16dp" >
+ android:padding="16dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/captcha"
- android:layout_gravity="center_horizontal"/>
+ android:layout_gravity="center_horizontal" />
+
<EditText
android:id="@+id/input"
android:layout_marginTop="8dp"
diff --git a/src/main/res/layout/certificate_information.xml b/src/main/res/layout/certificate_information.xml
index 4c085459b..18925c82b 100644
--- a/src/main/res/layout/certificate_information.xml
+++ b/src/main/res/layout/certificate_information.xml
@@ -1,8 +1,8 @@
<?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:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:padding="16dp">
<TextView
@@ -10,73 +10,84 @@
android:layout_height="wrap_content"
android:text="@string/certificate_subject"
android:textColor="@color/black87"
- android:textSize="?attr/TextSizeHeadline"/>
+ android:textSize="?attr/TextSizeHeadline" />
+
<TextView
android:layout_marginTop="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/certificate_cn"
android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
+
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/subject_cn"
android:textColor="@color/black54"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
+
<TextView
android:layout_marginTop="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/certificate_o"
android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
+
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/subject_o"
android:textColor="@color/black54"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
+
<TextView
android:layout_marginTop="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/certificate_issuer"
android:textColor="@color/black87"
- android:textSize="?attr/TextSizeHeadline"/>
+ android:textSize="?attr/TextSizeHeadline" />
+
<TextView
android:layout_marginTop="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/certificate_cn"
android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
+
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/issuer_cn"
android:textColor="@color/black54"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
+
<TextView
android:layout_marginTop="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/certificate_o"
android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
+
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/issuer_o"
android:textColor="@color/black54"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
+
<TextView
android:layout_marginTop="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/certificate_sha1"
android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
+
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -84,5 +95,5 @@
android:textColor="@color/black54"
android:textSize="?attr/TextSizeBody"
android:typeface="monospace"
- android:fontFamily="monospace"/>
+ android:fontFamily="monospace" />
</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/layout/contact.xml b/src/main/res/layout/contact.xml
index 445f16567..92e6020c9 100644
--- a/src/main/res/layout/contact.xml
+++ b/src/main/res/layout/contact.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?android:attr/activatedBackgroundIndicator"
- android:padding="8dp">
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:padding="8dp">
<com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/contact_photo"
@@ -22,7 +22,7 @@
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/contact_photo"
android:orientation="vertical"
- android:paddingLeft="8dp" >
+ android:paddingLeft="8dp">
<github.ankushsachdeva.emojicon.EmojiconTextView
android:id="@+id/contact_display_name"
@@ -39,13 +39,14 @@
android:singleLine="true"
android:textColor="@color/black87"
android:textSize="?attr/TextSizeBody" />
+
<com.wefika.flowlayout.FlowLayout
android:id="@+id/tags"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="-2dp"
- android:orientation="horizontal">
- </com.wefika.flowlayout.FlowLayout>
+ android:orientation="horizontal"></com.wefika.flowlayout.FlowLayout>
+
<TextView
android:id="@+id/key"
android:layout_width="wrap_content"
diff --git a/src/main/res/layout/contact_key.xml b/src/main/res/layout/contact_key.xml
index ec8ca1996..63a4e8518 100644
--- a/src/main/res/layout/contact_key.xml
+++ b/src/main/res/layout/contact_key.xml
@@ -23,7 +23,7 @@
android:textSize="?attr/TextSizeBody"
android:typeface="monospace"
android:fontFamily="monospace"
- android:longClickable="true"/>
+ android:longClickable="true" />
<TextView
android:id="@+id/key_type"
@@ -34,7 +34,7 @@
android:layout_below="@+id/key"
android:maxLines="1"
android:textSize="?attr/TextSizeInfo"
- android:longClickable="true"/>
+ android:longClickable="true" />
<TextView
android:id="@+id/key_trust"
@@ -45,7 +45,7 @@
android:visibility="gone"
android:textColor="@color/black54"
android:textSize="?attr/TextSizeInfo"
- android:longClickable="true"/>
+ android:longClickable="true" />
<LinearLayout
android:id="@+id/action_container"
@@ -55,6 +55,7 @@
android:orientation="vertical"
android:layout_alignParentRight="true"
android:layout_centerVertical="true">
+
<ImageButton
android:layout_gravity="center_horizontal"
android:id="@+id/button_remove"
@@ -82,7 +83,7 @@
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
- style="@style/MD"/>
+ style="@style/MD" />
</LinearLayout>
</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 42a55d09f..5a8f0736f 100644
--- a/src/main/res/layout/conversation_list_row.xml
+++ b/src/main/res/layout/conversation_list_row.xml
@@ -1,13 +1,13 @@
<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">
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:descendantFocusability="blocksDescendants">
<View
android:layout_width="fill_parent"
android:layout_height="match_parent"
- android:background="@color/primary"/>
+ android:background="@color/primary" />
<FrameLayout
android:id="@+id/swipeable_item"
@@ -32,7 +32,7 @@
android:padding="1dp"
app:riv_border_color="@color/black12"
app:riv_border_width="1dp"
- app:riv_corner_radius="5dp"/>
+ app:riv_corner_radius="5dp" />
<RelativeLayout
android:layout_width="fill_parent"
@@ -52,7 +52,7 @@
android:text="Awesome groupchat"
android:textColor="@color/black87"
android:textSize="?attr/TextSizeHeadline"
- android:typeface="sans"/>
+ android:typeface="sans" />
<RelativeLayout
android:id="@+id/conversation_lastwrapper"
@@ -61,12 +61,14 @@
android:layout_below="@id/conversation_name"
android:layout_marginTop="4dp">
- <LinearLayout android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_centerVertical="true"
- android:layout_toLeftOf="@+id/notification_status"
- android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:layout_toLeftOf="@+id/notification_status"
+ android:orientation="vertical">
+
<github.ankushsachdeva.emojicon.EmojiconTextView
android:id="@+id/conversation_lastmsg"
android:layout_width="match_parent"
@@ -75,7 +77,7 @@
android:singleLine="true"
android:text="This is a placeholder text to show the last messages"
android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
<com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/conversation_lastimage"
@@ -88,6 +90,7 @@
app:riv_corner_radius="5dp"
android:maxWidth="400dp" />
</LinearLayout>
+
<ImageView
android:id="@+id/notification_status"
android:layout_width="16sp"
@@ -95,8 +98,7 @@
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginLeft="4dp"
- android:src="@drawable/ic_notifications_grey600_24dp"
- />
+ android:src="@drawable/ic_notifications_grey600_24dp" />
</RelativeLayout>
<TextView
@@ -108,7 +110,7 @@
android:gravity="right"
android:text="23:42"
android:textColor="@color/black54"
- android:textSize="?attr/TextSizeInfo"/>
+ android:textSize="?attr/TextSizeInfo" />
</RelativeLayout>
</RelativeLayout>
</FrameLayout>
diff --git a/src/main/res/layout/dialog_block_contact.xml b/src/main/res/layout/dialog_block_contact.xml
index fd15a4665..2e43f6af8 100644
--- a/src/main/res/layout/dialog_block_contact.xml
+++ b/src/main/res/layout/dialog_block_contact.xml
@@ -13,7 +13,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="?attr/TextSizeBody"
- android:textColor="@color/black87"/>
+ android:textColor="@color/black87" />
+
<CheckBox
android:layout_marginTop="8dp"
android:id="@+id/report_spam"
diff --git a/src/main/res/layout/dialog_show_password.xml b/src/main/res/layout/dialog_show_password.xml
index 167659637..299b65dca 100644
--- a/src/main/res/layout/dialog_show_password.xml
+++ b/src/main/res/layout/dialog_show_password.xml
@@ -16,7 +16,6 @@
android:textSize="?attr/TextSizeHeadline"
android:fontFamily="monospace"
android:layout_gravity="center_horizontal"
- android:textColor="@color/black87" >
- </TextView>
+ android:textColor="@color/black87"></TextView>
</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/layout/form_boolean.xml b/src/main/res/layout/form_boolean.xml
index fd553acb2..6a1a3920a 100644
--- a/src/main/res/layout/form_boolean.xml
+++ b/src/main/res/layout/form_boolean.xml
@@ -1,13 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingTop="8dp"
- android:orientation="vertical">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="8dp"
+ android:orientation="vertical">
+
<CheckBox
android:id="@+id/field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/layout/form_text.xml b/src/main/res/layout/form_text.xml
index 31b521e89..398fa9bff 100644
--- a/src/main/res/layout/form_text.xml
+++ b/src/main/res/layout/form_text.xml
@@ -1,15 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingTop="8dp"
- android:orientation="vertical">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="8dp"
+ android:orientation="vertical">
+
<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
<EditText
android:id="@+id/field"
@@ -17,5 +18,5 @@
android:layout_height="wrap_content"
android:textColor="@color/black87"
android:textColorHint="@color/black54"
- android:textSize="?attr/TextSizeBody"/>
+ 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 70ee276b7..eff7afa8f 100644
--- a/src/main/res/layout/fragment_conversation.xml
+++ b/src/main/res/layout/fragment_conversation.xml
@@ -19,8 +19,7 @@
android:listSelector="@android:color/transparent"
android:stackFromBottom="true"
android:transcriptMode="normal"
- tools:listitem="@layout/message_sent">
- </ListView>
+ tools:listitem="@layout/message_sent"></ListView>
<RelativeLayout
android:id="@+id/textsend"
@@ -53,6 +52,7 @@
android:paddingTop="12dp"
android:textColor="@color/black87"
android:singleLine="false">
+
<requestFocus />
</de.pixart.messenger.ui.EditMessage>
@@ -76,7 +76,7 @@
android:layout_marginRight="8dp"
android:background="@drawable/snackbar"
android:minHeight="48dp"
- android:visibility="gone" >
+ android:visibility="gone">
<TextView
android:id="@+id/snackbar_message"
diff --git a/src/main/res/layout/fragment_conversations_overview.xml b/src/main/res/layout/fragment_conversations_overview.xml
index 9494fa067..90327136b 100644
--- a/src/main/res/layout/fragment_conversations_overview.xml
+++ b/src/main/res/layout/fragment_conversations_overview.xml
@@ -1,15 +1,14 @@
<android.support.v4.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content_view_spl"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_height="match_parent">
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_weight="1"
android:layout_height="match_parent"
android:background="@color/grey50"
- android:orientation="vertical" >
+ android:orientation="vertical">
<de.timroes.android.listview.EnhancedListView
android:id="@+id/list"
@@ -25,7 +24,6 @@
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_weight="1"
- android:orientation="vertical" >
- </LinearLayout>
+ android:orientation="vertical"></LinearLayout>
</android.support.v4.widget.SlidingPaneLayout> \ No newline at end of file
diff --git a/src/main/res/layout/join_conference_dialog.xml b/src/main/res/layout/join_conference_dialog.xml
index 9ef2dc047..a615346fc 100644
--- a/src/main/res/layout/join_conference_dialog.xml
+++ b/src/main/res/layout/join_conference_dialog.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:paddingBottom="8dp"
- android:paddingLeft="24dp"
- android:paddingRight="24dp"
- android:paddingTop="16dp">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingBottom="8dp"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp"
+ android:paddingTop="16dp">
<TextView
android:id="@+id/your_account"
@@ -40,7 +40,7 @@
android:inputType="textEmailAddress"
android:textColor="@color/black87"
android:textColorHint="@color/black54"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
<CheckBox
android:id="@+id/bookmark"
@@ -50,6 +50,6 @@
android:checked="true"
android:text="@string/save_as_bookmark"
android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/layout/keys_card.xml b/src/main/res/layout/keys_card.xml
index d3271d1bc..89c47c904 100644
--- a/src/main/res/layout/keys_card.xml
+++ b/src/main/res/layout/keys_card.xml
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/foreign_keys_card"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/activity_vertical_margin"
- android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
- android:layout_marginTop="@dimen/activity_vertical_margin"
- android:background="@drawable/infocard_border"
- android:orientation="vertical"
- android:padding="@dimen/infocard_padding">
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:background="@drawable/infocard_border"
+ android:orientation="vertical"
+ android:padding="@dimen/infocard_padding">
<TextView
android:id="@+id/foreign_keys_title"
@@ -17,7 +17,7 @@
android:layout_height="wrap_content"
android:textColor="@color/black87"
android:textSize="?attr/TextSizeHeadline"
- android:textStyle="bold"/>
+ android:textStyle="bold" />
<LinearLayout
android:id="@+id/foreign_keys_details"
@@ -25,8 +25,7 @@
android:layout_height="wrap_content"
android:divider="?android:dividerHorizontal"
android:orientation="vertical"
- android:showDividers="middle">
- </LinearLayout>
+ android:showDividers="middle"></LinearLayout>
<TextView
android:layout_marginTop="8dp"
@@ -35,5 +34,5 @@
android:layout_height="wrap_content"
android:textColor="@color/black87"
android:text="@string/no_keys_just_confirm"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/layout/list_item_tag.xml b/src/main/res/layout/list_item_tag.xml
index 5042d8d16..fcb7dd984 100644
--- a/src/main/res/layout/list_item_tag.xml
+++ b/src/main/res/layout/list_item_tag.xml
@@ -10,5 +10,4 @@
android:textColor="@color/white"
android:textAllCaps="true"
android:maxLines="1"
- android:layout_margin="2dp"
-/> \ No newline at end of file
+ android:layout_margin="2dp" /> \ No newline at end of file
diff --git a/src/main/res/layout/magic_create.xml b/src/main/res/layout/magic_create.xml
index c4adc0297..3330a7cbb 100644
--- a/src/main/res/layout/magic_create.xml
+++ b/src/main/res/layout/magic_create.xml
@@ -1,8 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fillViewport="true">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true">
+
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -20,24 +21,28 @@
android:paddingBottom="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp">
+
<Space
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_weight="1"/>
+ android:layout_weight="1" />
+
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pick_your_username"
android:textColor="@color/black87"
android:textSize="?attr/TextSizeHeadline"
- android:textStyle="bold"/>
+ android:textStyle="bold" />
+
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/magic_create_text"
android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
+
<EditText
android:id="@+id/username"
android:layout_width="match_parent"
@@ -45,7 +50,8 @@
android:layout_gravity="center_horizontal"
android:hint="@string/username_hint"
android:inputType="textNoSuggestions"
- android:textSize="?attr/TextSizeBody"/>
+ android:textSize="?attr/TextSizeBody" />
+
<TextView
android:id="@+id/full_jid"
android:layout_width="wrap_content"
@@ -54,7 +60,8 @@
android:text="@string/your_full_jid_will_be"
android:textColor="@color/black54"
android:textSize="?attr/TextSizeInfo"
- android:visibility="invisible"/>
+ android:visibility="invisible" />
+
<Button
android:id="@+id/create_account"
style="?android:attr/borderlessButtonStyle"
@@ -62,8 +69,9 @@
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="@string/next"
- android:textColor="@color/accent"/>
+ android:textColor="@color/accent" />
</LinearLayout>
+
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -71,13 +79,14 @@
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true">
+
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:padding="8dp"
- android:src="@drawable/main_logo"/>
+ android:src="@drawable/main_logo" />
</RelativeLayout>
</RelativeLayout>
</ScrollView> \ No newline at end of file
diff --git a/src/main/res/layout/manage_accounts.xml b/src/main/res/layout/manage_accounts.xml
index e55d8d40a..8d7167ea6 100644
--- a/src/main/res/layout/manage_accounts.xml
+++ b/src/main/res/layout/manage_accounts.xml
@@ -9,7 +9,6 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:divider="@color/black12"
- android:dividerHeight="1dp" >
- </ListView>
+ android:dividerHeight="1dp"></ListView>
</LinearLayout> \ 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 5d5fb7c43..9717081a8 100644
--- a/src/main/res/layout/message_received.xml
+++ b/src/main/res/layout/message_received.xml
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:emojicon="http://schemas.android.com/apk/res-auto"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingBottom="3dp"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:paddingTop="3dp">
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:emojicon="http://schemas.android.com/apk/res-auto"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingBottom="3dp"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:paddingTop="3dp">
<com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/message_photo"
diff --git a/src/main/res/layout/message_sent.xml b/src/main/res/layout/message_sent.xml
index 5c414ed2c..c44a216bd 100644
--- a/src/main/res/layout/message_sent.xml
+++ b/src/main/res/layout/message_sent.xml
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:emojicon="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:paddingBottom="3dp"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:paddingTop="3dp">
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:emojicon="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingBottom="3dp"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:paddingTop="3dp">
<com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/message_photo"
@@ -125,6 +125,7 @@
android:layout_marginLeft="4sp"
android:gravity="center_vertical"
android:src="@drawable/ic_received_indicator" />
+
<ImageView
android:id="@+id/indicator_read"
android:layout_width="?attr/TextSizeInfo"
diff --git a/src/main/res/layout/message_status.xml b/src/main/res/layout/message_status.xml
index 0a5804a2e..117f31264 100644
--- a/src/main/res/layout/message_status.xml
+++ b/src/main/res/layout/message_status.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="vertical"
- android:paddingBottom="5dp"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:paddingTop="5dp">
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingBottom="5dp"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:paddingTop="5dp">
<Button
android:id="@+id/load_more_messages"
@@ -15,8 +15,9 @@
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"/>
+ android:textColor="@color/accent"
+ android:layout_centerVertical="true"
+ android:layout_centerHorizontal="true" />
<com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/message_photo"
@@ -29,7 +30,7 @@
android:scaleType="fitXY"
android:src="@drawable/ic_profile"
android:background="@drawable/message_border"
- app:riv_corner_radius="5dp"/>
+ app:riv_corner_radius="5dp" />
<TextView
android:id="@+id/status_message"
@@ -42,6 +43,6 @@
android:text="@string/contact_has_read_up_to_this_point"
android:textColor="@color/black54"
android:textSize="?attr/TextSizeInfo"
- android:textStyle="italic"/>
+ android:textStyle="italic" />
</RelativeLayout> \ No newline at end of file
diff --git a/src/main/res/layout/password.xml b/src/main/res/layout/password.xml
index 5de9baa23..db8d82505 100644
--- a/src/main/res/layout/password.xml
+++ b/src/main/res/layout/password.xml
@@ -1,6 +1,7 @@
<?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:orientation="vertical"
+ android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
diff --git a/src/main/res/layout/presence_template.xml b/src/main/res/layout/presence_template.xml
index ea1d05bd8..ef6f01643 100644
--- a/src/main/res/layout/presence_template.xml
+++ b/src/main/res/layout/presence_template.xml
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="?android:attr/activatedBackgroundIndicator"
- android:paddingTop="8dp"
- android:paddingLeft="8dp"
- android:paddingBottom="8dp"
- android:id="@+id/presence_template">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:paddingTop="8dp"
+ android:paddingLeft="8dp"
+ android:paddingBottom="8dp"
+ android:id="@+id/presence_template">
+
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -17,25 +18,28 @@
android:layout_toLeftOf="@+id/delete_button"
android:layout_toStartOf="@+id/delete_button"
android:layout_marginRight="8dp">
- <github.ankushsachdeva.emojicon.EmojiconTextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/presence_status_message"
- android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"/>
- <github.ankushsachdeva.emojicon.EmojiconTextView
- android:id="@+id/status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingBottom="1dp"
- android:paddingLeft="4dp"
- android:paddingRight="4dp"
- android:paddingTop="1dp"
- android:textAllCaps="true"
- android:textColor="@color/white"
- android:textSize="?attr/TextSizeInfo"
- android:layout_marginTop="4dp"/>
+
+ <github.ankushsachdeva.emojicon.EmojiconTextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/presence_status_message"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody" />
+
+ <github.ankushsachdeva.emojicon.EmojiconTextView
+ android:id="@+id/status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="1dp"
+ android:paddingLeft="4dp"
+ android:paddingRight="4dp"
+ android:paddingTop="1dp"
+ android:textAllCaps="true"
+ android:textColor="@color/white"
+ android:textSize="?attr/TextSizeInfo"
+ android:layout_marginTop="4dp" />
</LinearLayout>
+
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -45,5 +49,5 @@
android:layout_alignParentEnd="true"
android:background="?android:selectableItemBackground"
android:padding="@dimen/image_button_padding"
- android:src="?attr/icon_remove"/>
+ android:src="?attr/icon_remove" />
</RelativeLayout> \ No newline at end of file
diff --git a/src/main/res/layout/quickedit.xml b/src/main/res/layout/quickedit.xml
index fb0b7d58f..5bbf7dad9 100644
--- a/src/main/res/layout/quickedit.xml
+++ b/src/main/res/layout/quickedit.xml
@@ -3,7 +3,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:padding="16dp" >
+ android:padding="16dp">
<github.ankushsachdeva.emojicon.EmojiconEditText
android:id="@+id/editor"
@@ -11,7 +11,7 @@
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
- android:textColor="@color/black87" >
+ android:textColor="@color/black87">
<requestFocus />
</github.ankushsachdeva.emojicon.EmojiconEditText>
diff --git a/src/main/res/layout/share_with.xml b/src/main/res/layout/share_with.xml
index 41b6033da..162d02efe 100644
--- a/src/main/res/layout/share_with.xml
+++ b/src/main/res/layout/share_with.xml
@@ -2,7 +2,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_height="match_parent">
<ListView
android:id="@+id/choose_conversation_list"
diff --git a/src/main/res/layout/show_location_infowindow.xml b/src/main/res/layout/show_location_infowindow.xml
index 0793ba7d5..a4bdf108f 100644
--- a/src/main/res/layout/show_location_infowindow.xml
+++ b/src/main/res/layout/show_location_infowindow.xml
@@ -14,12 +14,13 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="?attr/TextSizeBody"
- android:textStyle="bold"/>
+ android:textStyle="bold" />
+
<TextView
android:id="@+id/snippet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textSize="?attr/TextSizeInfo"/>
+ android:textSize="?attr/TextSizeInfo" />
</LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/layout/simple_list_item.xml b/src/main/res/layout/simple_list_item.xml
index 8cbc1f923..c54fd7833 100644
--- a/src/main/res/layout/simple_list_item.xml
+++ b/src/main/res/layout/simple_list_item.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2006 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2006 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.
@@ -15,12 +14,12 @@
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/text1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"
- android:gravity="center_vertical"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:minHeight="?android:attr/listPreferredItemHeightSmall" />
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"
+ android:gravity="center_vertical"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall" />
diff --git a/src/main/res/menu/attachment_choices.xml b/src/main/res/menu/attachment_choices.xml
index e2b0798ee..e56a9fa07 100644
--- a/src/main/res/menu/attachment_choices.xml
+++ b/src/main/res/menu/attachment_choices.xml
@@ -1,28 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/attach_location"
- android:title="@string/send_location"/>
+ android:title="@string/send_location" />
<item
android:id="@+id/attach_record_voice"
- android:title="@string/attach_record_voice"/>
+ android:title="@string/attach_record_voice" />
<item
android:id="@+id/attach_take_picture"
- android:title="@string/attach_take_picture"/>
+ android:title="@string/attach_take_picture" />
<item
android:id="@+id/attach_choose_picture"
- android:title="@string/attach_choose_picture"/>
+ android:title="@string/attach_choose_picture" />
<item
android:id="@+id/attach_choose_video"
- android:title="@string/attach_choose_video"/>
+ android:title="@string/attach_choose_video" />
<item
android:id="@+id/attach_choose_file"
- android:title="@string/choose_file"/>
+ android:title="@string/choose_file" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/change_presence.xml b/src/main/res/menu/change_presence.xml
index f3dfadfd0..1a320d59e 100644
--- a/src/main/res/menu/change_presence.xml
+++ b/src/main/res/menu/change_presence.xml
@@ -3,15 +3,15 @@
android:id="@+id/action_account_details"
android:title="@string/account_details"
android:showAsAction="always"
- android:icon="@drawable/ic_account_box_white_24dp"/>
+ android:icon="@drawable/ic_account_box_white_24dp" />
<item
android:id="@+id/action_accounts"
android:orderInCategory="90"
android:showAsAction="never"
- android:title="@string/action_accounts"/>
+ android:title="@string/action_accounts" />
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:showAsAction="never"
- android:title="@string/action_settings"/>
+ android:title="@string/action_settings" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/choose_contact.xml b/src/main/res/menu/choose_contact.xml
index 94b6479be..6c4d82153 100644
--- a/src/main/res/menu/choose_contact.xml
+++ b/src/main/res/menu/choose_contact.xml
@@ -6,12 +6,12 @@
android:actionLayout="@layout/actionview_search"
android:icon="?attr/icon_search"
android:showAsAction="collapseActionView|always"
- android:title="@string/search"/>
+ android:title="@string/search" />
<item
android:id="@+id/action_create_contact"
android:icon="?attr/icon_add_person"
android:showAsAction="always"
android:title="@string/create_contact"
- android:visible="false"/>
+ android:visible="false" />
</menu>
diff --git a/src/main/res/menu/conference_context.xml b/src/main/res/menu/conference_context.xml
index fd898580a..6a6eb92d2 100644
--- a/src/main/res/menu/conference_context.xml
+++ b/src/main/res/menu/conference_context.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/context_join_conference"
- android:title="@string/join_conference"/>
+ android:title="@string/join_conference" />
<item
android:id="@+id/context_delete_conference"
- android:title="@string/delete_bookmark"/>
+ android:title="@string/delete_bookmark" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/contact_context.xml b/src/main/res/menu/contact_context.xml
index 223c7ece0..1940d176e 100644
--- a/src/main/res/menu/contact_context.xml
+++ b/src/main/res/menu/contact_context.xml
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/context_start_conversation"
- android:title="@string/start_conversation"/>
+ android:title="@string/start_conversation" />
<item
android:id="@+id/context_contact_details"
- android:title="@string/view_contact_details"/>
+ android:title="@string/view_contact_details" />
<item
android:id="@+id/context_contact_block_unblock"
- android:title="@string/block_contact"/>
+ android:title="@string/block_contact" />
<item
android:id="@+id/context_delete_contact"
- android:title="@string/delete_contact"/>
+ android:title="@string/delete_contact" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/contact_details.xml b/src/main/res/menu/contact_details.xml
index 7fba3a4bd..328b46098 100644
--- a/src/main/res/menu/contact_details.xml
+++ b/src/main/res/menu/contact_details.xml
@@ -1,38 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_edit_contact"
android:icon="?attr/icon_edit"
android:orderInCategory="10"
android:showAsAction="always"
- android:title="@string/action_edit_contact"/>
+ android:title="@string/action_edit_contact" />
<item
android:id="@+id/action_share"
android:icon="?attr/icon_share"
android:showAsAction="always"
android:orderInCategory="15"
- android:title="@string/share_uri_with"/>
+ android:title="@string/share_uri_with" />
<item
android:id="@+id/action_delete_contact"
android:orderInCategory="10"
android:showAsAction="never"
- android:title="@string/action_delete_contact"/>
+ android:title="@string/action_delete_contact" />
<item
android:id="@+id/action_block"
android:orderInCategory="72"
android:showAsAction="always"
android:icon="@drawable/ic_speaker_notes_off_white_24dp"
- android:title="@string/action_block_contact"/>
+ android:title="@string/action_block_contact" />
<item
android:id="@+id/action_unblock"
android:orderInCategory="73"
android:showAsAction="always"
android:icon="@drawable/ic_speaker_notes_white_24dp"
- android:title="@string/action_unblock_contact"/>
+ android:title="@string/action_unblock_contact" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/conversations.xml b/src/main/res/menu/conversations.xml
index 4f0b98a34..22d8a513a 100644
--- a/src/main/res/menu/conversations.xml
+++ b/src/main/res/menu/conversations.xml
@@ -1,78 +1,78 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_add"
android:icon="?attr/icon_new"
android:orderInCategory="10"
android:showAsAction="always"
- android:title="@string/action_add"/>
+ android:title="@string/action_add" />
<item
android:id="@+id/action_security"
android:icon="?attr/icon_not_secure"
android:orderInCategory="20"
android:showAsAction="always"
- android:title="@string/action_secure"/>
+ android:title="@string/action_secure" />
<item
android:id="@+id/action_attach_file"
android:icon="?attr/icon_new_attachment"
android:orderInCategory="30"
android:showAsAction="always"
- android:title="@string/attach_file"/>
-<!-- <item
- android:id="@+id/action_contact_details"
- android:icon="?attr/icon_group"
- android:orderInCategory="40"
- android:showAsAction="never"
- android:title="@string/action_contact_details"/>
- <item
- android:id="@+id/action_muc_details"
- android:icon="?attr/icon_group"
- android:orderInCategory="40"
- android:showAsAction="never"
- android:title="@string/action_muc_details"/>
--->
+ android:title="@string/attach_file" />
+ <!-- <item
+ android:id="@+id/action_contact_details"
+ android:icon="?attr/icon_group"
+ android:orderInCategory="40"
+ android:showAsAction="never"
+ android:title="@string/action_contact_details"/>
+ <item
+ android:id="@+id/action_muc_details"
+ android:icon="?attr/icon_group"
+ android:orderInCategory="40"
+ android:showAsAction="never"
+ android:title="@string/action_muc_details"/>
+ -->
<item
android:id="@+id/action_invite"
android:orderInCategory="45"
android:showAsAction="never"
- android:title="@string/invite_contact"/>
+ android:title="@string/invite_contact" />
<item
android:id="@+id/action_clear_history"
android:orderInCategory="50"
android:showAsAction="never"
- android:title="@string/action_clear_history"/>
+ android:title="@string/action_clear_history" />
<item
android:id="@+id/action_archive_chat"
android:orderInCategory="60"
android:showAsAction="never"
- android:title="@string/action_end_conversation"/>
+ android:title="@string/action_end_conversation" />
<item
android:id="@+id/action_archive_muc"
android:orderInCategory="60"
android:showAsAction="never"
- android:title="@string/action_end_conversation_muc"/>
+ android:title="@string/action_end_conversation_muc" />
<item
android:id="@+id/action_mute"
android:orderInCategory="70"
android:showAsAction="never"
- android:title="@string/disable_notifications"/>
+ android:title="@string/disable_notifications" />
<item
android:id="@+id/action_unmute"
android:orderInCategory="71"
android:showAsAction="never"
- android:title="@string/enable_notifications"/>
+ android:title="@string/enable_notifications" />
<item
android:id="@+id/action_accounts"
android:orderInCategory="90"
android:showAsAction="never"
- android:title="@string/mgmt_account_edit"/>
+ android:title="@string/mgmt_account_edit" />
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:showAsAction="never"
- android:title="@string/action_settings"/>
+ android:title="@string/action_settings" />
<item
android:id="@+id/action_invite_user"
android:orderInCategory="100"
@@ -87,6 +87,6 @@
android:id="@+id/action_check_updates"
android:orderInCategory="100"
android:showAsAction="never"
- android:title="@string/action_check_update"/>
+ android:title="@string/action_check_update" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/editaccount.xml b/src/main/res/menu/editaccount.xml
index 5aa2fc04e..fa53c08de 100644
--- a/src/main/res/menu/editaccount.xml
+++ b/src/main/res/menu/editaccount.xml
@@ -4,59 +4,59 @@
android:id="@+id/action_change_presence"
android:showAsAction="always"
android:title="@string/change_presence"
- android:icon="@drawable/ic_new_releases_white_24dp"/>
+ android:icon="@drawable/ic_new_releases_white_24dp" />
<item
android:id="@+id/action_show_qr_code"
android:showAsAction="never"
- android:title="@string/show_qr_code"/>
+ android:title="@string/show_qr_code" />
<item
android:id="@+id/action_show_block_list"
android:showAsAction="always"
android:icon="@drawable/ic_speaker_notes_off_white_24dp"
- android:title="@string/show_block_list"/>
+ android:title="@string/show_block_list" />
<item
android:id="@+id/action_renew_certificate"
android:showAsAction="never"
android:title="@string/action_renew_certificate"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/action_server_info_show_more"
android:checkable="true"
android:checked="false"
android:showAsAction="never"
- android:title="@string/server_info_show_more"/>
+ android:title="@string/server_info_show_more" />
<item
android:id="@+id/action_mam_prefs"
android:icon="@drawable/ic_cloud_white_24dp"
android:showAsAction="always"
- android:title="@string/mam_prefs"/>
+ android:title="@string/mam_prefs" />
<item
android:id="@+id/action_show_password"
android:showAsAction="never"
- android:title="@string/show_password"/>
+ android:title="@string/show_password" />
<item
android:id="@+id/action_change_password_on_server"
android:showAsAction="always"
android:icon="@drawable/ic_vpn_key_white_24dp"
- android:title="@string/change_password"/>
+ android:title="@string/change_password" />
<item
android:id="@+id/mgmt_account_reconnect"
android:showAsAction="never"
- android:title="@string/mgmt_account_reconnect"/>
+ android:title="@string/mgmt_account_reconnect" />
<item
android:id="@+id/action_clear_devices"
android:showAsAction="never"
- android:title="@string/clear_other_devices"/>
+ android:title="@string/clear_other_devices" />
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:showAsAction="never"
- android:title="@string/action_settings"/>
+ android:title="@string/action_settings" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/encryption_choices.xml b/src/main/res/menu/encryption_choices.xml
index ab42a206b..89b6761ce 100644
--- a/src/main/res/menu/encryption_choices.xml
+++ b/src/main/res/menu/encryption_choices.xml
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <group android:checkableBehavior="single" >
+ <group android:checkableBehavior="single">
<item
android:id="@+id/encryption_choice_none"
- android:title="@string/encryption_choice_unencrypted"/>
+ 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" />
<item
android:id="@+id/encryption_choice_otr"
- android:title="@string/encryption_choice_otr"/>
+ android:title="@string/encryption_choice_otr" />
<item
android:id="@+id/encryption_choice_pgp"
- android:title="@string/encryption_choice_pgp"/>
+ android:title="@string/encryption_choice_pgp" />
</group>
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/manageaccounts.xml b/src/main/res/menu/manageaccounts.xml
index 2ce422a7b..38b82010b 100644
--- a/src/main/res/menu/manageaccounts.xml
+++ b/src/main/res/menu/manageaccounts.xml
@@ -1,21 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:id="@+id/action_add_account"
- android:icon="?attr/icon_add_person"
- android:showAsAction="always"
- android:title="@string/action_add_account"/>
- <item
- android:id="@+id/action_add_account_with_cert"
- android:showAsAction="never"
- android:icon="?attr/icon_add_person"
- android:title="@string/action_add_account_with_certificate"
- android:visible="true"/>
- <item
- android:id="@+id/action_settings"
- android:orderInCategory="100"
- android:showAsAction="never"
- android:title="@string/action_settings"/>
+ <item
+ android:id="@+id/action_add_account"
+ android:icon="?attr/icon_add_person"
+ android:showAsAction="always"
+ android:title="@string/action_add_account" />
+ <item
+ android:id="@+id/action_add_account_with_cert"
+ android:showAsAction="never"
+ android:icon="?attr/icon_add_person"
+ android:title="@string/action_add_account_with_certificate"
+ android:visible="true" />
+ <item
+ android:id="@+id/action_settings"
+ android:orderInCategory="100"
+ android:showAsAction="never"
+ android:title="@string/action_settings" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/manageaccounts_context.xml b/src/main/res/menu/manageaccounts_context.xml
index 1205b3291..beca7b8ec 100644
--- a/src/main/res/menu/manageaccounts_context.xml
+++ b/src/main/res/menu/manageaccounts_context.xml
@@ -1,21 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/mgmt_account_change_presence"
- android:title="@string/change_presence"/>
+ android:title="@string/change_presence" />
<item
android:id="@+id/mgmt_account_publish_avatar"
- android:title="@string/mgmt_account_publish_avatar"/>
+ android:title="@string/mgmt_account_publish_avatar" />
<item
android:id="@+id/mgmt_account_announce_pgp"
- android:title="@string/mgmt_account_publish_pgp"/>
+ android:title="@string/mgmt_account_publish_pgp" />
<item
android:id="@+id/mgmt_account_reconnect"
android:showAsAction="always"
- android:title="@string/mgmt_account_reconnect"/>
+ android:title="@string/mgmt_account_reconnect" />
<item
android:id="@+id/mgmt_account_delete"
- android:title="@string/mgmt_account_delete"/>
+ android:title="@string/mgmt_account_delete" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/message_context.xml b/src/main/res/menu/message_context.xml
index 7f95d712a..0c3220683 100644
--- a/src/main/res/menu/message_context.xml
+++ b/src/main/res/menu/message_context.xml
@@ -1,48 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/copy_text"
android:title="@string/copy_text"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/select_text"
android:title="@string/select_text"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/retry_decryption"
android:title="Retry decryption"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/correct_message"
android:title="@string/correct_message"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/share_with"
android:title="@string/share_with"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/copy_url"
android:title="@string/copy_original_url"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/show_error_message"
android:title="@string/show_error_message"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/send_again"
android:title="@string/send_again"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/download_file"
android:title="@string/download_x_file"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/cancel_transmission"
android:title="@string/cancel_transmission"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/delete_file"
android:title="@string/delete_x_file"
- android:visible="false"/>
+ android:visible="false" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/muc_details.xml b/src/main/res/menu/muc_details.xml
index 5ee463de5..cf0d6c3b8 100644
--- a/src/main/res/menu/muc_details.xml
+++ b/src/main/res/menu/muc_details.xml
@@ -6,30 +6,30 @@
android:icon="?attr/icon_edit"
android:orderInCategory="10"
android:showAsAction="always"
- android:title="@string/action_edit_subject"/>
+ android:title="@string/action_edit_subject" />
<item
android:id="@+id/action_share"
android:icon="?attr/icon_share"
android:showAsAction="always"
android:orderInCategory="15"
- android:title="@string/share_uri_with"/>
+ android:title="@string/share_uri_with" />
<item
android:id="@+id/action_save_as_bookmark"
android:orderInCategory="80"
android:showAsAction="never"
- android:title="@string/save_as_bookmark"/>
+ android:title="@string/save_as_bookmark" />
<item
android:id="@+id/action_delete_bookmark"
android:orderInCategory="80"
android:showAsAction="never"
- android:title="@string/delete_bookmark"/>
+ android:title="@string/delete_bookmark" />
<item
android:id="@+id/action_advanced_mode"
android:checkable="true"
android:checked="false"
android:orderInCategory="85"
android:showAsAction="never"
- android:title="@string/advanced_mode"/>
+ android:title="@string/advanced_mode" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/muc_details_context.xml b/src/main/res/menu/muc_details_context.xml
index edbb2264a..4760cec44 100644
--- a/src/main/res/menu/muc_details_context.xml
+++ b/src/main/res/menu/muc_details_context.xml
@@ -1,44 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item
+ <item
android:id="@+id/start_conversation"
android:title="@string/start_conversation"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/action_contact_details"
android:title="@string/action_contact_details"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/invite"
android:title="@string/invite_again"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/send_private_message"
android:title="@string/send_private_message"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/give_membership"
android:title="@string/grant_membership"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/give_admin_privileges"
android:title="@string/grant_admin_privileges"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/remove_admin_privileges"
android:title="@string/remove_admin_privileges"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/remove_membership"
android:title="@string/remove_membership"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/ban_from_conference"
android:title="@string/ban_from_conference"
- android:visible="false"/>
+ android:visible="false" />
<item
android:id="@+id/remove_from_room"
android:title="@string/remove_from_room"
- android:visible="false"/>
+ android:visible="false" />
</menu>
diff --git a/src/main/res/menu/omemo_key_context.xml b/src/main/res/menu/omemo_key_context.xml
index 2e2fc5dac..f63a3439b 100644
--- a/src/main/res/menu/omemo_key_context.xml
+++ b/src/main/res/menu/omemo_key_context.xml
@@ -2,8 +2,8 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/purge_omemo_key"
- android:title="@string/purge_key"/>
+ android:title="@string/purge_key" />
<item
android:id="@+id/copy_omemo_key"
- android:title="@string/copy_fingerprint"/>
+ android:title="@string/copy_fingerprint" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/publish_avatar.xml b/src/main/res/menu/publish_avatar.xml
index 1bffb2961..39478c0a4 100644
--- a/src/main/res/menu/publish_avatar.xml
+++ b/src/main/res/menu/publish_avatar.xml
@@ -4,5 +4,5 @@
android:id="@+id/action_crop_image"
android:showAsAction="always"
android:icon="@drawable/ic_crop_white_24dp"
- android:title="@string/select_image_and_crop"/>
+ android:title="@string/select_image_and_crop" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/select_multiple.xml b/src/main/res/menu/select_multiple.xml
index 4240849fd..efd6e4d0a 100644
--- a/src/main/res/menu/select_multiple.xml
+++ b/src/main/res/menu/select_multiple.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/selection_submit"
diff --git a/src/main/res/menu/share_with.xml b/src/main/res/menu/share_with.xml
index bc51f4625..ff87edc1a 100644
--- a/src/main/res/menu/share_with.xml
+++ b/src/main/res/menu/share_with.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_add"
android:icon="?attr/icon_new"
android:orderInCategory="10"
android:showAsAction="always"
- android:title="@string/action_add"/>
+ android:title="@string/action_add" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/showlocation.xml b/src/main/res/menu/showlocation.xml
index 227b9970f..3eb0647c9 100644
--- a/src/main/res/menu/showlocation.xml
+++ b/src/main/res/menu/showlocation.xml
@@ -4,5 +4,5 @@
android:id="@+id/action_navigate"
android:showAsAction="always"
android:title="@string/navigate"
- android:icon="@drawable/ic_navigation_white_24dp"/>
+ android:icon="@drawable/ic_navigation_white_24dp" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/start_conversation.xml b/src/main/res/menu/start_conversation.xml
index 305ffd223..27e9fc29b 100644
--- a/src/main/res/menu/start_conversation.xml
+++ b/src/main/res/menu/start_conversation.xml
@@ -20,10 +20,10 @@
<menu>
<item
android:id="@+id/action_join_conference"
- android:title="@string/join_conference"/>
+ android:title="@string/join_conference" />
<item
android:id="@+id/action_create_conference"
- android:title="@string/create_conference"/>
+ android:title="@string/create_conference" />
</menu>
</item>
@@ -38,6 +38,6 @@
android:checked="false"
android:orderInCategory="85"
android:showAsAction="never"
- android:title="@string/hide_offline"/>
+ android:title="@string/hide_offline" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/verification_choices.xml b/src/main/res/menu/verification_choices.xml
index e833ba79d..57d45dcde 100644
--- a/src/main/res/menu/verification_choices.xml
+++ b/src/main/res/menu/verification_choices.xml
@@ -3,10 +3,10 @@
<item
android:id="@+id/scan_fingerprint"
- android:title="@string/scan_qr_code"/>
+ android:title="@string/scan_qr_code" />
<item
android:id="@+id/ask_question"
- android:title="@string/ask_question"/>
+ android:title="@string/ask_question" />
<item
android:id="@+id/manual_verification"
diff --git a/src/main/res/menu/verify_otr.xml b/src/main/res/menu/verify_otr.xml
index 42b66ad5b..17b6562c2 100644
--- a/src/main/res/menu/verify_otr.xml
+++ b/src/main/res/menu/verify_otr.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:id="@+id/action_show_qr_code"
- android:title="@string/show_qr_code"
- android:showAsAction="never" />
+ <item
+ android:id="@+id/action_show_qr_code"
+ android:title="@string/show_qr_code"
+ android:showAsAction="never" />
<item
android:id="@+id/action_accounts"
diff --git a/src/main/res/values-v21/dimens.xml b/src/main/res/values-v21/dimens.xml
index b689d1008..4a3d93d1b 100644
--- a/src/main/res/values-v21/dimens.xml
+++ b/src/main/res/values-v21/dimens.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <dimen name="elv_undo_bottom_offset">63dp</dimen> <!-- 48dp + 15dp -->
- <dimen name="image_button_padding">12dp</dimen>
+ <dimen name="elv_undo_bottom_offset">63dp</dimen> <!-- 48dp + 15dp -->
+ <dimen name="image_button_padding">12dp</dimen>
</resources> \ No newline at end of file
diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml
index b6bd918c5..4c126478e 100644
--- a/src/main/res/values/arrays.xml
+++ b/src/main/res/values/arrays.xml
@@ -1,99 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string-array name="resources">
- <item>Mobile</item>
- <item>Phone</item>
- <item>Tablet</item>
- <item>@string/app_name</item>
- <item>Android</item>
- </string-array>
- <string-array name="filesizes">
- <item>@string/never</item>
- <item>256 KiB</item>
- <item>512 KiB</item>
- <item>1 MiB</item>
- <item>5 MiB</item>
- <item>10 MiB</item>
- </string-array>
- <string-array name="filesizes_values">
- <item>1</item>
- <item>262144</item>
- <item>524288</item>
- <item>1048576</item>
- <item>5242880</item>
- <item>10485760</item>
- </string-array>
- <string-array name="mute_options_descriptions">
- <item>@string/thirty_minutes</item>
- <item>@string/one_hour</item>
- <item>@string/two_hours</item>
- <item>@string/eight_hours</item>
- <item>@string/until_further_notice</item>
- </string-array>
+ <string-array name="resources">
+ <item>Mobile</item>
+ <item>Phone</item>
+ <item>Tablet</item>
+ <item>@string/app_name</item>
+ <item>Android</item>
+ </string-array>
+ <string-array name="filesizes">
+ <item>@string/never</item>
+ <item>256 KiB</item>
+ <item>512 KiB</item>
+ <item>1 MiB</item>
+ <item>5 MiB</item>
+ <item>10 MiB</item>
+ </string-array>
+ <string-array name="filesizes_values">
+ <item>1</item>
+ <item>262144</item>
+ <item>524288</item>
+ <item>1048576</item>
+ <item>5242880</item>
+ <item>10485760</item>
+ </string-array>
+ <string-array name="mute_options_descriptions">
+ <item>@string/thirty_minutes</item>
+ <item>@string/one_hour</item>
+ <item>@string/two_hours</item>
+ <item>@string/eight_hours</item>
+ <item>@string/until_further_notice</item>
+ </string-array>
- <integer-array name="mute_options_durations">
- <item>1800</item>
- <item>3600</item>
- <item>7200</item>
- <item>28800</item>
- <item>-1</item>
- </integer-array>
+ <integer-array name="mute_options_durations">
+ <item>1800</item>
+ <item>3600</item>
+ <item>7200</item>
+ <item>28800</item>
+ <item>-1</item>
+ </integer-array>
- <string-array name="quick_actions">
- <item>@string/none</item>
- <item>@string/recently_used</item>
- <item>@string/attach_take_picture</item>
- <item>@string/attach_choose_picture</item>
- <item>@string/attach_record_voice</item>
- <item>@string/send_location</item>
- </string-array>
+ <string-array name="quick_actions">
+ <item>@string/none</item>
+ <item>@string/recently_used</item>
+ <item>@string/attach_take_picture</item>
+ <item>@string/attach_choose_picture</item>
+ <item>@string/attach_record_voice</item>
+ <item>@string/send_location</item>
+ </string-array>
- <string-array name="quick_action_values">
- <item>none</item>
- <item>recent</item>
- <item>photo</item>
- <item>picture</item>
- <item>voice</item>
- <item>location</item>
- </string-array>
+ <string-array name="quick_action_values">
+ <item>none</item>
+ <item>recent</item>
+ <item>photo</item>
+ <item>picture</item>
+ <item>voice</item>
+ <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="picture_compression_values">
+ <item>never</item>
+ <item>auto</item>
+ <item>always</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="picture_compression_entries">
+ <item>@string/never</item>
+ <item>@string/automatically</item>
+ <item>@string/always</item>
+ </string-array>
- <string-array name="video_compression_values">
- <item>never</item>
- <item>auto</item>
- <item>always</item>
- </string-array>
+ <string-array name="video_compression_values">
+ <item>never</item>
+ <item>auto</item>
+ <item>always</item>
+ </string-array>
- <string-array name="video_compression_entries">
- <item>@string/never</item>
- <item>@string/automatically</item>
- <item>@string/always</item>
- </string-array>
+ <string-array name="video_compression_entries">
+ <item>@string/never</item>
+ <item>@string/automatically</item>
+ <item>@string/always</item>
+ </string-array>
- <string-array name="mam_prefs">
- <item>@string/never</item>
- <item>@string/contacts</item>
- <item>@string/always</item>
- </string-array>
+ <string-array name="mam_prefs">
+ <item>@string/never</item>
+ <item>@string/contacts</item>
+ <item>@string/always</item>
+ </string-array>
- <string-array name="presence_show_options">
- <item>@string/presence_chat</item>
- <item>@string/presence_online</item>
- <item>@string/presence_away</item>
- <item>@string/presence_xa</item>
- <item>@string/presence_dnd</item>
- </string-array>
+ <string-array name="presence_show_options">
+ <item>@string/presence_chat</item>
+ <item>@string/presence_online</item>
+ <item>@string/presence_away</item>
+ <item>@string/presence_xa</item>
+ <item>@string/presence_dnd</item>
+ </string-array>
</resources>
diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml
index 526ab66be..309c1fb30 100644
--- a/src/main/res/values/attrs.xml
+++ b/src/main/res/values/attrs.xml
@@ -5,34 +5,34 @@
<attr name="TextSizeBody" format="dimension" />
<attr name="TextSizeHeadline" format="dimension" />
- <attr name="icon_add_group" format="reference"/>
- <attr name="icon_add_person" format="reference"/>
- <attr name="icon_cancel" format="reference"/>
- <attr name="icon_chat" format="reference"/>
- <attr name="icon_copy" format="reference"/>
- <attr name="icon_discard" format="reference"/>
- <attr name="icon_download" format="reference"/>
- <attr name="icon_edit" format="reference"/>
- <attr name="icon_edit_dark" format="reference"/>
- <attr name="icon_done" format="reference"/>
- <attr name="icon_group" format="reference"/>
- <attr name="icon_new" format="reference"/>
- <attr name="icon_new_attachment" format="reference"/>
- <attr name="icon_not_secure" format="reference"/>
- <attr name="icon_refresh" format="reference"/>
- <attr name="icon_remove" format="reference"/>
- <attr name="icon_search" format="reference"/>
- <attr name="icon_secure" format="reference"/>
- <attr name="icon_settings" format="reference"/>
- <attr name="icon_share" format="reference"/>
- <attr name="icon_import_export" format="reference"/>
+ <attr name="icon_add_group" format="reference" />
+ <attr name="icon_add_person" format="reference" />
+ <attr name="icon_cancel" format="reference" />
+ <attr name="icon_chat" format="reference" />
+ <attr name="icon_copy" format="reference" />
+ <attr name="icon_discard" format="reference" />
+ <attr name="icon_download" format="reference" />
+ <attr name="icon_edit" format="reference" />
+ <attr name="icon_edit_dark" format="reference" />
+ <attr name="icon_done" format="reference" />
+ <attr name="icon_group" format="reference" />
+ <attr name="icon_new" format="reference" />
+ <attr name="icon_new_attachment" format="reference" />
+ <attr name="icon_not_secure" format="reference" />
+ <attr name="icon_refresh" format="reference" />
+ <attr name="icon_remove" format="reference" />
+ <attr name="icon_search" format="reference" />
+ <attr name="icon_secure" format="reference" />
+ <attr name="icon_settings" format="reference" />
+ <attr name="icon_share" format="reference" />
+ <attr name="icon_import_export" format="reference" />
- <attr name="icon_notifications" format="reference"/>
- <attr name="icon_notifications_off" format="reference"/>
- <attr name="icon_notifications_paused" format="reference"/>
- <attr name="icon_notifications_none" format="reference"/>
+ <attr name="icon_notifications" format="reference" />
+ <attr name="icon_notifications_off" format="reference" />
+ <attr name="icon_notifications_paused" format="reference" />
+ <attr name="icon_notifications_none" format="reference" />
- <attr name="dialog_horizontal_padding" format="dimension"/>
- <attr name="dialog_vertical_padding" format="dimension"/>
+ <attr name="dialog_horizontal_padding" format="dimension" />
+ <attr name="dialog_vertical_padding" format="dimension" />
</resources> \ No newline at end of file
diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml
index 4a7f235d9..a613cefb9 100644
--- a/src/main/res/values/colors.xml
+++ b/src/main/res/values/colors.xml
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <color name="realgreen">#ff259b24</color>
- <color name="primary">#ff2e4272</color>
- <color name="primary_dark">#ff08183e</color>
- <color name="dark">#ff000309</color>
- <color name="accent">#ff2e4272</color>
- <color name="black87">#de000309</color>
- <color name="black54">#8a000309</color>
+ <color name="realgreen">#ff259b24</color>
+ <color name="primary">#ff2e4272</color>
+ <color name="primary_dark">#ff08183e</color>
+ <color name="dark">#ff000309</color>
+ <color name="accent">#ff2e4272</color>
+ <color name="black87">#de000309</color>
+ <color name="black54">#8a000309</color>
<color name="black12">#1f000000</color>
- <color name="white">#ffe2e7f1</color>
- <color name="white70">#b2e2e7f1</color>
- <color name="grey50">#fafafa</color>
- <color name="grey200">#ffeeeeee</color>
- <color name="grey500">#ff9e9e9e</color>
- <color name="grey800">#ff424242</color>
+ <color name="white">#ffe2e7f1</color>
+ <color name="white70">#b2e2e7f1</color>
+ <color name="grey50">#fafafa</color>
+ <color name="grey200">#ffeeeeee</color>
+ <color name="grey500">#ff9e9e9e</color>
+ <color name="grey800">#ff424242</color>
<color name="red800">#ffc62828</color>
- <color name="orange500">#ffff9800</color>
+ <color name="orange500">#ffff9800</color>
</resources>
diff --git a/src/main/res/values/dimens.xml b/src/main/res/values/dimens.xml
index da216ef21..9ef329aa8 100644
--- a/src/main/res/values/dimens.xml
+++ b/src/main/res/values/dimens.xml
@@ -1,8 +1,8 @@
<resources>
- <!-- Default screen margins, per the Android Design guidelines. -->
- <dimen name="activity_horizontal_margin">8dp</dimen>
- <dimen name="activity_vertical_margin">8dp</dimen>
- <dimen name="infocard_padding">16dp</dimen>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">8dp</dimen>
+ <dimen name="activity_vertical_margin">8dp</dimen>
+ <dimen name="infocard_padding">16dp</dimen>
<dimen name="image_button_padding">8dp</dimen>
- <dimen name="elv_touch_slop">64dp</dimen>
+ <dimen name="elv_touch_slop">64dp</dimen>
</resources>
diff --git a/src/main/res/values/ids.xml b/src/main/res/values/ids.xml
index 84f9ac69e..78ccb5596 100644
--- a/src/main/res/values/ids.xml
+++ b/src/main/res/values/ids.xml
@@ -4,6 +4,6 @@
<item name="message_image_view" type="id" />
<item name="message_video_view" type="id" />
<item name="import_text" type="id" />
- <item type="id" name="TAG_ACCOUNT"/>
- <item type="id" name="TAG_FINGERPRINT"/>
+ <item type="id" name="TAG_ACCOUNT" />
+ <item type="id" name="TAG_FINGERPRINT" />
</resources> \ No newline at end of file
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 3bd18cfbf..21a09479c 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -1,280 +1,280 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="app_name" translatable="false">Pix-Art Messenger</string>
- <string name="action_settings">Settings</string>
- <string name="action_add">New conversation</string>
- <string name="action_accounts">Manage accounts</string>
- <string name="action_end_conversation">End this conversation</string>
- <string name="action_contact_details">Contact details</string>
- <string name="action_secure">Secure conversation</string>
- <string name="action_add_account">Add account</string>
- <string name="action_edit_contact">Edit name</string>
- <string name="action_add_phone_book">Add to address book</string>
- <string name="action_delete_contact">Delete from roster</string>
- <string name="action_block_contact">Block contact</string>
- <string name="action_unblock_contact">Unblock contact</string>
- <string name="action_block_domain">Block domain</string>
- <string name="action_unblock_domain">Unblock domain</string>
- <string name="title_activity_manage_accounts">Manage Accounts</string>
- <string name="title_activity_settings">Settings</string>
- <string name="title_activity_conference_details">Conference Details</string>
- <string name="title_activity_contact_details">Contact Details</string>
- <string name="title_activity_sharewith">Share with Conversation</string>
- <string name="title_activity_start_conversation">Start Conversation</string>
- <string name="title_activity_choose_contact">Choose contact</string>
- <string name="title_activity_block_list">Block list</string>
- <string name="just_now">just now</string>
- <string name="minute_ago">1 min ago</string>
- <string name="minutes_ago">%d mins ago</string>
- <string name="unread_conversations">unread Conversations</string>
- <string name="sending">sending…</string>
- <string name="message_decrypting">Decrypting message. Please wait…</string>
- <string name="pgp_message">OpenPGP encrypted message</string>
- <string name="nick_in_use">Nickname is already in use</string>
- <string name="admin">Admin</string>
- <string name="owner">Owner</string>
- <string name="moderator">Moderator</string>
- <string name="participant">Participant</string>
- <string name="visitor">Visitor</string>
- <string name="remove_contact_text">Would you like to remove %s from your roster? The conversation associated with this contact will not be removed.</string>
- <string name="block_contact_text">Would you like to block %s from sending you messages?</string>
- <string name="unblock_contact_text">Would you like to unblock %s and allow them to send you messages?</string>
- <string name="block_domain_text">Block all contacts from %s?</string>
- <string name="unblock_domain_text">Unblock all contacts from %s?</string>
- <string name="contact_blocked">Contact blocked</string>
- <string name="remove_bookmark_text">Would you like to remove %s as a bookmark? The conversation associated with this bookmark will not be removed.</string>
- <string name="register_account">Register new account on server</string>
- <string name="change_password_on_server">Change password on server</string>
- <string name="share_with">Share with…</string>
- <string name="start_conversation">Start Conversation</string>
- <string name="invite_contact">Invite Contact</string>
- <string name="contacts">Contacts</string>
- <string name="cancel">Cancel</string>
- <string name="set">Set</string>
- <string name="add">Add</string>
- <string name="edit">Edit</string>
- <string name="delete">Delete</string>
- <string name="block">Block</string>
- <string name="unblock">Unblock</string>
- <string name="save">Save</string>
- <string name="ok">OK</string>
- <string name="crash_report_title">Pix-Art Messenger has crashed</string>
- <string name="crash_report_message">By sending in stack traces you are helping the ongoing development of Pix-Art Messenger\n<b>Warning:</b> This will use your XMPP account to send the stack trace to the developer.</string>
- <string name="send_now">Send now</string>
- <string name="send_never">Never ask again</string>
- <string name="problem_connecting_to_account">Unable to connect to account</string>
- <string name="problem_connecting_to_accounts">Unable to connect to multiple accounts</string>
- <string name="touch_to_fix">Touch here to manage your accounts</string>
- <string name="attach_file">Attach file</string>
- <string name="not_in_roster">The contact is not in your roster. Would you like to add it?</string>
- <string name="add_contact">Add contact</string>
- <string name="send_failed">delivery failed</string>
- <string name="preparing_image">Preparing image for transmission</string>
- <string name="preparing_images">Preparing images for transmission</string>
- <string name="sharing_files_please_wait">Sharing files. Please wait…</string>
- <string name="action_clear_history">Clear history</string>
- <string name="clear_conversation_history">Clear Conversation History</string>
- <string name="clear_histor_msg">Do you want to delete all messages within this Conversation?\n\n<b>Warning:</b> This will not influence messages stored on other devices or servers.</string>
- <string name="delete_messages">Delete messages</string>
- <string name="also_end_conversation">End this conversation afterwards</string>
- <string name="choose_presence">Choose device</string>
- <string name="send_unencrypted_message">Send unencrypted message</string>
- <string name="send_message_to_x">Send message to %s</string>
- <string name="send_otr_message">Send OTR encrypted message</string>
- <string name="send_omemo_message">Send OMEMO encrypted message</string>
- <string name="send_omemo_x509_message">Send v\\OMEMO encrypted message</string>
- <string name="send_pgp_message">Send OpenPGP encrypted message</string>
- <string name="your_nick_has_been_changed">Your nickname has been changed</string>
- <string name="send_unencrypted">Send unencrypted</string>
- <string name="decryption_failed">Decryption failed. Maybe you don’t have the proper private key.</string>
- <string name="openkeychain_required">OpenKeychain</string>
- <string name="openkeychain_required_long">Pix-Art Messenger utilizes a third party app called <b>OpenKeychain</b> to encrypt and decrypt messages and to manage your public keys.\n\nOpenKeychain is licensed under GPLv3 and available on F-Droid and Google Play.\n\n<small>(Please restart Pix-Art Messenger afterwards.)</small></string>
- <string name="restart">Restart</string>
- <string name="install">Install</string>
- <string name="openkeychain_not_installed">Please install OpenKeychain</string>
- <string name="offering">offering…</string>
- <string name="waiting">waiting…</string>
- <string name="no_pgp_key">No OpenPGP Key found</string>
- <string name="contact_has_no_pgp_key">Pix-Art Messenger is unable to encrypt your messages because your contact is not announcing his or hers public key.\n\n<small>Please ask your contact to setup OpenPGP.</small></string>
- <string name="no_pgp_keys">No OpenPGP Keys found</string>
- <string name="pref_general">General</string>
- <string name="pref_xmpp_resource">XMPP resource</string>
- <string name="pref_xmpp_resource_summary">The name this client identifies itself with</string>
- <string name="pref_accept_files_wifi">Accept files in WiFi connections</string>
- <string name="pref_accept_files_summary_wifi">When connected to wifi automatically accept files smaller than…</string>
- <string name="pref_accept_files_mobile">Accept files in mobile connections</string>
- <string name="pref_accept_files_summary_mobile">When connected with mobile data automatically accept files smaller than…</string>
- <string name="pref_accept_files_mobileroaming">Accept files in mobile roaming connections</string>
- <string name="pref_accept_files_summary_mobileroaming">When connected with mobile data in roaming automatically accept files smaller than…</string>
- <string name="pref_attachments">Attachments</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>
- <string name="pref_vibrate">Vibrate</string>
- <string name="pref_vibrate_summary">Vibrate when a new message arrives</string>
- <string name="pref_led">LED Notification</string>
- <string name="pref_led_summary">Blink notification light when a new message arrives</string>
- <string name="pref_sound">Ringtone</string>
- <string name="pref_sound_summary">Play sound when a new message arrives</string>
- <string name="pref_send_crash">Send crash reports</string>
- <string name="pref_send_crash_summary">By sending in stack traces you are helping the ongoing development of Pix-Art Messenger</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_ui_options">UI</string>
- <string name="openpgp_error">OpenKeychain reported an error</string>
- <string name="accept">Accept</string>
- <string name="error">An error has occurred</string>
- <string name="pref_grant_presence_updates">Grant presence updates</string>
- <string name="pref_grant_presence_updates_summary">Preemptively grant and ask for presence subscription for contacts you created</string>
- <string name="your_account">Your account</string>
- <string name="send_presence_updates">Send presence updates</string>
- <string name="receive_presence_updates">Receive presence updates</string>
- <string name="ask_for_presence_updates">Ask for presence updates</string>
- <string name="attach_choose_picture">Choose picture</string>
- <string name="attach_take_picture">Take picture</string>
- <string name="preemptively_grant">Preemptively grant subscription request</string>
- <string name="error_not_an_image_file">The file you selected is not an image</string>
- <string name="error_compressing_image">Error while converting the image file</string>
- <string name="error_file_not_found">File not found</string>
- <string name="error_io_exception">General I/O error. Maybe you ran out of storage space?</string>
- <string name="error_security_exception_during_image_copy">The app you used to select this image did not provide us with enough permissions to read the file.\n\n<small>Use a different file manager to choose an image</small></string>
- <string name="account_status_unknown">Unknown</string>
- <string name="account_status_disabled">Temporarily disabled</string>
- <string name="account_status_online">Online</string>
- <string name="account_status_connecting">Connecting\u2026</string>
- <string name="account_status_offline">Offline</string>
- <string name="account_status_unauthorized">Unauthorized</string>
- <string name="account_status_not_found">Server not found</string>
- <string name="account_status_no_internet">No connectivity</string>
- <string name="account_status_regis_fail">Registration failed</string>
- <string name="account_status_regis_conflict">Username already in use</string>
- <string name="account_status_regis_success">Registration completed</string>
- <string name="account_status_regis_not_sup">Server does not support registration</string>
- <string name="account_status_security_error">Security error</string>
- <string name="account_status_policy_violation">Policy violation</string>
- <string name="account_status_incompatible_server">Incompatible server</string>
- <string name="account_status_stream_error">Stream error</string>
- <string name="encryption_choice_unencrypted">Unencrypted</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">Edit account</string>
- <string name="mgmt_account_delete">Delete account</string>
- <string name="mgmt_account_publish_avatar">Publish avatar</string>
- <string name="mgmt_account_publish_pgp">Publish OpenPGP public key</string>
- <string name="openpgp_has_been_published">OpenPGP public key has been published.</string>
- <string name="republish_pgp_keys">Remember to republish your OpenPGP public keys!</string>
- <string name="mgmt_account_are_you_sure">Are you sure?</string>
- <string name="mgmt_account_delete_confirm_text">If you delete your account your entire conversation history will be lost</string>
- <string name="attach_record_voice">Record voice</string>
- <string name="account_settings_jabber_id">Jabber ID</string>
- <string name="account_settings_password">Password</string>
- <string name="account_settings_example_jabber_id">username@pix-art.de</string>
- <string name="account_settings_confirm_password">Confirm password</string>
- <string name="password">Password</string>
- <string name="confirm_password">Confirm password</string>
- <string name="passwords_do_not_match">Passwords do not match</string>
- <string name="invalid_jid">This is not a valid Jabber ID</string>
- <string name="error_out_of_memory">Out of memory. Image is too large</string>
- <string name="add_phone_book_text">Do you want to add %s to your address book?</string>
- <string name="server_info_show_more">Server info</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">available</string>
- <string name="server_info_unavailable">unavailable</string>
- <string name="missing_public_keys">Missing public key announcements</string>
- <string name="last_seen_now">online</string>
- <string name="last_seen_min">1 minute ago</string>
- <string name="last_seen_mins">%d minutes ago</string>
- <string name="last_seen_hour">1 hour ago</string>
- <string name="last_seen_hours">%d hours ago</string>
- <string name="last_seen_day">1 day ago</string>
- <string name="last_seen_days">%d days ago</string>
- <string name="install_openkeychain">Encrypted message. Please install OpenKeychain to decrypt.</string>
- <string name="unknown_otr_fingerprint">Unknown OTR fingerprint</string>
- <string name="openpgp_messages_found">OpenPGP encrypted messages found</string>
- <string name="your_fingerprint">Your fingerprint</string>
- <string name="otr_fingerprint">OTR fingerprint</string>
- <string name="otr_fingerprint_selected_message">OTR fingerprint of message</string>
- <string name="openpgp_key_id">OpenPGP Key ID</string>
- <string name="omemo_fingerprint">OMEMO fingerprint</string>
- <string name="omemo_fingerprint_x509">v\\OMEMO fingerprint</string>
- <string name="omemo_fingerprint_selected_message">OMEMO fingerprint of message</string>
- <string name="omemo_fingerprint_x509_selected_message">v\\OMEMO fingerprint of message</string>
- <string name="this_device_omemo_fingerprint">Own OMEMO fingerprint</string>
- <string name="other_devices">Other devices</string>
- <string name="trust_omemo_fingerprints">Trust OMEMO Fingerprints</string>
- <string name="fetching_keys">Fetching keys…</string>
- <string name="done">Done</string>
- <string name="verify">Verify</string>
- <string name="decrypt">Decrypt</string>
- <string name="conferences">Conferences</string>
- <string name="search">Search</string>
- <string name="create_contact">Create Contact</string>
- <string name="enter_contact">Enter Contact</string>
- <string name="join_conference">Join Conference</string>
- <string name="delete_contact">Delete Contact</string>
- <string name="view_contact_details">View contact details</string>
- <string name="block_contact">Block contact</string>
- <string name="unblock_contact">Unblock contact</string>
- <string name="create">Create</string>
- <string name="select">Select</string>
- <string name="contact_already_exists">The contact already exists</string>
- <string name="join">Join</string>
- <string name="conference_address">Conference address</string>
- <string name="conference_address_example">conference@room.pix-art.de</string>
- <string name="save_as_bookmark">Save as bookmark</string>
- <string name="delete_bookmark">Delete bookmark</string>
- <string name="bookmark_already_exists">This bookmark already exists</string>
- <string name="action_edit_subject">Edit conference subject</string>
- <string name="edit_subject_hint">The subject of this conference</string>
- <string name="joining_conference">Joining conference…</string>
- <string name="leave">Leave</string>
- <string name="contact_added_you">Contact added you to contact list</string>
- <string name="add_back">Add back</string>
- <string name="contact_has_read_up_to_this_point">%s has read up to this point</string>
- <string name="publish">Publish</string>
- <string name="touch_to_choose_picture">Touch avatar to select picture from gallery</string>
- <string name="publish_avatar_explanation">Please note: Everyone subscribed to your presence updates will be allowed to see this picture.</string>
- <string name="publishing">Publishing…</string>
- <string name="error_publish_avatar_server_reject">The server rejected your publication</string>
- <string name="error_publish_avatar_converting">Something went wrong while converting your picture</string>
- <string name="error_saving_avatar">Could not save avatar to disk</string>
- <string name="or_long_press_for_default">(Or long press to bring back default)</string>
- <string name="error_publish_avatar_no_server_support">Your server does not support the publication of avatars</string>
- <string name="private_message">whispered</string>
- <string name="private_message_to">to %s</string>
- <string name="send_private_message_to">Send private message to %s</string>
- <string name="connect">Connect</string>
- <string name="account_already_exists">This account already exists</string>
- <string name="next">Next</string>
- <string name="server_info_session_established">Current session established</string>
- <string name="skip">Skip</string>
- <string name="disable_notifications">Disable notifications</string>
- <string name="enable">Enable</string>
- <string name="conference_requires_password">Conference requires password</string>
- <string name="enter_password">Enter password</string>
- <string name="request_presence_updates">Please request presence updates from your contact first.\n\n<small>This will be used to determine what client(s) your contact is using.</small></string>
- <string name="request_now">Request now</string>
- <string name="delete_fingerprint">Delete Fingerprint</string>
- <string name="sure_delete_fingerprint">Are you sure you would like to delete this fingerprint?</string>
- <string name="ignore">Ignore</string>
- <string name="without_mutual_presence_updates"><b>Warning:</b> Sending this without mutual presence updates could cause unexpected problems.\n\n<small>Go to contact details to verify your presence subscriptions.</small></string>
- <string name="pref_security_settings">Security and data protection</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>
- <string name="title_activity_about">About Pix-Art Messenger</string>
- <string name="pref_about_conversations_summary">Build and licensing information</string>
+ <string name="app_name" translatable="false">Pix-Art Messenger</string>
+ <string name="action_settings">Settings</string>
+ <string name="action_add">New conversation</string>
+ <string name="action_accounts">Manage accounts</string>
+ <string name="action_end_conversation">End this conversation</string>
+ <string name="action_contact_details">Contact details</string>
+ <string name="action_secure">Secure conversation</string>
+ <string name="action_add_account">Add account</string>
+ <string name="action_edit_contact">Edit name</string>
+ <string name="action_add_phone_book">Add to address book</string>
+ <string name="action_delete_contact">Delete from roster</string>
+ <string name="action_block_contact">Block contact</string>
+ <string name="action_unblock_contact">Unblock contact</string>
+ <string name="action_block_domain">Block domain</string>
+ <string name="action_unblock_domain">Unblock domain</string>
+ <string name="title_activity_manage_accounts">Manage Accounts</string>
+ <string name="title_activity_settings">Settings</string>
+ <string name="title_activity_conference_details">Conference Details</string>
+ <string name="title_activity_contact_details">Contact Details</string>
+ <string name="title_activity_sharewith">Share with Conversation</string>
+ <string name="title_activity_start_conversation">Start Conversation</string>
+ <string name="title_activity_choose_contact">Choose contact</string>
+ <string name="title_activity_block_list">Block list</string>
+ <string name="just_now">just now</string>
+ <string name="minute_ago">1 min ago</string>
+ <string name="minutes_ago">%d mins ago</string>
+ <string name="unread_conversations">unread Conversations</string>
+ <string name="sending">sending…</string>
+ <string name="message_decrypting">Decrypting message. Please wait…</string>
+ <string name="pgp_message">OpenPGP encrypted message</string>
+ <string name="nick_in_use">Nickname is already in use</string>
+ <string name="admin">Admin</string>
+ <string name="owner">Owner</string>
+ <string name="moderator">Moderator</string>
+ <string name="participant">Participant</string>
+ <string name="visitor">Visitor</string>
+ <string name="remove_contact_text">Would you like to remove %s from your roster? The conversation associated with this contact will not be removed.</string>
+ <string name="block_contact_text">Would you like to block %s from sending you messages?</string>
+ <string name="unblock_contact_text">Would you like to unblock %s and allow them to send you messages?</string>
+ <string name="block_domain_text">Block all contacts from %s?</string>
+ <string name="unblock_domain_text">Unblock all contacts from %s?</string>
+ <string name="contact_blocked">Contact blocked</string>
+ <string name="remove_bookmark_text">Would you like to remove %s as a bookmark? The conversation associated with this bookmark will not be removed.</string>
+ <string name="register_account">Register new account on server</string>
+ <string name="change_password_on_server">Change password on server</string>
+ <string name="share_with">Share with…</string>
+ <string name="start_conversation">Start Conversation</string>
+ <string name="invite_contact">Invite Contact</string>
+ <string name="contacts">Contacts</string>
+ <string name="cancel">Cancel</string>
+ <string name="set">Set</string>
+ <string name="add">Add</string>
+ <string name="edit">Edit</string>
+ <string name="delete">Delete</string>
+ <string name="block">Block</string>
+ <string name="unblock">Unblock</string>
+ <string name="save">Save</string>
+ <string name="ok">OK</string>
+ <string name="crash_report_title">Pix-Art Messenger has crashed</string>
+ <string name="crash_report_message">By sending in stack traces you are helping the ongoing development of Pix-Art Messenger\n<b>Warning:</b> This will use your XMPP account to send the stack trace to the developer.</string>
+ <string name="send_now">Send now</string>
+ <string name="send_never">Never ask again</string>
+ <string name="problem_connecting_to_account">Unable to connect to account</string>
+ <string name="problem_connecting_to_accounts">Unable to connect to multiple accounts</string>
+ <string name="touch_to_fix">Touch here to manage your accounts</string>
+ <string name="attach_file">Attach file</string>
+ <string name="not_in_roster">The contact is not in your roster. Would you like to add it?</string>
+ <string name="add_contact">Add contact</string>
+ <string name="send_failed">delivery failed</string>
+ <string name="preparing_image">Preparing image for transmission</string>
+ <string name="preparing_images">Preparing images for transmission</string>
+ <string name="sharing_files_please_wait">Sharing files. Please wait…</string>
+ <string name="action_clear_history">Clear history</string>
+ <string name="clear_conversation_history">Clear Conversation History</string>
+ <string name="clear_histor_msg">Do you want to delete all messages within this Conversation?\n\n<b>Warning:</b> This will not influence messages stored on other devices or servers.</string>
+ <string name="delete_messages">Delete messages</string>
+ <string name="also_end_conversation">End this conversation afterwards</string>
+ <string name="choose_presence">Choose device</string>
+ <string name="send_unencrypted_message">Send unencrypted message</string>
+ <string name="send_message_to_x">Send message to %s</string>
+ <string name="send_otr_message">Send OTR encrypted message</string>
+ <string name="send_omemo_message">Send OMEMO encrypted message</string>
+ <string name="send_omemo_x509_message">Send v\\OMEMO encrypted message</string>
+ <string name="send_pgp_message">Send OpenPGP encrypted message</string>
+ <string name="your_nick_has_been_changed">Your nickname has been changed</string>
+ <string name="send_unencrypted">Send unencrypted</string>
+ <string name="decryption_failed">Decryption failed. Maybe you don’t have the proper private key.</string>
+ <string name="openkeychain_required">OpenKeychain</string>
+ <string name="openkeychain_required_long">Pix-Art Messenger utilizes a third party app called <b>OpenKeychain</b> to encrypt and decrypt messages and to manage your public keys.\n\nOpenKeychain is licensed under GPLv3 and available on F-Droid and Google Play.\n\n<small>(Please restart Pix-Art Messenger afterwards.)</small></string>
+ <string name="restart">Restart</string>
+ <string name="install">Install</string>
+ <string name="openkeychain_not_installed">Please install OpenKeychain</string>
+ <string name="offering">offering…</string>
+ <string name="waiting">waiting…</string>
+ <string name="no_pgp_key">No OpenPGP Key found</string>
+ <string name="contact_has_no_pgp_key">Pix-Art Messenger is unable to encrypt your messages because your contact is not announcing his or hers public key.\n\n<small>Please ask your contact to setup OpenPGP.</small></string>
+ <string name="no_pgp_keys">No OpenPGP Keys found</string>
+ <string name="pref_general">General</string>
+ <string name="pref_xmpp_resource">XMPP resource</string>
+ <string name="pref_xmpp_resource_summary">The name this client identifies itself with</string>
+ <string name="pref_accept_files_wifi">Accept files in WiFi connections</string>
+ <string name="pref_accept_files_summary_wifi">When connected to wifi automatically accept files smaller than…</string>
+ <string name="pref_accept_files_mobile">Accept files in mobile connections</string>
+ <string name="pref_accept_files_summary_mobile">When connected with mobile data automatically accept files smaller than…</string>
+ <string name="pref_accept_files_mobileroaming">Accept files in mobile roaming connections</string>
+ <string name="pref_accept_files_summary_mobileroaming">When connected with mobile data in roaming automatically accept files smaller than…</string>
+ <string name="pref_attachments">Attachments</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>
+ <string name="pref_vibrate">Vibrate</string>
+ <string name="pref_vibrate_summary">Vibrate when a new message arrives</string>
+ <string name="pref_led">LED Notification</string>
+ <string name="pref_led_summary">Blink notification light when a new message arrives</string>
+ <string name="pref_sound">Ringtone</string>
+ <string name="pref_sound_summary">Play sound when a new message arrives</string>
+ <string name="pref_send_crash">Send crash reports</string>
+ <string name="pref_send_crash_summary">By sending in stack traces you are helping the ongoing development of Pix-Art Messenger</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_ui_options">UI</string>
+ <string name="openpgp_error">OpenKeychain reported an error</string>
+ <string name="accept">Accept</string>
+ <string name="error">An error has occurred</string>
+ <string name="pref_grant_presence_updates">Grant presence updates</string>
+ <string name="pref_grant_presence_updates_summary">Preemptively grant and ask for presence subscription for contacts you created</string>
+ <string name="your_account">Your account</string>
+ <string name="send_presence_updates">Send presence updates</string>
+ <string name="receive_presence_updates">Receive presence updates</string>
+ <string name="ask_for_presence_updates">Ask for presence updates</string>
+ <string name="attach_choose_picture">Choose picture</string>
+ <string name="attach_take_picture">Take picture</string>
+ <string name="preemptively_grant">Preemptively grant subscription request</string>
+ <string name="error_not_an_image_file">The file you selected is not an image</string>
+ <string name="error_compressing_image">Error while converting the image file</string>
+ <string name="error_file_not_found">File not found</string>
+ <string name="error_io_exception">General I/O error. Maybe you ran out of storage space?</string>
+ <string name="error_security_exception_during_image_copy">The app you used to select this image did not provide us with enough permissions to read the file.\n\n<small>Use a different file manager to choose an image</small></string>
+ <string name="account_status_unknown">Unknown</string>
+ <string name="account_status_disabled">Temporarily disabled</string>
+ <string name="account_status_online">Online</string>
+ <string name="account_status_connecting">Connecting\u2026</string>
+ <string name="account_status_offline">Offline</string>
+ <string name="account_status_unauthorized">Unauthorized</string>
+ <string name="account_status_not_found">Server not found</string>
+ <string name="account_status_no_internet">No connectivity</string>
+ <string name="account_status_regis_fail">Registration failed</string>
+ <string name="account_status_regis_conflict">Username already in use</string>
+ <string name="account_status_regis_success">Registration completed</string>
+ <string name="account_status_regis_not_sup">Server does not support registration</string>
+ <string name="account_status_security_error">Security error</string>
+ <string name="account_status_policy_violation">Policy violation</string>
+ <string name="account_status_incompatible_server">Incompatible server</string>
+ <string name="account_status_stream_error">Stream error</string>
+ <string name="encryption_choice_unencrypted">Unencrypted</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">Edit account</string>
+ <string name="mgmt_account_delete">Delete account</string>
+ <string name="mgmt_account_publish_avatar">Publish avatar</string>
+ <string name="mgmt_account_publish_pgp">Publish OpenPGP public key</string>
+ <string name="openpgp_has_been_published">OpenPGP public key has been published.</string>
+ <string name="republish_pgp_keys">Remember to republish your OpenPGP public keys!</string>
+ <string name="mgmt_account_are_you_sure">Are you sure?</string>
+ <string name="mgmt_account_delete_confirm_text">If you delete your account your entire conversation history will be lost</string>
+ <string name="attach_record_voice">Record voice</string>
+ <string name="account_settings_jabber_id">Jabber ID</string>
+ <string name="account_settings_password">Password</string>
+ <string name="account_settings_example_jabber_id">username@pix-art.de</string>
+ <string name="account_settings_confirm_password">Confirm password</string>
+ <string name="password">Password</string>
+ <string name="confirm_password">Confirm password</string>
+ <string name="passwords_do_not_match">Passwords do not match</string>
+ <string name="invalid_jid">This is not a valid Jabber ID</string>
+ <string name="error_out_of_memory">Out of memory. Image is too large</string>
+ <string name="add_phone_book_text">Do you want to add %s to your address book?</string>
+ <string name="server_info_show_more">Server info</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">available</string>
+ <string name="server_info_unavailable">unavailable</string>
+ <string name="missing_public_keys">Missing public key announcements</string>
+ <string name="last_seen_now">online</string>
+ <string name="last_seen_min">1 minute ago</string>
+ <string name="last_seen_mins">%d minutes ago</string>
+ <string name="last_seen_hour">1 hour ago</string>
+ <string name="last_seen_hours">%d hours ago</string>
+ <string name="last_seen_day">1 day ago</string>
+ <string name="last_seen_days">%d days ago</string>
+ <string name="install_openkeychain">Encrypted message. Please install OpenKeychain to decrypt.</string>
+ <string name="unknown_otr_fingerprint">Unknown OTR fingerprint</string>
+ <string name="openpgp_messages_found">OpenPGP encrypted messages found</string>
+ <string name="your_fingerprint">Your fingerprint</string>
+ <string name="otr_fingerprint">OTR fingerprint</string>
+ <string name="otr_fingerprint_selected_message">OTR fingerprint of message</string>
+ <string name="openpgp_key_id">OpenPGP Key ID</string>
+ <string name="omemo_fingerprint">OMEMO fingerprint</string>
+ <string name="omemo_fingerprint_x509">v\\OMEMO fingerprint</string>
+ <string name="omemo_fingerprint_selected_message">OMEMO fingerprint of message</string>
+ <string name="omemo_fingerprint_x509_selected_message">v\\OMEMO fingerprint of message</string>
+ <string name="this_device_omemo_fingerprint">Own OMEMO fingerprint</string>
+ <string name="other_devices">Other devices</string>
+ <string name="trust_omemo_fingerprints">Trust OMEMO Fingerprints</string>
+ <string name="fetching_keys">Fetching keys…</string>
+ <string name="done">Done</string>
+ <string name="verify">Verify</string>
+ <string name="decrypt">Decrypt</string>
+ <string name="conferences">Conferences</string>
+ <string name="search">Search</string>
+ <string name="create_contact">Create Contact</string>
+ <string name="enter_contact">Enter Contact</string>
+ <string name="join_conference">Join Conference</string>
+ <string name="delete_contact">Delete Contact</string>
+ <string name="view_contact_details">View contact details</string>
+ <string name="block_contact">Block contact</string>
+ <string name="unblock_contact">Unblock contact</string>
+ <string name="create">Create</string>
+ <string name="select">Select</string>
+ <string name="contact_already_exists">The contact already exists</string>
+ <string name="join">Join</string>
+ <string name="conference_address">Conference address</string>
+ <string name="conference_address_example">conference@room.pix-art.de</string>
+ <string name="save_as_bookmark">Save as bookmark</string>
+ <string name="delete_bookmark">Delete bookmark</string>
+ <string name="bookmark_already_exists">This bookmark already exists</string>
+ <string name="action_edit_subject">Edit conference subject</string>
+ <string name="edit_subject_hint">The subject of this conference</string>
+ <string name="joining_conference">Joining conference…</string>
+ <string name="leave">Leave</string>
+ <string name="contact_added_you">Contact added you to contact list</string>
+ <string name="add_back">Add back</string>
+ <string name="contact_has_read_up_to_this_point">%s has read up to this point</string>
+ <string name="publish">Publish</string>
+ <string name="touch_to_choose_picture">Touch avatar to select picture from gallery</string>
+ <string name="publish_avatar_explanation">Please note: Everyone subscribed to your presence updates will be allowed to see this picture.</string>
+ <string name="publishing">Publishing…</string>
+ <string name="error_publish_avatar_server_reject">The server rejected your publication</string>
+ <string name="error_publish_avatar_converting">Something went wrong while converting your picture</string>
+ <string name="error_saving_avatar">Could not save avatar to disk</string>
+ <string name="or_long_press_for_default">(Or long press to bring back default)</string>
+ <string name="error_publish_avatar_no_server_support">Your server does not support the publication of avatars</string>
+ <string name="private_message">whispered</string>
+ <string name="private_message_to">to %s</string>
+ <string name="send_private_message_to">Send private message to %s</string>
+ <string name="connect">Connect</string>
+ <string name="account_already_exists">This account already exists</string>
+ <string name="next">Next</string>
+ <string name="server_info_session_established">Current session established</string>
+ <string name="skip">Skip</string>
+ <string name="disable_notifications">Disable notifications</string>
+ <string name="enable">Enable</string>
+ <string name="conference_requires_password">Conference requires password</string>
+ <string name="enter_password">Enter password</string>
+ <string name="request_presence_updates">Please request presence updates from your contact first.\n\n<small>This will be used to determine what client(s) your contact is using.</small></string>
+ <string name="request_now">Request now</string>
+ <string name="delete_fingerprint">Delete Fingerprint</string>
+ <string name="sure_delete_fingerprint">Are you sure you would like to delete this fingerprint?</string>
+ <string name="ignore">Ignore</string>
+ <string name="without_mutual_presence_updates"><b>Warning:</b> Sending this without mutual presence updates could cause unexpected problems.\n\n<small>Go to contact details to verify your presence subscriptions.</small></string>
+ <string name="pref_security_settings">Security and data protection</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>
+ <string name="title_activity_about">About Pix-Art Messenger</string>
+ <string name="pref_about_conversations_summary">Build and licensing information</string>
<string name="pref_about_message" translatable="false">Pix-Art Messenger
\n\nCopyright © 2014-2016 Christian Schneppe
\n\nhttp://jabber.pix-art.de
@@ -318,397 +318,397 @@
\n\nhttps://github.com/blazsolar/FlowLayout\n(Apache License, Version 2.0)
\n\nhttps://github.com/ankushsachdeva/emojicon\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>
- <string name="title_pref_quiet_hours_end_time">End time</string>
- <string name="title_pref_enable_quiet_hours">Enable quiet hours</string>
- <string name="pref_quiet_hours_summary">Notifications will be silenced during quiet hours</string>
- <string name="pref_use_larger_font">Increase font size</string>
- <string name="pref_use_larger_font_summary">Use larger font sizes across the entire app</string>
- <string name="pref_use_send_button_to_indicate_status">Send button indicates status</string>
- <string name="pref_use_indicate_received">Request message receipts</string>
- <string name="pref_use_indicate_received_summary">Received messages will be marked with a green tick if supported</string>
- <string name="pref_use_send_button_to_indicate_status_summary">Colorize send button to indicate contact status</string>
- <string name="pref_expert_options_other">Other</string>
- <string name="pref_autojoin">Automatically join conferences</string>
- <string name="pref_autojoin_summary">Respect the autojoin flag in conference bookmarks</string>
- <string name="toast_message_otr_fingerprint">OTR fingerprint copied to clipboard!</string>
- <string name="toast_message_omemo_fingerprint">OMEMO fingerprint copied to clipboard!</string>
- <string name="conference_banned">You are banned from this conference</string>
- <string name="conference_members_only">This conference is members only</string>
- <string name="conference_kicked">You have been kicked from this conference</string>
- <string name="conference_shutdown">The conference was shut down</string>
- <string name="using_account">using account %s</string>
- <string name="checking_x">Checking %s on HTTP host</string>
- <string name="not_connected_try_again">You are not connected. Try again later</string>
- <string name="check_x_filesize">Check %s size</string>
- <string name="check_x_filesize_on_host">Check %1$s size on %2$s</string>
- <string name="message_options">Message options</string>
- <string name="copy_text">Copy text</string>
- <string name="copy_original_url">Copy original URL</string>
- <string name="send_again">Send again</string>
- <string name="file_url">File URL</string>
- <string name="message_text">Message text</string>
- <string name="url_copied_to_clipboard">URL copied to clipboard</string>
- <string name="message_copied_to_clipboard">Message copied to clipboard</string>
- <string name="scan_qr_code">Scan QR code</string>
- <string name="show_qr_code">Show QR code</string>
- <string name="show_block_list">Show block list</string>
- <string name="account_details">Account details</string>
- <string name="verify_otr">Verify OTR</string>
- <string name="remote_fingerprint">Remote Fingerprint</string>
- <string name="shared_secret_hint">Hint or Question</string>
- <string name="shared_secret_secret">Shared Secret</string>
- <string name="confirm">Confirm</string>
- <string name="in_progress">In progress</string>
- <string name="respond">Respond</string>
- <string name="failed">Failed</string>
- <string name="secrets_do_not_match">Secrets do not match</string>
- <string name="try_again">Try again</string>
- <string name="finish">Finish</string>
- <string name="verified">Verified!</string>
- <string name="smp_requested">Contact requested SMP verification</string>
- <string name="no_otr_session_found">No valid OTR session has been found!</string>
- <string name="conversations_foreground_service">Pix-Art Messenger</string>
- <string name="pref_export_logs">Export Logs</string>
- <string name="pref_export_logs_summary">Write logs to SD card</string>
- <string name="notification_export_logs_title">Writing backup to SD card</string>
- <string name="choose_file">Choose file</string>
- <string name="receiving_x_file">Receiving %1$s (%2$d%% completed)</string>
- <string name="download_x_file">Download %s</string>
- <string name="delete_x_file">Delete %s</string>
- <string name="file">file</string>
- <string name="open_x_file">Open %s</string>
- <string name="sending_file">sending (%1$d%% completed)</string>
- <string name="preparing_file">Preparing file for transmission</string>
- <string name="x_file_offered_for_download">%s offered for download</string>
- <string name="cancel_transmission">Cancel transmission</string>
- <string name="file_transmission_failed">file transmission failed</string>
- <string name="file_deleted">The file has been deleted</string>
- <string name="no_application_found_to_open_file">No application found to open file</string>
- <string name="could_not_verify_fingerprint">Could not verify fingerprint</string>
- <string name="manually_verify">Manually verify</string>
- <string name="are_you_sure_verify_fingerprint">Are you sure that you want to verify your contacts OTR fingerprint?</string>
- <string name="pref_show_dynamic_tags">Show dynamic tags</string>
- <string name="pref_show_dynamic_tags_summary">Display read-only tags underneath contacts</string>
- <string name="enable_notifications">Enable notifications</string>
- <string name="no_conference_server_found">No conference server found</string>
- <string name="conference_creation_failed">Conference creation failed!</string>s
- <string name="account_image_description">Account avatar</string>
- <string name="copy_otr_clipboard_description">Copy OTR fingerprint to clipboard</string>
- <string name="copy_omemo_clipboard_description">Copy OMEMO fingerprint to clipboard</string>
- <string name="regenerate_omemo_key">Regenerate OMEMO key</string>
- <string name="clear_other_devices">Clear devices</string>
- <string name="clear_other_devices_desc">Are you sure you want to clear all other devices from the OMEMO announcement? The next time your devices connect, they will reannounce themselves, but they might not receive messages sent in the meantime.</string>
- <string name="purge_key">Purge key</string>
- <string name="purge_key_desc_part1">Are you sure you want to purge this key?</string>
- <string name="purge_key_desc_part2">It will irreversibly be considered compromised, and you can never build a session with it again.</string>
- <string name="error_no_keys_to_trust_server_error">There are no usable keys available for this contact.\nFetching new keys from the server has been unsuccessful. Maybe there is something wrong with your contacts server.</string>
- <string name="error_no_keys_to_trust">There are no usable keys available for this contact. If you have purged any of their keys, they need to generate new ones.</string>
- <string name="error_trustkeys_title">Error</string>
- <string name="fetching_history_from_server">Fetching history from server</string>
- <string name="no_more_history_on_server">No more history on server</string>
- <string name="updating">Updating…</string>
- <string name="password_changed">Password changed!</string>
- <string name="could_not_change_password">Could not change password</string>
- <string name="otr_session_not_started">Send a message to start an encrypted chat</string>
- <string name="ask_question">Ask question</string>
- <string name="smp_explain_question">If you and your contact have a secret in common that no one else knows (like an inside joke or simply what you had for lunch the last time you met) you can use that secret to verify each other’s fingerprints.\n\nYou provide a hint or a question for your contact who will respond with a case-sensitive answer.</string>
- <string name="smp_explain_answer">Your contact would like to verify your fingerprint by challenging you with a shared secret. Your contact provided the following hint or question for that secret.</string>
- <string name="shared_secret_hint_should_not_be_empty">Your hint should not be empty</string>
- <string name="shared_secret_can_not_be_empty">Your shared secret can not be empty</string>
- <string name="manual_verification_explanation">Carefully compare the fingerprint shown below with the fingerprint of your contact.\nYou can use any trusted form of communication like an encrypted e-mail or a telephone call to exchange those.</string>
- <string name="change_password">Change password</string>
- <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="perform_action_with">Perform action with</string>
- <string name="no_affiliation">No affiliation</string>
- <string name="no_role">Offline</string>
- <string name="outcast">Outcast</string>
- <string name="member">Member</string>
- <string name="advanced_mode">Advanced mode</string>
- <string name="grant_membership">Grant membership</string>
- <string name="remove_membership">Revoke membership</string>
- <string name="grant_admin_privileges">Grant admin privileges</string>
- <string name="remove_admin_privileges">Revoke admin privileges</string>
- <string name="remove_from_room">Remove from conference</string>
- <string name="could_not_change_affiliation">Could not change affiliation of %s</string>
- <string name="ban_from_conference">Ban from conference</string>
- <string name="removing_from_public_conference">You are trying to remove %s from a public conference. The only way to do that is to ban that user for ever.</string>
- <string name="ban_now">Ban now</string>
- <string name="could_not_change_role">Could not change role of %s</string>
- <string name="public_conference">Publicly accessible conference</string>
- <string name="private_conference">Private, members only conference</string>
- <string name="conference_options">Conference options</string>
- <string name="members_only">Private, members only</string>
- <string name="non_anonymous">Non-anonymous</string>
- <string name="moderated">Moderated</string>
- <string name="you_are_not_participating">You are not participating</string>
- <string name="modified_conference_options">Modified conference options!</string>
- <string name="could_not_modify_conference_options">Could not modify conference options</string>
- <string name="never">Never</string>
- <string name="thirty_minutes">30 minutes</string>
- <string name="one_hour">1 hour</string>
- <string name="two_hours">2 hours</string>
- <string name="eight_hours">8 hours</string>
- <string name="until_further_notice">Until further notice</string>
- <string name="pref_input_options">Input</string>
- <string name="pref_enter_is_send">Enter is send</string>
- <string name="pref_enter_is_send_summary">Use enter key to send message</string>
- <string name="pref_display_enter_key">Show enter key</string>
- <string name="pref_display_enter_key_summary">Change the emoticons key to an enter key</string>
- <string name="audio">audio</string>
- <string name="video">video</string>
- <string name="image">image</string>
- <string name="pdf_document">PDF document</string>
- <string name="apk">Android App</string>
- <string name="vcard">Contact</string>
- <string name="received_x_file">Received %s</string>
- <string name="disable_foreground_service">Disable foreground service</string>
- <string name="touch_to_open_conversations">Touch to open Pix-Art Messenger</string>
- <string name="avatar_has_been_published">Avatar has been published!</string>
- <string name="sending_x_file">Sending %s</string>
- <string name="offering_x_file">Offering %s</string>
- <string name="hide_offline">Hide offline</string>
- <string name="is_typing">is typing…</string>
- <string name="pref_chat_states">Typing notifications</string>
- <string name="pref_chat_states_summary">Let your contact know when you are writing a new message</string>
- <string name="send_location">Send location</string>
- <string name="show_location">Show location</string>
- <string name="no_application_found_to_display_location">No application found to display location</string>
- <string name="location">Location</string>
- <string name="received_location">Received location</string>
- <string name="title_undo_swipe_out_conversation">Conversation closed</string>
- <string name="title_undo_swipe_out_muc">Left conference</string>
- <string name="pref_dont_trust_system_cas_title">Don’t trust system CAs</string>
- <string name="pref_dont_trust_system_cas_summary">All certificates must be manually approved</string>
- <string name="pref_remove_trusted_certificates_title">Remove certificates</string>
- <string name="pref_remove_trusted_certificates_summary">Delete manually approved certificates</string>
- <string name="toast_no_trusted_certs">No manually approved certificates</string>
- <string name="dialog_manage_certs_title">Remove certificates</string>
- <string name="dialog_manage_certs_positivebutton">Delete selection</string>
- <string name="dialog_manage_certs_negativebutton">Cancel</string>
- <plurals name="toast_delete_certificates">
- <item quantity="one">%d certificate deleted</item>
- <item quantity="other">%d certificates deleted</item>
- </plurals>
- <plurals name="select_contact">
- <item quantity="one">Select %d contact</item>
- <item quantity="other">Select %d contacts</item>
- </plurals>
- <string name="pref_quick_action_summary">Replace send button with quick action</string>
- <string name="pref_quick_action">Quick Action</string>
- <string name="none">None</string>
- <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="send_private_message">Send private message</string>
- <string name="user_has_left_conference">%s has left the conference!</string>
- <string name="username">Username</string>
- <string name="username_hint">Username</string>
- <string name="invalid_username">This is not a valid username</string>
- <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="elv_undo">undo</string>
- <string name="download_failed_could_not_write_file">Download failed: Could not write file</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="action_check_update">Check for Updates</string>
- <string name="title_activity_updater">Update Service</string>
- <string name="update_available">Version %1$s is available.\n\nFilesize: %2$s\n\nUpdate to version %1$s now?\n\n\n%4$s</string>
- <string name="checking_for_updates">Checking for Pix-Art Messenger updates</string>
- <string name="remind_later">later</string>
- <string name="update">update</string>
- <string name="no_update_available">No update available</string>
- <string name="download_started">Download started</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">Server not responsible for domain</string>
- <string name="server_info_broken">Broken</string>
- <string name="pref_presence_settings">Presence</string>
- <string name="pref_away_when_screen_off">Away when screen is off</string>
- <string name="pref_away_when_screen_off_summary">Marks your resource as away when the screen is turned off</string>
- <string name="pref_xa_on_silent_mode">Not available in silent mode</string>
- <string name="update_info">Pix-Art Messenger is checking for an update. If an update is available you will be asked, if you want to update your version. The update process is downloading and installing the new version automatically.\n\nPlease wait…</string>
- <string name="pref_xa_on_silent_mode_summary">Marks your resource as not available when device is in silent mode</string>
- <string name="pref_treat_vibrate_as_silent">Treat vibrate as silent mode</string>
- <string name="pref_treat_vibrate_as_silent_summary">Marks your resource as not available when device is on vibrate</string>
- <string name="hostname_example">xmpp.pix-art.de</string>
- <string name="action_add_account_with_certificate">Add account with certificate</string>
- <string name="unable_to_parse_certificate">Unable to parse certificate</string>
- <string name="authenticate_with_certificate">Leave empty to authenticate w/ certificate</string>
- <string name="mam_prefs">Archiving preferences</string>
- <string name="server_side_mam_prefs">Server-side archiving preferences</string>
- <string name="fetching_mam_prefs">Fetching archiving preferences. Please wait…</string>
- <string name="unable_to_fetch_mam_prefs">Unable to fetch archiving preferences</string>
- <string name="captcha_required">Captcha required</string>
- <string name="captcha_hint">Enter the text from the image above</string>
- <string name="certificate_chain_is_not_trusted">Certificate chain is not trusted</string>
- <string name="jid_does_not_match_certificate">Jabber ID does not match certificate</string>
- <string name="action_renew_certificate">Renew certificate</string>
- <string name="error_fetching_omemo_key">Error fetching OMEMO key!</string>
- <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="changelog">Changes</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>
- <plurals name="x_messages">
- <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="no_storage_permission">Pix-Art Messenger needs access to external storage</string>
- <string name="shared_images_with_x">Shared images with %s</string>
- <string name="shared_text_with_x">Shared text with %s</string>
- <string name="sync_with_contacts">Synchronize with contacts</string>
- <string name="sync_with_contacts_long">Pix-Art Messenger wants to match your XMPP roster with your contacts to show their full names and avatars.\n\nPix-Art Messenger will only read your contacts and match them locally without uploading them to your server.\n\nYou will now be asked to grant permission to access your contacts.</string>
- <string name="certificate_information">Certificate Information</string>
- <string name="certificate_subject">Subject</string>
- <string name="certificate_issuer">Issuer</string>
- <string name="certificate_cn">Common Name</string>
- <string name="certificate_o">Organization</string>
- <string name="certificate_sha1">SHA-1</string>
- <string name="certicate_info_not_available">(Not available)</string>
- <string name="certificate_not_found">No certificate found</string>
- <string name="notify_on_all_messages">Notify on all messages</string>
- <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="pref_video_compression">Compress Videos</string>
- <string name="pref_video_compression_summary">Resize and compress Videos</string>
- <string name="always">Always</string>
- <string name="automatically">Automatically</string>
- <string name="battery_optimizations_enabled">Battery optimizations enabled</string>
- <string name="battery_optimizations_enabled_explained">Your device is doing some heavy battery optimizations on Pix-Art Messenger that might lead to delayed notifications or even message loss.\nIt is recommended to disable those.</string>
- <string name="battery_optimizations_enabled_dialog">Your device is doing some heavy battery optimizations on Pix-Art Messenger that might lead to delayed notifications or even message loss.\n\nYou will now be asked to disable those.</string>
- <string name="disable">Disable</string>
- <string name="selection_too_large">The selected area is too large</string>
- <string name="cancel_update">Cancel update check?</string>
- <string name="yes">Yes</string>
- <string name="no">No</string>
- <string name="no_accounts">(No activated accounts)</string>
- <string name="this_field_is_required">This field is required</string>
- <string name="no_participants">No participants</string>
- <string name="correct_message">Correct message</string>
- <string name="send_corrected_message">Send corrected message</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="contacts_have_no_pgp_keys">Pix-Art Messenger is unable to encrypt your messages because your contacts are not announcing their public keys.\n\n</string>
- <string name="this_account_is_disabled">You have disabled this account</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>
- <string name="mgmt_account_reconnect">reconnect</string>
- <string name="welcome_header" translatable="false">Pix-Art Messenger</string>
- <string name="welcome_text">…is a free and secure XMPP/Jabber client for the use with pix-art.de</string>
- <string name="magic_create_text">We will guide you through the process of creating an account on pix-art.de with a random password, which you can use or change to your own password on the next page. \nThan you will be able to communicate with users of this and other providers by giving them your full Jabber-ID.</string>
- <string name="your_full_jid_will_be">Your Jabber-ID will be: %s</string>
- <string name="create_account">Create new account</string>
- <string name="use_existing_accout">Use an existing account</string>
- <string name="pick_your_username">Choose your username</string>
- <string name="pref_manually_change_presence">Manually change presence</string>
- <string name="pref_manually_change_presence_summary">Change your presence inside your account</string>
- <string name="change_presence">Change Presence</string>
- <string name="status_message">Status message</string>
- <string name="all_accounts_on_this_device">Set for all accounts on this device</string>
- <string name="presence_chat">Free for Chat</string>
- <string name="presence_online">Online</string>
- <string name="presence_away">Away</string>
- <string name="presence_xa">Not Available</string>
- <string name="presence_dnd">Busy</string>
- <string name="secure_password_generated">A secure password has been generated</string>
- <string name="device_does_not_support_battery_op">Your device does not support opting out of battery optimization</string>
- <string name="share">Share</string>
- <string name="share_location">Share location</string>
- <string name="location_sharing_disabled">Location sharing is disabled in settings</string>
- <string name="locating">Locating…</string>
- <string name="uninstall_plugins">We have integrated the VoiceRecorder and ShareLocation plugin directly into the messenger. You should uninstall the plugins.</string>
- <string name="uninstall">Uninstall</string>
- <string name="action_end_conversation_muc">Leave conference</string>
- <string name="leave_conference_warning">Do you really want to leave this conference? You will no longer be notified of new messages until joining the conference again.</string>
- <string name="show_password">Show password</string>
- <string name="registration_please_wait">Registration failed: Try again later</string>
- <string name="registration_password_too_weak">Registration failed: Password too weak</string>
- <string name="create_conference">Create conference</string>
- <string name="join_or_create_conference">Join or create conference</string>
- <string name="conference_subject">Subject</string>
- <string name="choose_participants">Choose participants</string>
- <string name="creating_conference">Creating conference…</string>
- <string name="import_text">There is a backup on your device which can be imported.\nPossibly you will be asked to uninstall the old version or Conversations and your Messenger will be restarted during backup process. Shall the backup be imported? </string>
- <string name="import_database">Import backup</string>
- <string name="invite_again">Invite again</string>
- <string name="inviteUser_Subject">has invited you via</string>
- <string name="InviteText">Hello,\n\nthe user %s has invited you to Pix-Art Messenger. If you are using Android, give it a try and click the following link to start over...</string>
- <string name="pref_broadcast_last_activity">Broadcast Last User Interaction</string>
- <string name="pref_broadcast_last_activity_summary">Let all your contacts know when use Pix-Art Messenger</string>
- <string name="invite_user">Invite to Pix-Art Messenger</string>
- <string name="request_permissions_message">Pix-Art Messenger will ask you to allow a few permissions. It is important that you allow all these permissions to use all features of this messenger. If you deny any of these permissions the app will close itself.</string>
- <string name="request_permissions_message_again">You have denied some or all permissions needed for Pix-Art Messenger. Would you like to jump to the settings and allow these permissions? If you denie any of these permissions, the app will close itself.</string>
- <string name="unable_to_connect_to_keychain">Unable to connect to OpenKeychain</string>
- <string name="no_permission">Pix-Art Messenger has no permissions</string>
- <string name="send_image">Send image?</string>
- <string name="this_device_is_no_longer_in_use">This device is no longer in use</string>
- <string name="type_pc">Computer</string>
- <string name="type_phone">Mobile phone</string>
- <string name="type_tablet">Tablet</string>
- <string name="type_web">Web browser</string>
- <string name="type_console">Console</string>
- <string name="import_canceled">Import canceled</string>
- <string name="Import_failed">Database import failed, an import is not possible</string>
- <string name="Password_wrong">Wrong password, try again</string>
- <string name="enter_account_password">Please enter your account password to import your backup.</string>
- <string name="please_wait">Please wait…</string>
- <string name="databaseimport_started">Backup will be imported, this may take awhile.</string>
- <string name="pref_export_plain_text_logs_summary">Enable an export of chat logs as human readable text files</string>
- <string name="pref_export_plain_text_logs">Export human readable chat logs</string>
- <string name="payment_required">Payment required</string>
- <string name="missing_internet_permission">Missing internet permission</string>
- <string name="me">Me</string>
- <string name="contact_asks_for_presence_subscription">Contact asks for presence subscription</string>
- <string name="allow">Allow</string>
- <string name="attach_choose_video">Choose video</string>
- <string name="preparing_video">Prepare video for transmission. Please wait…</string>
- <string name="compressing_video">Compressing video, please wait…</string>
- <string name="shared_video_with_x">Shared video with %s</string>
- <string name="no_permission_to_access_x">No permission to access %s</string>
- <string name="remote_server_not_found">Remote server not found</string>
- <string name="error_file_corrupt">This file seems to be corrupt.</string>
- <string name="unable_to_update_account">Unable to update account</string>
- <string name="missing_presence_subscription_with_x">Missing presence subscription with %s.</string>
- <string name="missing_keys_from_x">Missing OMEMO keys from %s.</string>
- <string name="wrong_conference_configuration">This is not a private, non-anonymous conference.</string>
- <string name="this_conference_has_no_members">There are no members in this conference.</string>
- <string name="report_jid_as_spammer">Report this JID as sending unwanted messages.</string>
- <string name="create_issue">Report an issue</string>
- <string name="pref_delete_omemo_identities">Delete OMEMO identities</string>
- <string name="pref_delete_omemo_identities_summary">Regenerate your OMEMO keys. All your contacts will have to verify you again. Use this only as a last resort.</string>
- <string name="delete_selected_keys">Delete selected keys</string>
- <string name="missing_presence_subscription">Missing presence subscription</string>
- <string name="missing_omemo_keys">Missing OMEMO keys</string>
- <string name="error_publish_avatar_offline">You need to be connected to publish your avatar.</string>
- <string name="select_text">Select text</string>
- <string name="show_error_message">Show error message</string>
- <string name="error_message">Error Message</string>
- <string name="data_saver_enabled">Data saver enabled</string>
- <string name="data_saver_enabled_explained">Your operating system is restricting Pix-Art Messenger from accessing the Internet when in background. To receive notifications of new messages you should allow Pix-Art Messenger unrestricted access when Data saver is on.\\nPix-Art Messenger will still make an effort to save data when possible.</string>
- <string name="device_does_not_support_data_saver">Your device does not supporting disabling Data saver for Pix-Art Messenger.</string>
- <string name="navigate">Navigate to location</string>
- <string name="add_to_contact_list">Add to contact list</string>
- <string name="received_contact">Received contact</string>
- <string name="contact">Contact</string>
- <string name="unable_to_start_recording">Unable to start recording</string>
- <string name="error_unable_to_create_temporary_file">Unable to create temporary file</string>
- <string name="this_device_has_been_verified">This device has been verified</string>
- <string name="copy_fingerprint">Copy fingerprint</string>
- <string name="all_omemo_keys_have_been_verified"><![CDATA[>All OMEMO keys have been verified]]></string>
+ <string name="title_pref_quiet_hours">Quiet Hours</string>
+ <string name="title_pref_quiet_hours_start_time">Start time</string>
+ <string name="title_pref_quiet_hours_end_time">End time</string>
+ <string name="title_pref_enable_quiet_hours">Enable quiet hours</string>
+ <string name="pref_quiet_hours_summary">Notifications will be silenced during quiet hours</string>
+ <string name="pref_use_larger_font">Increase font size</string>
+ <string name="pref_use_larger_font_summary">Use larger font sizes across the entire app</string>
+ <string name="pref_use_send_button_to_indicate_status">Send button indicates status</string>
+ <string name="pref_use_indicate_received">Request message receipts</string>
+ <string name="pref_use_indicate_received_summary">Received messages will be marked with a green tick if supported</string>
+ <string name="pref_use_send_button_to_indicate_status_summary">Colorize send button to indicate contact status</string>
+ <string name="pref_expert_options_other">Other</string>
+ <string name="pref_autojoin">Automatically join conferences</string>
+ <string name="pref_autojoin_summary">Respect the autojoin flag in conference bookmarks</string>
+ <string name="toast_message_otr_fingerprint">OTR fingerprint copied to clipboard!</string>
+ <string name="toast_message_omemo_fingerprint">OMEMO fingerprint copied to clipboard!</string>
+ <string name="conference_banned">You are banned from this conference</string>
+ <string name="conference_members_only">This conference is members only</string>
+ <string name="conference_kicked">You have been kicked from this conference</string>
+ <string name="conference_shutdown">The conference was shut down</string>
+ <string name="using_account">using account %s</string>
+ <string name="checking_x">Checking %s on HTTP host</string>
+ <string name="not_connected_try_again">You are not connected. Try again later</string>
+ <string name="check_x_filesize">Check %s size</string>
+ <string name="check_x_filesize_on_host">Check %1$s size on %2$s</string>
+ <string name="message_options">Message options</string>
+ <string name="copy_text">Copy text</string>
+ <string name="copy_original_url">Copy original URL</string>
+ <string name="send_again">Send again</string>
+ <string name="file_url">File URL</string>
+ <string name="message_text">Message text</string>
+ <string name="url_copied_to_clipboard">URL copied to clipboard</string>
+ <string name="message_copied_to_clipboard">Message copied to clipboard</string>
+ <string name="scan_qr_code">Scan QR code</string>
+ <string name="show_qr_code">Show QR code</string>
+ <string name="show_block_list">Show block list</string>
+ <string name="account_details">Account details</string>
+ <string name="verify_otr">Verify OTR</string>
+ <string name="remote_fingerprint">Remote Fingerprint</string>
+ <string name="shared_secret_hint">Hint or Question</string>
+ <string name="shared_secret_secret">Shared Secret</string>
+ <string name="confirm">Confirm</string>
+ <string name="in_progress">In progress</string>
+ <string name="respond">Respond</string>
+ <string name="failed">Failed</string>
+ <string name="secrets_do_not_match">Secrets do not match</string>
+ <string name="try_again">Try again</string>
+ <string name="finish">Finish</string>
+ <string name="verified">Verified!</string>
+ <string name="smp_requested">Contact requested SMP verification</string>
+ <string name="no_otr_session_found">No valid OTR session has been found!</string>
+ <string name="conversations_foreground_service">Pix-Art Messenger</string>
+ <string name="pref_export_logs">Export Logs</string>
+ <string name="pref_export_logs_summary">Write logs to SD card</string>
+ <string name="notification_export_logs_title">Writing backup to SD card</string>
+ <string name="choose_file">Choose file</string>
+ <string name="receiving_x_file">Receiving %1$s (%2$d%% completed)</string>
+ <string name="download_x_file">Download %s</string>
+ <string name="delete_x_file">Delete %s</string>
+ <string name="file">file</string>
+ <string name="open_x_file">Open %s</string>
+ <string name="sending_file">sending (%1$d%% completed)</string>
+ <string name="preparing_file">Preparing file for transmission</string>
+ <string name="x_file_offered_for_download">%s offered for download</string>
+ <string name="cancel_transmission">Cancel transmission</string>
+ <string name="file_transmission_failed">file transmission failed</string>
+ <string name="file_deleted">The file has been deleted</string>
+ <string name="no_application_found_to_open_file">No application found to open file</string>
+ <string name="could_not_verify_fingerprint">Could not verify fingerprint</string>
+ <string name="manually_verify">Manually verify</string>
+ <string name="are_you_sure_verify_fingerprint">Are you sure that you want to verify your contacts OTR fingerprint?</string>
+ <string name="pref_show_dynamic_tags">Show dynamic tags</string>
+ <string name="pref_show_dynamic_tags_summary">Display read-only tags underneath contacts</string>
+ <string name="enable_notifications">Enable notifications</string>
+ <string name="no_conference_server_found">No conference server found</string>
+ <string name="conference_creation_failed">Conference creation failed!</string>s
+ <string name="account_image_description">Account avatar</string>
+ <string name="copy_otr_clipboard_description">Copy OTR fingerprint to clipboard</string>
+ <string name="copy_omemo_clipboard_description">Copy OMEMO fingerprint to clipboard</string>
+ <string name="regenerate_omemo_key">Regenerate OMEMO key</string>
+ <string name="clear_other_devices">Clear devices</string>
+ <string name="clear_other_devices_desc">Are you sure you want to clear all other devices from the OMEMO announcement? The next time your devices connect, they will reannounce themselves, but they might not receive messages sent in the meantime.</string>
+ <string name="purge_key">Purge key</string>
+ <string name="purge_key_desc_part1">Are you sure you want to purge this key?</string>
+ <string name="purge_key_desc_part2">It will irreversibly be considered compromised, and you can never build a session with it again.</string>
+ <string name="error_no_keys_to_trust_server_error">There are no usable keys available for this contact.\nFetching new keys from the server has been unsuccessful. Maybe there is something wrong with your contacts server.</string>
+ <string name="error_no_keys_to_trust">There are no usable keys available for this contact. If you have purged any of their keys, they need to generate new ones.</string>
+ <string name="error_trustkeys_title">Error</string>
+ <string name="fetching_history_from_server">Fetching history from server</string>
+ <string name="no_more_history_on_server">No more history on server</string>
+ <string name="updating">Updating…</string>
+ <string name="password_changed">Password changed!</string>
+ <string name="could_not_change_password">Could not change password</string>
+ <string name="otr_session_not_started">Send a message to start an encrypted chat</string>
+ <string name="ask_question">Ask question</string>
+ <string name="smp_explain_question">If you and your contact have a secret in common that no one else knows (like an inside joke or simply what you had for lunch the last time you met) you can use that secret to verify each other’s fingerprints.\n\nYou provide a hint or a question for your contact who will respond with a case-sensitive answer.</string>
+ <string name="smp_explain_answer">Your contact would like to verify your fingerprint by challenging you with a shared secret. Your contact provided the following hint or question for that secret.</string>
+ <string name="shared_secret_hint_should_not_be_empty">Your hint should not be empty</string>
+ <string name="shared_secret_can_not_be_empty">Your shared secret can not be empty</string>
+ <string name="manual_verification_explanation">Carefully compare the fingerprint shown below with the fingerprint of your contact.\nYou can use any trusted form of communication like an encrypted e-mail or a telephone call to exchange those.</string>
+ <string name="change_password">Change password</string>
+ <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="perform_action_with">Perform action with</string>
+ <string name="no_affiliation">No affiliation</string>
+ <string name="no_role">Offline</string>
+ <string name="outcast">Outcast</string>
+ <string name="member">Member</string>
+ <string name="advanced_mode">Advanced mode</string>
+ <string name="grant_membership">Grant membership</string>
+ <string name="remove_membership">Revoke membership</string>
+ <string name="grant_admin_privileges">Grant admin privileges</string>
+ <string name="remove_admin_privileges">Revoke admin privileges</string>
+ <string name="remove_from_room">Remove from conference</string>
+ <string name="could_not_change_affiliation">Could not change affiliation of %s</string>
+ <string name="ban_from_conference">Ban from conference</string>
+ <string name="removing_from_public_conference">You are trying to remove %s from a public conference. The only way to do that is to ban that user for ever.</string>
+ <string name="ban_now">Ban now</string>
+ <string name="could_not_change_role">Could not change role of %s</string>
+ <string name="public_conference">Publicly accessible conference</string>
+ <string name="private_conference">Private, members only conference</string>
+ <string name="conference_options">Conference options</string>
+ <string name="members_only">Private, members only</string>
+ <string name="non_anonymous">Non-anonymous</string>
+ <string name="moderated">Moderated</string>
+ <string name="you_are_not_participating">You are not participating</string>
+ <string name="modified_conference_options">Modified conference options!</string>
+ <string name="could_not_modify_conference_options">Could not modify conference options</string>
+ <string name="never">Never</string>
+ <string name="thirty_minutes">30 minutes</string>
+ <string name="one_hour">1 hour</string>
+ <string name="two_hours">2 hours</string>
+ <string name="eight_hours">8 hours</string>
+ <string name="until_further_notice">Until further notice</string>
+ <string name="pref_input_options">Input</string>
+ <string name="pref_enter_is_send">Enter is send</string>
+ <string name="pref_enter_is_send_summary">Use enter key to send message</string>
+ <string name="pref_display_enter_key">Show enter key</string>
+ <string name="pref_display_enter_key_summary">Change the emoticons key to an enter key</string>
+ <string name="audio">audio</string>
+ <string name="video">video</string>
+ <string name="image">image</string>
+ <string name="pdf_document">PDF document</string>
+ <string name="apk">Android App</string>
+ <string name="vcard">Contact</string>
+ <string name="received_x_file">Received %s</string>
+ <string name="disable_foreground_service">Disable foreground service</string>
+ <string name="touch_to_open_conversations">Touch to open Pix-Art Messenger</string>
+ <string name="avatar_has_been_published">Avatar has been published!</string>
+ <string name="sending_x_file">Sending %s</string>
+ <string name="offering_x_file">Offering %s</string>
+ <string name="hide_offline">Hide offline</string>
+ <string name="is_typing">is typing…</string>
+ <string name="pref_chat_states">Typing notifications</string>
+ <string name="pref_chat_states_summary">Let your contact know when you are writing a new message</string>
+ <string name="send_location">Send location</string>
+ <string name="show_location">Show location</string>
+ <string name="no_application_found_to_display_location">No application found to display location</string>
+ <string name="location">Location</string>
+ <string name="received_location">Received location</string>
+ <string name="title_undo_swipe_out_conversation">Conversation closed</string>
+ <string name="title_undo_swipe_out_muc">Left conference</string>
+ <string name="pref_dont_trust_system_cas_title">Don’t trust system CAs</string>
+ <string name="pref_dont_trust_system_cas_summary">All certificates must be manually approved</string>
+ <string name="pref_remove_trusted_certificates_title">Remove certificates</string>
+ <string name="pref_remove_trusted_certificates_summary">Delete manually approved certificates</string>
+ <string name="toast_no_trusted_certs">No manually approved certificates</string>
+ <string name="dialog_manage_certs_title">Remove certificates</string>
+ <string name="dialog_manage_certs_positivebutton">Delete selection</string>
+ <string name="dialog_manage_certs_negativebutton">Cancel</string>
+ <plurals name="toast_delete_certificates">
+ <item quantity="one">%d certificate deleted</item>
+ <item quantity="other">%d certificates deleted</item>
+ </plurals>
+ <plurals name="select_contact">
+ <item quantity="one">Select %d contact</item>
+ <item quantity="other">Select %d contacts</item>
+ </plurals>
+ <string name="pref_quick_action_summary">Replace send button with quick action</string>
+ <string name="pref_quick_action">Quick Action</string>
+ <string name="none">None</string>
+ <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="send_private_message">Send private message</string>
+ <string name="user_has_left_conference">%s has left the conference!</string>
+ <string name="username">Username</string>
+ <string name="username_hint">Username</string>
+ <string name="invalid_username">This is not a valid username</string>
+ <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="elv_undo">undo</string>
+ <string name="download_failed_could_not_write_file">Download failed: Could not write file</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="action_check_update">Check for Updates</string>
+ <string name="title_activity_updater">Update Service</string>
+ <string name="update_available">Version %1$s is available.\n\nFilesize: %2$s\n\nUpdate to version %1$s now?\n\n\n%4$s</string>
+ <string name="checking_for_updates">Checking for Pix-Art Messenger updates</string>
+ <string name="remind_later">later</string>
+ <string name="update">update</string>
+ <string name="no_update_available">No update available</string>
+ <string name="download_started">Download started</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">Server not responsible for domain</string>
+ <string name="server_info_broken">Broken</string>
+ <string name="pref_presence_settings">Presence</string>
+ <string name="pref_away_when_screen_off">Away when screen is off</string>
+ <string name="pref_away_when_screen_off_summary">Marks your resource as away when the screen is turned off</string>
+ <string name="pref_xa_on_silent_mode">Not available in silent mode</string>
+ <string name="update_info">Pix-Art Messenger is checking for an update. If an update is available you will be asked, if you want to update your version. The update process is downloading and installing the new version automatically.\n\nPlease wait…</string>
+ <string name="pref_xa_on_silent_mode_summary">Marks your resource as not available when device is in silent mode</string>
+ <string name="pref_treat_vibrate_as_silent">Treat vibrate as silent mode</string>
+ <string name="pref_treat_vibrate_as_silent_summary">Marks your resource as not available when device is on vibrate</string>
+ <string name="hostname_example">xmpp.pix-art.de</string>
+ <string name="action_add_account_with_certificate">Add account with certificate</string>
+ <string name="unable_to_parse_certificate">Unable to parse certificate</string>
+ <string name="authenticate_with_certificate">Leave empty to authenticate w/ certificate</string>
+ <string name="mam_prefs">Archiving preferences</string>
+ <string name="server_side_mam_prefs">Server-side archiving preferences</string>
+ <string name="fetching_mam_prefs">Fetching archiving preferences. Please wait…</string>
+ <string name="unable_to_fetch_mam_prefs">Unable to fetch archiving preferences</string>
+ <string name="captcha_required">Captcha required</string>
+ <string name="captcha_hint">Enter the text from the image above</string>
+ <string name="certificate_chain_is_not_trusted">Certificate chain is not trusted</string>
+ <string name="jid_does_not_match_certificate">Jabber ID does not match certificate</string>
+ <string name="action_renew_certificate">Renew certificate</string>
+ <string name="error_fetching_omemo_key">Error fetching OMEMO key!</string>
+ <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="changelog">Changes</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>
+ <plurals name="x_messages">
+ <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="no_storage_permission">Pix-Art Messenger needs access to external storage</string>
+ <string name="shared_images_with_x">Shared images with %s</string>
+ <string name="shared_text_with_x">Shared text with %s</string>
+ <string name="sync_with_contacts">Synchronize with contacts</string>
+ <string name="sync_with_contacts_long">Pix-Art Messenger wants to match your XMPP roster with your contacts to show their full names and avatars.\n\nPix-Art Messenger will only read your contacts and match them locally without uploading them to your server.\n\nYou will now be asked to grant permission to access your contacts.</string>
+ <string name="certificate_information">Certificate Information</string>
+ <string name="certificate_subject">Subject</string>
+ <string name="certificate_issuer">Issuer</string>
+ <string name="certificate_cn">Common Name</string>
+ <string name="certificate_o">Organization</string>
+ <string name="certificate_sha1">SHA-1</string>
+ <string name="certicate_info_not_available">(Not available)</string>
+ <string name="certificate_not_found">No certificate found</string>
+ <string name="notify_on_all_messages">Notify on all messages</string>
+ <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="pref_video_compression">Compress Videos</string>
+ <string name="pref_video_compression_summary">Resize and compress Videos</string>
+ <string name="always">Always</string>
+ <string name="automatically">Automatically</string>
+ <string name="battery_optimizations_enabled">Battery optimizations enabled</string>
+ <string name="battery_optimizations_enabled_explained">Your device is doing some heavy battery optimizations on Pix-Art Messenger that might lead to delayed notifications or even message loss.\nIt is recommended to disable those.</string>
+ <string name="battery_optimizations_enabled_dialog">Your device is doing some heavy battery optimizations on Pix-Art Messenger that might lead to delayed notifications or even message loss.\n\nYou will now be asked to disable those.</string>
+ <string name="disable">Disable</string>
+ <string name="selection_too_large">The selected area is too large</string>
+ <string name="cancel_update">Cancel update check?</string>
+ <string name="yes">Yes</string>
+ <string name="no">No</string>
+ <string name="no_accounts">(No activated accounts)</string>
+ <string name="this_field_is_required">This field is required</string>
+ <string name="no_participants">No participants</string>
+ <string name="correct_message">Correct message</string>
+ <string name="send_corrected_message">Send corrected message</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="contacts_have_no_pgp_keys">Pix-Art Messenger is unable to encrypt your messages because your contacts are not announcing their public keys.\n\n</string>
+ <string name="this_account_is_disabled">You have disabled this account</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>
+ <string name="mgmt_account_reconnect">reconnect</string>
+ <string name="welcome_header" translatable="false">Pix-Art Messenger</string>
+ <string name="welcome_text">…is a free and secure XMPP/Jabber client for the use with pix-art.de</string>
+ <string name="magic_create_text">We will guide you through the process of creating an account on pix-art.de with a random password, which you can use or change to your own password on the next page. \nThan you will be able to communicate with users of this and other providers by giving them your full Jabber-ID.</string>
+ <string name="your_full_jid_will_be">Your Jabber-ID will be: %s</string>
+ <string name="create_account">Create new account</string>
+ <string name="use_existing_accout">Use an existing account</string>
+ <string name="pick_your_username">Choose your username</string>
+ <string name="pref_manually_change_presence">Manually change presence</string>
+ <string name="pref_manually_change_presence_summary">Change your presence inside your account</string>
+ <string name="change_presence">Change Presence</string>
+ <string name="status_message">Status message</string>
+ <string name="all_accounts_on_this_device">Set for all accounts on this device</string>
+ <string name="presence_chat">Free for Chat</string>
+ <string name="presence_online">Online</string>
+ <string name="presence_away">Away</string>
+ <string name="presence_xa">Not Available</string>
+ <string name="presence_dnd">Busy</string>
+ <string name="secure_password_generated">A secure password has been generated</string>
+ <string name="device_does_not_support_battery_op">Your device does not support opting out of battery optimization</string>
+ <string name="share">Share</string>
+ <string name="share_location">Share location</string>
+ <string name="location_sharing_disabled">Location sharing is disabled in settings</string>
+ <string name="locating">Locating…</string>
+ <string name="uninstall_plugins">We have integrated the VoiceRecorder and ShareLocation plugin directly into the messenger. You should uninstall the plugins.</string>
+ <string name="uninstall">Uninstall</string>
+ <string name="action_end_conversation_muc">Leave conference</string>
+ <string name="leave_conference_warning">Do you really want to leave this conference? You will no longer be notified of new messages until joining the conference again.</string>
+ <string name="show_password">Show password</string>
+ <string name="registration_please_wait">Registration failed: Try again later</string>
+ <string name="registration_password_too_weak">Registration failed: Password too weak</string>
+ <string name="create_conference">Create conference</string>
+ <string name="join_or_create_conference">Join or create conference</string>
+ <string name="conference_subject">Subject</string>
+ <string name="choose_participants">Choose participants</string>
+ <string name="creating_conference">Creating conference…</string>
+ <string name="import_text">There is a backup on your device which can be imported.\nPossibly you will be asked to uninstall the old version or Conversations and your Messenger will be restarted during backup process. Shall the backup be imported? </string>
+ <string name="import_database">Import backup</string>
+ <string name="invite_again">Invite again</string>
+ <string name="inviteUser_Subject">has invited you via</string>
+ <string name="InviteText">Hello,\n\nthe user %s has invited you to Pix-Art Messenger. If you are using Android, give it a try and click the following link to start over...</string>
+ <string name="pref_broadcast_last_activity">Broadcast Last User Interaction</string>
+ <string name="pref_broadcast_last_activity_summary">Let all your contacts know when use Pix-Art Messenger</string>
+ <string name="invite_user">Invite to Pix-Art Messenger</string>
+ <string name="request_permissions_message">Pix-Art Messenger will ask you to allow a few permissions. It is important that you allow all these permissions to use all features of this messenger. If you deny any of these permissions the app will close itself.</string>
+ <string name="request_permissions_message_again">You have denied some or all permissions needed for Pix-Art Messenger. Would you like to jump to the settings and allow these permissions? If you denie any of these permissions, the app will close itself.</string>
+ <string name="unable_to_connect_to_keychain">Unable to connect to OpenKeychain</string>
+ <string name="no_permission">Pix-Art Messenger has no permissions</string>
+ <string name="send_image">Send image?</string>
+ <string name="this_device_is_no_longer_in_use">This device is no longer in use</string>
+ <string name="type_pc">Computer</string>
+ <string name="type_phone">Mobile phone</string>
+ <string name="type_tablet">Tablet</string>
+ <string name="type_web">Web browser</string>
+ <string name="type_console">Console</string>
+ <string name="import_canceled">Import canceled</string>
+ <string name="Import_failed">Database import failed, an import is not possible</string>
+ <string name="Password_wrong">Wrong password, try again</string>
+ <string name="enter_account_password">Please enter your account password to import your backup.</string>
+ <string name="please_wait">Please wait…</string>
+ <string name="databaseimport_started">Backup will be imported, this may take awhile.</string>
+ <string name="pref_export_plain_text_logs_summary">Enable an export of chat logs as human readable text files</string>
+ <string name="pref_export_plain_text_logs">Export human readable chat logs</string>
+ <string name="payment_required">Payment required</string>
+ <string name="missing_internet_permission">Missing internet permission</string>
+ <string name="me">Me</string>
+ <string name="contact_asks_for_presence_subscription">Contact asks for presence subscription</string>
+ <string name="allow">Allow</string>
+ <string name="attach_choose_video">Choose video</string>
+ <string name="preparing_video">Prepare video for transmission. Please wait…</string>
+ <string name="compressing_video">Compressing video, please wait…</string>
+ <string name="shared_video_with_x">Shared video with %s</string>
+ <string name="no_permission_to_access_x">No permission to access %s</string>
+ <string name="remote_server_not_found">Remote server not found</string>
+ <string name="error_file_corrupt">This file seems to be corrupt.</string>
+ <string name="unable_to_update_account">Unable to update account</string>
+ <string name="missing_presence_subscription_with_x">Missing presence subscription with %s.</string>
+ <string name="missing_keys_from_x">Missing OMEMO keys from %s.</string>
+ <string name="wrong_conference_configuration">This is not a private, non-anonymous conference.</string>
+ <string name="this_conference_has_no_members">There are no members in this conference.</string>
+ <string name="report_jid_as_spammer">Report this JID as sending unwanted messages.</string>
+ <string name="create_issue">Report an issue</string>
+ <string name="pref_delete_omemo_identities">Delete OMEMO identities</string>
+ <string name="pref_delete_omemo_identities_summary">Regenerate your OMEMO keys. All your contacts will have to verify you again. Use this only as a last resort.</string>
+ <string name="delete_selected_keys">Delete selected keys</string>
+ <string name="missing_presence_subscription">Missing presence subscription</string>
+ <string name="missing_omemo_keys">Missing OMEMO keys</string>
+ <string name="error_publish_avatar_offline">You need to be connected to publish your avatar.</string>
+ <string name="select_text">Select text</string>
+ <string name="show_error_message">Show error message</string>
+ <string name="error_message">Error Message</string>
+ <string name="data_saver_enabled">Data saver enabled</string>
+ <string name="data_saver_enabled_explained">Your operating system is restricting Pix-Art Messenger from accessing the Internet when in background. To receive notifications of new messages you should allow Pix-Art Messenger unrestricted access when Data saver is on.\\nPix-Art Messenger will still make an effort to save data when possible.</string>
+ <string name="device_does_not_support_data_saver">Your device does not supporting disabling Data saver for Pix-Art Messenger.</string>
+ <string name="navigate">Navigate to location</string>
+ <string name="add_to_contact_list">Add to contact list</string>
+ <string name="received_contact">Received contact</string>
+ <string name="contact">Contact</string>
+ <string name="unable_to_start_recording">Unable to start recording</string>
+ <string name="error_unable_to_create_temporary_file">Unable to create temporary file</string>
+ <string name="this_device_has_been_verified">This device has been verified</string>
+ <string name="copy_fingerprint">Copy fingerprint</string>
+ <string name="all_omemo_keys_have_been_verified"><![CDATA[>All OMEMO keys have been verified]]></string>
</resources>
diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml
index 9837b8588..071d26d3c 100644
--- a/src/main/res/values/themes.xml
+++ b/src/main/res/values/themes.xml
@@ -32,7 +32,7 @@
<item name="attr/icon_settings">@drawable/ic_settings_grey600_24dp</item>
<item name="attr/icon_import_export">@drawable/ic_import_export_white_24dp</item>
<item name="attr/icon_share">@drawable/ic_share_white_24dp</item>
-
+
<item name="attr/dialog_horizontal_padding">16dp</item>
<item name="attr/dialog_vertical_padding">16dp</item>
</style>
@@ -52,12 +52,15 @@
<style name="ConversationsActionBarWidget" parent="android:Theme.Holo.Light">
<item name="android:popupMenuStyle">@android:style/Widget.Holo.Light.PopupMenu</item>
- <item name="android:dropDownListViewStyle">@android:style/Widget.Holo.Light.ListView.DropDown</item>
+ <item name="android:dropDownListViewStyle">
+ @android:style/Widget.Holo.Light.ListView.DropDown
+ </item>
</style>
<style name="ConversationsActionBarTabs" parent="@android:style/Widget.Holo.ActionBar.TabView">
<item name="android:background">@drawable/actionbar_tab_indicator</item>
</style>
+
<style name="ConversationsDialog" parent="@android:style/Theme.Holo.Light.Dialog">
<item name="android:windowNoTitle">true</item>
<item name="TextSizeInfo">10sp</item>
diff --git a/src/main/res/xml/file_paths.xml b/src/main/res/xml/file_paths.xml
index 2a8bc7667..6ba7c695b 100644
--- a/src/main/res/xml/file_paths.xml
+++ b/src/main/res/xml/file_paths.xml
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
- <external-path name="external" path="/"/>
+ <external-path
+ name="external"
+ path="/" />
</paths> \ No newline at end of file
diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml
index d88df7d38..3159fd61e 100644
--- a/src/main/res/xml/preferences.xml
+++ b/src/main/res/xml/preferences.xml
@@ -187,7 +187,7 @@
android:defaultValue="true"
android:key="last_activity"
android:title="@string/pref_broadcast_last_activity"
- android:summary="@string/pref_broadcast_last_activity_summary"/>
+ android:summary="@string/pref_broadcast_last_activity_summary" />
<CheckBoxPreference
android:defaultValue="true"
android:key="crashreport"
@@ -235,7 +235,7 @@
<Preference
android:key="delete_omemo_identities"
android:title="@string/pref_delete_omemo_identities"
- android:summary="@string/pref_delete_omemo_identities_summary"/>
+ android:summary="@string/pref_delete_omemo_identities_summary" />
<CheckBoxPreference
android:defaultValue="false"
android:key="dont_trust_system_cas"
diff --git a/src/open/java/de/pixart/messenger/services/PushManagementService.java b/src/open/java/de/pixart/messenger/services/PushManagementService.java
index 17e49cbbb..b76f5b4de 100644
--- a/src/open/java/de/pixart/messenger/services/PushManagementService.java
+++ b/src/open/java/de/pixart/messenger/services/PushManagementService.java
@@ -4,21 +4,21 @@ import de.pixart.messenger.entities.Account;
public class PushManagementService {
- protected final XmppConnectionService mXmppConnectionService;
+ protected final XmppConnectionService mXmppConnectionService;
- public PushManagementService(XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
+ public PushManagementService(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
- public void registerPushTokenOnServer(Account account) {
- //stub implementation. only affects playstore flavor
- }
+ public void registerPushTokenOnServer(Account account) {
+ //stub implementation. only affects playstore flavor
+ }
- public boolean available(Account account) {
- return false;
- }
+ public boolean available(Account account) {
+ return false;
+ }
- public boolean isStub() {
- return true;
- }
+ public boolean isStub() {
+ return true;
+ }
}
diff --git a/src/playstore/AndroidManifest.xml b/src/playstore/AndroidManifest.xml
index 780436aaa..dcce3cda3 100644
--- a/src/playstore/AndroidManifest.xml
+++ b/src/playstore/AndroidManifest.xml
@@ -1,34 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
-<manifest
- package="de.pixart.messenger"
+<manifest package="de.pixart.messenger"
xmlns:android="http://schemas.android.com/apk/res/android">
- <permission android:name="de.pixart.messenger.permission.C2D_MESSAGE"
- android:protectionLevel="signature"/>
- <uses-permission android:name="de.pixart.messenger.permission.C2D_MESSAGE"/>
+ <permission
+ android:name="de.pixart.messenger.permission.C2D_MESSAGE"
+ android:protectionLevel="signature" />
+ <uses-permission android:name="de.pixart.messenger.permission.C2D_MESSAGE" />
<application>
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
- android:permission="com.google.android.c2dm.permission.SEND" >
+ android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.example.gcm" />
</intent-filter>
</receiver>
+
<service
android:name=".services.PushMessageReceiver"
- android:exported="false" >
+ android:exported="false">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>
- <service android:name=".services.InstanceIdService" android:exported="false">
+ <service
+ android:name=".services.InstanceIdService"
+ android:exported="false">
<intent-filter>
- <action android:name="com.google.android.gms.iid.InstanceID"/>
+ <action android:name="com.google.android.gms.iid.InstanceID" />
</intent-filter>
</service>
</application>
diff --git a/src/playstore/java/de/pixart/messenger/services/InstanceIdService.java b/src/playstore/java/de/pixart/messenger/services/InstanceIdService.java
index c73652ba6..e97d7181a 100644
--- a/src/playstore/java/de/pixart/messenger/services/InstanceIdService.java
+++ b/src/playstore/java/de/pixart/messenger/services/InstanceIdService.java
@@ -6,10 +6,10 @@ import com.google.android.gms.iid.InstanceIDListenerService;
public class InstanceIdService extends InstanceIDListenerService {
- @Override
- public void onTokenRefresh() {
- Intent intent = new Intent(this, XmppConnectionService.class);
- intent.setAction(XmppConnectionService.ACTION_GCM_TOKEN_REFRESH);
- startService(intent);
- }
+ @Override
+ public void onTokenRefresh() {
+ Intent intent = new Intent(this, XmppConnectionService.class);
+ intent.setAction(XmppConnectionService.ACTION_GCM_TOKEN_REFRESH);
+ startService(intent);
+ }
}
diff --git a/src/playstore/java/de/pixart/messenger/services/PushManagementService.java b/src/playstore/java/de/pixart/messenger/services/PushManagementService.java
index 0ab9ba5ac..9680a65c2 100644
--- a/src/playstore/java/de/pixart/messenger/services/PushManagementService.java
+++ b/src/playstore/java/de/pixart/messenger/services/PushManagementService.java
@@ -21,98 +21,98 @@ import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class PushManagementService {
- private static final String APP_SERVER = "push.siacs.eu";
-
- protected final XmppConnectionService mXmppConnectionService;
-
- public PushManagementService(XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
-
- public void registerPushTokenOnServer(final Account account) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": has push support");
- retrieveGcmInstanceToken(new OnGcmInstanceTokenRetrieved() {
- @Override
- public void onGcmInstanceTokenRetrieved(String token) {
- try {
- final String deviceId = Settings.Secure.getString(mXmppConnectionService.getContentResolver(), Settings.Secure.ANDROID_ID);
- IqPacket packet = mXmppConnectionService.getIqGenerator().pushTokenToAppServer(Jid.fromString(APP_SERVER), token, deviceId);
- mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- Element command = packet.findChild("command","http://jabber.org/protocol/commands");
- if (packet.getType() == IqPacket.TYPE.RESULT && command != null) {
- Element x = command.findChild("x","jabber:x:data");
- if (x != null) {
- Data data = Data.parse(x);
- try {
- String node = data.getValue("node");
- String secret = data.getValue("secret");
- Jid jid = Jid.fromString(data.getValue("jid"));
- if (node != null && secret != null) {
- enablePushOnServer(account, jid, node, secret);
- }
- } catch (InvalidJidException e) {
- e.printStackTrace();
- }
- }
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": invalid response from app server");
- }
- }
- });
- } catch (InvalidJidException ignored) {
-
- }
- }
- });
- }
-
- private void enablePushOnServer(final Account account, final Jid jid, final String node, final String secret) {
- IqPacket enable = mXmppConnectionService.getIqGenerator().enablePush(jid, node, secret);
- mXmppConnectionService.sendIqPacket(account, enable, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": successfully enabled push on server");
- } else if (packet.getType() == IqPacket.TYPE.ERROR) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": enabling push on server failed");
- }
- }
- });
- }
-
- private void retrieveGcmInstanceToken(final OnGcmInstanceTokenRetrieved instanceTokenRetrieved) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- InstanceID instanceID = InstanceID.getInstance(mXmppConnectionService);
- try {
- String token = instanceID.getToken(mXmppConnectionService.getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
- instanceTokenRetrieved.onGcmInstanceTokenRetrieved(token);
- } catch (Exception e) {
- Log.d(Config.LOGTAG,"unable to get push token");
- }
- }
- }).start();
-
- }
-
-
- public boolean available(Account account) {
- final XmppConnection connection = account.getXmppConnection();
- return connection != null && connection.getFeatures().push() && playServicesAvailable();
- }
-
- private boolean playServicesAvailable() {
- return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(mXmppConnectionService) == ConnectionResult.SUCCESS;
- }
-
- public boolean isStub() {
- return false;
- }
-
- interface OnGcmInstanceTokenRetrieved {
- void onGcmInstanceTokenRetrieved(String token);
- }
+ private static final String APP_SERVER = "push.siacs.eu";
+
+ protected final XmppConnectionService mXmppConnectionService;
+
+ public PushManagementService(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
+
+ public void registerPushTokenOnServer(final Account account) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": has push support");
+ retrieveGcmInstanceToken(new OnGcmInstanceTokenRetrieved() {
+ @Override
+ public void onGcmInstanceTokenRetrieved(String token) {
+ try {
+ final String deviceId = Settings.Secure.getString(mXmppConnectionService.getContentResolver(), Settings.Secure.ANDROID_ID);
+ IqPacket packet = mXmppConnectionService.getIqGenerator().pushTokenToAppServer(Jid.fromString(APP_SERVER), token, deviceId);
+ mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Element command = packet.findChild("command", "http://jabber.org/protocol/commands");
+ if (packet.getType() == IqPacket.TYPE.RESULT && command != null) {
+ Element x = command.findChild("x", "jabber:x:data");
+ if (x != null) {
+ Data data = Data.parse(x);
+ try {
+ String node = data.getValue("node");
+ String secret = data.getValue("secret");
+ Jid jid = Jid.fromString(data.getValue("jid"));
+ if (node != null && secret != null) {
+ enablePushOnServer(account, jid, node, secret);
+ }
+ } catch (InvalidJidException e) {
+ e.printStackTrace();
+ }
+ }
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": invalid response from app server");
+ }
+ }
+ });
+ } catch (InvalidJidException ignored) {
+
+ }
+ }
+ });
+ }
+
+ private void enablePushOnServer(final Account account, final Jid jid, final String node, final String secret) {
+ IqPacket enable = mXmppConnectionService.getIqGenerator().enablePush(jid, node, secret);
+ mXmppConnectionService.sendIqPacket(account, enable, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": successfully enabled push on server");
+ } else if (packet.getType() == IqPacket.TYPE.ERROR) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": enabling push on server failed");
+ }
+ }
+ });
+ }
+
+ private void retrieveGcmInstanceToken(final OnGcmInstanceTokenRetrieved instanceTokenRetrieved) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ InstanceID instanceID = InstanceID.getInstance(mXmppConnectionService);
+ try {
+ String token = instanceID.getToken(mXmppConnectionService.getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
+ instanceTokenRetrieved.onGcmInstanceTokenRetrieved(token);
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, "unable to get push token");
+ }
+ }
+ }).start();
+
+ }
+
+
+ public boolean available(Account account) {
+ final XmppConnection connection = account.getXmppConnection();
+ return connection != null && connection.getFeatures().push() && playServicesAvailable();
+ }
+
+ private boolean playServicesAvailable() {
+ return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(mXmppConnectionService) == ConnectionResult.SUCCESS;
+ }
+
+ public boolean isStub() {
+ return false;
+ }
+
+ interface OnGcmInstanceTokenRetrieved {
+ void onGcmInstanceTokenRetrieved(String token);
+ }
}
diff --git a/src/playstore/java/de/pixart/messenger/services/PushMessageReceiver.java b/src/playstore/java/de/pixart/messenger/services/PushMessageReceiver.java
index 0b99cfd9f..33c5119e0 100644
--- a/src/playstore/java/de/pixart/messenger/services/PushMessageReceiver.java
+++ b/src/playstore/java/de/pixart/messenger/services/PushMessageReceiver.java
@@ -7,11 +7,11 @@ import com.google.android.gms.gcm.GcmListenerService;
public class PushMessageReceiver extends GcmListenerService {
- @Override
- public void onMessageReceived(String from, Bundle data) {
- Intent intent = new Intent(this, XmppConnectionService.class);
- intent.setAction(XmppConnectionService.ACTION_GCM_MESSAGE_RECEIVED);
- intent.replaceExtras(data);
- startService(intent);
- }
+ @Override
+ public void onMessageReceived(String from, Bundle data) {
+ Intent intent = new Intent(this, XmppConnectionService.class);
+ intent.setAction(XmppConnectionService.ACTION_GCM_MESSAGE_RECEIVED);
+ intent.replaceExtras(data);
+ startService(intent);
+ }
}