Compare commits

...

438 Commits

Author SHA1 Message Date
B. Petersen fab24ee956 Update messenger-backend submodule. 2017-03-22 23:26:07 +01:00
B. Petersen 94f4c8b302 Update changelog. 2017-03-22 23:25:50 +01:00
B. Petersen 008a2410a7 Update changelog. 2017-03-22 15:55:46 +01:00
B. Petersen b43f337af9 Show language name in english in language select activity. 2017-03-22 15:49:56 +01:00
Björn Petersen 21895cf00f Merge pull request #59 from aligitor/master
Add Hungarian translation.
2017-03-22 15:31:53 +01:00
aligitor 721d016d86 Create strings.xml
Hungarian translation file.
2017-03-22 12:42:38 +01:00
B. Petersen 89aaa9c072 Show percentage in the configuration progress dialog. 2017-03-22 00:04:27 +01:00
B. Petersen bcb00a9d23 Add IMAP/SMTP security options. 2017-03-20 21:11:59 +01:00
B. Petersen 34cd40fdd9 Support HTML-only messages. 2017-03-18 11:18:35 +01:00
B. Petersen c8001cedce Provide an http/https-get function for the backend. 2017-03-15 22:23:28 +01:00
B. Petersen 880353888b Add input types for the account configuration dialog. 2017-03-15 21:04:10 +01:00
B. Petersen 2c67d20233 Add options for SSL/TLS and STARTTLS. 2017-03-15 16:32:15 +01:00
B. Petersen 42c08a4d5b Test IMAP and SMTP server when calling the configure-function. 2017-03-14 18:25:20 +01:00
B. Petersen 01bf0f3af5 Remove unused notifications. 2017-03-14 11:41:29 +01:00
B. Petersen e765968c2c Language (do not show 'configuring account' message as the may be interpreted as we do something with the server settings). 2017-03-14 11:09:49 +01:00
B. Petersen 3837463e41 Use larger labels for the basic account settings. 2017-03-13 23:37:13 +01:00
B. Petersen 545a2ddc24 Hide advanced account settings by default (in general, we'll try to find them out automatically). 2017-03-13 23:12:56 +01:00
B. Petersen 4f6f0b4685 Cleanup cache settings code. 2017-03-13 19:36:41 +01:00
B. Petersen 1d1bad8b3b Fine-tuning error handling. 2017-03-13 14:00:30 +01:00
B. Petersen af5769f231 Improve error handling. 2017-03-12 21:03:28 +01:00
B. Petersen 42c6337c4d Show popup errors. 2017-03-12 00:33:08 +01:00
B. Petersen 37a49c9d39 Add Korean. 2017-03-11 22:41:45 +01:00
B. Petersen 3b2f778114 Repair 'Default language' option and cleanup some language stuff. 2017-03-11 21:10:31 +01:00
B. Petersen b55cc03f6d Correct string. 2017-03-11 18:31:02 +01:00
B. Petersen 2e6eead045 Change the order of the account input fields. 2017-03-11 18:17:44 +01:00
B. Petersen e77c3303a1 Show versionCode in about dialog. 2017-03-11 11:55:32 +01:00
B. Petersen e83c4f315c Force unique video names in the blob directory. 2017-03-11 11:35:34 +01:00
B. Petersen 833c75d874 Change order of 'new group icons'. 2017-03-11 10:45:47 +01:00
B. Petersen 9688003648 Force video-recoding for videos which size is okay but the bitrate is too high. Correct maximum bitrate handing (was checked too son before and scaled down afterwards, on typical devices, this resulted in a bitrate of about 500 kbit which we use as a maximum now; everyone is welcome to improve all this stuff). 2017-03-10 19:15:54 +01:00
B. Petersen a2609d5bbe Catch video thumbnail creation errors. 2017-03-10 19:11:49 +01:00
B. Petersen 227f129455 Update changelog. 2017-03-10 13:43:32 +01:00
B. Petersen 4e683e5b37 Update messenger-backend submodule. 2017-03-10 13:29:38 +01:00
B. Petersen c5a0a00dd8 In video editor, show resulting length and size in action bar. 2017-03-10 13:25:46 +01:00
B. Petersen de0bce163f Move 'done' button in the group-creation dialog to the very most right position. 2017-03-10 12:05:46 +01:00
B. Petersen 9dcb58c1ea Use SI-prefix for file sizes. 2017-03-10 11:30:33 +01:00
B. Petersen 4729642f87 Show play-button overlay in video preview. 2017-03-10 01:28:58 +01:00
B. Petersen 751cbfa635 Create and use video thumbnails. 2017-03-10 01:17:26 +01:00
B. Petersen e40cae149b Mark some members as being final or remove them. 2017-03-09 21:33:49 +01:00
B. Petersen 758dfbaa67 Remove unneeded FileDidUpload and FileDidFailUpload notifications. 2017-03-09 19:08:48 +01:00
B. Petersen 29ec49b806 Show sended video messages immediately by using the new .increation method (before, video messages are shown after compression is done; this may take several 10th's seconds). 2017-03-09 18:43:07 +01:00
B. Petersen 8afb5595d0 Read bytes from file system, not from database. 2017-03-09 12:25:33 +01:00
B. Petersen 8430e9ddc6 In full-screen-dialogs with okay-checkmarks, we use a cancel-button instead of an ambiguous back-button, see the Material Design Guidelines, https://material.io/guidelines/components/dialogs.html#dialogs-alerts . 2017-03-09 11:44:29 +01:00
B. Petersen 7439fafe2b Name passcode activity screens. 2017-03-09 11:33:51 +01:00
B. Petersen e1655a40bd Start showing MiB already at 999 KiB, this avoids confusion with four digit KiB which may be being already Megabytes ... 2017-03-09 10:00:04 +01:00
B. Petersen 92fc521d5b If we're not playing, a long press on the waveform selects the whole message as usual. 2017-03-09 09:30:49 +01:00
B. Petersen 023d34f2b6 Simplify size formatting.. 2017-03-09 09:15:58 +01:00
B. Petersen 8fc064a5a4 Show videos using the system player, this seems to be more reliable. 2017-03-08 22:04:17 +01:00
B. Petersen b9a329f416 Recode and send videos. 2017-03-08 21:59:39 +01:00
B. Petersen 4997fa7301 Prepare sendMessage() function for async messages (needed for videos which must be recorded first). 2017-03-08 20:51:14 +01:00
B. Petersen e267d67d6e Stop playback if the playing message gets deleted. 2017-03-08 20:48:02 +01:00
B. Petersen 83b04a97ef Recode videos with the same size but different rotation. 2017-03-08 20:44:06 +01:00
B. Petersen 7852c9e9f9 Light video editor layout. 2017-03-08 16:09:54 +01:00
B. Petersen 009adc09bc Removed unneeded image search function, cleanup gallery code. 2017-03-08 13:57:18 +01:00
B. Petersen 4d3929576d Use light gallery theme, use default colors. 2017-03-08 12:23:39 +01:00
B. Petersen 7712def225 Prepare sending videos. 2017-03-08 00:19:28 +01:00
B. Petersen 9509919431 Repeat the normal play/pause buttons in the status-bar-player for internal consistence. 2017-03-07 23:13:26 +01:00
B. Petersen 15cc62f559 Make status-bar-player layout more compatible. 2017-03-07 22:44:26 +01:00
B. Petersen ae46cbbf5e Remove content intent for status-bar-player (unexpected behaviour). 2017-03-07 19:20:00 +01:00
B. Petersen 1664fcdc84 Remove suspicious voice proximity commands. 2017-03-07 19:17:16 +01:00
B. Petersen 2c45f4af37 Allow generating waveforms without a messsage object. 2017-03-07 19:00:14 +01:00
B. Petersen b3d1747c0b Fix a redrawing bug on Kit Kat. 2017-03-07 18:05:40 +01:00
B. Petersen 693952d0eb Show waveform for voice messages. 2017-03-07 17:17:37 +01:00
B. Petersen 0ad5bd6e48 Update some colors and measures. 2017-03-07 14:47:34 +01:00
B. Petersen 89c34f89c9 Remove screenshot observer. We do not want to observe the users of the app; moreover, this is useless and results in false security. 2017-03-07 13:13:20 +01:00
B. Petersen 70971a73ad Remove unused download queues, this stuff goes to the backend, if needed. 2017-03-07 12:39:28 +01:00
B. Petersen 2c5b377712 Check permission on raise-to-speak. 2017-03-07 12:06:35 +01:00
B. Petersen ae4b5a711b Go to previous/next media. 2017-03-06 19:31:52 +01:00
B. Petersen 252bc52d47 Show correct media information from the backend. 2017-03-06 17:11:12 +01:00
B. Petersen 655783db1e Make mrpoortext_t more general-purpose-use. 2017-03-05 16:53:16 +01:00
B. Petersen 10b2af3656 Simplify audio select dialog. 2017-03-05 14:23:31 +01:00
B. Petersen 54c3a963a4 Smarter voice message preview. 2017-03-03 21:32:56 +01:00
B. Petersen a7836eaded Sending voice messages using 'Raise to speak' :-) 2017-03-03 19:20:25 +01:00
B. Petersen 10fcbdc81d Bump version. 2017-03-03 16:33:32 +01:00
B. Petersen b2a5885787 Show ABI separated from version. 2017-03-03 16:15:35 +01:00
B. Petersen 9886a4feb4 Add advanced settings dialog, remove some upload functions. 2017-03-03 15:26:17 +01:00
B. Petersen dcd04f7064 Remove always-empty-objects. 2017-03-03 15:05:37 +01:00
B. Petersen 5773ce8b83 Update gradle. 2017-03-03 14:38:46 +01:00
B. Petersen 0c3eb19de6 Rewrite settings dialogs. 2017-03-03 14:37:30 +01:00
B. Petersen 5e72e30d58 Fix endless redraw loop for voice message showing. 2017-03-02 23:23:48 +01:00
B. Petersen c9d8ad3d4a Show the status-bar-player for music as well as for voice messages. 2017-03-02 22:26:57 +01:00
B. Petersen 62bd4f6d9f Cleanup some ChatMessageCell conditions. 2017-03-02 21:48:02 +01:00
B. Petersen 15e1d55a77 Send voice messages as voice messages. 2017-03-02 21:22:34 +01:00
B. Petersen 04cab2ee0c Remove webpage message types. 2017-03-02 21:19:51 +01:00
B. Petersen fd28868ea8 Stop player when deselecting an audio track in the audio-select-activity. 2017-03-02 17:43:00 +01:00
B. Petersen 129e6ad487 Do not hide the default-send-key if send-by-enter is enables (looks like a bug). 2017-03-02 16:43:37 +01:00
B. Petersen 082f7229f2 Visualise enabled send-by-enter-function by hiding the normal send-button and showing a send key instead of a new-line-key on the keyboard. 2017-03-02 15:09:29 +01:00
B. Petersen 1c6001ed92 Remove unused top-view from ChatActivityEnterView. 2017-03-02 13:43:27 +01:00
B. Petersen 2ec48ef1e6 Remove unused cancelBotButton. 2017-03-02 13:19:43 +01:00
B. Petersen 04397f3cb0 Remove unused caption-text-view-layout. 2017-03-02 12:51:18 +01:00
B. Petersen 8697756083 Cleanup setting page. 2017-03-02 11:32:07 +01:00
B. Petersen 7be5ec589c Remove unneded permissions. 2017-03-01 22:05:57 +01:00
B. Petersen d3fbf529eb Remove (hopefully) unneeded AbstractThreadedSyncAdapter. 2017-03-01 20:14:39 +01:00
B. Petersen e47818b5df Remove currently unused and not implemented ChooserTargetService. 2017-03-01 18:56:23 +01:00
B. Petersen 201c9ebf4c Remove currently unused AccountAuthenticator service. 2017-03-01 18:36:14 +01:00
B. Petersen 0626192659 Allow 'Open messages' only for messages with an attachment. 2017-03-01 17:57:10 +01:00
B. Petersen 0acdd3f885 Enable basic voice message recording. 2017-03-01 15:18:26 +01:00
B. Petersen 6f0fd1072d Remove disturbing 'big' notification-music-player. Update notification-music-player logo and colors. 2017-03-01 14:22:36 +01:00
B. Petersen d17579b928 Do not expose data through MediaBrowserService, this seems unexpected for an messenger. Instead, the user can use 'save to music' or 'share' explicitly for items not privacy related. 2017-03-01 11:30:39 +01:00
B. Petersen 090039ec29 Remove internal player and rely on the MusicService 2017-02-28 23:18:07 +01:00
B. Petersen df8bbcd00a Orange audio seekbar. 2017-02-28 22:19:23 +01:00
B. Petersen fffc77cc82 Play audio files. 2017-02-28 21:53:49 +01:00
B. Petersen 9ee5af9fd2 Update German. 2017-02-28 20:53:45 +01:00
B. Petersen 5462f3d341 Add 'Open' command for all file types. 2017-02-28 19:15:31 +01:00
B. Petersen 46dca0d212 Remove unused resources. 2017-02-28 18:39:25 +01:00
B. Petersen 603572c796 Remove (hopefully) unneeded ShareBroadcastReceiver class. 2017-02-28 17:45:11 +01:00
B. Petersen db6c2d9d7c Show 'File not found' hint. 2017-02-28 17:03:25 +01:00
B. Petersen b6c6375492 More senseful name for new-chat-from-deaddrop-button. 2017-02-28 16:08:52 +01:00
B. Petersen 3f5255c116 Remove load and cancel buttons from ChatMessageCell, cleanup. 2017-02-28 15:59:15 +01:00
B. Petersen 68f3974aa2 Name button states. 2017-02-28 14:26:45 +01:00
B. Petersen 2c062d8d51 Select and send audio attachments via the music browser. 2017-02-27 22:35:20 +01:00
B. Petersen 1097157f69 Show icon for incoming audio files. 2017-02-27 22:01:52 +01:00
B. Petersen 21784aa951 Simplify and rework message sending. 2017-02-27 19:24:55 +01:00
B. Petersen f980f7285a Remove unused 'Reply'-parameters, continued. 2017-02-27 17:12:04 +01:00
B. Petersen 622b6bc786 Remove unused 'Reply'-parameters. 2017-02-27 16:26:44 +01:00
B. Petersen 61fc0485b7 Unify attach icons. 2017-02-27 12:25:22 +01:00
B. Petersen be2af9c871 Name the request codes. Wondering how this was editable before. 2017-02-24 21:42:31 +01:00
B. Petersen 898e4a45e3 Remove unneeded merge-dialog option. 2017-02-24 20:48:04 +01:00
B. Petersen e47c6406f8 Implement share from chat/photo view. 2017-02-24 19:41:18 +01:00
B. Petersen 1d0ed83bad Implement save to downloads/music/gallery. 2017-02-24 17:34:54 +01:00
Björn Petersen 404f6ea9da Update CHANGELOG.md 2017-02-24 12:00:52 +01:00
B. Petersen 8d75f867fd Add menu buttons for saving and sharing attachments. 2017-02-23 23:59:45 +01:00
B. Petersen 83f60e6252 Remove unused file observers. 2017-02-23 22:44:03 +01:00
B. Petersen c4bd6576d8 Simplify object creation. 2017-02-23 22:43:31 +01:00
B. Petersen 12df911162 Use the standard text view for pincode entering; otherwise, the last entered number is always shown for a moment, independently of the system setting (in fact, for pincodes, currently, the number is _never_ shown as setText() does not work this way; however, this is just fine, much better). 2017-02-23 21:06:53 +01:00
B. Petersen f5a37ee185 Redo false commit. 2017-02-23 20:36:01 +01:00
B. Petersen 92d71ad150 When locking the app, try to clear the screenshot that is shown in the task switcher. 2017-02-23 20:30:15 +01:00
B. Petersen 8824b30c4a Remove unused import. 2017-02-23 16:38:37 +01:00
B. Petersen 741e281a37 Dismiss app when touching the lock-symbol. 2017-02-23 16:37:22 +01:00
B. Petersen 354295ae7a Unify passcode-enter-dialog background. 2017-02-23 15:15:43 +01:00
B. Petersen 09af0640c7 Simplify fingerprint dialog. 2017-02-23 14:50:28 +01:00
B. Petersen 1485eeecf6 Do not use the new AttachmentsContentProvider for pulic URLs as used when attaching local files. 2017-02-23 13:28:31 +01:00
B. Petersen 8b78df6a14 Remove attach size limit. 2017-02-22 23:09:56 +01:00
B. Petersen 686ee6c246 Coloize attachment file name in chat view. 2017-02-22 22:56:32 +01:00
B. Petersen ca8114d9af Simplify and beautify file selector. 2017-02-22 22:33:23 +01:00
B. Petersen b2392c90c2 Use the same colors for the time and for the forward title. 2017-02-22 21:18:51 +01:00
B. Petersen d207fa67e5 Remove unused URL-searching when entering text. 2017-02-22 20:53:13 +01:00
B. Petersen 2e41fe756a Show correct error box when an attachment cannot be opened. 2017-02-22 20:43:05 +01:00
B. Petersen d533ea5c37 Open attachments by using a content provider. 2017-02-22 19:26:18 +01:00
B. Petersen 4365e176d8 Remove dead code. 2017-02-22 17:53:21 +01:00
B. Petersen 486b03e031 Update messenger-backend submodule. 2017-02-22 17:34:15 +01:00
B. Petersen 1dbf08327c Ignore some files. 2017-02-22 16:56:30 +01:00
B. Petersen 0d15bb3544 Add missing file, ignore files marked as .prv (useful for scripts etc.). 2017-02-22 16:18:57 +01:00
B. Petersen e3b48206e9 Remove 'Done'-hint after message deletion; as normally at least one of the selected message is in view when this function is used, there is enough visual feedback (we want the 'Done'-hint only for actions without visual feedback as copy-to-clipboard). 2017-02-22 15:50:25 +01:00
B. Petersen a0e364a50f Show any documents attached (opening still not working). 2017-02-21 23:21:09 +01:00
B. Petersen 0db3fc55c6 Use binary prefix for file sizes. 2017-02-21 22:18:47 +01:00
B. Petersen 95c3fca389 Show some hints on silent actions as copy-to-clipboard or delete-contact. 2017-02-21 21:52:24 +01:00
B. Petersen 68a72fb8fd Typo. 2017-02-21 18:43:15 +01:00
B. Petersen 473410ece2 Change urls to https://getdelta.org . 2017-02-21 18:40:42 +01:00
B. Petersen 4f760a790a Differ betweend music and voice messages. 2017-02-21 17:02:49 +01:00
B. Petersen ad7ee4428f Remove duplicate strings. 2017-02-21 14:28:31 +01:00
B. Petersen 92cf756ad2 Cleanup reply option in ChatMessageCell. 2017-02-20 22:38:24 +01:00
B. Petersen 3d746f08ba Remove unused message object types. 2017-02-20 21:58:25 +01:00
B. Petersen e272459fc9 Use named values for MessageObject.type; wondering how and by whom this was ever usable before. 2017-02-20 21:43:50 +01:00
B. Petersen a793ae52f9 Make file selection dialog usable. 2017-02-20 20:43:18 +01:00
B. Petersen f2349a9bdd Accept vcards shared to us by external apps. Remove unused contact and location code. 2017-02-20 19:12:29 +01:00
B. Petersen 7082798c03 Swap File- and Contact-attach icons. 2017-02-20 14:44:35 +01:00
B. Petersen f4fc5fae2b Cleanup camera preview code. 2017-02-20 13:57:36 +01:00
B. Petersen 5799e43b3a Update Polish. 2017-02-20 11:54:51 +01:00
B. Petersen f929d6cc31 Cleanup SendMessagesHelper.sendMessage__(), fix a crash when trying to send music files. 2017-02-19 13:04:17 +01:00
B. Petersen 9f92efc554 More neutral language when choosing contacts. 2017-02-19 12:14:23 +01:00
B. Petersen 3338b83910 Pimp attach gui. 2017-02-18 23:57:43 +01:00
B. Petersen a4c104431d Show a hint after deletion of a contact. 2017-02-18 22:49:08 +01:00
B. Petersen 31ba521244 Prefer the term 'contact' over 'user', unify usage. 2017-02-18 22:26:16 +01:00
B. Petersen 50dfe15f09 Show a hint if a user is already in a group. 2017-02-18 21:38:04 +01:00
Dr. Tobias Quathamer 4b06d19787 Add item to changelog for v0.1.20 2017-02-18 16:58:27 +01:00
B. Petersen 66d147eb8b Implement sending contact's addresses. 2017-02-17 21:50:31 +01:00
Björn Petersen d2591922a6 Update CHANGELOG.md 2017-02-17 21:14:34 +01:00
B. Petersen bcc6f79373 More options in URL's context menu: Create chat, create user, open externally, copy to clipboard. 2017-02-17 21:03:25 +01:00
B. Petersen 015edd6053 Create a new chat by clicking on email-links. 2017-02-17 19:26:25 +01:00
B. Petersen bec562bfb2 Linkify email-addresses. 2017-02-17 19:08:32 +01:00
B. Petersen 01a7c93771 If an avatar is shown for a message in group layout, make the bubble a little bit smaller. 2017-02-17 17:06:18 +01:00
B. Petersen 502bae625f Cleanup MessageObject. 2017-02-17 16:33:48 +01:00
B. Petersen a34d511922 Update Polish. 2017-02-17 15:50:57 +01:00
B. Petersen e65aa19109 Fix a null-pointer exception when using tablets. 2017-02-17 13:38:23 +01:00
B. Petersen 30585b8de4 More background wallpaper dynamic. 2017-02-16 23:19:09 +01:00
B. Petersen 0bd4234214 Cleanup drawer profile cell. 2017-02-16 22:42:07 +01:00
B. Petersen db4ceb6d68 Prepare release v0.1.20 (we forgot to update the submodule in v0.1.19). 2017-02-16 20:49:29 +01:00
B. Petersen 1612a5d7cd Update messenger-backend submodule. 2017-02-16 20:42:11 +01:00
B. Petersen f60f4539b5 Prepare release v0.1.19 2017-02-16 16:30:38 +01:00
B. Petersen db7c8a9dfc Update changelog. 2017-02-16 15:19:45 +01:00
B. Petersen 9d1f4266c2 Update changelog. 2017-02-15 22:46:07 +01:00
Björn Petersen 0614c4fc32 Update README.md 2017-02-14 19:57:56 +01:00
B. Petersen 58e452b711 Update polish. 2017-02-14 15:26:58 +01:00
Dr. Tobias Quathamer b478696bed Target API level 25 (Nougat 7.1) 2017-02-13 21:19:47 +01:00
B. Petersen f3584d9eaf Update Portuguese. 2017-02-13 20:37:35 +01:00
B. Petersen d9998167f4 Colorize the background image. 2017-02-13 17:59:06 +01:00
Dr. Tobias Quathamer 874e300220 Remove LocaleController calls 2017-02-11 17:48:41 +01:00
Dr. Tobias Quathamer 49e4b90cc3 Remove unused code 2017-02-11 17:42:35 +01:00
Dr. Tobias Quathamer f492f2cd71 Prepare release 0.1.18 2017-02-11 17:23:49 +01:00
Dr. Tobias Quathamer 2800a282ec Update backend to v0.1.18 2017-02-11 17:16:57 +01:00
Dr. Tobias Quathamer 593e93304c Update changelog 2017-02-10 21:43:36 +01:00
Dr. Tobias Quathamer 77b6108ca8 Fix lint warning, use apply() instead of commit().
commit() writes its data to persistent storage immediately,
whereas apply() will handle it in the background.
2017-02-10 21:43:11 +01:00
Dr. Tobias Quathamer 90bf2b6b19 Update changelog 2017-02-10 21:15:05 +01:00
B. Petersen dd91d52df4 Merge branch 'master' of https://github.com/r10s/messenger-android 2017-02-10 21:07:04 +01:00
B. Petersen de29317fa6 Use standard placeholders in local files. 2017-02-10 21:06:54 +01:00
Dr. Tobias Quathamer 44faee6208 Update backend for plural handling 2017-02-10 21:04:45 +01:00
Dr. Tobias Quathamer 41ccf19e58 Fix typo 2017-02-10 21:03:31 +01:00
B. Petersen 05c390980a Support 'plurals'-strings in the backend. 2017-02-10 20:11:02 +01:00
B. Petersen 4446ed0792 Remove unused peer/empty class. 2017-02-10 16:29:58 +01:00
Dr. Tobias Quathamer 11942da0c0 Update changelog 2017-02-10 16:25:24 +01:00
B. Petersen 9a0604a1a8 Update polish translation. 2017-02-10 16:07:11 +01:00
B. Petersen 93ca874e61 Merge branch 'master' of https://github.com/r10s/messenger-android 2017-02-10 15:47:25 +01:00
B. Petersen 9ba21c4346 Colorize settings headlines. 2017-02-10 15:47:08 +01:00
Dr. Tobias Quathamer c0e7798cad Remove class AndroidAuthenticator.
The class is never used and requires the permission USE_CREDENTIALS.
That permission has never been requested, so the code would fail anyway.
2017-02-10 14:40:20 +01:00
Dr. Tobias Quathamer 0aea5a2201 Replace LocaleController 2017-02-10 14:35:27 +01:00
B. Petersen 63873ec1d5 Slightly optimize colors. 2017-02-10 13:58:16 +01:00
B. Petersen 23f269d4b3 Remove complicated and unreliable calculation of shading colors. 2017-02-10 02:01:41 +01:00
B. Petersen 9ebacb23c4 Bold date headlines. 2017-02-10 00:55:38 +01:00
B. Petersen d1e83f46d8 Improve typography, do not mix system font with resource font which looks ugly when not using 'Roboto'. 2017-02-10 00:14:34 +01:00
B. Petersen 5f2dd23a6e Remove unused chat alert and profile search cell. 2017-02-09 22:48:51 +01:00
B. Petersen d29b24e9a6 Cleanup code in the chat activitie's date headlines. 2017-02-09 18:52:01 +01:00
Dr. Tobias Quathamer 4f1415e127 Remove condition which is always true 2017-02-09 14:32:55 +01:00
Dr. Tobias Quathamer c604313c09 Replace LocaleController with android methods 2017-02-09 14:32:08 +01:00
Dr. Tobias Quathamer e6bc14a8de Replace LocaleController with android methods 2017-02-09 14:14:27 +01:00
Dr. Tobias Quathamer 4a5729d371 Remove unneeded translations 2017-02-09 14:06:40 +01:00
Dr. Tobias Quathamer d9ac76a317 Remove custom plural handling from LocaleController 2017-02-09 13:54:39 +01:00
Dr. Tobias Quathamer 867df35d44 Remove unused strings 2017-02-09 11:25:00 +01:00
Dr. Tobias Quathamer e946d61b92 Use android plural handling for 'Times' 2017-02-09 10:30:42 +01:00
Dr. Tobias Quathamer fa2ffbd70f Rewrite more plural handling of 'Days' 2017-02-09 10:03:45 +01:00
Dr. Tobias Quathamer 1123f03134 Use android plural handling for 'Days' 2017-02-09 10:00:09 +01:00
Dr. Tobias Quathamer 49e86d08bf Remove unneeded string 'MuteFor' 2017-02-09 09:50:03 +01:00
Dr. Tobias Quathamer 5f00826e65 Rewrite more plural handling of 'Hours' 2017-02-09 09:48:22 +01:00
Dr. Tobias Quathamer 82382f31f1 Use android plural handling for 'Hours' 2017-02-09 09:37:09 +01:00
Dr. Tobias Quathamer 833cbf7302 Rewrite more plural handling of 'Minutes' 2017-02-09 09:31:53 +01:00
Dr. Tobias Quathamer 4e329adaca Use android plural handling for 'Minutes' 2017-02-08 22:39:31 +01:00
Dr. Tobias Quathamer dbc9e4c3c2 Add German translation 2017-02-08 22:05:33 +01:00
Dr. Tobias Quathamer d9cff40fd5 Remove unneeded translation 'FromChats' 2017-02-08 22:04:20 +01:00
Dr. Tobias Quathamer ef248712d6 Use android plural handling 2017-02-08 22:03:45 +01:00
Dr. Tobias Quathamer 9765ba1bac Remove unneeded plural string 'Stickers' 2017-02-08 19:35:52 +01:00
Dr. Tobias Quathamer afcccaa864 Remove unused class 'StickerSetCell' 2017-02-08 19:34:37 +01:00
Dr. Tobias Quathamer 7caef2ceb9 Remove unneeded plural string 'Photos' 2017-02-08 19:28:25 +01:00
Dr. Tobias Quathamer cb4480ad78 Remove accidentally translated unused strings 2017-02-08 19:27:42 +01:00
Dr. Tobias Quathamer 6388349c16 Remove unneeded tools hints 2017-02-08 19:27:42 +01:00
B. Petersen 73a111aeac Update default wallpaper, get rid of telegram logo there. 2017-02-08 18:15:04 +01:00
Björn Petersen 5869bdc9dd Update CHANGELOG.md 2017-02-08 16:19:39 +01:00
B. Petersen ff7f75abed Add Polish translation file. 2017-02-08 12:13:57 +01:00
B. Petersen 28ac3bd9eb Add Polish translation. 2017-02-08 12:12:33 +01:00
Dr. Tobias Quathamer 49f07874f8 Fix version number in changelog 2017-02-07 22:12:08 +01:00
Dr. Tobias Quathamer 59779b7f2a Prepare release 0.1.17 2017-02-07 22:10:15 +01:00
Dr. Tobias Quathamer 677df359ae Fix plural handling of message deletion 2017-02-07 22:04:33 +01:00
Dr. Tobias Quathamer 4e4aca675f Remove unneeded tools hints 2017-02-07 21:49:07 +01:00
Dr. Tobias Quathamer 76163e6012 Typography: Use ellipsis 2017-02-07 21:39:47 +01:00
Dr. Tobias Quathamer 85b319825d Update Portuguese translation 2017-02-07 21:37:04 +01:00
Dr. Tobias Quathamer 8edf8a6140 Add some more information to the changelog 2017-02-07 21:31:45 +01:00
Dr. Tobias Quathamer acba74142c Add changelog for easier access from F-Droid. Closes #39 2017-02-07 21:14:29 +01:00
Dr. Tobias Quathamer c06f34923e Remove reintroduced strings 2017-02-07 20:29:34 +01:00
B. Petersen bb00342b5d Update portuguese from strings given in issue #38. 2017-02-07 18:22:53 +01:00
Dr. Tobias Quathamer 25b43986d3 Remove unused strings 2017-02-07 12:18:01 +01:00
Dr. Tobias Quathamer 9b5984182f Refactor: Use android plural handling 2017-02-07 12:09:21 +01:00
Dr. Tobias Quathamer ae9dfb1595 Remove unused strings 2017-02-07 10:33:48 +01:00
Dr. Tobias Quathamer 001a4a8675 Remove unused strings 2017-02-07 10:31:15 +01:00
Dr. Tobias Quathamer 13a1e92321 Refactor: Use android plural handling 2017-02-07 10:28:48 +01:00
Dr. Tobias Quathamer fb97c8edaf Refactor: Use android plural handling 2017-02-07 09:55:37 +01:00
Dr. Tobias Quathamer ffce59ea7e Remove unused methods 2017-02-06 18:58:03 +01:00
Dr. Tobias Quathamer dd7ef2d744 Use android's own locale handling 2017-02-06 18:49:13 +01:00
Dr. Tobias Quathamer a76f840368 Remove unused method 2017-02-06 18:37:48 +01:00
Dr. Tobias Quathamer c4bac7359b Remove unused methods and translations 2017-02-06 14:50:32 +01:00
Dr. Tobias Quathamer 67e028502d Refactor: Use android plural handling 2017-02-06 14:39:48 +01:00
Dr. Tobias Quathamer e05b31a76b Remove LocaleController 2017-02-06 14:12:11 +01:00
Dr. Tobias Quathamer d1b95e369d Refactor: Use android plural handling 2017-02-06 14:11:36 +01:00
Dr. Tobias Quathamer 64a71f25f2 Refactor: Use android plural handling 2017-02-06 13:56:51 +01:00
Dr. Tobias Quathamer 76e906579b Really add French translation ... 2017-02-06 13:30:03 +01:00
Dr. Tobias Quathamer 23dc66622b Remove unneeded string resources 2017-02-06 13:01:05 +01:00
Dr. Tobias Quathamer 997bbd336c Comment out unused method 2017-02-06 12:55:06 +01:00
Dr. Tobias Quathamer d8a0225b1a Use only 'fat' productFlavor to speed up build process.
All other flavors are currently not supported by F-Droid, they can
be re-enabled when the app is available through Google Play.
2017-02-06 12:34:48 +01:00
Dr. Tobias Quathamer ce656ca374 Remove unneeded AndroidManifest.xml files and 'foss' build type.
This also drops the permissions ACCESS_COARSE_LOCATION
and ACCESS_FINE_LOCATION which are not used currently.
2017-02-06 11:55:35 +01:00
Dr. Tobias Quathamer dd202b4c72 Add missing android:label to manifest 2017-02-06 11:34:49 +01:00
Dr. Tobias Quathamer 522ff32432 Prepare release 0.1.16 2017-02-06 00:37:07 +01:00
Dr. Tobias Quathamer a96cb520dd Update to backend v0.1.16 2017-02-06 00:32:19 +01:00
Dr. Tobias Quathamer fe9cd52312 Fix some syntax errors 2017-02-05 22:12:29 +01:00
Gilles MOREL d4355ad076 Added French translation 2017-02-05 21:59:37 +01:00
Dr. Tobias Quathamer 057d0eb39e Fix typo 2017-02-05 15:39:47 +01:00
Dr. Tobias Quathamer 82f35621e2 Fix method name typo 2017-02-02 20:46:06 +01:00
Dr. Tobias Quathamer 633856b350 Replace deprecated attribute singleLine with maxLines 2017-02-02 20:00:21 +01:00
Dr. Tobias Quathamer 1c0c9004c6 Convert DOS line endings CRLF to unix line endings, LF. No content changes 2017-02-02 19:59:12 +01:00
Dr. Tobias Quathamer 833533adc3 Remove wrongly lowered TargetAPI 10, project wide is API 14 2017-02-02 17:56:28 +01:00
Dr. Tobias Quathamer 846df6a164 Use a short tag for logging.
There are only 23 characters allowed, the tag
"LinearLayoutManager#LayoutState" was 31 characters.

This fixes a lint error.
2017-01-31 21:20:30 +01:00
Dr. Tobias Quathamer f5de9e8a23 Translate two missing strings 2017-01-31 21:12:01 +01:00
Dr. Tobias Quathamer 404a0ca032 Update Gradle to version 2.14.1 2017-01-31 20:50:40 +01:00
Dr. Tobias Quathamer 6e8612172b Update Android Gradle plugin to version 2.2.3 2017-01-31 20:50:25 +01:00
Dr. Tobias Quathamer aed9e39287 Bump version code for next release 2017-01-31 13:10:38 +01:00
Dr. Tobias Quathamer 597100d9e3 Use new path of backend submodule 2017-01-31 12:27:30 +01:00
Dr. Tobias Quathamer b2775e0811 Include submodule for messenger-backend 2017-01-31 12:16:58 +01:00
Björn Petersen 2425f0dcc7 Merge pull request #17 from toddy15/master
Fix some lint errors and warnings
2017-01-30 18:19:24 +01:00
Dr. Tobias Quathamer ede60340f6 Remove superfluous xmlns from TextView 2017-01-30 16:53:04 +01:00
Dr. Tobias Quathamer a9d58ab3f3 Ignore false positive lint error 2017-01-29 21:05:51 +01:00
Dr. Tobias Quathamer 6d6b6f41c9 Do not fail to build upon lint errors 2017-01-29 19:41:32 +01:00
Dr. Tobias Quathamer f5f97e4bfc Add missing string in default locale 2017-01-29 19:02:22 +01:00
Dr. Tobias Quathamer c7ed50a225 Typography: use ellipsis instead of three dots 2017-01-29 18:56:29 +01:00
Dr. Tobias Quathamer f843c531b4 Typography: use ellipsis instead of three dots 2017-01-29 18:02:33 +01:00
B. Petersen 2389a7d136 Remove bad error message. 2017-01-29 03:13:32 +01:00
B. Petersen e1d0306a29 Typo 2017-01-29 01:27:30 +01:00
B. Petersen 04cbe3f825 Speed up deleting multiple messages. 2017-01-28 18:29:01 +01:00
B. Petersen 9796ac1c53 Show the correct number of unread messages in the home page's icon. 2017-01-28 15:22:43 +01:00
B. Petersen 5721b9f86d Show forwarded headers. 2017-01-27 23:58:00 +01:00
B. Petersen aa090a9cd4 Fix calling forward function. 2017-01-27 12:40:01 +01:00
B. Petersen 03c30bef47 Ask forwarding. 2017-01-26 19:20:43 +01:00
B. Petersen c59453f31b Update colors. 2017-01-26 18:49:11 +01:00
B. Petersen 49e7f19cb7 Prepare forwarding messages. 2017-01-26 17:17:46 +01:00
B. Petersen 469c56f1a6 Pimp selection-action-bar-menu. 2017-01-26 15:17:31 +01:00
B. Petersen 239c56e77d Move copy/reply/info to a submenu in the selection-action-bar-menu. 2017-01-26 01:03:14 +01:00
B. Petersen e32ca20604 Change defaults for functions not yet implemented. 2017-01-24 16:50:15 +01:00
B. Petersen d00ef0af32 Add option to reset a user's encryption key. 2017-01-23 22:45:20 +01:00
B. Petersen e616e57be6 Add E2EE backend file. 2017-01-22 01:05:01 +01:00
B. Petersen 5485db8509 Add E2EE options. 2017-01-21 14:36:47 +01:00
B. Petersen df75d2917f Add options for e2e-encryption and for read receipts. 2017-01-19 18:16:45 +01:00
B. Petersen 0478ea5162 Separate command line stuff from program. 2017-01-19 17:30:29 +01:00
B. Petersen 433e90072f Improve 'no messages' screen and add a hint about greylisting. 2017-01-17 15:06:13 +01:00
B. Petersen 5c646ea663 Show receivers email-address when send messages from extern. 2017-01-16 21:19:43 +01:00
B. Petersen e095d3f886 Cleanup 'Not yet implemented' messages. 2017-01-15 13:18:45 +01:00
B. Petersen 9a6315d0b2 Typo 2017-01-14 17:45:55 +01:00
B. Petersen 8e81f0bb8f Update profile on remote changes.. 2017-01-14 01:47:26 +01:00
B. Petersen d6bdebc865 Localize. 2017-01-14 00:56:29 +01:00
B. Petersen 7ddf368fd1 Cleanup. 2017-01-13 23:19:23 +01:00
B. Petersen 301c6a4430 Increase version. 2017-01-13 18:49:11 +01:00
B. Petersen adaf91e383 Show an error on impossible actions. 2017-01-13 15:18:09 +01:00
B. Petersen 40de9dfe98 Ask before adding/removing members from group. 2017-01-13 11:36:11 +01:00
B. Petersen 18a8e2358c Group name changing and member adding/removing implemented. 2017-01-13 01:09:13 +01:00
B. Petersen e2a4008742 Fix scroll bug. 2017-01-12 17:46:13 +01:00
B. Petersen 258633b672 Propose a message as a draft for new created groups. 2017-01-11 14:36:43 +01:00
B. Petersen 96cc35b84d Add/remove members from group chats. 2017-01-11 01:28:08 +01:00
B. Petersen 787efa3c5d Edit group names. 2017-01-11 00:46:42 +01:00
B. Petersen 848862ae84 Fix a memory leak. 2017-01-11 00:09:50 +01:00
B. Petersen 6d3c170e96 Update avatars on renamings. 2017-01-11 00:01:56 +01:00
B. Petersen a921523014 Add SELF to chat contacts. 2017-01-10 17:07:53 +01:00
B. Petersen 29e5133c63 Create and delete groups. 2017-01-09 23:49:28 +01:00
B. Petersen 8d4aebe8e7 Cleanup. 2017-01-09 21:16:05 +01:00
B. Petersen c0139b1b49 HACK around a keyboard-focus issue. 2017-01-09 17:08:28 +01:00
B. Petersen 920d55ee6d Cleanup, remove unused AOSP files. 2017-01-09 14:53:56 +01:00
B. Petersen 4348b80b02 Show unread count in search message results only if the message itself is unread. 2017-01-09 14:27:12 +01:00
B. Petersen 1a62ed853b Show search summaries. 2017-01-09 14:11:18 +01:00
B. Petersen f858cc7747 Implement global search. 2017-01-09 02:10:10 +01:00
B. Petersen 394b439a06 Explain non-hide-floating. 2017-01-08 21:53:16 +01:00
B. Petersen b515660bac Cleanup locale. 2017-01-08 21:24:20 +01:00
B. Petersen 88719df0af Remove dead mention code. 2017-01-08 21:00:09 +01:00
B. Petersen 495dfa0f59 Force redraw when the query gets empty. 2017-01-08 14:49:57 +01:00
B. Petersen f7372819a1 Pimp in-chat-search. 2017-01-08 14:39:51 +01:00
B. Petersen 9d622ecef7 Show empty bottom overlay on in-chat-search. 2017-01-08 12:56:17 +01:00
B. Petersen 221004354e Move search up/down buttons to action bar; also show the number of hits there. 2017-01-08 02:11:14 +01:00
B. Petersen 442baa6aa9 Implement search 2017-01-07 20:27:38 +01:00
B. Petersen 2e4f748093 Allow using the menu if there is a draft; add 'Attach files' as a normal menu entry to access this function if the normal 'attach' button is replaces by the 'send' button. 2017-01-07 18:26:34 +01:00
B. Petersen 56a846ab3b Remove dead 'mentions' code from ChatActivity. 2017-01-07 15:12:42 +01:00
B. Petersen f67e24b49e Cleanup 2017-01-07 14:36:58 +01:00
B. Petersen f40a3e49cb Also use 'speech bubbles' for photos and media. 2017-01-06 15:57:34 +01:00
B. Petersen 04b815bb49 Clearify coordinates. 2017-01-06 15:39:12 +01:00
B. Petersen 3292979696 Fast loading of large chats. 2017-01-06 02:54:29 +01:00
B. Petersen 3e24ca9be1 Update chat with incoming/outgoing messages. 2017-01-05 18:41:45 +01:00
B. Petersen 38e5350219 Use a virtual listbox for the messages to allow some thousands of messages in a chat. 2017-01-05 15:32:32 +01:00
B. Petersen 39d5c40945 Cleanup keyboard code. 2017-01-05 13:25:26 +01:00
B. Petersen ebcb132acf Cleanup. 2017-01-05 13:10:11 +01:00
B. Petersen 53447c793a Remove dead sticker code. 2017-01-05 12:22:42 +01:00
B. Petersen 54a2df818b Remove unused GIF code from ChatActivity. 2017-01-05 12:10:19 +01:00
B. Petersen 09a3b3bd26 Get rid of unused context menu code. 2017-01-05 11:59:50 +01:00
B. Petersen f02d4cd21a Remove suspicious forward creation of objects. 2017-01-05 02:34:01 +01:00
B. Petersen eb650ff089 Cleanup 2017-01-05 01:10:21 +01:00
B. Petersen da263b1e56 Remove unused service messages. 2017-01-05 00:25:38 +01:00
B. Petersen 44ee4aaef7 Cleanup. 2017-01-05 00:00:45 +01:00
B. Petersen 56874bd418 Cleanup notification strings. 2017-01-04 23:52:20 +01:00
B. Petersen bf1594fdef Use correct avatars for the created widgets. 2017-01-04 01:08:08 +01:00
B. Petersen e31821cf92 Test solid wallpapers. 2017-01-03 18:43:29 +01:00
B. Petersen 4efe3b4387 Go directly to the notified chat. 2017-01-03 17:52:34 +01:00
B. Petersen 69d0537425 Prepare MrChat to get the pointer-handle directly in the C-Part. 2017-01-03 17:20:30 +01:00
B. Petersen 0852e09815 Cleanup headers. 2017-01-03 16:40:43 +01:00
B. Petersen 2fc8799241 Hide unused empty-search-field-buttons (may be seen as close-search-bar-buttons otherwise). 2017-01-03 14:45:08 +01:00
B. Petersen ea01205421 Show correct chat after sharing texts. 2017-01-02 15:35:25 +01:00
B. Petersen 1ad31aa178 Can send share texts to Delta Chat. 2017-01-02 15:25:02 +01:00
B. Petersen 349b943a7e Unify 'disabled' and 'off' strings. 2017-01-02 14:55:31 +01:00
B. Petersen 268006d027 'Repeat notifications' default to 'off'. 2017-01-02 13:02:56 +01:00
B. Petersen e2bad18153 Show number of blocked users in settings. 2017-01-02 12:35:10 +01:00
B. Petersen 4749ddf9a8 Tweak notifications. 2016-12-24 01:33:23 +01:00
B. Petersen de1c568714 Localize 2016-12-23 23:55:49 +01:00
B. Petersen 0b90e66ad8 Tweak notifications. 2016-12-23 14:47:17 +01:00
B. Petersen 90fe75a764 Play notification sounds, vibrate. 2016-12-23 02:15:48 +01:00
B. Petersen c27fd27789 Remove seen messages from notifications. Display user and group names in notifications only if they differ from each other. 2016-12-23 02:08:37 +01:00
B. Petersen 5580f5ae62 Set user and message text in notifications. 2016-12-22 18:44:24 +01:00
B. Petersen d4e94b2fbc Set Delta Chat notification icon. 2016-12-22 12:27:23 +01:00
B. Petersen bb673dd992 Make notifications generally work. 2016-12-21 23:34:28 +01:00
B. Petersen 2dfacc4858 Make 'Reset notifications and sounds' work. 2016-12-21 23:04:52 +01:00
B. Petersen cce4e31b0f Remove popup notifications, for now. 2016-12-21 22:12:40 +01:00
B. Petersen 99615999b8 Re-init stock strings after language change, fixes issue #1. 2016-12-21 21:44:56 +01:00
B. Petersen cfca25d65d Hide button 'Notifications and Sounds' for deaddrops not shown in chatlist. 2016-12-21 21:26:42 +01:00
B. Petersen 665a2cdfd0 Prepare notifications. 2016-12-21 18:13:04 +01:00
B. Petersen a2768728d3 Play in-chat sounds. 2016-12-21 17:47:11 +01:00
B. Petersen 2790082705 Cleanup. 2016-12-21 16:44:31 +01:00
B. Petersen ae8a15d524 Cleanup. 2016-12-21 16:23:10 +01:00
B. Petersen e39d7b5d4d Add an extra event for incoming messages. 2016-12-21 01:17:14 +01:00
B. Petersen 1ae26ecfa5 Cleanup notify code. 2016-12-21 00:38:22 +01:00
B. Petersen 1e58912226 Showing the default font size in the NumberPicker. 2016-12-20 23:57:20 +01:00
B. Petersen 87807db64a Do not use the android cache directory as our 'blob repository'; the cache directory may be treated as a real cache; the files may disappear at any time. Instead, we're using a normal directory beside the database (the directory that is already used by the backend for incoming messages) and delete files ourself as needed. 2016-12-20 22:43:22 +01:00
B. Petersen 6221c97283 For non-empty strings, do not use NewStringUTF() as this is buggy on some Android versions. Instead, create the string using 'new String(ByteArray, 'UTF-8');' which seems to be programmed more properly. (eg. on KitKat a simple 'SMILING FACE WITH SMILING EYES' (U+1F60A, UTF-8 F0 9F 98 8A) will let the app crash, reporting 0xF0 is a bad UTF-8 start, see http://stackoverflow.com/questions/12127817/android-ics-4-0-ndk-newstringutf-is-crashing-down-the-app ) 2016-12-20 00:21:18 +01:00
B. Petersen 80226717b6 Set up photo type. 2016-12-19 16:29:59 +01:00
B. Petersen 959f7af2ab Change intro layout (paging below pages ...). 2016-12-19 14:16:06 +01:00
B. Petersen 2308990661 Cleanup paths. 2016-12-19 13:45:44 +01:00
B. Petersen 9f304f372a Remove save-to-gallery option. We save all stuff internally; this avoids probles if a user deletes an image. Additionally, photos taken are always saved in the gallery; plus, the user can explicitly save data to the gallery (this avoids saving all files twice). 2016-12-19 13:19:16 +01:00
B. Petersen 341580191f Tuning settings dialog. 2016-12-19 00:18:27 +01:00
B. Petersen 07065676f2 Cleanup WebPage stuff. 2016-12-13 12:58:27 +01:00
B. Petersen 47c9dc6e31 Simplify rights management. 2016-12-13 00:15:12 +01:00
B. Petersen 2514494d02 Force reloading avatars, do not clear old values (looks smarter). 2016-12-12 14:32:46 +01:00
B. Petersen 1c28c2d475 Load avatars in working thread, cache avatars, reload avatars after pause (they may have been changed in another app then). 2016-12-12 14:13:04 +01:00
B. Petersen 41a92d56c4 Using a mask instead of clipping to create round avatars; this avoid problems with canvas setting sets and allows antialiasing. 2016-12-12 12:37:36 +01:00
B. Petersen 6a6b024314 Draw avatar photos. 2016-12-10 01:23:38 +01:00
B. Petersen 12c95971a2 Cleanup avatar management, route everything through ContactsController.setupAvatar(). 2016-12-09 17:18:18 +01:00
B. Petersen 62ca3b6331 Neutral language. 2016-12-09 13:58:24 +01:00
B. Petersen c69f2fd75d Add invite function. 2016-12-09 13:10:35 +01:00
B. Petersen 0fc45bc57a Disable hiding of the floating button. 2016-12-08 13:55:36 +01:00
B. Petersen 0b9614f0bd Cleanup. 2016-12-06 14:13:37 +01:00
B. Petersen e8a073a42c Cleanup. 2016-12-06 14:00:53 +01:00
B. Petersen 97b33ec9c4 Cleanup. 2016-12-06 12:56:17 +01:00
B. Petersen f6458870ae Optimize unread display. 2016-12-06 12:38:52 +01:00
B. Petersen 665f24db1e Smart 'message seen' handling. 2016-12-05 23:55:32 +01:00
B. Petersen a59be9d2af Mark incoming messages as read when seen. 2016-12-05 22:56:02 +01:00
B. Petersen 1036139cb7 Properly add date headlines. 2016-12-05 22:30:45 +01:00
B. Petersen f1854e46be Cleanup. 2016-12-05 15:10:59 +01:00
B. Petersen bfad3e57c7 Remove unneeded reply functionality. 2016-12-05 14:01:32 +01:00
B. Petersen 24ee24325b Remove unneded loading cells. 2016-12-05 13:45:10 +01:00
B. Petersen c9f6db312f Remove unneded mergeDialogId stuff from ChatActivity. 2016-12-05 13:27:54 +01:00
B. Petersen 6f60ba113e Remove ChatObject class. 2016-12-05 12:29:07 +01:00
B. Petersen db112b226e Remove DraftQuery, StickersQuery, SharedMediaQuery & Co. 2016-12-05 12:20:26 +01:00
B. Petersen a87049e443 Cleanup. 2016-12-04 23:45:41 +01:00
B. Petersen 730b018b45 Cleanup. 2016-12-04 22:46:32 +01:00
B. Petersen 60e72f14d8 Simplify chat objects. 2016-12-03 18:48:09 +01:00
B. Petersen e89b2855ce Remove UserObject.isDeleted(). 2016-12-03 18:06:52 +01:00
B. Petersen 56fb73088e Increase version. 2016-12-02 14:47:08 +01:00
B. Petersen 6623513c2a Adapt colors. 2016-12-02 01:54:00 +01:00
B. Petersen 3da3325c9b Cleanup attach dialog. 2016-12-02 00:53:56 +01:00
B. Petersen c73d3564d4 Cleanup. 2016-12-01 22:30:36 +01:00
B. Petersen 2b822ecd22 Cleanup 2016-11-30 14:18:41 +01:00
B. Petersen a81c921504 Correct flow. 2016-11-30 13:55:03 +01:00
B. Petersen c902044cc3 Show incoming messages in chat view. 2016-11-29 17:20:57 +01:00
B. Petersen 072945b237 Cleanup. 2016-11-29 15:17:13 +01:00
B. Petersen ca49e9899d Remove unneeded report functionality. 2016-11-29 12:09:50 +01:00
B. Petersen f67d82452b Simplify messagesDidLoaded event. 2016-11-29 12:00:19 +01:00
B. Petersen 1c3b7ce051 Cleanup. 2016-11-28 14:20:50 +01:00
B. Petersen cd6d8114ad Cleanup 2016-11-28 14:04:27 +01:00
B. Petersen 1baa5a5d99 Rename 'MrEditTextCell' to more generic 'EditTextCell'. 2016-11-28 13:31:08 +01:00
B. Petersen a83dac9437 Show more detailed error information. 2016-11-27 22:16:01 +01:00
B. Petersen e033c04d7a Capitalize words instead of sentences on name input. 2016-11-25 16:06:01 +01:00
B. Petersen bbd06d37da Update and translate intro strings. 2016-11-25 16:01:57 +01:00
955 changed files with 11957 additions and 43785 deletions
+3
View File
@@ -6,3 +6,6 @@ obj/
local.properties
*.keystore
libs/
# ignore private scripts and directories, eg. local2github.prv.sh
*.prv*
+3
View File
@@ -0,0 +1,3 @@
[submodule "MessengerProj/jni/messenger-backend"]
path = MessengerProj/jni/messenger-backend
url = https://github.com/r10s/messenger-backend.git
+69
View File
@@ -0,0 +1,69 @@
# Delta Chat Changelog
## v0.1.22
2017-03-22
* Show HTML-only messages
* Show connection errors
* Add options for SSL/TLS and STARTTLS
* Automatic account configuration, if possible
* Recode large videos
* Add Hungarian translation
* Add Korean translation
## v0.1.21
2017-03-10
* Record and send voice messages
* Record and send videos
* Send and play music
* Send contacts and email addresses
* Sending and opening attachments of any type
* Share and open commands for all attachments
* Accept VCards send to us by other apps
* Clickable email addresses
* Update Polish translation
* Fix tablet startup bug
* Close the app when using the lock-app-via-pincode function
* Protect data by using a content provider for sharing
* Try to clear the task switcher's screenshots when locking the app via pincode
* Pimp GUI
## v0.1.20
2017-02-16
* Avoid unwanted downloads of lots of old messages
* Make the "Chats" folder visible if the server hides new folders by default
* Fix a crash when the server returns empty folders
* Update Polish and Portuguese translations
* Use API level 25 (Nougat 7.1) as target
## v0.1.18
2017-02-11
* Add Polish translation
* Use a new default background for chats
* Improve typography by using the system font instead of a custom resource font
* Remove custom plural handling, use Android's routines instead
* Remove unused source code and strings
* More fixes of lint errors and warnings
## v0.1.17
2017-02-07
* Drop two unnecessary permissions: ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION
* Really add French translation
* Update Portuguese translation
* Start fixing translation handling of the program
* Remove special "foss" build, because the whole program is free now.
## v0.1.16
2017-02-06
* Add French translation
* Fix some lint errors and warnings
## v0.1.15
2017-01-31
* Prepare for release on [F-Droid](https://f-droid.org/)
+12 -25
View File
@@ -24,15 +24,15 @@ tasks.withType(JavaCompile) {
}
dependencies {
compile 'com.android.support:support-v4:23.4.0'
compile 'com.android.support:appcompat-v7:25.1.1'
compile 'com.googlecode.mp4parser:isoparser:1.0.6'
compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
compile fileTree(dir: "$buildDir/native-libs", include: 'native-libs.jar')
}
android {
compileSdkVersion 23
buildToolsVersion '23.0.3'
compileSdkVersion 25
buildToolsVersion '25.0.2'
useLibrary 'org.apache.http.legacy'
defaultConfig.applicationId = "com.b44t.messenger"
@@ -42,6 +42,10 @@ android {
targetCompatibility JavaVersion.VERSION_1_7
}
lintOptions {
abortOnError false
}
signingConfigs {
debug {
storeFile file("config/debug.keystore")
@@ -70,34 +74,17 @@ android {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
foss {
debuggable false
jniDebuggable false
signingConfig signingConfigs.release
}
}
defaultConfig.versionCode = 13
defaultConfig.versionCode = 21
sourceSets.main {
jniLibs.srcDir 'libs'
jni.srcDirs = [] //disable automatic ndk-build call
}
sourceSets.debug {
manifest.srcFile 'config/debug/AndroidManifest.xml'
}
sourceSets.release {
manifest.srcFile 'config/release/AndroidManifest.xml'
}
sourceSets.foss {
manifest.srcFile 'config/foss/AndroidManifest.xml'
}
productFlavors {
/*
x86 {
ndk {
abiFilter "x86"
@@ -116,6 +103,7 @@ android {
}
versionCode = 1
}
*/
fat {
versionCode = 3
}
@@ -128,8 +116,7 @@ android {
defaultConfig {
minSdkVersion 14 // 14: Android 4.0 Ice Cream Sandwich 2011 (Telegram default), 21: Android 5.0 Lollipop 2014 (recommended for InstantRun)
targetSdkVersion 23
versionName "0.1.3"
targetSdkVersion 25
versionName "0.1.22" // do NOT forget to increase defaultConfig.versionCode!
}
}
@@ -1,24 +0,0 @@
<?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="com.b44t.messenger"
android:installLocation="auto">
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.location.network" android:required="false" />
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.LOCATION" android:required="false" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/AppNameBeta"
android:theme="@style/Theme.MessengerProj.Start"
android:name=".ApplicationLoader"
android:hardwareAccelerated="@bool/useHardwareAcceleration"
android:largeHeap="true">
</application>
</manifest>
@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.b44t.messenger"
android:installLocation="auto">
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/AppName"
android:theme="@style/Theme.MessengerProj.Start"
android:name=".ApplicationLoader"
android:hardwareAccelerated="@bool/useHardwareAcceleration"
android:largeHeap="true">
</application>
</manifest>
@@ -1,24 +0,0 @@
<?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="com.b44t.messenger"
android:installLocation="auto">
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.location.network" android:required="false" />
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.LOCATION" android:required="false" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/AppName"
android:theme="@style/Theme.MessengerProj.Start"
android:name=".ApplicationLoader"
android:hardwareAccelerated="@bool/useHardwareAcceleration"
android:largeHeap="true">
</application>
</manifest>
+22 -18
View File
@@ -1604,24 +1604,28 @@ LOCAL_SRC_FILES += \
./image.c \
./video.c \
./gifvideo.cpp \
../../../messenger-backend/src/mrchat.c \
../../../messenger-backend/src/mrchatlist.c \
../../../messenger-backend/src/mrcontact.c \
../../../messenger-backend/src/mrimap.c \
../../../messenger-backend/src/mrjob.c \
../../../messenger-backend/src/mrlog.c \
../../../messenger-backend/src/mrloginparam.c \
../../../messenger-backend/src/mrmailbox.c \
../../../messenger-backend/src/mrmimeparser.c \
../../../messenger-backend/src/mrmsg.c \
../../../messenger-backend/src/mrosnative.c \
../../../messenger-backend/src/mrparam.c \
../../../messenger-backend/src/mrpoortext.c \
../../../messenger-backend/src/mrsimplify.c \
../../../messenger-backend/src/mrsmtp.c \
../../../messenger-backend/src/mrsqlite3.c \
../../../messenger-backend/src/mrstock.c \
../../../messenger-backend/src/mrtools.c \
./messenger-backend/src/mrchat.c \
./messenger-backend/src/mrchatlist.c \
./messenger-backend/src/mrcmdline.c \
./messenger-backend/src/mrcontact.c \
./messenger-backend/src/mre2ee.c \
./messenger-backend/src/mrimap.c \
./messenger-backend/src/mrjob.c \
./messenger-backend/src/mrloginparam.c \
./messenger-backend/src/mrmailbox.c \
./messenger-backend/src/mrmailbox_configure.c \
./messenger-backend/src/mrmailbox_log.c \
./messenger-backend/src/mrmimeparser.c \
./messenger-backend/src/mrmsg.c \
./messenger-backend/src/mrosnative.c \
./messenger-backend/src/mrparam.c \
./messenger-backend/src/mrpoortext.c \
./messenger-backend/src/mrsaxparser.c \
./messenger-backend/src/mrsimplify.c \
./messenger-backend/src/mrsmtp.c \
./messenger-backend/src/mrsqlite3.c \
./messenger-backend/src/mrstock.c \
./messenger-backend/src/mrtools.c \
./mrwrapper.c
include $(BUILD_SHARED_LIBRARY)
+388 -105
View File
@@ -1,8 +1,8 @@
/*******************************************************************************
*
* Messenger Android Frontend
* Copyright (C) 2016 Björn Petersen Software Design and Development
* Contact: r10s@b44t.com, http://b44t.com
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
@@ -20,7 +20,6 @@
*******************************************************************************
*
* File: mr_wrapper.c
* Authors: Björn Petersen
* Purpose: The C part of the Java<->C Wrapper, see also MrMailbox.java
*
******************************************************************************/
@@ -28,7 +27,7 @@
#include <jni.h>
#include <android/log.h>
#include "../../../messenger-backend/src/mrmailbox.h"
#include "messenger-backend/src/mrmailbox.h"
#define CHAR_REF(a) \
@@ -37,49 +36,79 @@
#define CHAR_UNREF(a) \
if(a) { (*env)->ReleaseStringUTFChars(env, (a), a##Ptr); }
#define JSTRING_NEW(a) \
(*env)->NewStringUTF(env, a? a : "") /*should handle NULL arguments, does not return NULL!*/
/* our log handler */
static void s_log_callback_(int type, const char* msg)
#define JSTRING_NEW(a) jstring_new__(env, (a))
static jstring jstring_new__(JNIEnv* env, const char* a)
{
int prio;
switch( type ) {
case 'd': prio = ANDROID_LOG_DEBUG; break;
case 'i': prio = ANDROID_LOG_INFO; break;
case 'w': prio = ANDROID_LOG_WARN; break;
default: prio = ANDROID_LOG_ERROR; break;
if( a==NULL || a[0]==0 ) {
return (*env)->NewStringUTF(env, "");
}
__android_log_print(prio, "DeltaChat", "%s\n", msg); /* on problems, add `-llog` to `Android.mk` */
/* for non-empty strings, do not use NewStringUTF() as this is buggy on some Android versions.
Instead, create the string using `new String(ByteArray, "UTF-8);` which seems to be programmed more properly.
(eg. on KitKat a simple "SMILING FACE WITH SMILING EYES" (U+1F60A, UTF-8 F0 9F 98 8A) will let the app crash, reporting 0xF0 is a bad UTF-8 start,
see http://stackoverflow.com/questions/12127817/android-ics-4-0-ndk-newstringutf-is-crashing-down-the-app ) */
static jclass s_strCls = NULL;
static jmethodID s_strCtor = NULL;
static jclass s_strEncode = NULL;
if( s_strCls==NULL ) {
s_strCls = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/lang/String"));
s_strCtor = (*env)->GetMethodID(env, s_strCls, "<init>", "([BLjava/lang/String;)V");
s_strEncode = (*env)->NewGlobalRef(env, (*env)->NewStringUTF(env, "UTF-8"));
}
int a_bytes = strlen(a);
jbyteArray array = (*env)->NewByteArray(env, a_bytes);
(*env)->SetByteArrayRegion(env, array, 0, a_bytes, a);
jstring ret = (jstring) (*env)->NewObject(env, s_strCls, s_strCtor, array, s_strEncode);
(*env)->DeleteLocalRef(env, array); /* we have to delete the reference as it is not returned to Java, AFAIK */
return ret;
}
/* globl stuff */
/* global stuff */
static JavaVM* s_jvm = NULL;
static jclass s_MrMailbox_class = NULL;
static jmethodID s_MrCallback_methodID = NULL;
static int s_global_init_done = 0;
static void s_init_globals(JNIEnv *env, jclass MrMailbox_class)
{
/* make sure, the intialisation is done only once */
static bool s_global_init_done = 0;
if( s_global_init_done ) { return; }
s_global_init_done = 1;
/* init global callback */
mrlog_set_handler(s_log_callback_);
/* prepare calling back a Java function */
(*env)->GetJavaVM(env, &s_jvm); /* JNIEnv cannot be shared between threads, so we share the JavaVM object */
s_MrMailbox_class = (*env)->NewGlobalRef(env, MrMailbox_class);
s_MrCallback_methodID = (*env)->GetStaticMethodID(env, MrMailbox_class, "MrCallback","(IJJ)J" /*signature as "(param)ret" with I=int, J=long*/ );
}
/* system-specific backend initialisations */
mrosnative_init_android(env); /*this should be called before any other "important" routine is called*/
/* setup threads, only called directly by the backed */
int mrosnative_setup_thread(mrmailbox_t* mailbox)
{
if( s_jvm == NULL ) {
mrmailbox_log_error(mailbox, 0, "Not ready, cannot setup thread.");
return 0;
}
mrmailbox_log_info(mailbox, 0, "Attaching C-thread to Java VM...");
JNIEnv* env = NULL;
(*s_jvm)->AttachCurrentThread(s_jvm, &env, NULL);
mrmailbox_log_info(mailbox, 0, "Attaching ok.");
return 1;
}
void mrosnative_unsetup_thread(mrmailbox_t* mailbox)
{
mrmailbox_log_info(mailbox, 0, "Detaching C-thread from Java VM...");
(*s_jvm)->DetachCurrentThread(s_jvm);
mrmailbox_log_info(mailbox, 0, "Detaching done.");
}
@@ -112,9 +141,49 @@ static jintArray carray2jintArray_n_carray_free(JNIEnv *env, const carray* ca)
}
static uint32_t* jintArray2uint32Pointer(JNIEnv* env, jintArray ja, int* ret_icnt)
{
uint32_t* ret = NULL;
if( env && ja && ret_icnt )
{
int i, icnt = (*env)->GetArrayLength(env, ja);
if( icnt > 0 )
{
const jint* temp = (*env)->GetIntArrayElements(env, ja, NULL);
if( temp )
{
ret = calloc(icnt, sizeof(uint32_t));
if( ret )
{
for( i = 0; i < icnt; i++ ) {
ret[i] = (uint32_t)temp[i];
}
*ret_icnt = icnt;
}
(*env)->ReleaseIntArrayElements(env, ja, temp, 0);
}
}
}
return ret;
}
/*******************************************************************************
* MrMailbox
******************************************************************************/
static mrmailbox_t* get_mrmailbox_t(JNIEnv *env, jclass cls)
{
static jfieldID fid = 0;
if( fid == 0 ) {
fid = (*env)->GetStaticFieldID(env, cls, "m_hMailbox", "J" /*Signature, J=long*/);
}
if( fid ) {
return (mrmailbox_t*)(*env)->GetStaticLongField(env, cls, fid);
}
return NULL;
}
/* MrMailbox - new/delete */
@@ -124,15 +193,22 @@ static uintptr_t s_mailbox_callback_(mrmailbox_t* mailbox, int event, uintptr_t
jlong l;
JNIEnv* env;
if( event==MR_EVENT_INFO || event==MR_EVENT_WARNING ) {
__android_log_print(event==MR_EVENT_INFO? ANDROID_LOG_INFO : ANDROID_LOG_WARN, "DeltaChat", "%s", (char*)data2); /* on problems, add `-llog` to `Android.mk` */
return 0; /* speed up things for info/warning */
}
else if( event == MR_EVENT_ERROR ) {
__android_log_print(ANDROID_LOG_ERROR, "DeltaChat", "%s", (char*)data2);
/* errors are also forwarded to Java to show them in a bubble or so */
}
if( s_jvm==NULL || s_MrMailbox_class==NULL || s_MrCallback_methodID==NULL ) {
s_log_callback_('e', "Callback called but JavaVM not ready.");
return 0;
return 0; /* may happen on startup */
}
(*s_jvm)->GetEnv(s_jvm, &env, JNI_VERSION_1_6); /* as this function may be called from _any_ thread, we cannot use a static pointer to JNIEnv */
if( env==NULL ) {
s_log_callback_('e', "Callback called but cannot get JNIEnv.");
return 0;
return 0; /* may happen on startup */
}
l = (*env)->CallStaticLongMethod(env, s_MrMailbox_class, s_MrCallback_methodID, (jint)event, (jlong)data1, (jlong)data2);
@@ -149,67 +225,78 @@ JNIEXPORT jlong Java_com_b44t_messenger_MrMailbox_MrMailboxNew(JNIEnv *env, jcla
/* MrMailbox - open/configure/connect/fetch */
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_MrMailboxOpen(JNIEnv *env, jclass c, jlong hMailbox, jstring dbfile, jstring blobdir)
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_open(JNIEnv *env, jclass cls, jstring dbfile)
{
CHAR_REF(dbfile);
CHAR_REF(blobdir);
jint ret = mrmailbox_open((mrmailbox_t*)hMailbox, dbfilePtr, blobdirPtr);
CHAR_UNREF(blobdir);
jint ret = mrmailbox_open(get_mrmailbox_t(env, cls), dbfilePtr, NULL);
CHAR_UNREF(dbfile)
return ret;
}
JNIEXPORT void Java_com_b44t_messenger_MrMailbox_MrMailboxClose(JNIEnv *env, jclass c, jlong hMailbox)
JNIEXPORT void Java_com_b44t_messenger_MrMailbox_close(JNIEnv *env, jclass cls)
{
mrmailbox_close((mrmailbox_t*)hMailbox);
mrmailbox_close(get_mrmailbox_t(env, cls));
}
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_MrMailboxConfigure(JNIEnv *env, jclass c, jlong hMailbox)
JNIEXPORT jstring Java_com_b44t_messenger_MrMailbox_getBlobdir(JNIEnv *env, jclass cls)
{
return mrmailbox_configure((mrmailbox_t*)hMailbox);
mrmailbox_t* ths = get_mrmailbox_t(env, cls);
return JSTRING_NEW((ths&&ths->m_blobdir)? ths->m_blobdir : NULL);
}
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_MrMailboxIsConfigured(JNIEnv *env, jclass c, jlong hMailbox)
JNIEXPORT void Java_com_b44t_messenger_MrMailbox_configureAndConnect(JNIEnv *env, jclass cls)
{
return (jint)mrmailbox_is_configured((mrmailbox_t*)hMailbox);
mrmailbox_configure_and_connect(get_mrmailbox_t(env, cls));
}
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_MrMailboxConnect(JNIEnv *env, jclass c, jlong hMailbox)
JNIEXPORT void Java_com_b44t_messenger_MrMailbox_configureCancel(JNIEnv *env, jclass cls)
{
return mrmailbox_connect((mrmailbox_t*)hMailbox);
mrmailbox_configure_cancel(get_mrmailbox_t(env, cls));
}
JNIEXPORT void Java_com_b44t_messenger_MrMailbox_MrMailboxDisconnect(JNIEnv *env, jclass c, jlong hMailbox)
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_isConfigured(JNIEnv *env, jclass cls)
{
mrmailbox_disconnect((mrmailbox_t*)hMailbox);
return (jint)mrmailbox_is_configured(get_mrmailbox_t(env, cls));
}
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_MrMailboxFetch(JNIEnv *env, jclass c, jlong hMailbox)
JNIEXPORT void Java_com_b44t_messenger_MrMailbox_connect(JNIEnv *env, jclass cls)
{
return mrmailbox_fetch((mrmailbox_t*)hMailbox);
mrmailbox_connect(get_mrmailbox_t(env, cls));
}
JNIEXPORT void Java_com_b44t_messenger_MrMailbox_disconnect(JNIEnv *env, jclass cls)
{
mrmailbox_disconnect(get_mrmailbox_t(env, cls));
}
/* MrMailbox - handle contacts */
JNIEXPORT jintArray Java_com_b44t_messenger_MrMailbox_MrMailboxGetKnownContacts(JNIEnv *env, jclass c, jlong hMailbox, jstring query)
JNIEXPORT jintArray Java_com_b44t_messenger_MrMailbox_getKnownContacts(JNIEnv *env, jclass cls, jstring query)
{
CHAR_REF(query);
carray* ca = mrmailbox_get_known_contacts((mrmailbox_t*)hMailbox, queryPtr);
CHAR_REF(query);
carray* ca = mrmailbox_get_known_contacts(get_mrmailbox_t(env, cls), queryPtr);
CHAR_UNREF(query);
return carray2jintArray_n_carray_free(env, ca);
}
JNIEXPORT jintArray Java_com_b44t_messenger_MrMailbox_MrMailboxGetBlockedContacts(JNIEnv *env, jclass c, jlong hMailbox)
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_getBlockedCount(JNIEnv *env, jclass cls)
{
carray* ca = mrmailbox_get_blocked_contacts((mrmailbox_t*)hMailbox);
return mrmailbox_get_blocked_count(get_mrmailbox_t(env, cls));
}
JNIEXPORT jintArray Java_com_b44t_messenger_MrMailbox_getBlockedContacts(JNIEnv *env, jclass cls)
{
carray* ca = mrmailbox_get_blocked_contacts(get_mrmailbox_t(env, cls));
return carray2jintArray_n_carray_free(env, ca);
}
@@ -245,9 +332,18 @@ JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_MrMailboxDeleteContact(JNIEnv *
/* MrMailbox - handle chats */
JNIEXPORT jlong Java_com_b44t_messenger_MrMailbox_MrMailboxGetChatlist(JNIEnv *env, jclass c, jlong hMailbox)
JNIEXPORT jlong Java_com_b44t_messenger_MrMailbox_MrMailboxGetChatlist(JNIEnv *env, jclass c, jlong hMailbox, jstring query)
{
return (jlong)mrmailbox_get_chatlist((mrmailbox_t*)hMailbox);
jlong ret;
if( query ) {
CHAR_REF(query);
ret = (jlong)mrmailbox_get_chatlist((mrmailbox_t*)hMailbox, queryPtr);
CHAR_UNREF(query);
}
else {
ret = (jlong)mrmailbox_get_chatlist((mrmailbox_t*)hMailbox, NULL);
}
return ret;
}
@@ -263,9 +359,15 @@ JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_MrMailboxGetChatIdByContactId(J
}
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_MrMailboxMarkseenChat(JNIEnv *env, jclass c, jlong hMailbox, jint chat_id)
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_markseenMsg(JNIEnv *env, jclass cls, jint msg_id)
{
return (jlong)mrmailbox_markseen_chat((mrmailbox_t*)hMailbox, chat_id);
return (jint)mrmailbox_markseen_msg(get_mrmailbox_t(env, cls), msg_id);
}
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_markseenChat(JNIEnv *env, jclass cls, jint chat_id)
{
return (jlong)mrmailbox_markseen_chat(get_mrmailbox_t(env, cls), chat_id);
}
@@ -275,9 +377,45 @@ JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_MrMailboxCreateChatByContactId(
}
JNIEXPORT void Java_com_b44t_messenger_MrMailbox_MrMailboxDeleteChat(JNIEnv *env, jclass c, jlong hMailbox, jint chat_id)
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_createGroupChat(JNIEnv *env, jclass cls, jstring name)
{
mrmailbox_delete_chat((mrmailbox_t*)hMailbox, chat_id);
CHAR_REF(name);
jint ret = (jint)mrmailbox_create_group_chat(get_mrmailbox_t(env, cls), namePtr);
CHAR_UNREF(name);
return ret;
}
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_isContactInChat(JNIEnv *env, jclass cls, jint chat_id, jint contact_id)
{
return (jint)mrmailbox_is_contact_in_chat(get_mrmailbox_t(env, cls), chat_id, contact_id);
}
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_addContactToChat(JNIEnv *env, jclass cls, jint chat_id, jint contact_id)
{
return (jint)mrmailbox_add_contact_to_chat(get_mrmailbox_t(env, cls), chat_id, contact_id);
}
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_removeContactFromChat(JNIEnv *env, jclass cls, jint chat_id, jint contact_id)
{
return (jint)mrmailbox_remove_contact_from_chat(get_mrmailbox_t(env, cls), chat_id, contact_id);
}
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_setChatName(JNIEnv *env, jclass cls, jint chat_id, jstring name)
{
CHAR_REF(name);
jint ret = (jint)mrmailbox_set_chat_name(get_mrmailbox_t(env, cls), chat_id, namePtr);
CHAR_UNREF(name);
return ret;
}
JNIEXPORT void Java_com_b44t_messenger_MrMailbox_deleteChat(JNIEnv *env, jclass cls, jint chat_id)
{
mrmailbox_delete_chat(get_mrmailbox_t(env, cls), chat_id);
}
@@ -298,30 +436,49 @@ JNIEXPORT jstring Java_com_b44t_messenger_MrMailbox_MrMailboxGetMsgInfo(JNIEnv *
}
JNIEXPORT void Java_com_b44t_messenger_MrMailbox_MrMailboxDeleteMsg(JNIEnv *env, jclass c, jlong hMailbox, jint id)
JNIEXPORT void Java_com_b44t_messenger_MrMailbox_deleteMsgs(JNIEnv *env, jclass cls, jintArray msg_ids)
{
mrmailbox_delete_msg((mrmailbox_t*)hMailbox, id);
int msg_ids_cnt;
const uint32_t* msg_ids_ptr = jintArray2uint32Pointer(env, msg_ids, &msg_ids_cnt);
mrmailbox_delete_msgs(get_mrmailbox_t(env, cls), msg_ids_ptr, msg_ids_cnt);
free(msg_ids_ptr);
}
JNIEXPORT void Java_com_b44t_messenger_MrMailbox_forwardMsgs(JNIEnv *env, jclass cls, jintArray msg_ids, jint chat_id)
{
int msg_ids_cnt;
const uint32_t* msg_ids_ptr = jintArray2uint32Pointer(env, msg_ids, &msg_ids_cnt);
mrmailbox_forward_msgs(get_mrmailbox_t(env, cls), msg_ids_ptr, msg_ids_cnt, chat_id);
free(msg_ids_ptr);
}
/* MrMailbox - handle config */
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_MrMailboxSetConfig(JNIEnv *env, jclass c, jlong hMailbox, jstring key, jstring value)
JNIEXPORT void Java_com_b44t_messenger_MrMailbox_setConfig(JNIEnv *env, jclass cls, jstring key, jstring value)
{
CHAR_REF(key);
CHAR_REF(value);
jint ret = (jint)mrmailbox_set_config((mrmailbox_t*)hMailbox, keyPtr, valuePtr);
mrmailbox_set_config(get_mrmailbox_t(env, cls), keyPtr, valuePtr);
CHAR_UNREF(key);
CHAR_UNREF(value);
return ret;
}
JNIEXPORT jstring Java_com_b44t_messenger_MrMailbox_MrMailboxGetConfig(JNIEnv *env, jclass c, jlong hMailbox, jstring key, jstring def)
JNIEXPORT void Java_com_b44t_messenger_MrMailbox_setConfigInt(JNIEnv *env, jclass cls, jstring key, jint value)
{
CHAR_REF(key);
mrmailbox_set_config_int(get_mrmailbox_t(env, cls), keyPtr, value);
CHAR_UNREF(key);
}
JNIEXPORT jstring Java_com_b44t_messenger_MrMailbox_getConfig(JNIEnv *env, jclass cls, jstring key, jstring def)
{
CHAR_REF(key);
CHAR_REF(def);
char* temp = mrmailbox_get_config((mrmailbox_t*)hMailbox, keyPtr, defPtr);
char* temp = mrmailbox_get_config(get_mrmailbox_t(env, cls), keyPtr, defPtr);
jstring ret = JSTRING_NEW(temp);
free(temp);
CHAR_UNREF(key);
@@ -330,10 +487,10 @@ JNIEXPORT jstring Java_com_b44t_messenger_MrMailbox_MrMailboxGetConfig(JNIEnv *e
}
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_MrMailboxGetConfigInt(JNIEnv *env, jclass c, jlong hMailbox, jstring key, jint def)
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_getConfigInt(JNIEnv *env, jclass cls, jstring key, jint def)
{
CHAR_REF(key);
jint ret = mrmailbox_get_config_int((mrmailbox_t*)hMailbox, keyPtr, def);
jint ret = mrmailbox_get_config_int(get_mrmailbox_t(env, cls), keyPtr, def);
CHAR_UNREF(key);
return ret;
}
@@ -341,19 +498,19 @@ JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_MrMailboxGetConfigInt(JNIEnv *e
/* MrMailbox - misc. */
JNIEXPORT jstring Java_com_b44t_messenger_MrMailbox_MrMailboxGetInfo(JNIEnv *env, jclass c, jlong hMailbox)
JNIEXPORT jstring Java_com_b44t_messenger_MrMailbox_getInfo(JNIEnv *env, jclass cls)
{
char* temp = mrmailbox_get_info((mrmailbox_t*)hMailbox);
char* temp = mrmailbox_get_info(get_mrmailbox_t(env, cls));
jstring ret = JSTRING_NEW(temp);
free(temp);
return ret;
}
JNIEXPORT jstring Java_com_b44t_messenger_MrMailbox_MrMailboxExecute(JNIEnv *env, jclass c, jlong hMailbox, jstring cmd)
JNIEXPORT jstring Java_com_b44t_messenger_MrMailbox_cmdline(JNIEnv *env, jclass cls, jstring cmd)
{
CHAR_REF(cmd);
char* temp = mrmailbox_execute((mrmailbox_t*)hMailbox, cmdPtr);
char* temp = mrmailbox_cmdline(get_mrmailbox_t(env, cls), cmdPtr);
jstring ret = JSTRING_NEW(temp);
free(temp);
CHAR_UNREF(cmd);
@@ -405,42 +562,63 @@ JNIEXPORT jlong Java_com_b44t_messenger_MrChatlist_MrChatlistGetSummaryByIndex(J
******************************************************************************/
static mrchat_t* get_mrchat_t(JNIEnv *env, jobject obj)
{
static jfieldID fid = 0;
if( fid == 0 ) {
jclass cls = (*env)->GetObjectClass(env, obj);
fid = (*env)->GetFieldID(env, cls, "m_hChat", "J" /*Signature, J=long*/);
}
if( fid ) {
return (mrchat_t*)(*env)->GetLongField(env, obj, fid);
}
return NULL;
}
JNIEXPORT void Java_com_b44t_messenger_MrChat_MrChatUnref(JNIEnv *env, jclass c, jlong hChat)
{
mrchat_unref((mrchat_t*)hChat);
}
JNIEXPORT jint Java_com_b44t_messenger_MrChat_MrChatGetId(JNIEnv *env, jclass c, jlong hChat)
JNIEXPORT jint Java_com_b44t_messenger_MrChat_getId(JNIEnv *env, jclass cls)
{
mrchat_t* ths = (mrchat_t*)hChat; if( ths == NULL ) { return 0; }
mrchat_t* ths = get_mrchat_t(env, cls); if( ths == NULL ) { return 0; }
return ths->m_id;
}
JNIEXPORT jint Java_com_b44t_messenger_MrChat_MrChatGetType(JNIEnv *env, jclass c, jlong hChat)
JNIEXPORT jint Java_com_b44t_messenger_MrChat_getType(JNIEnv *env, jclass cls)
{
mrchat_t* ths = (mrchat_t*)hChat; if( ths == NULL ) { return 0; }
mrchat_t* ths = get_mrchat_t(env, cls); if( ths == NULL ) { return 0; }
return ths->m_type;
}
JNIEXPORT jstring Java_com_b44t_messenger_MrChat_MrChatGetName(JNIEnv *env, jclass c, jlong hChat)
JNIEXPORT jstring Java_com_b44t_messenger_MrChat_getName(JNIEnv *env, jclass cls)
{
mrchat_t* ths = (mrchat_t*)hChat; if( ths == NULL ) { return JSTRING_NEW(NULL); }
mrchat_t* ths = get_mrchat_t(env, cls); if( ths == NULL ) { return JSTRING_NEW(NULL); }
return JSTRING_NEW(ths->m_name);
}
JNIEXPORT jstring Java_com_b44t_messenger_MrChat_MrChatGetSubtitle(JNIEnv *env, jclass c, jlong hChat)
JNIEXPORT jstring Java_com_b44t_messenger_MrChat_getSubtitle(JNIEnv *env, jclass cls)
{
const char* temp = mrchat_get_subtitle((mrchat_t*)hChat);
const char* temp = mrchat_get_subtitle(get_mrchat_t(env, cls));
jstring ret = JSTRING_NEW(temp);
free(temp);
return ret;
}
JNIEXPORT jint Java_com_b44t_messenger_MrChat_getParamInt(JNIEnv *env, jclass cls, jint key, jint def)
{
mrchat_t* ths = get_mrchat_t(env, cls);
return mrparam_get_int(ths? ths->m_param:NULL, key, def);
}
JNIEXPORT jstring Java_com_b44t_messenger_MrChat_MrChatGetDraft(JNIEnv *env, jclass c, jlong hChat) /* returns NULL for "no draft" */
{
mrchat_t* ths = (mrchat_t*)hChat;
@@ -486,20 +664,20 @@ JNIEXPORT jint Java_com_b44t_messenger_MrChat_MrChatSetDraft(JNIEnv *env, jclass
}
JNIEXPORT jint Java_com_b44t_messenger_MrChat_MrChatSendText(JNIEnv *env, jclass c, jlong hChat, jstring text)
JNIEXPORT jint Java_com_b44t_messenger_MrChat_sendText(JNIEnv *env, jclass cls, jstring text)
{
mrmsg_t* msg = mrmsg_new();
msg->m_type = MR_MSG_TEXT;
CHAR_REF(text);
msg->m_text = textPtr? strdup(textPtr) : NULL;
CHAR_UNREF(text);
jint msg_id = mrchat_send_msg((mrchat_t*)hChat, msg);
jint msg_id = mrchat_send_msg(get_mrchat_t(env, cls), msg);
mrmsg_unref(msg);
return msg_id;
}
JNIEXPORT jint Java_com_b44t_messenger_MrChat_MrChatSendMedia(JNIEnv *env, jclass c, jlong hChat, jint type, jstring file, jstring mime, jint w, jint h, jint ms)
JNIEXPORT jint Java_com_b44t_messenger_MrChat_sendMedia(JNIEnv *env, jclass cls, jint type, jstring file, jstring mime, jint w, jint h, jint ms, jstring author, jstring trackname)
{
mrmsg_t* msg = mrmsg_new();
msg->m_type = type;
@@ -513,33 +691,62 @@ JNIEXPORT jint Java_com_b44t_messenger_MrChat_MrChatSendMedia(JNIEnv *env, jclas
mrparam_set_int(msg->m_param, 'w', w);
mrparam_set_int(msg->m_param, 'h', h);
}
if( type == MR_MSG_AUDIO || type == MR_MSG_VIDEO ) {
if( type == MR_MSG_AUDIO || type == MR_MSG_VOICE || type == MR_MSG_VIDEO ) {
mrparam_set_int(msg->m_param, 'd', ms);
}
jint msg_id = mrchat_send_msg((mrchat_t*)hChat, msg);
if( type == MR_MSG_AUDIO ) {
CHAR_REF(author);
mrparam_set(msg->m_param, 'N', authorPtr);
CHAR_UNREF(author);
CHAR_REF(trackname);
mrparam_set(msg->m_param, 'n', tracknamePtr);
CHAR_UNREF(trackname);
}
jint msg_id = mrchat_send_msg(get_mrchat_t(env, cls), msg);
mrmsg_unref(msg);
return msg_id;
}
JNIEXPORT jintArray Java_com_b44t_messenger_MrMailbox_MrMailboxGetChatMedia(JNIEnv *env, jclass c, jlong hMailbox, jint chat_id, jint msg_type, jint or_msg_type)
JNIEXPORT jintArray Java_com_b44t_messenger_MrMailbox_getChatMedia(JNIEnv *env, jclass cls, jint chat_id, jint msg_type, jint or_msg_type)
{
carray* ca = mrmailbox_get_chat_media((mrmailbox_t*)hMailbox, chat_id, msg_type, or_msg_type);
carray* ca = mrmailbox_get_chat_media(get_mrmailbox_t(env, cls), chat_id, msg_type, or_msg_type);
return carray2jintArray_n_carray_free(env, ca);
}
JNIEXPORT jintArray Java_com_b44t_messenger_MrMailbox_MrMailboxGetChatMsgs(JNIEnv *env, jclass c, jlong hMailbox, jint chat_id)
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_getNextMedia(JNIEnv *env, jclass cls, jint msg_id, jint dir)
{
carray* ca = mrmailbox_get_chat_msgs((mrmailbox_t*)hMailbox, chat_id);
return mrmailbox_get_next_media(get_mrmailbox_t(env, cls), msg_id, dir);
}
JNIEXPORT jintArray Java_com_b44t_messenger_MrMailbox_getChatMsgs(JNIEnv *env, jclass cls, jint chat_id, jint flags, jint marker1before)
{
carray* ca = mrmailbox_get_chat_msgs(get_mrmailbox_t(env, cls), chat_id, flags, marker1before);
return carray2jintArray_n_carray_free(env, ca);
}
JNIEXPORT jintArray Java_com_b44t_messenger_MrMailbox_MrMailboxGetChatContacts(JNIEnv *env, jclass c, jlong hMailbox, jint chat_id)
JNIEXPORT jintArray Java_com_b44t_messenger_MrMailbox_searchMsgs(JNIEnv *env, jclass cls, jint chat_id, jstring query)
{
carray* ca = mrmailbox_get_chat_contacts((mrmailbox_t*)hMailbox, chat_id);
CHAR_REF(query);
carray* ca = mrmailbox_search_msgs(get_mrmailbox_t(env, cls), chat_id, queryPtr);
CHAR_UNREF(query);
return carray2jintArray_n_carray_free(env, ca);
}
JNIEXPORT jintArray Java_com_b44t_messenger_MrMailbox_getUnseenMsgs(JNIEnv *env, jclass cls)
{
carray* ca = mrmailbox_get_unseen_msgs(get_mrmailbox_t(env, cls));
return carray2jintArray_n_carray_free(env, ca);
}
JNIEXPORT jintArray Java_com_b44t_messenger_MrMailbox_getChatContacts(JNIEnv *env, jclass cls, jint chat_id)
{
carray* ca = mrmailbox_get_chat_contacts(get_mrmailbox_t(env, cls), chat_id);
return carray2jintArray_n_carray_free(env, ca);
}
@@ -549,6 +756,20 @@ JNIEXPORT jintArray Java_com_b44t_messenger_MrMailbox_MrMailboxGetChatContacts(J
******************************************************************************/
static mrmsg_t* get_mrmsg_t(JNIEnv *env, jobject obj)
{
static jfieldID fid = 0;
if( fid == 0 ) {
jclass cls = (*env)->GetObjectClass(env, obj);
fid = (*env)->GetFieldID(env, cls, "m_hMsg", "J" /*Signature, J=long*/);
}
if( fid ) {
return (mrmsg_t*)(*env)->GetLongField(env, obj, fid);
}
return NULL;
}
JNIEXPORT void Java_com_b44t_messenger_MrMsg_MrMsgUnref(JNIEnv *env, jclass c, jlong hMsg)
{
mrmsg_unref((mrmsg_t*)hMsg);
@@ -611,9 +832,9 @@ JNIEXPORT jint Java_com_b44t_messenger_MrMsg_MrMsgGetToId(JNIEnv *env, jclass c,
}
JNIEXPORT jstring Java_com_b44t_messenger_MrMsg_MrMsgGetParam(JNIEnv *env, jclass c, jlong hMsg, jint key, jstring def)
JNIEXPORT jstring Java_com_b44t_messenger_MrMsg_getParam(JNIEnv *env, jobject obj, jint key, jstring def)
{
mrmsg_t* ths = (mrmsg_t*)hMsg;
mrmsg_t* ths = get_mrmsg_t(env, obj);
CHAR_REF(def);
char* temp = mrparam_get(ths? ths->m_param:NULL, key, defPtr);
jstring ret = JSTRING_NEW(temp);
@@ -623,10 +844,68 @@ JNIEXPORT jstring Java_com_b44t_messenger_MrMsg_MrMsgGetParam(JNIEnv *env, jclas
}
JNIEXPORT jint Java_com_b44t_messenger_MrMsg_MrMsgGetParamInt(JNIEnv *env, jclass c, jlong hMsg, jint key, jint def)
JNIEXPORT jint Java_com_b44t_messenger_MrMsg_getParamInt(JNIEnv *env, jobject obj, jint key, jint def)
{
mrmsg_t* ths = (mrmsg_t*)hMsg;
return mrparam_get_int(ths? ths->m_param:NULL, key, def);
mrmsg_t* ths = get_mrmsg_t(env, obj);
return mrparam_get_int(ths? ths->m_param:NULL, key, def);
}
JNIEXPORT void Java_com_b44t_messenger_MrMsg_setParamInt(JNIEnv *env, jobject obj, jint key, jint value)
{
mrmsg_t* ths = get_mrmsg_t(env, obj);
mrparam_set_int(ths? ths->m_param:NULL, key, value);
}
JNIEXPORT void Java_com_b44t_messenger_MrMsg_saveParamToDisk(JNIEnv *env, jobject obj)
{
mrmsg_save_param_to_disk(get_mrmsg_t(env, obj));
}
JNIEXPORT jint Java_com_b44t_messenger_MrMsg_getBytes(JNIEnv *env, jobject obj)
{
jint ret = 0;
mrmsg_t* ths = get_mrmsg_t(env, obj);
if( ths ) {
const char* file = mrparam_get(ths->m_param, 'f', NULL);
if( file ) {
ret = mr_get_filebytes(file);
free(file);
}
}
return ret;
}
JNIEXPORT jlong Java_com_b44t_messenger_MrMsg_getSummaryCPtr(JNIEnv *env, jobject obj, jlong hChat)
{
return (jlong)mrmsg_get_summary(get_mrmsg_t(env, obj), (mrchat_t*)hChat);
}
JNIEXPORT jint Java_com_b44t_messenger_MrMsg_getSummarytext(JNIEnv *env, jobject obj, jint approx_characters)
{
return JSTRING_NEW(mrmsg_get_summarytext(get_mrmsg_t(env, obj), approx_characters));
}
JNIEXPORT jint Java_com_b44t_messenger_MrMsg_getFilename(JNIEnv *env, jobject obj)
{
return JSTRING_NEW(mrmsg_get_filename(get_mrmsg_t(env, obj)));
}
JNIEXPORT jlong Java_com_b44t_messenger_MrMsg_getMediainfoCPtr(JNIEnv *env, jobject obj)
{
return (jlong)mrmsg_get_mediainfo(get_mrmsg_t(env, obj));
}
JNIEXPORT jint Java_com_b44t_messenger_MrMsg_isIncreation(JNIEnv *env, jobject obj)
{
return (jint)mrmsg_is_increation(get_mrmsg_t(env, obj));
}
@@ -673,24 +952,24 @@ JNIEXPORT void Java_com_b44t_messenger_MrPoortext_MrPoortextUnref(JNIEnv *env, j
}
JNIEXPORT jstring Java_com_b44t_messenger_MrPoortext_MrPoortextGetTitle(JNIEnv *env, jclass c, jlong hPoortext)
JNIEXPORT jstring Java_com_b44t_messenger_MrPoortext_MrPoortextGetText1(JNIEnv *env, jclass c, jlong hPoortext)
{
mrpoortext_t* ths = (mrpoortext_t*)hPoortext; if( ths == NULL ) { return JSTRING_NEW(NULL); }
return JSTRING_NEW(ths->m_title);
return JSTRING_NEW(ths->m_text1);
}
JNIEXPORT jint Java_com_b44t_messenger_MrPoortext_MrPoortextGetTitleMeaning(JNIEnv *env, jclass c, jlong hPoortext)
JNIEXPORT jint Java_com_b44t_messenger_MrPoortext_MrPoortextGetText1Meaning(JNIEnv *env, jclass c, jlong hPoortext)
{
mrpoortext_t* ths = (mrpoortext_t*)hPoortext; if( ths == NULL ) { return 0; }
return ths->m_title_meaning;
return ths->m_text1_meaning;
}
JNIEXPORT jstring Java_com_b44t_messenger_MrPoortext_MrPoortextGetText(JNIEnv *env, jclass c, jlong hPoortext)
JNIEXPORT jstring Java_com_b44t_messenger_MrPoortext_MrPoortextGetText2(JNIEnv *env, jclass c, jlong hPoortext)
{
mrpoortext_t* ths = (mrpoortext_t*)hPoortext; if( ths == NULL ) { return JSTRING_NEW(NULL); }
return JSTRING_NEW(ths->m_text);
return JSTRING_NEW(ths->m_text2);
}
@@ -724,11 +1003,15 @@ JNIEXPORT jstring Java_com_b44t_messenger_MrMailbox_CPtr2String(JNIEnv *env, jcl
}
JNIEXPORT void Java_com_b44t_messenger_MrMailbox_MrStockAddStr(JNIEnv* env, jclass c, jint id, jstring str)
JNIEXPORT jlong Java_com_b44t_messenger_MrMailbox_String2CPtr(JNIEnv *env, jclass c, jstring str)
{
CHAR_REF(str);
mrstock_add_str(id, strPtr);
CHAR_UNREF(str)
char* hStr = NULL;
if( str ) {
CHAR_REF(str);
hStr = strdup(strPtr);
CHAR_UNREF(str);
}
return (jlong)hStr;
}
+29 -26
View File
@@ -20,34 +20,35 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- we need it eg. for getActiveNetworkInfo() -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<!-- <uses-permission android:name="android.permission.GET_ACCOUNTS" /> - required for AccountManager -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
<uses-permission android:name="android.permission.READ_PROFILE"/>
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<!-- <uses-permission android:name="android.permission.WRITE_CONTACTS" /> -->
<!-- <uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/> - required for AccountManager in API level <= 22 -->
<!-- <uses-permission android:name="android.permission.READ_PROFILE"/> - required for ContactsContract.Profile -->
<!-- <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/> required for AbstractThreadedSyncAdapter-->
<!-- <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/> - required for AbstractThreadedSyncAdapter -->
<!-- <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/> - seems to be unneeded and is no longer supported by Android, was needed for AbstractAccountAuthenticator, see http://stackoverflow.com/questions/32601456/marshmallow-permission-implementation#36907308 -->
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> - required for windows using the TYPE_SYSTEM_ALERT flag -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- we need the TelephonyManager to pause the audio player on calls -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"/>
<!--<uses-permission android:name="android.permission.CAMERA" />-->
<!-- <uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT" /> - required for AndroidUtilities.uninstallShortcut() -->
<!-- <uses-permission android:name="android.permission.CAMERA" /> - also disabled in T'gram; we're using the camera via an intent only -->
<application
android:name=".ApplicationLoader"
android:allowBackup="false"
android:hardwareAccelerated="@bool/useHardwareAcceleration"
android:icon="@drawable/ic_launcher"
android:label="@string/AppName"
android:largeHeap="true"
android:theme="@style/Theme.MessengerProj.Start"
android:manageSpaceActivity="com.b44t.ui.ManageSpaceActivity">
@@ -113,7 +114,7 @@
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tg" />
</intent-filter> -->
<meta-data android:name="android.service.chooser.chooser_target_service" android:value=".TgChooserTargetService" />
<!-- see note below, <meta-data android:name="android.service.chooser.chooser_target_service" android:value=".TgChooserTargetService" /> -->
</activity>
<activity
android:name="com.b44t.ui.ManageSpaceActivity"
@@ -131,15 +132,6 @@
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:exported="true">
</activity>
<activity
android:name="com.b44t.ui.PopupNotificationActivity"
android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:theme="@style/Theme.MessengerProj.PopupNotification"
android:windowSoftInputMode="adjustResize|stateHidden">
</activity>
<receiver
android:name=".AutoMessageHeardReceiver"
@@ -165,6 +157,7 @@
</receiver>
-->
<!-- currently, we do not use the android account system
<service android:name=".AuthenticatorService" android:exported="true">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/>
@@ -172,7 +165,9 @@
<meta-data android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/auth"/>
</service>
-->
<!-- seems to be not needed, derived from AbstractThreadedSyncAdapter, would need the READ/WRITE_SYNC_SETTINGS
<service android:name=".ContactsSyncAdapterService" android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
@@ -182,7 +177,11 @@
<meta-data android:name="android.provider.CONTACTS_STRUCTURE"
android:resource="@xml/contacts" />
</service>
-->
<!-- this would implement the "direct share" functionality (the system shows some direct links eg.
to the most recent chats when calling "share" from another app, see https://developer.android.com/reference/android/service/chooser/ChooserTargetService.html )
however, if we want this feature, we have to reimplement TgChooserTargetService or sth. like that.
<service
android:name="com.b44t.messenger.TgChooserTargetService"
android:label="@string/AppName"
@@ -190,18 +189,18 @@
<intent-filter>
<action android:name="android.service.chooser.ChooserTargetService" />
</intent-filter>
</service>
</service> -->
<service android:name=".NotificationsService" android:enabled="true"/>
<service android:name=".NotificationRepeat" android:exported="false"/>
<service android:name=".ClearCacheService" android:exported="false"/>
<service android:name=".VideoEncodingService" android:enabled="true"/>
<service android:name=".MusicPlayerService" android:exported="true" android:enabled="true"/>
<!-- Do not expose data through MediaBrowserService, this seems unexpected for an messenger.
Instead, the user can use 'save to music' or 'share' explicitly for items not privacy related.
<service android:name=".MusicBrowserService" android:exported="true">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
</service> -->
<receiver android:name=".MusicPlayerReceiver" >
<intent-filter>
@@ -225,10 +224,14 @@
<receiver android:name=".WearReplyReceiver" android:enabled="true"/>
<!-- <receiver android:name=".NetworkAlarm" android:enabled="true"/> -->
<!-- seems to be unneeded (bp)
<receiver android:name=".ShareBroadcastReceiver" android:enabled="true"/>
-->
<receiver android:name=".NotificationDismissReceiver" android:exported="false"/>
<provider android:name=".AttachmentsContentProvider" android:authorities="${applicationId}.attachments" android:exported="true" />
<uses-library android:name="com.sec.android.app.multiwindow" android:required="false" />
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
<meta-data android:name="com.sec.android.multiwindow.DEFAULT_SIZE_W" android:value="632dp" />
Binary file not shown.
-232
View File
@@ -1,232 +0,0 @@
1876;JM;Jamaica;XXX XXXX
1869;KN;Saint Kitts & Nevis;XXX XXXX
1868;TT;Trinidad & Tobago;XXX XXXX
1784;VC;Saint Vincent & the Grenadines;XXX XXXX
1767;DM;Dominica;XXX XXXX
1758;LC;Saint Lucia;XXX XXXX
1721;SX;Sint Maarten;XXX XXXX
1684;AS;American Samoa;XXX XXXX
1671;GU;Guam;XXX XXXX
1670;MP;Northern Mariana Islands;XXX XXXX
1664;MS;Montserrat;XXX XXXX
1649;TC;Turks & Caicos Islands;XXX XXXX
1473;GD;Grenada;XXX XXXX
1441;BM;Bermuda;XXX XXXX
1345;KY;Cayman Islands;XXX XXXX
1340;VI;US Virgin Islands;XXX XXXX
1284;VG;British Virgin Islands;XXX XXXX
1268;AG;Antigua & Barbuda;XXX XXXX
1264;AI;Anguilla;XXX XXXX
1246;BB;Barbados;XXX XXXX
1242;BS;Bahamas;XXX XXXX
998;UZ;Uzbekistan;XX XXXXXXX
996;KG;Kyrgyzstan;XXX XXXXXX
995;GE;Georgia;XXX XXX XXX
994;AZ;Azerbaijan;XX XXX XXXX
993;TM;Turkmenistan;XX XXXXXX
992;TJ;Tajikistan;XX XXX XXXX
977;NP;Nepal;XX XXXX XXXX
976;MN;Mongolia;XX XX XXXX
975;BT;Bhutan;XX XXX XXX
974;QA;Qatar;XX XXX XXX
973;BH;Bahrain;XXXX XXXX
972;IL;Israel;XX XXX XXXX
971;AE;United Arab Emirates;XX XXX XXXX
970;PS;Palestine;XXX XX XXXX
968;OM;Oman;XXXX XXXX
967;YE;Yemen;XXX XXX XXX
966;SA;Saudi Arabia;XX XXX XXXX
965;KW;Kuwait;XXXX XXXX
964;IQ;Iraq;XXX XXX XXXX
963;SY;Syria;XXX XXX XXX
962;JO;Jordan;X XXXX XXXX
961;LB;Lebanon
960;MV;Maldives;XXX XXXX
886;TW;Taiwan;XXX XXX XXX
880;BD;Bangladesh
856;LA;Laos;XX XX XXX XXX
855;KH;Cambodia
853;MO;Macau;XXXX XXXX
852;HK;Hong Kong;X XXX XXXX
850;KP;North Korea
692;MH;Marshall Islands
691;FM;Micronesia
690;TK;Tokelau
689;PF;French Polynesia
688;TV;Tuvalu
687;NC;New Caledonia
686;KI;Kiribati
685;WS;Samoa
683;NU;Niue
682;CK;Cook Islands
681;WF;Wallis & Futuna
680;PW;Palau
679;FJ;Fiji
678;VU;Vanuatu
677;SB;Solomon Islands
676;TO;Tonga
675;PG;Papua New Guinea
674;NR;Nauru
673;BN;Brunei Darussalam;XXX XXXX
672;NF;Norfolk Island
670;TL;Timor-Leste
599;BQ;Bonaire, Sint Eustatius & Saba
599;CW;Curaçao
598;UY;Uruguay;X XXX XXXX
597;SR;Suriname;XXX XXXX
596;MQ;Martinique
595;PY;Paraguay;XXX XXX XXX
594;GF;French Guiana
593;EC;Ecuador;XX XXX XXXX
592;GY;Guyana
591;BO;Bolivia;X XXX XXXX
590;GP;Guadeloupe;XXX XX XX XX
509;HT;Haiti
508;PM;Saint Pierre & Miquelon
507;PA;Panama;XXXX XXXX
506;CR;Costa Rica;XXXX XXXX
505;NI;Nicaragua;XXXX XXXX
504;HN;Honduras;XXXX XXXX
503;SV;El Salvador;XXXX XXXX
502;GT;Guatemala;X XXX XXXX
501;BZ;Belize
500;FK;Falkland Islands
423;LI;Liechtenstein
421;SK;Slovakia;XXX XXX XXX
420;CZ;Czech Republic;XXX XXX XXX
389;MK;Macedonia;XX XXX XXX
387;BA;Bosnia & Herzegovina;XX XXX XXX
386;SI;Slovenia;XX XXX XXX
385;HR;Croatia
382;ME;Montenegro
381;RS;Serbia;XX XXX XXXX
380;UA;Ukraine;XX XXX XX XX
378;SM;San Marino;XXX XXX XXXX
377;MC;Monaco;XXXX XXXX
376;AD;Andorra;XX XX XX
375;BY;Belarus;XX XXX XXXX
374;AM;Armenia;XX XXX XXX
373;MD;Moldova;XX XXX XXX
372;EE;Estonia
371;LV;Latvia;XXX XXXXX
370;LT;Lithuania;XXX XXXXX
359;BG;Bulgaria
358;FI;Finland
357;CY;Cyprus;XXXX XXXX
356;MT;Malta;XX XX XX XX
355;AL;Albania;XX XXX XXXX
354;IS;Iceland;XXX XXXX
353;IE;Ireland;XX XXX XXXX
352;LU;Luxembourg
351;PT;Portugal;X XXXX XXXX
350;GI;Gibraltar;XXXX XXXX
299;GL;Greenland;XXX XXX
298;FO;Faroe Islands;XXX XXX
297;AW;Aruba;XXX XXXX
291;ER;Eritrea;X XXX XXX
290;SH;Saint Helena;XX XXX
269;KM;Comoros;XXX XXXX
268;SZ;Swaziland;XXXX XXXX
267;BW;Botswana;XX XXX XXX
266;LS;Lesotho;XX XXX XXX
265;MW;Malawi;77 XXX XXXX
264;NA;Namibia;XX XXX XXXX
263;ZW;Zimbabwe;XX XXX XXXX
262;RE;Réunion;XXX XXX XXX
261;MG;Madagascar;XX XX XXX XX
260;ZM;Zambia;XX XXX XXXX
258;MZ;Mozambique;XX XXX XXXX
257;BI;Burundi;XX XX XXXX
256;UG;Uganda;XX XXX XXXX
255;TZ;Tanzania;XX XXX XXXX
254;KE;Kenya;XXX XXX XXX
253;DJ;Djibouti;XX XX XX XX
252;SO;Somalia;XX XXX XXX
251;ET;Ethiopia;XX XXX XXXX
250;RW;Rwanda;XXX XXX XXX
249;SD;Sudan;XX XXX XXXX
248;SC;Seychelles;X XX XX XX
247;SH;Saint Helena;XXXX
246;IO;Diego Garcia;XXX XXXX
245;GW;Guinea-Bissau;XXX XXXX
244;AO;Angola;XXX XXX XXX
243;CD;Congo (Dem. Rep.);XX XXX XXXX
242;CG;Congo (Rep.);XX XXX XXXX
241;GA;Gabon;X XX XX XX
240;GQ;Equatorial Guinea;XXX XXX XXX
239;ST;São Tomé & Príncipe;XX XXXXX
238;CV;Cape Verde;XXX XXXX
237;CM;Cameroon;XXXX XXXX
236;CF;Central African Rep.;XX XX XX XX
235;TD;Chad;XX XX XX XX
234;NG;Nigeria
233;GH;Ghana
232;SL;Sierra Leone;XX XXX XXX
231;LR;Liberia
230;MU;Mauritius
229;BJ;Benin;XX XXX XXX
228;TG;Togo;XX XXX XXX
227;NE;Niger;XX XX XX XX
226;BF;Burkina Faso;XX XX XX XX
225;CI;Côte d`Ivoire;XX XXX XXX
224;GN;Guinea;XXX XXX XXX
223;ML;Mali;XXXX XXXX
222;MR;Mauritania;XXXX XXXX
221;SN;Senegal;XX XXX XXXX
220;GM;Gambia;XXX XXXX
218;LY;Libya;XX XXX XXXX
216;TN;Tunisia;XX XXX XXX
213;DZ;Algeria;XXX XX XX XX
212;MA;Morocco;XX XXX XXXX
211;SS;South Sudan;XX XXX XXXX
98;IR;Iran;XXX XXX XXXX
95;MM;Myanmar
94;LK;Sri Lanka;XX XXX XXXX
93;AF;Afghanistan;XXX XXX XXX
92;PK;Pakistan;XXX XXX XXXX
91;IN;India;XXXXX XXXXX
90;TR;Turkey;XXX XXX XXXX
86;CN;China;XXX XXXX XXXX
84;VN;Vietnam
82;KR;South Korea
81;JP;Japan;XX XXXX XXXX
66;TH;Thailand;X XXXX XXXX
65;SG;Singapore;XXXX XXXX
64;NZ;New Zealand
63;PH;Philippines;XXX XXX XXXX
62;ID;Indonesia
61;AU;Australia;XXX XXX XXX
60;MY;Malaysia
58;VE;Venezuela;XXX XXX XXXX
57;CO;Colombia;XXX XXX XXXX
56;CL;Chile;X XXXX XXXX
55;BR;Brazil;XX XXXXX XXXX
54;AR;Argentina
53;CU;Cuba;XXXX XXXX
52;MX;Mexico
51;PE;Peru;XXX XXX XXX
49;DE;Germany
48;PL;Poland;XX XXX XXXX
47;NO;Norway;XXXX XXXX
46;SE;Sweden;XX XXX XXXX
45;DK;Denmark;XXXX XXXX
44;GB;United Kingdom;XXXX XXXXXX
43;AT;Austria
42;YL;Y-land
41;CH;Switzerland;XX XXX XXXX
40;RO;Romania;XXX XXX XXX
39;IT;Italy
36;HU;Hungary;XXX XXX XXX
34;ES;Spain;XXX XXX XXX
33;FR;France;X XX XX XX XX
32;BE;Belgium;XXX XX XX XX
31;NL;Netherlands;X XX XX XX XX
30;GR;Greece;XXX XXX XXXX
27;ZA;South Africa;XX XXX XXXX
20;EG;Egypt;XX XXXX XXXX
7;KZ;Kazakhstan;XXX XXX XX XX
7;RU;Russian Federation;XXX XXX XXXX
1;PR;Puerto Rico;XXX XXX XXXX
1;DO;Dominican Rep.;XXX XXX XXXX
1;CA;Canada;XXX XXX XXXX
1;US;USA;XXX XXX XXXX
Binary file not shown.
Binary file not shown.
@@ -1,40 +1,46 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
import android.Manifest;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DownloadManager;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
@@ -56,10 +62,9 @@ import android.widget.AbsListView;
import android.widget.EdgeEffect;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.b44t.ui.ActionBar.BaseFragment;
import com.b44t.ui.Components.ForegroundDetector;
import com.b44t.ui.Components.TypefaceSpan;
@@ -72,6 +77,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
@@ -81,12 +87,7 @@ import java.util.regex.Pattern;
public class AndroidUtilities {
private static final Hashtable<String, Typeface> typefaceCache = new Hashtable<>();
private static int prevOrientation = -10;
private static boolean waitingForSms = false;
private static boolean waitingForCall = false;
private static final Object smsLock = new Object();
private static final Object callLock = new Object();
public static int statusBarHeight = 0;
public static float density = 1;
@@ -98,10 +99,6 @@ public class AndroidUtilities {
private static Boolean isTablet = null;
private static int adjustOwnerClassGuid = 0;
private static Paint roundPaint;
private static RectF bitmapRect;
public static Pattern WEB_URL = null;
static {
try {
final String GOOD_IRI_CHAR = "a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF";
@@ -115,15 +112,6 @@ public class AndroidUtilities {
final String GTLD = "[" + GOOD_GTLD_CHAR + "]{2,63}";
final String HOST_NAME = "(" + IRI + "\\.)+" + GTLD;
final Pattern DOMAIN_NAME = Pattern.compile("(" + HOST_NAME + "|" + IP_ADDRESS + ")");
WEB_URL = Pattern.compile(
"((?:(http|https|Http|Https):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)"
+ "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_"
+ "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?"
+ "(?:" + DOMAIN_NAME + ")"
+ "(?:\\:\\d{1,5})?)" // plus option port number
+ "(\\/(?:(?:[" + GOOD_IRI_CHAR + "\\;\\/\\?\\:\\@\\&\\=\\#\\~" // plus option query params
+ "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?"
+ "(?:\\b|$)");
} catch (Exception e) {
FileLog.e("messenger", e);
}
@@ -135,101 +123,6 @@ public class AndroidUtilities {
checkDisplaySize();
}
public static int[] calcDrawableColor(Drawable drawable) {
int bitmapColor = 0xff000000;
int result[] = new int[2];
try {
if (drawable instanceof BitmapDrawable) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
if (bitmap != null) {
Bitmap b = Bitmaps.createScaledBitmap(bitmap, 1, 1, true);
if (b != null) {
bitmapColor = b.getPixel(0, 0);
b.recycle();
}
}
} else if (drawable instanceof ColorDrawable) {
bitmapColor = ((ColorDrawable) drawable).getColor();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
double[] hsv = rgbToHsv((bitmapColor >> 16) & 0xff, (bitmapColor >> 8) & 0xff, bitmapColor & 0xff);
hsv[1] = Math.min(1.0, hsv[1] + 0.05 + 0.1 * (1.0 - hsv[1]));
hsv[2] = Math.max(0, hsv[2] * 0.65);
int rgb[] = hsvToRgb(hsv[0], hsv[1], hsv[2]);
result[0] = Color.argb(0x66, rgb[0], rgb[1], rgb[2]);
result[1] = Color.argb(0x88, rgb[0], rgb[1], rgb[2]);
return result;
}
private static double[] rgbToHsv(int r, int g, int b) {
double rf = r / 255.0;
double gf = g / 255.0;
double bf = b / 255.0;
double max = (rf > gf && rf > bf) ? rf : (gf > bf) ? gf : bf;
double min = (rf < gf && rf < bf) ? rf : (gf < bf) ? gf : bf;
double h, s;
double d = max - min;
s = max == 0 ? 0 : d / max;
if (max == min) {
h = 0;
} else {
if (rf > gf && rf > bf) {
h = (gf - bf) / d + (gf < bf ? 6 : 0);
} else if (gf > bf) {
h = (bf - rf) / d + 2;
} else {
h = (rf - gf) / d + 4;
}
h /= 6;
}
return new double[]{h, s, max};
}
private static int[] hsvToRgb(double h, double s, double v) {
double r = 0, g = 0, b = 0;
double i = (int) Math.floor(h * 6);
double f = h * 6 - i;
double p = v * (1 - s);
double q = v * (1 - f * s);
double t = v * (1 - (1 - f) * s);
switch ((int) i % 6) {
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
case 5:
r = v;
g = p;
b = q;
break;
}
return new int[]{(int) (r * 255), (int) (g * 255), (int) (b * 255)};
}
public static void requestAdjustResize(Activity activity, int classGuid) {
if (activity == null || isTablet()) {
return;
@@ -247,34 +140,6 @@ public class AndroidUtilities {
}
}
public static boolean isGoogleMapsInstalled(final BaseFragment fragment) {
try {
ApplicationLoader.applicationContext.getPackageManager().getApplicationInfo("com.google.android.apps.maps", 0);
return true;
} catch (PackageManager.NameNotFoundException e) {
if (fragment.getParentActivity() == null) {
return false;
}
AlertDialog.Builder builder = new AlertDialog.Builder(fragment.getParentActivity());
builder.setMessage("Install Google Maps?");
builder.setCancelable(true);
builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.google.android.apps.maps"));
fragment.getParentActivity().startActivityForResult(intent, 500);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
});
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
fragment.showDialog(builder.create());
return false;
}
}
public static boolean isInternalUri(Uri uri) {
String pathString = uri.getPath();
if (pathString == null) {
@@ -346,49 +211,6 @@ public class AndroidUtilities {
}
}
public static Typeface getTypeface(String assetPath) {
synchronized (typefaceCache) {
if (!typefaceCache.containsKey(assetPath)) {
try {
Typeface t = Typeface.createFromAsset(ApplicationLoader.applicationContext.getAssets(), assetPath);
typefaceCache.put(assetPath, t);
} catch (Exception e) {
FileLog.e("Typefaces", "Could not get typeface '" + assetPath + "' because " + e.getMessage());
return null;
}
}
return typefaceCache.get(assetPath);
}
}
public static boolean isWaitingForSms() {
boolean value;
synchronized (smsLock) {
value = waitingForSms;
}
return value;
}
public static void setWaitingForSms(boolean value) {
synchronized (smsLock) {
waitingForSms = value;
}
}
public static boolean isWaitingForCall() {
boolean value;
synchronized (callLock) {
value = waitingForCall;
}
return value;
}
public static void setWaitingForCall(boolean value) {
synchronized (callLock) {
waitingForCall = value;
}
}
public static void showKeyboard(View view) {
if (view == null) {
return;
@@ -401,19 +223,6 @@ public class AndroidUtilities {
}
}
public static boolean isKeyboardShowed(View view) {
if (view == null) {
return false;
}
try {
InputMethodManager inputManager = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
return inputManager.isActive(view);
} catch (Exception e) {
FileLog.e("messenger", e);
}
return false;
}
public static void hideKeyboard(View view) {
if (view == null) {
return;
@@ -429,6 +238,7 @@ public class AndroidUtilities {
}
}
/*
public static File getCacheDir() {
String state = null;
try {
@@ -456,6 +266,7 @@ public class AndroidUtilities {
}
return new File("");
}
*/
public static int dp(float value) {
if (value == 0) {
@@ -502,10 +313,6 @@ public class AndroidUtilities {
return (cm / 2.54f) * (isX ? displayMetrics.xdpi : displayMetrics.ydpi);
}
public static int getPeerLayerVersion(int layer) {
return (layer >> 16) & 0xffff;
}
public static void runOnUIThread(Runnable runnable) {
runOnUIThread(runnable, 0);
}
@@ -564,97 +371,6 @@ public class AndroidUtilities {
return photoSize;
}
public static String formatTTLString(int ttl) {
if (ttl < 60) {
return LocaleController.formatPluralString("Seconds", ttl);
} else if (ttl < 60 * 60) {
return LocaleController.formatPluralString("Minutes", ttl / 60);
} else if (ttl < 60 * 60 * 24) {
return LocaleController.formatPluralString("Hours", ttl / 60 / 60);
} else if (ttl < 60 * 60 * 24 * 7) {
return LocaleController.formatPluralString("Days", ttl / 60 / 60 / 24);
} else {
int days = ttl / 60 / 60 / 24;
if (ttl % 7 == 0) {
return LocaleController.formatPluralString("Weeks", days / 7);
} else {
return String.format("%s %s", LocaleController.formatPluralString("Weeks", days / 7), LocaleController.formatPluralString("Days", days % 7));
}
}
}
/*
public static AlertDialog.Builder buildTTLAlert(final Context context, final TLRPC.EncryptedChat encryptedChat) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(LocaleController.getString("MessageLifetime", R.string.MessageLifetime));
final NumberPicker numberPicker = new NumberPicker(context);
numberPicker.setMinValue(0);
numberPicker.setMaxValue(20);
if (encryptedChat.ttl > 0 && encryptedChat.ttl < 16) {
numberPicker.setValue(encryptedChat.ttl);
} else if (encryptedChat.ttl == 30) {
numberPicker.setValue(16);
} else if (encryptedChat.ttl == 60) {
numberPicker.setValue(17);
} else if (encryptedChat.ttl == 60 * 60) {
numberPicker.setValue(18);
} else if (encryptedChat.ttl == 60 * 60 * 24) {
numberPicker.setValue(19);
} else if (encryptedChat.ttl == 60 * 60 * 24 * 7) {
numberPicker.setValue(20);
} else if (encryptedChat.ttl == 0) {
numberPicker.setValue(0);
}
numberPicker.setFormatter(new NumberPicker.Formatter() {
@Override
public String format(int value) {
if (value == 0) {
return LocaleController.getString("ShortMessageLifetimeForever", R.string.ShortMessageLifetimeForever);
} else if (value >= 1 && value < 16) {
return AndroidUtilities.formatTTLString(value);
} else if (value == 16) {
return AndroidUtilities.formatTTLString(30);
} else if (value == 17) {
return AndroidUtilities.formatTTLString(60);
} else if (value == 18) {
return AndroidUtilities.formatTTLString(60 * 60);
} else if (value == 19) {
return AndroidUtilities.formatTTLString(60 * 60 * 24);
} else if (value == 20) {
return AndroidUtilities.formatTTLString(60 * 60 * 24 * 7);
}
return "";
}
});
builder.setView(numberPicker);
builder.setNegativeButton(LocaleController.getString("Done", R.string.Done), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int oldValue = encryptedChat.ttl;
which = numberPicker.getValue();
if (which >= 0 && which < 16) {
encryptedChat.ttl = which;
} else if (which == 16) {
encryptedChat.ttl = 30;
} else if (which == 17) {
encryptedChat.ttl = 60;
} else if (which == 18) {
encryptedChat.ttl = 60 * 60;
} else if (which == 19) {
encryptedChat.ttl = 60 * 60 * 24;
} else if (which == 20) {
encryptedChat.ttl = 60 * 60 * 24 * 7;
}
if (oldValue != encryptedChat.ttl) {
//SecretChatHelper.getInstance().sendTTLMessage(encryptedChat, null);
//MessagesStorage.getInstance().updateEncryptedChatTTL(encryptedChat);
}
}
});
return builder;
}
*/
public static void clearCursorDrawable(EditText editText) {
if (editText == null) {
return;
@@ -668,71 +384,13 @@ public class AndroidUtilities {
}
}
public static void setProgressBarAnimationDuration(ProgressBar progressBar, int duration) {
if (progressBar == null) {
return;
}
try {
Field mCursorDrawableRes = ProgressBar.class.getDeclaredField("mDuration");
mCursorDrawableRes.setAccessible(true);
mCursorDrawableRes.setInt(progressBar, duration);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
private static Intent createShortcutIntent(long did, boolean forDelete) {
private static Intent createShortcutIntent(int did, Bitmap bitmap) {
Intent shortcutIntent = new Intent(ApplicationLoader.applicationContext, OpenChatReceiver.class);
/*
int lower_id = (int) did;
int high_id = (int) (did >> 32);
TLRPC.User user = null;
TLRPC.Chat chat = null;
if (lower_id == 0) {
shortcutIntent.putExtra("encId", high_id);
TLRPC.EncryptedChat encryptedChat = MessagesController.getInstance().getEncryptedChat(high_id);
if (encryptedChat == null) {
return null;
}
user = MessagesController.getInstance().getUser(encryptedChat.user_id);
} else if (lower_id > 0) {
shortcutIntent.putExtra("userId", lower_id);
user = MessagesController.getInstance().getUser(lower_id);
} else if (lower_id < 0) {
chat = MessagesController.getInstance().getChat(-lower_id);
shortcutIntent.putExtra("chatId", -lower_id);
} else {
return null;
}
if (user == null && chat == null) {
return null;
}
String name;
TLRPC.FileLocation photo = null;
if (user != null) {
name = ContactsController.formatName(user.first_name, user.last_name);
if (user.photo != null) {
photo = user.photo.photo_small;
}
} else {
name = chat.title;
if (chat.photo != null) {
photo = chat.photo.photo_small;
}
}
*/
TLRPC.FileLocation photo = null;
shortcutIntent.putExtra("chatId", did);
MrChat mrChat = MrMailbox.getChat((int)did);
MrChat mrChat = MrMailbox.getChat(did);
if( mrChat.getId() == 0 ) { return null; }
String name = mrChat.getName();
shortcutIntent.setAction("com.b44t.messenger.openchat" + did);
shortcutIntent.addFlags(0x4000000);
@@ -740,70 +398,15 @@ public class AndroidUtilities {
addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
addIntent.putExtra("duplicate", false);
if (!forDelete) {
Bitmap bitmap = null;
if (photo != null) {
try {
File path = FileLoader.getPathToAttach(photo, true);
bitmap = BitmapFactory.decodeFile(path.toString());
if (bitmap != null) {
int size = AndroidUtilities.dp(58);
Bitmap result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
result.eraseColor(Color.TRANSPARENT);
Canvas canvas = new Canvas(result);
BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
if (roundPaint == null) {
roundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bitmapRect = new RectF();
}
float scale = size / (float) bitmap.getWidth();
canvas.save();
canvas.scale(scale, scale);
roundPaint.setShader(shader);
bitmapRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
canvas.drawRoundRect(bitmapRect, bitmap.getWidth(), bitmap.getHeight(), roundPaint);
canvas.restore();
Drawable drawable = ApplicationLoader.applicationContext.getResources().getDrawable(R.drawable.book_logo);
int w = AndroidUtilities.dp(15);
int left = size - w - AndroidUtilities.dp(2);
int top = size - w - AndroidUtilities.dp(2);
drawable.setBounds(left, top, left + w, top + w);
drawable.draw(canvas);
try {
canvas.setBitmap(null);
} catch (Exception e) {
//don't promt, this will crash on 2.x
}
bitmap = result;
}
} catch (Throwable e) {
FileLog.e("messenger", e);
}
}
if (bitmap != null) {
addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);
} else {
/*if (user != null) {
if (user.bot) {
addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(ApplicationLoader.applicationContext, R.drawable.book_bot));
} else {
addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(ApplicationLoader.applicationContext, R.drawable.book_user));
}
} else if (chat != null) {
if (ChatObject.isChannel(chat) && !chat.megagroup) {
addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(ApplicationLoader.applicationContext, R.drawable.book_channel));
} else {*/
addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(ApplicationLoader.applicationContext, R.drawable.book_group));
/*}
}*/
}
if( bitmap != null ) {
addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);
}
return addIntent;
}
public static void installShortcut(long did) {
public static void installShortcut(int did, Bitmap bitmap) {
try {
Intent addIntent = createShortcutIntent(did, false);
Intent addIntent = createShortcutIntent(did, bitmap);
addIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
ApplicationLoader.applicationContext.sendBroadcast(addIntent);
} catch (Exception e) {
@@ -811,15 +414,18 @@ public class AndroidUtilities {
}
}
public static void uninstallShortcut(long did) {
/* -- uninstalling shortcuts would be needed if a chat is deleted. However,
-- com.b44t.messenger.openchatNNN just opens the overview in this case, which is okay for the moment.
-- uninstalling shortcuts would required the permission com.android.launcher.permission.UNINSTALL_SHORTCUT
public static void uninstallShortcut(int did) {
try {
Intent addIntent = createShortcutIntent(did, true);
Intent addIntent = createShortcutIntent(did, null);
addIntent.setAction("com.android.launcher.action.UNINSTALL_SHORTCUT");
ApplicationLoader.applicationContext.sendBroadcast(addIntent);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}*/
public static int getViewInset(View view) {
if (view == null || Build.VERSION.SDK_INT < 21 || view.getHeight() == AndroidUtilities.displaySize.y || view.getHeight() == AndroidUtilities.displaySize.y - statusBarHeight) {
@@ -969,7 +575,7 @@ public class AndroidUtilities {
}
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(stringBuilder);
for (int a = 0; a < bolds.size() / 2; a++) {
spannableStringBuilder.setSpan(new TypefaceSpan(AndroidUtilities.getTypeface("fonts/rmedium.ttf")), bolds.get(a * 2), bolds.get(a * 2 + 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannableStringBuilder.setSpan(new TypefaceSpan(Typeface.DEFAULT_BOLD), bolds.get(a * 2), bolds.get(a * 2 + 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
for (int a = 0; a < colors.size() / 3; a++) {
spannableStringBuilder.setSpan(new ForegroundColorSpan(colors.get(a * 3 + 2)), colors.get(a * 3), colors.get(a * 3 + 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
@@ -1007,54 +613,25 @@ public class AndroidUtilities {
animatorSet.start();
}
/*public static String ellipsize(String text, int maxLines, int maxWidth, TextPaint paint) {
if (text == null || paint == null) {
return null;
}
int count;
int offset = 0;
StringBuilder result = null;
TextView
for (int a = 0; a < maxLines; a++) {
count = paint.breakText(text, true, maxWidth, null);
if (a != maxLines - 1) {
if (result == null) {
result = new StringBuilder(count * maxLines + 1);
}
boolean foundSpace = false;
for (int c = count - 1; c >= offset; c--) {
if (text.charAt(c) == ' ') {
foundSpace = true;
result.append(text.substring(offset, c - 1));
offset = c - 1;
}
}
if (!foundSpace) {
offset = count;
}
text = text.substring(0, offset);
} else if (maxLines == 1) {
return text.substring(0, count);
} else {
result.append(text.substring(0, count));
}
}
return result.toString();
}*/
public static void showDoneHint(Context context)
{
// this hint should be shown when an action is complete and not directly visible on the screen,
// eg. copy-to-clipboard or item deletion in a long list
Toast.makeText(context, ApplicationLoader.applicationContext.getString(R.string.DoneHint), Toast.LENGTH_SHORT).show();
}
/*public static void turnOffHardwareAcceleration(Window window) {
if (window == null || Build.MODEL == null) {
return;
}
if (Build.MODEL.contains("GT-S5301") ||
Build.MODEL.contains("GT-S5303") ||
Build.MODEL.contains("GT-B5330") ||
Build.MODEL.contains("GT-S5302") ||
Build.MODEL.contains("GT-S6012B") ||
Build.MODEL.contains("MegaFon_SP-AI")) {
window.clearFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
}
}*/
public static void showErrorHint(Context context)
{
// this hint should be shown on errors that normally should not happen.
showHint(context, ApplicationLoader.applicationContext.getString(R.string.ErrorHint));
}
public static Toast showHint(Context context, String text)
{
Toast t = Toast.makeText(context, text, Toast.LENGTH_LONG);
t.show();
return t;
}
public static void addToClipboard(CharSequence str) {
try {
@@ -1211,49 +788,6 @@ public class AndroidUtilities {
return null;
}
public static CharSequence generateSearchName(String name, String name2, String q) {
if (name == null && name2 == null) {
return "";
}
SpannableStringBuilder builder = new SpannableStringBuilder();
String wholeString = name;
if (wholeString == null || wholeString.length() == 0) {
wholeString = name2;
} else if (name2 != null && name2.length() != 0) {
wholeString += " " + name2;
}
wholeString = wholeString.trim();
String lower = " " + wholeString.toLowerCase();
int index;
int lastIndex = 0;
while ((index = lower.indexOf(" " + q, lastIndex)) != -1) {
int idx = index - (index == 0 ? 0 : 1);
int end = q.length() + (index == 0 ? 0 : 1) + idx;
if (lastIndex != 0 && lastIndex != idx + 1) {
builder.append(wholeString.substring(lastIndex, idx));
} else if (lastIndex == 0 && idx != 0) {
builder.append(wholeString.substring(0, idx));
}
String query = wholeString.substring(idx, end);
if (query.startsWith(" ")) {
builder.append(" ");
}
query = query.trim();
builder.append(AndroidUtilities.replaceTags("<c#ff4d83b3>" + query + "</c>"));
lastIndex = end;
}
if (lastIndex != -1 && lastIndex != wholeString.length()) {
builder.append(wholeString.substring(lastIndex, wholeString.length()));
}
return builder;
}
public static File generateVideoPath() {
try {
File storageDir = getAlbumDir();
@@ -1265,15 +799,15 @@ public class AndroidUtilities {
return null;
}
public static String formatFileSize(long size) {
if (size < 1024) {
return String.format("%d B", size);
} else if (size < 1024 * 1024) {
return String.format("%.1f KB", size / 1024.0f);
} else if (size < 1024 * 1024 * 1024) {
return String.format("%.1f MB", size / 1024.0f / 1024.0f);
public static String formatFileSize(long bytes) {
// We use the SI-prefix with the usual base as defined in the 1990's.
// The other option would be to show MiB and KiB, however, these abbreviations are not very usual.
if (bytes < 1000) {
return String.format("%d Byte", bytes);
} else if (bytes < (1000 * 1000)-500) { // 999499 Byte=999 KB, 999500 Byte=1,0 MB
return String.format("%.0f KB", bytes / 1000.0f);
} else {
return String.format("%.1f GB", size / 1024.0f / 1024.0f / 1024.0f);
return String.format("%.1f MB", bytes / 1000.0f / 1000.0f);
}
}
@@ -1342,56 +876,186 @@ public class AndroidUtilities {
return true;
}
public static byte[] calcAuthKeyHash(byte[] auth_key) {
byte[] sha1 = Utilities.computeSHA1(auth_key);
byte[] key_hash = new byte[16];
System.arraycopy(sha1, 0, key_hash, 0, 16);
return key_hash;
}
/* open, view, download files
**********************************************************************************************/
public static void openForView(MessageObject message, Activity activity) throws Exception {
File f = null;
String fileName = message.getFileName();
if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) {
f = new File(message.messageOwner.attachPath);
}
if (f == null || !f.exists()) {
f = FileLoader.getPathToMessage(message.messageOwner);
}
if (f != null && f.exists()) {
String realMimeType = null;
Intent intent = new Intent(Intent.ACTION_VIEW);
MimeTypeMap myMime = MimeTypeMap.getSingleton();
public static String getMimetype(String fileName, String def)
{
String mimeType = "application/octet-stream";
try {
MimeTypeMap mimeMap = MimeTypeMap.getSingleton();
int idx = fileName.lastIndexOf('.');
if (idx != -1) {
String ext = fileName.substring(idx + 1);
realMimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase());
if (realMimeType == null) {
if (message.type == 9 || message.type == 0) {
realMimeType = message.getDocument().mime_type;
}
if (realMimeType == null || realMimeType.length() == 0) {
realMimeType = null;
}
}
if (realMimeType != null) {
intent.setDataAndType(Uri.fromFile(f), realMimeType);
} else {
intent.setDataAndType(Uri.fromFile(f), "text/plain");
}
} else {
intent.setDataAndType(Uri.fromFile(f), "text/plain");
mimeType = mimeMap.getMimeTypeFromExtension(ext.toLowerCase());
}
if (realMimeType != null) {
try {
activity.startActivityForResult(intent, 500);
} catch (Exception e) {
intent.setDataAndType(Uri.fromFile(f), "text/plain");
activity.startActivityForResult(intent, 500);
}
} else {
activity.startActivityForResult(intent, 500);
if (mimeType == null) {
mimeType = def==null? "application/octet-stream" : def;
}
}
catch(Exception e) {
}
return mimeType;
}
public static void openForViewOrShare(Activity activity, int msg_id, String cmd)
{
MrMsg msg = MrMailbox.getMsg(msg_id);
String path = msg.getParam('f', "");
String mimeType = getMimetype(path, msg.getParam('m', "application/octet-stream"));
try {
File file = new File(path);
if( !file.exists() ) {
showHint(activity, LocaleController.formatString("FileNotFound", R.string.FileNotFound, path));
return;
}
Uri uri;
if (path.startsWith(MrMailbox.getBlobdir())) {
uri = Uri.parse("content://" + BuildConfig.APPLICATION_ID + ".attachments/" + file.getName());
} else {
uri = Uri.fromFile(file);
}
if( cmd == Intent.ACTION_VIEW ) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, mimeType);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivity(intent);
}
else {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType(mimeType);
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivity(Intent.createChooser(intent, ApplicationLoader.applicationContext.getString(R.string.Share)));
}
}
catch (Exception e) {
showHint(activity, LocaleController.formatString("NoHandleAppInstalled", R.string.NoHandleAppInstalled, mimeType));
}
}
public static File getFineFilename(File path, String desiredName)
{
// get a fine file name by adding a number to the basename and avoid overwrites
for( int i = 0; i < 1000; i++ ) {
String testName = desiredName;
if( i > 0 ) {
String baseName=desiredName, ext = "";
int idx = desiredName.lastIndexOf('.');
if( idx != -1 ) {
baseName = desiredName.substring(0, idx);
ext = desiredName.substring(idx);
}
testName = String.format("%s-%d%s", baseName, i, ext);
}
File pathNFile = new File(path, testName);
if (!pathNFile.exists()) {
return pathNFile;
}
}
return null;
}
public static void saveMessageFileToExt(final Activity context, int msg_id)
{
if (Build.VERSION.SDK_INT >= 23 && context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
context.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 4);
return;
}
MrMsg msg = MrMailbox.getMsg(msg_id);
final int msg_type = msg.getType();
String msg_file_path = msg.getParam('f', "");
final String msg_mime = msg.getParam('m', "application/octet-stream");
final String msg_file_name = msg.getFilename();
final File sourceFile = new File(msg_file_path);
if( !sourceFile.exists() ) {
showHint(context, LocaleController.formatString("FileNotFound", R.string.FileNotFound, msg_file_path));
return;
}
final Toast waitingHint = showHint(context, ApplicationLoader.applicationContext.getString(R.string.OneMoment));
new Thread(new Runnable() {
@Override
public void run() {
boolean allOkay = false;
try {
// get destination path
File destPath = null;
boolean add_to_download_manager = false;
if (msg_type == MrMsg.MR_MSG_IMAGE || msg_type == MrMsg.MR_MSG_VIDEO) {
destPath = AndroidUtilities.getAlbumDir();
} else if (msg_type == MrMsg.MR_MSG_FILE) {
destPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
add_to_download_manager = true;
} else if (msg_type == MrMsg.MR_MSG_AUDIO || msg_type == MrMsg.MR_MSG_VOICE ) {
destPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
}
// create destination path (must be done, see documentation for getExternalStoragePublicDirectory())
destPath.mkdirs();
File destPathNFile = getFineFilename(destPath, msg_file_name);
if( destPathNFile != null )
{
destPathNFile.createNewFile();
boolean copied = true;
FileChannel source = null;
FileChannel destination = null;
try {
source = new FileInputStream(sourceFile).getChannel();
destination = new FileOutputStream(destPathNFile).getChannel();
long size = source.size();
for (long a = 0; a < size; a += 4096) {
destination.transferFrom(source, a, Math.min(4096, size - a));
}
} catch (Exception e) {
copied = false;
} finally {
if (source != null) {
source.close();
}
if (destination != null) {
destination.close();
}
}
if (copied) {
if ( add_to_download_manager ) {
DownloadManager downloadManager = (DownloadManager) ApplicationLoader.applicationContext.getSystemService(Context.DOWNLOAD_SERVICE);
downloadManager.addCompletedDownload(destPathNFile.getName(), destPathNFile.getName(), false, getMimetype(msg_file_name, msg_mime), destPathNFile.getAbsolutePath(), destPathNFile.length(), true);
} else {
addMediaToGallery(Uri.fromFile(destPathNFile));
}
allOkay = true;
}
}
}
catch (Exception e) {
;
}
final boolean allOkayFinal = allOkay;
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
waitingHint.cancel();
if( allOkayFinal ) {
showDoneHint(context);
}
else {
showHint(context, ApplicationLoader.applicationContext.getString(R.string.AccessError));
}
}
});
}
}).start();
}
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -38,9 +53,6 @@ public class ApplicationLoader extends Application {
private static boolean isCustomTheme;
private static final Object sync = new Object();
private static int serviceMessageColor;
private static int serviceSelectedMessageColor;
public static volatile Context applicationContext;
public static volatile Handler applicationHandler;
private static volatile boolean applicationInited = false;
@@ -58,25 +70,11 @@ public class ApplicationLoader extends Application {
public static void reloadWallpaper() {
cachedWallpaper = null;
serviceMessageColor = 0;
ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE).edit().remove("serviceMessageColor").commit();
loadWallpaper();
}
private static void calcBackgroundColor() {
int result[] = AndroidUtilities.calcDrawableColor(cachedWallpaper);
serviceMessageColor = result[0];
serviceSelectedMessageColor = result[1];
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
preferences.edit().putInt("serviceMessageColor", serviceMessageColor).putInt("serviceSelectedMessageColor", serviceSelectedMessageColor).commit();
}
public static int getServiceMessageColor() {
return serviceMessageColor;
}
public static int getServiceSelectedMessageColor() {
return serviceSelectedMessageColor;
return 0x44000000; // this color is used as a background for date headlines, empty chat hints and in the drawer
}
public static void loadWallpaper() {
@@ -92,8 +90,6 @@ public class ApplicationLoader extends Application {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
int selectedBackground = preferences.getInt("selectedBackground", 1000001);
selectedColor = preferences.getInt("selectedColor", 0);
serviceMessageColor = preferences.getInt("serviceMessageColor", 0);
serviceSelectedMessageColor = preferences.getInt("serviceSelectedMessageColor", 0);
if (selectedColor == 0) {
if (selectedBackground == 1000001) {
cachedWallpaper = applicationContext.getResources().getDrawable(R.drawable.background_hd);
@@ -118,9 +114,6 @@ public class ApplicationLoader extends Application {
}
cachedWallpaper = new ColorDrawable(selectedColor);
}
if (serviceMessageColor == 0) {
calcBackgroundColor();
}
}
}
});
@@ -183,26 +176,20 @@ public class ApplicationLoader extends Application {
UserConfig.loadConfig();
String deviceModel;
String langCode;
String appVersion;
String systemVersion;
String configPath = getFilesDirFixed().toString();
try {
langCode = LocaleController.getLocaleStringIso639();
deviceModel = Build.MANUFACTURER + Build.MODEL;
PackageInfo pInfo = ApplicationLoader.applicationContext.getPackageManager().getPackageInfo(ApplicationLoader.applicationContext.getPackageName(), 0);
appVersion = pInfo.versionName + " (" + pInfo.versionCode + ")";
systemVersion = "SDK " + Build.VERSION.SDK_INT;
} catch (Exception e) {
langCode = "en";
deviceModel = "Android unknown";
appVersion = "App version unknown";
systemVersion = "SDK " + Build.VERSION.SDK_INT;
}
if (langCode.trim().length() == 0) {
langCode = "en";
}
if (deviceModel.trim().length() == 0) {
deviceModel = "Android unknown";
}
@@ -220,26 +207,22 @@ public class ApplicationLoader extends Application {
// make sure, the notifications for the "deaddrop" dialog are muted by default
SharedPreferences.Editor editor = preferences.edit();
editor.putInt("notify2_"+MrChat.MR_CHAT_ID_DEADDROP, 2);
editor.commit();
editor.apply();
}
// open() sqlite file (you can inspect the file eg. with "Tools / Android Device Monitor / File Explorer")
// open() should be called before MessagesController.getInstance() as this also initilizes directories based upon getBlobdir().
File dbfile = new File(getFilesDirFixed(), "messenger.db");
MrMailbox.open(dbfile.getAbsolutePath());
MrMailbox.connect();
// create other default objects
MessagesController.getInstance();
ConnectionsManager.getInstance().init(deviceModel, systemVersion, appVersion, langCode, configPath, FileLog.getNetworkLogPath(), UserConfig.getClientUserId(), enablePushConnection);
ConnectionsManager.getInstance().init();
if (UserConfig.getCurrentUser() != null) {
MessagesController.getInstance().putUser(UserConfig.getCurrentUser(), true);
SendMessagesHelper.getInstance().checkUnsentMessages();
}
ApplicationLoader app = (ApplicationLoader)ApplicationLoader.applicationContext;
FileLog.e("messenger", "app initied");
//ContactsController.getInstance().checkAppAccount();
MediaController.getInstance();
// EDIT BY MR - open my sqlite file (you can inspect the file eg. with "Tools / Android Device Monitor / File Explorer")
File dbfile = new File(getFilesDirFixed(), "messenger.db");
MrMailbox.open(dbfile.getAbsolutePath(), "");
MrMailbox.connect();
}
@Override
@@ -255,45 +238,12 @@ public class ApplicationLoader extends Application {
// however, we may want to to have a look at onPause() eg. of activities (eg. for flushing data, if needed)
MrMailbox.MrCallback(0, 0, 0); // do not remove this call; this makes sure, the function is not removed from build or warnings are printed!
MrMailbox.init();
MrMailbox.MrStockAddStr(1, LocaleController.getString("NoMessages", R.string.NoMessages));
MrMailbox.MrStockAddStr(2, LocaleController.getString("FromSelf", R.string.FromSelf));
MrMailbox.MrStockAddStr(3, LocaleController.getString("Draft", R.string.Draft));
MrMailbox.MrStockAddStr(4, LocaleController.getString("MemberSg", R.string.MemberSg));
MrMailbox.MrStockAddStr(5, LocaleController.getString("MemberPl", R.string.MemberPl));
MrMailbox.MrStockAddStr(6, LocaleController.getString("ContactSg", R.string.ContactSg));
MrMailbox.MrStockAddStr(7, LocaleController.getString("ContactPl", R.string.ContactPl));
MrMailbox.MrStockAddStr(8, LocaleController.getString("Deaddrop", R.string.Deaddrop));
MrMailbox.MrStockAddStr(9, LocaleController.getString("AttachPhoto", R.string.AttachPhoto));
MrMailbox.MrStockAddStr(10, LocaleController.getString("AttachVideo", R.string.AttachVideo));
MrMailbox.MrStockAddStr(11, LocaleController.getString("AttachAudio", R.string.AttachAudio));
MrMailbox.MrStockAddStr(12, LocaleController.getString("AttachDocument", R.string.AttachDocument));
MrMailbox.MrStockAddStr(13, LocaleController.getString("DefaultStatusText", R.string.DefaultStatusText));
MrMailbox.MrStockAddStr(14, LocaleController.getString("SubjectPrefix", R.string.SubjectPrefix));
applicationHandler = new Handler(applicationContext.getMainLooper());
startPushService();
}
/*public static void sendRegIdToBackend(final String token) {
Utilities.stageQueue.postRunnable(new Runnable() {
@Override
public void run() {
UserConfig.pushString = token;
UserConfig.registeredForPush = false;
UserConfig.saveConfig(false);
if (UserConfig.getClientUserId() != 0) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
MessagesController.getInstance().registerForPush(token);
}
});
}
}
});
}*/
public static void startPushService() {
SharedPreferences preferences = applicationContext.getSharedPreferences("Notifications", MODE_PRIVATE);
@@ -0,0 +1,85 @@
/*******************************************************************************
*
* Messenger Android Frontend
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import java.io.File;
import java.io.FileNotFoundException;
public class AttachmentsContentProvider extends ContentProvider {
/* We save all attachments in our private files-directory
that cannot be read by other apps.
So starting, when starting an Intent for viewing (eg. openForView()), we cannot use the paths.
Instead, we give a content://-url that results in calls to this class.
(An alternative would be to copy files to view to a public directory, however, this would
lead to duplicate data.
Another alterntive would be to write all attachments to a public directory, however, this
may lead to security problems as files are system-wide-readable and would also cause problems
if the user or another app deletes these files) */
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
File privateFile = new File(MrMailbox.getBlobdir(), uri.getPath());
return ParcelFileDescriptor.open(privateFile, ParcelFileDescriptor.MODE_READ_ONLY);
}
@Override
public int delete(Uri arg0, String arg1, String[] arg2) {
return 0;
}
@Override
public String getType(Uri arg0) {
return null;
}
@Override
public Uri insert(Uri arg0, ContentValues arg1) {
return null;
}
@Override
public boolean onCreate() {
return false;
}
@Override
public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3,
String arg4) {
return null;
}
@Override
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
return 0;
}
}
@@ -1,97 +0,0 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package com.b44t.messenger;
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.accounts.NetworkErrorException;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
public class AuthenticatorService extends Service {
private static class Authenticator extends AbstractAccountAuthenticator {
private final Context context;
public Authenticator(Context context) {
super(context);
this.context = context;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options)
throws NetworkErrorException {
return null;
}
@Override
public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response, Account account) throws NetworkErrorException {
return super.getAccountRemovalAllowed(response, account);
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options)
throws NetworkErrorException {
return null;
}
@Override
public String getAuthTokenLabel(String authTokenType) {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response,
Account account, String[] features)
throws NetworkErrorException {
return null;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options)
throws NetworkErrorException {
return null;
}
}
private static Authenticator authenticator = null;
protected Authenticator getAuthenticator() {
if (authenticator == null) {
authenticator = new Authenticator(this);
}
return authenticator;
}
@Override
public IBinder onBind(Intent intent) {
if (intent.getAction().equals(AccountManager.ACTION_AUTHENTICATOR_INTENT)) {
return getAuthenticator().getIBinder();
} else {
return null;
}
}
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -22,6 +37,7 @@ public class AutoMessageHeardReceiver extends BroadcastReceiver {
if (dialog_id == 0 || max_id == 0) {
return;
}
MessagesController.getInstance().markDialogAsRead(dialog_id, max_id, max_id, 0, true, false);
MrMailbox.markseenChat((int)dialog_id);
NotificationsController.getInstance().removeSeenMessages();
}
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -32,7 +47,8 @@ public class AutoMessageReplyReceiver extends BroadcastReceiver {
if (dialog_id == 0 || max_id == 0) {
return;
}
SendMessagesHelper.getInstance().sendMessageText(text.toString(), dialog_id, null, null, true, null, null);
MessagesController.getInstance().markDialogAsRead(dialog_id, max_id, max_id, 0, true, false);
SendMessagesHelper.getInstance().sendMessageText(text.toString(), dialog_id, null);
MrMailbox.markseenChat((int)dialog_id);
NotificationsController.getInstance().removeSeenMessages();
}
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -1,10 +1,24 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -1,30 +0,0 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package com.b44t.messenger;
public class ChatObject {
public static final int CHAT_TYPE_CHAT = 0;
public static boolean isNotInChat(TLRPC.Chat chat) {
return chat == null || chat instanceof TLRPC.TL_chatEmpty || chat.left || chat.kicked || chat.deactivated;
}
public static boolean isChannel(TLRPC.Chat chat) {
return false;
}
public static boolean isChannel(int chatId) {
return false;
}
public static boolean canWriteToChat(TLRPC.Chat chat) {
return true;
}
}
@@ -1,82 +0,0 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package com.b44t.messenger;
import android.app.Activity;
import android.app.IntentService;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.system.Os;
import android.system.StructStat;
import java.io.File;
import java.util.HashMap;
public class ClearCacheService extends IntentService {
public ClearCacheService() {
super("ClearCacheService");
}
@Override
protected void onHandleIntent(Intent intent) {
ApplicationLoader.postInitApplication();
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
final int keepMedia = preferences.getInt("keep_media", 2);
if (keepMedia == 2) {
return;
}
Utilities.globalQueue.postRunnable(new Runnable() {
@Override
public void run() {
long currentTime = System.currentTimeMillis();
long diff = 60 * 60 * 1000 * 24 * (keepMedia == 0 ? 7 : 30);
final HashMap<Integer, File> paths = ImageLoader.getInstance().createMediaPaths();
for (HashMap.Entry<Integer, File> entry : paths.entrySet()) {
if (entry.getKey() == FileLoader.MEDIA_DIR_CACHE) {
continue;
}
try {
File[] array = entry.getValue().listFiles();
if (array != null) {
for (int b = 0; b < array.length; b++) {
File f = array[b];
if (f.isFile()) {
if (f.getName().equals(".nomedia")) {
continue;
}
if (Build.VERSION.SDK_INT >= 21) {
try {
StructStat stat = Os.stat(f.getPath());
if (stat.st_atime != 0) {
if (stat.st_atime + diff < currentTime) {
f.delete();
}
} else if (stat.st_mtime + diff < currentTime) {
f.delete();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
} else if (f.lastModified() + diff < currentTime) {
f.delete();
}
}
}
}
} catch (Throwable e) {
FileLog.e("messenger", e);
}
}
}
});
}
}
@@ -73,7 +73,7 @@ public class ConnectionsManager {
//native_setPushConnectionEnabled(value);
}
public void init(String deviceModel, String systemVersion, String appVersion, String langCode, String configPath, String logPath, int userId, boolean enablePushConnection) {
public void init() {
//native_init(version, layer, apiId, deviceModel, systemVersion, appVersion, langCode, configPath, logPath, userId, enablePushConnection);
//checkConnection();
BroadcastReceiver networkStateReceiver = new BroadcastReceiver() {
@@ -117,6 +117,7 @@ public class ConnectionsManager {
return lastClassGuid++;
}
/* -- leave this for future use
public static boolean isRoaming() {
try {
ConnectivityManager cm = (ConnectivityManager) ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -128,8 +129,9 @@ public class ConnectionsManager {
FileLog.e("messenger", e);
}
return false;
}
} */
/* -- leave this for future use
public static boolean isConnectedToWiFi() {
try {
ConnectivityManager cm = (ConnectivityManager) ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -141,17 +143,16 @@ public class ConnectionsManager {
FileLog.e("messenger", e);
}
return false;
}
} */
public static boolean isNetworkOnline() {
try {
ConnectivityManager cm = (ConnectivityManager) ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
if (netInfo != null && (netInfo.isConnectedOrConnecting() || netInfo.isAvailable())) {
if (netInfo != null && netInfo.isConnected()) {
return true;
}
netInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
/* netInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
if (netInfo != null && netInfo.isConnectedOrConnecting()) {
return true;
@@ -160,10 +161,8 @@ public class ConnectionsManager {
if (netInfo != null && netInfo.isConnectedOrConnecting()) {
return true;
}
}
} */
} catch (Exception e) {
FileLog.e("messenger", e);
return true;
}
return false;
}
@@ -1,51 +1,71 @@
/*
* This is the source code of Telegram for Android v. 1.3.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
*******************************************************************************
*
* File: ContactsController.java
* Authors: Björn Petersen
*
******************************************************************************/
package com.b44t.messenger;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.net.Uri;
import android.os.Build;
import android.provider.ContactsContract;
import android.util.Log;
import android.view.View;
import com.b44t.ui.Components.AvatarDrawable;
import java.io.InputStream;
import java.util.HashMap;
public class ContactsController {
static ContentResolver s_cr;
private static final String TAG = "ContactsController";
public static class Contact {
public String name;
public String email;
}
private String[] projectionNames = {
private final static String[] projectionNames = {
ContactsContract.Data.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Email.ADDRESS
};
private static volatile ContactsController Instance = null;
public static ContactsController getInstance() {
ContactsController localInstance = Instance;
if (localInstance == null) {
synchronized (ContactsController.class) {
localInstance = Instance;
if (localInstance == null) {
Instance = localInstance = new ContactsController();
}
}
}
return localInstance;
}
public ContactsController() {
}
public String readContactsFromPhoneBook() {
public static String readContactsFromPhoneBook() {
HashMap<String, String> contactsMap = new HashMap<String, String>();
String allContacts = "";
try {
@@ -77,7 +97,7 @@ public class ContactsController {
return allContacts;
}
private boolean hasContactsPermission() {
private static boolean hasContactsPermission() {
if (Build.VERSION.SDK_INT >= 23) {
return ApplicationLoader.applicationContext.checkSelfPermission(android.Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED;
}
@@ -89,14 +109,14 @@ public class ContactsController {
return false;
}
} catch (Exception e) {
FileLog.e("messenger", e);
;
} finally {
try {
if (cursor != null) {
cursor.close();
}
} catch (Exception e) {
FileLog.e("messenger", e);
;
}
}
return true;
@@ -134,4 +154,233 @@ public class ContactsController {
}
return result.toString();
}
/* Handle contact images
**********************************************************************************************/
static class AvtCacheEntry {
public Bitmap m_avatarBitmap;
public String m_fallbackName;
public boolean m_needsReload;
AvtCacheEntry(Bitmap avatarBitmap, String fallbackName) {
m_avatarBitmap = avatarBitmap;
m_fallbackName = fallbackName;
m_needsReload = false;
}
}
private final static String[] s_projectionAvatars = new String[]{
ContactsContract.Contacts._ID,
ContactsContract.Contacts.PHOTO_ID,
ContactsContract.CommonDataKinds.Email.ADDRESS
//ContactsContract.CommonDataKinds.Email.DISPLAY_NAME,
};
private static final Object s_sync = new Object();
private static HashMap<String, AvtCacheEntry> s_avtCache = new HashMap<>();
public static void cleanupAvatarCache() {
// to detect changes of the avatar images eg. in the Contacts App,
// this function should be called whenever the App goes to background or back to foreground.
// instead of emptying the cache, we force a reloading (looks smarter - most times, the avatars do not change)
synchronized (s_sync) {
for (AvtCacheEntry cacheEntry : s_avtCache.values()) {
cacheEntry.m_needsReload = true;
}
}
}
public static void setupAvatar(final View avtView,
final ImageReceiver avtImageReceiver,
final AvatarDrawable avtDrawable,
MrContact mrContact,
MrChat mrChat)
{
// get email/name to search avatar image for
String tempEmail = null;
String tempName = "";
if (mrContact != null) {
tempEmail = mrContact.getAddr();
tempName = mrContact.getDisplayName();
} else if (mrChat != null) {
tempName = mrChat.getName();
if (mrChat.getType() == MrChat.MR_CHAT_NORMAL) {
int[] contact_ids = MrMailbox.getChatContacts(mrChat.getId());
if (contact_ids.length == 1) {
MrContact mrc = MrMailbox.getContact(contact_ids[0]);
tempEmail = mrc.getAddr();
tempName = mrc.getDisplayName();
}
}
}
setupAvatarByStrings(avtView, avtImageReceiver, avtDrawable, tempEmail, tempName);
}
public static void setupAvatarByStrings(final View avtView,
final ImageReceiver avtImageReceiver,
final AvatarDrawable avtDrawable,
String tempEmail,
String tempName)
{
if( tempEmail == null ) {
tempEmail = "fallback:" + tempName;
}
final String email = tempEmail;
final String fallbackName = tempName;
// bind e-mail+name address to view object to detect overwrites and discard loading old images (may happen on fast scrolling)
// moreover, check if the avatar is in cache
AvtCacheEntry cacheEntry;
synchronized (s_sync) {
avtImageReceiver.m_userDataUnique = email+fallbackName;
cacheEntry = s_avtCache.get(email+fallbackName);
}
if( cacheEntry != null )
{
// can use avatar from cache, very fine
if (cacheEntry.m_avatarBitmap != null) {
avtImageReceiver.setImageBitmap(cacheEntry.m_avatarBitmap);
} else {
avtDrawable.setInfoByName(cacheEntry.m_fallbackName);
avtImageReceiver.setImage(null, "50_50", avtDrawable, null, false);
}
}
else
{
// avatar is not in cache, empty the image (may be the bitmap of another use as we re-use the objects)
avtImageReceiver.setImage(null, "50_50", null, null, false);
}
if( cacheEntry==null || cacheEntry.m_needsReload )
{
// avatar is not in cache or needs reloading:
// load avatar in a working thread (when loaded, we'll add it to cache and invalidate back in the GUI thread)
Utilities.searchQueue.postRunnable(new Runnable() {
@Override
public void run() {
// is the avatar still desired?
synchronized (s_sync) {
if (!avtImageReceiver.m_userDataUnique.equals(email+fallbackName)) {
return;
}
}
// try to get avatar image from the address book
Bitmap tempBitmap = null;
if (!email.startsWith("fallback:")) {
try {
if (s_cr == null) {
s_cr = ApplicationLoader.applicationContext.getContentResolver();
}
Uri uri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI, Uri.encode(email));
Cursor pCur = s_cr.query(uri, s_projectionAvatars, null, null, null);
if (pCur != null) {
if (pCur.getCount() > 0) {
while (pCur.moveToNext()) {
int contact_id = pCur.getInt(0);
int photo_id = pCur.getInt(1);
String addr = pCur.getString(2);
if (addr.equalsIgnoreCase(email) && contact_id > 0 && photo_id > 0) {
Bitmap tempBitmap2 = loadContactPhoto(s_cr, contact_id, photo_id);
if (tempBitmap2 != null) {
tempBitmap = createRoundBitmap(tempBitmap2);
}
break;
}
}
}
pCur.close();
}
} catch (Exception e) {
;
}
}
final Bitmap photoBitmap = tempBitmap;
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
// is the avatar still desired?
synchronized (s_sync) {
if (!avtImageReceiver.m_userDataUnique.equals(email+fallbackName)) {
return;
}
}
if (photoBitmap != null) {
avtImageReceiver.setImageBitmap(photoBitmap);
} else {
avtDrawable.setInfoByName(fallbackName);
avtImageReceiver.setImage(null, "50_50", avtDrawable, null, false);
}
avtView.invalidate();
synchronized (s_sync) {
s_avtCache.put(email+fallbackName, new AvtCacheEntry(photoBitmap, fallbackName));
}
}
});
}
});
}
}
// from http://stackoverflow.com/questions/2383580/how-do-i-load-a-contact-photo
public static Bitmap loadContactPhoto(ContentResolver cr, long contact_id, long photo_id)
{
// first try using photo_id
byte[] photoBytes = null;
Uri photoUri = ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, photo_id);
Cursor c = cr.query(photoUri, new String[] {ContactsContract.CommonDataKinds.Photo.PHOTO}, null, null, null);
if( c != null ) {
try {
if (c.moveToFirst()) {
photoBytes = c.getBlob(0);
}
} catch (Exception e) {
;
} finally {
c.close();
}
}
if (photoBytes != null) {
return BitmapFactory.decodeByteArray(photoBytes, 0, photoBytes.length);
}
// second try using contact_id
Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contact_id);
InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(cr, uri);
if (input != null) {
return BitmapFactory.decodeStream(input);
}
return null;
}
private static Paint roundPaint;
private static RectF bitmapRect;
private static Bitmap createRoundBitmap(Bitmap bitmap) {
try {
Bitmap result = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
result.eraseColor(Color.TRANSPARENT);
Canvas canvas = new Canvas(result);
BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
if (roundPaint == null) {
roundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bitmapRect = new RectF();
}
roundPaint.setShader(shader);
bitmapRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
canvas.drawRoundRect(bitmapRect, bitmap.getWidth(), bitmap.getHeight(), roundPaint);
return result;
} catch (Throwable e) {
;
}
return null;
}
}
@@ -1,63 +0,0 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package com.b44t.messenger;
import android.accounts.Account;
import android.accounts.OperationCanceledException;
import android.app.Service;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
import android.content.SyncResult;
import android.os.Bundle;
import android.os.IBinder;
public class ContactsSyncAdapterService extends Service {
private static SyncAdapterImpl sSyncAdapter = null;
public ContactsSyncAdapterService() {
super();
}
private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
private Context mContext;
public SyncAdapterImpl(Context context) {
super(context, true);
mContext = context;
}
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
try {
ContactsSyncAdapterService.performSync(mContext, account, extras, authority, provider, syncResult);
} catch (OperationCanceledException e) {
FileLog.e("messenger", e);
}
}
}
@Override
public IBinder onBind(Intent intent) {
return getSyncAdapter().getSyncAdapterBinder();
}
private SyncAdapterImpl getSyncAdapter() {
if (sSyncAdapter == null) {
sSyncAdapter = new SyncAdapterImpl(this);
}
return sSyncAdapter;
}
private static void performSync(Context context, Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult)
throws OperationCanceledException {
FileLog.d("messenger", "performSync: " + account.toString());
}
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -1,15 +0,0 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package com.b44t.messenger;
public class DownloadObject {
public TLObject object;
public int type;
public long id;
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -18,11 +33,6 @@ import java.util.concurrent.Semaphore;
public class FileLoader {
public interface FileLoaderDelegate {
void fileUploadProgressChanged(String location, float progress, boolean isEncrypted);
void fileDidUploaded(String location, TLRPC.InputFile inputFile, TLRPC.InputEncryptedFile inputEncryptedFile, byte[] key, byte[] iv, long totalFileSize);
void fileDidFailedUpload(String location, boolean isEncrypted);
void fileDidLoaded(String location, File finalFile, int type);
@@ -40,23 +50,16 @@ public class FileLoader {
private HashMap<Integer, File> mediaDirs = null;
private volatile DispatchQueue fileLoaderQueue = new DispatchQueue("fileUploadQueue");
private LinkedList<FileUploadOperation> uploadOperationQueue = new LinkedList<>();
private LinkedList<FileUploadOperation> uploadSmallOperationQueue = new LinkedList<>();
private LinkedList<FileLoadOperation> loadOperationQueue = new LinkedList<>();
private LinkedList<FileLoadOperation> audioLoadOperationQueue = new LinkedList<>();
private LinkedList<FileLoadOperation> photoLoadOperationQueue = new LinkedList<>();
private ConcurrentHashMap<String, FileUploadOperation> uploadOperationPaths = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, FileUploadOperation> uploadOperationPathsEnc = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, FileLoadOperation> loadOperationPaths = new ConcurrentHashMap<>();
private HashMap<String, Long> uploadSizes = new HashMap<>();
private FileLoaderDelegate delegate = null;
private int currentLoadOperationsCount = 0;
private int currentAudioLoadOperationsCount = 0;
private int currentPhotoLoadOperationsCount = 0;
private int currentUploadOperationsCount = 0;
private int currentUploadSmallOperationsCount = 0;
private static volatile FileLoader Instance = null;
@@ -77,11 +80,7 @@ public class FileLoader {
mediaDirs = dirs;
}
public File checkDirectory(int type) {
return mediaDirs.get(type);
}
public File getDirectory(int type) {
public File getDirectory(int type) { // always returns the cache directory as this is the only one set in mediaDirs[] - we do not use the other directories at the moment
File dir = mediaDirs.get(type);
if (dir == null && type != MEDIA_DIR_CACHE) {
dir = mediaDirs.get(MEDIA_DIR_CACHE);
@@ -96,160 +95,6 @@ public class FileLoader {
return dir;
}
/*
public void checkUploadNewDataAvailable(final String location, final boolean encrypted, final long finalSize) {
fileLoaderQueue.postRunnable(new Runnable() {
@Override
public void run() {
FileUploadOperation operation;
if (encrypted) {
operation = uploadOperationPathsEnc.get(location);
} else {
operation = uploadOperationPaths.get(location);
}
if (operation != null) {
operation.checkNewDataAvailable(finalSize);
} else if (finalSize != 0) {
uploadSizes.put(location, finalSize);
}
}
});
}
*/
public void uploadFile(final String location, final boolean encrypted, final boolean small) {
uploadFile(location, encrypted, small, 0);
}
public void uploadFile(final String location, final boolean encrypted, final boolean small, final int estimatedSize) {
if (location == null) {
return;
}
fileLoaderQueue.postRunnable(new Runnable() {
@Override
public void run() {
if (encrypted) {
if (uploadOperationPathsEnc.containsKey(location)) {
return;
}
} else {
if (uploadOperationPaths.containsKey(location)) {
return;
}
}
int esimated = estimatedSize;
if (esimated != 0) {
Long finalSize = uploadSizes.get(location);
if (finalSize != null) {
esimated = 0;
uploadSizes.remove(location);
}
}
FileUploadOperation operation = new FileUploadOperation(location, encrypted, esimated);
if (encrypted) {
uploadOperationPathsEnc.put(location, operation);
} else {
uploadOperationPaths.put(location, operation);
}
operation.delegate = new FileUploadOperation.FileUploadOperationDelegate() {
@Override
public void didFinishUploadingFile(final FileUploadOperation operation, final TLRPC.InputFile inputFile, final TLRPC.InputEncryptedFile inputEncryptedFile, final byte[] key, final byte[] iv) {
fileLoaderQueue.postRunnable(new Runnable() {
@Override
public void run() {
if (encrypted) {
uploadOperationPathsEnc.remove(location);
} else {
uploadOperationPaths.remove(location);
}
if (small) {
currentUploadSmallOperationsCount--;
if (currentUploadSmallOperationsCount < 1) {
FileUploadOperation operation = uploadSmallOperationQueue.poll();
if (operation != null) {
currentUploadSmallOperationsCount++;
operation.start();
}
}
} else {
currentUploadOperationsCount--;
if (currentUploadOperationsCount < 1) {
FileUploadOperation operation = uploadOperationQueue.poll();
if (operation != null) {
currentUploadOperationsCount++;
operation.start();
}
}
}
if (delegate != null) {
delegate.fileDidUploaded(location, inputFile, inputEncryptedFile, key, iv, operation.getTotalFileSize());
}
}
});
}
@Override
public void didFailedUploadingFile(final FileUploadOperation operation) {
fileLoaderQueue.postRunnable(new Runnable() {
@Override
public void run() {
if (encrypted) {
uploadOperationPathsEnc.remove(location);
} else {
uploadOperationPaths.remove(location);
}
if (delegate != null) {
delegate.fileDidFailedUpload(location, encrypted);
}
if (small) {
currentUploadSmallOperationsCount--;
if (currentUploadSmallOperationsCount < 1) {
FileUploadOperation operation = uploadSmallOperationQueue.poll();
if (operation != null) {
currentUploadSmallOperationsCount++;
operation.start();
}
}
} else {
currentUploadOperationsCount--;
if (currentUploadOperationsCount < 1) {
FileUploadOperation operation = uploadOperationQueue.poll();
if (operation != null) {
currentUploadOperationsCount++;
operation.start();
}
}
}
}
});
}
@Override
public void didChangedUploadProgress(FileUploadOperation operation, final float progress) {
if (delegate != null) {
delegate.fileUploadProgressChanged(location, progress, encrypted);
}
}
};
if (small) {
if (currentUploadSmallOperationsCount < 1) {
currentUploadSmallOperationsCount++;
operation.start();
} else {
uploadSmallOperationQueue.add(operation);
}
} else {
if (currentUploadOperationsCount < 1) {
currentUploadOperationsCount++;
operation.start();
} else {
uploadOperationQueue.add(operation);
}
}
}
});
}
public void cancelLoadFile(TLRPC.Document document) {
cancelLoadFile(document, null, null);
}
@@ -507,7 +352,7 @@ public class FileLoader {
if (message == null) {
return "";
}
if (message instanceof TLRPC.TL_messageService) {
/*if (message instanceof TLRPC.TL_messageService) {
if (message.action.photo != null) {
ArrayList<TLRPC.PhotoSize> sizes = message.action.photo.sizes;
if (sizes.size() > 0) {
@@ -517,7 +362,7 @@ public class FileLoader {
}
}
}
} else {
} else*/ {
if (message.media instanceof TLRPC.TL_messageMediaDocument) {
return getAttachFileName(message.media.document);
} else if (message.media instanceof TLRPC.TL_messageMediaPhoto) {
@@ -528,7 +373,7 @@ public class FileLoader {
return getAttachFileName(sizeFull);
}
}
} else if (message.media instanceof TLRPC.TL_messageMediaWebPage) {
} /*else if (message.media instanceof TLRPC.TL_messageMediaWebPage) {
if (message.media.webpage.photo != null) {
ArrayList<TLRPC.PhotoSize> sizes = message.media.webpage.photo.sizes;
if (sizes.size() > 0) {
@@ -540,7 +385,7 @@ public class FileLoader {
} else if (message.media.webpage.document != null) {
return getAttachFileName(message.media.webpage.document);
}
}
}*/
}
return "";
}
@@ -549,7 +394,7 @@ public class FileLoader {
if (message == null) {
return new File("");
}
if (message instanceof TLRPC.TL_messageService) {
/*if (message instanceof TLRPC.TL_messageService) {
if (message.action.photo != null) {
ArrayList<TLRPC.PhotoSize> sizes = message.action.photo.sizes;
if (sizes.size() > 0) {
@@ -559,7 +404,7 @@ public class FileLoader {
}
}
}
} else {
} else*/ {
if (message.media instanceof TLRPC.TL_messageMediaDocument) {
return getPathToAttach(message.media.document);
} else if (message.media instanceof TLRPC.TL_messageMediaPhoto) {
@@ -570,7 +415,7 @@ public class FileLoader {
return getPathToAttach(sizeFull);
}
}
} else if (message.media instanceof TLRPC.TL_messageMediaWebPage) {
} /*else if (message.media instanceof TLRPC.TL_messageMediaWebPage) {
if (message.media.webpage.document != null) {
return getPathToAttach(message.media.webpage.document);
} else if (message.media.webpage.photo != null) {
@@ -582,7 +427,7 @@ public class FileLoader {
}
}
}
}
}*/
}
return new File("");
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -1,79 +0,0 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package com.b44t.messenger;
import java.io.FileInputStream;
public class FileUploadOperation {
public int state = 0;
public FileUploadOperationDelegate delegate;
private int requestToken = 0;
private long totalFileSize = 0;
private boolean isEncrypted = false;
private int estimatedSize = 0;
private FileInputStream stream;
public interface FileUploadOperationDelegate {
void didFinishUploadingFile(FileUploadOperation operation, TLRPC.InputFile inputFile, TLRPC.InputEncryptedFile inputEncryptedFile, byte[] key, byte[] iv);
void didFailedUploadingFile(FileUploadOperation operation);
void didChangedUploadProgress(FileUploadOperation operation, float progress);
}
public FileUploadOperation(String location, boolean encrypted, int estimated) {
isEncrypted = encrypted;
estimatedSize = estimated;
}
public long getTotalFileSize() {
return totalFileSize;
}
public void start() {
}
public void cancel() {
if (state == 3) {
return;
}
state = 2;
if (requestToken != 0) {
ConnectionsManager.getInstance().cancelRequest(requestToken, true);
}
delegate.didFailedUploadingFile(this);
cleanup();
}
private void cleanup() {
}
/*
protected void checkNewDataAvailable(final long finalSize) {
Utilities.stageQueue.postRunnable(new Runnable() {
@Override
public void run() {
if (estimatedSize != 0 && finalSize != 0) {
estimatedSize = 0;
totalFileSize = finalSize;
totalPartsCount = (int) (totalFileSize + uploadChunkSize - 1) / uploadChunkSize;
if (started) {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("uploadinfo", Activity.MODE_PRIVATE);
storeFileUploadInfo(preferences);
}
}
if (requestToken == 0) {
startUploadRequest();
}
}
});
}
*/
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -22,7 +37,6 @@ import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import com.b44t.ui.Components.AnimatedFileDrawable;
@@ -70,147 +84,18 @@ public class ImageLoader {
private static byte[] headerThumb = new byte[12];
private int currentHttpTasksCount = 0;
private LinkedList<HttpFileTask> httpFileLoadTasks = new LinkedList<>();
private HashMap<String, HttpFileTask> httpFileLoadTasksByKeys = new HashMap<>();
private HashMap<String, Runnable> retryHttpsTasks = new HashMap<>();
private int currentHttpFileLoadTasksCount = 0;
private String ignoreRemoval = null;
private volatile long lastCacheOutTime = 0;
private int lastImageNum = 0;
private long lastProgressUpdateTime = 0;
private File messengerPath = null;
private class ThumbGenerateInfo {
private int count;
private TLRPC.FileLocation fileLocation;
private String filter;
}
private class HttpFileTask extends AsyncTask<Void, Void, Boolean> {
private String url;
private File tempFile;
private String ext;
private RandomAccessFile fileOutputStream = null;
private boolean canRetry = true;
public HttpFileTask(String url, File tempFile, String ext) {
this.url = url;
this.tempFile = tempFile;
this.ext = ext;
}
protected Boolean doInBackground(Void... voids) {
InputStream httpConnectionStream = null;
boolean done = false;
URLConnection httpConnection = null;
try {
URL downloadUrl = new URL(url);
httpConnection = downloadUrl.openConnection();
httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/_BuildID_) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36");
httpConnection.addRequestProperty("Referer", "google.com");
httpConnection.setConnectTimeout(5000);
httpConnection.setReadTimeout(5000);
if (httpConnection instanceof HttpURLConnection) {
HttpURLConnection httpURLConnection = (HttpURLConnection) httpConnection;
httpURLConnection.setInstanceFollowRedirects(true);
int status = httpURLConnection.getResponseCode();
if (status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_MOVED_PERM || status == HttpURLConnection.HTTP_SEE_OTHER) {
String newUrl = httpURLConnection.getHeaderField("Location");
String cookies = httpURLConnection.getHeaderField("Set-Cookie");
downloadUrl = new URL(newUrl);
httpConnection = downloadUrl.openConnection();
httpConnection.setRequestProperty("Cookie", cookies);
httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/_BuildID_) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36");
httpConnection.addRequestProperty("Referer", "google.com");
}
}
httpConnection.connect();
httpConnectionStream = httpConnection.getInputStream();
fileOutputStream = new RandomAccessFile(tempFile, "rws");
} catch (Throwable e) {
if (e instanceof UnknownHostException) {
canRetry = false;
}
FileLog.e("messenger", e);
}
if (canRetry) {
try {
if (httpConnection != null && httpConnection instanceof HttpURLConnection) {
int code = ((HttpURLConnection) httpConnection).getResponseCode();
if (code != HttpURLConnection.HTTP_OK && code != HttpURLConnection.HTTP_ACCEPTED && code != HttpURLConnection.HTTP_NOT_MODIFIED) {
canRetry = false;
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
if (httpConnectionStream != null) {
try {
byte[] data = new byte[1024 * 4];
while (true) {
if (isCancelled()) {
break;
}
try {
int read = httpConnectionStream.read(data);
if (read > 0) {
fileOutputStream.write(data, 0, read);
} else if (read == -1) {
done = true;
break;
} else {
break;
}
} catch (Exception e) {
FileLog.e("messenger", e);
break;
}
}
} catch (Throwable e) {
FileLog.e("messenger", e);
}
}
try {
if (fileOutputStream != null) {
fileOutputStream.close();
fileOutputStream = null;
}
} catch (Throwable e) {
FileLog.e("messenger", e);
}
try {
if (httpConnectionStream != null) {
httpConnectionStream.close();
}
} catch (Throwable e) {
FileLog.e("messenger", e);
}
}
return done;
}
@Override
protected void onPostExecute(Boolean result) {
runHttpFileLoadTasks(this, result ? 2 : 1);
}
@Override
protected void onCancelled() {
runHttpFileLoadTasks(this, 2);
}
}
private class HttpImageTask extends AsyncTask<Void, Void, Boolean> {
private CacheImage cacheImage = null;
@@ -515,16 +400,6 @@ public class ImageLoader {
kf += "@" + filter;
}
NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageThumbGenerated, bitmapDrawable, kf);
/*BitmapDrawable old = memCache.get(kf);
if (old != null) {
Bitmap image = old.getBitmap();
if (runtimeHack != null) {
runtimeHack.trackAlloc(image.getRowBytes() * image.getHeight());
}
if (!image.isRecycled()) {
image.recycle();
}
}*/
memCache.put(kf, bitmapDrawable);
}
});
@@ -1105,6 +980,7 @@ public class ImageLoader {
};
FileLoader.getInstance().setDelegate(new FileLoader.FileLoaderDelegate() {
/*
@Override
public void fileUploadProgressChanged(final String location, final float progress, final boolean isEncrypted) {
fileProgresses.put(location, progress);
@@ -1152,6 +1028,7 @@ public class ImageLoader {
}
});
}
*/
@Override
public void fileDidLoaded(final String location, final File finalFile, final int type) {
@@ -1159,11 +1036,11 @@ public class ImageLoader {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (MediaController.getInstance().canSaveToGallery() && messengerPath != null && finalFile != null && (location.endsWith(".mp4") || location.endsWith(".jpg"))) {
/*if (MediaController.getInstance().canSaveToGallery() && messengerPath != null && finalFile != null && (location.endsWith(".mp4") || location.endsWith(".jpg"))) {
if (finalFile.toString().startsWith(messengerPath.toString())) {
AndroidUtilities.addMediaToGallery(finalFile.toString());
}
}
}*/
NotificationCenter.getInstance().postNotificationName(NotificationCenter.FileDidLoaded, location);
ImageLoader.this.fileDidLoaded(location, finalFile, type);
}
@@ -1228,165 +1105,19 @@ public class ImageLoader {
filter.addDataScheme("file");
ApplicationLoader.applicationContext.registerReceiver(receiver, filter);
HashMap<Integer, File> mediaDirs = new HashMap<>();
File cachePath = AndroidUtilities.getCacheDir();
if (!cachePath.isDirectory()) {
try {
cachePath.mkdirs();
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
try {
new File(cachePath, ".nomedia").createNewFile();
} catch (Exception e) {
FileLog.e("messenger", e);
}
mediaDirs.put(FileLoader.MEDIA_DIR_CACHE, cachePath);
FileLoader.getInstance().setMediaDirs(mediaDirs);
checkMediaPaths();
}
public void checkMediaPaths() {
cacheOutQueue.postRunnable(new Runnable() {
@Override
public void run() {
final HashMap<Integer, File> paths = createMediaPaths();
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
FileLoader.getInstance().setMediaDirs(paths);
}
});
}
});
}
public HashMap<Integer, File> createMediaPaths() {
HashMap<Integer, File> mediaDirs = new HashMap<>();
File cachePath = AndroidUtilities.getCacheDir();
if (!cachePath.isDirectory()) {
try {
cachePath.mkdirs();
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
File cachePath = new File(MrMailbox.getBlobdir());
try {
new File(cachePath, ".nomedia").createNewFile();
} catch (Exception e) {
FileLog.e("messenger", e);
}
mediaDirs.put(FileLoader.MEDIA_DIR_CACHE, cachePath);
FileLog.e("messenger", "cache path = " + cachePath);
try {
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
messengerPath = new File(Environment.getExternalStorageDirectory(), "Delta Chat");
messengerPath.mkdirs();
if (messengerPath.isDirectory()) {
try {
File imagePath = new File(messengerPath, "Delta Chat Images");
imagePath.mkdir();
if (imagePath.isDirectory() && canMoveFiles(cachePath, imagePath, FileLoader.MEDIA_DIR_IMAGE)) {
mediaDirs.put(FileLoader.MEDIA_DIR_IMAGE, imagePath);
FileLog.e("messenger", "image path = " + imagePath);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
try {
File videoPath = new File(messengerPath, "Delta Chat Video");
videoPath.mkdir();
if (videoPath.isDirectory() && canMoveFiles(cachePath, videoPath, FileLoader.MEDIA_DIR_VIDEO)) {
mediaDirs.put(FileLoader.MEDIA_DIR_VIDEO, videoPath);
FileLog.e("messenger", "video path = " + videoPath);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
try {
File audioPath = new File(messengerPath, "Delta Chat Audio");
audioPath.mkdir();
if (audioPath.isDirectory() && canMoveFiles(cachePath, audioPath, FileLoader.MEDIA_DIR_AUDIO)) {
new File(audioPath, ".nomedia").createNewFile();
mediaDirs.put(FileLoader.MEDIA_DIR_AUDIO, audioPath);
FileLog.e("messenger", "audio path = " + audioPath);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
try {
File documentPath = new File(messengerPath, "Delta Chat Documents");
documentPath.mkdir();
if (documentPath.isDirectory() && canMoveFiles(cachePath, documentPath, FileLoader.MEDIA_DIR_DOCUMENT)) {
new File(documentPath, ".nomedia").createNewFile();
mediaDirs.put(FileLoader.MEDIA_DIR_DOCUMENT, documentPath);
FileLog.e("messenger", "documents path = " + documentPath);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
} else {
FileLog.e("messenger", "this Android can't rename files");
}
MediaController.getInstance().checkSaveToGalleryFiles();
} catch (Exception e) {
FileLog.e("messenger", e);
}
return mediaDirs;
}
private boolean canMoveFiles(File from, File to, int type) {
RandomAccessFile file = null;
try {
File srcFile = null;
File dstFile = null;
if (type == FileLoader.MEDIA_DIR_IMAGE) {
srcFile = new File(from, "000000000_999999_temp.jpg");
dstFile = new File(to, "000000000_999999.jpg");
} else if (type == FileLoader.MEDIA_DIR_DOCUMENT) {
srcFile = new File(from, "000000000_999999_temp.doc");
dstFile = new File(to, "000000000_999999.doc");
} else if (type == FileLoader.MEDIA_DIR_AUDIO) {
srcFile = new File(from, "000000000_999999_temp.ogg");
dstFile = new File(to, "000000000_999999.ogg");
} else if (type == FileLoader.MEDIA_DIR_VIDEO) {
srcFile = new File(from, "000000000_999999_temp.mp4");
dstFile = new File(to, "000000000_999999.mp4");
}
byte[] buffer = new byte[1024];
srcFile.createNewFile();
file = new RandomAccessFile(srcFile, "rws");
file.write(buffer);
file.close();
file = null;
boolean canRename = srcFile.renameTo(dstFile);
srcFile.delete();
dstFile.delete();
if (canRename) {
return true;
}
} catch (Exception e) {
FileLog.e("messenger", e);
} finally {
try {
if (file != null) {
file.close();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
return false;
FileLoader.getInstance().setMediaDirs(mediaDirs);
}
public Float getFileProgress(String location) {
@@ -1396,21 +1127,6 @@ public class ImageLoader {
return fileProgresses.get(location);
}
private void performReplace(String oldKey, String newKey) {
BitmapDrawable b = memCache.get(oldKey);
if (b != null) {
ignoreRemoval = oldKey;
memCache.remove(oldKey);
memCache.put(newKey, b);
ignoreRemoval = null;
}
Integer val = bitmapUseCounts.get(oldKey);
if (val != null) {
bitmapUseCounts.put(newKey, val);
bitmapUseCounts.remove(oldKey);
}
}
public void incrementUseCount(String key) {
Integer count = bitmapUseCounts.get(key);
if (count == null) {
@@ -1491,65 +1207,6 @@ public class ImageLoader {
});
}
public BitmapDrawable getImageFromMemory(String key) {
return memCache.get(key);
}
public BitmapDrawable getImageFromMemory(TLObject fileLocation, String httpUrl, String filter) {
if (fileLocation == null && httpUrl == null) {
return null;
}
String key = null;
if (httpUrl != null) {
key = Utilities.MD5(httpUrl);
} else {
if (fileLocation instanceof TLRPC.FileLocation) {
TLRPC.FileLocation location = (TLRPC.FileLocation) fileLocation;
key = location.volume_id + "_" + location.local_id;
} else if (fileLocation instanceof TLRPC.Document) {
TLRPC.Document location = (TLRPC.Document) fileLocation;
key = location.dc_id + "_" + location.id;
}
}
if (filter != null) {
key += "@" + filter;
}
return memCache.get(key);
}
private void replaceImageInCacheInternal(final String oldKey, final String newKey, final TLRPC.FileLocation newLocation) {
ArrayList<String> arr = memCache.getFilterKeys(oldKey);
if (arr != null) {
for (int a = 0; a < arr.size(); a++) {
String filter = arr.get(a);
String oldK = oldKey + "@" + filter;
String newK = newKey + "@" + filter;
performReplace(oldK, newK);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.didReplacedPhotoInMemCache, oldK, newK, newLocation);
}
} else {
performReplace(oldKey, newKey);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.didReplacedPhotoInMemCache, oldKey, newKey, newLocation);
}
}
public void replaceImageInCache(final String oldKey, final String newKey, final TLRPC.FileLocation newLocation, boolean post) {
if (post) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
replaceImageInCacheInternal(oldKey, newKey, newLocation);
}
});
} else {
replaceImageInCacheInternal(oldKey, newKey, newLocation);
}
}
public void putImageToCache(BitmapDrawable bitmap, String key) {
memCache.put(key, bitmap);
}
private void generateThumb(int mediaType, File originalPath, TLRPC.FileLocation thumbLocation, String filter) {
if (mediaType != FileLoader.MEDIA_DIR_IMAGE && mediaType != FileLoader.MEDIA_DIR_VIDEO && mediaType != FileLoader.MEDIA_DIR_DOCUMENT || originalPath == null || thumbLocation == null) {
return;
@@ -1607,7 +1264,6 @@ public class ImageLoader {
if (!added) {
boolean onlyCache = false;
boolean isQuality = false;
File cacheFile = null;
if (httpLocation != null) {
@@ -1920,80 +1576,12 @@ public class ImageLoader {
}
}
public void loadHttpFile(String url, String defaultExt) {
if (url == null || url.length() == 0 || httpFileLoadTasksByKeys.containsKey(url)) {
return;
}
String ext = getHttpUrlExtension(url, defaultExt);
File file = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), Utilities.MD5(url) + "_temp." + ext);
file.delete();
HttpFileTask task = new HttpFileTask(url, file, ext);
httpFileLoadTasks.add(task);
httpFileLoadTasksByKeys.put(url, task);
runHttpFileLoadTasks(null, 0);
}
public void cancelLoadHttpFile(String url) {
HttpFileTask task = httpFileLoadTasksByKeys.get(url);
if (task != null) {
task.cancel(true);
httpFileLoadTasksByKeys.remove(url);
httpFileLoadTasks.remove(task);
}
Runnable runnable = retryHttpsTasks.get(url);
if (runnable != null) {
AndroidUtilities.cancelRunOnUIThread(runnable);
}
runHttpFileLoadTasks(null, 0);
}
private void runHttpFileLoadTasks(final HttpFileTask oldTask, final int reason) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (oldTask != null) {
currentHttpFileLoadTasksCount--;
}
if (oldTask != null) {
if (reason == 1) {
if (oldTask.canRetry) {
final HttpFileTask newTask = new HttpFileTask(oldTask.url, oldTask.tempFile, oldTask.ext);
Runnable runnable = new Runnable() {
@Override
public void run() {
httpFileLoadTasks.add(newTask);
runHttpFileLoadTasks(null, 0);
}
};
retryHttpsTasks.put(oldTask.url, runnable);
AndroidUtilities.runOnUIThread(runnable, 1000);
} else {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.httpFileDidFailedLoad, oldTask.url);
}
} else if (reason == 2) {
httpFileLoadTasksByKeys.remove(oldTask.url);
File file = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), Utilities.MD5(oldTask.url) + "." + oldTask.ext);
String result = oldTask.tempFile.renameTo(file) ? file.toString() : oldTask.tempFile.toString();
NotificationCenter.getInstance().postNotificationName(NotificationCenter.httpFileDidLoaded, oldTask.url, result);
}
}
while (currentHttpFileLoadTasksCount < 2 && !httpFileLoadTasks.isEmpty()) {
HttpFileTask task = httpFileLoadTasks.poll();
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null, null, null);
currentHttpFileLoadTasksCount++;
}
}
});
}
public static Bitmap loadBitmap(String path, Uri uri, float maxWidth, float maxHeight, boolean useMaxScale) {
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
InputStream inputStream = null;
if (path == null && uri != null && uri.getScheme() != null) {
String imageFilePath = null;
if (uri.getScheme().contains("file")) {
path = uri.getPath();
} else {
@@ -2008,7 +1596,6 @@ public class ImageLoader {
if (path != null) {
BitmapFactory.decodeFile(path, bmOptions);
} else if (uri != null) {
boolean error = false;
try {
inputStream = ApplicationLoader.applicationContext.getContentResolver().openInputStream(uri);
BitmapFactory.decodeStream(inputStream, null, bmOptions);
@@ -2122,7 +1709,7 @@ public class ImageLoader {
return b;
}
private static TLRPC.PhotoSize scaleAndSaveImageInternal(Bitmap bitmap, int w, int h, float photoW, float photoH, float scaleFactor, int quality, boolean cache, boolean scaleAnyway) throws Exception {
private static TLRPC.PhotoSize scaleAndSaveImageInternal(File cacheFile, Bitmap bitmap, int w, int h, float photoW, float photoH, float scaleFactor, int quality, boolean cache, boolean scaleAnyway) throws Exception {
Bitmap scaledBitmap;
if (scaleFactor > 1 || scaleAnyway) {
scaledBitmap = Bitmaps.createScaledBitmap(bitmap, w, h, true);
@@ -2151,8 +1738,11 @@ public class ImageLoader {
size.type = "w";
}
String fileName = location.volume_id + "_" + location.local_id + ".jpg";
final File cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName);
if( cacheFile == null ) {
String fileName = location.volume_id + "_" + location.local_id + ".jpg";
cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName);
}
FileOutputStream stream = new FileOutputStream(cacheFile);
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, quality, stream);
if (cache) {
@@ -2173,10 +1763,18 @@ public class ImageLoader {
}
public static TLRPC.PhotoSize scaleAndSaveImage(Bitmap bitmap, float maxWidth, float maxHeight, int quality, boolean cache) {
return scaleAndSaveImage(bitmap, maxWidth, maxHeight, quality, cache, 0, 0);
return scaleAndSaveImage(null, bitmap, maxWidth, maxHeight, quality, cache, 0, 0);
}
public static TLRPC.PhotoSize scaleAndSaveImage(Bitmap bitmap, float maxWidth, float maxHeight, int quality, boolean cache, int minWidth, int minHeight) {
return scaleAndSaveImage(null, bitmap, maxWidth, maxHeight, quality, cache, minWidth, minHeight);
}
public static TLRPC.PhotoSize scaleAndSaveImage(File cacheFile, Bitmap bitmap, float maxWidth, float maxHeight, int quality, boolean cache) {
return scaleAndSaveImage(cacheFile, bitmap, maxWidth, maxHeight, quality, cache, 0, 0);
}
private static TLRPC.PhotoSize scaleAndSaveImage(File cacheFile, Bitmap bitmap, float maxWidth, float maxHeight, int quality, boolean cache, int minWidth, int minHeight) {
if (bitmap == null) {
return null;
}
@@ -2204,13 +1802,13 @@ public class ImageLoader {
}
try {
return scaleAndSaveImageInternal(bitmap, w, h, photoW, photoH, scaleFactor, quality, cache, scaleAnyway);
return scaleAndSaveImageInternal(cacheFile, bitmap, w, h, photoW, photoH, scaleFactor, quality, cache, scaleAnyway);
} catch (Throwable e) {
FileLog.e("messenger", e);
ImageLoader.getInstance().clearMemory();
System.gc();
try {
return scaleAndSaveImageInternal(bitmap, w, h, photoW, photoH, scaleFactor, quality, cache, scaleAnyway);
return scaleAndSaveImageInternal(cacheFile, bitmap, w, h, photoW, photoH, scaleFactor, quality, cache, scaleAnyway);
} catch (Throwable e2) {
FileLog.e("messenger", e2);
return null;
@@ -2230,6 +1828,7 @@ public class ImageLoader {
return ext;
}
/*
public static void saveMessageThumbs(TLRPC.Message message) {
TLRPC.PhotoSize photoSize = null;
if (message.media instanceof TLRPC.TL_messageMediaPhoto) {
@@ -2243,16 +1842,17 @@ public class ImageLoader {
if (message.media.document.thumb instanceof TLRPC.TL_photoCachedSize) {
photoSize = message.media.document.thumb;
}
} else if (message.media instanceof TLRPC.TL_messageMediaWebPage) {
if (message.media.webpage.photo != null) {
for (TLRPC.PhotoSize size : message.media.webpage.photo.sizes) {
if (size instanceof TLRPC.TL_photoCachedSize) {
photoSize = size;
break;
}
}
}
}
//else if (message.media instanceof TLRPC.TL_messageMediaWebPage) {
// if (message.media.webpage.photo != null) {
// for (TLRPC.PhotoSize size : message.media.webpage.photo.sizes) {
// if (size instanceof TLRPC.TL_photoCachedSize) {
// photoSize = size;
// break;
// }
// }
// }
//}
if (photoSize != null && photoSize.bytes != null && photoSize.bytes.length != 0) {
if (photoSize.location instanceof TLRPC.TL_fileLocationUnavailable) {
photoSize.location = new TLRPC.TL_fileLocation();
@@ -2287,17 +1887,20 @@ public class ImageLoader {
}
} else if (message.media instanceof TLRPC.TL_messageMediaDocument) {
message.media.document.thumb = newPhotoSize;
} else if (message.media instanceof TLRPC.TL_messageMediaWebPage) {
for (int a = 0; a < message.media.webpage.photo.sizes.size(); a++) {
if (message.media.webpage.photo.sizes.get(a) instanceof TLRPC.TL_photoCachedSize) {
message.media.webpage.photo.sizes.set(a, newPhotoSize);
break;
}
}
}
//else if (message.media instanceof TLRPC.TL_messageMediaWebPage) {
// for (int a = 0; a < message.media.webpage.photo.sizes.size(); a++) {
// if (message.media.webpage.photo.sizes.get(a) instanceof TLRPC.TL_photoCachedSize) {
// message.media.webpage.photo.sizes.set(a, newPhotoSize);
// break;
// }
// }
//}
}
}
*/
/*
public static void saveMessagesThumbs(ArrayList<TLRPC.Message> messages) {
if (messages == null || messages.isEmpty()) {
return;
@@ -2307,4 +1910,5 @@ public class ImageLoader {
saveMessageThumbs(message);
}
}
*/
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 1.3.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -14,6 +29,7 @@ import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
@@ -27,6 +43,7 @@ import com.b44t.ui.Components.AnimatedFileDrawable;
public class ImageReceiver implements NotificationCenter.NotificationCenterDelegate {
public interface ImageReceiverDelegate {
void didSetImage(ImageReceiver imageReceiver, boolean set, boolean thumb);
}
@@ -43,6 +60,11 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
public String ext;
}
public String m_userDataUnique = ""; // used to discard images loaded and no longer required as there was a new request
// before, we used "private static HashMap<View, String> s_viewBindings = new HashMap<>();" in ContactsController -
// however, this was a bad idea as this resulted in _lots_ of objects that were never freed...
private View parentView;
private Integer tag;
private Integer thumbTag;
@@ -349,12 +371,10 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
setImageBackup.ext = currentExt;
setImageBackup.cacheOnly = currentCacheOnly;
}
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReplacedPhotoInMemCache);
clearImage();
}
public boolean onAttachedToWindow() {
NotificationCenter.getInstance().addObserver(this, NotificationCenter.didReplacedPhotoInMemCache);
if (setImageBackup != null && (setImageBackup.fileLocation != null || setImageBackup.httpUrl != null || setImageBackup.thumbLocation != null || setImageBackup.thumb != null)) {
setImage(setImageBackup.fileLocation, setImageBackup.httpUrl, setImageBackup.filter, setImageBackup.thumb, setImageBackup.thumbLocation, setImageBackup.thumbFilter, setImageBackup.size, setImageBackup.ext, setImageBackup.cacheOnly);
return true;
@@ -1036,26 +1056,6 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
}
}
}
} else if (id == NotificationCenter.didReplacedPhotoInMemCache) {
String oldKey = (String) args[0];
if (currentKey != null && currentKey.equals(oldKey)) {
currentKey = (String) args[1];
currentImageLocation = (TLRPC.FileLocation) args[2];
}
if (currentThumbKey != null && currentThumbKey.equals(oldKey)) {
currentThumbKey = (String) args[1];
currentThumbLocation = (TLRPC.FileLocation) args[2];
}
if (setImageBackup != null) {
if (currentKey != null && currentKey.equals(oldKey)) {
currentKey = (String) args[1];
currentImageLocation = (TLRPC.FileLocation) args[2];
}
if (currentThumbKey != null && currentThumbKey.equals(oldKey)) {
currentThumbKey = (String) args[1];
currentThumbLocation = (TLRPC.FileLocation) args[2];
}
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,8 +1,8 @@
/*******************************************************************************
*
* Messenger Android Frontend
* Copyright (C) 2016 Björn Petersen Software Design and Development
* Contact: r10s@b44t.com, http://b44t.com
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
@@ -20,13 +20,14 @@
*******************************************************************************
*
* File: MrChat.java
* Authors: Björn Petersen
* Purpose: Wrap around mrchat_t
*
******************************************************************************/
package com.b44t.messenger;
import android.text.TextUtils;
public class MrChat {
public final static int MR_CHAT_UNDEFINED = 0;
@@ -44,20 +45,13 @@ public class MrChat {
MrChatUnref(m_hChat);
}
public int getId() {
return MrChatGetId(m_hChat);
}
public int getType() {
return MrChatGetType(m_hChat);
}
public native int getId();
public native int getType();
public native String getName();
public native String getSubtitle();
public String getName() {
return MrChatGetName(m_hChat);
}
public String getSubtitle() {
return MrChatGetSubtitle(m_hChat);
}
public static int MR_CHAT_PARAM_UNPROMOTED = 'U';
public native int getParamInt(int key, int def);
public String getDraft() {
return MrChatGetDraft(m_hChat);
@@ -82,29 +76,18 @@ public class MrChat {
return MrChatGetTotalMsgCount(m_hChat);
}
public int sendText(String text) {
return MrChatSendText(m_hChat, text);
}
public native int sendText(String text);
public int sendMedia(int type, String file, String mime, int w, int h, int time_ms) {
return MrChatSendMedia(m_hChat, type, file, mime, w, h, time_ms);
}
public native int sendMedia(int type, String file, String mime, int w, int h, int time_ms, String author, String trackname);
private long m_hChat;
private long m_hChat; // must not be renamed as referenced by JNI under the name "m_hChat"
private native static void MrChatUnref (long hChat);
private native static int MrChatGetId (long hChat);
private native static int MrChatGetType (long hChat);
private native static String MrChatGetName (long hChat);
private native static String MrChatGetSubtitle (long hChat);
private native static String MrChatGetDraft (long hChat); // returns null for "no draft"
private native static long MrChatGetDraftTimestamp (long hChat); // returns 0 for "no draft"
private native static int MrChatGetDraftReplyToMsgId (long hChat); // returns 0 for "no draft"
private native static int MrChatSetDraft (long hChat, String draft/*NULL=delete*/, long replyToMsgId);
private native static int MrChatGetUnseenCount (long hChat);
private native static int MrChatGetTotalMsgCount (long hChat);
private native static long MrChatGetSummary (long hChat); // returns hPoortext
private native static int MrChatSendText (long hChat, String text); // returns message id
private native static int MrChatSendMedia (long hChat, int type, String file, String mime, int w, int h, int time_ms);
/* additional functions that are not 1:1 available in the backend
@@ -128,7 +111,51 @@ public class MrChat {
return ret;
}
public long GetCPtr() {
public long getCPtr() {
return m_hChat;
}
public TLRPC.DraftMessage getDraftMessageObj() {
if( getId() == 0 ) {
return null;
}
TLRPC.DraftMessage ret = new TLRPC.DraftMessage();
ret.message = getDraft();
if( ret.message==null || ret.message.isEmpty() ) {
return null;
}
ret.date = (int)getDraftTimestamp();
ret.reply_to_msg_id = getDraftReplyToMsgId();
return ret;
}
public void saveDraft(CharSequence message, TLRPC.Message replyToMessage) {
if( message == null || TextUtils.isEmpty(message) ) {
setDraft(null, 0);
}
else {
setDraft(message.toString(), 0);
}
}
public void cleanDraft()
{
setDraft(null, 0);
}
public String getNameNAddr()
{
// returns name of group chats or name+e-mail-address for normal chats
String name = "ErrGrpNameNAddr";
if( getType()==MR_CHAT_GROUP ) {
name = getName();
}
else {
int contacts[] = MrMailbox.getChatContacts(getId());
if( contacts.length==1 ) {
name = MrMailbox.getContact(contacts[0]).getNameNAddr();
}
}
return name;
}
}
@@ -1,8 +1,8 @@
/*******************************************************************************
*
* Messenger Android Frontend
* Copyright (C) 2016 Björn Petersen Software Design and Development
* Contact: r10s@b44t.com, http://b44t.com
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
@@ -20,7 +20,6 @@
*******************************************************************************
*
* File: MrChatlist.java
* Authors: Björn Petersen
* Purpose: Wrap around mrchatlist_t
*
******************************************************************************/
@@ -48,7 +47,7 @@ public class MrChatlist {
}
public MrPoortext getSummaryByIndex(int index, MrChat chat) {
return new MrPoortext(MrChatlistGetSummaryByIndex(m_hChatlist, index, chat.GetCPtr()));
return new MrPoortext(MrChatlistGetSummaryByIndex(m_hChatlist, index, chat.getCPtr()));
}
private long m_hChatlist;
@@ -1,8 +1,8 @@
/*******************************************************************************
*
* Messenger Android Frontend
* Copyright (C) 2016 Björn Petersen Software Design and Development
* Contact: r10s@b44t.com, http://b44t.com
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
@@ -20,7 +20,6 @@
*******************************************************************************
*
* File: MrContact.java
* Authors: Björn Petersen
* Purpose: Wrap around mrcontact_t
*
******************************************************************************/
@@ -32,6 +31,7 @@ package com.b44t.messenger;
public class MrContact {
public final static int MR_CONTACT_ID_SELF = 1;
public final static int MR_CONTACT_ID_LAST_SPECIAL = 9;
public MrContact(long hContact) {
m_hContact = hContact;
@@ -1,8 +1,8 @@
/*******************************************************************************
*
* Messenger Android Frontend
* Copyright (C) 2016 Björn Petersen Software Design and Development
* Contact: r10s@b44t.com, http://b44t.com
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
@@ -20,7 +20,6 @@
*******************************************************************************
*
* File: MrMailbox.java
* Authors: Björn Petersen
* Purpose: Wrap around mrmailbox_t
*
******************************************************************************/
@@ -29,84 +28,46 @@
package com.b44t.messenger;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class MrMailbox {
public static void init () {
m_hMailbox = MrMailboxNew();
}
public static int open(String dbfile, String blobdir) {
return MrMailboxOpen(m_hMailbox, dbfile, blobdir);
}
public native static int open(String dbfile);
public native static void close();
public native static String getBlobdir();
public static void close() {
MrMailboxClose(m_hMailbox);
}
public native static void configureAndConnect();
public native static void configureCancel();
public static int configure() {
return MrMailboxConfigure(m_hMailbox);
}
public native static int isConfigured();
public static int isConfigured() {
return MrMailboxIsConfigured(m_hMailbox);
}
public native static void connect();
public native static void disconnect();
public static int connect() {
return MrMailboxConnect(m_hMailbox);
}
public native static void setConfig(String key, String value);
public native static void setConfigInt(String key, int value);
public native static String getConfig(String key, String def);
public native static int getConfigInt(String key, int def);
public static void disconnect() {
MrMailboxDisconnect(m_hMailbox);
}
public static int fetch() {
return MrMailboxFetch(m_hMailbox);
}
public static int setConfig(String key, String value) {
return MrMailboxSetConfig(m_hMailbox, key, value);
}
public static String getConfig(String key, String def) {
return MrMailboxGetConfig(m_hMailbox, key, def);
}
public static int getConfigInt(String key, int def) {
return MrMailboxGetConfigInt(m_hMailbox, key, def);
}
public static String getInfo() {
return MrMailboxGetInfo(m_hMailbox);
}
public static String execute(String cmd) {
return MrMailboxExecute(m_hMailbox, cmd);
}
private static long m_hMailbox = 0;
private native static long MrMailboxNew (); // returns hMailbox which must be unref'd after usage (Names as mrmailbox_new don't work due to the additional underscore)
private native static int MrMailboxOpen (long hMailbox, String dbfile, String blobdir);
private native static void MrMailboxClose (long hMailbox);
private native static int MrMailboxConfigure (long hMailbox);
private native static int MrMailboxIsConfigured (long hMailbox);
private native static int MrMailboxConnect (long hMailbox);
private native static void MrMailboxDisconnect (long hMailbox);
private native static int MrMailboxFetch (long hMailbox);
private native static int MrMailboxSetConfig (long hMailbox, String key, String value); // value may be NULL
private native static String MrMailboxGetConfig (long hMailbox, String key, String def); // def may be NULL, returns empty string as NULL
private native static int MrMailboxGetConfigInt (long hMailbox, String key, int def); // def may be NULL, returns empty string as NULL
private native static String MrMailboxGetInfo (long hMailbox);
private native static String MrMailboxExecute (long hMailbox, String cmd);
public native static String getInfo();
public native static String cmdline(String cmd);
private static long m_hMailbox = 0; // do not rename this, is used in C-part
private native static long MrMailboxNew(); // returns hMailbox which must be unref'd after usage (Names as mrmailbox_new don't work due to the additional underscore)
// contacts
public static int[] getKnownContacts(String query) {
return MrMailboxGetKnownContacts(m_hMailbox, query);
}
public static int[] getBlockedContacts() {
return MrMailboxGetBlockedContacts(m_hMailbox);
}
public native static int[] getKnownContacts(String query);
public native static int getBlockedCount();
public native static int[] getBlockedContacts();
public static MrContact getContact(int contact_id) {
return new MrContact(MrMailboxGetContact(m_hMailbox, contact_id));
@@ -128,8 +89,6 @@ public class MrMailbox {
return MrMailboxAddAddressBook(m_hMailbox, adrbook);
}
private native static int[] MrMailboxGetKnownContacts (long hMailbox, String query);
private native static int[] MrMailboxGetBlockedContacts(long hMailbox);
private native static long MrMailboxGetContact (long hMailbox, int id);// returns hContact which must be unref'd after usage
private native static int MrMailboxCreateContact (long hMailbox, String name, String addr);
private native static int MrMailboxBlockContact (long hMailbox, int id, int block);
@@ -138,17 +97,16 @@ public class MrMailbox {
// chats
public static MrChatlist getChatlist() {
return new MrChatlist(MrMailboxGetChatlist(m_hMailbox));
public static MrChatlist getChatlist(String query) {
return new MrChatlist(MrMailboxGetChatlist(m_hMailbox, query));
}
public static MrChat getChat(int contact_id) {
return new MrChat(MrMailboxGetChat(m_hMailbox, contact_id));
public static MrChat getChat(int chat_id) {
return new MrChat(MrMailboxGetChat(m_hMailbox, chat_id));
}
public static int markseenChat(int id) {
return MrMailboxMarkseenChat(m_hMailbox, id);
}
public native static int markseenMsg (int msg_id);
public native static int markseenChat (int chat_id);
public static int getChatIdByContactId (int contact_id) {
return MrMailboxGetChatIdByContactId(m_hMailbox, contact_id);
@@ -158,31 +116,28 @@ public class MrMailbox {
return MrMailboxCreateChatByContactId(m_hMailbox, contact_id);
}
public static int[] getChatMsgs(int chat_id) {
return MrMailboxGetChatMsgs(m_hMailbox, chat_id);
}
public native static int createGroupChat (String name);
public native static int isContactInChat (int chat_id, int contact_id);
public native static int addContactToChat (int chat_id, int contact_id);
public native static int removeContactFromChat (int chat_id, int contact_id);
public native static int setChatName (int chat_id, String name);
public static int[] getChatMedia(int chat_id, int msg_type, int or_msg_type) {
return MrMailboxGetChatMedia(m_hMailbox, chat_id, msg_type, or_msg_type);
}
public final static int MR_GCM_ADDDAYMARKER = 0x01;
public native static int[] getChatMsgs(int chat_id, int flags, int marker1before);
public static int[] getChatContacts(int chat_id) {
return MrMailboxGetChatContacts(m_hMailbox, chat_id);
}
public native static int[] searchMsgs(int chat_id, String query);
public static int deleteChat(int chat_id) {
return MrMailboxDeleteChat(m_hMailbox, chat_id);
}
public native static int[] getUnseenMsgs();
private native static long MrMailboxGetChatlist (long hMailbox); // returns hChatlist which must be unref'd after usage
public native static int[] getChatMedia(int chat_id, int msg_type, int or_msg_type);
public native static int getNextMedia(int msg_id, int dir);
public native static int[] getChatContacts(int chat_id);
public native static int deleteChat(int chat_id);
private native static long MrMailboxGetChatlist (long hMailbox, String query); // returns hChatlist which must be unref'd after usage
private native static long MrMailboxGetChat (long hMailbox, int chat_id); // return hChat which must be unref'd after usage
private native static int MrMailboxMarkseenChat (long hMailbox, int id);
private native static int MrMailboxGetChatIdByContactId (long hMailbox, int contact_id);
private native static int MrMailboxCreateChatByContactId(long hMailbox, int contact_id); // returns chat_id
private native static int[] MrMailboxGetChatMsgs (long hMailbox, int chat_id);
private native static int[] MrMailboxGetChatMedia (long hMailbox, int chat_id, int msg_type, int or_msg_type);
private native static int[] MrMailboxGetChatContacts (long hMailbox, int chat_id);
private native static int MrMailboxDeleteChat (long hMailbox, int chat_id);
// msgs
@@ -194,46 +149,76 @@ public class MrMailbox {
return MrMailboxGetMsgInfo(m_hMailbox, id);
}
public static void deleteMsg(int id) {
MrMailboxDeleteMsg(m_hMailbox, id);
}
public native static void deleteMsgs(int msg_ids[]);
public native static void forwardMsgs(int msg_ids[], int chat_ids);
private native static long MrMailboxGetMsg (long hMailbox, int id); // return hMsg which must be unref'd after usage
private native static String MrMailboxGetMsgInfo (long hMailbox, int id);
private native static void MrMailboxDeleteMsg (long hMailbox, int id);
// static
public native static void MrStockAddStr (int id, String str);
public native static String MrGetVersionStr ();
public native static String CPtr2String (long hString); // get strings eg. from data1 from the callback
public native static long String2CPtr (String str);
/* receive events
**********************************************************************************************/
public final static int MR_EVENT_MSGS_UPDATED = 2000;
public final static int MR_EVENT_ERROR = 400; // INFO and WARNING are blocked in the mrwrapper.c
public final static int MR_EVENT_MSGS_CHANGED = 2000;
public final static int MR_EVENT_INCOMING_MSG = 2005;
public final static int MR_EVENT_MSG_DELIVERED = 2010;
public final static int MR_EVENT_MSG_READ = 2015;
public final static int MR_EVENT_CHAT_MODIFIED = 2020;
public final static int MR_EVENT_CONTACTS_CHANGED = 2030;
public final static int MR_EVENT_MSG_DELIVERED = 3000;
public final static int MR_EVENT_MSG_READ = 3010;
public final static int MR_EVENT_CONNECTION_STATE_CHANGED = 3020;
public final static int MR_EVENT_CONFIGURE_ENDED = 2040;
public final static int MR_EVENT_CONFIGURE_PROGRESS = 2041;
public final static int MR_EVENT_IS_ONLINE = 2080;
public final static int MR_EVENT_GET_STRING = 2091;
public final static int MR_EVENT_GET_QUANTITIY_STRING = 2092;
public final static int MR_EVENT_HTTP_GET = 2100;
public static final Object m_lastErrorLock = new Object();
public static int m_lastErrorCode = 0;
public static String m_lastErrorString = "";
public static boolean m_showNextErrorAsToast = true;
public static long MrCallback(final int event, final long data1, final long data2) // this function is called from within the C-wrapper
{
switch(event) {
case MR_EVENT_CONNECTION_STATE_CHANGED:
case MR_EVENT_CONFIGURE_ENDED:
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.connectionStateChanged, (int)data1);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.configureEnded, (int)data1);
}
});
return 0;
case MR_EVENT_MSGS_UPDATED:
case MR_EVENT_CONFIGURE_PROGRESS:
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.configureProgress, (int)data1);
}
});
return 0;
case MR_EVENT_MSGS_CHANGED:
case MR_EVENT_INCOMING_MSG:
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
reloadMainChatlist();
NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload, event, (int)data1, (int)data2);
if( event == MR_EVENT_INCOMING_MSG ) {
NotificationsController.getInstance().processNewMessages((int)data1, (int)data2);
}
}
});
return 0;
@@ -261,6 +246,96 @@ public class MrMailbox {
}
});
return 0;
case MR_EVENT_CHAT_MODIFIED:
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
reloadMainChatlist();
NotificationCenter.getInstance().postNotificationName(NotificationCenter.updateInterfaces,
MessagesController.UPDATE_MASK_NAME|MessagesController.UPDATE_MASK_CHAT_NAME|
MessagesController.UPDATE_MASK_CHAT_MEMBERS|MessagesController.UPDATE_MASK_AVATAR);
}
});
return 0;
case MR_EVENT_ERROR:
synchronized (m_lastErrorLock) {
m_lastErrorCode = (int)data1;
m_lastErrorString = CPtr2String(data2);
}
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
synchronized (m_lastErrorLock) {
if( m_showNextErrorAsToast ) {
AndroidUtilities.showHint(ApplicationLoader.applicationContext, m_lastErrorString);
}
m_showNextErrorAsToast = true;
}
}
});
return 0;
case MR_EVENT_GET_STRING:
String s = "ErrStrBadId";
switch( (int)data1 ) {
// the string-IDs are defined in the backend; as this is the only place where they're used, there is no benefit in creating an enum or sth. like that.
case 1: s = ApplicationLoader.applicationContext.getString(R.string.NoMessages); break;
case 2: s = ApplicationLoader.applicationContext.getString(R.string.FromSelf); break;
case 3: s = ApplicationLoader.applicationContext.getString(R.string.Draft); break;
case 7: s = ApplicationLoader.applicationContext.getString(R.string.AttachVoiceMessage); break;
case 8: s = ApplicationLoader.applicationContext.getString(R.string.Deaddrop); break;
case 9: s = ApplicationLoader.applicationContext.getString(R.string.AttachPhoto); break;
case 10: s = ApplicationLoader.applicationContext.getString(R.string.AttachVideo); break;
case 11: s = ApplicationLoader.applicationContext.getString(R.string.AttachMusic); break;
case 12: s = ApplicationLoader.applicationContext.getString(R.string.AttachDocument); break;
case 13: s = ApplicationLoader.applicationContext.getString(R.string.DefaultStatusText); break;
case 14: s = ApplicationLoader.applicationContext.getString(R.string.MsgNewGroupDraft); break;
case 15: s = ApplicationLoader.applicationContext.getString(R.string.MsgGroupNameChanged); break;
case 16: s = ApplicationLoader.applicationContext.getString(R.string.MsgGroupImageChanged); break;
case 17: s = ApplicationLoader.applicationContext.getString(R.string.MsgMemberAddedToGroup); break;
case 18: s = ApplicationLoader.applicationContext.getString(R.string.MsgMemberRemovedFromToGroup); break;
case 19: s = ApplicationLoader.applicationContext.getString(R.string.MsgGroupLeft); break;
case 20: s = ApplicationLoader.applicationContext.getString(R.string.Error); break;
case 21: s = ApplicationLoader.applicationContext.getString(R.string.ErrSelfNotInGroup); break;
case 22: s = ApplicationLoader.applicationContext.getString(R.string.NoNetwork); break;
}
return String2CPtr(s);
case MR_EVENT_GET_QUANTITIY_STRING:
String sp = "ErrQtyStrBadId";
switch( (int)data1 ) {
// the string-IDs are defined in the backend; as this is the only place where they're used, there is no benefit in creating an enum or sth. like that.
case 4: sp = ApplicationLoader.applicationContext.getResources().getQuantityString(R.plurals.Members, (int)data2, (int)data2); break;
case 6: sp = ApplicationLoader.applicationContext.getResources().getQuantityString(R.plurals.Contacts, (int)data2, (int)data2); break;
}
return String2CPtr(sp);
case MR_EVENT_IS_ONLINE:
return ConnectionsManager.isNetworkOnline()? 1 : 0;
case MR_EVENT_HTTP_GET:
String httpContent = null;
try {
URL url = new URL(CPtr2String(data1));
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream inputStream = new BufferedInputStream(urlConnection.getInputStream());
BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder total = new StringBuilder();
String line;
while ((line = r.readLine()) != null) {
total.append(line).append('\n');
}
httpContent = total.toString();
} finally {
urlConnection.disconnect();
}
}
catch(Exception e) {}
return String2CPtr(httpContent);
}
return 0;
}
@@ -273,6 +348,31 @@ public class MrMailbox {
public native static int getCurrentTime ();
public static void reloadMainChatlist()
{
m_currChatlist = getChatlist();
m_currChatlist = getChatlist(null);
}
public final static int MEDIA_PHOTOVIDEO = 0;
public static void getMediaCount(final long uid, final int type, final int classGuid, boolean fromCache) {
Utilities.globalQueue.postRunnable(new Runnable() {
@Override
public void run() {
int[] media = new int[0];
if( type == MEDIA_PHOTOVIDEO ) {
media = MrMailbox.getChatMedia((int)uid, MrMsg.MR_MSG_IMAGE, MrMsg.MR_MSG_VIDEO);
}
NotificationCenter.getInstance().postNotificationName(NotificationCenter.mediaCountDidLoaded,
uid, media.length, false /*not from cache*/, type, media);
}
});
}
public static String getInviteText() {
String url = "https://getdelta.org";
String email = getConfig("addr", "");
String text = LocaleController.formatString("InviteText", R.string.InviteText, url, email);
return text;
}
}
@@ -1,8 +1,8 @@
/*******************************************************************************
*
* Messenger Android Frontend
* Copyright (C) 2016 Björn Petersen Software Design and Development
* Contact: r10s@b44t.com, http://b44t.com
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
@@ -20,7 +20,6 @@
*******************************************************************************
*
* File: MrMsg.java
* Authors: Björn Petersen
* Purpose: Wrap around mrmsg_t
*
******************************************************************************/
@@ -28,12 +27,22 @@
package com.b44t.messenger;
import android.graphics.Bitmap;
import android.media.ThumbnailUtils;
import android.provider.MediaStore;
import android.util.Log;
import java.io.File;
public class MrMsg {
private static final String TAG = "MrMsg";
public final static int MR_MSG_UNDEFINED = 0;
public final static int MR_MSG_TEXT = 10;
public final static int MR_MSG_IMAGE = 20;
public final static int MR_MSG_AUDIO = 40;
public final static int MR_MSG_VOICE = 41;
public final static int MR_MSG_VIDEO = 50;
public final static int MR_MSG_FILE = 60;
@@ -46,6 +55,9 @@ public class MrMsg {
public final static int MR_OUT_DELIVERED = 26;
public final static int MR_OUT_READ = 28;
public final static int MR_MSG_ID_MARKER1 = 1;
public final static int MR_MSG_ID_DAYMARKER = 9;
public MrMsg(long hMsg) {
m_hMsg = hMsg;
}
@@ -84,14 +96,21 @@ public class MrMsg {
return MrMsgGetToId(m_hMsg);
}
public String getParam (int key, String def) {
return MrMsgGetParam(m_hMsg, key, def);
}
public int getParamInt(int key, int def) {
return MrMsgGetParamInt(m_hMsg, key, def);
}
public native String getParam(int key, String def);
public native int getParamInt(int key, int def);
public native void setParamInt(int key, int def);
public native void saveParamToDisk();
private long m_hMsg;
public native int getBytes();
public MrPoortext getSummary(MrChat chat) { return new MrPoortext(getSummaryCPtr(chat.getCPtr())); }
private native long getSummaryCPtr(long hChat);
public native String getSummarytext(int approx_characters);
public MrPoortext getMediainfo() { return new MrPoortext(getMediainfoCPtr()); }
private native long getMediainfoCPtr();
public native String getFilename();
public native int isIncreation();
private long m_hMsg; // must not be renamed as referenced by JNI under the name "m_hMsg"
private native static void MrMsgUnref (long hMsg);
private native static int MrMsgGetId (long hMsg);
private native static String MrMsgGetText (long hMsg);
@@ -101,8 +120,6 @@ public class MrMsg {
private native static int MrMsgGetChatId (long hMsg);
private native static int MrMsgGetFromId (long hMsg);
private native static int MrMsgGetToId (long hMsg);
private native static String MrMsgGetParam (long hMsg, int key, String def);
private native static int MrMsgGetParamInt (long hMsg, int key, int def);
/* additional functions that are not 1:1 available in the backend
@@ -132,7 +149,6 @@ public class MrMsg {
ret.unread = state!=MR_OUT_READ; // the state of outgoing messages
ret.media_unread = ret.unread;
ret.flags = 0; // posible flags: MESSAGE_FLAG_HAS_FROM_ID, however, this seems to be read only
ret.post = false; // ? true=avatar wird in gruppen nicht angezeigt, wird aber in isFromUser() auch überprüft...
ret.out = ret.from_id==MrContact.MR_CONTACT_ID_SELF; // true=outgoing message, read eg. in MessageObject.isOutOwner()
ret.created_by_mr = true;
@@ -147,11 +163,21 @@ public class MrMsg {
TLRPC.TL_photoSize photoSize = new TLRPC.TL_photoSize();
photoSize.w = getParamInt('w', 800);
photoSize.h = getParamInt('h', 800);
photoSize.size = 0;
photoSize.size = 0; // not sure what to use here, maybe `getBytes();`?
photoSize.location = new TLRPC.TL_fileLocation();
photoSize.location.mr_path = path;
photoSize.location.local_id = -ret.id; // this forces the document to be searched in the cache dir
photoSize.type = "x";
if (photoSize.w <= 100 && photoSize.h <= 100) {
photoSize.type = "s";
} else if (photoSize.w <= 320 && photoSize.h <= 320) {
photoSize.type = "m";
} else if (photoSize.w <= 800 && photoSize.h <= 800) {
photoSize.type = "x";
} else if (photoSize.w <= 1280 && photoSize.h <= 1280) {
photoSize.type = "y";
} else {
photoSize.type = "w";
}
photo = new TLRPC.TL_photo();
photo.sizes.add(photoSize);
} catch (Exception e) {
@@ -169,37 +195,78 @@ public class MrMsg {
ret.message = "<cannot load image>";
}
}
else if( type == MR_MSG_AUDIO || type == MR_MSG_VIDEO ) {
else if( type == MR_MSG_AUDIO || type == MR_MSG_VOICE || type == MR_MSG_VIDEO || type == MR_MSG_FILE ) {
String path = getParam('f', "");
if( !path.isEmpty()) {
ret.message = "-1"; // may be misused for video editing information
ret.media = new TLRPC.TL_messageMediaDocument();
ret.media.caption = "";
ret.media.document = new TLRPC.TL_document();
ret.media.document.file_name = getFilename();
ret.media.document.mr_path = path;
if( type == MR_MSG_AUDIO ) {
ret.media.document.size = getBytes();
if( type == MR_MSG_AUDIO || type == MR_MSG_VOICE ) {
TLRPC.TL_documentAttributeAudio attr = new TLRPC.TL_documentAttributeAudio();
attr.voice = true; // !voice = music
attr.voice = type == MR_MSG_VOICE;
attr.duration = getParamInt('d', 0) / 1000;
ret.media.document.attributes.add(attr);
}
else if( type == MR_MSG_VIDEO) {
else if( type == MR_MSG_VIDEO ) {
File vfile = new File(path);
File tfile = new File(MrMailbox.getBlobdir(), vfile.getName()+"-preview.jpg");
if( !tfile.exists() ) {
try {
Bitmap thumb = ThumbnailUtils.createVideoThumbnail(path, MediaStore.Video.Thumbnails.MINI_KIND);
TLRPC.PhotoSize size = ImageLoader.scaleAndSaveImage(tfile, thumb, 90, 90, 55, false);
size.location.mr_path = tfile.getAbsolutePath();
size.type = "s";
ret.media.document.thumb = size;
setParamInt('w', size.w);
setParamInt('h', size.h);
saveParamToDisk();
}
catch (Exception e) {}
}
else {
TLRPC.PhotoSize size = new TLRPC.PhotoSize();
size.location = new TLRPC.TL_fileLocation();
size.location.mr_path = tfile.getAbsolutePath();
size.location.local_id = -ret.id;
size.w = getParamInt('w', 320);
size.h = getParamInt('h', 240);
size.type = "s";
ret.media.document.thumb = size;
}
TLRPC.TL_documentAttributeVideo attr = new TLRPC.TL_documentAttributeVideo();
attr.duration = getParamInt('d', 0) / 1000;
attr.w = getParamInt('w', 0);
attr.h = getParamInt('h', 0);
attr.w = getParamInt('w', 320);
attr.h = getParamInt('h', 240);
ret.media.document.attributes.add(attr);
}
else {
ret.media.document.mime_type = getParam('m', "application/octet-stream");
}
}
else {
ret.message = "<path missing>";
ret.message = "<file path missing>";
}
}
else {
ret.message = String.format("<unsupported message type #%d for id #%d>", type, ret.id);
}
if( !getParam('a', "").equals("") ) {
ret.flags |= TLRPC.MESSAGE_FLAG_FWD;
ret.fwd_from = new TLRPC.TL_messageFwdHeader();
ret.fwd_from.m_name = getParam('A', "");
if( ret.fwd_from.m_name.isEmpty() ) {
ret.fwd_from.m_name = getParam('a', "");
}
}
return ret;
}
};
@@ -1,8 +1,8 @@
/*******************************************************************************
*
* Messenger Android Frontend
* Copyright (C) 2016 Björn Petersen Software Design and Development
* Contact: r10s@b44t.com, http://b44t.com
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
@@ -20,7 +20,6 @@
*******************************************************************************
*
* File: MrPoortext.java
* Authors: Björn Petersen
* Purpose: Wrap around mrpoortext_t
*
******************************************************************************/
@@ -31,10 +30,10 @@ package com.b44t.messenger;
public class MrPoortext {
public final static int MR_TITLE_NORMAL = 0;
public final static int MR_TITLE_DRAFT = 1;
public final static int MR_TITLE_USERNAME = 2;
public final static int MR_TITLE_SELF = 3;
public final static int MR_TEXT1_NORMAL = 0;
public final static int MR_TEXT1_DRAFT = 1;
public final static int MR_TEXT1_USERNAME = 2;
public final static int MR_TEXT1_SELF = 3;
public MrPoortext(long hPoortext) {
m_hPoortext = hPoortext;
@@ -45,16 +44,16 @@ public class MrPoortext {
MrPoortextUnref(m_hPoortext);
}
public String getTitle() {
return MrPoortextGetTitle(m_hPoortext);
public String getText1() {
return MrPoortextGetText1(m_hPoortext);
}
public int getTitleMeaning() {
return MrPoortextGetTitleMeaning(m_hPoortext);
public int getText1Meaning() {
return MrPoortextGetText1Meaning(m_hPoortext);
}
public String getText() {
return MrPoortextGetText(m_hPoortext);
public String getText2() {
return MrPoortextGetText2(m_hPoortext);
}
public long getTimestamp() {
@@ -67,9 +66,9 @@ public class MrPoortext {
private long m_hPoortext;
private native static void MrPoortextUnref (long hPoortext);
private native static String MrPoortextGetTitle (long hPoortext);
private native static int MrPoortextGetTitleMeaning (long hPoortext);
private native static String MrPoortextGetText (long hPoortext);
private native static String MrPoortextGetText1 (long hPoortext);
private native static int MrPoortextGetText1Meaning (long hPoortext);
private native static String MrPoortextGetText2 (long hPoortext);
private native static long MrPoortextGetTimestamp (long hPoortext);
private native static int MrPoortextGetState (long hPoortext);
}
@@ -1,589 +0,0 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package com.b44t.messenger;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.browse.MediaBrowser;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
import android.os.SystemClock;
import android.service.media.MediaBrowserService;
import com.b44t.messenger.audioinfo.AudioInfo;
import com.b44t.ui.LaunchActivity;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class MusicBrowserService extends MediaBrowserService implements NotificationCenter.NotificationCenterDelegate {
private static final String AUTO_APP_PACKAGE_NAME = "com.google.android.projection.gearhead";
private static final String SLOT_RESERVATION_SKIP_TO_NEXT = "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_NEXT";
private static final String SLOT_RESERVATION_SKIP_TO_PREV = "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_PREVIOUS";
private static final String SLOT_RESERVATION_QUEUE = "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_QUEUE";
private MediaSession mediaSession;
private static final String MEDIA_ID_ROOT = "__ROOT__";
private boolean chatsLoaded;
private boolean loadingChats;
private ArrayList<Integer> dialogs = new ArrayList<>();
private HashMap<Integer, TLRPC.User> users = new HashMap<>();
private HashMap<Integer, TLRPC.Chat> chats = new HashMap<>();
private HashMap<Integer, ArrayList<MessageObject>> musicObjects = new HashMap<>();
private HashMap<Integer, ArrayList<MediaSession.QueueItem>> musicQueues = new HashMap<>();
public static final String ACTION_CMD = "com.example.android.mediabrowserservice.ACTION_CMD";
public static final String CMD_NAME = "CMD_NAME";
public static final String CMD_PAUSE = "CMD_PAUSE";
private Paint roundPaint;
private RectF bitmapRect;
private boolean serviceStarted;
private int lastSelectedDialog;
private static final int STOP_DELAY = 30000;
private DelayedStopHandler delayedStopHandler = new DelayedStopHandler(this);
@Override
public void onCreate() {
super.onCreate();
ApplicationLoader.postInitApplication();
lastSelectedDialog = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE).getInt("auto_lastSelectedDialog", 0);
mediaSession = new MediaSession(this, "MusicService");
setSessionToken(mediaSession.getSessionToken());
mediaSession.setCallback(new MediaSessionCallback());
mediaSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
Context context = getApplicationContext();
Intent intent = new Intent(context, LaunchActivity.class);
PendingIntent pi = PendingIntent.getActivity(context, 99, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mediaSession.setSessionActivity(pi);
Bundle extras = new Bundle();
extras.putBoolean(SLOT_RESERVATION_QUEUE, true);
extras.putBoolean(SLOT_RESERVATION_SKIP_TO_PREV, true);
extras.putBoolean(SLOT_RESERVATION_SKIP_TO_NEXT, true);
mediaSession.setExtras(extras);
updatePlaybackState(null);
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioPlayStateChanged);
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioDidStarted);
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioDidReset);
}
@Override
public int onStartCommand(Intent startIntent, int flags, int startId) {
/*if (startIntent != null) {
String action = startIntent.getAction();
String command = startIntent.getStringExtra(CMD_NAME);
if (ACTION_CMD.equals(action)) {
if (CMD_PAUSE.equals(command)) {
if (mPlayback != null && mPlayback.isPlaying()) {
handlePauseRequest();
}
}
}
}*/
return START_STICKY;
}
@Override
public void onDestroy() {
handleStopRequest(null);
delayedStopHandler.removeCallbacksAndMessages(null);
mediaSession.release();
}
@Override
public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
if (clientPackageName == null || Process.SYSTEM_UID != clientUid && Process.myUid() != clientUid && !clientPackageName.equals("com.google.android.mediasimulator") && !clientPackageName.equals("com.google.android.projection.gearhead")) {
return null;
}
return new BrowserRoot(MEDIA_ID_ROOT, null);
}
@Override
public void onLoadChildren(final String parentMediaId, final Result<List<MediaBrowser.MediaItem>> result) {
if (!chatsLoaded) {
result.detach();
if (loadingChats) {
return;
}
loadingChats = true;
/*
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
try {
ArrayList<Integer> usersToLoad = new ArrayList<>();
ArrayList<Integer> chatsToLoad = new ArrayList<>();
SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT DISTINCT uid FROM media_v2 WHERE uid != 0 AND mid > 0 AND type = %d", SharedMediaQuery.MEDIA_MUSIC));
while (cursor.next()) {
int lower_part = (int) cursor.longValue(0);
if (lower_part == 0) {
continue;
}
dialogs.add(lower_part);
if (lower_part > 0) {
usersToLoad.add(lower_part);
} else {
chatsToLoad.add(-lower_part);
}
}
cursor.dispose();
if (!dialogs.isEmpty()) {
String ids = TextUtils.join(",", dialogs);
cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT uid, data, mid FROM media_v2 WHERE uid IN (%s) AND mid > 0 AND type = %d ORDER BY date DESC, mid DESC", ids, SharedMediaQuery.MEDIA_MUSIC));
while (cursor.next()) {
NativeByteBuffer data = cursor.byteBufferValue(1);
if (data != null) {
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
data.reuse();
if (MessageObject.isMusicMessage(message)) {
int did = cursor.intValue(0);
message.id = cursor.intValue(2);
message.dialog_id = did;
ArrayList<MessageObject> arrayList = musicObjects.get(did);
ArrayList<MediaSession.QueueItem> arrayList1 = musicQueues.get(did);
if (arrayList == null) {
arrayList = new ArrayList<>();
musicObjects.put(did, arrayList);
arrayList1 = new ArrayList<>();
musicQueues.put(did, arrayList1);
}
MessageObject messageObject = new MessageObject(message, null, false);
arrayList.add(0, messageObject);
MediaDescription.Builder builder = new MediaDescription.Builder().setMediaId(did + "_" + arrayList.size());
builder.setTitle(messageObject.getMusicTitle());
builder.setSubtitle(messageObject.getMusicAuthor());
arrayList1.add(0, new MediaSession.QueueItem(builder.build(), arrayList1.size()));
}
}
}
cursor.dispose();
if (!usersToLoad.isEmpty()) {
ArrayList<TLRPC.User> usersArrayList = new ArrayList<>();
//MessagesStorage.getInstance().getUsersInternal(TextUtils.join(",", usersToLoad), usersArrayList);
for (int a = 0; a < usersArrayList.size(); a++) {
TLRPC.User user = usersArrayList.get(a);
users.put(user.id, user);
}
}
if (!chatsToLoad.isEmpty()) {
ArrayList<TLRPC.Chat> chatsArrayList = new ArrayList<>();
//MessagesStorage.getInstance().getChatsInternal(TextUtils.join(",", chatsToLoad), chatsArrayList);
for (int a = 0; a < chatsArrayList.size(); a++) {
TLRPC.Chat chat = chatsArrayList.get(a);
chats.put(chat.id, chat);
}
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
chatsLoaded = true;
loadingChats = false;
loadChildrenImpl(parentMediaId, result);
if (lastSelectedDialog == 0 && !dialogs.isEmpty()) {
lastSelectedDialog = dialogs.get(0);
}
if (lastSelectedDialog != 0) {
ArrayList<MessageObject> arrayList = musicObjects.get(lastSelectedDialog);
ArrayList<MediaSession.QueueItem> arrayList1 = musicQueues.get(lastSelectedDialog);
if (arrayList != null && !arrayList.isEmpty()) {
mediaSession.setQueue(arrayList1);
if (lastSelectedDialog > 0) {
TLRPC.User user = users.get(lastSelectedDialog);
if (user != null) {
mediaSession.setQueueTitle(ContactsController.formatName(user.first_name, user.last_name));
} else {
mediaSession.setQueueTitle("DELETED USER");
}
} else {
TLRPC.Chat chat = chats.get(-lastSelectedDialog);
if (chat != null) {
mediaSession.setQueueTitle(chat.title);
} else {
mediaSession.setQueueTitle("DELETED CHAT");
}
}
MessageObject messageObject = arrayList.get(0);
MediaMetadata.Builder builder = new MediaMetadata.Builder();
builder.putLong(MediaMetadata.METADATA_KEY_DURATION, messageObject.getDuration() * 1000);
builder.putString(MediaMetadata.METADATA_KEY_ARTIST, messageObject.getMusicAuthor());
builder.putString(MediaMetadata.METADATA_KEY_TITLE, messageObject.getMusicTitle());
mediaSession.setMetadata(builder.build());
}
}
updatePlaybackState(null);
}
});
}
});
*/
} else {
loadChildrenImpl(parentMediaId, result);
}
}
private void loadChildrenImpl(final String parentMediaId, final Result<List<MediaBrowser.MediaItem>> result) {
List<MediaBrowser.MediaItem> mediaItems = new ArrayList<>();
if (MEDIA_ID_ROOT.equals(parentMediaId)) {
for (int a = 0; a < dialogs.size(); a++) {
int did = dialogs.get(a);
MediaDescription.Builder builder = new MediaDescription.Builder().setMediaId("__CHAT_" + did);
TLRPC.FileLocation avatar = null;
if (did > 0) {
TLRPC.User user = users.get(did);
if (user != null) {
builder.setTitle(ContactsController.formatName(user.first_name, user.last_name));
if (user.photo != null && user.photo.photo_small instanceof TLRPC.TL_fileLocation) {
avatar = user.photo.photo_small;
}
} else {
builder.setTitle("DELETED USER");
}
} else {
TLRPC.Chat chat = chats.get(-did);
if (chat != null) {
builder.setTitle(chat.title);
if (chat.photo != null && chat.photo.photo_small instanceof TLRPC.TL_fileLocation) {
avatar = chat.photo.photo_small;
}
} else {
builder.setTitle("DELETED CHAT");
}
}
Bitmap bitmap = null;
if (avatar != null) {
bitmap = createRoundBitmap(FileLoader.getPathToAttach(avatar, true));
if (bitmap != null) {
builder.setIconBitmap(bitmap);
}
}
if (avatar == null || bitmap == null) {
builder.setIconUri(Uri.parse("android.resource://" + getApplicationContext().getPackageName() + "/drawable/contact_blue"));
}
mediaItems.add(new MediaBrowser.MediaItem(builder.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE));
}
} else if (parentMediaId != null && parentMediaId.startsWith("__CHAT_")) {
int did = 0;
try {
did = Integer.parseInt(parentMediaId.replace("__CHAT_", ""));
} catch (Exception e) {
FileLog.e("messenger", e);
}
ArrayList<MessageObject> arrayList = musicObjects.get(did);
if (arrayList != null) {
for (int a = 0; a < arrayList.size(); a++) {
MessageObject messageObject = arrayList.get(a);
MediaDescription.Builder builder = new MediaDescription.Builder().setMediaId(did + "_" + a);
builder.setTitle(messageObject.getMusicTitle());
builder.setSubtitle(messageObject.getMusicAuthor());
mediaItems.add(new MediaBrowser.MediaItem(builder.build(), MediaBrowser.MediaItem.FLAG_PLAYABLE));
}
}
}
result.sendResult(mediaItems);
}
private Bitmap createRoundBitmap(File path) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeFile(path.toString(), options);
if (bitmap != null) {
Bitmap result = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
result.eraseColor(Color.TRANSPARENT);
Canvas canvas = new Canvas(result);
BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
if (roundPaint == null) {
roundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bitmapRect = new RectF();
}
roundPaint.setShader(shader);
bitmapRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
canvas.drawRoundRect(bitmapRect, bitmap.getWidth(), bitmap.getHeight(), roundPaint);
return result;
}
} catch (Throwable e) {
FileLog.e("messenger", e);
}
return null;
}
private final class MediaSessionCallback extends MediaSession.Callback {
@Override
public void onPlay() {
MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject();
if (messageObject == null) {
onPlayFromMediaId(lastSelectedDialog + "_" + 0, null);
} else {
MediaController.getInstance().playAudio(messageObject);
}
}
@Override
public void onSkipToQueueItem(long queueId) {
MediaController.getInstance().playMessageAtIndex((int) queueId);
handlePlayRequest();
}
@Override
public void onSeekTo(long position) {
MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject();
if (messageObject != null) {
MediaController.getInstance().seekToProgress(messageObject, position / 1000 / (float) messageObject.getDuration());
}
}
@Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
String args[] = mediaId.split("_");
if (args.length != 2) {
return;
}
try {
int did = Integer.parseInt(args[0]);
int id = Integer.parseInt(args[1]);
ArrayList<MessageObject> arrayList = musicObjects.get(did);
ArrayList<MediaSession.QueueItem> arrayList1 = musicQueues.get(did);
if (arrayList == null || id < 0 || id >= arrayList.size()) {
return;
}
lastSelectedDialog = did;
ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE).edit().putInt("auto_lastSelectedDialog", did).commit();
MediaController.getInstance().setPlaylist(arrayList, arrayList.get(id), false);
mediaSession.setQueue(arrayList1);
if (did > 0) {
TLRPC.User user = users.get(did);
if (user != null) {
mediaSession.setQueueTitle(ContactsController.formatName(user.first_name, user.last_name));
} else {
mediaSession.setQueueTitle("DELETED USER");
}
} else {
TLRPC.Chat chat = chats.get(-did);
if (chat != null) {
mediaSession.setQueueTitle(chat.title);
} else {
mediaSession.setQueueTitle("DELETED CHAT");
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
handlePlayRequest();
}
@Override
public void onPause() {
handlePauseRequest();
}
@Override
public void onStop() {
handleStopRequest(null);
}
@Override
public void onSkipToNext() {
MediaController.getInstance().playNextMessage();
}
@Override
public void onSkipToPrevious() {
MediaController.getInstance().playPreviousMessage();
}
@Override
public void onPlayFromSearch(String query, Bundle extras) {
if (query == null || query.length() == 0) {
return;
}
query = query.toLowerCase();
for (int a = 0; a < dialogs.size(); a++) {
int did = dialogs.get(a);
if (did > 0) {
TLRPC.User user = users.get(did);
if (user == null) {
continue;
}
if (user.first_name != null && user.first_name.startsWith(query) || user.last_name != null && user.last_name.startsWith(query)) {
onPlayFromMediaId(did + "_" + 0, null);
break;
}
} else {
TLRPC.Chat chat = chats.get(-did);
if (chat == null) {
continue;
}
if (chat.title != null && chat.title.toLowerCase().contains(query)) {
onPlayFromMediaId(did + "_" + 0, null);
break;
}
}
}
}
}
private void updatePlaybackState(String error) {
long position = PlaybackState.PLAYBACK_POSITION_UNKNOWN;
MessageObject playingMessageObject = MediaController.getInstance().getPlayingMessageObject();
if (playingMessageObject != null) {
position = playingMessageObject.audioProgressSec * 1000;
}
PlaybackState.Builder stateBuilder = new PlaybackState.Builder().setActions(getAvailableActions());
int state;
if (playingMessageObject == null) {
state = PlaybackState.STATE_STOPPED;
} else {
if (MediaController.getInstance().isDownloadingCurrentMessage()) {
state = PlaybackState.STATE_BUFFERING;
} else {
state = MediaController.getInstance().isAudioPaused() ? PlaybackState.STATE_PAUSED : PlaybackState.STATE_PLAYING;
}
}
if (error != null) {
stateBuilder.setErrorMessage(error);
state = PlaybackState.STATE_ERROR;
}
stateBuilder.setState(state, position, 1.0f, SystemClock.elapsedRealtime());
if (playingMessageObject != null) {
stateBuilder.setActiveQueueItemId(MediaController.getInstance().getPlayingMessageObjectNum());
} else {
stateBuilder.setActiveQueueItemId(0);
}
mediaSession.setPlaybackState(stateBuilder.build());
}
private long getAvailableActions() {
long actions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PLAY_FROM_MEDIA_ID | PlaybackState.ACTION_PLAY_FROM_SEARCH;
MessageObject playingMessageObject = MediaController.getInstance().getPlayingMessageObject();
if (playingMessageObject != null) {
if (!MediaController.getInstance().isAudioPaused()) {
actions |= PlaybackState.ACTION_PAUSE;
}
actions |= PlaybackState.ACTION_SKIP_TO_PREVIOUS;
actions |= PlaybackState.ACTION_SKIP_TO_NEXT;
}
return actions;
}
private void handleStopRequest(String withError) {
delayedStopHandler.removeCallbacksAndMessages(null);
delayedStopHandler.sendEmptyMessageDelayed(0, STOP_DELAY);
updatePlaybackState(withError);
stopSelf();
serviceStarted = false;
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.audioPlayStateChanged);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.audioDidStarted);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.audioDidReset);
}
private void handlePlayRequest() {
delayedStopHandler.removeCallbacksAndMessages(null);
if (!serviceStarted) {
startService(new Intent(getApplicationContext(), MusicBrowserService.class));
serviceStarted = true;
}
if (!mediaSession.isActive()) {
mediaSession.setActive(true);
}
MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject();
if (messageObject == null) {
return;
}
MediaMetadata.Builder builder = new MediaMetadata.Builder();
builder.putLong(MediaMetadata.METADATA_KEY_DURATION, messageObject.getDuration() * 1000);
builder.putString(MediaMetadata.METADATA_KEY_ARTIST, messageObject.getMusicAuthor());
builder.putString(MediaMetadata.METADATA_KEY_TITLE, messageObject.getMusicTitle());
AudioInfo audioInfo = MediaController.getInstance().getAudioInfo();
if (audioInfo != null) {
Bitmap bitmap = audioInfo.getCover();
if (bitmap != null) {
builder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bitmap);
}
}
mediaSession.setMetadata(builder.build());
}
private void handlePauseRequest() {
MediaController.getInstance().pauseAudio(MediaController.getInstance().getPlayingMessageObject());
delayedStopHandler.removeCallbacksAndMessages(null);
delayedStopHandler.sendEmptyMessageDelayed(0, STOP_DELAY);
}
@Override
public void didReceivedNotification(int id, Object... args) {
updatePlaybackState(null);
handlePlayRequest();
}
private static class DelayedStopHandler extends Handler {
private final WeakReference<MusicBrowserService> mWeakReference;
private DelayedStopHandler(MusicBrowserService service) {
mWeakReference = new WeakReference<>(service);
}
@Override
public void handleMessage(Message msg) {
MusicBrowserService service = mWeakReference.get();
if (service != null) {
MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject();
if (messageObject != null && !MediaController.getInstance().isAudioPaused()) {
return;
}
service.stopSelf();
service.serviceStarted = false;
}
}
}
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -46,10 +61,10 @@ public class MusicPlayerReceiver extends BroadcastReceiver {
case KeyEvent.KEYCODE_MEDIA_STOP:
break;
case KeyEvent.KEYCODE_MEDIA_NEXT:
MediaController.getInstance().playNextMessage();
MediaController.getInstance().playNextMessage(+1, false);
break;
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
MediaController.getInstance().playPreviousMessage();
MediaController.getInstance().playNextMessage(-1, false);
break;
}
} else {
@@ -58,11 +73,11 @@ public class MusicPlayerReceiver extends BroadcastReceiver {
} else if (intent.getAction().equals(MusicPlayerService.NOTIFY_PAUSE) || intent.getAction().equals(android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
MediaController.getInstance().pauseAudio(MediaController.getInstance().getPlayingMessageObject());
} else if (intent.getAction().equals(MusicPlayerService.NOTIFY_NEXT)) {
MediaController.getInstance().playNextMessage();
MediaController.getInstance().playNextMessage(+1, false);
} else if (intent.getAction().equals(MusicPlayerService.NOTIFY_CLOSE)) {
MediaController.getInstance().cleanupPlayer(true, true);
} else if (intent.getAction().equals(MusicPlayerService.NOTIFY_PREVIOUS)) {
MediaController.getInstance().playPreviousMessage();
MediaController.getInstance().playNextMessage(-1, false);
}
}
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -38,7 +53,6 @@ public class MusicPlayerService extends Service implements NotificationCenter.No
private RemoteControlClient remoteControlClient;
private AudioManager audioManager;
private static boolean supportBigNotifications = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
private static boolean supportLockScreenControls = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
@Override
@@ -93,95 +107,70 @@ public class MusicPlayerService extends Service implements NotificationCenter.No
}
@SuppressLint("NewApi")
private void createNotification(MessageObject messageObject) {
String songName = messageObject.getMusicTitle();
String authorName = messageObject.getMusicAuthor();
private void createNotification(MessageObject messageObject)
{
AudioInfo audioInfo = MediaController.getInstance().getAudioInfo();
RemoteViews simpleContentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.player_small_notification);
RemoteViews expandedView = null;
if (supportBigNotifications) {
expandedView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.player_big_notification);
MrPoortext pt = MrMailbox.getMsg(messageObject.getId()).getMediainfo();
String authorName = pt.getText1();
String songName = pt.getText2();
if( songName == null || songName.length()==0 ) {
TLRPC.Document document = messageObject.messageOwner.media.document;
for (int i = 0; i < document.attributes.size(); i++) {
TLRPC.DocumentAttribute attr = document.attributes.get(i);
if (attr instanceof TLRPC.TL_documentAttributeAudio) {
authorName = attr.performer;
songName = attr.title;
break;
}
}
}
Intent intent = new Intent(ApplicationLoader.applicationContext, LaunchActivity.class);
intent.setAction("com.b44t.messenger.openplayer");
intent.setFlags(32768);
PendingIntent contentIntent = PendingIntent.getActivity(ApplicationLoader.applicationContext, 0, intent, 0);
if( songName == null || songName.length()==0 ) {
// preview of just recorded and not send voice messages
authorName = ApplicationLoader.applicationContext.getString(R.string.FromSelf);
songName = ApplicationLoader.applicationContext.getString(R.string.AttachVoiceMessage);
}
RemoteViews simpleContentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.player_small_notification);
//Intent intent = new Intent(ApplicationLoader.applicationContext, LaunchActivity.class);
//intent.setAction("com.b44t.messenger.openchat"+messageObject.getDialogId());
//intent.setFlags(32768);
//PendingIntent contentIntent = PendingIntent.getActivity(ApplicationLoader.applicationContext, 0, intent, 0);
Notification notification = new NotificationCompat.Builder(getApplicationContext())
.setSmallIcon(R.drawable.player)
.setContentIntent(contentIntent)
.setSmallIcon(R.drawable.notification_player)
//.setContentIntent(contentIntent)
.setContentTitle(songName).build();
notification.contentView = simpleContentView;
if (supportBigNotifications) {
notification.bigContentView = expandedView;
}
setListeners(simpleContentView);
if (supportBigNotifications) {
setListeners(expandedView);
}
Bitmap albumArt = audioInfo != null ? audioInfo.getSmallCover() : null;
if (albumArt != null) {
notification.contentView.setImageViewBitmap(R.id.player_album_art, albumArt);
if (supportBigNotifications) {
notification.bigContentView.setImageViewBitmap(R.id.player_album_art, albumArt);
}
notification.contentView.setViewVisibility(R.id.player_album_art, View.VISIBLE);
} else {
notification.contentView.setImageViewResource(R.id.player_album_art, R.drawable.nocover_small);
if (supportBigNotifications) {
notification.bigContentView.setImageViewResource(R.id.player_album_art, R.drawable.nocover_big);
}
notification.contentView.setViewVisibility(R.id.player_album_art, View.GONE);
}
if (MediaController.getInstance().isDownloadingCurrentMessage()) {
notification.contentView.setViewVisibility(R.id.player_pause, View.GONE);
notification.contentView.setViewVisibility(R.id.player_play, View.GONE);
notification.contentView.setViewVisibility(R.id.player_next, View.GONE);
notification.contentView.setViewVisibility(R.id.player_previous, View.GONE);
notification.contentView.setViewVisibility(R.id.player_progress_bar, View.VISIBLE);
if (supportBigNotifications) {
notification.bigContentView.setViewVisibility(R.id.player_pause, View.GONE);
notification.bigContentView.setViewVisibility(R.id.player_play, View.GONE);
notification.bigContentView.setViewVisibility(R.id.player_next, View.GONE);
notification.bigContentView.setViewVisibility(R.id.player_previous, View.GONE);
notification.bigContentView.setViewVisibility(R.id.player_progress_bar, View.VISIBLE);
}
} else {
notification.contentView.setViewVisibility(R.id.player_progress_bar, View.GONE);
{
notification.contentView.setViewVisibility(R.id.player_next, View.VISIBLE);
notification.contentView.setViewVisibility(R.id.player_previous, View.VISIBLE);
if (supportBigNotifications) {
notification.bigContentView.setViewVisibility(R.id.player_next, View.VISIBLE);
notification.bigContentView.setViewVisibility(R.id.player_previous, View.VISIBLE);
notification.bigContentView.setViewVisibility(R.id.player_progress_bar, View.GONE);
}
if (MediaController.getInstance().isAudioPaused()) {
notification.contentView.setViewVisibility(R.id.player_pause, View.GONE);
notification.contentView.setViewVisibility(R.id.player_play, View.VISIBLE);
if (supportBigNotifications) {
notification.bigContentView.setViewVisibility(R.id.player_pause, View.GONE);
notification.bigContentView.setViewVisibility(R.id.player_play, View.VISIBLE);
}
} else {
notification.contentView.setViewVisibility(R.id.player_pause, View.VISIBLE);
notification.contentView.setViewVisibility(R.id.player_play, View.GONE);
if (supportBigNotifications) {
notification.bigContentView.setViewVisibility(R.id.player_pause, View.VISIBLE);
notification.bigContentView.setViewVisibility(R.id.player_play, View.GONE);
}
}
}
notification.contentView.setTextViewText(R.id.player_song_name, songName);
notification.contentView.setTextViewText(R.id.player_author_name, authorName);
if (supportBigNotifications) {
notification.bigContentView.setTextViewText(R.id.player_song_name, songName);
notification.bigContentView.setTextViewText(R.id.player_author_name, authorName);
}
notification.flags |= Notification.FLAG_ONGOING_EVENT;
startForeground(5, notification);
@@ -203,12 +192,16 @@ public class MusicPlayerService extends Service implements NotificationCenter.No
public void setListeners(RemoteViews view) {
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent(NOTIFY_PREVIOUS), PendingIntent.FLAG_UPDATE_CURRENT);
view.setOnClickPendingIntent(R.id.player_previous, pendingIntent);
pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent(NOTIFY_CLOSE), PendingIntent.FLAG_UPDATE_CURRENT);
view.setOnClickPendingIntent(R.id.player_close, pendingIntent);
pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent(NOTIFY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT);
view.setOnClickPendingIntent(R.id.player_pause, pendingIntent);
pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent(NOTIFY_NEXT), PendingIntent.FLAG_UPDATE_CURRENT);
view.setOnClickPendingIntent(R.id.player_next, pendingIntent);
pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent(NOTIFY_PLAY), PendingIntent.FLAG_UPDATE_CURRENT);
view.setOnClickPendingIntent(R.id.player_play, pendingIntent);
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 1.3.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -113,7 +128,7 @@ public class NativeLoader {
return;
}
String crashDir = context.getCacheDir().getAbsolutePath();
//String crashDir = context.getCacheDir().getAbsolutePath();
try {
String folder;
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -16,71 +31,35 @@ public class NotificationCenter {
private static int totalEvents = 1;
public static final int connectionStateChanged = totalEvents++;
public static final int configureEnded = totalEvents++;
public static final int configureProgress = totalEvents++;
public static final int didReceivedNewMessages = totalEvents++;
public static final int updateInterfaces = totalEvents++;
public static final int dialogsNeedReload = totalEvents++;
public static final int closeChats = totalEvents++;
public static final int messagesDeleted = totalEvents++;
public static final int messagesSentOrRead = totalEvents++;
public static final int messagesDidLoaded = totalEvents++;
public static final int messageReceivedByAck = totalEvents++;
public static final int messageReceivedByServer = totalEvents++;
public static final int messageSendError = totalEvents++;
public static final int contactsDidLoaded = totalEvents++;
public static final int chatDidCreated = totalEvents++;
public static final int chatDidFailCreate = totalEvents++;
public static final int mediaDidLoaded = totalEvents++;
public static final int mediaCountDidLoaded = totalEvents++;
public static final int dialogPhotosLoaded = totalEvents++;
public static final int removeAllMessagesFromDialog = totalEvents++;
public static final int notificationsSettingsUpdated = totalEvents++;
public static final int pushMessagesUpdated = totalEvents++;
public static final int blockedUsersDidLoaded = totalEvents++;
public static final int openedChatChanged = totalEvents++;
public static final int stopEncodingService = totalEvents++;
public static final int didCreatedNewDeleteTask = totalEvents++;
public static final int mainUserInfoChanged = totalEvents++;
public static final int updateMessageMedia = totalEvents++;
public static final int recentImagesDidLoaded = totalEvents++;
public static final int replaceMessagesObjects = totalEvents++;
public static final int waveformCalculated = totalEvents++;
public static final int didSetPasscode = totalEvents++;
//public static final int screenStateChanged = totalEvents++; -- currently not used, but this may get handy
public static final int didLoadedReplyMessages = totalEvents++;
public static final int didReceivedWebpages = totalEvents++;
public static final int didReceivedWebpagesInUpdates = totalEvents++;
public static final int stickersDidLoaded = totalEvents++;
public static final int didReplacedPhotoInMemCache = totalEvents++;
public static final int messagesReadContent = totalEvents++;
public static final int userInfoDidLoaded = totalEvents++;
public static final int chatSearchResultsAvailable = totalEvents++;
public static final int musicDidLoaded = totalEvents++;
public static final int needShowAlert = totalEvents++;
public static final int didUpdatedMessagesViews = totalEvents++;
public static final int needReloadRecentDialogsSearch = totalEvents++;
public static final int locationPermissionGranted = totalEvents++;
public static final int peerSettingsDidLoaded = totalEvents++;
public static final int wasUnableToFindCurrentLocation = totalEvents++;
public static final int reloadHints = totalEvents++;
public static final int reloadInlineHints = totalEvents++;
public static final int newDraftReceived = totalEvents++;
public static final int httpFileDidLoaded = totalEvents++;
public static final int httpFileDidFailedLoad = totalEvents++;
public static final int messageThumbGenerated = totalEvents++;
public static final int wallpapersDidLoaded = totalEvents++;
public static final int closeOtherAppActivities = totalEvents++;
public static final int didUpdatedConnectionState = totalEvents++;
public static final int didReceiveSmsCode = totalEvents++;
public static final int didReceiveCall = totalEvents++;
public static final int emojiDidLoaded = totalEvents++;
public static final int appDidLogout = totalEvents++;
public static final int FileDidUpload = totalEvents++;
public static final int FileDidFailUpload = totalEvents++;
public static final int FileUploadProgressChanged = totalEvents++;
public static final int FileLoadProgressChanged = totalEvents++;
public static final int FileDidLoaded = totalEvents++;
public static final int FileDidFailedLoad = totalEvents++;
@@ -95,7 +74,6 @@ public class NotificationCenter {
public static final int recordStarted = totalEvents++;
public static final int recordStartError = totalEvents++;
public static final int recordStopped = totalEvents++;
public static final int screenshotTook = totalEvents++;
public static final int albumsDidLoaded = totalEvents++;
public static final int audioDidSent = totalEvents++;
public static final int audioDidStarted = totalEvents++;
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -18,6 +33,6 @@ public class NotificationDismissReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Context.MODE_PRIVATE);
preferences.edit().putInt("dismissDate", intent.getIntExtra("messageDate", 0)).commit();
preferences.edit().putInt("dismissDate", intent.getIntExtra("messageDate", 0)).apply();
}
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
File diff suppressed because it is too large Load Diff
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 1.3.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 1.3.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
File diff suppressed because it is too large Load Diff
@@ -1,29 +0,0 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package com.b44t.messenger;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class ShareBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String url = intent.getDataString();
if (url != null) {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, url);
Intent chooserIntent = Intent.createChooser(shareIntent, LocaleController.getString("ShareLink", R.string.ShareLink));
chooserIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(chooserIntent);
}
}
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -17,19 +32,15 @@ public class TLRPC {
public static final int MESSAGE_FLAG_REPLY = 0x00000008;
public static final int MESSAGE_FLAG_HAS_FROM_ID = 0x00000100;
public static final int MESSAGE_FLAG_HAS_MEDIA = 0x00000200;
public static final int MESSAGE_FLAG_EDITED = 0x00008000;
public static class DraftMessage extends TLObject {
public int flags;
public boolean no_webpage;
public int reply_to_msg_id;
public String message;
public ArrayList<MessageEntity> entities = new ArrayList<>();
public int date;
}
public static class ChatPhoto extends TLObject {
public FileLocation photo_small;
public FileLocation photo_big;
}
@@ -39,7 +50,6 @@ public class TLRPC {
public int duration;
public String alt;
public InputStickerSet stickerset;
public int flags;
public boolean voice;
public String title;
public String performer;
@@ -65,180 +75,43 @@ public class TLRPC {
public static class TL_documentAttributeFilename extends DocumentAttribute {
}
public static class messages_Messages extends TLObject {
public ArrayList<Message> messages = new ArrayList<>();
public ArrayList<Chat> chats = new ArrayList<>();
public ArrayList<User> users = new ArrayList<>();
public int flags;
public int pts;
public int count;
}
public static class TL_messages_messages extends messages_Messages {
}
public static class Peer extends TLObject {
public final int channel_id = 0;
public int user_id;
public int chat_id;
public final int chat_id = 0;
}
public static class TL_peerUser extends Peer {
}
public static class TL_peerChat extends Peer {
}
public static class GeoPoint extends TLObject {
public double _long;
public double lat;
}
public static class TL_geoPoint extends GeoPoint {
}
public static class SendMessageAction extends TLObject {
public int progress;
}
public static class PrivacyRule extends TLObject {
public ArrayList<Integer> users = new ArrayList<>();
}
public static class TL_messageMediaEmpty extends MessageMedia {
}
public static class TL_messageMediaVenue extends MessageMedia {
}
public static class TL_messageMediaDocument extends MessageMedia {
}
public static class TL_messageMediaContact extends MessageMedia {
}
public static class TL_messageMediaPhoto extends MessageMedia {
}
public static class TL_messageMediaGeo extends MessageMedia {
}
public static class TL_messageMediaWebPage extends MessageMedia {
}
public static class BotInlineResult extends TLObject {
public int flags;
public String id;
public String type;
public String title;
public String description;
public String url;
public String thumb_url;
public String content_url;
public String content_type;
public int w;
public int h;
public int duration;
public Photo photo;
public Document document;
}
public static class PeerNotifySettings extends TLObject {
public int flags;
public boolean silent;
public int mute_until;
public String sound;
}
public static class TL_peerNotifySettings extends PeerNotifySettings {
}
public static class TL_messages_stickerSet extends TLObject {
public StickerSet set;
public ArrayList<TL_stickerPack> packs = new ArrayList<>();
public ArrayList<Document> documents = new ArrayList<>();
}
public static class InputGeoPoint extends TLObject {
public double lat;
public double _long;
}
public static class TL_inputGeoPoint extends InputGeoPoint {
}
public static class Audio extends TLObject {
public long id;
public int date;
public int duration;
public String mime_type;
public int size;
public int dc_id;
public final int dc_id = 0;
public int user_id;
public byte[] key;
public byte[] iv;
}
public static class ChatFull extends TLObject {
public int flags;
public int id;
public String about;
public int pinned_msg_id;
public ChatParticipants participants;
}
public static class TL_topPeerCategoryPeers extends TLObject {
public TopPeerCategory category;
public int count;
}
public static class InputUser extends TLObject {
public int user_id;
public long access_hash;
}
public static class TL_inputUserEmpty extends InputUser {
}
public static class TL_inputUserSelf extends InputUser {
}
public static class TL_inputUser extends InputUser {
}
public static class WebPage extends TLObject {
public int flags;
public long id;
public String url;
public String display_url;
public String type;
public String site_name;
public String title;
public String description;
public Photo photo;
public String embed_url;
public int embed_width;
public int embed_height;
public int duration;
public String author;
public int date;
public Document document;
}
public static class TL_webPageEmpty extends WebPage {
}
public static class TL_webPage extends WebPage {
}
public static class InputFileLocation extends TLObject {
public long id;
public long access_hash;
public long volume_id;
public int local_id;
public long secret;
}
public static class TL_inputDocumentFileLocation extends InputFileLocation {
@@ -252,62 +125,10 @@ public class TLRPC {
public String first_name;
public String last_name;
public String username;
public long access_hash;
public String phone;
public UserProfilePhoto photo;
public UserStatus status;
public int flags;
public boolean self;
public boolean contact;
public boolean mutual_contact;
public boolean deleted;
public final boolean bot = false;
public boolean verified;
public boolean min;
public String restriction_reason;
}
public static class TL_userContact_old2 extends User {
}
public static class MessageAction extends TLObject {
public String title;
public String address;
public ArrayList<Integer> users = new ArrayList<>();
public final int channel_id = 0;
public Photo photo;
public int chat_id;
public int user_id;
public UserProfilePhoto newUserPhoto;
}
public static class TL_messageActionChatCreate extends MessageAction {
}
public static class TL_messageActionHistoryClear extends MessageAction {
}
public static class TL_messageActionChatEditPhoto extends MessageAction {
}
public static class TL_messageActionChatAddUser extends MessageAction {
}
public static class TL_messageActionChatDeleteUser extends MessageAction {
}
public static class TL_messageActionUserUpdatedPhoto extends MessageAction {
}
public static class TL_messageActionChatDeletePhoto extends MessageAction {
}
public static class TL_messageActionChatEditTitle extends MessageAction {
}
public static class TL_messageActionEmpty extends MessageAction {
}
public static class Video extends TLObject {
public long id;
public int user_id;
@@ -320,19 +141,6 @@ public class TLRPC {
public int h;
public String mime_type;
public String caption;
public byte[] key;
public byte[] iv;
}
public static class TopPeerCategory extends TLObject {
}
public static class InputDocument extends TLObject {
public long id;
public long access_hash;
}
public static class TL_inputDocument extends InputDocument {
}
public static class Document extends TLObject {
@@ -355,169 +163,21 @@ public class TLRPC {
public static class TL_document extends Document {
}
public static class InputMedia extends TLObject {
public InputFile file;
public InputFile thumb;
public String mime_type;
public ArrayList<DocumentAttribute> attributes = new ArrayList<>();
public String caption;
public InputGeoPoint geo_point;
public String title;
public String address;
public String provider;
public String venue_id;
public String phone_number;
public String first_name;
public String last_name;
public String url;
public String q;
}
public static class TL_inputMediaContact extends InputMedia {
}
public static class TL_inputMediaUploadedThumbDocument extends InputMedia {
}
public static class TL_inputMediaDocument extends InputMedia {
public InputDocument id;
}
public static class TL_inputMediaGifExternal extends InputMedia {
}
public static class TL_inputMediaGeoPoint extends InputMedia {
}
public static class TL_inputMediaUploadedPhoto extends InputMedia {
}
public static class TL_inputMediaVenue extends InputMedia {
}
public static class TL_inputMediaUploadedDocument extends InputMedia {
}
public static class TL_inputMediaPhoto extends InputMedia {
public InputPhoto id;
}
public static class InputStickerSet extends TLObject {
public long id;
public long access_hash;
public String short_name;
}
public static class TL_inputStickerSetEmpty extends InputStickerSet {
}
public static class TL_inputStickerSetID extends InputStickerSet {
}
public static class TL_inputStickerSetShortName extends InputStickerSet {
}
public static class UserStatus extends TLObject {
public int expires;
}
public static class TL_userStatusLastWeek extends UserStatus {
}
public static class TL_userStatusLastMonth extends UserStatus {
}
public static class TL_userStatusRecently extends UserStatus {
}
public static class Update extends TLObject {
public int chat_id;
public int user_id;
public int date;
public int version;
public DraftMessage draft;
public int pts;
public byte[] data;
public int flags;
public String query;
public String offset;
public int channel_id;
public SendMessageAction action;
public boolean blocked;
public String location;
public boolean enabled;
public ArrayList<PrivacyRule> rules = new ArrayList<>();
public UserStatus status;
public int views;
public String type;
public MessageMedia media;
public String first_name;
public String last_name;
public String username;
public ArrayList<Integer> messages = new ArrayList<>();
public String phone;
public WebPage webpage;
public EncryptedChat chat;
public ArrayList<Long> order = new ArrayList<>();
public UserProfilePhoto photo;
public boolean previous;
}
public static class InputEncryptedFile extends TLObject {
public long id;
public int parts;
}
public static class UserProfilePhoto extends TLObject {
public FileLocation photo_small;
public FileLocation photo_big;
}
public static class MessageEntity extends TLObject {
public int offset;
public int length;
public String url;
public String language;
}
public static class TL_messageEntityTextUrl extends MessageEntity {
}
public static class TL_messageEntityEmail extends MessageEntity {
}
public static class TL_messageEntityPre extends MessageEntity {
}
public static class TL_messageEntityUrl extends MessageEntity {
}
public static class TL_messageEntityItalic extends MessageEntity {
}
public static class TL_messageEntityMention extends MessageEntity {
}
public static class TL_messageEntityMentionName extends MessageEntity {
public int user_id;
}
public static class TL_inputMessageEntityMentionName extends MessageEntity {
public InputUser user_id;
}
public static class TL_messageEntityBold extends MessageEntity {
}
public static class TL_messageEntityHashtag extends MessageEntity {
}
public static class TL_messageEntityCode extends MessageEntity {
}
public static class Photo extends TLObject {
public long id;
public long access_hash;
public int user_id;
public int date;
public String caption;
@@ -530,49 +190,23 @@ public class TLRPC {
public static class TL_photoEmpty extends Photo {
}
public static class TL_contact extends TLObject {
public int user_id;
public boolean mutual;
}
public static class ChatParticipants extends TLObject {
public int flags;
public int chat_id;
public ArrayList<ChatParticipant> participants = new ArrayList<>();
public int version;
}
public static class ChatParticipant extends TLObject {
public int user_id;
public int date;
}
public static class Chat extends TLObject {
public int flags;
public boolean creator;
public boolean kicked;
public boolean deactivated;
public int id;
public String title;
public final ChatPhoto photo = null;
public int date;
public int version;
public boolean editor;
public final boolean broadcast = false;
public boolean verified;
public final boolean megagroup = false;
public boolean left;
public final int version = 0;
public final boolean editor = false;
public String username;
public boolean restricted;
public boolean signatures;
public String restriction_reason;
public boolean min;
public String address;
public final boolean signatures = false;
public final boolean min = false;
public final String address = null;
}
public static class StickerSet extends TLObject {
public long id;
public long access_hash;
public String title;
public int flags;
public boolean installed;
@@ -582,33 +216,8 @@ public class TLRPC {
public int hash;
}
public static class MessagesFilter extends TLObject {
}
public static class TL_inputMessagesFilterDocument extends MessagesFilter {
}
public static class TL_inputMessagesFilterMusic extends MessagesFilter {
}
public static class TL_inputMessagesFilterUrl extends MessagesFilter {
}
public static class TL_inputMessagesFilterVoice extends MessagesFilter {
}
public static class TL_inputMessagesFilterEmpty extends MessagesFilter {
}
public static class TL_inputMessagesFilterPhotoVideo extends MessagesFilter {
}
public static class TL_messageFwdHeader extends TLObject {
public int flags;
public int from_id;
public int date;
public int channel_id;
public int channel_post;
public String m_name;
}
public static class FileLocation extends TLObject {
@@ -627,7 +236,7 @@ public class TLRPC {
}
public static class PhotoSize extends TLObject {
public String type;
public String type; // s, m, x, y, w for small, medium, ... widest
public FileLocation location;
public int w;
public int h;
@@ -651,26 +260,6 @@ public class TLRPC {
public String name;
}
public static class Updates extends TLObject {
public ArrayList<Update> updates = new ArrayList<>();
public ArrayList<User> users = new ArrayList<>();
public ArrayList<Chat> chats = new ArrayList<>();
public int date;
public int seq;
public int flags;
public boolean out;
public boolean mentioned;
public boolean silent;
public int id;
public int user_id;
public String message;
public int pts;
public MessageMedia media;
public Update update;
public int from_id;
public int chat_id;
}
public static class WallPaper extends TLObject {
public int id;
public String title;
@@ -685,140 +274,51 @@ public class TLRPC {
public static class TL_wallPaperSolid extends WallPaper {
}
public static class TL_stickerPack extends TLObject {
public ArrayList<Long> documents = new ArrayList<>();
}
public static class InputPhoto extends TLObject {
public long id;
public long access_hash;
}
public static class TL_inputPhoto extends InputPhoto {
}
public static class InputPeer extends TLObject {
public int user_id;
public long access_hash;
public int chat_id;
public int channel_id;
}
public static class TL_inputPeerUser extends InputPeer {
}
public static class TL_inputPeerEmpty extends InputPeer {
}
public static class TL_topPeer extends TLObject {
public Peer peer;
}
public static class TL_messages_search extends TLObject {
public int flags;
public InputPeer peer;
public String q;
public MessagesFilter filter;
public int offset;
public int max_id;
public int limit;
}
public static class TL_messages_searchGlobal extends TLObject {
public String q;
public int offset_date;
public InputPeer offset_peer;
public int offset_id;
public int limit;
}
public static class MessageMedia extends TLObject {
public byte[] bytes;
public Photo photo;
public GeoPoint geo;
public String title;
public String address;
public String provider;
public String venue_id;
public Document document;
public String caption;
public String phone_number;
public String first_name;
public String last_name;
public int user_id;
public WebPage webpage;
}
public static class EncryptedChat extends TLObject {
public int id;
public int date;
public byte[] nonce;
public byte[] auth_key; //custom
public int user_id; //custom
public int ttl; //custom
public int layer; //custom
public byte[] key_hash; //custom
}
public static class Message extends TLObject {
public int id;
public int from_id;
public Peer to_id;
public int date;
public MessageAction action;
public int reply_to_msg_id;
public long reply_to_random_id;
public final int reply_to_msg_id = 0;
public String message;
public MessageMedia media;
public int flags;
public boolean mentioned;
public boolean media_unread;
public boolean out;
public boolean unread;
public ArrayList<MessageEntity> entities = new ArrayList<>();
public String via_bot_name;
public int views;
public int edit_date;
public boolean silent;
public boolean post;
public final int views = 0;
public final boolean silent = false;
public final boolean post = false;// ? true=avatar wird in gruppen nicht angezeigt, wird aber in isFromUser() auch überprüft...
public TL_messageFwdHeader fwd_from;
public int via_bot_id;
public int send_state = 0; //custom
public String attachPath = ""; //custom
public HashMap<String, String> params; //custom
public long random_id; //custom
public long dialog_id; //custom
public int ttl; //custom
public int destroyTime; //custom
public int layer; //custom
public TLRPC.Message replyMessage; //custom
public final int layer = 0; //custom
public boolean created_by_mr;
}
public static class TL_messageEmpty extends Message {
}
public static class TL_message extends Message {
}
public static class TL_messageService extends Message {
}
public static class TL_dialog extends TLObject {
final public int flags = 0;
public Peer peer;
public int top_message;
public int unread_count;
public PeerNotifySettings notify_settings;
public int pts;
public final int top_message = 0;
public final int unread_count = 0;
public final int pts = 0;
public DraftMessage draft;
public int last_message_date; //custom
public long id; //custom
}
public static class TL_chatEmpty extends Chat {
}
public static class Vector extends TLObject {
public ArrayList<Object> objects = new ArrayList<>();
}
}
@@ -1,172 +0,0 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package com.b44t.messenger;
// ChooserTargetService, see https://developer.android.com/reference/android/service/chooser/ChooserTargetService.html
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ComponentName;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Build;
import android.service.chooser.ChooserTarget;
import android.service.chooser.ChooserTargetService;
import com.b44t.ui.LaunchActivity;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
@TargetApi(Build.VERSION_CODES.M)
public class TgChooserTargetService extends ChooserTargetService {
@Override
public List<ChooserTarget> onGetChooserTargets(ComponentName targetActivityName, IntentFilter matchedFilter) {
final List<ChooserTarget> targets = new ArrayList<>();
if (!UserConfig.isClientActivated()) {
return targets;
}
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
if (!preferences.getBoolean("direct_share", true)) {
return targets;
}
ImageLoader imageLoader = ImageLoader.getInstance();
final Semaphore semaphore = new Semaphore(0);
final ComponentName componentName = new ComponentName(getPackageName(), LaunchActivity.class.getCanonicalName());
/*
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
ArrayList<Integer> dialogs = new ArrayList<>();
ArrayList<TLRPC.Chat> chats = new ArrayList<>();
ArrayList<TLRPC.User> users = new ArrayList<>();
try {
ArrayList<Integer> usersToLoad = new ArrayList<>();
usersToLoad.add(UserConfig.getClientUserId());
ArrayList<Integer> chatsToLoad = new ArrayList<>();
SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT did FROM dialogs ORDER BY date DESC LIMIT %d,%d", 0, 30));
while (cursor.next()) {
long id = cursor.longValue(0);
int lower_id = (int) id;
int high_id = (int) (id >> 32);
if (lower_id != 0) {
if (high_id == 1) {
continue;
} else {
if (lower_id > 0) {
if (!usersToLoad.contains(lower_id)) {
usersToLoad.add(lower_id);
}
} else {
if (!chatsToLoad.contains(-lower_id)) {
chatsToLoad.add(-lower_id);
}
}
}
} else {
continue;
}
dialogs.add(lower_id);
if (dialogs.size() == 8) {
break;
}
}
cursor.dispose();
if (!chatsToLoad.isEmpty()) {
//MessagesStorage.getInstance().getChatsInternal(TextUtils.join(",", chatsToLoad), chats);
}
if (!usersToLoad.isEmpty()) {
//MessagesStorage.getInstance().getUsersInternal(TextUtils.join(",", usersToLoad), users);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
for (int a = 0; a < dialogs.size(); a++) {
Bundle extras = new Bundle();
Icon icon = null;
String name = null;
int id = dialogs.get(a);
if (id > 0) {
for (int b = 0; b < users.size(); b++) {
TLRPC.User user = users.get(b);
if (user.id == id) {
if (!user.bot) {
extras.putLong("dialogId", (long) id);
if (user.photo != null && user.photo.photo_small != null) {
icon = createRoundBitmap(FileLoader.getPathToAttach(user.photo.photo_small, true));
}
name = ContactsController.formatName(user.first_name, user.last_name);
}
break;
}
}
} else {
for (int b = 0; b < chats.size(); b++) {
TLRPC.Chat chat = chats.get(b);
if (chat.id == -id) {
if (!ChatObject.isNotInChat(chat) && (!ChatObject.isChannel(chat) || chat.megagroup)) {
extras.putLong("dialogId", (long) id);
if (chat.photo != null && chat.photo.photo_small != null) {
icon = createRoundBitmap(FileLoader.getPathToAttach(chat.photo.photo_small, true));
}
name = chat.title;
}
break;
}
}
}
if (name != null) {
if (icon == null) {
icon = Icon.createWithResource(ApplicationLoader.applicationContext, R.drawable.logo_avatar);
}
targets.add(new ChooserTarget(name, icon, 1.0f, componentName, extras));
}
}
semaphore.release();
}
});
*/
try {
semaphore.acquire();
} catch (Exception e) {
FileLog.e("messenger", e);
}
return targets;
}
/*
private Icon createRoundBitmap(File path) {
try {
Bitmap bitmap = BitmapFactory.decodeFile(path.toString());
if (bitmap != null) {
Bitmap result = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
result.eraseColor(Color.TRANSPARENT);
Canvas canvas = new Canvas(result);
BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
if (roundPaint == null) {
roundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bitmapRect = new RectF();
}
roundPaint.setShader(shader);
bitmapRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
canvas.drawRoundRect(bitmapRect, bitmap.getWidth(), bitmap.getHeight(), roundPaint);
return Icon.createWithBitmap(result);
}
} catch (Throwable e) {
FileLog.e("messenger", e);
}
return null;
}
*/
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -17,15 +32,8 @@ import java.io.File;
public class UserConfig {
private static TLRPC.User currentUser;
public static boolean registeredForPush;
public static String pushString = "";
public static int lastSendMessageId = -210000;
public static int lastLocalId = -210000;
public static int lastBroadcastId = -1;
public static String contactsHash = "";
public static boolean blockedUsersLoaded;
private final static Object sync = new Object();
public static boolean saveIncomingPhotos;
public static String passcodeHash = "";
public static byte[] passcodeSalt = new byte[0];
public static boolean appLocked;
@@ -34,26 +42,6 @@ public class UserConfig {
public static int lastPauseTime;
public static boolean isWaitingForPasscodeEnter;
public static boolean useFingerprint = true;
public static String lastUpdateVersion;
public static int lastContactsSyncTime;
public static int lastHintsSyncTime;
public static boolean draftsLoaded;
public static int migrateOffsetId = -1;
public static int migrateOffsetDate = -1;
public static int migrateOffsetUserId = -1;
public static int migrateOffsetChatId = -1;
public static int migrateOffsetChannelId = -1;
public static long migrateOffsetAccess = -1;
public static int getNewMessageId() {
int id;
synchronized (sync) {
id = lastSendMessageId;
lastSendMessageId--;
}
return id;
}
public static void saveConfig(boolean withFile) {
saveConfig(withFile, null);
@@ -64,34 +52,14 @@ public class UserConfig {
try {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("userconfing", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("registeredForPush", registeredForPush);
editor.putString("pushString2", pushString);
editor.putInt("lastSendMessageId", lastSendMessageId);
editor.putInt("lastLocalId", lastLocalId);
editor.putString("contactsHash", contactsHash);
editor.putBoolean("saveIncomingPhotos", saveIncomingPhotos);
editor.putInt("lastBroadcastId", lastBroadcastId);
editor.putBoolean("blockedUsersLoaded", blockedUsersLoaded);
editor.putString("passcodeHash1", passcodeHash);
editor.putString("passcodeSalt", passcodeSalt.length > 0 ? Base64.encodeToString(passcodeSalt, Base64.DEFAULT) : "");
editor.putBoolean("appLocked", appLocked);
editor.putInt("passcodeType", passcodeType);
editor.putInt("autoLockIn", autoLockIn);
editor.putInt("lastPauseTime", lastPauseTime);
editor.putString("lastUpdateVersion2", lastUpdateVersion);
editor.putInt("lastContactsSyncTime", lastContactsSyncTime);
editor.putBoolean("useFingerprint", useFingerprint);
editor.putInt("lastHintsSyncTime", lastHintsSyncTime);
editor.putBoolean("draftsLoaded", draftsLoaded);
editor.putInt("migrateOffsetId", migrateOffsetId);
if (migrateOffsetId != -1) {
editor.putInt("migrateOffsetDate", migrateOffsetDate);
editor.putInt("migrateOffsetUserId", migrateOffsetUserId);
editor.putInt("migrateOffsetChatId", migrateOffsetChatId);
editor.putInt("migrateOffsetChannelId", migrateOffsetChannelId);
editor.putLong("migrateOffsetAccess", migrateOffsetAccess);
}
/*
if (currentUser != null) {
@@ -107,7 +75,7 @@ public class UserConfig {
}
*/
editor.commit();
editor.apply();
if (oldFile != null) {
oldFile.delete();
}
@@ -216,33 +184,13 @@ public class UserConfig {
*/
{
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("userconfing", Context.MODE_PRIVATE);
registeredForPush = preferences.getBoolean("registeredForPush", false);
pushString = preferences.getString("pushString2", "");
lastSendMessageId = preferences.getInt("lastSendMessageId", -210000);
lastLocalId = preferences.getInt("lastLocalId", -210000);
contactsHash = preferences.getString("contactsHash", "");
saveIncomingPhotos = preferences.getBoolean("saveIncomingPhotos", false);
lastBroadcastId = preferences.getInt("lastBroadcastId", -1);
blockedUsersLoaded = preferences.getBoolean("blockedUsersLoaded", false);
passcodeHash = preferences.getString("passcodeHash1", "");
appLocked = preferences.getBoolean("appLocked", false);
passcodeType = preferences.getInt("passcodeType", 0);
autoLockIn = preferences.getInt("autoLockIn", 60 * 60);
lastPauseTime = preferences.getInt("lastPauseTime", 0);
useFingerprint = preferences.getBoolean("useFingerprint", true);
lastUpdateVersion = preferences.getString("lastUpdateVersion2", "3.5");
lastContactsSyncTime = preferences.getInt("lastContactsSyncTime", (int) (System.currentTimeMillis() / 1000) - 23 * 60 * 60);
lastHintsSyncTime = preferences.getInt("lastHintsSyncTime", (int) (System.currentTimeMillis() / 1000) - 25 * 60 * 60);
draftsLoaded = preferences.getBoolean("draftsLoaded", false);
migrateOffsetId = preferences.getInt("migrateOffsetId", 0);
if (migrateOffsetId != -1) {
migrateOffsetDate = preferences.getInt("migrateOffsetDate", 0);
migrateOffsetUserId = preferences.getInt("migrateOffsetUserId", 0);
migrateOffsetChatId = preferences.getInt("migrateOffsetChatId", 0);
migrateOffsetChannelId = preferences.getInt("migrateOffsetChannelId", 0);
migrateOffsetAccess = preferences.getLong("migrateOffsetAccess", 0);
}
/*
String user = preferences.getString("user", null);
@@ -1,39 +1,31 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
public class UserObject {
public static boolean isDeleted(TLRPC.User user) {
return user == null || user.deleted;
}
public static boolean isUserSelf(TLRPC.User user) {
return user.self;
}
public static String getUserName(TLRPC.User user) {
if (user == null || isDeleted(user)) {
return "ErrName";
}
String name = ContactsController.formatName(user.first_name, user.last_name);
return name.length() != 0 || user.phone == null || user.phone.length() == 0 ? name : ""/*PhoneFormat.getInstance().format("+" + user.phone)*/;
}
public static String getFirstName(TLRPC.User user) {
if (user == null || isDeleted(user)) {
return "DELETED";
}
String name = user.first_name;
if (name == null || name.length() == 0) {
name = user.last_name;
}
return name != null && name.length() > 0 ? name : "ErrFirstName";
return "ErrName"; // use MrContact.getName() instead
}
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -1,57 +1,37 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
import java.util.Locale;
public class VideoEditedInfo {
public long startTime;
public long endTime;
public int rotationValue;
public int originalWidth;
public int originalHeight;
public int originalBitrate;
public int resultWidth;
public int resultHeight;
public int bitrate;
public int resultBitrate;
public String originalPath;
public String getString() {
return String.format(Locale.US, "-1_%d_%d_%d_%d_%d_%d_%d_%d_%s", startTime, endTime, rotationValue, originalWidth, originalHeight, bitrate, resultWidth, resultHeight, originalPath);
}
public boolean parseString(String string) {
if (string.length() < 6) {
return false;
}
try {
String args[] = string.split("_");
if (args.length >= 10) {
startTime = Long.parseLong(args[1]);
endTime = Long.parseLong(args[2]);
rotationValue = Integer.parseInt(args[3]);
originalWidth = Integer.parseInt(args[4]);
originalHeight = Integer.parseInt(args[5]);
bitrate = Integer.parseInt(args[6]);
resultWidth = Integer.parseInt(args[7]);
resultHeight = Integer.parseInt(args[8]);
for (int a = 9; a < args.length; a++) {
if (originalPath == null) {
originalPath = args[a];
} else {
originalPath += "_" + args[a];
}
}
}
return true;
} catch (Exception e) {
FileLog.e("messenger", e);
}
return false;
}
}
@@ -1,80 +0,0 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package com.b44t.messenger;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
public class VideoEncodingService extends Service implements NotificationCenter.NotificationCenterDelegate {
private NotificationCompat.Builder builder = null;
private String path = null;
private int currentProgress = 0;
public VideoEncodingService() {
super();
NotificationCenter.getInstance().addObserver(this, NotificationCenter.FileUploadProgressChanged);
NotificationCenter.getInstance().addObserver(this, NotificationCenter.stopEncodingService);
}
public IBinder onBind(Intent arg2) {
return null;
}
public void onDestroy() {
stopForeground(true);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.FileUploadProgressChanged);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.stopEncodingService);
FileLog.e("messenger", "destroy video service");
}
@Override
public void didReceivedNotification(int id, Object... args) {
if (id == NotificationCenter.FileUploadProgressChanged) {
String fileName = (String)args[0];
if (path != null && path.equals(fileName)) {
Float progress = (Float) args[1];
Boolean enc = (Boolean) args[2];
currentProgress = (int)(progress * 100);
builder.setProgress(100, currentProgress, currentProgress == 0);
NotificationManagerCompat.from(ApplicationLoader.applicationContext).notify(4, builder.build());
}
} else if (id == NotificationCenter.stopEncodingService) {
String filepath = (String)args[0];
if (filepath == null || filepath.equals(path)) {
stopSelf();
}
}
}
public int onStartCommand(Intent intent, int flags, int startId) {
path = intent.getStringExtra("path");
if (path == null) {
stopSelf();
return Service.START_NOT_STICKY;
}
FileLog.e("messenger", "start video service");
if (builder == null) {
builder = new NotificationCompat.Builder(ApplicationLoader.applicationContext);
builder.setSmallIcon(android.R.drawable.stat_sys_upload);
builder.setWhen(System.currentTimeMillis());
builder.setContentTitle(LocaleController.getString("AppName", R.string.AppName));
builder.setTicker(LocaleController.getString("SendingVideo", R.string.SendingVideo));
builder.setContentText(LocaleController.getString("SendingVideo", R.string.SendingVideo));
}
currentProgress = 0;
builder.setProgress(100, currentProgress, currentProgress == 0);
startForeground(4, builder.build());
NotificationManagerCompat.from(ApplicationLoader.applicationContext).notify(4, builder.build());
return Service.START_NOT_STICKY;
}
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger;
@@ -32,7 +47,8 @@ public class WearReplyReceiver extends BroadcastReceiver {
if (dialog_id == 0 || max_id == 0) {
return;
}
SendMessagesHelper.getInstance().sendMessageText(text.toString(), dialog_id, null, null, true, null, null);
MessagesController.getInstance().markDialogAsRead(dialog_id, max_id, max_id, 0, true, false);
SendMessagesHelper.getInstance().sendMessageText(text.toString(), dialog_id, null);
MrMailbox.markseenChat((int)dialog_id);
NotificationsController.getInstance().removeSeenMessages();
}
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger.browser;
@@ -1,208 +0,0 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package com.b44t.messenger.query;
import android.text.TextUtils;
import com.b44t.messenger.MrChat;
import com.b44t.messenger.MrMailbox;
import com.b44t.messenger.TLRPC;
import java.util.ArrayList;
public class DraftQuery {
/* EDIT BY MR
private static HashMap<Long, TLRPC.DraftMessage> drafts = new HashMap<>();
private static HashMap<Long, TLRPC.Message> draftMessages = new HashMap<>();
private static boolean inTransaction;
private static SharedPreferences preferences;
private static boolean loadingDrafts;
static {
preferences = ApplicationLoader.applicationContext.getSharedPreferences("drafts", Activity.MODE_PRIVATE);
Map<String, ?> values = preferences.getAll();
for (Map.Entry<String, ?> entry : values.entrySet()) {
try {
String key = entry.getKey();
long did = Utilities.parseLong(key);
byte[] bytes = Utilities.hexToBytes((String) entry.getValue());
SerializedData serializedData = new SerializedData(bytes);
if (key.startsWith("r_")) {
TLRPC.Message message = TLRPC.Message.TLdeserialize(serializedData, serializedData.readInt32(true), true);
if (message != null) {
draftMessages.put(did, message);
}
} else {
TLRPC.DraftMessage draftMessage = TLRPC.DraftMessage.TLdeserialize(serializedData, serializedData.readInt32(true), true);
if (draftMessage != null) {
drafts.put(did, draftMessage);
}
}
} catch (Exception e) {
//igonre
}
}
}
*/
public static void cleanup() {
/* EDIT BY MR
drafts.clear();
draftMessages.clear();
preferences.edit().clear().commit();
*/
}
public static TLRPC.DraftMessage getDraft(long did) { // returns null for "no draft"
MrChat mrChat = MrMailbox.getChat((int)did);
if( mrChat.getId() == 0 ) {
return null;
}
TLRPC.DraftMessage ret = new TLRPC.DraftMessage();
ret.message = mrChat.getDraft();
ret.date = (int)mrChat.getDraftTimestamp();
ret.reply_to_msg_id = mrChat.getDraftReplyToMsgId();
return ret;
}
public static TLRPC.Message getDraftMessage(long did) { // returns null for "no draft"
MrChat mrChat = MrMailbox.getChat((int)did);
if( mrChat.getId() == 0 ) {
return null;
}
TLRPC.Message ret = new TLRPC.Message();
ret.message = mrChat.getDraft();
ret.date = (int)mrChat.getDraftTimestamp();
ret.reply_to_msg_id = mrChat.getDraftReplyToMsgId();
return ret;
}
private static void saveDraft__(long did, String message, long replyToMessageId) // message may be null
{
MrChat mrChat = MrMailbox.getChat((int)did);
mrChat.setDraft(message, replyToMessageId);
}
public static void saveDraft(long did, CharSequence message, ArrayList<TLRPC.MessageEntity> entities, TLRPC.Message replyToMessage, boolean noWebpage) {
saveDraft(did, message, entities, replyToMessage, noWebpage, false);
}
public static void saveDraft(long did, CharSequence message, ArrayList<TLRPC.MessageEntity> entities, TLRPC.Message replyToMessage, boolean noWebpage, boolean clean) {
if( message == null || TextUtils.isEmpty(message) ) {
saveDraft__(did, null, 0);
}
else {
saveDraft__(did, message.toString(), 0);
}
/* EDIT BY MR
TLRPC.DraftMessage draftMessage;
if (!TextUtils.isEmpty(message) || replyToMessage != null) {
draftMessage = new TLRPC.TL_draftMessage();
} else {
draftMessage = new TLRPC.TL_draftMessageEmpty();
}
draftMessage.date = (int) (System.currentTimeMillis() / 1000);
draftMessage.message = message == null ? "" : message.toString();
draftMessage.no_webpage = noWebpage;
if (replyToMessage != null) {
draftMessage.reply_to_msg_id = replyToMessage.id;
draftMessage.flags |= 1;
}
if (entities != null && !entities.isEmpty()) {
draftMessage.entities = entities;
draftMessage.flags |= 8;
}
TLRPC.DraftMessage currentDraft = drafts.get(did);
if (!clean) {
if (currentDraft != null && currentDraft.message.equals(draftMessage.message) && currentDraft.reply_to_msg_id == draftMessage.reply_to_msg_id && currentDraft.no_webpage == draftMessage.no_webpage ||
currentDraft == null && TextUtils.isEmpty(draftMessage.message) && draftMessage.reply_to_msg_id == 0) {
return;
}
}
saveDraft(did, draftMessage, replyToMessage, false);
int lower_id = (int) did;
if (lower_id != 0) {
TLRPC.TL_messages_saveDraft req = new TLRPC.TL_messages_saveDraft();
req.peer = MessagesController.getInputPeer(lower_id);
if (req.peer == null) {
return;
}
req.message = draftMessage.message;
req.no_webpage = draftMessage.no_webpage;
req.reply_to_msg_id = draftMessage.reply_to_msg_id;
req.entities = draftMessage.entities;
req.flags = draftMessage.flags;
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
}
});
}
MessagesController.getInstance().sortDialogs(null);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload);
*/
}
/* EDIT BY MR
private static void saveDraftReplyMessage(final long did, final TLRPC.Message message) {
if (message == null) {
return;
}
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
TLRPC.DraftMessage draftMessage = drafts.get(did);
if (draftMessage != null && draftMessage.reply_to_msg_id == message.id) {
draftMessages.put(did, message);
SerializedData serializedData = new SerializedData(message.getObjectSize());
message.serializeToStream(serializedData);
preferences.edit().putString("r_" + did, Utilities.bytesToHex(serializedData.toByteArray())).commit();
NotificationCenter.getInstance().postNotificationName(NotificationCenter.newDraftReceived, did);
}
}
});
}
*/
public static void cleanDraft(long did, boolean replyOnly) {
saveDraft__(did, null, 0);
/* EDIT BY MR
TLRPC.DraftMessage draftMessage = drafts.get(did);
if (draftMessage == null) {
return;
}
if (!replyOnly) {
drafts.remove(did);
draftMessages.remove(did);
preferences.edit().remove("" + did).remove("r_" + did).commit();
MessagesController.getInstance().sortDialogs(null);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload);
} else if (draftMessage.reply_to_msg_id != 0) {
draftMessage.reply_to_msg_id = 0;
draftMessage.flags &= ~1;
saveDraft(did, draftMessage.message, draftMessage.entities, null, draftMessage.no_webpage, true);
}
*/
}
/* EDIT BY MR
public static void beginTransaction() {
inTransaction = true;
}
public static void endTransaction() {
inTransaction = false;
}
*/
}
@@ -1,445 +0,0 @@
/*
* This is the source code of Telegram for Android v. 3.x.x
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package com.b44t.messenger.query;
import android.text.Spannable;
import com.b44t.messenger.MessageObject;
import com.b44t.messenger.MessagesController;
import com.b44t.messenger.Utilities;
import com.b44t.messenger.TLRPC;
import com.b44t.ui.Components.URLSpanUserMention;
import java.util.ArrayList;
import java.util.HashMap;
public class MessagesQuery {
public static MessageObject loadPinnedMessage(final int channelId, final int mid, boolean useQueue) {
if (useQueue) {
/*
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
loadPinnedMessageInternal(channelId, mid, false);
}
});
*/
} else {
return loadPinnedMessageInternal(channelId, mid, true);
}
return null;
}
private static MessageObject loadPinnedMessageInternal(final int channelId, final int mid, boolean returnValue) {
/*
try {
long messageId = ((long) mid) | ((long) channelId) << 32;
TLRPC.Message result = null;
final ArrayList<TLRPC.User> users = new ArrayList<>();
final ArrayList<TLRPC.Chat> chats = new ArrayList<>();
ArrayList<Integer> usersToLoad = new ArrayList<>();
ArrayList<Integer> chatsToLoad = new ArrayList<>();
SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT data, mid, date FROM messages WHERE mid = %d", messageId));
if (cursor.next()) {
NativeByteBuffer data = cursor.byteBufferValue(0);
if (data != null) {
result = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
data.reuse();
result.id = cursor.intValue(1);
result.date = cursor.intValue(2);
result.dialog_id = -channelId;
MessagesStorage.addUsersAndChatsFromMessage(result, usersToLoad, chatsToLoad);
}
}
cursor.dispose();
if (result == null) {
cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT data FROM chat_pinned WHERE uid = %d", channelId));
if (cursor.next()) {
NativeByteBuffer data = cursor.byteBufferValue(0);
if (data != null) {
result = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
data.reuse();
if (result.id != mid) {
result = null;
} else {
result.dialog_id = -channelId;
MessagesStorage.addUsersAndChatsFromMessage(result, usersToLoad, chatsToLoad);
}
}
}
cursor.dispose();
}
if (result == null) {
final TLRPC.TL_channels_getMessages req = new TLRPC.TL_channels_getMessages();
req.channel = MessagesController.getInputChannel(channelId);
req.id.add(mid);
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
boolean ok = false;
if (error == null) {
TLRPC.messages_Messages messagesRes = (TLRPC.messages_Messages) response;
if (!messagesRes.messages.isEmpty()) {
ImageLoader.saveMessagesThumbs(messagesRes.messages);
broadcastPinnedMessage(messagesRes.messages.get(0), messagesRes.users, messagesRes.chats, false, false);
//MessagesStorage.getInstance().putUsersAndChats(messagesRes.users, messagesRes.chats, true, true);
savePinnedMessage(messagesRes.messages.get(0));
ok = true;
}
}
if (!ok) {
MessagesStorage.getInstance().updateChannelPinnedMessage(channelId, 0);
}
}
});
} else {
if (returnValue) {
return broadcastPinnedMessage(result, users, chats, true, returnValue);
} else {
if (!usersToLoad.isEmpty()) {
//MessagesStorage.getInstance().getUsersInternal(TextUtils.join(",", usersToLoad), users);
}
if (!chatsToLoad.isEmpty()) {
//MessagesStorage.getInstance().getChatsInternal(TextUtils.join(",", chatsToLoad), chats);
}
broadcastPinnedMessage(result, users, chats, true, false);
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
*/
return null;
}
/*
private static MessageObject broadcastPinnedMessage(final TLRPC.Message result, final ArrayList<TLRPC.User> users, final ArrayList<TLRPC.Chat> chats, final boolean isCache, boolean returnValue) {
final HashMap<Integer, TLRPC.User> usersDict = new HashMap<>();
for (int a = 0; a < users.size(); a++) {
TLRPC.User user = users.get(a);
usersDict.put(user.id, user);
}
final HashMap<Integer, TLRPC.Chat> chatsDict = new HashMap<>();
for (int a = 0; a < chats.size(); a++) {
TLRPC.Chat chat = chats.get(a);
chatsDict.put(chat.id, chat);
}
if (returnValue) {
return new MessageObject(result, usersDict, chatsDict, false);
} else {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
MessagesController.getInstance().putUsers(users, isCache);
MessagesController.getInstance().putChats(chats, isCache);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.didLoadedPinnedMessage, new MessageObject(result, usersDict, chatsDict, false));
}
});
}
return null;
}
*/
public static void loadReplyMessagesForMessages(final ArrayList<MessageObject> messages, final long dialogId) {
if ((int) dialogId == 0) {
final ArrayList<Long> replyMessages = new ArrayList<>();
final HashMap<Long, ArrayList<MessageObject>> replyMessageRandomOwners = new HashMap<>();
final StringBuilder stringBuilder = new StringBuilder();
for (int a = 0; a < messages.size(); a++) {
MessageObject messageObject = messages.get(a);
if (messageObject.isReply() && messageObject.replyMessageObject == null) {
Long id = messageObject.messageOwner.reply_to_random_id;
if (stringBuilder.length() > 0) {
stringBuilder.append(',');
}
stringBuilder.append(id);
ArrayList<MessageObject> messageObjects = replyMessageRandomOwners.get(id);
if (messageObjects == null) {
messageObjects = new ArrayList<>();
replyMessageRandomOwners.put(id, messageObjects);
}
messageObjects.add(messageObject);
if (!replyMessages.contains(id)) {
replyMessages.add(id);
}
}
}
if (replyMessages.isEmpty()) {
return;
}
/*
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
try {
SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT m.data, m.mid, m.date, r.random_id FROM randoms as r INNER JOIN messages as m ON r.mid = m.mid WHERE r.random_id IN(%s)", TextUtils.join(",", replyMessages)));
while (cursor.next()) {
NativeByteBuffer data = cursor.byteBufferValue(0);
if (data != null) {
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
data.reuse();
message.id = cursor.intValue(1);
message.date = cursor.intValue(2);
message.dialog_id = dialogId;
ArrayList<MessageObject> arrayList = replyMessageRandomOwners.remove(cursor.longValue(3));
if (arrayList != null) {
MessageObject messageObject = new MessageObject(message, null, null, false);
for (int b = 0; b < arrayList.size(); b++) {
MessageObject object = arrayList.get(b);
object.replyMessageObject = messageObject;
object.messageOwner.reply_to_msg_id = messageObject.getId();
}
}
}
}
cursor.dispose();
if (!replyMessageRandomOwners.isEmpty()) {
for (HashMap.Entry<Long, ArrayList<MessageObject>> entry : replyMessageRandomOwners.entrySet()) {
ArrayList<MessageObject> arrayList = entry.getValue();
for (int a = 0; a < arrayList.size(); a++) {
arrayList.get(a).messageOwner.reply_to_random_id = 0;
}
}
}
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.didLoadedReplyMessages, dialogId);
}
});
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
});
*/
} else {
final ArrayList<Integer> replyMessages = new ArrayList<>();
final HashMap<Integer, ArrayList<MessageObject>> replyMessageOwners = new HashMap<>();
final StringBuilder stringBuilder = new StringBuilder();
int channelId = 0;
for (int a = 0; a < messages.size(); a++) {
MessageObject messageObject = messages.get(a);
if (messageObject.getId() > 0 && messageObject.isReply() && messageObject.replyMessageObject == null) {
Integer id = messageObject.messageOwner.reply_to_msg_id;
long messageId = id;
if (messageObject.messageOwner.to_id.channel_id != 0) {
messageId |= ((long) messageObject.messageOwner.to_id.channel_id) << 32;
channelId = messageObject.messageOwner.to_id.channel_id;
}
if (stringBuilder.length() > 0) {
stringBuilder.append(',');
}
stringBuilder.append(messageId);
ArrayList<MessageObject> messageObjects = replyMessageOwners.get(id);
if (messageObjects == null) {
messageObjects = new ArrayList<>();
replyMessageOwners.put(id, messageObjects);
}
messageObjects.add(messageObject);
if (!replyMessages.contains(id)) {
replyMessages.add(id);
}
}
}
if (replyMessages.isEmpty()) {
return;
}
/*
final int channelIdFinal = channelId;
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
try {
final ArrayList<TLRPC.Message> result = new ArrayList<>();
final ArrayList<TLRPC.User> users = new ArrayList<>();
final ArrayList<TLRPC.Chat> chats = new ArrayList<>();
ArrayList<Integer> usersToLoad = new ArrayList<>();
ArrayList<Integer> chatsToLoad = new ArrayList<>();
SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT data, mid, date FROM messages WHERE mid IN(%s)", stringBuilder.toString()));
while (cursor.next()) {
NativeByteBuffer data = cursor.byteBufferValue(0);
if (data != null) {
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
data.reuse();
message.id = cursor.intValue(1);
message.date = cursor.intValue(2);
message.dialog_id = dialogId;
MessagesStorage.addUsersAndChatsFromMessage(message, usersToLoad, chatsToLoad);
result.add(message);
replyMessages.remove((Integer) message.id);
}
}
cursor.dispose();
if (!usersToLoad.isEmpty()) {
//MessagesStorage.getInstance().getUsersInternal(TextUtils.join(",", usersToLoad), users);
}
if (!chatsToLoad.isEmpty()) {
//MessagesStorage.getInstance().getChatsInternal(TextUtils.join(",", chatsToLoad), chats);
}
broadcastReplyMessages(result, replyMessageOwners, users, chats, dialogId, true);
if (!replyMessages.isEmpty()) {
if (channelIdFinal != 0) {
final TLRPC.TL_channels_getMessages req = new TLRPC.TL_channels_getMessages();
req.channel = MessagesController.getInputChannel(channelIdFinal);
req.id = replyMessages;
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
if (error == null) {
TLRPC.messages_Messages messagesRes = (TLRPC.messages_Messages) response;
ImageLoader.saveMessagesThumbs(messagesRes.messages);
broadcastReplyMessages(messagesRes.messages, replyMessageOwners, messagesRes.users, messagesRes.chats, dialogId, false);
//MessagesStorage.getInstance().putUsersAndChats(messagesRes.users, messagesRes.chats, true, true);
saveReplyMessages(replyMessageOwners, messagesRes.messages);
}
}
});
} else {
TLRPC.TL_messages_getMessages req = new TLRPC.TL_messages_getMessages();
req.id = replyMessages;
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
if (error == null) {
TLRPC.messages_Messages messagesRes = (TLRPC.messages_Messages) response;
ImageLoader.saveMessagesThumbs(messagesRes.messages);
broadcastReplyMessages(messagesRes.messages, replyMessageOwners, messagesRes.users, messagesRes.chats, dialogId, false);
//MessagesStorage.getInstance().putUsersAndChats(messagesRes.users, messagesRes.chats, true, true);
saveReplyMessages(replyMessageOwners, messagesRes.messages);
}
}
});
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
});
*/
}
}
/*
private static void saveReplyMessages(final HashMap<Integer, ArrayList<MessageObject>> replyMessageOwners, final ArrayList<TLRPC.Message> result) {
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
try {
MessagesStorage.getInstance().getDatabase().beginTransaction();
SQLitePreparedStatement state = MessagesStorage.getInstance().getDatabase().executeFast("UPDATE messages SET replydata = ? WHERE mid = ?");
for (int a = 0; a < result.size(); a++) {
TLRPC.Message message = result.get(a);
ArrayList<MessageObject> messageObjects = replyMessageOwners.get(message.id);
if (messageObjects != null) {
NativeByteBuffer data = new NativeByteBuffer(message.getObjectSize());
message.serializeToStream(data);
for (int b = 0; b < messageObjects.size(); b++) {
MessageObject messageObject = messageObjects.get(b);
state.requery();
long messageId = messageObject.getId();
if (messageObject.messageOwner.to_id.channel_id != 0) {
messageId |= ((long) messageObject.messageOwner.to_id.channel_id) << 32;
}
state.bindByteBuffer(1, data);
state.bindLong(2, messageId);
state.step();
}
data.reuse();
}
}
state.dispose();
MessagesStorage.getInstance().getDatabase().commitTransaction();
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
});
}
*/
/*
private static void broadcastReplyMessages(final ArrayList<TLRPC.Message> result, final HashMap<Integer, ArrayList<MessageObject>> replyMessageOwners, final ArrayList<TLRPC.User> users, final ArrayList<TLRPC.Chat> chats, final long dialog_id, final boolean isCache) {
final HashMap<Integer, TLRPC.User> usersDict = new HashMap<>();
for (int a = 0; a < users.size(); a++) {
TLRPC.User user = users.get(a);
usersDict.put(user.id, user);
}
final HashMap<Integer, TLRPC.Chat> chatsDict = new HashMap<>();
for (int a = 0; a < chats.size(); a++) {
TLRPC.Chat chat = chats.get(a);
chatsDict.put(chat.id, chat);
}
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
MessagesController.getInstance().putUsers(users, isCache);
MessagesController.getInstance().putChats(chats, isCache);
boolean changed = false;
for (int a = 0; a < result.size(); a++) {
TLRPC.Message message = result.get(a);
ArrayList<MessageObject> arrayList = replyMessageOwners.get(message.id);
if (arrayList != null) {
MessageObject messageObject = new MessageObject(message, usersDict, chatsDict, false);
for (int b = 0; b < arrayList.size(); b++) {
MessageObject m = arrayList.get(b);
m.replyMessageObject = messageObject;
//if (m.messageOwner.action instanceof TLRPC.TL_messageActionPinMessage) {
// m.generatePinMessageText(null, null);
//}
}
changed = true;
}
}
if (changed) {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.didLoadedReplyMessages, dialog_id);
}
}
});
}
*/
public static ArrayList<TLRPC.MessageEntity> getEntities(CharSequence message) {
if (message == null) {
return null;
}
ArrayList<TLRPC.MessageEntity> entities = null;
if (message instanceof Spannable) {
Spannable spannable = (Spannable) message;
URLSpanUserMention spans[] = spannable.getSpans(0, message.length(), URLSpanUserMention.class);
if (spans != null && spans.length > 0) {
entities = new ArrayList<>();
for (int b = 0; b < spans.length; b++) {
TLRPC.TL_inputMessageEntityMentionName entity = new TLRPC.TL_inputMessageEntityMentionName();
entity.user_id = MessagesController.getInputUser(Utilities.parseInt(spans[b].getURL()));
if (entity.user_id != null) {
entity.offset = spannable.getSpanStart(spans[b]);
entity.length = Math.min(spannable.getSpanEnd(spans[b]), message.length()) - entity.offset;
if (message.charAt(entity.offset + entity.length - 1) == ' ') {
entity.length--;
}
entities.add(entity);
}
}
}
}
return entities;
}
}
@@ -1,208 +0,0 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package com.b44t.messenger.query;
import com.b44t.messenger.MessageObject;
import com.b44t.messenger.MessagesController;
import com.b44t.messenger.NotificationCenter;
import com.b44t.messenger.ConnectionsManager;
import com.b44t.messenger.TLRPC;
import java.util.ArrayList;
@SuppressWarnings("unchecked")
public class MessagesSearchQuery {
private static int reqId;
private static int mergeReqId;
private static long lastMergeDialogId;
private static int lastReqId;
private static int messagesSearchCount[] = new int[] {0, 0};
private static boolean messagesSearchEndReached[] = new boolean[] {false, false};
private static ArrayList<MessageObject> searchResultMessages = new ArrayList<>();
private static String lastSearchQuery;
private static int lastReturnedNum;
private static int getMask() {
int mask = 0;
if (lastReturnedNum < searchResultMessages.size() - 1 || !messagesSearchEndReached[0] || !messagesSearchEndReached[1]) {
mask |= 1;
}
if (lastReturnedNum > 0) {
mask |= 2;
}
return mask;
}
public static void searchMessagesInChat(String query, final long dialog_id, final long mergeDialogId, final int guid, final int direction) {
searchMessagesInChat(query, dialog_id, mergeDialogId, guid, direction, false);
}
private static void searchMessagesInChat(String query, final long dialog_id, final long mergeDialogId, final int guid, final int direction, final boolean internal) {
int max_id = 0;
long queryWithDialog = dialog_id;
boolean firstQuery = !internal;
if (reqId != 0) {
ConnectionsManager.getInstance().cancelRequest(reqId, true);
reqId = 0;
}
if (mergeReqId != 0) {
ConnectionsManager.getInstance().cancelRequest(mergeReqId, true);
mergeReqId = 0;
}
if (query == null || query.length() == 0) {
if (searchResultMessages.isEmpty()) {
return;
}
if (direction == 1) {
lastReturnedNum++;
if (lastReturnedNum < searchResultMessages.size()) {
MessageObject messageObject = searchResultMessages.get(lastReturnedNum);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatSearchResultsAvailable, guid, messageObject.getId(), getMask(), messageObject.getDialogId(), lastReturnedNum, messagesSearchCount[0] + messagesSearchCount[1]);
return;
} else {
if (messagesSearchEndReached[0] && mergeDialogId == 0 || messagesSearchEndReached[1]) {
lastReturnedNum--;
return;
}
firstQuery = false;
query = lastSearchQuery;
MessageObject messageObject = searchResultMessages.get(searchResultMessages.size() - 1);
if (messageObject.getDialogId() == dialog_id && !messagesSearchEndReached[0]) {
max_id = messageObject.getId();
queryWithDialog = dialog_id;
} else {
if (messageObject.getDialogId() == mergeDialogId) {
max_id = messageObject.getId();
}
queryWithDialog = mergeDialogId;
messagesSearchEndReached[1] = false;
}
}
} else if (direction == 2) {
lastReturnedNum--;
if (lastReturnedNum < 0) {
lastReturnedNum = 0;
return;
}
if (lastReturnedNum >= searchResultMessages.size()) {
lastReturnedNum = searchResultMessages.size() - 1;
}
MessageObject messageObject = searchResultMessages.get(lastReturnedNum);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatSearchResultsAvailable, guid, messageObject.getId(), getMask(), messageObject.getDialogId(), lastReturnedNum, messagesSearchCount[0] + messagesSearchCount[1]);
return;
} else {
return;
}
}
if (messagesSearchEndReached[0] && !messagesSearchEndReached[1] && mergeDialogId != 0) {
queryWithDialog = mergeDialogId;
}
if (queryWithDialog == dialog_id && firstQuery) {
if (mergeDialogId != 0) {
TLRPC.InputPeer inputPeer = MessagesController.getInputPeer((int) mergeDialogId);
if (inputPeer == null) {
return;
}
final TLRPC.TL_messages_search req = new TLRPC.TL_messages_search();
req.peer = inputPeer;
lastMergeDialogId = mergeDialogId;
req.limit = 1;
req.q = query;
req.filter = new TLRPC.TL_inputMessagesFilterEmpty();
mergeReqId = 0;/*ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(final TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (lastMergeDialogId == mergeDialogId) {
mergeReqId = 0;
if (response != null) {
TLRPC.messages_Messages res = (TLRPC.messages_Messages) response;
messagesSearchEndReached[1] = res.messages.isEmpty();
messagesSearchCount[1] = res instanceof TLRPC.TL_messages_messagesSlice ? res.count : res.messages.size();
searchMessagesInChat(req.q, dialog_id, mergeDialogId, guid, direction, true);
}
}
}
});
}
}, ConnectionsManager.RequestFlagFailOnServerErrors);*/
return;
} else {
lastMergeDialogId = 0;
messagesSearchEndReached[1] = true;
messagesSearchCount[1] = 0;
}
}
final TLRPC.TL_messages_search req = new TLRPC.TL_messages_search();
req.peer = MessagesController.getInputPeer((int) queryWithDialog);
if (req.peer == null) {
return;
}
req.limit = 21;
req.q = query;
req.max_id = max_id;
req.filter = new TLRPC.TL_inputMessagesFilterEmpty();
final int currentReqId = ++lastReqId;
lastSearchQuery = query;
final long queryWithDialogFinal = queryWithDialog;
reqId = 0; /* ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(final TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (currentReqId == lastReqId) {
reqId = 0;
if (response != null) {
TLRPC.messages_Messages res = (TLRPC.messages_Messages) response;
//MessagesStorage.getInstance().putUsersAndChats(res.users, res.chats, true, true);
MessagesController.getInstance().putUsers(res.users, false);
MessagesController.getInstance().putChats(res.chats, false);
if (req.max_id == 0 && queryWithDialogFinal == dialog_id) {
lastReturnedNum = 0;
searchResultMessages.clear();
messagesSearchCount[0] = 0;
}
boolean added = false;
for (int a = 0; a < Math.min(res.messages.size(), 20); a++) {
TLRPC.Message message = res.messages.get(a);
added = true;
searchResultMessages.add(new MessageObject(message, null, false));
}
messagesSearchEndReached[queryWithDialogFinal == dialog_id ? 0 : 1] = res.messages.size() != 21;
messagesSearchCount[queryWithDialogFinal == dialog_id ? 0 : 1] = res instanceof TLRPC.TL_messages_messagesSlice ? res.count : res.messages.size();
if (searchResultMessages.isEmpty()) {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatSearchResultsAvailable, guid, 0, getMask(), (long) 0, 0, 0);
} else {
if (added) {
if (lastReturnedNum >= searchResultMessages.size()) {
lastReturnedNum = searchResultMessages.size() - 1;
}
MessageObject messageObject = searchResultMessages.get(lastReturnedNum);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatSearchResultsAvailable, guid, messageObject.getId(), getMask(), messageObject.getDialogId(), lastReturnedNum, messagesSearchCount[0] + messagesSearchCount[1]);
}
}
if (queryWithDialogFinal == dialog_id && messagesSearchEndReached[0] && mergeDialogId != 0 && !messagesSearchEndReached[1]) {
searchMessagesInChat(lastSearchQuery, dialog_id, mergeDialogId, guid, 0, true);
}
}
}
}
});
}
}, ConnectionsManager.RequestFlagFailOnServerErrors);*/
}
public static String getLastSearchQuery() {
return lastSearchQuery;
}
}
@@ -1,327 +0,0 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package com.b44t.messenger.query;
import com.b44t.messenger.MessagesController;
import com.b44t.messenger.NotificationCenter;
import com.b44t.messenger.TLRPC;
import java.util.ArrayList;
import java.util.HashMap;
public class SearchQuery {
public static ArrayList<TLRPC.TL_topPeer> hints = new ArrayList<>();
public static ArrayList<TLRPC.TL_topPeer> inlineBots = new ArrayList<>();
private static HashMap<Integer, Integer> inlineDates = new HashMap<>();
private static boolean loaded;
private static boolean loading;
public static void cleanup() {
loading = false;
loaded = false;
hints.clear();
inlineBots.clear();
inlineDates.clear();
NotificationCenter.getInstance().postNotificationName(NotificationCenter.reloadHints);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.reloadInlineHints);
}
public static void loadHints(boolean cache) {
if (loading) {
return;
}
if (cache) {
if (loaded) {
return;
}
loading = true;
/*
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
final ArrayList<TLRPC.TL_topPeer> hintsNew = new ArrayList<>();
final ArrayList<TLRPC.TL_topPeer> inlineBotsNew = new ArrayList<>();
final HashMap<Integer, Integer> inlineDatesNew = new HashMap<>();
final ArrayList<TLRPC.User> users = new ArrayList<>();
final ArrayList<TLRPC.Chat> chats = new ArrayList<>();
try {
ArrayList<Integer> usersToLoad = new ArrayList<>();
ArrayList<Integer> chatsToLoad = new ArrayList<>();
SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized("SELECT did, type, rating, date FROM chat_hints WHERE 1 ORDER BY rating DESC");
while (cursor.next()) {
int did = cursor.intValue(0);
int type = cursor.intValue(1);
TLRPC.TL_topPeer peer = new TLRPC.TL_topPeer();
peer.rating = cursor.doubleValue(2);
if (did > 0) {
peer.peer = new TLRPC.TL_peerUser();
peer.peer.user_id = did;
usersToLoad.add(did);
} else {
peer.peer = new TLRPC.TL_peerChat();
peer.peer.chat_id = -did;
chatsToLoad.add(-did);
}
if (type == 0) {
hintsNew.add(peer);
} else if (type == 1) {
inlineBotsNew.add(peer);
inlineDatesNew.put(did, cursor.intValue(3));
}
}
cursor.dispose();
if (!usersToLoad.isEmpty()) {
//MessagesStorage.getInstance().getUsersInternal(TextUtils.join(",", usersToLoad), users);
}
if (!chatsToLoad.isEmpty()) {
//MessagesStorage.getInstance().getChatsInternal(TextUtils.join(",", chatsToLoad), chats);
}
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
MessagesController.getInstance().putUsers(users, true);
MessagesController.getInstance().putChats(chats, true);
loading = false;
loaded = true;
hints = hintsNew;
inlineBots = inlineBotsNew;
inlineDates = inlineDatesNew;
NotificationCenter.getInstance().postNotificationName(NotificationCenter.reloadHints);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.reloadInlineHints);
if (Math.abs(UserConfig.lastHintsSyncTime - (int) (System.currentTimeMillis() / 1000)) >= 24 * 60 * 60) {
loadHints(false);
}
}
});
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
});
*/
loaded = true;
} else {
loading = true;
/*
TLRPC.TL_contacts_getTopPeers req = new TLRPC.TL_contacts_getTopPeers();
req.hash = 0;
req.bots_pm = false;
req.correspondents = true;
req.groups = false;
req.channels = false;
req.bots_inline = true;
req.offset = 0;
req.limit = 20;
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(final TLObject response, TLRPC.TL_error error) {
if (response instanceof TLRPC.TL_contacts_topPeers) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
final TLRPC.TL_contacts_topPeers topPeers = (TLRPC.TL_contacts_topPeers) response;
MessagesController.getInstance().putUsers(topPeers.users, false);
MessagesController.getInstance().putChats(topPeers.chats, false);
for (int a = 0; a < topPeers.categories.size(); a++) {
TLRPC.TL_topPeerCategoryPeers category = topPeers.categories.get(a);
if (category.category instanceof TLRPC.TL_topPeerCategoryBotsInline) {
inlineBots = category.peers;
} else {
hints = category.peers;
}
}
NotificationCenter.getInstance().postNotificationName(NotificationCenter.reloadHints);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.reloadInlineHints);
final HashMap<Integer, Integer> inlineDatesCopy = new HashMap<>(inlineDates);
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
try {
MessagesStorage.getInstance().getDatabase().executeFast("DELETE FROM chat_hints WHERE 1").stepThis().dispose();
MessagesStorage.getInstance().getDatabase().beginTransaction();
MessagesStorage.getInstance().putUsersAndChats(topPeers.users, topPeers.chats, false, false);
SQLitePreparedStatement state = MessagesStorage.getInstance().getDatabase().executeFast("REPLACE INTO chat_hints VALUES(?, ?, ?, ?)");
for (int a = 0; a < topPeers.categories.size(); a++) {
int type;
TLRPC.TL_topPeerCategoryPeers category = topPeers.categories.get(a);
if (category.category instanceof TLRPC.TL_topPeerCategoryBotsInline) {
type = 1;
} else {
type = 0;
}
for (int b = 0; b < category.peers.size(); b++) {
TLRPC.TL_topPeer peer = category.peers.get(b);
int did;
if (peer.peer instanceof TLRPC.TL_peerUser) {
did = peer.peer.user_id;
} else if (peer.peer instanceof TLRPC.TL_peerChat) {
did = -peer.peer.chat_id;
} else {
did = -peer.peer.channel_id;
}
Integer date = inlineDatesCopy.get(did);
state.requery();
state.bindInteger(1, did);
state.bindInteger(2, type);
state.bindDouble(3, peer.rating);
state.bindInteger(4, date != null ? date : 0);
state.step();
}
}
state.dispose();
MessagesStorage.getInstance().getDatabase().commitTransaction();
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
UserConfig.lastHintsSyncTime = (int) (System.currentTimeMillis() / 1000);
UserConfig.saveConfig(false);
}
});
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
});
}
});
}
}
});*/
}
}
public static void removeInline(final int uid) {
TLRPC.TL_topPeerCategoryPeers category = null;
for (int a = 0; a < inlineBots.size(); a++) {
if (inlineBots.get(a).peer.user_id == uid) {
inlineBots.remove(a);
/*TLRPC.TL_contacts_resetTopPeerRating req = new TLRPC.TL_contacts_resetTopPeerRating();
req.category = new TLRPC.TL_topPeerCategoryBotsInline();
req.peer = MessagesController.getInputPeer(uid);
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
}
});
deletePeer(uid, 1);
*/
NotificationCenter.getInstance().postNotificationName(NotificationCenter.reloadInlineHints);
return;
}
}
}
public static void removePeer(final int uid) {
TLRPC.TL_topPeerCategoryPeers category = null;
for (int a = 0; a < hints.size(); a++) {
if (hints.get(a).peer.user_id == uid) {
hints.remove(a);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.reloadHints);
/*TLRPC.TL_contacts_resetTopPeerRating req = new TLRPC.TL_contacts_resetTopPeerRating();
req.category = new TLRPC.TL_topPeerCategoryCorrespondents();
req.peer = MessagesController.getInputPeer(uid);
deletePeer(uid, 0);
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
}
});*/
return;
}
}
}
public static void increasePeerRaiting(final long did) {
final int lower_id = (int) did;
if (lower_id <= 0) {
return;
}
//remove chats and bots for now
final TLRPC.User user = lower_id > 0 ? MessagesController.getInstance().getUser(lower_id) : null;
//final TLRPC.Chat chat = lower_id < 0 ? MessagesController.getInstance().getChat(-lower_id) : null;
if (user == null || user.bot/*&& chat == null || ChatObject.isChannel(chat) && !chat.megagroup*/) {
return;
}
/*
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
double dt = 0;
try {
int lastTime = 0;
int lastMid = 0;
SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT MAX(mid), MAX(date) FROM messages WHERE uid = %d AND out = 1", did));
if (cursor.next()) {
lastMid = cursor.intValue(0);
lastTime = cursor.intValue(1);
}
cursor.dispose();
if (lastMid > 0) {
cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT date FROM messages WHERE uid = %d AND mid < %d AND out = 1 ORDER BY date DESC", did, lastMid));
if (cursor.next()) {
dt = (lastTime - cursor.intValue(0));
}
cursor.dispose();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
final double dtFinal = dt;
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
TLRPC.TL_topPeer peer = null;
for (int a = 0; a < hints.size(); a++) {
TLRPC.TL_topPeer p = hints.get(a);
if (lower_id < 0 && (p.peer.chat_id == -lower_id || p.peer.channel_id == -lower_id) || lower_id > 0 && p.peer.user_id == lower_id) {
peer = p;
break;
}
}
if (peer == null) {
peer = new TLRPC.TL_topPeer();
if (lower_id > 0) {
peer.peer = new TLRPC.TL_peerUser();
peer.peer.user_id = lower_id;
} else {
peer.peer = new TLRPC.TL_peerChat();
peer.peer.chat_id = -lower_id;
}
hints.add(peer);
}
peer.rating += Math.exp(dtFinal / MessagesController.getInstance().ratingDecay);
Collections.sort(hints, new Comparator<TLRPC.TL_topPeer>() {
@Override
public int compare(TLRPC.TL_topPeer lhs, TLRPC.TL_topPeer rhs) {
if (lhs.rating > rhs.rating) {
return -1;
} else if (lhs.rating < rhs.rating) {
return 1;
}
return 0;
}
});
savePeer((int) did, 0, peer.rating);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.reloadHints);
}
});
}
});
*/
}
}
@@ -1,439 +0,0 @@
/*
* This is the source code of Telegram for Android v. 2.0.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package com.b44t.messenger.query;
import com.b44t.messenger.ChatObject;
import com.b44t.messenger.MessagesController;
import com.b44t.messenger.MrMailbox;
import com.b44t.messenger.MrMsg;
import com.b44t.messenger.NotificationCenter;
import com.b44t.messenger.Utilities;
import com.b44t.messenger.TLRPC;
public class SharedMediaQuery {
public final static int MEDIA_PHOTOVIDEO = 0;
public final static int MEDIA_FILE = 1;
public final static int MEDIA_AUDIO = 2;
public final static int MEDIA_URL = 3;
public final static int MEDIA_MUSIC = 4;
public final static int MEDIA_TYPES_COUNT = 5;
public static void loadMedia(final long uid, final int offset, final int count, final int max_id, final int type, final boolean fromCache, final int classGuid) {
final boolean isChannel = false;//(int) uid < 0 && ChatObject.isChannel(-(int) uid);
int lower_part = (int)uid;
if (fromCache || lower_part == 0) {
loadMediaDatabase(uid, offset, count, max_id, type, classGuid, isChannel);
} else {
TLRPC.TL_messages_search req = new TLRPC.TL_messages_search();
req.offset = offset;
req.limit = count + 1;
req.max_id = max_id;
if (type == MEDIA_PHOTOVIDEO) {
req.filter = new TLRPC.TL_inputMessagesFilterPhotoVideo();
} else if (type == MEDIA_FILE) {
req.filter = new TLRPC.TL_inputMessagesFilterDocument();
} else if (type == MEDIA_AUDIO) {
req.filter = new TLRPC.TL_inputMessagesFilterVoice();
} else if (type == MEDIA_URL) {
req.filter = new TLRPC.TL_inputMessagesFilterUrl();
} else if (type == MEDIA_MUSIC) {
req.filter = new TLRPC.TL_inputMessagesFilterMusic();
}
req.q = "";
req.peer = MessagesController.getInputPeer(lower_part);
if (req.peer == null) {
return;
}
/*int reqId = ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
if (error == null) {
final TLRPC.messages_Messages res = (TLRPC.messages_Messages) response;
boolean topReached;
if (res.messages.size() > count) {
topReached = false;
res.messages.remove(res.messages.size() - 1);
} else {
topReached = true;
}
processLoadedMedia(res, uid, offset, count, max_id, type, false, classGuid, isChannel, topReached);
}
}
});
ConnectionsManager.getInstance().bindRequestToGuid(reqId, classGuid);*/
}
}
public static void getMediaCount(final long uid, final int type, final int classGuid, boolean fromCache) {
Utilities.globalQueue.postRunnable(new Runnable() {
@Override
public void run() {
int[] media = new int[0];
if( type == SharedMediaQuery.MEDIA_PHOTOVIDEO ) {
media = MrMailbox.getChatMedia((int)uid, MrMsg.MR_MSG_IMAGE, MrMsg.MR_MSG_VIDEO);
}
NotificationCenter.getInstance().postNotificationName(NotificationCenter.mediaCountDidLoaded,
uid, media.length, false /*not from cache*/, type, media);
}
});
/*
int lower_part = (int)uid;
if (fromCache || lower_part == 0) {
getMediaCountDatabase(uid, type, classGuid);
} else {
TLRPC.TL_messages_search req = new TLRPC.TL_messages_search();
req.offset = 0;
req.limit = 1;
req.max_id = 0;
if (type == MEDIA_PHOTOVIDEO) {
req.filter = new TLRPC.TL_inputMessagesFilterPhotoVideo();
} else if (type == MEDIA_FILE) {
req.filter = new TLRPC.TL_inputMessagesFilterDocument();
} else if (type == MEDIA_AUDIO) {
req.filter = new TLRPC.TL_inputMessagesFilterVoice();
} else if (type == MEDIA_URL) {
req.filter = new TLRPC.TL_inputMessagesFilterUrl();
} else if (type == MEDIA_MUSIC) {
req.filter = new TLRPC.TL_inputMessagesFilterMusic();
}
req.q = "";
req.peer = MessagesController.getInputPeer(lower_part);
if (req.peer == null) {
return;
}
int reqId = ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
if (error == null) {
final TLRPC.messages_Messages res = (TLRPC.messages_Messages) response;
//MessagesStorage.getInstance().putUsersAndChats(res.users, res.chats, true, true);
int count;
if (res instanceof TLRPC.TL_messages_messages) {
count = res.messages.size();
} else {
count = res.count;
}
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
MessagesController.getInstance().putUsers(res.users, false);
MessagesController.getInstance().putChats(res.chats, false);
}
});
processLoadedMediaCount(count, uid, type, classGuid, false);
}
}
});
ConnectionsManager.getInstance().bindRequestToGuid(reqId, classGuid);
}
*/
}
/*
public static int getMediaType(TLRPC.Message message) {
if (message == null) {
return -1;
}
if (message.media instanceof TLRPC.TL_messageMediaPhoto) {
return MEDIA_PHOTOVIDEO;
} else if (message.media instanceof TLRPC.TL_messageMediaDocument) {
if (MessageObject.isVoiceMessage(message)) {
return MEDIA_AUDIO;
} else if (MessageObject.isVideoMessage(message)) {
return MEDIA_PHOTOVIDEO;
} else if (MessageObject.isStickerMessage(message)) {
return -1;
} else if (MessageObject.isMusicMessage(message)) {
return MEDIA_MUSIC;
} else {
return MEDIA_FILE;
}
} else if (!message.entities.isEmpty()) {
for (int a = 0; a < message.entities.size(); a++) {
TLRPC.MessageEntity entity = message.entities.get(a);
if (entity instanceof TLRPC.TL_messageEntityUrl || entity instanceof TLRPC.TL_messageEntityTextUrl || entity instanceof TLRPC.TL_messageEntityEmail) {
return MEDIA_URL;
}
}
}
return -1;
}
*/
/*
private static void processLoadedMedia(final TLRPC.messages_Messages res, final long uid, int offset, int count, int max_id, final int type, final boolean fromCache, final int classGuid, final boolean isChannel, final boolean topReached) {
int lower_part = (int)uid;
if (fromCache && res.messages.isEmpty() && lower_part != 0) {
loadMedia(uid, offset, count, max_id, type, false, classGuid);
} else {
if (!fromCache) {
ImageLoader.saveMessagesThumbs(res.messages);
//MessagesStorage.getInstance().putUsersAndChats(res.users, res.chats, true, true);
putMediaDatabase(uid, type, res.messages, max_id, topReached);
}
final HashMap<Integer, TLRPC.User> usersDict = new HashMap<>();
for (int a = 0; a < res.users.size(); a++) {
TLRPC.User u = res.users.get(a);
usersDict.put(u.id, u);
}
final ArrayList<MessageObject> objects = new ArrayList<>();
for (int a = 0; a < res.messages.size(); a++) {
TLRPC.Message message = res.messages.get(a);
objects.add(new MessageObject(message, usersDict, true));
}
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
int totalCount = res.count;
MessagesController.getInstance().putUsers(res.users, fromCache);
MessagesController.getInstance().putChats(res.chats, fromCache);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.mediaDidLoaded, uid, totalCount, objects, classGuid, type, topReached);
}
});
}
}
*/
private static void loadMediaDatabase(final long uid, final int offset, final int count, final int max_id, final int type, final int classGuid, final boolean isChannel) {
/*
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
boolean topReached = false;
TLRPC.TL_messages_messages res = new TLRPC.TL_messages_messages();
try {
ArrayList<Integer> usersToLoad = new ArrayList<>();
ArrayList<Integer> chatsToLoad = new ArrayList<>();
int countToLoad = count + 1;
SQLiteCursor cursor;
SQLiteDatabase database = MessagesStorage.getInstance().getDatabase();
boolean isEnd = false;
if ((int) uid != 0) {
int channelId = 0;
long messageMaxId = max_id;
if (isChannel) {
channelId = -(int) uid;
}
if (messageMaxId != 0 && channelId != 0) {
messageMaxId |= ((long) channelId) << 32;
}
cursor = database.queryFinalized(String.format(Locale.US, "SELECT start FROM media_holes_v2 WHERE uid = %d AND type = %d AND start IN (0, 1)", uid, type));
if (cursor.next()) {
isEnd = cursor.intValue(0) == 1;
cursor.dispose();
} else {
cursor.dispose();
cursor = database.queryFinalized(String.format(Locale.US, "SELECT min(mid) FROM media_v2 WHERE uid = %d AND type = %d AND mid > 0", uid, type));
if (cursor.next()) {
int mid = cursor.intValue(0);
if (mid != 0) {
SQLitePreparedStatement state = database.executeFast("REPLACE INTO media_holes_v2 VALUES(?, ?, ?, ?)");
state.requery();
state.bindLong(1, uid);
state.bindInteger(2, type);
state.bindInteger(3, 0);
state.bindInteger(4, mid);
state.step();
state.dispose();
}
}
cursor.dispose();
}
if (messageMaxId != 0) {
long holeMessageId = 0;
cursor = database.queryFinalized(String.format(Locale.US, "SELECT end FROM media_holes_v2 WHERE uid = %d AND type = %d AND end <= %d ORDER BY end DESC LIMIT 1", uid, type, max_id));
if (cursor.next()) {
holeMessageId = cursor.intValue(0);
if (channelId != 0) {
holeMessageId |= ((long) channelId) << 32;
}
}
cursor.dispose();
if (holeMessageId > 1) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v2 WHERE uid = %d AND mid > 0 AND mid < %d AND mid >= %d AND type = %d ORDER BY date DESC, mid DESC LIMIT %d", uid, messageMaxId, holeMessageId, type, countToLoad));
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v2 WHERE uid = %d AND mid > 0 AND mid < %d AND type = %d ORDER BY date DESC, mid DESC LIMIT %d", uid, messageMaxId, type, countToLoad));
}
} else {
long holeMessageId = 0;
cursor = database.queryFinalized(String.format(Locale.US, "SELECT max(end) FROM media_holes_v2 WHERE uid = %d AND type = %d", uid, type));
if (cursor.next()) {
holeMessageId = cursor.intValue(0);
if (channelId != 0) {
holeMessageId |= ((long) channelId) << 32;
}
}
cursor.dispose();
if (holeMessageId > 1) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v2 WHERE uid = %d AND mid >= %d AND type = %d ORDER BY date DESC, mid DESC LIMIT %d,%d", uid, holeMessageId, type, offset, countToLoad));
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v2 WHERE uid = %d AND mid > 0 AND type = %d ORDER BY date DESC, mid DESC LIMIT %d,%d", uid, type, offset, countToLoad));
}
}
} else {
isEnd = true;
if (max_id != 0) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.data, m.mid, r.random_id FROM media_v2 as m LEFT JOIN randoms as r ON r.mid = m.mid WHERE m.uid = %d AND m.mid > %d AND type = %d ORDER BY m.mid ASC LIMIT %d", uid, max_id, type, countToLoad));
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.data, m.mid, r.random_id FROM media_v2 as m LEFT JOIN randoms as r ON r.mid = m.mid WHERE m.uid = %d AND type = %d ORDER BY m.mid ASC LIMIT %d,%d", uid, type, offset, countToLoad));
}
}
while (cursor.next()) {
NativeByteBuffer data = cursor.byteBufferValue(0);
if (data != null) {
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
data.reuse();
message.id = cursor.intValue(1);
message.dialog_id = uid;
if ((int) uid == 0) {
message.random_id = cursor.longValue(2);
}
res.messages.add(message);
if (message.from_id > 0) {
if (!usersToLoad.contains(message.from_id)) {
usersToLoad.add(message.from_id);
}
} else {
if (!chatsToLoad.contains(-message.from_id)) {
chatsToLoad.add(-message.from_id);
}
}
}
}
cursor.dispose();
if (!usersToLoad.isEmpty()) {
//MessagesStorage.getInstance().getUsersInternal(TextUtils.join(",", usersToLoad), res.users);
}
if (!chatsToLoad.isEmpty()) {
//MessagesStorage.getInstance().getChatsInternal(TextUtils.join(",", chatsToLoad), res.chats);
}
if (res.messages.size() > count) {
topReached = false;
res.messages.remove(res.messages.size() - 1);
} else {
topReached = isEnd;
}
} catch (Exception e) {
res.messages.clear();
res.chats.clear();
res.users.clear();
FileLog.e("messenger", e);
} finally {
processLoadedMedia(res, uid, offset, count, max_id, type, true, classGuid, isChannel, topReached);
}
}
});
*/
}
/*
private static void putMediaDatabase(final long uid, final int type, final ArrayList<TLRPC.Message> messages, final int max_id, final boolean topReached) {
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
try {
if (messages.isEmpty() || topReached) {
MessagesStorage.getInstance().doneHolesInMedia(uid, max_id, type);
if (messages.isEmpty()) {
return;
}
}
MessagesStorage.getInstance().getDatabase().beginTransaction();
SQLitePreparedStatement state2 = MessagesStorage.getInstance().getDatabase().executeFast("REPLACE INTO media_v2 VALUES(?, ?, ?, ?, ?)");
for (TLRPC.Message message : messages) {
if (canAddMessageToMedia(message)) {
long messageId = message.id;
if (message.to_id.channel_id != 0) {
messageId |= ((long) message.to_id.channel_id) << 32;
}
state2.requery();
NativeByteBuffer data = new NativeByteBuffer(message.getObjectSize());
message.serializeToStream(data);
state2.bindLong(1, messageId);
state2.bindLong(2, uid);
state2.bindInteger(3, message.date);
state2.bindInteger(4, type);
state2.bindByteBuffer(5, data);
state2.step();
data.reuse();
}
}
state2.dispose();
if (!topReached || max_id != 0) {
int minId = topReached ? 1 : messages.get(messages.size() - 1).id;
if (max_id != 0) {
MessagesStorage.getInstance().closeHolesInMedia(uid, minId, max_id, type);
} else {
MessagesStorage.getInstance().closeHolesInMedia(uid, minId, Integer.MAX_VALUE, type);
}
}
MessagesStorage.getInstance().getDatabase().commitTransaction();
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
});
}
*/
public static void loadMusic(final long uid, final int max_id) {
/*
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
final ArrayList<MessageObject> arrayList = new ArrayList<>();
try {
SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v2 WHERE uid = %d AND mid < %d AND type = %d ORDER BY date DESC, mid DESC LIMIT 1000", uid, max_id, MEDIA_MUSIC));
while (cursor.next()) {
NativeByteBuffer data = cursor.byteBufferValue(0);
if (data != null) {
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
data.reuse();
if (MessageObject.isMusicMessage(message)) {
message.id = cursor.intValue(1);
message.dialog_id = uid;
arrayList.add(0, new MessageObject(mess age, null, false));
}
}
}
cursor.dispose();
} catch (Exception e) {
FileLog.e("messenger", e);
}
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.musicDidLoaded, uid, arrayList);
}
});
}
});
*/
}
}
@@ -1,519 +0,0 @@
/*
* This is the source code of Telegram for Android v. 3.x.x
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package com.b44t.messenger.query;
import android.content.Context;
import com.b44t.messenger.NotificationCenter;
import com.b44t.messenger.TLRPC;
import java.util.ArrayList;
import java.util.HashMap;
public class StickersQuery {
private static int loadHash;
private static int loadDate;
private static ArrayList<TLRPC.TL_messages_stickerSet> stickerSets = new ArrayList<>();
private static HashMap<Long, TLRPC.TL_messages_stickerSet> stickerSetsById = new HashMap<>();
private static HashMap<String, TLRPC.TL_messages_stickerSet> stickerSetsByName = new HashMap<>();
private static HashMap<Long, String> stickersByEmoji = new HashMap<>();
private static HashMap<Long, TLRPC.Document> stickersById = new HashMap<>();
private static HashMap<String, ArrayList<TLRPC.Document>> allStickers = new HashMap<>();
private static boolean loadingStickers;
private static boolean stickersLoaded;
public static void cleanup() {
loadHash = 0;
loadDate = 0;
allStickers.clear();
stickerSets.clear();
stickersByEmoji.clear();
stickerSetsById.clear();
stickerSetsByName.clear();
loadingStickers = false;
stickersLoaded = false;
}
public static void checkStickers() {
if (!loadingStickers && (!stickersLoaded || Math.abs(System.currentTimeMillis() / 1000 - loadDate) >= 60 * 60)) {
loadStickers(true, false);
}
}
/*
public static boolean isLoadingStickers() {
return loadingStickers;
}
*/
public static TLRPC.Document getStickerById(long id) {
TLRPC.Document document = stickersById.get(id);
if (document != null) {
long setId = getStickerSetId(document);
TLRPC.TL_messages_stickerSet stickerSet = stickerSetsById.get(setId);
if (stickerSet != null && stickerSet.set.disabled) {
return null;
}
}
return document;
}
public static TLRPC.TL_messages_stickerSet getStickerSetByName(String name) {
return stickerSetsByName.get(name);
}
public static TLRPC.TL_messages_stickerSet getStickerSetById(Long id) {
return stickerSetsById.get(id);
}
public static HashMap<String, ArrayList<TLRPC.Document>> getAllStickers() {
return allStickers;
}
public static ArrayList<TLRPC.TL_messages_stickerSet> getStickerSets() {
return stickerSets;
}
public static boolean isStickerPackInstalled(long id) {
return stickerSetsById.containsKey(id);
}
public static boolean isStickerPackInstalled(String name) {
return stickerSetsByName.containsKey(name);
}
public static String getEmojiForSticker(long id) {
String value = stickersByEmoji.get(id);
return value != null ? value : "";
}
/*
public static void reorderStickers(final ArrayList<Long> order) {
Collections.sort(stickerSets, new Comparator<TLRPC.TL_messages_stickerSet>() {
@Override
public int compare(TLRPC.TL_messages_stickerSet lhs, TLRPC.TL_messages_stickerSet rhs) {
int index1 = order.indexOf(lhs.set.id);
int index2 = order.indexOf(rhs.set.id);
if (index1 > index2) {
return 1;
} else if (index1 < index2) {
return -1;
}
return 0;
}
});
loadHash = calcStickersHash(stickerSets);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.stickersDidLoaded);
StickersQuery.loadStickers(false, true);
}
*/
/*
public static void calcNewHash() {
loadHash = calcStickersHash(stickerSets);
}
*/
/*
public static void addNewStickerSet(final TLRPC.TL_messages_stickerSet set) {
if (stickerSetsById.containsKey(set.set.id) || stickerSetsByName.containsKey(set.set.short_name)) {
return;
}
stickerSets.add(0, set);
stickerSetsById.put(set.set.id, set);
stickerSetsByName.put(set.set.short_name, set);
for (int a = 0; a < set.documents.size(); a++) {
TLRPC.Document document = set.documents.get(a);
stickersById.put(document.id, document);
}
for (int a = 0; a < set.packs.size(); a++) {
TLRPC.TL_stickerPack stickerPack = set.packs.get(a);
stickerPack.emoticon = stickerPack.emoticon.replace("\uFE0F", "");
ArrayList<TLRPC.Document> arrayList = allStickers.get(stickerPack.emoticon);
if (arrayList == null) {
arrayList = new ArrayList<>();
allStickers.put(stickerPack.emoticon, arrayList);
}
for (int c = 0; c < stickerPack.documents.size(); c++) {
Long id = stickerPack.documents.get(c);
if (!stickersByEmoji.containsKey(id)) {
stickersByEmoji.put(id, stickerPack.emoticon);
}
TLRPC.Document sticker = stickersById.get(id);
if (sticker != null) {
arrayList.add(sticker);
}
}
}
loadHash = calcStickersHash(stickerSets);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.stickersDidLoaded);
StickersQuery.loadStickers(false, true);
}
*/
public static void loadStickers(boolean cache, boolean force) {
if (loadingStickers) {
return;
}
loadingStickers = true;
if (cache) {
/*
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
ArrayList<TLRPC.TL_messages_stickerSet> newStickerArray = null;
int date = 0;
int hash = 0;
SQLiteCursor cursor = null;
try {
cursor = MessagesStorage.getInstance().getDatabase().queryFinalized("SELECT data, date, hash FROM stickers_v2 WHERE 1");
if (cursor.next()) {
NativeByteBuffer data = cursor.byteBufferValue(0);
if (data != null) {
newStickerArray = new ArrayList<>();
int count = data.readInt32(false);
for (int a = 0; a < count; a++) {
TLRPC.TL_messages_stickerSet stickerSet = TLRPC.TL_messages_stickerSet.TLdeserialize(data, data.readInt32(false), false);
newStickerArray.add(stickerSet);
}
data.reuse();
}
date = cursor.intValue(1);
hash = calcStickersHash(newStickerArray);
}
} catch (Throwable e) {
FileLog.e("messenger", e);
} finally {
if (cursor != null) {
cursor.dispose();
}
}
processLoadedStickers(newStickerArray, true, date, hash);
}
});
*/
} else {
/*final TLRPC.TL_messages_getAllStickers req = new TLRPC.TL_messages_getAllStickers();
req.hash = force ? 0 : loadHash;
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(final TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (response instanceof TLRPC.TL_messages_allStickers) {
final HashMap<Long, TLRPC.TL_messages_stickerSet> newStickerSets = new HashMap<>();
final ArrayList<TLRPC.TL_messages_stickerSet> newStickerArray = new ArrayList<>();
final TLRPC.TL_messages_allStickers res = (TLRPC.TL_messages_allStickers) response;
for (int a = 0; a < res.sets.size(); a++) {
final TLRPC.StickerSet stickerSet = res.sets.get(a);
TLRPC.TL_messages_stickerSet oldSet = stickerSetsById.get(stickerSet.id);
if (oldSet != null && oldSet.set.hash == stickerSet.hash) {
oldSet.set.disabled = stickerSet.disabled;
oldSet.set.installed = stickerSet.installed;
oldSet.set.official = stickerSet.official;
newStickerSets.put(oldSet.set.id, oldSet);
newStickerArray.add(oldSet);
if (newStickerSets.size() == res.sets.size()) {
processLoadedStickers(newStickerArray, false, (int) (System.currentTimeMillis() / 1000), res.hash);
}
continue;
}
newStickerArray.add(null);
final int index = a;
TLRPC.TL_messages_getStickerSet req = new TLRPC.TL_messages_getStickerSet();
req.stickerset = new TLRPC.TL_inputStickerSetID();
req.stickerset.id = stickerSet.id;
req.stickerset.access_hash = stickerSet.access_hash;
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(final TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
TLRPC.TL_messages_stickerSet res1 = (TLRPC.TL_messages_stickerSet) response;
newStickerArray.set(index, res1);
newStickerSets.put(stickerSet.id, res1);
if (newStickerSets.size() == res.sets.size()) {
processLoadedStickers(newStickerArray, false, (int) (System.currentTimeMillis() / 1000), res.hash);
}
}
});
}
});
}
} else {
processLoadedStickers(null, false, (int) (System.currentTimeMillis() / 1000), req.hash);
}
}
});
}
});*/
}
}
private static void putStickersToCache(ArrayList<TLRPC.TL_messages_stickerSet> stickers, final int date, final int hash) {
final ArrayList<TLRPC.TL_messages_stickerSet> stickersFinal = stickers != null ? new ArrayList<>(stickers) : null;
/*
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
try {
if (stickersFinal != null) {
SQLitePreparedStatement state = MessagesStorage.getInstance().getDatabase().executeFast("REPLACE INTO stickers_v2 VALUES(?, ?, ?, ?)");
state.requery();
int size = 4;
for (int a = 0; a < stickersFinal.size(); a++) {
size += stickersFinal.get(a).getObjectSize();
}
NativeByteBuffer data = new NativeByteBuffer(size);
data.writeInt32(stickersFinal.size());
for (int a = 0; a < stickersFinal.size(); a++) {
stickersFinal.get(a).serializeToStream(data);
}
state.bindInteger(1, 1);
state.bindByteBuffer(2, data);
state.bindInteger(3, date);
state.bindInteger(4, hash);
state.step();
data.reuse();
state.dispose();
} else {
SQLitePreparedStatement state = MessagesStorage.getInstance().getDatabase().executeFast("UPDATE stickers_v2 SET date = ?");
state.requery();
state.bindInteger(1, date);
state.step();
state.dispose();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
});
*/
}
/*
public static String getStickerSetName(long setId) {
TLRPC.TL_messages_stickerSet stickerSet = stickerSetsById.get(setId);
return stickerSet != null ? stickerSet.set.short_name : null;
}
*/
public static long getStickerSetId(TLRPC.Document document) {
for (int a = 0; a < document.attributes.size(); a++) {
TLRPC.DocumentAttribute attribute = document.attributes.get(a);
if (attribute instanceof TLRPC.TL_documentAttributeSticker) {
if (attribute.stickerset instanceof TLRPC.TL_inputStickerSetID) {
return attribute.stickerset.id;
}
break;
}
}
return -1;
}
private static int calcStickersHash(ArrayList<TLRPC.TL_messages_stickerSet> sets) {
long acc = 0;
for (int a = 0; a < sets.size(); a++) {
TLRPC.StickerSet set = sets.get(a).set;
if (set.disabled) {
continue;
}
acc = ((acc * 20261) + 0x80000000L + set.hash) % 0x80000000L;
}
return (int) acc;
}
/*
private static void processLoadedStickers(final ArrayList<TLRPC.TL_messages_stickerSet> res, final boolean cache, final int date, final int hash) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
loadingStickers = false;
stickersLoaded = true;
}
});
Utilities.stageQueue.postRunnable(new Runnable() {
@Override
public void run() {
if (cache && (res == null || Math.abs(System.currentTimeMillis() / 1000 - date) >= 60 * 60) || !cache && res == null && hash == 0) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (res != null && hash != 0) {
loadHash = hash;
}
loadStickers(false, false);
}
}, res == null && !cache ? 1000 : 0);
if (res == null) {
return;
}
}
if (res != null) {
try {
final ArrayList<TLRPC.TL_messages_stickerSet> stickerSetsNew = new ArrayList<>();
final HashMap<Long, TLRPC.TL_messages_stickerSet> stickerSetsByIdNew = new HashMap<>();
final HashMap<String, TLRPC.TL_messages_stickerSet> stickerSetsByNameNew = new HashMap<>();
final HashMap<Long, String> stickersByEmojiNew = new HashMap<>();
final HashMap<Long, TLRPC.Document> stickersByIdNew = new HashMap<>();
final HashMap<String, ArrayList<TLRPC.Document>> allStickersNew = new HashMap<>();
for (int a = 0; a < res.size(); a++) {
TLRPC.TL_messages_stickerSet stickerSet = res.get(a);
if (stickerSet == null) {
continue;
}
stickerSetsNew.add(stickerSet);
stickerSetsByIdNew.put(stickerSet.set.id, stickerSet);
stickerSetsByNameNew.put(stickerSet.set.short_name, stickerSet);
for (int b = 0; b < stickerSet.documents.size(); b++) {
TLRPC.Document document = stickerSet.documents.get(b);
if (document == null || document instanceof TLRPC.TL_documentEmpty) {
continue;
}
stickersByIdNew.put(document.id, document);
}
if (!stickerSet.set.disabled) {
for (int b = 0; b < stickerSet.packs.size(); b++) {
TLRPC.TL_stickerPack stickerPack = stickerSet.packs.get(b);
if (stickerPack == null || stickerPack.emoticon == null) {
continue;
}
stickerPack.emoticon = stickerPack.emoticon.replace("\uFE0F", "");
ArrayList<TLRPC.Document> arrayList = allStickersNew.get(stickerPack.emoticon);
if (arrayList == null) {
arrayList = new ArrayList<>();
allStickersNew.put(stickerPack.emoticon, arrayList);
}
for (int c = 0; c < stickerPack.documents.size(); c++) {
Long id = stickerPack.documents.get(c);
if (!stickersByEmojiNew.containsKey(id)) {
stickersByEmojiNew.put(id, stickerPack.emoticon);
}
TLRPC.Document sticker = stickersByIdNew.get(id);
if (sticker != null) {
arrayList.add(sticker);
}
}
}
}
}
if (!cache) {
putStickersToCache(stickerSetsNew, date, hash);
}
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
stickersById = stickersByIdNew;
stickerSetsById = stickerSetsByIdNew;
stickerSetsByName = stickerSetsByNameNew;
stickerSets = stickerSetsNew;
allStickers = allStickersNew;
stickersByEmoji = stickersByEmojiNew;
loadHash = hash;
loadDate = date;
NotificationCenter.getInstance().postNotificationName(NotificationCenter.stickersDidLoaded);
}
});
} catch (Throwable e) {
FileLog.e("messenger", e);
}
} else if (!cache) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
loadDate = date;
}
});
putStickersToCache(null, date, 0);
}
}
});
}
*/
public static void removeStickersSet(final Context context, TLRPC.StickerSet stickerSet, int hide) {
TLRPC.TL_inputStickerSetID stickerSetID = new TLRPC.TL_inputStickerSetID();
stickerSetID.access_hash = stickerSet.access_hash;
stickerSetID.id = stickerSet.id;
if (hide != 0) {
stickerSet.disabled = hide == 1;
for (int a = 0; a < stickerSets.size(); a++) {
TLRPC.TL_messages_stickerSet set = stickerSets.get(a);
if (set.set.id == stickerSet.id) {
stickerSets.remove(a);
if (hide == 2) {
stickerSets.add(0, set);
} else {
for (int b = stickerSets.size() - 1; b >= 0; b--) {
if (stickerSets.get(b).set.disabled) {
continue;
}
stickerSets.add(b + 1, set);
break;
}
}
break;
}
}
loadHash = calcStickersHash(stickerSets);
putStickersToCache(stickerSets, loadDate, loadHash);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.stickersDidLoaded);
/*TLRPC.TL_messages_installStickerSet req = new TLRPC.TL_messages_installStickerSet();
req.stickerset = stickerSetID;
req.disabled = hide == 1;
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
loadStickers(false, false);
}
}, 1000);
}
});*/
} else {
/*TLRPC.TL_messages_uninstallStickerSet req = new TLRPC.TL_messages_uninstallStickerSet();
req.stickerset = stickerSetID;
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
try {
if (error == null) {
Toast.makeText(context, LocaleController.getString("StickersRemoved", R.string.StickersRemoved), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, LocaleController.getString("ErrorOccurred", R.string.ErrorOccurred), Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
loadStickers(false, true);
}
});
}
});*/
}
}
}
@@ -1,592 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.support.util;
import android.support.annotation.UiThread;
import android.support.annotation.WorkerThread;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
/**
* A utility class that supports asynchronous content loading.
* <p>
* It can be used to load Cursor data in chunks without querying the Cursor on the UI Thread while
* keeping UI and cache synchronous for better user experience.
* <p>
* It loads the data on a background thread and keeps only a limited number of fixed sized
* chunks in memory at all times.
* <p>
* {@link AsyncListUtil} queries the currently visible range through {@link ViewCallback},
* loads the required data items in the background through {@link DataCallback}, and notifies a
* {@link ViewCallback} when the data is loaded. It may load some extra items for smoother
* scrolling.
* <p>
* Note that this class uses a single thread to load the data, so it suitable to load data from
* secondary storage such as disk, but not from network.
* <p>
* This class is designed to work with {@link android.support.v7.widget.RecyclerView}, but it does
* not depend on it and can be used with other list views.
*
*/
public class AsyncListUtil<T> {
private static final String TAG = "AsyncListUtil";
private static final boolean DEBUG = false;
final Class<T> mTClass;
final int mTileSize;
final DataCallback<T> mDataCallback;
final ViewCallback mViewCallback;
final TileList<T> mTileList;
final ThreadUtil.MainThreadCallback<T> mMainThreadProxy;
final ThreadUtil.BackgroundCallback<T> mBackgroundProxy;
final int[] mTmpRange = new int[2];
final int[] mPrevRange = new int[2];
final int[] mTmpRangeExtended = new int[2];
private boolean mAllowScrollHints;
private int mScrollHint = ViewCallback.HINT_SCROLL_NONE;
private int mItemCount = 0;
int mDisplayedGeneration = 0;
int mRequestedGeneration = mDisplayedGeneration;
final private SparseIntArray mMissingPositions = new SparseIntArray();
private void log(String s, Object... args) {
Log.d(TAG, "[MAIN] " + String.format(s, args));
}
/**
* Creates an AsyncListUtil.
*
* @param klass Class of the data item.
* @param tileSize Number of item per chunk loaded at once.
* @param dataCallback Data access callback.
* @param viewCallback Callback for querying visible item range and update notifications.
*/
public AsyncListUtil(Class<T> klass, int tileSize, DataCallback<T> dataCallback,
ViewCallback viewCallback) {
mTClass = klass;
mTileSize = tileSize;
mDataCallback = dataCallback;
mViewCallback = viewCallback;
mTileList = new TileList<T>(mTileSize);
ThreadUtil<T> threadUtil = new MessageThreadUtil<T>();
mMainThreadProxy = threadUtil.getMainThreadProxy(mMainThreadCallback);
mBackgroundProxy = threadUtil.getBackgroundProxy(mBackgroundCallback);
refresh();
}
private boolean isRefreshPending() {
return mRequestedGeneration != mDisplayedGeneration;
}
/**
* Updates the currently visible item range.
*
* <p>
* Identifies the data items that have not been loaded yet and initiates loading them in the
* background. Should be called from the view's scroll listener (such as
* {@link android.support.v7.widget.RecyclerView.OnScrollListener#onScrolled}).
*/
public void onRangeChanged() {
if (isRefreshPending()) {
return; // Will update range will the refresh result arrives.
}
updateRange();
mAllowScrollHints = true;
}
/**
* Forces reloading the data.
* <p>
* Discards all the cached data and reloads all required data items for the currently visible
* range. To be called when the data item count and/or contents has changed.
*/
public void refresh() {
mMissingPositions.clear();
mBackgroundProxy.refresh(++mRequestedGeneration);
}
/**
* Returns the data item at the given position or <code>null</code> if it has not been loaded
* yet.
*
* <p>
* If this method has been called for a specific position and returned <code>null</code>, then
* {@link ViewCallback#onItemLoaded(int)} will be called when it finally loads. Note that if
* this position stays outside of the cached item range (as defined by
* {@link ViewCallback#extendRangeInto} method), then the callback will never be called for
* this position.
*
* @param position Item position.
*
* @return The data item at the given position or <code>null</code> if it has not been loaded
* yet.
*/
public T getItem(int position) {
if (position < 0 || position >= mItemCount) {
throw new IndexOutOfBoundsException(position + " is not within 0 and " + mItemCount);
}
T item = mTileList.getItemAt(position);
if (item == null && !isRefreshPending()) {
mMissingPositions.put(position, 0);
}
return item;
}
/**
* Returns the number of items in the data set.
*
* <p>
* This is the number returned by a recent call to
* {@link DataCallback#refreshData()}.
*
* @return Number of items.
*/
public int getItemCount() {
return mItemCount;
}
private void updateRange() {
mViewCallback.getItemRangeInto(mTmpRange);
if (mTmpRange[0] > mTmpRange[1] || mTmpRange[0] < 0) {
return;
}
if (mTmpRange[1] >= mItemCount) {
// Invalid range may arrive soon after the refresh.
return;
}
if (!mAllowScrollHints) {
mScrollHint = ViewCallback.HINT_SCROLL_NONE;
} else if (mTmpRange[0] > mPrevRange[1] || mPrevRange[0] > mTmpRange[1]) {
// Ranges do not intersect, long leap not a scroll.
mScrollHint = ViewCallback.HINT_SCROLL_NONE;
} else if (mTmpRange[0] < mPrevRange[0]) {
mScrollHint = ViewCallback.HINT_SCROLL_DESC;
} else if (mTmpRange[0] > mPrevRange[0]) {
mScrollHint = ViewCallback.HINT_SCROLL_ASC;
}
mPrevRange[0] = mTmpRange[0];
mPrevRange[1] = mTmpRange[1];
mViewCallback.extendRangeInto(mTmpRange, mTmpRangeExtended, mScrollHint);
mTmpRangeExtended[0] = Math.min(mTmpRange[0], Math.max(mTmpRangeExtended[0], 0));
mTmpRangeExtended[1] =
Math.max(mTmpRange[1], Math.min(mTmpRangeExtended[1], mItemCount - 1));
mBackgroundProxy.updateRange(mTmpRange[0], mTmpRange[1],
mTmpRangeExtended[0], mTmpRangeExtended[1], mScrollHint);
}
private final ThreadUtil.MainThreadCallback<T>
mMainThreadCallback = new ThreadUtil.MainThreadCallback<T>() {
@Override
public void updateItemCount(int generation, int itemCount) {
if (DEBUG) {
log("updateItemCount: size=%d, gen #%d", itemCount, generation);
}
if (!isRequestedGeneration(generation)) {
return;
}
mItemCount = itemCount;
mViewCallback.onDataRefresh();
mDisplayedGeneration = mRequestedGeneration;
recycleAllTiles();
mAllowScrollHints = false; // Will be set to true after a first real scroll.
// There will be no scroll event if the size change does not affect the current range.
updateRange();
}
@Override
public void addTile(int generation, TileList.Tile<T> tile) {
if (!isRequestedGeneration(generation)) {
if (DEBUG) {
log("recycling an older generation tile @%d", tile.mStartPosition);
}
mBackgroundProxy.recycleTile(tile);
return;
}
TileList.Tile<T> duplicate = mTileList.addOrReplace(tile);
if (duplicate != null) {
Log.e(TAG, "duplicate tile @" + duplicate.mStartPosition);
mBackgroundProxy.recycleTile(duplicate);
}
if (DEBUG) {
log("gen #%d, added tile @%d, total tiles: %d",
generation, tile.mStartPosition, mTileList.size());
}
int endPosition = tile.mStartPosition + tile.mItemCount;
int index = 0;
while (index < mMissingPositions.size()) {
final int position = mMissingPositions.keyAt(index);
if (tile.mStartPosition <= position && position < endPosition) {
mMissingPositions.removeAt(index);
mViewCallback.onItemLoaded(position);
} else {
index++;
}
}
}
@Override
public void removeTile(int generation, int position) {
if (!isRequestedGeneration(generation)) {
return;
}
TileList.Tile<T> tile = mTileList.removeAtPos(position);
if (tile == null) {
Log.e(TAG, "tile not found @" + position);
return;
}
if (DEBUG) {
log("recycling tile @%d, total tiles: %d", tile.mStartPosition, mTileList.size());
}
mBackgroundProxy.recycleTile(tile);
}
private void recycleAllTiles() {
if (DEBUG) {
log("recycling all %d tiles", mTileList.size());
}
for (int i = 0; i < mTileList.size(); i++) {
mBackgroundProxy.recycleTile(mTileList.getAtIndex(i));
}
mTileList.clear();
}
private boolean isRequestedGeneration(int generation) {
return generation == mRequestedGeneration;
}
};
private final ThreadUtil.BackgroundCallback<T>
mBackgroundCallback = new ThreadUtil.BackgroundCallback<T>() {
private TileList.Tile<T> mRecycledRoot;
final SparseBooleanArray mLoadedTiles = new SparseBooleanArray();
private int mGeneration;
private int mItemCount;
private int mFirstRequiredTileStart;
private int mLastRequiredTileStart;
@Override
public void refresh(int generation) {
mGeneration = generation;
mLoadedTiles.clear();
mItemCount = mDataCallback.refreshData();
mMainThreadProxy.updateItemCount(mGeneration, mItemCount);
}
@Override
public void updateRange(int rangeStart, int rangeEnd, int extRangeStart, int extRangeEnd,
int scrollHint) {
if (DEBUG) {
log("updateRange: %d..%d extended to %d..%d, scroll hint: %d",
rangeStart, rangeEnd, extRangeStart, extRangeEnd, scrollHint);
}
if (rangeStart > rangeEnd) {
return;
}
final int firstVisibleTileStart = getTileStart(rangeStart);
final int lastVisibleTileStart = getTileStart(rangeEnd);
mFirstRequiredTileStart = getTileStart(extRangeStart);
mLastRequiredTileStart = getTileStart(extRangeEnd);
if (DEBUG) {
log("requesting tile range: %d..%d",
mFirstRequiredTileStart, mLastRequiredTileStart);
}
// All pending tile requests are removed by ThreadUtil at this point.
// Re-request all required tiles in the most optimal order.
if (scrollHint == ViewCallback.HINT_SCROLL_DESC) {
requestTiles(mFirstRequiredTileStart, lastVisibleTileStart, scrollHint, true);
requestTiles(lastVisibleTileStart + mTileSize, mLastRequiredTileStart, scrollHint,
false);
} else {
requestTiles(firstVisibleTileStart, mLastRequiredTileStart, scrollHint, false);
requestTiles(mFirstRequiredTileStart, firstVisibleTileStart - mTileSize, scrollHint,
true);
}
}
private int getTileStart(int position) {
return position - position % mTileSize;
}
private void requestTiles(int firstTileStart, int lastTileStart, int scrollHint,
boolean backwards) {
for (int i = firstTileStart; i <= lastTileStart; i += mTileSize) {
int tileStart = backwards ? (lastTileStart + firstTileStart - i) : i;
if (DEBUG) {
log("requesting tile @%d", tileStart);
}
mBackgroundProxy.loadTile(tileStart, scrollHint);
}
}
@Override
public void loadTile(int position, int scrollHint) {
if (isTileLoaded(position)) {
if (DEBUG) {
log("already loaded tile @%d", position);
}
return;
}
TileList.Tile<T> tile = acquireTile();
tile.mStartPosition = position;
tile.mItemCount = Math.min(mTileSize, mItemCount - tile.mStartPosition);
mDataCallback.fillData(tile.mItems, tile.mStartPosition, tile.mItemCount);
flushTileCache(scrollHint);
addTile(tile);
}
@Override
public void recycleTile(TileList.Tile<T> tile) {
if (DEBUG) {
log("recycling tile @%d", tile.mStartPosition);
}
mDataCallback.recycleData(tile.mItems, tile.mItemCount);
tile.mNext = mRecycledRoot;
mRecycledRoot = tile;
}
private TileList.Tile<T> acquireTile() {
if (mRecycledRoot != null) {
TileList.Tile<T> result = mRecycledRoot;
mRecycledRoot = mRecycledRoot.mNext;
return result;
}
return new TileList.Tile<T>(mTClass, mTileSize);
}
private boolean isTileLoaded(int position) {
return mLoadedTiles.get(position);
}
private void addTile(TileList.Tile<T> tile) {
mLoadedTiles.put(tile.mStartPosition, true);
mMainThreadProxy.addTile(mGeneration, tile);
if (DEBUG) {
log("loaded tile @%d, total tiles: %d", tile.mStartPosition, mLoadedTiles.size());
}
}
private void removeTile(int position) {
mLoadedTiles.delete(position);
mMainThreadProxy.removeTile(mGeneration, position);
if (DEBUG) {
log("flushed tile @%d, total tiles: %s", position, mLoadedTiles.size());
}
}
private void flushTileCache(int scrollHint) {
final int cacheSizeLimit = mDataCallback.getMaxCachedTiles();
while (mLoadedTiles.size() >= cacheSizeLimit) {
int firstLoadedTileStart = mLoadedTiles.keyAt(0);
int lastLoadedTileStart = mLoadedTiles.keyAt(mLoadedTiles.size() - 1);
int startMargin = mFirstRequiredTileStart - firstLoadedTileStart;
int endMargin = lastLoadedTileStart - mLastRequiredTileStart;
if (startMargin > 0 && (startMargin >= endMargin ||
(scrollHint == ViewCallback.HINT_SCROLL_ASC))) {
removeTile(firstLoadedTileStart);
} else if (endMargin > 0 && (startMargin < endMargin ||
(scrollHint == ViewCallback.HINT_SCROLL_DESC))){
removeTile(lastLoadedTileStart);
} else {
// Could not flush on either side, bail out.
return;
}
}
}
private void log(String s, Object... args) {
Log.d(TAG, "[BKGR] " + String.format(s, args));
}
};
/**
* The callback that provides data access for {@link AsyncListUtil}.
*
* <p>
* All methods are called on the background thread.
*/
public static abstract class DataCallback<T> {
/**
* Refresh the data set and return the new data item count.
*
* <p>
* If the data is being accessed through {@link android.database.Cursor} this is where
* the new cursor should be created.
*
* @return Data item count.
*/
@WorkerThread
public abstract int refreshData();
/**
* Fill the given tile.
*
* <p>
* The provided tile might be a recycled tile, in which case it will already have objects.
* It is suggested to re-use these objects if possible in your use case.
*
* @param startPosition The start position in the list.
* @param itemCount The data item count.
* @param data The data item array to fill into. Should not be accessed beyond
* <code>itemCount</code>.
*/
@WorkerThread
public abstract void fillData(T[] data, int startPosition, int itemCount);
/**
* Recycle the objects created in {@link #fillData} if necessary.
*
*
* @param data Array of data items. Should not be accessed beyond <code>itemCount</code>.
* @param itemCount The data item count.
*/
@WorkerThread
public void recycleData(T[] data, int itemCount) {
}
/**
* Returns tile cache size limit (in tiles).
*
* <p>
* The actual number of cached tiles will be the maximum of this value and the number of
* tiles that is required to cover the range returned by
* {@link ViewCallback#extendRangeInto(int[], int[], int)}.
* <p>
* For example, if this method returns 10, and the most
* recent call to {@link ViewCallback#extendRangeInto(int[], int[], int)} returned
* {100, 179}, and the tile size is 5, then the maximum number of cached tiles will be 16.
* <p>
* However, if the tile size is 20, then the maximum number of cached tiles will be 10.
* <p>
* The default implementation returns 10.
*
* @return Maximum cache size.
*/
@WorkerThread
public int getMaxCachedTiles() {
return 10;
}
}
/**
* The callback that links {@link AsyncListUtil} with the list view.
*
* <p>
* All methods are called on the main thread.
*/
public static abstract class ViewCallback {
/**
* No scroll direction hint available.
*/
public static final int HINT_SCROLL_NONE = 0;
/**
* Scrolling in descending order (from higher to lower positions in the order of the backing
* storage).
*/
public static final int HINT_SCROLL_DESC = 1;
/**
* Scrolling in ascending order (from lower to higher positions in the order of the backing
* storage).
*/
public static final int HINT_SCROLL_ASC = 2;
/**
* Compute the range of visible item positions.
* <p>
* outRange[0] is the position of the first visible item (in the order of the backing
* storage).
* <p>
* outRange[1] is the position of the last visible item (in the order of the backing
* storage).
* <p>
* Negative positions and positions greater or equal to {@link #getItemCount} are invalid.
* If the returned range contains invalid positions it is ignored (no item will be loaded).
*
* @param outRange The visible item range.
*/
@UiThread
public abstract void getItemRangeInto(int[] outRange);
/**
* Compute a wider range of items that will be loaded for smoother scrolling.
*
* <p>
* If there is no scroll hint, the default implementation extends the visible range by half
* its length in both directions. If there is a scroll hint, the range is extended by
* its full length in the scroll direction, and by half in the other direction.
* <p>
* For example, if <code>range</code> is <code>{100, 200}</code> and <code>scrollHint</code>
* is {@link #HINT_SCROLL_ASC}, then <code>outRange</code> will be <code>{50, 300}</code>.
* <p>
* However, if <code>scrollHint</code> is {@link #HINT_SCROLL_NONE}, then
* <code>outRange</code> will be <code>{50, 250}</code>
*
* @param range Visible item range.
* @param outRange Extended range.
* @param scrollHint The scroll direction hint.
*/
@UiThread
public void extendRangeInto(int[] range, int[] outRange, int scrollHint) {
final int fullRange = range[1] - range[0] + 1;
final int halfRange = fullRange / 2;
outRange[0] = range[0] - (scrollHint == HINT_SCROLL_DESC ? fullRange : halfRange);
outRange[1] = range[1] + (scrollHint == HINT_SCROLL_ASC ? fullRange : halfRange);
}
/**
* Called when the entire data set has changed.
*/
@UiThread
public abstract void onDataRefresh();
/**
* Called when an item at the given position is loaded.
* @param position Item position.
*/
@UiThread
public abstract void onItemLoaded(int position);
}
}
@@ -1,281 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.support.util;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.content.ParallelExecutorCompat;
import android.util.Log;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
class MessageThreadUtil<T> implements ThreadUtil<T> {
public MainThreadCallback<T> getMainThreadProxy(final MainThreadCallback<T> callback) {
return new MainThreadCallback<T>() {
final private MessageQueue mQueue = new MessageQueue();
final private Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private static final int UPDATE_ITEM_COUNT = 1;
private static final int ADD_TILE = 2;
private static final int REMOVE_TILE = 3;
@Override
public void updateItemCount(int generation, int itemCount) {
sendMessage(SyncQueueItem.obtainMessage(UPDATE_ITEM_COUNT, generation, itemCount));
}
@Override
public void addTile(int generation, TileList.Tile<T> tile) {
sendMessage(SyncQueueItem.obtainMessage(ADD_TILE, generation, tile));
}
@Override
public void removeTile(int generation, int position) {
sendMessage(SyncQueueItem.obtainMessage(REMOVE_TILE, generation, position));
}
private void sendMessage(SyncQueueItem msg) {
mQueue.sendMessage(msg);
mMainThreadHandler.post(mMainThreadRunnable);
}
private Runnable mMainThreadRunnable = new Runnable() {
@Override
public void run() {
SyncQueueItem msg = mQueue.next();
while (msg != null) {
switch (msg.what) {
case UPDATE_ITEM_COUNT:
callback.updateItemCount(msg.arg1, msg.arg2);
break;
case ADD_TILE:
//noinspection unchecked
callback.addTile(msg.arg1, (TileList.Tile<T>) msg.data);
break;
case REMOVE_TILE:
callback.removeTile(msg.arg1, msg.arg2);
break;
default:
Log.e("ThreadUtil", "Unsupported message, what=" + msg.what);
}
msg = mQueue.next();
}
}
};
};
}
public BackgroundCallback<T> getBackgroundProxy(final BackgroundCallback<T> callback) {
return new BackgroundCallback<T>() {
final private MessageQueue mQueue = new MessageQueue();
final private Executor mExecutor = ParallelExecutorCompat.getParallelExecutor();
AtomicBoolean mBackgroundRunning = new AtomicBoolean(false);
private static final int REFRESH = 1;
private static final int UPDATE_RANGE = 2;
private static final int LOAD_TILE = 3;
private static final int RECYCLE_TILE = 4;
@Override
public void refresh(int generation) {
sendMessageAtFrontOfQueue(SyncQueueItem.obtainMessage(REFRESH, generation, null));
}
@Override
public void updateRange(int rangeStart, int rangeEnd,
int extRangeStart, int extRangeEnd, int scrollHint) {
sendMessageAtFrontOfQueue(SyncQueueItem.obtainMessage(UPDATE_RANGE,
rangeStart, rangeEnd, extRangeStart, extRangeEnd, scrollHint, null));
}
@Override
public void loadTile(int position, int scrollHint) {
sendMessage(SyncQueueItem.obtainMessage(LOAD_TILE, position, scrollHint));
}
@Override
public void recycleTile(TileList.Tile<T> tile) {
sendMessage(SyncQueueItem.obtainMessage(RECYCLE_TILE, 0, tile));
}
private void sendMessage(SyncQueueItem msg) {
mQueue.sendMessage(msg);
maybeExecuteBackgroundRunnable();
}
private void sendMessageAtFrontOfQueue(SyncQueueItem msg) {
mQueue.sendMessageAtFrontOfQueue(msg);
maybeExecuteBackgroundRunnable();
}
private void maybeExecuteBackgroundRunnable() {
if (mBackgroundRunning.compareAndSet(false, true)) {
mExecutor.execute(mBackgroundRunnable);
}
}
private Runnable mBackgroundRunnable = new Runnable() {
@Override
public void run() {
while (true) {
SyncQueueItem msg = mQueue.next();
if (msg == null) {
break;
}
switch (msg.what) {
case REFRESH:
mQueue.removeMessages(REFRESH);
callback.refresh(msg.arg1);
break;
case UPDATE_RANGE:
mQueue.removeMessages(UPDATE_RANGE);
mQueue.removeMessages(LOAD_TILE);
callback.updateRange(
msg.arg1, msg.arg2, msg.arg3, msg.arg4, msg.arg5);
break;
case LOAD_TILE:
callback.loadTile(msg.arg1, msg.arg2);
break;
case RECYCLE_TILE:
//noinspection unchecked
callback.recycleTile((TileList.Tile<T>) msg.data);
break;
default:
Log.e("ThreadUtil", "Unsupported message, what=" + msg.what);
}
}
mBackgroundRunning.set(false);
}
};
};
}
/**
* Replica of android.os.Message. Unfortunately, cannot use it without a Handler and don't want
* to create a thread just for this component.
*/
static class SyncQueueItem {
private static SyncQueueItem sPool;
private static final Object sPoolLock = new Object();
private SyncQueueItem next;
public int what;
public int arg1;
public int arg2;
public int arg3;
public int arg4;
public int arg5;
public Object data;
void recycle() {
next = null;
what = arg1 = arg2 = arg3 = arg4 = arg5 = 0;
data = null;
synchronized (sPoolLock) {
if (sPool != null) {
next = sPool;
}
sPool = this;
}
}
static SyncQueueItem obtainMessage(int what, int arg1, int arg2, int arg3, int arg4,
int arg5, Object data) {
synchronized (sPoolLock) {
final SyncQueueItem item;
if (sPool == null) {
item = new SyncQueueItem();
} else {
item = sPool;
sPool = sPool.next;
item.next = null;
}
item.what = what;
item.arg1 = arg1;
item.arg2 = arg2;
item.arg3 = arg3;
item.arg4 = arg4;
item.arg5 = arg5;
item.data = data;
return item;
}
}
static SyncQueueItem obtainMessage(int what, int arg1, int arg2) {
return obtainMessage(what, arg1, arg2, 0, 0, 0, null);
}
static SyncQueueItem obtainMessage(int what, int arg1, Object data) {
return obtainMessage(what, arg1, 0, 0, 0, 0, data);
}
}
static class MessageQueue {
private SyncQueueItem mRoot;
synchronized SyncQueueItem next() {
if (mRoot == null) {
return null;
}
final SyncQueueItem next = mRoot;
mRoot = mRoot.next;
return next;
}
synchronized void sendMessageAtFrontOfQueue(SyncQueueItem item) {
item.next = mRoot;
mRoot = item;
}
synchronized void sendMessage(SyncQueueItem item) {
if (mRoot == null) {
mRoot = item;
return;
}
SyncQueueItem last = mRoot;
while (last.next != null) {
last = last.next;
}
last.next = item;
}
synchronized void removeMessages(int what) {
while (mRoot != null && mRoot.what == what) {
SyncQueueItem item = mRoot;
mRoot = mRoot.next;
item.recycle();
}
if (mRoot != null) {
SyncQueueItem prev = mRoot;
SyncQueueItem item = prev.next;
while (item != null) {
SyncQueueItem next = item.next;
if (item.what == what) {
prev.next = next;
item.recycle();
} else {
prev = item;
}
item = next;
}
}
}
}
}
@@ -1,891 +0,0 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.support.util;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
/**
* A Sorted list implementation that can keep items in order and also notify for changes in the
* list
* such that it can be bound to a {@link android.support.v7.widget.RecyclerView.Adapter
* RecyclerView.Adapter}.
* <p>
* It keeps items ordered using the {@link Callback#compare(Object, Object)} method and uses
* binary search to retrieve items. If the sorting criteria of your items may change, make sure you
* call appropriate methods while editing them to avoid data inconsistencies.
* <p>
* You can control the order of items and change notifications via the {@link Callback} parameter.
*/
@SuppressWarnings("unchecked")
public class SortedList<T> {
/**
* Used by {@link #indexOf(Object)} when he item cannot be found in the list.
*/
public static final int INVALID_POSITION = -1;
private static final int MIN_CAPACITY = 10;
private static final int CAPACITY_GROWTH = MIN_CAPACITY;
private static final int INSERTION = 1;
private static final int DELETION = 1 << 1;
private static final int LOOKUP = 1 << 2;
T[] mData;
/**
* A copy of the previous list contents used during the merge phase of addAll.
*/
private T[] mOldData;
private int mOldDataStart;
private int mOldDataSize;
/**
* The size of the valid portion of mData during the merge phase of addAll.
*/
private int mMergedSize;
/**
* The callback instance that controls the behavior of the SortedList and get notified when
* changes happen.
*/
private Callback mCallback;
private BatchedCallback mBatchedCallback;
private int mSize;
private final Class<T> mTClass;
/**
* Creates a new SortedList of type T.
*
* @param klass The class of the contents of the SortedList.
* @param callback The callback that controls the behavior of SortedList.
*/
public SortedList(Class<T> klass, Callback<T> callback) {
this(klass, callback, MIN_CAPACITY);
}
/**
* Creates a new SortedList of type T.
*
* @param klass The class of the contents of the SortedList.
* @param callback The callback that controls the behavior of SortedList.
* @param initialCapacity The initial capacity to hold items.
*/
public SortedList(Class<T> klass, Callback<T> callback, int initialCapacity) {
mTClass = klass;
mData = (T[]) Array.newInstance(klass, initialCapacity);
mCallback = callback;
mSize = 0;
}
/**
* The number of items in the list.
*
* @return The number of items in the list.
*/
public int size() {
return mSize;
}
/**
* Adds the given item to the list. If this is a new item, SortedList calls
* {@link Callback#onInserted(int, int)}.
* <p>
* If the item already exists in the list and its sorting criteria is not changed, it is
* replaced with the existing Item. SortedList uses
* {@link Callback#areItemsTheSame(Object, Object)} to check if two items are the same item
* and uses {@link Callback#areContentsTheSame(Object, Object)} to decide whether it should
* call {@link Callback#onChanged(int, int)} or not. In both cases, it always removes the
* reference to the old item and puts the new item into the backing array even if
* {@link Callback#areContentsTheSame(Object, Object)} returns false.
* <p>
* If the sorting criteria of the item is changed, SortedList won't be able to find
* its duplicate in the list which will result in having a duplicate of the Item in the list.
* If you need to update sorting criteria of an item that already exists in the list,
* use {@link #updateItemAt(int, Object)}. You can find the index of the item using
* {@link #indexOf(Object)} before you update the object.
*
* @param item The item to be added into the list.
*
* @return The index of the newly added item.
* @see {@link Callback#compare(Object, Object)}
* @see {@link Callback#areItemsTheSame(Object, Object)}
* @see {@link Callback#areContentsTheSame(Object, Object)}}
*/
public int add(T item) {
throwIfMerging();
return add(item, true);
}
/**
* Adds the given items to the list. Equivalent to calling {@link SortedList#add} in a loop,
* except the callback events may be in a different order/granularity since addAll can batch
* them for better performance.
* <p>
* If allowed, may modify the input array and even take the ownership over it in order
* to avoid extra memory allocation during sorting and deduplication.
* </p>
* @param items Array of items to be added into the list.
* @param mayModifyInput If true, SortedList is allowed to modify the input.
* @see {@link SortedList#addAll(T[] items)}.
*/
public void addAll(T[] items, boolean mayModifyInput) {
throwIfMerging();
if (items.length == 0) {
return;
}
if (mayModifyInput) {
addAllInternal(items);
} else {
T[] copy = (T[]) Array.newInstance(mTClass, items.length);
System.arraycopy(items, 0, copy, 0, items.length);
addAllInternal(copy);
}
}
/**
* Adds the given items to the list. Does not modify the input.
*
* @see {@link SortedList#addAll(T[] items, boolean mayModifyInput)}
*
* @param items Array of items to be added into the list.
*/
public void addAll(T... items) {
addAll(items, false);
}
/**
* Adds the given items to the list. Does not modify the input.
*
* @see {@link SortedList#addAll(T[] items, boolean mayModifyInput)}
*
* @param items Collection of items to be added into the list.
*/
public void addAll(Collection<T> items) {
T[] copy = (T[]) Array.newInstance(mTClass, items.size());
addAll(items.toArray(copy), true);
}
private void addAllInternal(T[] newItems) {
final boolean forceBatchedUpdates = !(mCallback instanceof BatchedCallback);
if (forceBatchedUpdates) {
beginBatchedUpdates();
}
mOldData = mData;
mOldDataStart = 0;
mOldDataSize = mSize;
Arrays.sort(newItems, mCallback); // Arrays.sort is stable.
final int newSize = deduplicate(newItems);
if (mSize == 0) {
mData = newItems;
mSize = newSize;
mMergedSize = newSize;
mCallback.onInserted(0, newSize);
} else {
merge(newItems, newSize);
}
mOldData = null;
if (forceBatchedUpdates) {
endBatchedUpdates();
}
}
/**
* Remove duplicate items, leaving only the last item from each group of "same" items.
* Move the remaining items to the beginning of the array.
*
* @return Number of deduplicated items at the beginning of the array.
*/
private int deduplicate(T[] items) {
if (items.length == 0) {
throw new IllegalArgumentException("Input array must be non-empty");
}
// Keep track of the range of equal items at the end of the output.
// Start with the range containing just the first item.
int rangeStart = 0;
int rangeEnd = 1;
for (int i = 1; i < items.length; ++i) {
T currentItem = items[i];
int compare = mCallback.compare(items[rangeStart], currentItem);
if (compare > 0) {
throw new IllegalArgumentException("Input must be sorted in ascending order.");
}
if (compare == 0) {
// The range of equal items continues, update it.
final int sameItemPos = findSameItem(currentItem, items, rangeStart, rangeEnd);
if (sameItemPos != INVALID_POSITION) {
// Replace the duplicate item.
items[sameItemPos] = currentItem;
} else {
// Expand the range.
if (rangeEnd != i) { // Avoid redundant copy.
items[rangeEnd] = currentItem;
}
rangeEnd++;
}
} else {
// The range has ended. Reset it to contain just the current item.
if (rangeEnd != i) { // Avoid redundant copy.
items[rangeEnd] = currentItem;
}
rangeStart = rangeEnd++;
}
}
return rangeEnd;
}
private int findSameItem(T item, T[] items, int from, int to) {
for (int pos = from; pos < to; pos++) {
if (mCallback.areItemsTheSame(items[pos], item)) {
return pos;
}
}
return INVALID_POSITION;
}
/**
* This method assumes that newItems are sorted and deduplicated.
*/
private void merge(T[] newData, int newDataSize) {
final int mergedCapacity = mSize + newDataSize + CAPACITY_GROWTH;
mData = (T[]) Array.newInstance(mTClass, mergedCapacity);
mMergedSize = 0;
int newDataStart = 0;
while (mOldDataStart < mOldDataSize || newDataStart < newDataSize) {
if (mOldDataStart == mOldDataSize) {
// No more old items, copy the remaining new items.
int itemCount = newDataSize - newDataStart;
System.arraycopy(newData, newDataStart, mData, mMergedSize, itemCount);
mMergedSize += itemCount;
mSize += itemCount;
mCallback.onInserted(mMergedSize - itemCount, itemCount);
break;
}
if (newDataStart == newDataSize) {
// No more new items, copy the remaining old items.
int itemCount = mOldDataSize - mOldDataStart;
System.arraycopy(mOldData, mOldDataStart, mData, mMergedSize, itemCount);
mMergedSize += itemCount;
break;
}
T oldItem = mOldData[mOldDataStart];
T newItem = newData[newDataStart];
int compare = mCallback.compare(oldItem, newItem);
if (compare > 0) {
// New item is lower, output it.
mData[mMergedSize++] = newItem;
mSize++;
newDataStart++;
mCallback.onInserted(mMergedSize - 1, 1);
} else if (compare == 0 && mCallback.areItemsTheSame(oldItem, newItem)) {
// Items are the same. Output the new item, but consume both.
mData[mMergedSize++] = newItem;
newDataStart++;
mOldDataStart++;
if (!mCallback.areContentsTheSame(oldItem, newItem)) {
mCallback.onChanged(mMergedSize - 1, 1);
}
} else {
// Old item is lower than or equal to (but not the same as the new). Output it.
// New item with the same sort order will be inserted later.
mData[mMergedSize++] = oldItem;
mOldDataStart++;
}
}
}
private void throwIfMerging() {
if (mOldData != null) {
throw new IllegalStateException("Cannot call this method from within addAll");
}
}
/**
* Batches adapter updates that happen between calling this method until calling
* {@link #endBatchedUpdates()}. For example, if you add multiple items in a loop
* and they are placed into consecutive indices, SortedList calls
* {@link Callback#onInserted(int, int)} only once with the proper item count. If an event
* cannot be merged with the previous event, the previous event is dispatched
* to the callback instantly.
* <p>
* After running your data updates, you <b>must</b> call {@link #endBatchedUpdates()}
* which will dispatch any deferred data change event to the current callback.
* <p>
* A sample implementation may look like this:
* <pre>
* mSortedList.beginBatchedUpdates();
* try {
* mSortedList.add(item1)
* mSortedList.add(item2)
* mSortedList.remove(item3)
* ...
* } finally {
* mSortedList.endBatchedUpdates();
* }
* </pre>
* <p>
* Instead of using this method to batch calls, you can use a Callback that extends
* {@link BatchedCallback}. In that case, you must make sure that you are manually calling
* {@link BatchedCallback#dispatchLastEvent()} right after you complete your data changes.
* Failing to do so may create data inconsistencies with the Callback.
* <p>
* If the current Callback in an instance of {@link BatchedCallback}, calling this method
* has no effect.
*/
public void beginBatchedUpdates() {
throwIfMerging();
if (mCallback instanceof BatchedCallback) {
return;
}
if (mBatchedCallback == null) {
mBatchedCallback = new BatchedCallback(mCallback);
}
mCallback = mBatchedCallback;
}
/**
* Ends the update transaction and dispatches any remaining event to the callback.
*/
public void endBatchedUpdates() {
throwIfMerging();
if (mCallback instanceof BatchedCallback) {
((BatchedCallback) mCallback).dispatchLastEvent();
}
if (mCallback == mBatchedCallback) {
mCallback = mBatchedCallback.mWrappedCallback;
}
}
private int add(T item, boolean notify) {
int index = findIndexOf(item, mData, 0, mSize, INSERTION);
if (index == INVALID_POSITION) {
index = 0;
} else if (index < mSize) {
T existing = mData[index];
if (mCallback.areItemsTheSame(existing, item)) {
if (mCallback.areContentsTheSame(existing, item)) {
//no change but still replace the item
mData[index] = item;
return index;
} else {
mData[index] = item;
mCallback.onChanged(index, 1);
return index;
}
}
}
addToData(index, item);
if (notify) {
mCallback.onInserted(index, 1);
}
return index;
}
/**
* Removes the provided item from the list and calls {@link Callback#onRemoved(int, int)}.
*
* @param item The item to be removed from the list.
*
* @return True if item is removed, false if item cannot be found in the list.
*/
public boolean remove(T item) {
throwIfMerging();
return remove(item, true);
}
/**
* Removes the item at the given index and calls {@link Callback#onRemoved(int, int)}.
*
* @param index The index of the item to be removed.
*
* @return The removed item.
*/
public T removeItemAt(int index) {
throwIfMerging();
T item = get(index);
removeItemAtIndex(index, true);
return item;
}
private boolean remove(T item, boolean notify) {
int index = findIndexOf(item, mData, 0, mSize, DELETION);
if (index == INVALID_POSITION) {
return false;
}
removeItemAtIndex(index, notify);
return true;
}
private void removeItemAtIndex(int index, boolean notify) {
System.arraycopy(mData, index + 1, mData, index, mSize - index - 1);
mSize--;
mData[mSize] = null;
if (notify) {
mCallback.onRemoved(index, 1);
}
}
/**
* Updates the item at the given index and calls {@link Callback#onChanged(int, int)} and/or
* {@link Callback#onMoved(int, int)} if necessary.
* <p>
* You can use this method if you need to change an existing Item such that its position in the
* list may change.
* <p>
* If the new object is a different object (<code>get(index) != item</code>) and
* {@link Callback#areContentsTheSame(Object, Object)} returns <code>true</code>, SortedList
* avoids calling {@link Callback#onChanged(int, int)} otherwise it calls
* {@link Callback#onChanged(int, int)}.
* <p>
* If the new position of the item is different than the provided <code>index</code>,
* SortedList
* calls {@link Callback#onMoved(int, int)}.
*
* @param index The index of the item to replace
* @param item The item to replace the item at the given Index.
* @see #add(Object)
*/
public void updateItemAt(int index, T item) {
throwIfMerging();
final T existing = get(index);
// assume changed if the same object is given back
boolean contentsChanged = existing == item || !mCallback.areContentsTheSame(existing, item);
if (existing != item) {
// different items, we can use comparison and may avoid lookup
final int cmp = mCallback.compare(existing, item);
if (cmp == 0) {
mData[index] = item;
if (contentsChanged) {
mCallback.onChanged(index, 1);
}
return;
}
}
if (contentsChanged) {
mCallback.onChanged(index, 1);
}
// TODO this done in 1 pass to avoid shifting twice.
removeItemAtIndex(index, false);
int newIndex = add(item, false);
if (index != newIndex) {
mCallback.onMoved(index, newIndex);
}
}
/**
* This method can be used to recalculate the position of the item at the given index, without
* triggering an {@link Callback#onChanged(int, int)} callback.
* <p>
* If you are editing objects in the list such that their position in the list may change but
* you don't want to trigger an onChange animation, you can use this method to re-position it.
* If the item changes position, SortedList will call {@link Callback#onMoved(int, int)}
* without
* calling {@link Callback#onChanged(int, int)}.
* <p>
* A sample usage may look like:
*
* <pre>
* final int position = mSortedList.indexOf(item);
* item.incrementPriority(); // assume items are sorted by priority
* mSortedList.recalculatePositionOfItemAt(position);
* </pre>
* In the example above, because the sorting criteria of the item has been changed,
* mSortedList.indexOf(item) will not be able to find the item. This is why the code above
* first
* gets the position before editing the item, edits it and informs the SortedList that item
* should be repositioned.
*
* @param index The current index of the Item whose position should be re-calculated.
* @see #updateItemAt(int, Object)
* @see #add(Object)
*/
public void recalculatePositionOfItemAt(int index) {
throwIfMerging();
// TODO can be improved
final T item = get(index);
removeItemAtIndex(index, false);
int newIndex = add(item, false);
if (index != newIndex) {
mCallback.onMoved(index, newIndex);
}
}
/**
* Returns the item at the given index.
*
* @param index The index of the item to retrieve.
*
* @return The item at the given index.
* @throws java.lang.IndexOutOfBoundsException if provided index is negative or larger than the
* size of the list.
*/
public T get(int index) throws IndexOutOfBoundsException {
if (index >= mSize || index < 0) {
throw new IndexOutOfBoundsException("Asked to get item at " + index + " but size is "
+ mSize);
}
if (mOldData != null) {
// The call is made from a callback during addAll execution. The data is split
// between mData and mOldData.
if (index >= mMergedSize) {
return mOldData[index - mMergedSize + mOldDataStart];
}
}
return mData[index];
}
/**
* Returns the position of the provided item.
*
* @param item The item to query for position.
*
* @return The position of the provided item or {@link #INVALID_POSITION} if item is not in the
* list.
*/
public int indexOf(T item) {
if (mOldData != null) {
int index = findIndexOf(item, mData, 0, mMergedSize, LOOKUP);
if (index != INVALID_POSITION) {
return index;
}
index = findIndexOf(item, mOldData, mOldDataStart, mOldDataSize, LOOKUP);
if (index != INVALID_POSITION) {
return index - mOldDataStart + mMergedSize;
}
return INVALID_POSITION;
}
return findIndexOf(item, mData, 0, mSize, LOOKUP);
}
private int findIndexOf(T item, T[] mData, int left, int right, int reason) {
while (left < right) {
final int middle = (left + right) / 2;
T myItem = mData[middle];
final int cmp = mCallback.compare(myItem, item);
if (cmp < 0) {
left = middle + 1;
} else if (cmp == 0) {
if (mCallback.areItemsTheSame(myItem, item)) {
return middle;
} else {
int exact = linearEqualitySearch(item, middle, left, right);
if (reason == INSERTION) {
return exact == INVALID_POSITION ? middle : exact;
} else {
return exact;
}
}
} else {
right = middle;
}
}
return reason == INSERTION ? left : INVALID_POSITION;
}
private int linearEqualitySearch(T item, int middle, int left, int right) {
// go left
for (int next = middle - 1; next >= left; next--) {
T nextItem = mData[next];
int cmp = mCallback.compare(nextItem, item);
if (cmp != 0) {
break;
}
if (mCallback.areItemsTheSame(nextItem, item)) {
return next;
}
}
for (int next = middle + 1; next < right; next++) {
T nextItem = mData[next];
int cmp = mCallback.compare(nextItem, item);
if (cmp != 0) {
break;
}
if (mCallback.areItemsTheSame(nextItem, item)) {
return next;
}
}
return INVALID_POSITION;
}
private void addToData(int index, T item) {
if (index > mSize) {
throw new IndexOutOfBoundsException(
"cannot add item to " + index + " because size is " + mSize);
}
if (mSize == mData.length) {
// we are at the limit enlarge
T[] newData = (T[]) Array.newInstance(mTClass, mData.length + CAPACITY_GROWTH);
System.arraycopy(mData, 0, newData, 0, index);
newData[index] = item;
System.arraycopy(mData, index, newData, index + 1, mSize - index);
mData = newData;
} else {
// just shift, we fit
System.arraycopy(mData, index, mData, index + 1, mSize - index);
mData[index] = item;
}
mSize++;
}
/**
* Removes all items from the SortedList.
*/
public void clear() {
throwIfMerging();
if (mSize == 0) {
return;
}
final int prevSize = mSize;
Arrays.fill(mData, 0, prevSize, null);
mSize = 0;
mCallback.onRemoved(0, prevSize);
}
/**
* The class that controls the behavior of the {@link SortedList}.
* <p>
* It defines how items should be sorted and how duplicates should be handled.
* <p>
* SortedList calls the callback methods on this class to notify changes about the underlying
* data.
*/
public static abstract class Callback<T2> implements Comparator<T2> {
/**
* Similar to {@link java.util.Comparator#compare(Object, Object)}, should compare two and
* return how they should be ordered.
*
* @param o1 The first object to compare.
* @param o2 The second object to compare.
*
* @return a negative integer, zero, or a positive integer as the
* first argument is less than, equal to, or greater than the
* second.
*/
abstract public int compare(T2 o1, T2 o2);
/**
* Called by the SortedList when an item is inserted at the given position.
*
* @param position The position of the new item.
* @param count The number of items that have been added.
*/
abstract public void onInserted(int position, int count);
/**
* Called by the SortedList when an item is removed from the given position.
*
* @param position The position of the item which has been removed.
* @param count The number of items which have been removed.
*/
abstract public void onRemoved(int position, int count);
/**
* Called by the SortedList when an item changes its position in the list.
*
* @param fromPosition The previous position of the item before the move.
* @param toPosition The new position of the item.
*/
abstract public void onMoved(int fromPosition, int toPosition);
/**
* Called by the SortedList when the item at the given position is updated.
*
* @param position The position of the item which has been updated.
* @param count The number of items which has changed.
*/
abstract public void onChanged(int position, int count);
/**
* Called by the SortedList when it wants to check whether two items have the same data
* or not. SortedList uses this information to decide whether it should call
* {@link #onChanged(int, int)} or not.
* <p>
* SortedList uses this method to check equality instead of {@link Object#equals(Object)}
* so
* that you can change its behavior depending on your UI.
* <p>
* For example, if you are using SortedList with a {@link android.support.v7.widget.RecyclerView.Adapter
* RecyclerView.Adapter}, you should
* return whether the items' visual representations are the same or not.
*
* @param oldItem The previous representation of the object.
* @param newItem The new object that replaces the previous one.
*
* @return True if the contents of the items are the same or false if they are different.
*/
abstract public boolean areContentsTheSame(T2 oldItem, T2 newItem);
/**
* Called by the SortedList to decide whether two object represent the same Item or not.
* <p>
* For example, if your items have unique ids, this method should check their equality.
*
* @param item1 The first item to check.
* @param item2 The second item to check.
*
* @return True if the two items represent the same object or false if they are different.
*/
abstract public boolean areItemsTheSame(T2 item1, T2 item2);
}
/**
* A callback implementation that can batch notify events dispatched by the SortedList.
* <p>
* This class can be useful if you want to do multiple operations on a SortedList but don't
* want to dispatch each event one by one, which may result in a performance issue.
* <p>
* For example, if you are going to add multiple items to a SortedList, BatchedCallback call
* convert individual <code>onInserted(index, 1)</code> calls into one
* <code>onInserted(index, N)</code> if items are added into consecutive indices. This change
* can help RecyclerView resolve changes much more easily.
* <p>
* If consecutive changes in the SortedList are not suitable for batching, BatchingCallback
* dispatches them as soon as such case is detected. After your edits on the SortedList is
* complete, you <b>must</b> always call {@link BatchedCallback#dispatchLastEvent()} to flush
* all changes to the Callback.
*/
public static class BatchedCallback<T2> extends Callback<T2> {
private final Callback<T2> mWrappedCallback;
static final int TYPE_NONE = 0;
static final int TYPE_ADD = 1;
static final int TYPE_REMOVE = 2;
static final int TYPE_CHANGE = 3;
static final int TYPE_MOVE = 4;
int mLastEventType = TYPE_NONE;
int mLastEventPosition = -1;
int mLastEventCount = -1;
/**
* Creates a new BatchedCallback that wraps the provided Callback.
*
* @param wrappedCallback The Callback which should received the data change callbacks.
* Other method calls (e.g. {@link #compare(Object, Object)} from
* the SortedList are directly forwarded to this Callback.
*/
public BatchedCallback(Callback<T2> wrappedCallback) {
mWrappedCallback = wrappedCallback;
}
@Override
public int compare(T2 o1, T2 o2) {
return mWrappedCallback.compare(o1, o2);
}
@Override
public void onInserted(int position, int count) {
if (mLastEventType == TYPE_ADD && position >= mLastEventPosition
&& position <= mLastEventPosition + mLastEventCount) {
mLastEventCount += count;
mLastEventPosition = Math.min(position, mLastEventPosition);
return;
}
dispatchLastEvent();
mLastEventPosition = position;
mLastEventCount = count;
mLastEventType = TYPE_ADD;
}
@Override
public void onRemoved(int position, int count) {
if (mLastEventType == TYPE_REMOVE && mLastEventPosition == position) {
mLastEventCount += count;
return;
}
dispatchLastEvent();
mLastEventPosition = position;
mLastEventCount = count;
mLastEventType = TYPE_REMOVE;
}
@Override
public void onMoved(int fromPosition, int toPosition) {
dispatchLastEvent();//moves are not merged
mWrappedCallback.onMoved(fromPosition, toPosition);
}
@Override
public void onChanged(int position, int count) {
if (mLastEventType == TYPE_CHANGE &&
!(position > mLastEventPosition + mLastEventCount
|| position + count < mLastEventPosition)) {
// take potential overlap into account
int previousEnd = mLastEventPosition + mLastEventCount;
mLastEventPosition = Math.min(position, mLastEventPosition);
mLastEventCount = Math.max(previousEnd, position + count) - mLastEventPosition;
return;
}
dispatchLastEvent();
mLastEventPosition = position;
mLastEventCount = count;
mLastEventType = TYPE_CHANGE;
}
@Override
public boolean areContentsTheSame(T2 oldItem, T2 newItem) {
return mWrappedCallback.areContentsTheSame(oldItem, newItem);
}
@Override
public boolean areItemsTheSame(T2 item1, T2 item2) {
return mWrappedCallback.areItemsTheSame(item1, item2);
}
/**
* This method dispatches any pending event notifications to the wrapped Callback.
* You <b>must</b> always call this method after you are done with editing the SortedList.
*/
public void dispatchLastEvent() {
if (mLastEventType == TYPE_NONE) {
return;
}
switch (mLastEventType) {
case TYPE_ADD:
mWrappedCallback.onInserted(mLastEventPosition, mLastEventCount);
break;
case TYPE_REMOVE:
mWrappedCallback.onRemoved(mLastEventPosition, mLastEventCount);
break;
case TYPE_CHANGE:
mWrappedCallback.onChanged(mLastEventPosition, mLastEventCount);
break;
}
mLastEventType = TYPE_NONE;
}
}
}
@@ -1,45 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.support.util;
interface ThreadUtil<T> {
interface MainThreadCallback<T> {
void updateItemCount(int generation, int itemCount);
void addTile(int generation, TileList.Tile<T> tile);
void removeTile(int generation, int position);
}
interface BackgroundCallback<T> {
void refresh(int generation);
void updateRange(int rangeStart, int rangeEnd, int extRangeStart, int extRangeEnd,
int scrollHint);
void loadTile(int position, int scrollHint);
void recycleTile(TileList.Tile<T> tile);
}
MainThreadCallback<T> getMainThreadProxy(MainThreadCallback<T> callback);
BackgroundCallback<T> getBackgroundProxy(BackgroundCallback<T> callback);
}
@@ -1,105 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.support.util;
import android.util.SparseArray;
import java.lang.reflect.Array;
/**
* A sparse collection of tiles sorted for efficient access.
*/
class TileList<T> {
final int mTileSize;
// Keyed by start position.
private final SparseArray<Tile<T>> mTiles = new SparseArray<Tile<T>>(10);
Tile<T> mLastAccessedTile;
public TileList(int tileSize) {
mTileSize = tileSize;
}
public T getItemAt(int pos) {
if (mLastAccessedTile == null || !mLastAccessedTile.containsPosition(pos)) {
final int startPosition = pos - (pos % mTileSize);
final int index = mTiles.indexOfKey(startPosition);
if (index < 0) {
return null;
}
mLastAccessedTile = mTiles.valueAt(index);
}
return mLastAccessedTile.getByPosition(pos);
}
public int size() {
return mTiles.size();
}
public void clear() {
mTiles.clear();
}
public Tile<T> getAtIndex(int index) {
return mTiles.valueAt(index);
}
public Tile<T> addOrReplace(Tile<T> newTile) {
final int index = mTiles.indexOfKey(newTile.mStartPosition);
if (index < 0) {
mTiles.put(newTile.mStartPosition, newTile);
return null;
}
Tile<T> oldTile = mTiles.valueAt(index);
mTiles.setValueAt(index, newTile);
if (mLastAccessedTile == oldTile) {
mLastAccessedTile = newTile;
}
return oldTile;
}
public Tile<T> removeAtPos(int startPosition) {
Tile<T> tile = mTiles.get(startPosition);
if (mLastAccessedTile == tile) {
mLastAccessedTile = null;
}
mTiles.delete(startPosition);
return tile;
}
public static class Tile<T> {
public final T[] mItems;
public int mStartPosition;
public int mItemCount;
Tile<T> mNext; // Used only for pooling recycled tiles.
public Tile(Class<T> klass, int size) {
//noinspection unchecked
mItems = (T[]) Array.newInstance(klass, size);
}
boolean containsPosition(int pos) {
return mStartPosition <= pos && pos < mStartPosition + mItemCount;
}
T getByPosition(int pos) {
return mItems[pos - mStartPosition];
}
}
}
@@ -1890,7 +1890,7 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
*/
static class LayoutState {
final static String TAG = "LinearLayoutManager#LayoutState";
final static String TAG = "LinLayMngr#LayoutState";
final static int LAYOUT_START = -1;
@@ -1,460 +0,0 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.support.widget;
import java.util.ArrayList;
/**
* Like a SparseArray, but with the ability to offset key ranges for bulk insertions/deletions.
*/
class PositionMap<E> implements Cloneable {
private static final Object DELETED = new Object();
private boolean mGarbage = false;
private int[] mKeys;
private Object[] mValues;
private int mSize;
/**
* Creates a new SparseArray containing no mappings.
*/
public PositionMap() {
this(10);
}
/**
* Creates a new PositionMap containing no mappings that will not
* require any additional memory allocation to store the specified
* number of mappings. If you supply an initial capacity of 0, the
* sparse array will be initialized with a light-weight representation
* not requiring any additional array allocations.
*/
public PositionMap(int initialCapacity) {
if (initialCapacity == 0) {
mKeys = ContainerHelpers.EMPTY_INTS;
mValues = ContainerHelpers.EMPTY_OBJECTS;
} else {
initialCapacity = idealIntArraySize(initialCapacity);
mKeys = new int[initialCapacity];
mValues = new Object[initialCapacity];
}
mSize = 0;
}
@Override
@SuppressWarnings("unchecked")
public PositionMap<E> clone() {
PositionMap<E> clone = null;
try {
clone = (PositionMap<E>) super.clone();
clone.mKeys = mKeys.clone();
clone.mValues = mValues.clone();
} catch (CloneNotSupportedException cnse) {
/* ignore */
}
return clone;
}
/**
* Gets the Object mapped from the specified key, or <code>null</code>
* if no such mapping has been made.
*/
public E get(int key) {
return get(key, null);
}
/**
* Gets the Object mapped from the specified key, or the specified Object
* if no such mapping has been made.
*/
@SuppressWarnings("unchecked")
public E get(int key, E valueIfKeyNotFound) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i < 0 || mValues[i] == DELETED) {
return valueIfKeyNotFound;
} else {
return (E) mValues[i];
}
}
/**
* Removes the mapping from the specified key, if there was any.
*/
public void delete(int key) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i >= 0) {
if (mValues[i] != DELETED) {
mValues[i] = DELETED;
mGarbage = true;
}
}
}
/**
* Alias for {@link #delete(int)}.
*/
public void remove(int key) {
delete(key);
}
/**
* Removes the mapping at the specified index.
*/
public void removeAt(int index) {
if (mValues[index] != DELETED) {
mValues[index] = DELETED;
mGarbage = true;
}
}
/**
* Remove a range of mappings as a batch.
*
* @param index Index to begin at
* @param size Number of mappings to remove
*/
public void removeAtRange(int index, int size) {
final int end = Math.min(mSize, index + size);
for (int i = index; i < end; i++) {
removeAt(i);
}
}
public void insertKeyRange(int keyStart, int count) {
}
public void removeKeyRange(ArrayList<E> removedItems, int keyStart, int count) {
}
private void gc() {
// Log.e("SparseArray", "gc start with " + mSize);
int n = mSize;
int o = 0;
int[] keys = mKeys;
Object[] values = mValues;
for (int i = 0; i < n; i++) {
Object val = values[i];
if (val != DELETED) {
if (i != o) {
keys[o] = keys[i];
values[o] = val;
values[i] = null;
}
o++;
}
}
mGarbage = false;
mSize = o;
// Log.e("SparseArray", "gc end with " + mSize);
}
/**
* Adds a mapping from the specified key to the specified value,
* replacing the previous mapping from the specified key if there
* was one.
*/
public void put(int key, E value) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i >= 0) {
mValues[i] = value;
} else {
i = ~i;
if (i < mSize && mValues[i] == DELETED) {
mKeys[i] = key;
mValues[i] = value;
return;
}
if (mGarbage && mSize >= mKeys.length) {
gc();
// Search again because indices may have changed.
i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
}
if (mSize >= mKeys.length) {
int n = idealIntArraySize(mSize + 1);
int[] nkeys = new int[n];
Object[] nvalues = new Object[n];
// Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
mKeys = nkeys;
mValues = nvalues;
}
if (mSize - i != 0) {
// Log.e("SparseArray", "move " + (mSize - i));
System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
}
mKeys[i] = key;
mValues[i] = value;
mSize++;
}
}
/**
* Returns the number of key-value mappings that this SparseArray
* currently stores.
*/
public int size() {
if (mGarbage) {
gc();
}
return mSize;
}
/**
* Given an index in the range <code>0...size()-1</code>, returns
* the key from the <code>index</code>th key-value mapping that this
* SparseArray stores.
*/
public int keyAt(int index) {
if (mGarbage) {
gc();
}
return mKeys[index];
}
/**
* Given an index in the range <code>0...size()-1</code>, returns
* the value from the <code>index</code>th key-value mapping that this
* SparseArray stores.
*/
@SuppressWarnings("unchecked")
public E valueAt(int index) {
if (mGarbage) {
gc();
}
return (E) mValues[index];
}
/**
* Given an index in the range <code>0...size()-1</code>, sets a new
* value for the <code>index</code>th key-value mapping that this
* SparseArray stores.
*/
public void setValueAt(int index, E value) {
if (mGarbage) {
gc();
}
mValues[index] = value;
}
/**
* Returns the index for which {@link #keyAt} would return the
* specified key, or a negative number if the specified
* key is not mapped.
*/
public int indexOfKey(int key) {
if (mGarbage) {
gc();
}
return ContainerHelpers.binarySearch(mKeys, mSize, key);
}
/**
* Returns an index for which {@link #valueAt} would return the
* specified key, or a negative number if no keys map to the
* specified value.
* <p>Beware that this is a linear search, unlike lookups by key,
* and that multiple keys can map to the same value and this will
* find only one of them.
* <p>Note also that unlike most collections' {@code indexOf} methods,
* this method compares values using {@code ==} rather than {@code equals}.
*/
public int indexOfValue(E value) {
if (mGarbage) {
gc();
}
for (int i = 0; i < mSize; i++)
if (mValues[i] == value)
return i;
return -1;
}
/**
* Removes all key-value mappings from this SparseArray.
*/
public void clear() {
int n = mSize;
Object[] values = mValues;
for (int i = 0; i < n; i++) {
values[i] = null;
}
mSize = 0;
mGarbage = false;
}
/**
* Puts a key/value pair into the array, optimizing for the case where
* the key is greater than all existing keys in the array.
*/
public void append(int key, E value) {
if (mSize != 0 && key <= mKeys[mSize - 1]) {
put(key, value);
return;
}
if (mGarbage && mSize >= mKeys.length) {
gc();
}
int pos = mSize;
if (pos >= mKeys.length) {
int n = idealIntArraySize(pos + 1);
int[] nkeys = new int[n];
Object[] nvalues = new Object[n];
// Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
mKeys = nkeys;
mValues = nvalues;
}
mKeys[pos] = key;
mValues[pos] = value;
mSize = pos + 1;
}
/**
* {@inheritDoc}
*
* <p>This implementation composes a string by iterating over its mappings. If
* this map contains itself as a value, the string "(this Map)"
* will appear in its place.
*/
@Override
public String toString() {
if (size() <= 0) {
return "{}";
}
StringBuilder buffer = new StringBuilder(mSize * 28);
buffer.append('{');
for (int i=0; i<mSize; i++) {
if (i > 0) {
buffer.append(", ");
}
int key = keyAt(i);
buffer.append(key);
buffer.append('=');
Object value = valueAt(i);
if (value != this) {
buffer.append(value);
} else {
buffer.append("(this Map)");
}
}
buffer.append('}');
return buffer.toString();
}
static int idealByteArraySize(int need) {
for (int i = 4; i < 32; i++)
if (need <= (1 << i) - 12)
return (1 << i) - 12;
return need;
}
static int idealBooleanArraySize(int need) {
return idealByteArraySize(need);
}
static int idealShortArraySize(int need) {
return idealByteArraySize(need * 2) / 2;
}
static int idealCharArraySize(int need) {
return idealByteArraySize(need * 2) / 2;
}
static int idealIntArraySize(int need) {
return idealByteArraySize(need * 4) / 4;
}
static int idealFloatArraySize(int need) {
return idealByteArraySize(need * 4) / 4;
}
static int idealObjectArraySize(int need) {
return idealByteArraySize(need * 4) / 4;
}
static int idealLongArraySize(int need) {
return idealByteArraySize(need * 8) / 8;
}
static class ContainerHelpers {
static final boolean[] EMPTY_BOOLEANS = new boolean[0];
static final int[] EMPTY_INTS = new int[0];
static final long[] EMPTY_LONGS = new long[0];
static final Object[] EMPTY_OBJECTS = new Object[0];
// This is Arrays.binarySearch(), but doesn't do any argument validation.
static int binarySearch(int[] array, int size, int value) {
int lo = 0;
int hi = size - 1;
while (lo <= hi) {
final int mid = (lo + hi) >>> 1;
final int midVal = array[mid];
if (midVal < value) {
lo = mid + 1;
} else if (midVal > value) {
hi = mid - 1;
} else {
return mid; // value found
}
}
return ~lo; // value not present
}
}
}
@@ -7538,6 +7538,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
resultMode = MeasureSpec.AT_MOST;
}
}
//noinspection WrongConstant
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
@@ -1,59 +0,0 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.support.widget.util;
import com.b44t.messenger.support.util.SortedList;
import com.b44t.messenger.support.widget.RecyclerView;
/**
* A {@link SortedList.Callback} implementation that can bind a {@link SortedList} to a
* {@link RecyclerView.Adapter}.
*/
public abstract class SortedListAdapterCallback<T2> extends SortedList.Callback<T2> {
final RecyclerView.Adapter mAdapter;
/**
* Creates a {@link SortedList.Callback} that will forward data change events to the provided
* Adapter.
*
* @param adapter The Adapter instance which should receive events from the SortedList.
*/
public SortedListAdapterCallback(RecyclerView.Adapter adapter) {
mAdapter = adapter;
}
@Override
public void onInserted(int position, int count) {
mAdapter.notifyItemRangeInserted(position, count);
}
@Override
public void onRemoved(int position, int count) {
mAdapter.notifyItemRangeRemoved(position, count);
}
@Override
public void onMoved(int fromPosition, int toPosition) {
mAdapter.notifyItemMoved(fromPosition, toPosition);
}
@Override
public void onChanged(int position, int count) {
mAdapter.notifyItemRangeChanged(position, count);
}
}
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger.video;
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger.video;
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger.video;
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.messenger.video;
@@ -1,107 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import com.b44t.messenger.volley.AuthFailureError;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerFuture;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
/**
* An Authenticator that uses {@link AccountManager} to get auth
* tokens of a specified type for a specified account.
*/
public class AndroidAuthenticator implements Authenticator {
private final AccountManager mAccountManager;
private final Account mAccount;
private final String mAuthTokenType;
private final boolean mNotifyAuthFailure;
/**
* Creates a new authenticator.
* @param context Context for accessing AccountManager
* @param account Account to authenticate as
* @param authTokenType Auth token type passed to AccountManager
*/
public AndroidAuthenticator(Context context, Account account, String authTokenType) {
this(context, account, authTokenType, false);
}
/**
* Creates a new authenticator.
* @param context Context for accessing AccountManager
* @param account Account to authenticate as
* @param authTokenType Auth token type passed to AccountManager
* @param notifyAuthFailure Whether to raise a notification upon auth failure
*/
public AndroidAuthenticator(Context context, Account account, String authTokenType,
boolean notifyAuthFailure) {
this(AccountManager.get(context), account, authTokenType, notifyAuthFailure);
}
// Visible for testing. Allows injection of a mock AccountManager.
AndroidAuthenticator(AccountManager accountManager, Account account,
String authTokenType, boolean notifyAuthFailure) {
mAccountManager = accountManager;
mAccount = account;
mAuthTokenType = authTokenType;
mNotifyAuthFailure = notifyAuthFailure;
}
/**
* Returns the Account being used by this authenticator.
*/
public Account getAccount() {
return mAccount;
}
// TODO: Figure out what to do about notifyAuthFailure
@SuppressWarnings("deprecation")
@Override
public String getAuthToken() throws AuthFailureError {
AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(mAccount,
mAuthTokenType, mNotifyAuthFailure, null, null);
Bundle result;
try {
result = future.getResult();
} catch (Exception e) {
throw new AuthFailureError("Error while retrieving auth token", e);
}
String authToken = null;
if (future.isDone() && !future.isCancelled()) {
if (result.containsKey(AccountManager.KEY_INTENT)) {
Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
throw new AuthFailureError(intent);
}
authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
}
if (authToken == null) {
throw new AuthFailureError("Got null auth token for type: " + mAuthTokenType);
}
return authToken;
}
@Override
public void invalidateAuthToken(String authToken) {
mAccountManager.invalidateAuthToken(mAccount.type, authToken);
}
}
@@ -1,36 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import com.b44t.messenger.volley.AuthFailureError;
/**
* An interface for interacting with auth tokens.
*/
public interface Authenticator {
/**
* Synchronously retrieves an auth token.
*
* @throws AuthFailureError If authentication did not succeed
*/
public String getAuthToken() throws AuthFailureError;
/**
* Invalidates the provided auth token.
*/
public void invalidateAuthToken(String authToken);
}
@@ -1,8 +1,8 @@
/*******************************************************************************
*
* Messenger Android Frontend
* Copyright (C) 2016 Björn Petersen Software Design and Development
* Contact: r10s@b44t.com, http://b44t.com
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
@@ -20,7 +20,6 @@
*******************************************************************************
*
* File: MrAccoutSettingsActivity.java
* Authors: Björn Petersen
* Purpose: Let the user configure his name
*
******************************************************************************/
@@ -38,71 +37,85 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.Toast;
import com.b44t.messenger.AndroidUtilities;
import com.b44t.messenger.ContactsController;
import com.b44t.messenger.ApplicationLoader;
import com.b44t.messenger.LocaleController;
import com.b44t.messenger.MessagesController;
import com.b44t.messenger.MrMailbox;
import com.b44t.messenger.NotificationCenter;
import com.b44t.messenger.R;
import com.b44t.messenger.Utilities;
import com.b44t.ui.ActionBar.ActionBar;
import com.b44t.ui.ActionBar.ActionBarMenu;
import com.b44t.ui.ActionBar.BaseFragment;
import com.b44t.ui.Adapters.BaseFragmentAdapter;
import com.b44t.ui.Cells.HeaderCell;
import com.b44t.ui.Cells.MrEditTextCell;
import com.b44t.ui.Cells.EditTextCell;
import com.b44t.ui.Cells.ShadowSectionCell;
import com.b44t.ui.Cells.TextInfoPrivacyCell;
import com.b44t.ui.Cells.TextInfoCell;
import com.b44t.ui.Cells.TextSettingsCell;
import com.b44t.ui.Components.LayoutHelper;
import static android.app.ProgressDialog.STYLE_HORIZONTAL;
public class AccountSettingsActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate {
// the list
private ListView listView;
private ListAdapter listAdapter;
private int rowSectionBasic;
private int rowAddrHeadline;
private int rowAddr;
private int rowMailPwHeadline;
private int rowMailPw;
private int rowInfoBelowMailPw2;
private int rowOpenAdvOpions;
private int rowSectionMail;
private int rowMailHeadline;
private int rowMailServer;
private int rowMailPort;
private int rowMailUser;
private int rowBreak2;
private int rowMailSecurity;
private int rowBreak1;
private int rowSectionSend;
private int rowSendHeadline;
private int rowSendServer;
private int rowSendPort;
private int rowSendUser;
private int rowSendPw;
private int rowSendSecurity;
private int rowInfoBelowSendPw;
private int rowCount;
private final int typeInfo = 0; // no gaps here!
private final int typeTextEntry = 1;
private final int typeShadowSection = 2;
private final int typeSection = 3;
private final int ROWTYPE_INFO = 0; // no gaps here!
private final int ROWTYPE_TEXT_ENTRY = 1;
private final int ROWTYPE_SHADOW_BREAK = 2;
private final int ROWTYPE_HEADLINE = 3;
private final int ROWTYPE_TEXT_FLAGS = 4;
MrEditTextCell addrCell; // warning all these objects may be null!
MrEditTextCell mailPwCell;
MrEditTextCell mailServerCell;
MrEditTextCell mailPortCell;
MrEditTextCell mailUserCell;
MrEditTextCell sendPwCell;
MrEditTextCell sendServerCell;
MrEditTextCell sendPortCell;
MrEditTextCell sendUserCell;
private EditTextCell addrCell; // warning all these objects may be null!
private EditTextCell mailPwCell;
private EditTextCell mailServerCell;
private EditTextCell mailPortCell;
private EditTextCell mailUserCell;
private EditTextCell sendPwCell;
private EditTextCell sendServerCell;
private EditTextCell sendPortCell;
private EditTextCell sendUserCell;
private final int MR_IMAP_SOCKET_STARTTLS = 0x100;
private final int MR_IMAP_SOCKET_SSL = 0x200;
private final int MR_IMAP_SOCKET_PLAIN = 0x400;
private final int MR_SMTP_SOCKET_STARTTLS = 0x10000;
private final int MR_SMTP_SOCKET_SSL = 0x20000;
private final int MR_SMTP_SOCKET_PLAIN = 0x40000;
private int m_serverFlags;
// misc.
private View doneButton;
private final static int done_button = 1;
private final int ID_DONE_BUTTON = 1;
private ProgressDialog progressDialog = null;
boolean fromIntro;
private boolean fromIntro;
private boolean m_expanded = false;
public AccountSettingsActivity(Bundle args) {
super();
@@ -115,34 +128,77 @@ public class AccountSettingsActivity extends BaseFragment implements Notificatio
public boolean onFragmentCreate() {
super.onFragmentCreate();
NotificationCenter.getInstance().addObserver(this, NotificationCenter.connectionStateChanged);
NotificationCenter.getInstance().addObserver(this, NotificationCenter.configureEnded);
NotificationCenter.getInstance().addObserver(this, NotificationCenter.configureProgress);
rowCount = 0;
rowSectionBasic = rowCount++;
rowAddr = rowCount++;
rowMailPw = rowCount++;
rowInfoBelowMailPw2 = rowCount++;
m_serverFlags = MrMailbox.getConfigInt("server_flags", 0);
rowSectionMail = rowCount++;
rowMailServer = rowCount++;
rowMailUser = rowCount++;
rowMailPort = rowCount++;
rowBreak2 = rowCount++;
m_expanded = false;
if( !MrMailbox.getConfig("mail_user", "").isEmpty()
|| !MrMailbox.getConfig("mail_server", "").isEmpty()
|| !MrMailbox.getConfig("mail_port", "").isEmpty()
|| !MrMailbox.getConfig("send_user", "").isEmpty()
|| !MrMailbox.getConfig("send_pw", "").isEmpty()
|| !MrMailbox.getConfig("send_server", "").isEmpty()
|| !MrMailbox.getConfig("send_port", "").isEmpty()
|| (m_serverFlags!=0) ) {
m_expanded = true;
}
rowSectionSend = rowCount++;
rowSendServer = rowCount++;
rowSendUser = rowCount++;
rowSendPw = rowCount++;
rowSendPort = rowCount++;
rowInfoBelowSendPw = rowCount++;
calculateRows();
return true;
}
private void calculateRows()
{
rowCount = 0;
rowAddrHeadline = rowCount++;
rowAddr = rowCount++;
rowMailPwHeadline= rowCount++;
rowMailPw = rowCount++;
rowOpenAdvOpions = rowCount++;
if( m_expanded ) {
rowMailHeadline = rowCount++;
rowMailUser = rowCount++; // should be the first additional option, the loginname is the component, that cannot be configured automatically (if not derivable from the address)
rowMailServer = rowCount++;
rowMailPort = rowCount++;
rowMailSecurity = rowCount++;
rowBreak1 = rowCount++;
rowSendHeadline = rowCount++;
rowSendUser = rowCount++;
rowSendPw = rowCount++;
rowSendServer = rowCount++;
rowSendPort = rowCount++;
rowSendSecurity = rowCount++;
}
else {
rowMailHeadline = -1;
rowMailUser = -1;
rowMailServer = -1;
rowMailPort = -1;
rowMailSecurity = -1;
rowBreak1 = -1;
rowSendHeadline = -1;
rowSendUser = -1;
rowSendPw = -1;
rowSendServer = -1;
rowSendPort = -1;
rowSendSecurity = -1;
}
rowInfoBelowSendPw = rowCount++;
}
@Override
public void onFragmentDestroy() {
super.onFragmentDestroy();
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.connectionStateChanged);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.configureEnded);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.configureProgress);
}
@Override
@@ -150,7 +206,7 @@ public class AccountSettingsActivity extends BaseFragment implements Notificatio
// create action bar
if( !fromIntro ) {
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
actionBar.setBackButtonImage(R.drawable.ic_close_white);
}
actionBar.setAllowOverlayTitle(true);
@@ -158,30 +214,16 @@ public class AccountSettingsActivity extends BaseFragment implements Notificatio
actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() {
@Override
public void onItemClick(int id) {
if (id == -1 && !fromIntro ) {
if( isModified() ) { // TODO: maybe we should also ask if the user presses the "back" button
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
builder.setMessage(LocaleController.getString("DiscardChanges", R.string.DiscardChanges));
builder.setPositiveButton(LocaleController.getString("Yes", R.string.Yes), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
finishFragment();
}
});
builder.setNegativeButton(LocaleController.getString("No", R.string.No), null);
showDialog(builder.create());
}
else {
finishFragment();
}
} else if (id == done_button) {
if (id == -1 && !fromIntro ) { // no "is modified" check: as we use "close/ok" buttons instead of a "back" button it is more clear what happens. moreover, the user may have done a failed "OK" in between, so a question "discard changes?" would be ambiguously
finishFragment();
} else if (id == ID_DONE_BUTTON) {
saveData();
}
}
});
ActionBarMenu menu = actionBar.createMenu();
doneButton = menu.addItemWithWidth(done_button, R.drawable.ic_done, AndroidUtilities.dp(56));
menu.addItemWithWidth(ID_DONE_BUTTON, R.drawable.ic_done, AndroidUtilities.dp(56));
// create object to hold the whole view
fragmentView = new FrameLayout(context);
@@ -191,7 +233,7 @@ public class AccountSettingsActivity extends BaseFragment implements Notificatio
// create the main layout list
listAdapter = new ListAdapter(context);
ListView listView = new ListView(context);
listView = new ListView(context);
listView.setDivider(null);
listView.setDividerHeight(0);
listView.setVerticalScrollBarEnabled(false);
@@ -201,6 +243,47 @@ public class AccountSettingsActivity extends BaseFragment implements Notificatio
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(final AdapterView<?> adapterView, View view, final int i, long l) {
if( i==rowOpenAdvOpions )
{
m_expanded = !m_expanded;
calculateRows();
listAdapter.notifyDataSetChanged();
}
else if( i==rowMailSecurity || i==rowSendSecurity )
{
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
builder.setTitle(ApplicationLoader.applicationContext.getString(R.string.SecurityTitle));
builder.setItems(new CharSequence[]{
ApplicationLoader.applicationContext.getString(R.string.Automatic),
"SSL/TLS", /*1*/
"STARTTLS", /*2*/
ApplicationLoader.applicationContext.getString(R.string.Disabled) /*3*/
}, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if( i==rowMailSecurity ) {
m_serverFlags &= ~(MR_IMAP_SOCKET_SSL | MR_IMAP_SOCKET_STARTTLS | MR_IMAP_SOCKET_PLAIN);
switch( which ) {
case 1: m_serverFlags |= MR_IMAP_SOCKET_SSL; break;
case 2: m_serverFlags |= MR_IMAP_SOCKET_STARTTLS; break;
case 3: m_serverFlags |= MR_IMAP_SOCKET_PLAIN; break;
}
}
else if( i==rowSendSecurity ) {
m_serverFlags &= ~(MR_SMTP_SOCKET_SSL | MR_SMTP_SOCKET_STARTTLS | MR_SMTP_SOCKET_PLAIN);
switch( which ) {
case 1: m_serverFlags |= MR_SMTP_SOCKET_SSL; break;
case 2: m_serverFlags |= MR_SMTP_SOCKET_STARTTLS; break;
case 3: m_serverFlags |= MR_SMTP_SOCKET_PLAIN; break;
}
}
listView.invalidateViews();
}
});
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
showDialog(builder.create());
}
}
});
@@ -211,13 +294,6 @@ public class AccountSettingsActivity extends BaseFragment implements Notificatio
// Warning: the widgets are created as needed and may not be present!
String v;
/*
if( !isModified() && MrMailbox.MrMailboxIsConfigured(MrMailbox.hMailbox)!=0 ) {
finishFragment();
return; // nothing to do
}
*/
if( addrCell!=null) {
v = addrCell.getValue().trim();
MrMailbox.setConfig("addr", v.isEmpty() ? null : v);
@@ -263,6 +339,8 @@ public class AccountSettingsActivity extends BaseFragment implements Notificatio
MrMailbox.setConfig("send_pw", v.isEmpty() ? null : v);
}
MrMailbox.setConfigInt("server_flags", m_serverFlags);
// show dialog
if( progressDialog!=null ) {
progressDialog.dismiss();
@@ -270,70 +348,77 @@ public class AccountSettingsActivity extends BaseFragment implements Notificatio
}
progressDialog = new ProgressDialog(getParentActivity());
progressDialog.setMessage(LocaleController.getString("ConfiguringAccount", R.string.ConfiguringAccount));
progressDialog.setMessage(ApplicationLoader.applicationContext.getString(R.string.OneMoment));
progressDialog.setCanceledOnTouchOutside(false);
progressDialog.setCancelable(false);
progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, LocaleController.getString("Cancel", R.string.Cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
progressDialog = null;
}
});
@Override
public void onClick(DialogInterface dialog, int which) {
MrMailbox.configureCancel();
}
});
progressDialog.show();
// try to connect
// (for the future, we may put all this togehter in a single command, that is executed
// asynchronously by the backend; then we can skip creating a runnable here)
Utilities.searchQueue.postRunnable(new Runnable() {
@Override
public void run() {
MrMailbox.disconnect();
MrMailbox.configure();
MrMailbox.connect();
}
});
synchronized (MrMailbox.m_lastErrorLock) {
MrMailbox.m_showNextErrorAsToast = false;
MrMailbox.m_lastErrorString = "";
}
// try to connect, this results in an MR_EVENT_CONFIGURE_ENDED resp. NotificationCenter.configureEnded event
MrMailbox.configureAndConnect();
}
@Override
public void didReceivedNotification(int id, Object... args) {
if (id == NotificationCenter.connectionStateChanged ) {
if( id == NotificationCenter.configureProgress )
{
if( progressDialog!=null ) {
// we want the spinner together with a progress info
int percent = (Integer)args[0];
progressDialog.setMessage(ApplicationLoader.applicationContext.getString(R.string.OneMoment)+String.format(" %d%%", percent));
}
}
else if (id == NotificationCenter.configureEnded )
{
final String errorString;
synchronized (MrMailbox.m_lastErrorLock) {
MrMailbox.m_showNextErrorAsToast = true;
errorString = MrMailbox.m_lastErrorString;
}
if( progressDialog!=null ) {
progressDialog.dismiss();
progressDialog = null;
}
if( (int)args[0]==1 ) {
if( fromIntro ) {
if (fromIntro) {
presentFragment(new DialogsActivity(null), true);
((LaunchActivity)getParentActivity()).drawerLayoutContainer.setAllowOpenDrawer(true, false);
}
else {
LaunchActivity la = ((LaunchActivity) getParentActivity());
if (la != null) {
la.drawerLayoutContainer.setAllowOpenDrawer(true, false);
}
} else {
finishFragment();
}
AndroidUtilities.showDoneHint(ApplicationLoader.applicationContext);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.mainUserInfoChanged);
}
else {
Toast.makeText(getParentActivity(), LocaleController.getString("CannotConnect", R.string.CannotConnect), Toast.LENGTH_LONG).show();
else if( ! MrMailbox.m_lastErrorString.isEmpty() ){
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
builder.setMessage(errorString);
builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
;
}
});
showDialog(builder.create());
}
}
}
private boolean isModified(){
// Warning: the widgets are created as needed and may not be present!
if( addrCell!=null && addrCell.isModified()) { return true; }
if( mailPwCell!=null && mailPwCell.isModified()) { return true; }
if( mailServerCell!=null && mailServerCell.isModified()) { return true; }
if( mailPortCell!=null && mailPortCell.isModified()) { return true; }
if( mailUserCell!=null && mailUserCell.isModified()) { return true; }
if( sendServerCell!=null && sendServerCell.isModified()) { return true; }
if( sendPortCell!=null && sendPortCell.isModified()) { return true; }
if( sendUserCell!=null && sendUserCell.isModified()) { return true; }
if( sendPwCell!=null && sendPwCell.isModified()) { return true; }
return false;
}
@Override
public void onTransitionAnimationEnd(boolean isOpen, boolean backward) {
if (isOpen && addrCell!=null) {
@@ -358,8 +443,7 @@ public class AccountSettingsActivity extends BaseFragment implements Notificatio
@Override
public boolean isEnabled(int i) {
return (i == rowAddr || i==rowMailPw || i==rowMailServer || i==rowMailPort|| i==rowMailUser
|| i==rowSendServer || i==rowSendPort || i==rowSendUser || i== rowSendPw);
return !(i==rowAddrHeadline || i==rowMailPwHeadline || i== rowMailHeadline || i== rowBreak1 || i== rowSendHeadline || i==rowInfoBelowSendPw);
}
@Override
@@ -385,76 +469,80 @@ public class AccountSettingsActivity extends BaseFragment implements Notificatio
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
int type = getItemViewType__(i);
if (type == typeTextEntry) {
if (type == ROWTYPE_TEXT_ENTRY) {
if (i == rowAddr) {
if( addrCell==null) {
addrCell = new MrEditTextCell(mContext);
addrCell = new EditTextCell(mContext, false);
addrCell.setValueHintAndLabel(MrMailbox.getConfig("addr", ""),
"", LocaleController.getString("MyEmailAddress", R.string.MyEmailAddress), false);
"", "", false);
addrCell.getEditTextView().setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
}
view = addrCell;
}
else if (i == rowMailPw) {
if( mailPwCell==null) {
mailPwCell = new MrEditTextCell(mContext);
mailPwCell = new EditTextCell(mContext, false);
mailPwCell.setValueHintAndLabel(MrMailbox.getConfig("mail_pw", ""),
"", LocaleController.getString("Password", R.string.Password), false);
"", "", false);
mailPwCell.getEditTextView().setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_VARIATION_PASSWORD);
}
view = mailPwCell;
}
else if (i == rowMailServer) {
if( mailServerCell==null) {
mailServerCell = new MrEditTextCell(mContext);
mailServerCell = new EditTextCell(mContext);
mailServerCell.setValueHintAndLabel(MrMailbox.getConfig("mail_server", ""),
LocaleController.getString("Automatic", R.string.Automatic), LocaleController.getString("ImapServer", R.string.ImapServer), false);
ApplicationLoader.applicationContext.getString(R.string.Automatic), LocaleController.getString("ImapServer", R.string.ImapServer), false);
mailServerCell.getEditTextView().setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI);
}
view = mailServerCell;
}
else if (i == rowMailPort) {
if( mailPortCell==null) {
mailPortCell = new MrEditTextCell(mContext);
mailPortCell = new EditTextCell(mContext);
mailPortCell.setValueHintAndLabel(MrMailbox.getConfig("mail_port", ""),
LocaleController.getString("Default", R.string.Default), LocaleController.getString("ImapPort", R.string.ImapPort), false);
ApplicationLoader.applicationContext.getString(R.string.Automatic), LocaleController.getString("ImapPort", R.string.ImapPort), false);
mailPortCell.getEditTextView().setInputType(InputType.TYPE_CLASS_NUMBER);
}
view = mailPortCell;
}
else if (i == rowMailUser) {
if( mailUserCell==null) {
mailUserCell = new MrEditTextCell(mContext);
mailUserCell = new EditTextCell(mContext);
mailUserCell.setValueHintAndLabel(MrMailbox.getConfig("mail_user", ""),
LocaleController.getString("FromAbove", R.string.FromAbove), LocaleController.getString("ImapLoginname", R.string.ImapLoginname), false);
ApplicationLoader.applicationContext.getString(R.string.Automatic), LocaleController.getString("ImapLoginname", R.string.ImapLoginname), false);
}
view = mailUserCell;
}
else if (i == rowSendServer) {
if( sendServerCell==null) {
sendServerCell = new MrEditTextCell(mContext);
sendServerCell = new EditTextCell(mContext);
sendServerCell.setValueHintAndLabel(MrMailbox.getConfig("send_server", ""),
LocaleController.getString("Automatic", R.string.Automatic), LocaleController.getString("SmtpServer", R.string.SmtpServer), false);
ApplicationLoader.applicationContext.getString(R.string.Automatic), LocaleController.getString("SmtpServer", R.string.SmtpServer), false);
sendServerCell.getEditTextView().setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI);
}
view = sendServerCell;
}
else if (i == rowSendPort) {
if( sendPortCell==null) {
sendPortCell = new MrEditTextCell(mContext);
sendPortCell = new EditTextCell(mContext);
sendPortCell.setValueHintAndLabel(MrMailbox.getConfig("send_port", ""),
LocaleController.getString("Default", R.string.Default), LocaleController.getString("SmtpPort", R.string.SmtpPort), false);
ApplicationLoader.applicationContext.getString(R.string.Automatic), LocaleController.getString("SmtpPort", R.string.SmtpPort), false);
sendPortCell.getEditTextView().setInputType(InputType.TYPE_CLASS_NUMBER);
}
view = sendPortCell;
}
else if (i == rowSendUser) {
if( sendUserCell==null) {
sendUserCell = new MrEditTextCell(mContext);
sendUserCell = new EditTextCell(mContext);
sendUserCell.setValueHintAndLabel(MrMailbox.getConfig("send_user", ""),
LocaleController.getString("FromAbove", R.string.FromAbove), LocaleController.getString("SmtpLoginname", R.string.SmtpLoginname), false);
ApplicationLoader.applicationContext.getString(R.string.Automatic), LocaleController.getString("SmtpLoginname", R.string.SmtpLoginname), false);
}
view = sendUserCell;
}
else if (i == rowSendPw) {
if( sendPwCell==null) {
sendPwCell = new MrEditTextCell(mContext);
sendPwCell = new EditTextCell(mContext);
sendPwCell.setValueHintAndLabel(MrMailbox.getConfig("send_pw", ""),
LocaleController.getString("FromAbove", R.string.FromAbove), LocaleController.getString("SmtpPassword", R.string.SmtpPassword), false);
sendPwCell.getEditTextView().setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_VARIATION_PASSWORD);
@@ -462,35 +550,59 @@ public class AccountSettingsActivity extends BaseFragment implements Notificatio
view = sendPwCell;
}
}
else if (type == typeSection) {
else if (type ==ROWTYPE_TEXT_FLAGS) {
if (view == null) {
view = new TextSettingsCell(mContext);
view.setBackgroundColor(0xffffffff);
}
TextSettingsCell textCell = (TextSettingsCell) view;
String value = ApplicationLoader.applicationContext.getString(R.string.Automatic);
if( i == rowMailSecurity ) {
if( (m_serverFlags&MR_IMAP_SOCKET_SSL)!=0 ) { value = "SSL/TLS"; }
if( (m_serverFlags&MR_IMAP_SOCKET_STARTTLS)!=0 ) { value = "STARTTLS"; }
if( (m_serverFlags&MR_IMAP_SOCKET_PLAIN)!=0 ) { value = ApplicationLoader.applicationContext.getString(R.string.Disabled); }
}
else if( i == rowSendSecurity ) {
if( (m_serverFlags&MR_SMTP_SOCKET_SSL)!=0 ) { value = "SSL/TLS"; }
if( (m_serverFlags&MR_SMTP_SOCKET_STARTTLS)!=0 ) { value = "STARTTLS"; }
if( (m_serverFlags&MR_SMTP_SOCKET_PLAIN)!=0 ) { value = ApplicationLoader.applicationContext.getString(R.string.Disabled); }
}
textCell.setTextAndValue(ApplicationLoader.applicationContext.getString(R.string.SecurityTitle), value, false);
}
else if (type == ROWTYPE_HEADLINE) {
if (view == null) {
view = new HeaderCell(mContext);
view.setBackgroundColor(0xffffffff);
}
if (i == rowSectionBasic) {
((HeaderCell) view).setText(LocaleController.getString("BasicSettings", R.string.BasicSettings));
} else if (i == rowSectionMail) {
if (i == rowAddrHeadline) {
((HeaderCell) view).setText(ApplicationLoader.applicationContext.getString(R.string.EmailAddress));
} else if (i == rowMailPwHeadline) {
((HeaderCell) view).setText(ApplicationLoader.applicationContext.getString(R.string.Password));
} else if (i == rowMailHeadline) {
((HeaderCell) view).setText(LocaleController.getString("InboxHeadline", R.string.InboxHeadline));
} else if (i == rowSectionSend) {
} else if (i == rowSendHeadline) {
((HeaderCell) view).setText(LocaleController.getString("OutboxHeadline", R.string.OutboxHeadline));
}
}
else if (type == typeShadowSection) {
else if (type == ROWTYPE_SHADOW_BREAK) {
if (view == null) {
view = new ShadowSectionCell(mContext);
}
}
else if (type == typeInfo) {
else if (type == ROWTYPE_INFO) {
if (view == null) {
view = new TextInfoPrivacyCell(mContext);
view = new TextInfoCell(mContext);
}
if( i==rowInfoBelowMailPw2) {
((TextInfoPrivacyCell) view).setText(LocaleController.getString("MyAccoutExplain", R.string.MyAccountExplain)+"\n");
view.setBackgroundResource(R.drawable.greydivider); // has shadow top+bottom
if( i== rowOpenAdvOpions) {
((TextInfoCell) view).setText(LocaleController.getString("MyAccoutExplain", R.string.MyAccountExplain),
m_expanded? " \u2212" /*minus-sign*/ : "+", m_expanded /*draw bottom border?*/);
view.setBackgroundResource(m_expanded? R.drawable.greydivider : R.drawable.greydivider_bottom); // has shadow top+bottom
}
else if( i==rowInfoBelowSendPw) {
((TextInfoPrivacyCell) view).setText(LocaleController.getString("MyAccountExplain2", R.string.MyAccountExplain2));
view.setBackgroundResource(R.drawable.greydivider_bottom);
((TextInfoCell) view).setText(AndroidUtilities.replaceTags(LocaleController.getString("MyAccountExplain2", R.string.MyAccountExplain2)));
if( m_expanded ) {
view.setBackgroundResource(R.drawable.greydivider_bottom);
}
}
}
return view;
@@ -504,15 +616,18 @@ public class AccountSettingsActivity extends BaseFragment implements Notificatio
private int getItemViewType__(int i) {
if (i == rowAddr || i==rowMailPw || i==rowMailServer || i==rowMailPort|| i==rowMailUser
|| i==rowSendServer || i==rowSendPort || i==rowSendUser || i== rowSendPw ) {
return typeTextEntry;
return ROWTYPE_TEXT_ENTRY;
}
else if( i==rowSectionBasic || i==rowSectionMail || i==rowSectionSend ) {
return typeSection;
else if( i==rowAddrHeadline || i==rowMailPwHeadline || i== rowMailHeadline || i== rowSendHeadline ) {
return ROWTYPE_HEADLINE;
}
else if( i== rowBreak2) {
return typeShadowSection;
else if( i== rowBreak1 ) {
return ROWTYPE_SHADOW_BREAK;
}
return typeInfo;
else if( i==rowMailSecurity || i==rowSendSecurity) {
return ROWTYPE_TEXT_FLAGS;
}
return ROWTYPE_INFO;
}
@Override
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.ui.ActionBar;
@@ -13,6 +28,7 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.Gravity;
@@ -43,14 +59,13 @@ public class ActionBar extends FrameLayout {
private ImageView backButtonImageView;
private SimpleTextView titleTextView;
private SimpleTextView subtitleTextView;
private View actionModeTop;
//private View actionModeTop;
private ActionBarMenu menu;
private ActionBarMenu actionMode;
private boolean occupyStatusBar = Build.VERSION.SDK_INT >= 21;
private boolean actionModeVisible;
private boolean addToContainer = true;
private boolean interceptTouches = true;
private int extraHeight;
private AnimatorSet actionModeAnimation;
private boolean allowOverlayTitle;
@@ -146,7 +161,6 @@ public class ActionBar extends FrameLayout {
titleTextView = new SimpleTextView(getContext());
titleTextView.setGravity(Gravity.LEFT);
titleTextView.setTextColor(0xffffffff);
titleTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
addView(titleTextView, 0, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP));
}
@@ -194,7 +208,7 @@ public class ActionBar extends FrameLayout {
return actionMode;
}
actionMode = new ActionBarMenu(getContext(), this);
actionMode.setBackgroundColor(0xffffffff);
actionMode.setBackgroundColor(0xfff8f8f8);
addView(actionMode, indexOfChild(backButtonImageView));
actionMode.setPadding(0, occupyStatusBar ? AndroidUtilities.statusBarHeight : 0, 0, 0);
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)actionMode.getLayoutParams();
@@ -204,6 +218,7 @@ public class ActionBar extends FrameLayout {
actionMode.setLayoutParams(layoutParams);
actionMode.setVisibility(INVISIBLE);
/*
if (occupyStatusBar && actionModeTop == null) {
actionModeTop = new View(getContext());
actionModeTop.setBackgroundColor(0x99000000);
@@ -215,6 +230,7 @@ public class ActionBar extends FrameLayout {
actionModeTop.setLayoutParams(layoutParams);
actionModeTop.setVisibility(INVISIBLE);
}
*/
return actionMode;
}
@@ -226,9 +242,9 @@ public class ActionBar extends FrameLayout {
actionModeVisible = true;
ArrayList<Animator> animators = new ArrayList<>();
animators.add(ObjectAnimator.ofFloat(actionMode, "alpha", 0.0f, 1.0f));
if (occupyStatusBar && actionModeTop != null) {
/*if (occupyStatusBar && actionModeTop != null) {
animators.add(ObjectAnimator.ofFloat(actionModeTop, "alpha", 0.0f, 1.0f));
}
}*/
if (actionModeAnimation != null) {
actionModeAnimation.cancel();
}
@@ -239,9 +255,9 @@ public class ActionBar extends FrameLayout {
@Override
public void onAnimationStart(Animator animation) {
actionMode.setVisibility(VISIBLE);
if (occupyStatusBar && actionModeTop != null) {
/*if (occupyStatusBar && actionModeTop != null) {
actionModeTop.setVisibility(VISIBLE);
}
}*/
}
@Override
@@ -284,9 +300,9 @@ public class ActionBar extends FrameLayout {
actionModeVisible = false;
ArrayList<Animator> animators = new ArrayList<>();
animators.add(ObjectAnimator.ofFloat(actionMode, "alpha", 0.0f));
if (occupyStatusBar && actionModeTop != null) {
/*if (occupyStatusBar && actionModeTop != null) {
animators.add(ObjectAnimator.ofFloat(actionModeTop, "alpha", 0.0f));
}
}*/
if (actionModeAnimation != null) {
actionModeAnimation.cancel();
}
@@ -299,9 +315,9 @@ public class ActionBar extends FrameLayout {
if (actionModeAnimation != null && actionModeAnimation.equals(animation)) {
actionModeAnimation = null;
actionMode.setVisibility(INVISIBLE);
if (occupyStatusBar && actionModeTop != null) {
/*if (occupyStatusBar && actionModeTop != null) {
actionModeTop.setVisibility(INVISIBLE);
}
}*/
}
}
@@ -331,6 +347,7 @@ public class ActionBar extends FrameLayout {
}
}
/*
public void showActionModeTop() {
if (occupyStatusBar && actionModeTop == null) {
actionModeTop = new View(getContext());
@@ -343,6 +360,7 @@ public class ActionBar extends FrameLayout {
actionModeTop.setLayoutParams(layoutParams);
}
}
*/
public boolean isActionModeShowed() {
return actionMode != null && actionModeVisible;
@@ -366,10 +384,6 @@ public class ActionBar extends FrameLayout {
interceptTouches = value;
}
public void setExtraHeight(int value) {
extraHeight = value;
}
public void closeSearchField() {
if (!isSearchFieldVisible || menu == null) {
return;
@@ -391,7 +405,7 @@ public class ActionBar extends FrameLayout {
int actionBarHeight = getCurrentActionBarHeight();
int actionBarHeightSpec = MeasureSpec.makeMeasureSpec(actionBarHeight, MeasureSpec.EXACTLY);
setMeasuredDimension(width, actionBarHeight + (occupyStatusBar ? AndroidUtilities.statusBarHeight : 0) + extraHeight);
setMeasuredDimension(width, actionBarHeight + (occupyStatusBar ? AndroidUtilities.statusBarHeight : 0));
int textLeft;
if (backButtonImageView != null && backButtonImageView.getVisibility() != GONE) {
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.ui.ActionBar;
@@ -640,7 +655,7 @@ public class ActionBarLayout extends FrameLayout {
if (parentActivity.getCurrentFocus() != null) {
AndroidUtilities.hideKeyboard(parentActivity.getCurrentFocus());
}
boolean needAnimation = !forceWithoutAnimation && parentActivity.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE).getBoolean("view_animations", true);
boolean needAnimation = !forceWithoutAnimation && parentActivity.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE).getBoolean("view_animations2", true);
final BaseFragment currentFragment = !fragmentsStack.isEmpty() ? fragmentsStack.get(fragmentsStack.size() - 1) : null;
@@ -848,7 +863,7 @@ public class ActionBarLayout extends FrameLayout {
AndroidUtilities.hideKeyboard(parentActivity.getCurrentFocus());
}
setInnerTranslationX(0);
boolean needAnimation = animated && parentActivity.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE).getBoolean("view_animations", true);
boolean needAnimation = animated && parentActivity.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE).getBoolean("view_animations2", true);
final BaseFragment currentFragment = fragmentsStack.get(fragmentsStack.size() - 1);
BaseFragment previousFragment = null;
if (fragmentsStack.size() > 1) {
@@ -1,10 +1,25 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
/*******************************************************************************
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.ui.ActionBar;
@@ -31,6 +46,7 @@ public class ActionBarMenu extends LinearLayout {
super(context);
}
/*
public View addItemResource(int id, int resourceId) {
LayoutInflater li = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = li.inflate(resourceId, null);
@@ -48,6 +64,7 @@ public class ActionBarMenu extends LinearLayout {
});
return view;
}
*/
public ActionBarMenuItem addItem(int id, Drawable drawable) { /* */
return addItem(id, 0, parentActionBar.itemsBackgroundColor, drawable, AndroidUtilities.dp(48));
@@ -129,10 +146,10 @@ public class ActionBarMenu extends LinearLayout {
if (item.hasSubMenu()) {
item.toggleSubMenu();
break;
} else if (item.overrideMenuClick) {
} /*else if (item.overrideMenuClick) {
onItemClick((Integer) item.getTag());
break;
}
}*/
}
}
}

Some files were not shown because too many files have changed in this diff Show More