Compare commits
1024 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b0b87f148 | |||
| a955d6f2f7 | |||
| 2143bc01d1 | |||
| 5a3728428b | |||
| f236085b92 | |||
| b0cea8f099 | |||
| 7590b21928 | |||
| ea7fd0ec22 | |||
| 6a4e40d17b | |||
| 5302e86011 | |||
| 5382e880a9 | |||
| 2385b236c7 | |||
| 5224119f9e | |||
| dfef2b4120 | |||
| 6d7f417233 | |||
| 05ef95e757 | |||
| 8aa45b7bc9 | |||
| 7cea23caf5 | |||
| 4a2bed3e6c | |||
| 3aa2fbf67a | |||
| c5d9471f2a | |||
| 5117b70c58 | |||
| da4d17aa14 | |||
| 6135813bff | |||
| 8547f7aab8 | |||
| 6dca20045c | |||
| 58ef777091 | |||
| 4a836b3d82 | |||
| 363122fd15 | |||
| b751219c78 | |||
| e586fafc54 | |||
| 2c04319c52 | |||
| f9607e4453 | |||
| 6aec209ebc | |||
| 9adc9b34e5 | |||
| 9267501bc1 | |||
| 924c3f8920 | |||
| 8f93dacbff | |||
| a07e9312ea | |||
| 79f6010d5f | |||
| c5af1d4f2b | |||
| 20a3820871 | |||
| cb0d1106b4 | |||
| 291ef86a4f | |||
| b0457bc036 | |||
| 4d7107314a | |||
| 4185219088 | |||
| ef51492d0b | |||
| 4dc10f977e | |||
| aaba52dba6 | |||
| ca4808aaca | |||
| 66324fe914 | |||
| a5f67e9d9f | |||
| 8b5c887402 | |||
| 183cb03577 | |||
| b1e6d59ba9 | |||
| 33f38889c1 | |||
| f97e800907 | |||
| 21e597d09e | |||
| 6b040b160d | |||
| 57e5523bf3 | |||
| 874492c9ae | |||
| 8ae6407461 | |||
| ae8ce94c00 | |||
| 74208fff69 | |||
| 7ace2b9baf | |||
| f28fffb9df | |||
| 349e8d80c8 | |||
| 4b9217e3e6 | |||
| 4e6e805ede | |||
| 02a1c86f01 | |||
| b28fb4ac9e | |||
| fddc80e60b | |||
| e5b1becf13 | |||
| 01b78e37fd | |||
| d0b1f36f60 | |||
| e388464d28 | |||
| afc7a04b63 | |||
| b871e42b86 | |||
| 3479233f82 | |||
| 7e3e35a9a1 | |||
| 92711444e4 | |||
| ec576d7159 | |||
| 70a05221ab | |||
| 6335cfa178 | |||
| ebda8c4577 | |||
| de9a2df1b4 | |||
| a3c7e3db3d | |||
| 249fc829be | |||
| 5b62f7bf25 | |||
| df6c858b92 | |||
| c9816ae785 | |||
| a24069ce3d | |||
| f770f817d0 | |||
| bee804e095 | |||
| 6b1c6b37ac | |||
| 881e69c176 | |||
| ac6df2ff85 | |||
| 7698ac5c06 | |||
| c2abe42b60 | |||
| 3e95197d02 | |||
| 534a75feaa | |||
| 7a11234431 | |||
| 0e91537a4d | |||
| b074bf1819 | |||
| b8d2552aa8 | |||
| 320c120a61 | |||
| 6c1a3c850a | |||
| 7ec87f55b6 | |||
| f6ce98f9a2 | |||
| cb465ec85e | |||
| 6f10a90c5f | |||
| 9f439c852f | |||
| 2cc0451a8a | |||
| 02230d05a1 | |||
| 9a6f6d7ea6 | |||
| f4cd299f70 | |||
| d0358a4d0c | |||
| 4fc115b93d | |||
| 0157510c5d | |||
| 831ce19689 | |||
| 9431e26696 | |||
| 9ef01dd352 | |||
| 01c766db41 | |||
| 364d15442a | |||
| 32eefaab42 | |||
| 4b8177f7f0 | |||
| 092a5caf8b | |||
| bfe4438773 | |||
| d251fffc07 | |||
| 180c2bc8de | |||
| 082815f676 | |||
| 640688d774 | |||
| 30868198a2 | |||
| 75673ef1ad | |||
| 478d937b3c | |||
| 7a5eb6bea6 | |||
| 0142d51835 | |||
| 075bad3638 | |||
| 1f173bae9b | |||
| 2deb61477f | |||
| fcc1ab1079 | |||
| c72207da8a | |||
| 82233d7862 | |||
| 2547a0435c | |||
| b6ce6d908f | |||
| c6bbb0a524 | |||
| 826258a1be | |||
| 05dde13e68 | |||
| 06130c7cd8 | |||
| 5d7c701da8 | |||
| 91e898ee96 | |||
| 61e9466c59 | |||
| 39c48d3dcd | |||
| 1c7c3f06e6 | |||
| d4ddb68137 | |||
| c9ac421315 | |||
| e14bf24bb1 | |||
| 700d11f600 | |||
| fbb33c735c | |||
| 7cc9d7ea2f | |||
| ca27be240e | |||
| 8ab9b92517 | |||
| f95b48ad6a | |||
| 8ebdc5414a | |||
| a0e07cb2ea | |||
| 152360d94a | |||
| 8513f7cd36 | |||
| 17e3f3afdd | |||
| 7d7f62dae1 | |||
| 2bd0a0d935 | |||
| cf0414dd66 | |||
| 5fbd065882 | |||
| 5982a9dd8f | |||
| 4b20e3f41f | |||
| ac8d8886cc | |||
| a803d472bf | |||
| 396ed14067 | |||
| 5faea5e40a | |||
| 08bb248919 | |||
| 30dd7a9c33 | |||
| 28f57a2ccb | |||
| c0717237ad | |||
| 5716372453 | |||
| d362cf4467 | |||
| 56118d3732 | |||
| a03f51f7c5 | |||
| 6cf59133c2 | |||
| ef8d833f5e | |||
| b110c1f91e | |||
| e53a1f0b22 | |||
| 5a5cbfceb7 | |||
| 57a557a9b4 | |||
| 2328bf1e2a | |||
| ce4c9aaba0 | |||
| 71a2cb7a5b | |||
| 79462c0596 | |||
| 68f5d1ae27 | |||
| 77caced5ed | |||
| 88a4f99cf8 | |||
| 42544fd447 | |||
| 5ff869cd4a | |||
| 4af1666a31 | |||
| 786fd3f47d | |||
| a0d9315ff4 | |||
| 86954a3c42 | |||
| f136b6d7d1 | |||
| 9e4b744046 | |||
| 8db4604224 | |||
| 43f9094c04 | |||
| 1217e65889 | |||
| d8eaf0ad4b | |||
| 4fbb8d2b78 | |||
| 7cebd04086 | |||
| b584fdfd89 | |||
| db35fef6db | |||
| 068102a0d8 | |||
| 02ff8a2a6b | |||
| 321b5a223b | |||
| ab7fd960ca | |||
| dec685023e | |||
| 168d2ae16c | |||
| e55249182a | |||
| 0966487978 | |||
| fd057485db | |||
| a68f3a7024 | |||
| 1dad6ebf8a | |||
| a77b1a5dd6 | |||
| c0a65e2e9f | |||
| 82ef0560a5 | |||
| 9166b2a42a | |||
| 2ef3811a56 | |||
| 1b91c1c482 | |||
| 0d8962f76c | |||
| e98535418c | |||
| 5eb9065d89 | |||
| 9e70c43ab8 | |||
| fc8bfabeb2 | |||
| 681c22293f | |||
| bed8d511fe | |||
| eeeb686868 | |||
| 76dda10a31 | |||
| 6a7c4f24e9 | |||
| 8cdfafd7c3 | |||
| 1c6faeac04 | |||
| 7ecf0a7daa | |||
| b2bf7e8ae7 | |||
| 0a34456302 | |||
| 3ebcc1dd99 | |||
| ff7df0e1e6 | |||
| 68cd070b98 | |||
| 8ff8cffa15 | |||
| dc216feb30 | |||
| 84fc885617 | |||
| 72bcbe80c0 | |||
| b4ed26b842 | |||
| 7be6b0a1ee | |||
| 9a2fdcdcbd | |||
| e81960f98a | |||
| a37fcfcaf7 | |||
| fd4a377752 | |||
| d40652d394 | |||
| aacca5531c | |||
| 51cb844e63 | |||
| ef0b8efcfa | |||
| 3e943483da | |||
| a9013bb574 | |||
| aa5fcb139e | |||
| c526c42d19 | |||
| f66003c6b1 | |||
| cc64142e7b | |||
| a280407370 | |||
| dda5efedc4 | |||
| 1cd4ec1ec9 | |||
| 1a8b905071 | |||
| de13a70c66 | |||
| 2caa98e08a | |||
| ba997d9f71 | |||
| 3985f2f468 | |||
| 86bd38bdad | |||
| ffa1c69be2 | |||
| 3a3388ed59 | |||
| 8c04c199e6 | |||
| ecdbbed8c1 | |||
| e6f4a616fb | |||
| 68f270eea3 | |||
| 52aeec8d17 | |||
| bdcaffde3d | |||
| d19dcb41ec | |||
| 5525cfa5a9 | |||
| 8715e9eba2 | |||
| 3cc58c2a0f | |||
| 5cc5a80990 | |||
| 52c0197682 | |||
| c75adbe93c | |||
| f16c59897d | |||
| 636235148f | |||
| 46226a7ab5 | |||
| f11ae0a5ff | |||
| 2264e1bf22 | |||
| c095510a42 | |||
| fe6838e315 | |||
| 6dbbdae2df | |||
| a5070c879a | |||
| 1bbf352c4c | |||
| 011ba4595b | |||
| 10345ab8c3 | |||
| b5d0878511 | |||
| 8a393ad274 | |||
| 84e8be9776 | |||
| da6aa901ca | |||
| 8b52a99327 | |||
| 9c6271b1d1 | |||
| 8564259787 | |||
| 9e7ee3170e | |||
| b2cdff820a | |||
| f1789950a6 | |||
| 0bc8750984 | |||
| 6351bcc11e | |||
| 66ae731a37 | |||
| cc1fd53021 | |||
| 3937f0e149 | |||
| 2839250011 | |||
| 5844563de4 | |||
| a8698a03df | |||
| 6a6044c77d | |||
| 2a0be883c2 | |||
| 5ff8101c2c | |||
| 9e87a4b2a8 | |||
| 9740921f25 | |||
| de2892a4c2 | |||
| c344b18326 | |||
| 54d2516f6c | |||
| 8f61bec805 | |||
| 107fe00afa | |||
| 8642563c86 | |||
| c9d4d2bdbc | |||
| 83f5d0793a | |||
| 16dd79aa9a | |||
| d7c3303cec | |||
| a9541a936e | |||
| 54f2d2e991 | |||
| 8daaa45aa3 | |||
| fa40d4fb44 | |||
| a26ce76964 | |||
| 2e798c5b04 | |||
| 9104971baa | |||
| 3d6dc80d8a | |||
| 410072358d | |||
| 1d7d0dd329 | |||
| 15ed570dce | |||
| 90cf75421b | |||
| a8aecea814 | |||
| fcf98ab7e9 | |||
| e1d6b8f3b8 | |||
| 629532a996 | |||
| 36798a2f57 | |||
| 717777f628 | |||
| 5af2d4fa8f | |||
| 23d521beed | |||
| b699451c2a | |||
| fa6fad5787 | |||
| ab641df8f9 | |||
| 90ced3efb0 | |||
| 0d50af1aa5 | |||
| 7f982e6302 | |||
| 0ba20e9fc8 | |||
| cf0df03d9f | |||
| 111629ace2 | |||
| 8afe5739a8 | |||
| b9520c95ed | |||
| 1cf01c6414 | |||
| 98d5259d0e | |||
| 0945696450 | |||
| 59ec5826aa | |||
| e6415abda2 | |||
| f9885fbcad | |||
| 4663299951 | |||
| 0ee08fee42 | |||
| bd8698c3f3 | |||
| 0f694876f5 | |||
| 55ea8bbbce | |||
| ae2c152382 | |||
| 825ee6a79e | |||
| edd7938497 | |||
| f0d62f41c9 | |||
| b5e7f13819 | |||
| 9a1c6ce2ad | |||
| 966d8784f3 | |||
| 571f49ab13 | |||
| 365f1dbbe6 | |||
| 100bb06e20 | |||
| 27f662707a | |||
| 3086c72304 | |||
| eee218a3b6 | |||
| fa868077b0 | |||
| e8776177ba | |||
| 9caf94d035 | |||
| 7593ec1831 | |||
| 677c0592fc | |||
| 0cc58006ba | |||
| 60e3f475de | |||
| 8f47783aaf | |||
| a521974b3b | |||
| 2aa71e3eb2 | |||
| 005cf835a1 | |||
| 5fb61b008b | |||
| dd93de3833 | |||
| a13e31a5f7 | |||
| 3d891d07a9 | |||
| bd3c4f9186 | |||
| f4f0df3adb | |||
| 6fbe5e2f56 | |||
| edeec415a1 | |||
| 90350729d5 | |||
| 7ae4cd4dd3 | |||
| 0b50dd445f | |||
| bd40f4eb04 | |||
| cfdfab1716 | |||
| f8d0fd3f77 | |||
| 7186afa321 | |||
| 201f6e46b0 | |||
| 813d4324f7 | |||
| 27875b83bc | |||
| 29d04afc51 | |||
| e6655fa21d | |||
| 7712ada816 | |||
| 52788976e3 | |||
| 56f55be938 | |||
| 47350b1809 | |||
| bb410a1bb1 | |||
| 04ed9e27c1 | |||
| 93c52ec527 | |||
| a7b86dd59a | |||
| 5587eda657 | |||
| b8a1daff45 | |||
| deddc5dd29 | |||
| 2f371c69cf | |||
| c4aa00d40d | |||
| 46b5ae4831 | |||
| 16e89bb58a | |||
| 1566ea87d3 | |||
| cf653f41b7 | |||
| 785729272a | |||
| a881f84a03 | |||
| d3b2f2df97 | |||
| f518a3eea9 | |||
| 20819912ee | |||
| 25ad133e1f | |||
| 4eef4a86b5 | |||
| fb50b03af4 | |||
| b6ffc8df9e | |||
| 9a71de6944 | |||
| f6ab0b18a2 | |||
| f023961239 | |||
| aeb78d579e | |||
| cdc47423f9 | |||
| 9790730d75 | |||
| 6cc9a2594f | |||
| 6814df2976 | |||
| 8e8b49508d | |||
| 619ac2bb21 | |||
| f6750a1583 | |||
| ce895355f5 | |||
| 17e45fd2ab | |||
| 8f2b761361 | |||
| bc72fce060 | |||
| 501a77e1dc | |||
| ad6f232d34 | |||
| efbe43ec70 | |||
| 5e38b25c43 | |||
| 6f3f356997 | |||
| d6f485511c | |||
| a527b2f404 | |||
| 7c77b946ed | |||
| ca77886012 | |||
| fa5e305dff | |||
| 9e70bfb5ae | |||
| f4c7a9439d | |||
| 5d05e99ad6 | |||
| 79bd1c7cb3 | |||
| 848a86c718 | |||
| e759efc389 | |||
| 63ea218b98 | |||
| 07f4a859f3 | |||
| 34eeec4a39 | |||
| 7f1734b57c | |||
| 83fa4c493d | |||
| cbc8d2b659 | |||
| 6ff1c7a689 | |||
| b10185b6b9 | |||
| 6ef35c6ce1 | |||
| e04e6dfe06 | |||
| 60b322d83e | |||
| 94e597d2d1 | |||
| c1168566f3 | |||
| 72fc6ca4dc | |||
| b2a3042d02 | |||
| ed4c5e7a72 | |||
| 29a0f3dadb | |||
| 1820af288e | |||
| be48179a25 | |||
| 2aa4245563 | |||
| 54b5dba6ea | |||
| 60fa0c98df | |||
| ea60418a70 | |||
| 2bd1493a42 | |||
| 04a22cc8c6 | |||
| d2780c4c03 | |||
| bc73e1a2ba | |||
| 0522aef0fd | |||
| d5a0be0c9c | |||
| d66acbb71e | |||
| 5c06156463 | |||
| c07b2b4aa1 | |||
| 389dddc9ea | |||
| 5dfdc94468 | |||
| 52d65c1bbb | |||
| f0f07055cf | |||
| c6c66f19f2 | |||
| fb3d95264a | |||
| 92e67bd80a | |||
| 1518bab671 | |||
| 632da643af | |||
| 7353873281 | |||
| f9ac0375b9 | |||
| 78820ec18a | |||
| 3c0e4fd853 | |||
| 31c9de1f03 | |||
| 4240c8b324 | |||
| f47fff292f | |||
| 9106f12b77 | |||
| be709fb6df | |||
| fca0c03087 | |||
| 2613be7216 | |||
| 43941f9075 | |||
| 765e2b490e | |||
| cfae361b88 | |||
| bdebd98627 | |||
| be159407ff | |||
| 9daa2d77cd | |||
| 05d2d0964f | |||
| 10c4a105bb | |||
| fe602004c7 | |||
| e2d7f2c3d3 | |||
| 6996836f80 | |||
| 7d689dc1d6 | |||
| 9960423b57 | |||
| 06183a9135 | |||
| 796acf1bb0 | |||
| 64e4816f5e | |||
| 1680d332f5 | |||
| 77ee5c6bc0 | |||
| f8c678b7ff | |||
| cde74ae2e1 | |||
| 5fd3352d81 | |||
| e41b7a4701 | |||
| dd85322333 | |||
| c7bffb85fd | |||
| 3518f3d213 | |||
| b31774c899 | |||
| eebb64ff04 | |||
| ee1a6ea5c6 | |||
| 063e80bbf7 | |||
| 9f30ebd62c | |||
| ae53415545 | |||
| 8a495411a3 | |||
| 2f432bfd3b | |||
| b2b006c512 | |||
| 7b63d383ee | |||
| 2b3a56bb26 | |||
| e3408af5be | |||
| b14b8eea9d | |||
| 5e6c5d7c5e | |||
| c1eff8362f | |||
| fbca401488 | |||
| 39fe0dd523 | |||
| 0491018c24 | |||
| f91ceb8463 | |||
| 9f1def20cd | |||
| 64a8c7755d | |||
| 12807763d6 | |||
| fbf8637c97 | |||
| 8aedbc60a4 | |||
| 38c3b5561b | |||
| 3e21f9be4d | |||
| ad1159c05c | |||
| 314ecf1639 | |||
| df6ac480c6 | |||
| 8974a62a0c | |||
| d2c8e2e63a | |||
| 11600184be | |||
| ee78397614 | |||
| b33311b9fb | |||
| b90f6e296a | |||
| 5789390451 | |||
| 1a84cd05a5 | |||
| 3578a9bef1 | |||
| 52280bf744 | |||
| 26645da56d | |||
| 828ff3d8ca | |||
| 7954e94d45 | |||
| c1e47f4a9c | |||
| d3193221f6 | |||
| e7b4accea0 | |||
| 67baedc0dc | |||
| 74c02932c3 | |||
| 2968d033f2 | |||
| 43d48fa510 | |||
| 337f353220 | |||
| 218295c4f3 | |||
| bf86d4f1b7 | |||
| d383bcc451 | |||
| 2e1a40b606 | |||
| 10c538ac8e | |||
| 35852604ea | |||
| 0c8ecaf8df | |||
| cbe70a93db | |||
| 7768ef3ff9 | |||
| d337230a4b | |||
| de76377f3b | |||
| f223b22ff8 | |||
| 9850e6fff7 | |||
| 6ab94814ca | |||
| e0db504b18 | |||
| fe44eec6c8 | |||
| 7bc595a889 | |||
| 5133461eb7 | |||
| b4b12862fd | |||
| 1340166a91 | |||
| 1663502d35 | |||
| 9a67f5b32a | |||
| 773913409f | |||
| e6988e3f61 | |||
| 0047259457 | |||
| 3e9c977b3f | |||
| 3a49e30d93 | |||
| 11addb6522 | |||
| 6353bb94f4 | |||
| 5df2ba4325 | |||
| 2f79310db6 | |||
| 129278443d | |||
| 0e27692c32 | |||
| 92be6947db | |||
| 884ba32ce9 | |||
| fe2ac243c7 | |||
| 4b978c0124 | |||
| 8bf799ec2a | |||
| f92225528c | |||
| 796b723be7 | |||
| 544ab41aa4 | |||
| cca9dfb04a | |||
| faa94acbcb | |||
| e7517c23ad | |||
| 370afbf2ce | |||
| bcee64317d | |||
| 6fe4d57b48 | |||
| 906db9155a | |||
| 9b7b35acb1 | |||
| 5d25ed7715 | |||
| 5691ab9e84 | |||
| 64aa0fb49c | |||
| d3fe672ec0 | |||
| 4e199c0080 | |||
| 8fd8927293 | |||
| 320f0ba27d | |||
| c7d903f64a | |||
| f394b22e45 | |||
| e7ced91670 | |||
| 35836ad18f | |||
| 2e99f6ee93 | |||
| 5c551cb2a8 | |||
| 6490a641da | |||
| 06164d240d | |||
| b729e93b8a | |||
| d7345f75e2 | |||
| 695e1e06dd | |||
| ccedf31b96 | |||
| 31f89cec02 | |||
| 42561266ba | |||
| 17a182625d | |||
| 625d0ed66c | |||
| f7f1f29531 | |||
| 134fbc7188 | |||
| 5d26387aa3 | |||
| 4a3070bb54 | |||
| 8377f23c58 | |||
| ec4c96dd92 | |||
| f0fb8e62f8 | |||
| 823e6b6e1f | |||
| 49a59fbebe | |||
| 3424d60ea4 | |||
| 1ead71c87a | |||
| 7a44182c3f | |||
| 75c111cb0b | |||
| 1c9b51f5dc | |||
| 5e88059283 | |||
| 76307706f1 | |||
| 4db770b1f8 | |||
| 96e635f0bd | |||
| 7dd3176a06 | |||
| 16037e7693 | |||
| 6f5756c331 | |||
| 8aa7dfabe6 | |||
| 13c45f37e7 | |||
| c45b5fe9e4 | |||
| 0a9d042747 | |||
| 6cbbb9608e | |||
| f8766820db | |||
| a2847ea45e | |||
| 841e5ee30a | |||
| 624f797888 | |||
| a640c81c73 | |||
| cd344daf5a | |||
| 247ddfcf1a | |||
| d09ad5748d | |||
| 4658adc2cd | |||
| 30ddf2b03e | |||
| edb2c963c6 | |||
| cf2f6bd2ec | |||
| 62ad0e942f | |||
| 8cc8ee404d | |||
| 180b278a95 | |||
| e91671b958 | |||
| 7e0fc59687 | |||
| 255a96d393 | |||
| dfed8aec00 | |||
| a498d58c2a | |||
| 4ec02a59de | |||
| 276c0785fa | |||
| 6a4efb3358 | |||
| bcad4a7302 | |||
| 120007d87a | |||
| 55eab9dd0b | |||
| ea99f0cd48 | |||
| 6f6cd80516 | |||
| 2bb63878b1 | |||
| c5d7ec0946 | |||
| da7a3fc50a | |||
| 9c36749619 | |||
| cb3fd74af9 | |||
| 7b11c5dd79 | |||
| e3c108c773 | |||
| 6621046d6d | |||
| ed23c93526 | |||
| 19cd640dc8 | |||
| f1fe3e0807 | |||
| fce62453d1 | |||
| 441f943190 | |||
| fb759e12f0 | |||
| d1737cb69f | |||
| c94f9371fe | |||
| c88a1b53b0 | |||
| 2e2aeed127 | |||
| 6e764a6f2f | |||
| 669d8f4cba | |||
| d8cdc21880 | |||
| c083195acf | |||
| 139dafab7a | |||
| ef8ac7ae04 | |||
| 7c6d47bfae | |||
| 185fd5d730 | |||
| ef4f79b4a3 | |||
| c09be6c35b | |||
| 2a385c6768 | |||
| 93a114cff4 | |||
| 49dab181aa | |||
| 3ad9ffd94f | |||
| 53f1c73e52 | |||
| 641c3da629 | |||
| 491bbfade0 | |||
| f8dc6bb3db | |||
| b0d842b3c4 | |||
| 9cc4422f9e | |||
| dce7d56fb2 | |||
| 9b9a546e69 | |||
| 1787d8665c | |||
| f29f8bf5f9 | |||
| 3b7fd24fcb | |||
| acdbba6351 | |||
| 2134f97956 | |||
| f45fd60ccd | |||
| dabe68fbf8 | |||
| 7193b4d64e | |||
| bf138aa2e7 | |||
| b5000e2fb9 | |||
| cbd4e2b520 | |||
| bf4087792d | |||
| 25164af440 | |||
| 9579bd5bc8 | |||
| 713623c11d | |||
| f2ee08a7f1 | |||
| 66339d77bf | |||
| 1da80e9264 | |||
| f501c41d16 | |||
| ec7e3fc91a | |||
| 5e91902a26 | |||
| 8b5b2d2f95 | |||
| 523b62cfeb | |||
| 19c158d1c4 | |||
| af1cbd09ce | |||
| 9b3dc7d434 | |||
| 4547f771f5 | |||
| be6f30da24 | |||
| bb0ae0772e | |||
| c38c5a5d36 | |||
| bd3ab20981 | |||
| 3dc216ca7f | |||
| 9d9630041e | |||
| 5095892847 | |||
| 25d0f4d017 | |||
| 3306c3e8d1 | |||
| fa0c59d01c | |||
| 94291bd0ab | |||
| ff0e004e20 | |||
| 415785b59d | |||
| a8f17d618b | |||
| c0e45abd3d | |||
| ca8767e0b0 | |||
| de96a4dc6e | |||
| 04d8437b89 | |||
| 38ed95003f | |||
| ca174e82a2 | |||
| f08d5a64b0 | |||
| d467285036 | |||
| 7b833b43d7 | |||
| f8cee59e28 | |||
| 2f85654567 | |||
| 205a7fd10e | |||
| b0ab464ef8 | |||
| c1a49332d0 | |||
| a69ff20ac4 | |||
| ddd4cacd1d | |||
| 836e11ca27 | |||
| 4fc53a4632 | |||
| 1391a902b0 | |||
| 876bdcacf1 | |||
| 37e6bbc53c | |||
| ab05222c32 | |||
| e87a3de296 | |||
| 9d96d7311c | |||
| 52b7f10c1b | |||
| 3ae4bf238e | |||
| a630fad9b0 | |||
| 6543ad0893 | |||
| 4ca4d81345 | |||
| 3b8a3e10ad | |||
| 814f892e98 | |||
| 8a2149b67d | |||
| ebfd9ca122 | |||
| ba34971d2b | |||
| 91f8e88db3 | |||
| a93b12909d | |||
| 8403abc5b9 | |||
| c49599f9c8 | |||
| deed7a569c | |||
| ec590613d0 | |||
| 24d1ca9015 | |||
| 0c914b3771 | |||
| 24e363e130 | |||
| 85b9444f86 | |||
| 19f6bd3b5c | |||
| 1999bab9a0 | |||
| e6325578ca | |||
| 44eb9b32fd | |||
| c5cf11d36a | |||
| fc36c6793e | |||
| 669a1a769d | |||
| 257e200c9c | |||
| 2c2bd2bf48 | |||
| 95fbe25b0a | |||
| ab7b2f4b1b | |||
| 97552bc751 | |||
| a81cdb79ad | |||
| 1b8de09c41 | |||
| db427a036e | |||
| ab2151b974 | |||
| 0fa59fa22d | |||
| ec7807e662 | |||
| d47196d899 | |||
| b692abc235 | |||
| 07b55b739e | |||
| e3809cf1f8 | |||
| 564c0fe27d | |||
| 705e283cf5 | |||
| 40b5e10999 | |||
| d1f77d9de5 | |||
| 0704f7f1e7 | |||
| 78b3c3883d | |||
| e28e6b9b73 | |||
| 4ed51b6b56 | |||
| 084707eb67 | |||
| 0eb02b59a3 | |||
| 00d411dd5a | |||
| 770861d6db | |||
| 17986e9d86 | |||
| 5ce1f415ce | |||
| 1816a50dee | |||
| 6c6c04ba53 | |||
| 145432a57d | |||
| 20acd38ab4 | |||
| bf7095d0ee | |||
| a6b168f4eb | |||
| 883536a3a5 | |||
| 3013af2a7a | |||
| 7b52bb6c85 | |||
| 1439bc0a86 | |||
| 9a5415eb66 | |||
| 5da3748a6a | |||
| 2308feea8c | |||
| a3b0ee1ef9 | |||
| 4fa5f1f5c6 | |||
| f4701b92e2 | |||
| 6649165b56 | |||
| 1c9b5977a8 | |||
| 49471999b9 | |||
| 86a8fbf463 | |||
| 238ec5b299 | |||
| 4c810c2b50 | |||
| f4d5547da7 | |||
| 52850c25b0 | |||
| 5117e103bb | |||
| 056f9bd4fb | |||
| 877989b5c1 | |||
| eef8609a66 | |||
| b43685bcdf | |||
| 794062b9ed | |||
| 876721fae5 | |||
| 0fdac57c68 | |||
| b3375f4f19 | |||
| b607ca21ff | |||
| d6efa3c7e5 | |||
| e461e2f7f1 | |||
| 684f8834fa | |||
| b440a31820 | |||
| 568a3f383f | |||
| 6c212a5d3d | |||
| b6e43a6994 | |||
| d1f85e6dc4 | |||
| 74ef8dc1c6 | |||
| 1da2d35af4 | |||
| bb3f4a2e06 | |||
| 8e4c5d6b63 | |||
| 1b46edaddf | |||
| aa0c72356f | |||
| ee8fd429f4 | |||
| bc022f5196 | |||
| d4dcdcf3bd | |||
| 74b2369ca9 | |||
| 9eafdbd817 | |||
| 0a21d1d1b4 | |||
| b0cb6c73ba | |||
| b6367416e5 | |||
| 0b582dd287 | |||
| d5cf919bcb | |||
| 490f1efe36 | |||
| c125d4fa24 | |||
| 389098699a | |||
| df696e5fab | |||
| e8801d1b64 | |||
| 8bfc44c8ca | |||
| 36e963e717 | |||
| 9e8fcf8ed7 | |||
| 6412d25420 | |||
| a470366f03 | |||
| 1434ce6f57 | |||
| 654ba5b460 | |||
| e3835f70f7 | |||
| bbb803f91c | |||
| 6a4c73c821 | |||
| 853ce3c7a3 | |||
| d2ab4817b1 | |||
| 9ff1cafd5e | |||
| 40a79fbad5 | |||
| bf6b678e2b | |||
| 6c1f787a38 | |||
| b059741161 | |||
| fdd5cc5d4b | |||
| 77465b5802 | |||
| d17567029d | |||
| 4610a64732 | |||
| 0358e8ffcb | |||
| 406c263d82 | |||
| 6ed070e437 | |||
| 1e4cfa124e | |||
| c42ba0f599 | |||
| 8798275170 | |||
| ac6495ff20 | |||
| 8ae47e97cf | |||
| 6a1ff0bace | |||
| 047cd9a121 | |||
| 555547b6cc | |||
| 5fe355fe90 | |||
| 6af3b0d985 | |||
| 92d6147644 | |||
| 72fb9992a1 | |||
| e8413019ff | |||
| 289118e671 | |||
| 286a84b1d4 | |||
| 8c8c003f2b | |||
| a06e36993d | |||
| ce054d93a8 | |||
| cabc159cd8 | |||
| c78cbbe145 | |||
| a297c71a90 | |||
| 247de63362 | |||
| 20f7c7c616 | |||
| 6364903a28 | |||
| 48bb77cfae | |||
| 53bfe52797 | |||
| a8bc01e6c1 | |||
| 3fbccf3408 | |||
| 86656f1155 | |||
| 89473b8067 | |||
| 887f5881f0 | |||
| aee757e71c | |||
| 30d5eeeb45 | |||
| d2c2f2cf07 | |||
| f83d5a0dbb | |||
| c30ddf9c77 | |||
| e14b878aef | |||
| 9e3ebda12a | |||
| 4bb42b9995 | |||
| 59b158fe09 |
@@ -0,0 +1,22 @@
|
||||
version: 2
|
||||
mergeable:
|
||||
- when: pull_request.*
|
||||
name: "Changelog check"
|
||||
validate:
|
||||
- do: or
|
||||
validate:
|
||||
- do: description
|
||||
must_include:
|
||||
regex: '#skip-changelog'
|
||||
- do: and
|
||||
validate:
|
||||
- do: dependent
|
||||
changed:
|
||||
file: '**/*.java'
|
||||
required: ['CHANGELOG.md']
|
||||
fail:
|
||||
- do: checks
|
||||
status: 'action_required'
|
||||
payload:
|
||||
title: CHANGELOG.md might need an update
|
||||
summary: "Please update CHANGELOG.md or add #skip-changelog to the description"
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
artifacts-url-comments:
|
||||
name: add artifact links to pull request
|
||||
runs-on: windows-2019
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
steps:
|
||||
- name: add artifact links to pull request
|
||||
|
||||
@@ -2,16 +2,16 @@ name: Upload Preview APK
|
||||
|
||||
on: pull_request
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Upload Preview APK
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Cancel Previous Runs
|
||||
uses: styfle/cancel-workflow-action@0.9.1
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Validate Fastlane Metadata
|
||||
@@ -19,12 +19,12 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
working-directory: jni/deltachat-core-rust
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
- uses: android-actions/setup-android@v3
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
@@ -37,6 +37,9 @@ jobs:
|
||||
with:
|
||||
ndk-version: r27
|
||||
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/actions/wrapper-validation@v4
|
||||
|
||||
- name: Compile core
|
||||
env:
|
||||
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
@@ -44,9 +47,6 @@ jobs:
|
||||
export PATH="${PATH}:${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64/bin/"
|
||||
scripts/install-toolchains.sh && scripts/ndk-make.sh armeabi-v7a
|
||||
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
|
||||
- name: Build APK
|
||||
run: ./gradlew --no-daemon -PABI_FILTER=armeabi-v7a assembleFossDebug
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
body: '[<img src="store/get-it-on-gplay.png" alt="Get it on Google Play" height="48">](https://play.google.com/store/apps/details?id=com.github.arcanechat) [<img src="store/get-it-on-fdroid.png" alt="Get it on F-Droid" height="48">](https://f-droid.org/packages/chat.delta.lite) [<img src="store/get-it-on-IzzyOnDroid.png" alt="Get it on IzzyOnDroid" height="48">](https://apt.izzysoft.de/fdroid/index/apk/chat.delta.lite) [<img src="store/get-it-on-apklis.png" alt="Disponible en Apklis" height="48">](https://www.apklis.cu/application/chat.delta.lite) [<img src="store/get-it-on-github.png" alt="Get it on GitHub" height="48">](https://github.com/ArcaneChat/android/releases/latest/download/ArcaneChat-gplay.apk)'
|
||||
body: '[<img src="store/get-it-on-gplay.png" alt="Get it on Google Play" height="48">](https://play.google.com/store/apps/details?id=com.github.arcanechat) [<img src="store/get-it-on-fdroid.png" alt="Get it on F-Droid" height="48">](https://f-droid.org/packages/chat.delta.lite) [<img src="store/get-it-on-github.png" alt="Get it on GitHub" height="48">](https://github.com/ArcaneChat/android/releases/latest/download/ArcaneChat-gplay.apk)'
|
||||
prerelease: ${{ contains(github.event.ref, '-beta') }}
|
||||
fail_on_unmatched_files: true
|
||||
files: build/outputs/apk/foss/release/*.apk
|
||||
|
||||
@@ -1,4 +1,186 @@
|
||||
# ArcaneChat Android Changelog
|
||||
# Delta Chat Android Changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
* target Android 15
|
||||
* improve readability of info messages in dark mode
|
||||
* drop too short disappearing messages options
|
||||
* fix Direct Share shortcuts
|
||||
* fix: don't show error message when cancelling profile creation
|
||||
* enable permanent notification by default if push notifications are not available
|
||||
* hide "clone chat" and member list for incoming channels
|
||||
* show warning if background notifications will be unreliable
|
||||
* warn if the app has not been updated after 6 months instead of 1 year
|
||||
* avoid "unknown sender for this chat" error
|
||||
* properly display "Messages are end-to-end encrypted." in all encrypted groups
|
||||
* show dialog if user has permanently denied camera permission and tries to take picture for group avatar
|
||||
* several small fixes and improvements
|
||||
* add experimental built-in calls
|
||||
* update to core 2.20.0
|
||||
|
||||
## v2.11.0
|
||||
2025-08
|
||||
|
||||
* add "After 1 year" option to disappearing messages
|
||||
* improve image quality when setting group avatars
|
||||
* add Estonian translation, update other translations
|
||||
* allow to clone email chats
|
||||
* fix some small bugs
|
||||
* update to core 2.11.0
|
||||
|
||||
## v2.10.0
|
||||
2025-08
|
||||
|
||||
* fix "Archived" item's layout in chat-list
|
||||
* don't enlarge "Saved Messages" and "Devices Messages" avatars on click
|
||||
* share email address for email contacts instead of vCard
|
||||
* open existing encrypted chat when opening a mailto link or clicking an email address in a message bubble
|
||||
* update to core 2.10.0
|
||||
|
||||
## v2.9.0
|
||||
2025-07
|
||||
|
||||
* hide contact email addresses in search results
|
||||
* disable non-functional message editing and ephemeral messages timer settings in classic email thread chat
|
||||
* don't enlarge email chats avatar placeholder
|
||||
* improve message date/status footer layout, also in RTL languages
|
||||
* display correct text when receiving a "Disappearing messages enabled" system message
|
||||
* Update to core 2.9.0
|
||||
|
||||
## v2.8.0
|
||||
2025-07
|
||||
|
||||
* Profiles focus on recognizing contacts
|
||||
* See the number of media directly in the profile, no need to tap around
|
||||
* Clearer app lists by removing redundant "App" subtitle
|
||||
* New button for quick access to the apps sent in current chat
|
||||
* New icon for the in-chat apps button
|
||||
* Improve hint for app drafts
|
||||
* Add Text-To-Speech (TTS) support for in-chat apps
|
||||
* New icon for the QR icon
|
||||
* Start rebuilding the experimental broadcast lists
|
||||
into proper channels - note that this is work-in-progress
|
||||
* Improved separation between unencrypted chats/contacts and encrypted ones, avoiding mixing of encrypted and unencrypted messages in the same chat
|
||||
* Removed padlocks, as encrypted is the default "normal" state. Instead, unencrypted email is marked with a small email / letter (✉️) icon
|
||||
* Classic email chats/threads get a big email / letter icon making it easy to recognize
|
||||
* After some time, add a device message asking to donate. Can't wait? Donate today at https://delta.chat/donate
|
||||
* Allow to sort profiles up in the profile switcher
|
||||
* Add new option to create unencrypted email thread
|
||||
* Green checkmarks are removed where they mostly refer to guaranteed encryption, which is the default now. They are still used for profile's "Introduced by"
|
||||
* Update to core 2.8.0
|
||||
|
||||
## v1.58.4
|
||||
2025-05
|
||||
|
||||
* make in-chat apps properly work when they are not sent yet, in draft mode
|
||||
* better avatar quality
|
||||
* some more bug fixes and updated translations
|
||||
* update to core 1.159.5
|
||||
|
||||
## v1.58.3
|
||||
2025-05
|
||||
|
||||
* fix: webxdc.selfName uses the name otherwise displayed
|
||||
* fix potential crash on startup
|
||||
* add donation link to app settings
|
||||
* update to core 1.159.3
|
||||
|
||||
## v1.58.2
|
||||
2025-04
|
||||
|
||||
* fix draft writing area disappearing for some chats
|
||||
* update to core 1.159.2
|
||||
|
||||
## v1.58.1
|
||||
2025-04
|
||||
|
||||
* tapping info messages with contacts open the contact's profile
|
||||
* hide superfluous "Show Classic E-mails" advanced setting for chatmail
|
||||
* show profile bio/status under name in main settings screen
|
||||
* remove mostly non-telling transport addresses when referring to a contact;
|
||||
the contact's profile gives a much better overview
|
||||
* Disable AEAP to enable us to overhaul some things - there are big changes underway in this area, which will come in a few months
|
||||
* don't display email address in contact list and member list for contacts with green-checkmark
|
||||
* avoid crash in Notifications preferences if ringtone title can't be read
|
||||
* don't display forwarded messages as "edited" if original message was edited
|
||||
* support importing contact from ProtonMail vCard attachments received in chats
|
||||
* send encrypted in the experimental broadcast lists feature
|
||||
* wait for QR scan (or invite link click) process to complete before allowing to send messages
|
||||
* show connectivity status dot when profile is connecting or not connected
|
||||
* never send Autocrypt-Gossip in broadcast lists.
|
||||
* update to core 1.159.1
|
||||
|
||||
## v1.56.1
|
||||
2025-03
|
||||
|
||||
* ignore click in info-messages from deleted in-chat apps
|
||||
* data saving: do not send messages to the server if user is the only member of the chat in single-device usage
|
||||
* protect metadata: encrypt message's sent date
|
||||
* do not fail to send messages in groups if some encryption keys are missing
|
||||
* synchronize contact name changes across devices
|
||||
* fix changing group names that was not working in some situations
|
||||
* fix: do not show outdated message text in "Message Info" of an edited message
|
||||
* some more small bug fixes and updated translations
|
||||
* update to core 1.158.0
|
||||
|
||||
## v1.56.0
|
||||
2025-03
|
||||
|
||||
* allow to edit messages
|
||||
* allow to delete messages for everyone
|
||||
* add mute option "8 hours"
|
||||
* add menu option to easily save/unsave selected message
|
||||
* improve deletion confirmation for "Device Messages"
|
||||
* remove dangerous encryption options
|
||||
* always paste as plain text in message draft area
|
||||
* some small bug fixes and updated translations
|
||||
* update to core 1.157.2
|
||||
|
||||
## v1.54.4
|
||||
2025-03
|
||||
|
||||
* allow better avatar (profile picture) quality
|
||||
* remove notifications from chat that was deleted from other device
|
||||
* when a chat is deleted, also delete its messages from server
|
||||
* avoid freezing when opening the app for the first time after install
|
||||
* avoid crash when adding chat shortcut to home screen
|
||||
* some small bug fixes and updated translations
|
||||
* update to core 1.156.3
|
||||
|
||||
## v1.54.3
|
||||
2025-03
|
||||
|
||||
* allow to add any chat to the home screen
|
||||
* update "forward message" icon and organize the messages actions bar
|
||||
* do not allow non-members to change ephemeral timer settings of groups
|
||||
* properly display padlock when the message is not sent over the network
|
||||
* sync message deletion to other devices
|
||||
* sync chat deletion across devices
|
||||
* Show sender in "Saved Messages"
|
||||
* allow scanning multiple QR-invitation codes without needing to wait for completion to scan the next one
|
||||
* when reactions are seen in one device, remove notification from your other devices
|
||||
* don't disturb with notification when someone leave a group
|
||||
* detect incompatible profiles from newer app version when importing them
|
||||
* prepare the app for receiving edited messages
|
||||
* prepare the app for receiving message deletion requests
|
||||
* do some small bug fixes
|
||||
* update translations
|
||||
* update to core 1.156.2
|
||||
|
||||
## v1.54.0
|
||||
2025-02
|
||||
|
||||
* enhanced "Saved Messages" feature, now when forwarding a message to "Saved Messages" chat, it retains the sender information and a button to jump to the original message
|
||||
* Saved messages are marked by a bookmark sign
|
||||
* improve explanation when blocking a contact
|
||||
* improve wording in empty "apps" and "files" tabs in chat media screen
|
||||
* remove deprecated/legacy built-in "half-camera"
|
||||
* UI improvement: keep avatars aligned to message bubble when message has reactions
|
||||
* fix problems when opening attachments in external apps
|
||||
* fix a bug with some big images appearing as blank/transparent
|
||||
* some other small bug fixes
|
||||
* update translations
|
||||
* update to core 1.155.4
|
||||
|
||||
## v1.52.1
|
||||
2025-01
|
||||
@@ -4,8 +4,6 @@ A [Delta Chat](https://delta.chat/) client for Android. Learn more at: https://a
|
||||
|
||||
[<img src="store/get-it-on-gplay.png" alt="Get it on Google Play" height="48">](https://play.google.com/store/apps/details?id=com.github.arcanechat)
|
||||
[<img src="store/get-it-on-fdroid.png" alt="Get it on F-Droid" height="48">](https://f-droid.org/packages/chat.delta.lite)
|
||||
[<img src="store/get-it-on-IzzyOnDroid.png" alt="Get it on IzzyOnDroid" height="48">](https://apt.izzysoft.de/fdroid/index/apk/chat.delta.lite)
|
||||
[<img src="store/get-it-on-apklis.png" alt="Disponible en Apklis" height="48">](https://www.apklis.cu/application/chat.delta.lite)
|
||||
[<img src="store/get-it-on-github.png" alt="Get it on GitHub" height="48">](https://github.com/ArcaneChat/android/releases/latest/download/ArcaneChat-gplay.apk)
|
||||
|
||||
|
||||
@@ -17,7 +15,6 @@ This app has some extended support for WebXDC apps:
|
||||
|
||||
- `window.webxdc.arcanechat` a string with the ArcaneChat version and can be used by app developers
|
||||
to detect when they can use the ArcaneChat-specific features.
|
||||
- `window.webxdc.isCommunity` true if the current account is a community account
|
||||
- `sendToChat()`: extra property `subject` can be set to a text string to set message/email's subject.
|
||||
- `sendToChat()`: extra property `html` can be set to a string of html markup to set the HTML part of the email/message.
|
||||
- `sendToChat()`: the file object parameter also accepts a `type` field that can be one of:
|
||||
@@ -33,4 +30,4 @@ This app has some extended support for WebXDC apps:
|
||||
|
||||
This app is based on the [official Delta Chat client](https://github.com/deltachat/deltachat-android) with several improvements.
|
||||
|
||||
This app uses a [modified](https://github.com/ArcaneChat/core) version of the [Delta Chat Core Library](https://github.com/deltachat/deltachat-core-rust).
|
||||
This app uses a [modified](https://github.com/ArcaneChat/core) version of the [Chatmail Core Library](https://github.com/chatmail/core).
|
||||
|
||||
@@ -13,25 +13,24 @@ on the command-line, in a PR called "update-core-and-stuff-DATE":
|
||||
./scripts/ndk-make.sh
|
||||
```
|
||||
|
||||
a) Update `CHANGELOG.md`
|
||||
from <https://github.com/chatmail/core/blob/main/CHANGELOG.md>,
|
||||
do not just copy and avoid technical terms.
|
||||
The changelog is for the end user and shall show impacts form that angle.
|
||||
Add used core version to end of changelog entry
|
||||
as `update to core 1.2.3` or `using core 1.2.3`
|
||||
|
||||
|
||||
2. update translations and local help:
|
||||
```
|
||||
./scripts/tx-pull-translations.sh
|
||||
./scripts/create-local-help.sh # requires deltachat-pages checked out at ../deltachat-pages
|
||||
```
|
||||
|
||||
the "update-core-and-stuff-DATE" PR can be merged without review
|
||||
(as everything was already reviewed in their repos).
|
||||
|
||||
the following steps are done in a PR called `prep-VERSION` (no leading "v"):
|
||||
|
||||
3. a) update `CHANGELOG.md`
|
||||
from <https://github.com/deltachat/deltachat-core-rust/blob/main/CHANGELOG.md>
|
||||
and <https://github.com/deltachat/deltachat-android/pulls?q=is%3Apr+is%3Aclosed+sort%3Aupdated-desc>.
|
||||
do not just copy and avoid technical terms.
|
||||
the changelog is for the end user and shall show impacts form that angle.
|
||||
b) update changelog date as `YYYY-MM`
|
||||
c) add used core version to end of changelog entry
|
||||
as `update to core 1.2.3` or `using core 1.2.3`
|
||||
3. Update `CHANGELOG.md`:
|
||||
Rename header with version number and add date as `YYYY-MM`
|
||||
|
||||
in case previous entries of the changelog refer to betas or to not officially released versions,
|
||||
the entries can be summarized.
|
||||
@@ -114,7 +113,7 @@ This may take some days.
|
||||
|
||||
on <https://developer.amazon.com/dashboard>:
|
||||
|
||||
12. a) for "Delta Chat", select "Add upcoming version" on the left
|
||||
12. a) click "App List", for "Delta Chat", select "Add upcoming version" on the left
|
||||
b) at "Step 1 / Existing file(s)" hit "Replace", upload the APK from above
|
||||
c) on the "Step 1" page, add "Release notes" from CHANGELOG.md, hit "Next"
|
||||
d) on "Step 2" and "Step 3" pages, hit "Next"
|
||||
@@ -131,7 +130,7 @@ on <https://developer.huawei.com/consumer/en/appgallery>:
|
||||
d) Hit "Submit"; on the next page, confirm version and language
|
||||
|
||||
|
||||
## Releases on Apklis, Passkoocheh
|
||||
## Releases on other stores (ex. Passkoocheh)
|
||||
|
||||
These stores are not under our control.
|
||||
On important updates **ping store maintainers** and ask to update.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id 'com.android.application' version '8.5.2'
|
||||
id 'com.android.application' version '8.11.1'
|
||||
id 'com.google.gms.google-services' version '4.4.1'
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ android {
|
||||
}
|
||||
namespace "org.thoughtcrime.securesms"
|
||||
flavorDimensions "none"
|
||||
compileSdk 34
|
||||
compileSdk 35
|
||||
|
||||
// Set NDK version to strip native libraries.
|
||||
// Even though we compile our libraries outside Gradle with `scripts/ndk-make.sh`,
|
||||
@@ -33,14 +33,14 @@ android {
|
||||
useLibrary 'org.apache.http.legacy'
|
||||
|
||||
defaultConfig {
|
||||
versionCode 30000708
|
||||
versionName "1.52.2"
|
||||
versionCode 30000729
|
||||
versionName "2.20.0"
|
||||
|
||||
applicationId "chat.delta.lite"
|
||||
multiDexEnabled true
|
||||
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 34
|
||||
targetSdkVersion 35
|
||||
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
@@ -209,9 +209,9 @@ dependencies {
|
||||
implementation 'com.airbnb.android:lottie:4.2.2' // Lottie animations support.
|
||||
|
||||
implementation 'androidx.sharetarget:sharetarget:1.2.0'
|
||||
implementation 'androidx.webkit:webkit:1.12.1'
|
||||
implementation 'androidx.webkit:webkit:1.14.0'
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.7.1'
|
||||
implementation 'com.google.android.material:material:1.12.0'
|
||||
implementation 'androidx.legacy:legacy-support-v13:1.0.0'
|
||||
implementation ('androidx.preference:preference:1.2.1') {
|
||||
@@ -219,7 +219,7 @@ dependencies {
|
||||
exclude group: 'androidx.lifecycle', module:'lifecycle-viewmodel-ktx'
|
||||
}
|
||||
implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.7'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.4.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-common-java8:2.6.2'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.6.2'
|
||||
@@ -233,53 +233,46 @@ dependencies {
|
||||
implementation 'com.google.zxing:core:3.3.0' // fixed version to support SDK<24
|
||||
implementation ('com.journeyapps:zxing-android-embedded:4.3.0') { transitive = false } // QR Code scanner
|
||||
implementation 'com.fasterxml.jackson.core:jackson-databind:2.11.1' // used as JSON library
|
||||
implementation 'com.google.code.gson:gson:2.9.1' // used as JSON library. Don't upgrade to 2.10.1: https://github.com/deltachat/deltachat-android/pull/2610
|
||||
implementation "me.leolin:ShortcutBadger:1.1.16" // display messagecount on the home screen icon.
|
||||
implementation 'com.jpardogo.materialtabstrip:library:1.0.9' // used in the emoji selector for the tab selection.
|
||||
implementation 'com.github.chrisbanes:PhotoView:2.1.3' // does the zooming on photos / media
|
||||
implementation 'com.github.penfeizhou.android.animation:glide-plugin:3.0.2' // APNG & animated webp support.
|
||||
implementation 'com.github.Baseflow:PhotoView:2.3.0' // does the zooming on photos / media
|
||||
implementation 'com.caverock:androidsvg-aar:1.4' // SVG support.
|
||||
implementation 'com.github.bumptech.glide:glide:4.12.0'
|
||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
|
||||
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
|
||||
annotationProcessor 'androidx.annotation:annotation:1.9.1'
|
||||
implementation 'com.makeramen:roundedimageview:2.1.0' // crops the avatars to circles
|
||||
implementation 'com.pnikosis:materialish-progress:1.5' // used only in the "Progress Wheel" in Share Activity.
|
||||
implementation 'com.makeramen:roundedimageview:2.3.0' // crops the avatars to circles
|
||||
implementation 'com.github.amulyakhare:TextDrawable:558677ea31' // number of unread messages,
|
||||
// the one-letter circle for the contacts (when there is not avatar) and a white background.
|
||||
implementation 'com.googlecode.mp4parser:isoparser:1.0.6' // MP4 recoding; upgrading eg. to 1.1.22 breaks recoding, however, i have not investigated further, just reset to 1.0.6
|
||||
implementation ('com.davemorrissey.labs:subsampling-scale-image-view:3.6.0') { // for the zooming on photos / media
|
||||
implementation ('com.davemorrissey.labs:subsampling-scale-image-view:3.10.0') { // for the zooming on photos / media
|
||||
exclude group: 'com.android.support', module: 'support-annotations'
|
||||
}
|
||||
implementation 'com.annimon:stream:1.1.8' // brings future java streams api to SDK Version < 24
|
||||
|
||||
implementation 'com.getkeepsafe.relinker:relinker:1.4.4' // needed to avoid safe-content-resolver-v14 trying to fetch older non-existing version
|
||||
// Replacement for ContentResolver
|
||||
// that protects against the Surreptitious Sharing attack.
|
||||
// <https://github.com/cketti/SafeContentResolver>
|
||||
implementation 'de.cketti.safecontentresolver:safe-content-resolver-v14:1.0.0'
|
||||
implementation 'de.cketti.safecontentresolver:safe-content-resolver-v21:1.0.0'
|
||||
|
||||
gplayImplementation('com.google.firebase:firebase-messaging:24.1.0') { // for PUSH notifications
|
||||
gplayImplementation('com.google.firebase:firebase-messaging:24.1.2') { // for PUSH notifications, don't upgrade: v25.0.0 requires minSdk>=23
|
||||
exclude group: 'com.google.firebase', module: 'firebase-core'
|
||||
exclude group: 'com.google.firebase', module: 'firebase-analytics'
|
||||
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
|
||||
}
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.assertj:assertj-core:1.7.1'
|
||||
testImplementation 'org.mockito:mockito-core:1.9.5'
|
||||
testImplementation 'org.powermock:powermock-api-mockito:1.6.1'
|
||||
testImplementation 'org.powermock:powermock-module-junit4:1.6.1'
|
||||
testImplementation 'org.powermock:powermock-module-junit4-rule:1.6.1'
|
||||
testImplementation 'org.powermock:powermock-classloading-xstream:1.6.1'
|
||||
testImplementation 'org.assertj:assertj-core:3.27.3'
|
||||
testImplementation 'org.mockito:mockito-core:5.18.0'
|
||||
testImplementation 'org.powermock:powermock-api-mockito:1.7.4'
|
||||
testImplementation 'org.powermock:powermock-module-junit4:2.0.9'
|
||||
testImplementation 'org.powermock:powermock-module-junit4-rule:2.0.9'
|
||||
testImplementation 'org.powermock:powermock-classloading-xstream:2.0.9'
|
||||
|
||||
androidTestImplementation 'androidx.test:runner:1.6.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.6.1'
|
||||
androidTestImplementation 'androidx.test:rules:1.6.1'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
||||
androidTestImplementation 'androidx.test:runner:1.7.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.7.0'
|
||||
androidTestImplementation 'androidx.test:rules:1.7.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
|
||||
androidTestImplementation 'com.android.support:support-annotations:28.0.0'
|
||||
|
||||
androidTestImplementation ('org.assertj:assertj-core:1.7.1') {
|
||||
androidTestImplementation ('org.assertj:assertj-core:3.27.3') {
|
||||
exclude group: 'org.hamcrest', module: 'hamcrest-core'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
ArcaneChat is a decentralized and secure messenger app that is compatible with the existing e-mail infrastructure.
|
||||
ArcaneChat is a decentralized and secure instant messenger that is easy to use for friends and family.
|
||||
|
||||
Some features at a glance:
|
||||
• Anonymous. Instant onboarding without a phone number, e-mail or other private data.
|
||||
|
||||
💬 Reliable instant messaging with multi-profile and multi-device support.
|
||||
• Flexible. Supports multiple chat profiles and is easy to setup on multiple devices.
|
||||
|
||||
⚡️ Sign-up easily using secure fast chatmail servers. You don’t need a phone number or any private data.
|
||||
• Extensible. Use mini-apps in chats like shopping lists, calendars or games.
|
||||
|
||||
📧 Alternatively, use your existing classic e-mail address to read your inbox as chats.
|
||||
• Reliable. Works under bad and adversarial network conditions.
|
||||
|
||||
🎮 Interactive mini-apps in chats for gaming and collaboration.
|
||||
• Secure. Audited End-to-End encryption safe against network and server attacks.
|
||||
|
||||
🔒 Audited end-to-end encryption safe against network and server attacks.
|
||||
• Sovereign. Can be run with your own e-mail address or server.
|
||||
|
||||
ArcaneChat is a Delta Chat client and was created with a focus on usability, good user experience, and saving data plan. Also the app usually experiments with new features that eventually might get added to the official Delta Chat client.
|
||||
|
||||
@@ -24,7 +24,7 @@ ArcaneChat is a Delta Chat client and was created with a focus on usability, goo
|
||||
<li>It is possible to disable profiles to completely disconnect them saving data/bandwidth</li>
|
||||
<li>You can easily see the connection status of all your profiles in the profile switcher</li>
|
||||
<li>A videochat instance is set by default</li>
|
||||
<li>Location streaming feature enabled by default and extra option to share location for 12 hours</li>
|
||||
<li>Extra option to share location for 12 hours</li>
|
||||
<li>Clicking on a message with a POI location, will open the POI on the map</li>
|
||||
<li>Last-seen status of contacts is shown in your contact list, like in WhatsApp, Telegram, etc.</li>
|
||||
<li>Videos are played in loop, useful for short GIF videos</li>
|
||||
|
||||
|
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 137 KiB |
@@ -7,11 +7,11 @@
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1733948466,
|
||||
"narHash": "sha256-o/uq/tU458Ykudi8Zk3sRga5iazkuSczt9wDOCUDOSU=",
|
||||
"lastModified": 1756239746,
|
||||
"narHash": "sha256-0ibN685tT+u/Nbmbrrq9G3mRUzct2Votyv/a7Wwv26s=",
|
||||
"owner": "tadfisher",
|
||||
"repo": "android-nixpkgs",
|
||||
"rev": "0bf99ffaea6a7c0948ae10cf2e40c2905e4e4d6b",
|
||||
"rev": "256631d162ec883b2341ee59621516e1f65f0f6b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -28,11 +28,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1728330715,
|
||||
"narHash": "sha256-xRJ2nPOXb//u1jaBnDP56M7v5ldavjbtR6lfGqSvcKg=",
|
||||
"lastModified": 1741473158,
|
||||
"narHash": "sha256-kWNaq6wQUbUMlPgw8Y+9/9wP0F8SHkjy24/mN3UAppg=",
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"rev": "dd6b80932022cea34a019e2bb32f6fa9e494dfef",
|
||||
"rev": "7c9e793ebe66bcba8292989a68c0419b737a22a0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -79,11 +79,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1733759999,
|
||||
"narHash": "sha256-463SNPWmz46iLzJKRzO3Q2b0Aurff3U1n0nYItxq7jU=",
|
||||
"lastModified": 1756125398,
|
||||
"narHash": "sha256-XexyKZpf46cMiO5Vbj+dWSAXOnr285GHsMch8FBoHbc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a73246e2eef4c6ed172979932bc80e1404ba2d56",
|
||||
"rev": "3b9f00d7a7bf68acd4c4abb9d43695afb04e03a5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -95,11 +95,11 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1733749988,
|
||||
"narHash": "sha256-+5qdtgXceqhK5ZR1YbP1fAUsweBIrhL38726oIEAtDs=",
|
||||
"lastModified": 1756159630,
|
||||
"narHash": "sha256-ohMvsjtSVdT/bruXf5ClBh8ZYXRmD4krmjKrXhEvwMg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "bc27f0fde01ce4e1bfec1ab122d72b7380278e68",
|
||||
"rev": "84c256e42600cb0fdf25763b48d28df2f25a0c8b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -111,11 +111,11 @@
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1728538411,
|
||||
"narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=",
|
||||
"lastModified": 1744536153,
|
||||
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221",
|
||||
"rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -138,11 +138,11 @@
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1733970833,
|
||||
"narHash": "sha256-sPEKtSaZk2CtfF9cdhtbY93S6qGq+d2PKI1fcoDfDaI=",
|
||||
"lastModified": 1756197489,
|
||||
"narHash": "sha256-S16rPaBH1TnMbDyL5NlGSJcYd7wPlOEWTStdBDL7BHw=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "f7f4c59ccdf1bec3f1547d27398e9589aa94e3e8",
|
||||
"rev": "8ec04f46f1edeeed3f870da62191745b93975da7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -15,19 +15,22 @@
|
||||
pkgs = import nixpkgs { inherit system overlays; };
|
||||
android-sdk = android.sdk.${system} (sdkPkgs:
|
||||
with sdkPkgs; [
|
||||
build-tools-34-0-0
|
||||
build-tools-35-0-0
|
||||
cmdline-tools-latest
|
||||
platform-tools
|
||||
platforms-android-34
|
||||
ndk-27-0-11902837
|
||||
platforms-android-35
|
||||
ndk-27-2-12479018
|
||||
]);
|
||||
rust-version = pkgs.lib.removeSuffix "\n"
|
||||
(builtins.readFile ./scripts/rust-toolchain);
|
||||
in {
|
||||
in
|
||||
{
|
||||
formatter = pkgs.nixpkgs-fmt;
|
||||
|
||||
devShells.default = pkgs.mkShell {
|
||||
ANDROID_SDK_ROOT = "${android-sdk}/share/android-sdk";
|
||||
ANDROID_NDK_ROOT =
|
||||
"${android-sdk}/share/android-sdk/ndk/27.0.11902837";
|
||||
"${android-sdk}/share/android-sdk/ndk/27.2.12479018";
|
||||
buildInputs = [
|
||||
android-sdk
|
||||
pkgs.openjdk17
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
android.defaults.buildfeatures.buildconfig=true
|
||||
android.enableJetifier=true
|
||||
android.nonFinalResIds=false
|
||||
android.nonTransitiveRClass=false
|
||||
android.useAndroidX=true
|
||||
org.gradle.jvmargs=-Xmx4608m
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
distributionSha256Sum=20f1b1176237254a6fc204d8434196fa11a4cfb387567519c61556e8710aed78
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -231,12 +231,6 @@ JNIEXPORT jboolean Java_com_b44t_messenger_DcAccounts_backgroundFetch(JNIEnv *en
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jint Java_com_b44t_messenger_DcAccounts_addAccount(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return dc_accounts_add_account(get_dc_accounts(env, obj));
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jint Java_com_b44t_messenger_DcAccounts_migrateAccount(JNIEnv *env, jobject obj, jstring dbfile)
|
||||
{
|
||||
CHAR_REF(dbfile);
|
||||
@@ -659,6 +653,14 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcContext_getMsgInfo(JNIEnv *env, jobj
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcContext_sendEditRequest(JNIEnv *env, jobject obj, jint msg_id, jstring text)
|
||||
{
|
||||
CHAR_REF(text);
|
||||
dc_send_edit_request(get_dc_context(env, obj), msg_id, textPtr);
|
||||
CHAR_UNREF(text);
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jstring Java_com_b44t_messenger_DcContext_getMsgHtml(JNIEnv *env, jobject obj, jint msg_id)
|
||||
{
|
||||
char* temp = dc_get_msg_html(get_dc_context(env, obj), msg_id);
|
||||
@@ -683,6 +685,15 @@ JNIEXPORT void Java_com_b44t_messenger_DcContext_deleteMsgs(JNIEnv *env, jobject
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcContext_sendDeleteRequest(JNIEnv *env, jobject obj, jintArray msg_ids)
|
||||
{
|
||||
int msg_ids_cnt = 0;
|
||||
uint32_t* msg_ids_ptr = jintArray2uint32Pointer(env, msg_ids, &msg_ids_cnt);
|
||||
dc_send_delete_request(get_dc_context(env, obj), msg_ids_ptr, msg_ids_cnt);
|
||||
free(msg_ids_ptr);
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcContext_forwardMsgs(JNIEnv *env, jobject obj, jintArray msg_ids, jint chat_id)
|
||||
{
|
||||
int msg_ids_cnt = 0;
|
||||
@@ -691,6 +702,13 @@ JNIEXPORT void Java_com_b44t_messenger_DcContext_forwardMsgs(JNIEnv *env, jobjec
|
||||
free(msg_ids_ptr);
|
||||
}
|
||||
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcContext_saveMsgs(JNIEnv *env, jobject obj, jintArray msg_ids)
|
||||
{
|
||||
int msg_ids_cnt = 0;
|
||||
uint32_t* msg_ids_ptr = jintArray2uint32Pointer(env, msg_ids, &msg_ids_cnt);
|
||||
dc_save_msgs(get_dc_context(env, obj), msg_ids_ptr, msg_ids_cnt);
|
||||
free(msg_ids_ptr);
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean Java_com_b44t_messenger_DcContext_resendMsgs(JNIEnv *env, jobject obj, jintArray msg_ids)
|
||||
{
|
||||
@@ -717,12 +735,6 @@ JNIEXPORT jint Java_com_b44t_messenger_DcContext_sendTextMsg(JNIEnv *env, jobjec
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jint Java_com_b44t_messenger_DcContext_sendVideochatInvitation(JNIEnv *env, jobject obj, jint chat_id)
|
||||
{
|
||||
return (jint)dc_send_videochat_invitation(get_dc_context(env, obj), chat_id);
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jboolean Java_com_b44t_messenger_DcContext_sendWebxdcStatusUpdate(JNIEnv *env, jobject obj, jint msg_id, jstring payload)
|
||||
{
|
||||
CHAR_REF(payload);
|
||||
@@ -904,15 +916,6 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcContext_initiateKeyTransfer(JNIEnv *
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jboolean Java_com_b44t_messenger_DcContext_continueKeyTransfer(JNIEnv *env, jobject obj, jint msg_id, jstring setupCode)
|
||||
{
|
||||
CHAR_REF(setupCode);
|
||||
jboolean ret = dc_continue_key_transfer(get_dc_context(env, obj), msg_id, setupCodePtr);
|
||||
CHAR_UNREF(setupCode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcContext_imex(JNIEnv *env, jobject obj, jint what, jstring dir)
|
||||
{
|
||||
CHAR_REF(dir);
|
||||
@@ -1228,6 +1231,12 @@ JNIEXPORT jint Java_com_b44t_messenger_DcChat_getColor(JNIEnv *env, jobject obj)
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jboolean Java_com_b44t_messenger_DcChat_isEncrypted(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return dc_chat_is_encrypted(get_dc_chat(env, obj))!=0;
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jboolean Java_com_b44t_messenger_DcChat_isUnpromoted(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return dc_chat_is_unpromoted(get_dc_chat(env, obj))!=0;
|
||||
@@ -1257,11 +1266,6 @@ JNIEXPORT jboolean Java_com_b44t_messenger_DcChat_isProtected(JNIEnv *env, jobje
|
||||
return dc_chat_is_protected(get_dc_chat(env, obj))!=0;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean Java_com_b44t_messenger_DcChat_isProtectionBroken(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return dc_chat_is_protection_broken(get_dc_chat(env, obj))!=0;
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jboolean Java_com_b44t_messenger_DcChat_isSendingLocations(JNIEnv *env, jobject obj)
|
||||
{
|
||||
@@ -1364,11 +1368,6 @@ JNIEXPORT jint Java_com_b44t_messenger_DcMsg_getId(JNIEnv *env, jobject obj)
|
||||
return dc_msg_get_id(get_dc_msg(env, obj));
|
||||
}
|
||||
|
||||
JNIEXPORT jint Java_com_b44t_messenger_DcMsg_getSenderColor(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return dc_msg_get_sender_color(get_dc_msg(env, obj));
|
||||
}
|
||||
|
||||
JNIEXPORT jstring Java_com_b44t_messenger_DcMsg_getText(JNIEnv *env, jobject obj)
|
||||
{
|
||||
char* temp = dc_msg_get_text(get_dc_msg(env, obj));
|
||||
@@ -1423,6 +1422,12 @@ JNIEXPORT jint Java_com_b44t_messenger_DcMsg_getInfoType(JNIEnv *env, jobject ob
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jint Java_com_b44t_messenger_DcMsg_getInfoContactId(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return dc_msg_get_info_contact_id(get_dc_msg(env, obj));
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jint Java_com_b44t_messenger_DcMsg_getState(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return dc_msg_get_state(get_dc_msg(env, obj));
|
||||
@@ -1582,12 +1587,6 @@ JNIEXPORT jboolean Java_com_b44t_messenger_DcMsg_isInfo(JNIEnv *env, jobject obj
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jboolean Java_com_b44t_messenger_DcMsg_isSetupMessage(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return dc_msg_is_setupmessage(get_dc_msg(env, obj));
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jboolean Java_com_b44t_messenger_DcMsg_hasHtml(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return dc_msg_has_html(get_dc_msg(env, obj))!=0;
|
||||
@@ -1603,21 +1602,6 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcMsg_getSetupCodeBegin(JNIEnv *env, j
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jstring Java_com_b44t_messenger_DcMsg_getVideochatUrl(JNIEnv *env, jobject obj)
|
||||
{
|
||||
char* temp = dc_msg_get_videochat_url(get_dc_msg(env, obj));
|
||||
jstring ret = JSTRING_NEW(temp);
|
||||
dc_str_unref(temp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jint Java_com_b44t_messenger_DcMsg_getVideochatType(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return (jint)dc_msg_get_videochat_type(get_dc_msg(env, obj));
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcMsg_setSubject(JNIEnv *env, jobject obj, jstring text)
|
||||
{
|
||||
CHAR_REF(text);
|
||||
@@ -1642,16 +1626,6 @@ JNIEXPORT void Java_com_b44t_messenger_DcMsg_setHtml(JNIEnv *env, jobject obj, j
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcMsg_setFile(JNIEnv *env, jobject obj, jstring file, jstring filemime)
|
||||
{
|
||||
CHAR_REF(file);
|
||||
CHAR_REF(filemime);
|
||||
dc_msg_set_file(get_dc_msg(env, obj), filePtr, filemimePtr);
|
||||
CHAR_UNREF(filemime);
|
||||
CHAR_UNREF(file);
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcMsg_forceSticker(JNIEnv *env, jobject obj)
|
||||
{
|
||||
dc_msg_force_sticker(get_dc_msg(env, obj));
|
||||
@@ -1667,6 +1641,12 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcMsg_getPOILocation(JNIEnv *env, jobj
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jboolean Java_com_b44t_messenger_DcMsg_isEdited(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return dc_msg_is_edited(get_dc_msg(env, obj));
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcMsg_setFileAndDeduplicate(JNIEnv *env, jobject obj, jstring file, jstring name, jstring filemime)
|
||||
{
|
||||
CHAR_REF(file);
|
||||
@@ -1723,6 +1703,15 @@ JNIEXPORT jlong Java_com_b44t_messenger_DcMsg_getParentCPtr(JNIEnv *env, jobject
|
||||
return (jlong)dc_msg_get_parent(get_dc_msg(env, obj));
|
||||
}
|
||||
|
||||
JNIEXPORT jint Java_com_b44t_messenger_DcMsg_getOriginalMsgId(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return (jint)dc_msg_get_original_msg_id(get_dc_msg(env, obj));
|
||||
}
|
||||
|
||||
JNIEXPORT jint Java_com_b44t_messenger_DcMsg_getSavedMsgId(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return (jint)dc_msg_get_saved_msg_id(get_dc_msg(env, obj));
|
||||
}
|
||||
|
||||
JNIEXPORT jstring Java_com_b44t_messenger_DcMsg_getError(JNIEnv *env, jobject obj)
|
||||
{
|
||||
@@ -1800,15 +1789,6 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcContact_getAddr(JNIEnv *env, jobject
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jstring Java_com_b44t_messenger_DcContact_getNameNAddr(JNIEnv *env, jobject obj)
|
||||
{
|
||||
char* temp = dc_contact_get_name_n_addr(get_dc_contact(env, obj));
|
||||
jstring ret = JSTRING_NEW(temp);
|
||||
dc_str_unref(temp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jstring Java_com_b44t_messenger_DcContact_getProfileImage(JNIEnv *env, jobject obj)
|
||||
{
|
||||
char* temp = dc_contact_get_profile_image(get_dc_contact(env, obj));
|
||||
@@ -1856,6 +1836,12 @@ JNIEXPORT jboolean Java_com_b44t_messenger_DcContact_isVerified(JNIEnv *env, job
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jboolean Java_com_b44t_messenger_DcContact_isKeyContact(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return dc_contact_is_key_contact(get_dc_contact(env, obj))==1;
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jint Java_com_b44t_messenger_DcContact_getVerifierId(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return dc_contact_get_verifier_id(get_dc_contact(env, obj));
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
- nuovo inserimento: puoi creare un nuovo profilo con un tocco su "Crea Nuovo Profilo" - oppure utilizzare un accesso esistente o la configurazione del secondo dispositivo come al solito
|
||||
i contatti possono essere allegati come "Schede" in "Allega/Contatto"; quando il destinatario tocca le carte, è possibile stabilire la crittografia end-to-end garantita
|
||||
- aggiungi contatti manualmente in "Nuova Chat / Nuovo Contatto / Aggiungi Contatto Manualmente"
|
||||
- invia qualsiasi emoji come reazione
|
||||
- mostrare le reazioni nei riepiloghi
|
||||
- blocca/archivia/ecc. le chat direttamente dai risultati di ricerca
|
||||
- Risolti bug e altro ancora
|
||||
@@ -1,4 +0,0 @@
|
||||
- aggiungi un'opzione per contrassegnare tutte le chat selezionate come "Lette" (tocca a lungo una chat per avviare la modalità di selezione)
|
||||
- i nuovi profili chatmail per dispositivo singolo hanno come impostazione predefinita "Elimina Messaggi Dopo il Download"
|
||||
- quando si utilizza un profilo chatmail su più dispositivi, la cancellazione viene modificata in "Automatica" (la strategia di cancellazione dipende quindi dal server)
|
||||
- Risolti bug e altro ancora
|
||||
@@ -1,15 +1,11 @@
|
||||
# native methods
|
||||
-keep class com.b44t.messenger.** { * ; }
|
||||
|
||||
# Gson uses generic type information stored in a class file when working with
|
||||
# fields. Proguard removes such information by default, keep it.
|
||||
-keepattributes Signature
|
||||
# This is also needed for R8 in compat mode since multiple
|
||||
# optimizations will remove the generic signature such as class
|
||||
# merging and argument removal. See:
|
||||
# https://r8.googlesource.com/r8/+/refs/heads/main/compatibility-faq.md#troubleshooting-gson-gson
|
||||
-keep class com.google.gson.reflect.TypeToken { *; }
|
||||
-keep class * extends com.google.gson.reflect.TypeToken
|
||||
# Keep metadata needed by the JSON parser
|
||||
-keep class chat.delta.rpc.** { * ; }
|
||||
-keepattributes *Annotation*,EnclosingMethod,Signature
|
||||
-keepnames class com.fasterxml.jackson.** { *; }
|
||||
#-dontwarn com.fasterxml.jackson.databind.**
|
||||
|
||||
# bug with video recoder
|
||||
-keep class com.coremedia.iso.** { *; }
|
||||
|
||||
@@ -11,13 +11,13 @@ echo "==================== ANDROID USAGE ===================="
|
||||
grep --exclude={*.apk,*.a,*.o,*.so,strings.xml,*symbols.zip} --exclude-dir={.git,.gradle,obj,release,.idea,build,deltachat-core-rust} -ri $TEXT .
|
||||
|
||||
echo "==================== IOS USAGE ===================="
|
||||
grep --exclude=*.strings --exclude-dir={.git,libraries,Pods,deltachat-ios.xcodeproj,deltachat-ios.xcworkspace} -ri $TEXT ../deltachat-ios/
|
||||
grep --exclude=*.strings* --exclude-dir={.git,libraries,Pods,deltachat-ios.xcodeproj,deltachat-ios.xcworkspace} -ri $TEXT ../deltachat-ios/
|
||||
|
||||
echo "==================== DESKTOP USAGE ===================="
|
||||
grep --exclude-dir={.cache,.git,html-dist,node_modules,_locales} -ri $TEXT ../deltachat-desktop/
|
||||
|
||||
echo "==================== JSONRPC USAGE ===================="
|
||||
grep --exclude-dir={.git} -ri $TEXT ../deltachat-core-rust/deltachat-jsonrpc
|
||||
grep --exclude-dir={.git} -ri $TEXT ../chatmail/core/deltachat-jsonrpc
|
||||
|
||||
echo "==================== UBUNTU TOUCH USAGE ===================="
|
||||
grep --exclude-dir={.git} -ri $TEXT ../deltatouch/
|
||||
|
||||
@@ -53,7 +53,6 @@ fi
|
||||
# for reproducible build:
|
||||
export RUSTFLAGS="-C link-args=-Wl,--build-id=none --remap-path-prefix=$HOME/.cargo= --remap-path-prefix=$(realpath $(dirname $(dirname "$0")))="
|
||||
export SOURCE_DATE_EPOCH=1
|
||||
export CARGO_TARGET_DIR=/tmp/arcanechat-build
|
||||
# always use the same path to NDK:
|
||||
rm -f /tmp/android-ndk-root
|
||||
ln -s "$ANDROID_NDK_ROOT" /tmp/android-ndk-root
|
||||
@@ -74,6 +73,10 @@ if test -z "$NDK_HOST_TAG"; then
|
||||
NDK_HOST_TAG="$KERNEL-$ARCH"
|
||||
fi
|
||||
|
||||
if test -z "$CARGO_TARGET_DIR"; then
|
||||
export CARGO_TARGET_DIR=/tmp/arcanechat-build
|
||||
fi
|
||||
|
||||
TOOLCHAIN="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/$NDK_HOST_TAG"
|
||||
export CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER="$TOOLCHAIN/bin/armv7a-linux-androideabi21-clang"
|
||||
export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="$TOOLCHAIN/bin/aarch64-linux-android21-clang"
|
||||
@@ -127,7 +130,7 @@ if test -z $1 || test $1 = armeabi-v7a; then
|
||||
TARGET_AR="$TOOLCHAIN/bin/llvm-ar" \
|
||||
TARGET_RANLIB="$TOOLCHAIN/bin/llvm-ranlib" \
|
||||
cargo build $RELEASEFLAG --target armv7-linux-androideabi -p deltachat_ffi
|
||||
cp $CARGO_TARGET_DIR/armv7-linux-androideabi/$RELEASE/libdeltachat.a $jnidir/armeabi-v7a
|
||||
cp "$CARGO_TARGET_DIR/armv7-linux-androideabi/$RELEASE/libdeltachat.a" "$jnidir/armeabi-v7a"
|
||||
fi
|
||||
|
||||
if test -z $1 || test $1 = arm64-v8a; then
|
||||
@@ -136,7 +139,7 @@ if test -z $1 || test $1 = arm64-v8a; then
|
||||
TARGET_AR="$TOOLCHAIN/bin/llvm-ar" \
|
||||
TARGET_RANLIB="$TOOLCHAIN/bin/llvm-ranlib" \
|
||||
cargo build $RELEASEFLAG --target aarch64-linux-android -p deltachat_ffi
|
||||
cp $CARGO_TARGET_DIR/aarch64-linux-android/$RELEASE/libdeltachat.a $jnidir/arm64-v8a
|
||||
cp "$CARGO_TARGET_DIR/aarch64-linux-android/$RELEASE/libdeltachat.a" "$jnidir/arm64-v8a"
|
||||
fi
|
||||
|
||||
if test -z $1 || test $1 = x86; then
|
||||
@@ -145,17 +148,17 @@ if test -z $1 || test $1 = x86; then
|
||||
TARGET_AR="$TOOLCHAIN/bin/llvm-ar" \
|
||||
TARGET_RANLIB="$TOOLCHAIN/bin/llvm-ranlib" \
|
||||
cargo build $RELEASEFLAG --target i686-linux-android -p deltachat_ffi
|
||||
cp $CARGO_TARGET_DIR/i686-linux-android/$RELEASE/libdeltachat.a $jnidir/x86
|
||||
cp "$CARGO_TARGET_DIR/i686-linux-android/$RELEASE/libdeltachat.a" "$jnidir/x86"
|
||||
fi
|
||||
|
||||
if test -z $1 || test $1 = x86_64; then
|
||||
echo "-- cross compiling to x86_64-linux-android (x86_64) --"
|
||||
TARGET_CC="$TOOLCHAIN/bin/x86_64-linux-android21-clang" \
|
||||
TARGET_AR="$TOOLCHAIN/bin/llvm-ar" \
|
||||
TARGET_RANLIB="$TOOLCHAIN/bin/llvm-ranlib" \
|
||||
cargo build $RELEASEFLAG --target x86_64-linux-android -p deltachat_ffi
|
||||
cp $CARGO_TARGET_DIR/x86_64-linux-android/$RELEASE/libdeltachat.a $jnidir/x86_64
|
||||
fi
|
||||
if test -z $1 || test $1 = x86_64; then
|
||||
echo "-- cross compiling to x86_64-linux-android (x86_64) --"
|
||||
TARGET_CC="$TOOLCHAIN/bin/x86_64-linux-android21-clang" \
|
||||
TARGET_AR="$TOOLCHAIN/bin/llvm-ar" \
|
||||
TARGET_RANLIB="$TOOLCHAIN/bin/llvm-ranlib" \
|
||||
cargo build $RELEASEFLAG --target x86_64-linux-android -p deltachat_ffi
|
||||
cp "$CARGO_TARGET_DIR/x86_64-linux-android/$RELEASE/libdeltachat.a" "$jnidir/x86_64"
|
||||
fi
|
||||
|
||||
echo -- ndk-build --
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
# replace Delta Chat with ArcaneChat
|
||||
find ./src/main/assets/help/ -type f -name '*.html' | xargs sed -i 's/get.delta.chat/github.com\/ArcaneChat/g'
|
||||
find ./src/main/assets/help/ -type f -name '*.html' | xargs sed -i 's/Delta Chat/ArcaneChat/g'
|
||||
|
||||
find ./src/ -type f -name 'strings.xml' | xargs sed -i 's/get.delta.chat/github.com\/ArcaneChat/g'
|
||||
find ./src/ -type f -name 'strings.xml' | xargs sed -i 's/delta.chat\/donate/arcanechat.me\/#contribute/g'
|
||||
find ./src/ -type f -name 'strings.xml' | xargs sed -i 's/Delta Chat/ArcaneChat/g'
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.83.0
|
||||
1.86.0
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
find ./src/main/assets/help/ -type f -name '*.html' | xargs sed -i 's/github.com\/ArcaneChat/get.delta.chat/g'
|
||||
find ./src/main/assets/help/ -type f -name '*.html' | xargs sed -i 's/ArcaneChat/Delta Chat/g'
|
||||
|
||||
find ./src/ -type f -name 'strings.xml' | xargs sed -i 's/github.com\/ArcaneChat/get.delta.chat/g'
|
||||
find ./src/ -type f -name 'strings.xml' | xargs sed -i 's/arcanechat.me\/#contribute/delta.chat\/donate/g'
|
||||
find ./src/ -type f -name 'strings.xml' | xargs sed -i 's/ArcaneChat/Delta Chat/g'
|
||||
|
||||
# don't revert the app name
|
||||
sed -i 's/>Delta Chat</>ArcaneChat</g' ./src/main/res/values/strings.xml
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package com.b44t.messenger;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.typeText;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -23,12 +29,6 @@ import org.thoughtcrime.securesms.util.AccessibilityUtil;
|
||||
import org.thoughtcrime.securesms.util.Prefs;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.typeText;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
public class TestUtils {
|
||||
private static int createdAccountId = 0;
|
||||
private static boolean resetEnterSends = false;
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
package com.b44t.messenger.uibenchmarks;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.Espresso.pressBack;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.replaceText;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.test.espresso.contrib.RecyclerViewActions;
|
||||
@@ -17,15 +26,6 @@ import org.junit.runner.RunWith;
|
||||
import org.thoughtcrime.securesms.ConversationListActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.Espresso.pressBack;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.replaceText;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
|
||||
@Ignore("This is not a test, but a benchmark. Remove the @Ignore to run it.")
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
package com.b44t.messenger.uitests.offline;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.Espresso.pressBack;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
@@ -25,17 +36,6 @@ import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.Espresso.pressBack;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
@@ -93,7 +93,7 @@ public class SharingTest {
|
||||
}
|
||||
}
|
||||
Uri uri = Uri.parse("content://" + BuildConfig.APPLICATION_ID + ".attachments/" + Uri.encode(pngImage));
|
||||
DcHelper.sharedFiles.put(pngImage, 1);
|
||||
DcHelper.sharedFiles.put(pngImage, "image/png");
|
||||
|
||||
Intent i = new Intent(Intent.ACTION_SEND);
|
||||
i.setType("image/png");
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
package com.b44t.messenger.uitests.online;
|
||||
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.replaceText;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
@@ -17,15 +26,6 @@ import org.thoughtcrime.securesms.BuildConfig;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.WelcomeActivity;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.replaceText;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
public class OnboardingTest {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.thoughtcrime.securesms.notifications;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/*
|
||||
|
||||
@@ -78,17 +78,6 @@
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<action android:name="android.intent.action.GET_CONTENT"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="content"/>
|
||||
<data android:scheme="file"/>
|
||||
<data android:mimeType="*/*" />
|
||||
<data android:mimeType="application/octet-stream" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
@@ -224,8 +213,6 @@
|
||||
|
||||
<intent-filter>
|
||||
<data android:scheme="mailto"/>
|
||||
<data android:scheme="http"/>
|
||||
<data android:scheme="https"/>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
@@ -300,6 +287,10 @@
|
||||
android:theme="@style/TextSecure.LightNoActionBar"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".AllMediaActivity"
|
||||
android:theme="@style/TextSecure.LightNoActionBar"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".DummyActivity"
|
||||
android:theme="@android:style/Theme.NoDisplay"
|
||||
android:enabled="true"
|
||||
@@ -379,6 +370,13 @@
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
|
||||
</activity>
|
||||
|
||||
<activity android:name=".calls.CallActivity"
|
||||
android:label=""
|
||||
android:theme="@style/TextSecure.LightTheme"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"
|
||||
android:exported="true">
|
||||
</activity>
|
||||
|
||||
<activity android:name=".WebxdcActivity"
|
||||
android:label=""
|
||||
android:theme="@style/TextSecure.LightTheme"
|
||||
@@ -438,6 +436,14 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".notifications.DeclineCallReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="org.thoughtcrime.securesms.notifications.DECLINE_CALL_NOTICED"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".notifications.RemoteReplyReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
@@ -511,5 +517,8 @@
|
||||
<intent>
|
||||
<action android:name="android.media.action.IMAGE_CAPTURE" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.TTS_SERVICE" />
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
||||
|
||||
|
After Width: | Height: | Size: 183 B |
|
After Width: | Height: | Size: 349 B |
|
After Width: | Height: | Size: 300 B |
|
Before Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 324 B |
|
Before Width: | Height: | Size: 2.2 KiB |
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
|
||||
<html lang="pl"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
|
||||
<li><a href="#czym-jest-delta-chat">Czym jest Delta Chat?</a>
|
||||
<ul>
|
||||
<li><a href="#jak-mogę-znaleźć-ludzi-z-którymi-mogę-czatować">Jak mogę znaleźć ludzi, z którymi mogę czatować?</a></li>
|
||||
@@ -10,7 +10,9 @@
|
||||
<li><a href="#kto-widzi-moje-zdjęcie-profilowe">Kto widzi moje zdjęcie profilowe?</a></li>
|
||||
<li><a href="#signature">Czy w Delta Chat mogę ustawić Biografię/Podpis/Status/Motto?</a></li>
|
||||
<li><a href="#co-oznacza-przypinanie-wyciszanie-i-archiwizowanie">Co oznacza przypinanie, wyciszanie i archiwizowanie?</a></li>
|
||||
<li><a href="#save">Jak działają „Zapisane wiadomości”?</a></li>
|
||||
<li><a href="#co-oznacza-zielona-kropka">Co oznacza zielona kropka?</a></li>
|
||||
<li><a href="#edit">Poprawianie literówek i usuwanie wiadomości po wysłaniu</a></li>
|
||||
<li><a href="#ephemeralmsgs">Jak działają znikające wiadomości?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -34,29 +36,25 @@
|
||||
<li><a href="#dlaczego-delta-chat-integruje-się-ze-scentralizowanymi-zastrzeżonymi-usługami-push-applegoogle">Dlaczego Delta Chat integruje się ze scentralizowanymi, zastrzeżonymi usługami push Apple/Google?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#szyfrowanie-i-bezpieczeństwo">Szyfrowanie i bezpieczeństwo</a>
|
||||
<li><a href="#e2ee">Szyfrowanie i bezpieczeństwo</a>
|
||||
<ul>
|
||||
<li><a href="#jakie-standardy-są-stosowane-do-szyfrowania-end-to-end">Jakie standardy są stosowane do szyfrowania end-to-end?</a></li>
|
||||
<li><a href="#whene2e">Skąd mogę wiedzieć, czy wiadomości są szyfrowane metodą e2e?</a></li>
|
||||
<li><a href="#howtoe2ee">Jak mogę uzyskać gwarancję kompleksowego szyfrowania i zielone znaczniki wyboru?</a></li>
|
||||
<li><a href="#e2eeguarantee">Co oznacza zielony znacznik wyboru i „gwarantowane kompleksowe szyfrowanie”?</a></li>
|
||||
<li><a href="#nocryptanymore">Kontakt „wysłał wiadomość z innego urządzenia”, co mogę zrobić?</a></li>
|
||||
<li><a href="#czy-nadal-mogę-odbierać-lub-wysyłać-wiadomości-bez-szyfrowania-end-to-end">Czy nadal mogę odbierać lub wysyłać wiadomości bez szyfrowania end-to-end?</a></li>
|
||||
<li><a href="#howtoe2ee">Jak mogę nawiązać czat z nowym kontaktem?</a></li>
|
||||
<li><a href="#e2eeguarantee">Co oznacza zielony znacznik wyboru w profilu kontaktu?</a></li>
|
||||
<li><a href="#czy-załączniki-zdjęcia-pliki-pliki-audio-itp-są-szyfrowane-metodą-end-to-end">Czy załączniki (zdjęcia, pliki, pliki audio itp.) są szyfrowane metodą end-to-end?</a></li>
|
||||
<li><a href="#openpgp-secure">Czy OpenPGP jest bezpieczny?</a></li>
|
||||
<li><a href="#openpgp-alternatives">Czy rozważałeś użycie alternatyw dla OpenPGP do szyfrowania typu end-to-end?</a></li>
|
||||
<li><a href="#czy-delta-chat-jest-podatny-na-efail">Czy Delta Chat jest podatny na EFAIL?</a></li>
|
||||
<li><a href="#tls">Czy wiadomość jest udostępniana w postaci zwykłego tekstu, jeśli szyfrowanie end-to-end nie jest dostępne?</a></li>
|
||||
<li><a href="#tls">Czy wiadomości oznaczone ikoną poczty są widoczne w internecie?</a></li>
|
||||
<li><a href="#message-metadata">W jaki sposób Delta Chat chroni metadane w wiadomościach?</a></li>
|
||||
<li><a href="#device-seizure">Jak chronić metadane i kontakty w przypadku przejęcia urządzenia?</a></li>
|
||||
<li><a href="#jak-mogę-sprawdzić-informacje-o-szyfrowaniu">Jak mogę sprawdzić informacje o szyfrowaniu?</a></li>
|
||||
<li><a href="#jak-mogę-sprawdzić-stan-szyfrowania-wiadomości">Jak mogę sprawdzić stan szyfrowania wiadomości?</a></li>
|
||||
<li><a href="#dlaczego-widzę-niezaszyfrowane-wiadomości">Dlaczego widzę niezaszyfrowane wiadomości?</a></li>
|
||||
<li><a href="#jak-mogę-uzyskać-zaszyfrowany-ened-to-end-czat-z-kontaktem-delta-chat-który-czasami-korzysta-z-poczty-internetowej-lub-innej-aplikacji-e-mail-bez-autocrypt">Jak mogę uzyskać zaszyfrowany ened-to-end czat z kontaktem Delta Chat, który czasami korzysta z poczty internetowej lub innej aplikacji e-mail bez Autocrypt?</a></li>
|
||||
<li><a href="#jak-mogę-zapewnić-kompleksowe-szyfrowanie-i-usuwanie-wiadomości">Jak mogę zapewnić kompleksowe szyfrowanie i usuwanie wiadomości?</a></li>
|
||||
<li><a href="#sealedsender">Czy Delta Chat obsługuje funkcję „Sealed Sender”?</a></li>
|
||||
<li><a href="#pfs">Czy Delta Chat obsługuje funkcję Perfect Forward Secrecy?</a></li>
|
||||
<li><a href="#czy-szyfrowanie-end-to-end-delta-chat-jest-tak-samo-bezpieczne-jak-signal">Czy szyfrowanie end-to-end Delta Chat jest tak samo bezpieczne jak Signal?</a></li>
|
||||
<li><a href="#pqc">Czy Delta Chat obsługuje kryptografię postkwantową?</a></li>
|
||||
<li><a href="#jak-mogę-ręcznie-sprawdzić-informacje-o-szyfrowaniu">Jak mogę ręcznie sprawdzić informacje o szyfrowaniu?</a></li>
|
||||
<li><a href="#importkey">Czy mogę ponownie wykorzystać mój istniejący klucz prywatny?</a></li>
|
||||
<li><a href="#nie-mogę-zaimportować-istniejącego-klucza-pgp-do-delta-chat">Nie mogę zaimportować istniejącego klucza PGP do Delta Chat.</a></li>
|
||||
<li><a href="#security-audits">Czy Delta Chat był niezależnie kontrolowany pod kątem luk w zabezpieczeniach?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -65,6 +63,7 @@
|
||||
<li><a href="#czy-mogę-korzystać-z-delta-chat-na-wielu-urządzeniach-w-tym-samym-czasie">Czy mogę korzystać z Delta Chat na wielu urządzeniach w tym samym czasie?</a></li>
|
||||
<li><a href="#rozwiązywanie-problemów">Rozwiązywanie problemów</a></li>
|
||||
<li><a href="#backup">Ręczny transfer</a></li>
|
||||
<li><a href="#czy-usuwanie-przypinanie-archiwizowanie-zapisywanie-wyciszanie-itp-jest-synchronizowane-ze-wszystkimi-urządzeniami">Czy usuwanie, przypinanie, archiwizowanie, zapisywanie, wyciszanie itp. jest synchronizowane ze wszystkimi urządzeniami?</a></li>
|
||||
<li><a href="#czy-są-jakieś-plany-wprowadzenia-klienta-web-delta-chat">Czy są jakieś plany wprowadzenia klienta Web Delta Chat?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -88,7 +87,8 @@
|
||||
<ul>
|
||||
<li><a href="#jakich-uprawnień-potrzebuje-delta-chat">Jakich uprawnień potrzebuje Delta Chat?</a></li>
|
||||
<li><a href="#czy-delta-chat-działa-z-moim-dostawcą-poczty-e-mail">Czy Delta Chat działa z <em>moim</em> dostawcą poczty e-mail?</a></li>
|
||||
<li><a href="#chcę-zarządzać-własnym-serwerem-e-mail-dla-delta-chat-co-polecacie">Chcę zarządzać własnym serwerem e-mail dla Delta Chat. Co polecacie?</a></li>
|
||||
<li><a href="#can-i-use-delta-chat-in-parallel-with-other-e-mail-apps">Can I use Delta Chat in parallel with other E-Mail apps?</a></li>
|
||||
<li><a href="#chcę-zarządzać-własnym-serwerem-dla-delta-chat-co-polecacie">Chcę zarządzać własnym serwerem dla Delta Chat. Co polecacie?</a></li>
|
||||
<li><a href="#dlaczego-muszę-wpisać-moje-hasło-e-mail-do-delta-chat-czy-to-jest-bezpieczne">Dlaczego muszę wpisać moje hasło e-mail do Delta Chat? Czy to jest bezpieczne?</a></li>
|
||||
<li><a href="#jakie-wiadomości-pojawiają-się-w-delta-chat">Jakie wiadomości pojawiają się w Delta Chat?</a></li>
|
||||
<li><a href="#czy-delta-chat-obsługuje-wiadomości-e-mail-w-formacie-html">Czy Delta Chat obsługuje wiadomości e-mail w formacie HTML?</a></li>
|
||||
@@ -96,7 +96,7 @@
|
||||
<li><a href="#do-czego-służy-ustawienie-wyślij-kopię-do-siebie">Do czego służy ustawienie „Wyślij kopię do siebie”?</a></li>
|
||||
<li><a href="#po-co-mam-wybrać-opcję-oglądania-folderu-wysłane">Po co mam wybrać opcję oglądania folderu „Wysłane”?</a></li>
|
||||
<li><a href="#dlaczego-mogę-nie-widzieć-folderu-deltachat">Dlaczego mogę nie widzieć folderu DeltaChat?</a></li>
|
||||
<li><a href="#czy-delta-chat-jest-kompatybilny-z-protonmail--tutanota--criptext">Czy Delta Chat jest kompatybilny z Protonmail / Tutanota / Criptext?</a></li>
|
||||
<li><a href="#czy-delta-chat-jest-kompatybilny-z-proton-mail--tutanota--criptext">Czy Delta Chat jest kompatybilny z Proton Mail / Tutanota / Criptext?</a></li>
|
||||
<li><a href="#remove-account">Jak mogę usunąć swoje konto?</a></li>
|
||||
<li><a href="#interesują-mnie-szczegóły-techniczne-możesz-powiedzieć-mi-coś-więcej">Interesują mnie szczegóły techniczne. Możesz powiedzieć mi coś więcej?</a></li>
|
||||
<li><a href="#gdzie-moi-znajomi-mogą-znaleźć-delta-chat">Gdzie moi znajomi mogą znaleźć Delta Chat?</a></li>
|
||||
@@ -154,7 +154,7 @@
|
||||
<p><a href="#security-audits">Audytowne szyfrowanie end-to-end</a> zabezpieczające przed atakami sieciowymi i serwerowymi.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Bezpłatne i otwartoźródłowe oprogramowanie zarówno po stronie aplikacji, jak i serwera. Stworzone w oparciu o <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md">standardy e-mail i internetowe</a>, <a href="https://xkcd.com/927/">aby uniknąć „kolejnego standardowego syndromu (xkcd 927)”</a>.</p>
|
||||
<p>Bezpłatne i otwartoźródłowe oprogramowanie zarówno po stronie aplikacji, jak i serwera. Stworzone w oparciu o <a href="https://github.com/chatmail/core/blob/main/standards.md#standards-used-in-delta-chat">standardy e-mail i internetowe</a>, <a href="https://xkcd.com/927/">aby uniknąć „kolejnego standardowego syndromu (xkcd 927)”</a>.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -166,10 +166,18 @@
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Jeśli wiadomość pochodzi od nieznanego kontaktu, wyświetla się jako <strong>prośba</strong>.</p>
|
||||
|
||||
<ul>
|
||||
<li>Jeśli wiadomość przychodzi od nieznanego kontaktu, pojawia się jako <strong>prośba</strong>. Musisz zaakceptować prośbę, zanim będziesz mógł odpowiedzieć.</li>
|
||||
<li>Możesz również „usunąć” ją, jeśli nie chcesz na razie z nią rozmawiać. To <em>nie</em> usuwa wiadomości na serwerze, tylko na Twoim urządzeniu. Możesz więc nadal zajmować się tą wiadomością w innej aplikacji pocztowej.</li>
|
||||
<li>Jeśli usuniesz prośbę, przyszłe wiadomości od tego kontaktu będą nadal wyświetlane jako prośba o wiadomość, więc możesz zmienić zdanie. Jeśli naprawdę nie chcesz otrzymywać wiadomości od tej osoby, rozważ <em>zablokowanie</em> jej.</li>
|
||||
<li>
|
||||
<p>Musisz <strong>zaakceptować</strong> prośbę, zanim będziesz mógł odpowiedzieć.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Możesz także <strong>usunąć</strong> wiadomość, jeśli nie chcesz w tej chwili z nią rozmawiać.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Jeśli usuniesz prośbę, przyszłe wiadomości od tego kontaktu nadal będą wyświetlane jako prośba o wiadomość, więc możesz zmienić zdanie. Jeśli naprawdę nie chcesz otrzymywać wiadomości od tej osoby, rozważ <strong>zablokowanie</strong> jej.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="czy-delta-chat-obsługuje-obrazy-filmy-i-inne-załączniki">
|
||||
@@ -182,7 +190,7 @@
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>Tak. Oprócz zwykłego tekstu wszystkie załączniki wiadomości e-mail są wyświetlane jako oddzielne wiadomości. Wiadomości wychodzące automatycznie otrzymują załączniki w razie potrzeby.</p>
|
||||
<p>Tak. Obrazy, filmy, pliki, wiadomości głosowe itp. są obsługiwane jak zwykle.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Ze względu na wydajność obrazy są domyślnie optymalizowane i wysyłane w mniejszym rozmiarze, ale można je wysłać jako „plik”, aby zachować oryginał.</p>
|
||||
@@ -268,6 +276,36 @@
|
||||
|
||||
<p>Aby skorzystać z tych funkcji, przytrzymaj dłużej lub kliknij prawym przyciskiem myszy czat na liście czatów.</p>
|
||||
|
||||
<h3 id="save">
|
||||
|
||||
|
||||
Jak działają „Zapisane wiadomości”? <a href="#save" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p><strong>Zapisane wiadomości</strong> to czat, którego możesz użyć, aby łatwo zapisać i znaleźć wiadomości.</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>W dowolnym czacie naciśnij i przytrzymaj lub kliknij prawym przyciskiem myszy wiadomość i wybierz <strong>Zapisz</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Zapisane wiadomości są oznaczone symbolem <img style="vertical-align:middle; width:1.2em; margin:1px" src="../saved-icon.png" alt="ikona Zapisz" /> obok znacznika czasu</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Później otwórz czat „Zapisane wiadomości” — zobaczysz tam zapisane wiadomości. Naciskając <img style="vertical-align:middle; width:1.2em; margin:1px" src="../go-to-original.png" alt="ikona strzałki w prawo" />, możesz wrócić do oryginalnej wiadomości w oryginalnym czacie</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Na koniec możesz również użyć „Zapisz wiadomości”, aby robić <strong>osobiste notatki</strong> — otwórz czat, wpisz coś, dodaj zdjęcie lub wiadomość głosową itp.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Ponieważ „Zapisane wiadomości” są zsynchronizowane, mogą być bardzo przydatne do przesyłania danych między urządzeniami</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Wiadomości pozostają zapisane, nawet jeśli zostaną edytowane lub usunięte — może to być przez <a href="#edit">nadawcę</a>, <a href="#delold">czyszczenie urządzenia</a> lub <a href="#ephemeralmsgs">znikające wiadomości z innych czatów</a>.</p>
|
||||
|
||||
<h3 id="co-oznacza-zielona-kropka">
|
||||
|
||||
|
||||
@@ -276,16 +314,40 @@
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Czasami można zobaczyć <strong>zieloną kropkę</strong> <img style="vertical-align:middle; width:1.2em; margin:1px" src="../green-dot.png" alt="" /> obok awatara kontaktu. Oznacza to, że był on „niedawno widziany” w ciągu ostatnich 10 minut:</p>
|
||||
|
||||
<ul>
|
||||
<li>Od wersji Delta Chat 1.34 czasami można zobaczyć „zieloną kropkę” obok awatara kontaktu. Oznacza to, że był on „niedawno widziany”.</li>
|
||||
<li>Dokładnie oznacza to, że w ciągu ostatnich 10 minut Delta Chat widział go:</li>
|
||||
<li>dlatego, że wysłał Ci bezpośrednio wiadomość,</li>
|
||||
<li>ponieważ napisał coś do grupy, której oboje jesteście członkami,</li>
|
||||
<li>ponieważ wysłał Ci potwierdzenie przeczytania napisanej przez Ciebie wiadomości,</li>
|
||||
<li>lub dlatego, że wysłał dane do Twojej aplikacji Delta Chat za pomocą <a href="#webxdc">Webxdc aplikacji</a>.</li>
|
||||
<li>Nie jest to więc status online w czasie rzeczywistym – a jeśli ktoś nie odpowie od razu, mimo że wydaje się, że jest online, nie martw się i daj mu trochę miejsca ;-)</li>
|
||||
<li>Z drugiej strony inni nie zawsze „zobaczą, że jesteś online”. Jeśli wyłączyłeś potwierdzenia odczytu, nie zobaczą oni zielonej kropki, dopóki nie wyślesz do nich wiadomości lub nie napiszesz do grupy, w której również się znajdują.</li>
|
||||
<li>ponieważ edytował lub usunął wiadomość w udostępnionym Tobie czacie,</li>
|
||||
<li>lub dlatego, że używał <a href="#webxdc">aplikacji</a> w udostępnionym Tobie czacie.</li>
|
||||
</ul>
|
||||
|
||||
<p>Nie jest to więc status online w czasie rzeczywistym – a jeśli ktoś nie odpowie od razu, mimo że wydaje się, że jest online, nie martw się i daj mu trochę czasu :)</p>
|
||||
|
||||
<p>Z drugiej strony, inni nie zawsze „zauważą, że jesteś online”. Jeśli wyłączyłeś potwierdzenia odczytu, nie zobaczą zielonej kropki, dopóki nie zostanie spełniony jeden z powyższych warunków.</p>
|
||||
|
||||
<h3 id="edit">
|
||||
|
||||
|
||||
Poprawianie literówek i usuwanie wiadomości po wysłaniu <a href="#edit" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>Możesz edytować tekst wiadomości po wysłaniu. W tym celu naciśnij i przytrzymaj lub kliknij prawym przyciskiem myszy wiadomość i wybierz Edytuj, lub<img style="vertical-align:middle; width:1.2em; margin:1px" src="../edit-icon.png" alt="ikona Edytuj" />.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Jeśli wysłałeś wiadomość przypadkowo, z tego samego menu wybierz <strong>Usuń</strong>, a następnie <strong>Usuń u wszystkich</strong>.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Podczas gdy edytowane wiadomości będą miały słowo „Edytowana” obok znacznika czasu, usunięte wiadomości zostaną usunięte bez znacznika na czacie. Powiadomienia nie są wysyłane i nie ma limitu czasowego.</p>
|
||||
|
||||
<p>Pamiętaj, że oryginalną wiadomość nadal mogą otrzymać członkowie czatu, którzy mogli już odpowiedzieć, przesłać dalej, zapisać, wykonać zrzut ekranu lub w inny sposób skopiować wiadomość.</p>
|
||||
|
||||
<h3 id="ephemeralmsgs">
|
||||
|
||||
@@ -295,7 +357,11 @@
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Możesz włączyć „znikające wiadomości” w ustawieniach czatu, w prawym górnym rogu okna czatu, wybierając przedział czasu od 1 minuty do 5 tygodni.</p>
|
||||
<p>You can turn on “disappearing messages”
|
||||
in the settings of a chat,
|
||||
at the top right of the chat window,
|
||||
by selecting a time span
|
||||
between 5 minutes and 1 year.</p>
|
||||
|
||||
<p>Dopóki ustawienie nie zostanie ponownie wyłączone, aplikacja Delta Chat u każdego członka czatu zajmie się usuwaniem wiadomości po wybranym okresie. Przedział czasu rozpoczyna się w momencie, gdy odbiorca po raz pierwszy zobaczy wiadomość w Delta Chat. Wiadomości są usuwane zarówno na każdym koncie e-mail na serwerze, jak i w samej aplikacji.</p>
|
||||
|
||||
@@ -525,10 +591,10 @@ nadal będziesz mógł pisać, ale nie będziesz już powiadamiany o żadnych no
|
||||
|
||||
<p>Pamiętaj, że Delta Chat posiada <a href="#privacy-notifications">mały i chroniący prywatność system powiadomień push</a>, który zapewnia „natychmiastowe dostarczanie” wiadomości ze wszystkich serwerów chatmail, w tym potencjalnego, który <a href="https://delta.chat/chatmail#selfhosted">możesz skonfigurować samodzielnie bez naszej zgody</a>. Witamy w potędze interoperacyjnego i ogromnego systemu chatmail i e-mail :)</p>
|
||||
|
||||
<h2 id="szyfrowanie-i-bezpieczeństwo">
|
||||
<h2 id="e2ee">
|
||||
|
||||
|
||||
Szyfrowanie i bezpieczeństwo <a href="#szyfrowanie-i-bezpieczeństwo" class="anchor"></a>
|
||||
Szyfrowanie i bezpieczeństwo <a href="#e2ee" class="anchor"></a>
|
||||
|
||||
|
||||
</h2>
|
||||
@@ -541,9 +607,21 @@ nadal będziesz mógł pisać, ale nie będziesz już powiadamiany o żadnych no
|
||||
|
||||
</h3>
|
||||
|
||||
<p><a href="https://autocrypt.org">Autocrypt</a> służy do automatycznego ustanawiania szyfrowania end-to-end kontaktów i czatów grupowych. Autocrypt wykorzystuje ograniczony i <a href="#openpgp-secure">bezpieczny podzbiór standardu OpenPGP</a>. W pełni zaszyfrowane wiadomości są oznaczone kłódką <img style="vertical-align:middle; width:1.2em; margin:1px" src="../lock-icon.png" alt="padlock" />.</p>
|
||||
<p>Delta Chat wykorzystuje <a href="#openpgp-secure">bezpieczny podzbiór standardu OpenPGP</a> do automatycznego szyfrowania typu end-to-end za pomocą następujących protokołów:</p>
|
||||
|
||||
<p><a href="https://securejoin.delta.chat/en/latest/new.html">Protokoły Secure-Join</a> służą do nawiązywania rozmów z gwarantowanym szyfrowaniem typu end-to-end, które chroni przed atakami sieciowymi i zagrożonymi serwerami. Czaty oznaczone zielonym znacznikiem wyboru <img style="vertical-align:middle; width:1.5em; margin:1px" src="../green-checkmark.png" alt="green checkmark" /> gwarantują szyfrowanie end-to-end wiadomości.</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="https://securejoin.delta.chat/en/latest/new.html">Secure-Join</a> do wymiany informacji o konfiguracji szyfrowania poprzez skanowanie kodów QR lub „linki zaproszeń”.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="https://autocrypt.org">Autocrypt</a> służy do automatycznego ustanawiania szyfrowania typu end-to-end między kontaktami a wszystkimi członkami czatu grupowego.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">Udostępnienie kontaktu na czacie</a> umożliwia odbiorcom korzystanie z szyfrowania typu end-to-end z tym kontaktem.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Delta Chat nie wysyła zapytań, nie publikuje ani nie wchodzi w interakcję z żadnymi serwerami kluczy OpenPGP.</p>
|
||||
|
||||
<h3 id="whene2e">
|
||||
|
||||
@@ -553,100 +631,69 @@ nadal będziesz mógł pisać, ale nie będziesz już powiadamiany o żadnych no
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Wszystkie całkowicie zaszyfrowane wiadomości są oznaczone kłódką:</p>
|
||||
|
||||
<p><img style="width:160px; margin:1px" src="../lock-screenshot.png" alt="padlock in bubble" /></p>
|
||||
|
||||
<p>Pełne szyfrowanie jest gwarantowane, jeśli obok tytułu czatu znajduje się zielony znacznik wyboru:</p>
|
||||
|
||||
<p><img style="width:211px; margin:1px" src="../green-checkmark-screenshot.png" alt="green checkmark in title" /></p>
|
||||
<p>Wszystkie wiadomości w Delta Chat są <strong>domyślnie szyfrowane metodą end-to-end</strong>. Od wydania Delta Chat w wersji 2 (lipiec 2025 r.) nie ma już blokad ani podobnych znaczników na wiadomościach szyfrowanych metodą end-to-end.</p>
|
||||
|
||||
<h3 id="howtoe2ee">
|
||||
<h3 id="czy-nadal-mogę-odbierać-lub-wysyłać-wiadomości-bez-szyfrowania-end-to-end">
|
||||
|
||||
|
||||
Jak mogę uzyskać gwarancję kompleksowego szyfrowania i zielone znaczniki wyboru? <a href="#howtoe2ee" class="anchor"></a>
|
||||
Czy nadal mogę odbierać lub wysyłać wiadomości bez szyfrowania end-to-end? <a href="#czy-nadal-mogę-odbierać-lub-wysyłać-wiadomości-bez-szyfrowania-end-to-end" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Jeśli masz drugi kanał komunikacji ze swoim partnerem na czacie, np. czat wideo lub inny komunikator, możesz wygenerować link zaproszenia.</p>
|
||||
<p>Jeśli korzystasz z domyślnych <a href="https://chatmail.at/relays">przekaźników chatmail</a>, odbieranie ani wysyłanie wiadomości bez szyfrowania end-to-end jest niemożliwe.</p>
|
||||
|
||||
<p>Jeśli jesteście razem osobiście, możesz pokazać kod QR swojemu rozmówcy.</p>
|
||||
<p>If you instead create a profile using a classic e-mail server,
|
||||
you can send and receive messages with or without end-to-end encryption.
|
||||
Messages lacking end-to-end encryption are marked with an e-mail icon
|
||||
<img style="vertical-align:middle; width:1.2em; margin:1px" src="../email-icon.png" alt="email" />.</p>
|
||||
|
||||
<h3 id="howtoe2ee">
|
||||
|
||||
|
||||
Jak mogę nawiązać czat z nowym kontaktem? <a href="#howtoe2ee" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Możesz wysłać link z zaproszeniem za pośrednictwem innego prywatnego czatu, wyświetlić kod QR zaproszenia, gdy kontakty znajdują się obok siebie lub w rozmowie wideo, albo dotknąć „kontakt”, który został udostępniony na czacie.</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>W przypadku <strong>zaproszeń grupowych</strong> dotknij tytułu czatu grupy, aby wyświetlić listę jej członków, i wybierz „Kod QR zaproszenia”.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Aby uzyskać <strong>bezpośrednie zaproszenia na czat 1:1</strong>, dotknij ikony kodu QR <img style="vertical-align:middle; width:1.8em; margin:1px" src="../qr-icon.png" /> na ekranie głównym aplikacji Delta Chat.</p>
|
||||
<p>Aby <strong>wysłać zaproszenie do czatu 1:1</strong>, dotknij ikony kodu QR <img style="vertical-align:middle; width:1.8em; margin:1px" src="../qr-icon.png" /> na ekranie głównym aplikacji Delta Chat.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Pozwól swojemu partnerowi czatu zeskanować obraz QR za pomocą aplikacji Delta Chat lub dotknij „Kopiuj” lub „Udostępnij”, aby utworzyć link zaproszenia i udostępnić go swojemu partnerowi czatu.</p>
|
||||
<p>Poproś rozmówcę o zeskanowanie obrazu QR za pomocą aplikacji Delta Chat lub dotknij „Kopiuj” lub „Udostępnij”, aby utworzyć link zaproszenia i udostępnić go rozmówcy.</p>
|
||||
|
||||
<p>Teraz poczekaj, aż między obydwoma urządzeniami <a href="https://securejoin.delta.chat/en/latest/new.html#setup-contact-protocol">nastąpi wymiana wiadomości sieciowych Secure-Join</a>.</p>
|
||||
<p>Poczekaj, aż <a href="https://securejoin.delta.chat/en/latest/new.html#setup-contact-protocol">szyfrowanie typu end-to-end zostanie nawiązane</a>.</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>Jeśli oba urządzenia są w trybie online, obie strony ostatecznie zobaczą czat (grupowy lub bezpośredni) z zielonym znacznikiem wyboru <img style="vertical-align:middle; width:1.5em; margin:1px" src="../green-checkmark.png" alt="green checkmark" /> obok tytułu.</p>
|
||||
<p>Jeśli obie strony są online, wkrótce zobaczą czat (grupowy lub bezpośredni) i będą mogły bezpiecznie wysyłać wiadomości.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Jeśli jedno z urządzeń jest w trybie offline, zielone znaczniki wyboru pojawią się później, dopiero gdy urządzenie zostanie ponownie połączone z Internetem i zostanie zrealizowany protokół sieciowy Secure-Join.</p>
|
||||
<p>Jeśli jedna ze stron jest offline lub ma słaby zasięg, możliwość czatowania zostanie wstrzymana do czasu przywrócenia połączenia.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Gratulacje! Teraz będziecie automatycznie korzystać z gwarantowanego kompleksowego szyfrowania tego kontaktu i oboje będziecie mogli dodawać się nawzajem do grup oznaczonych zielonym znacznikiem wyboru <img style="vertical-align:middle; width:1.5em; margin:1px" src="../green-checkmark.png" alt="green checkmark" /> , w ten sposób automatycznie rozpowszechniając gwarantowane kompleksowe szyfrowanie wśród swoich członków.</p>
|
||||
<p>Gratulacje! Teraz będziesz automatycznie korzystać z szyfrowania typu end-to-end dla tego kontaktu. Jeśli dodacie się nawzajem do czatów grupowych, szyfrowanie typu end-to-end zostanie nawiązane między wszystkimi członkami.</p>
|
||||
|
||||
<h3 id="e2eeguarantee">
|
||||
|
||||
|
||||
Co oznacza zielony znacznik wyboru i „gwarantowane kompleksowe szyfrowanie”? <a href="#e2eeguarantee" class="anchor"></a>
|
||||
Co oznacza zielony znacznik wyboru w profilu kontaktu? <a href="#e2eeguarantee" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Tytuły czatów z zielonymi znacznikami wyboru <img style="vertical-align:middle; width:1.5em; margin:1px" src="../green-checkmark.png" alt="green checkmark" /> oznaczają, że wszystkie wiadomości na czacie będą w pełni szyfrowane i nie będą mogły zostać odczytane ani zmodyfikowane przez zhakowane serwery poczty e-mail lub dostawców Internetu. Dołączanie do czatów grupowych oznaczonych zielonym znacznikiem bezpiecznie rozpowszechnia informacje o szyfrowaniu wszystkich osób (i zielone znaczniki wyboru) w sposób gwarantujący kompleksowe szyfrowanie w grupie i pomiędzy członkami.</p>
|
||||
|
||||
<p>Profile kontaktów z zielonymi znacznikami wyboru <img style="vertical-align:middle; width:1.5em; margin:1px" src="../green-checkmark.png" alt="green checkmark" /> oznaczają, że obecnie gwarantujemy, że wiadomości kontaktu będą szyfrowane metodą end-to-end. Każdy kontakt oznaczony zielonym znacznikiem albo wykonał z tobą bezpośrednie <a href="#howtoe2ee">skanowanie QR</a>, albo został zweryfikowany przez inny kontakt oznaczony zielonym znacznikiem. Weryfikacje odbywają się automatycznie podczas dodawania członków do grup. Ktokolwiek doda kontakt do grupy oznaczonej zielonym znacznikiem, staje się osobą weryfikującą dla tych członków, którzy jeszcze nie wiedzieli o dodanym kontakcie. W profilu kontaktu możesz wielokrotnie dotykać tekstu „Zweryfikowano przez…”, aż dojdziesz do osoby, z którą bezpośrednio wykonałeś <a href="#howtoe2ee">skanowanie QR</a>.</p>
|
||||
|
||||
<p>Pamiętaj, że w profilu kontaktu możesz widzieć i klikać osoby weryfikujące, ale w tytule profilu nie ma zielonego znacznika wyboru. Zwykle oznacza to, że kontakt <a href="#nocryptanymore">„wysłał wiadomość z innego urządzenia”</a>.</p>
|
||||
<p>Profil kontaktu może wyświetlać zielony znacznik wyboru <img style="vertical-align:middle; width:1.5em; margin:1px" src="../green-checkmark.png" alt="green checkmark" /> i wiersz „Zweryfikowano przez…”. Każdy kontakt oznaczony zielonym znacznikiem albo wykonał z tobą bezpośrednie <a href="#howtoe2ee">skanowanie QR</a>, albo został zweryfikowany przez inny kontakt oznaczony zielonym znacznikiem. Weryfikacje odbywają się automatycznie podczas dodawania członków do grup. Osoba, która doda kontakt oznaczony zielonym znacznikiem wyboru do grupy zawierającej wyłącznie członków oznaczonych zielonym znacznikiem wyboru, staje się osobą weryfikującą. W profilu kontaktu możesz wielokrotnie dotykać tekstu „Zweryfikowano przez…”, aż dojdziesz do osoby, z którą bezpośrednio wykonałeś <a href="#howtoe2ee">skanowanie QR</a>.</p>
|
||||
|
||||
<p>Aby uzyskać bardziej szczegółowe omówienie „gwarantowanego szyfrowania typu end-to-end”, zobacz <a href="https://securejoin.delta.chat/en/latest/new.html">Protokoły Secure-Join</a>, a w szczególności przeczytaj o „zweryfikowanych grupach”, technicznym określeniu tak zwanego tutaj „zielonego znacznika wyboru” lub „gwarantowanego szyfrowania end-to-end” czatów.</p>
|
||||
|
||||
<h3 id="nocryptanymore">
|
||||
|
||||
|
||||
Kontakt „wysłał wiadomość z innego urządzenia”, co mogę zrobić? <a href="#nocryptanymore" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Twój czat z kontaktem utracił gwarantowane szyfrowanie typu end-to-end. Gdy zobaczysz to ostrzeżenie, zielony znacznik wyboru dla tego czatu i kontaktu został usunięty. <strong>Jeśli zdziwi cię nagły spadek gwarantowanego szyfrowania end-to-end tego kontaktu, nie akceptuj ostrzeżenia!</strong> Zamiast tego skontaktuj się ze swoim kontaktem za pośrednictwem drugiego kanału, takiego jak rozmowa wideo, inny komunikator lub rozmowa telefoniczna, aby dowiedzieć się, co się stało.</p>
|
||||
|
||||
<p>Jeśli twój kontakt rzeczywiście spowodował spadek gwarantowanego szyfrowania typu end-to-end, zapoznaj się z kolejnymi akapitami, aby poznać typowe przyczyny i sposoby ich łagodzenia. Niezależnie od tego, wszystkie inne czaty oznaczone zielonym znacznikiem są w pełni szyfrowane, nawet jeśli kontakt jest ich członkiem.</p>
|
||||
|
||||
<p><strong>Twój kontakt korzysta z Delta Chat na drugim urządzeniu (telefonie lub laptopie)</strong></p>
|
||||
|
||||
<p>Jeśli ma inne urządzenie z uruchomioną aplikacją Delta Chat, powinien usunąć konto z nowego urządzenia i dodać je <a href="#multiclient">jako drugie urządzenie zgodnie z opisem tutaj</a>. Gdy tylko skontaktuje się z tobą później, ostrzeżenie zniknie, a na obu urządzeniach twojego kontaktu zostanie ustanowione gwarantowane szyfrowanie.</p>
|
||||
|
||||
<p><strong>Twój kontakt ponownie zainstalował Delta Chat, używając swojego starego loginu do konta</strong></p>
|
||||
|
||||
<p>Jeśli ma <a href="#backup">plik kopii zapasowej</a>, powinien usunąć konto z nowego urządzenia i zamiast tego zaimportować plik kopii zapasowej, aby ponownie utworzyć konto. Gdy tylko skontaktuje się z tobą później, ostrzeżenie zniknie, a dla tego kontaktu zostanie przywrócone gwarantowane szyfrowanie.</p>
|
||||
|
||||
<p>Jeśli nie ma pliku kopii zapasowej, najlepiej wykonać <a href="#howtoe2ee">skanowanie QR</a> ze swoim partnerem czatu, aby przywrócić gwarantowane szyfrowanie end-to-end.</p>
|
||||
|
||||
<p><strong>Twój kontakt wysłał wiadomość e-mail za pośrednictwem interfejsu poczty internetowej lub innej aplikacji e-mail i wkrótce ponownie zacznie korzystać z Delta Chat.</strong></p>
|
||||
|
||||
<p>Jeśli masz pewność, że kontakt czasami korzysta z poczty internetowej lub innej aplikacji pocztowej, która nie zapewnia szyfrowania end-to-end, możesz zaakceptować ostrzeżenie. Gdy tylko twój kontakt ponownie skorzysta z Delta Chat, gwarantowane szyfrowanie end-to-end zostanie automatycznie przywrócone.</p>
|
||||
|
||||
<p><strong>Twój kontakt całkowicie przestał korzystać z Delta Chat</strong></p>
|
||||
|
||||
<p>Czasami utrzymanie kontaktu jest ważniejsze niż szyfrowanie end-to-end. <a href="#tls">„Szyfrowanie warstwy transportowej” (TLS)</a> może w dalszym ciągu w znaczący sposób chronić poufność wiadomości przesyłanych między urządzeniem a serwerem poczty e-mail. Jednak bez kompleksowego szyfrowania Ty i Twój kontakt ufacie swojemu serwerowi poczty e-mail, że nie będzie czytać ani manipulować waszymi wiadomościami i nie będzie przekazywać ich osobom trzecim.</p>
|
||||
|
||||
<p>W każdym razie nie możesz zrobić nic innego, jak zaakceptować ostrzeżenie. Usuń także kontakt z aktywnej grupy oznaczonej zielonym znacznikiem, którą znajdziesz w sekcji „Współdzielone czaty” w profilu kontaktu. Dzięki temu twój kontakt nie otrzyma „nieczytelnych” wiadomości.</p>
|
||||
|
||||
<p>Jeśli kontakt usunął Delta Chat z powodu błędów lub niepożądanego zachowania, rozważ opublikowanie postu na naszym <a href="https://support.delta.chat">forum pomocy</a>, aby pomóc nam zidentyfikować i rozwiązać typowe problemy. Dzięki!</p>
|
||||
|
||||
<h3 id="czy-załączniki-zdjęcia-pliki-pliki-audio-itp-są-szyfrowane-metodą-end-to-end">
|
||||
|
||||
|
||||
@@ -667,9 +714,9 @@ nadal będziesz mógł pisać, ale nie będziesz już powiadamiany o żadnych no
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Tak, Delta Chat korzysta z bezpiecznego podzbioru OpenPGP i wyświetla wskaźnik bezpieczeństwa kłódki na wiadomości tylko wtedy, gdy cała wiadomość jest prawidłowo zaszyfrowana i podpisana. Na przykład „Odłączone podpisy” nie są traktowane jako bezpieczne.</p>
|
||||
<p>Tak, Delta Chat korzysta z bezpiecznego podzbioru OpenPGP, który wymaga prawidłowego zaszyfrowania i podpisania całej wiadomości. Na przykład „Odłączone podpisy” nie są traktowane jako bezpieczne.</p>
|
||||
|
||||
<p>OpenPGP samo w sobie nie jest niebezpieczne. Większość publicznie omawianych problemów związanych z bezpieczeństwem OpenPGP tak naprawdę wynika ze złej użyteczności lub złej implementacji narzędzi, lub aplikacji (lub obu). Szczególnie ważne jest rozróżnienie pomiędzy OpenPGP, standardem szyfrowania IETF, a GnuPG (GPG), narzędziem wiersza poleceń implementującym OpenPGP. Wiele publicznych komentarzy krytycznych na temat OpenPGP tak naprawdę omawia GnuPG, którego Delta Chat nigdy nie używał. Delta Chat korzysta raczej z implementacji OpenPGP Rust <a href="https://github.com/rpgp/rpgp">rPGP</a>, dostępnej jako <a href="https://crates.io/crates/pgp">niezależny pakiet „pgp”</a> i poddanej <a href="https://delta.chat/assets/blog/2019-first-security-review.pdf">audytowi bezpieczeństwa w 2019 roku</a>.</p>
|
||||
<p>OpenPGP samo w sobie nie jest niebezpieczne. Większość publicznie omawianych problemów związanych z bezpieczeństwem OpenPGP tak naprawdę wynika ze złej użyteczności lub złej implementacji narzędzi, lub aplikacji (lub obu). Szczególnie ważne jest rozróżnienie pomiędzy OpenPGP, standardem szyfrowania IETF, a GnuPG (GPG), narzędziem wiersza poleceń implementującym OpenPGP. Wiele publicznych komentarzy krytycznych na temat OpenPGP tak naprawdę omawia GnuPG, którego Delta Chat nigdy nie używał. Delta Chat korzysta raczej z implementacji OpenPGP Rust <a href="https://github.com/rpgp/rpgp">rPGP</a>, dostępnej jako <a href="https://crates.io/crates/pgp">niezależny pakiet „pgp”</a> i poddanej <a href="#security-audits">audytowi bezpieczeństwa w 2019 i 2024 roku</a>.</p>
|
||||
|
||||
<p>Naszym celem, wraz z innymi wdrażającymi OpenPGP, jest dalsza poprawa parametrów bezpieczeństwa poprzez wdrożenie <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/">nowego IETF OpenPGP Crypto-Refresh</a>, który na szczęście został przyjęty latem 2023 roku.</p>
|
||||
|
||||
@@ -700,16 +747,14 @@ nadal będziesz mógł pisać, ale nie będziesz już powiadamiany o żadnych no
|
||||
<h3 id="tls">
|
||||
|
||||
|
||||
Czy wiadomość jest udostępniana w postaci zwykłego tekstu, jeśli szyfrowanie end-to-end nie jest dostępne? <a href="#tls" class="anchor"></a>
|
||||
Czy wiadomości oznaczone ikoną poczty są widoczne w internecie? <a href="#tls" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Nawet jeśli nie ma gwarancji, że twoje wiadomości będą zaszyfrowane metodą end-to-end, nadal są chronione przed dostawcami Internetu, takimi jak operatorzy komórkowi lub telewizji kablowej. Jednakże dostawcy poczty e-mail zarówno twoi, jak i twojego odbiorcy mogą czytać, analizować, a nawet modyfikować twoje wiadomości, w tym wszelkie załączniki, jeśli nie są one w pełni zaszyfrowane.</p>
|
||||
<p>Jeśli wysyłasz lub odbierasz wiadomości e-mail bez szyfrowania end-to-end (korzystając z klasycznego serwera poczty), są one nadal chronione przed operatorami komórkowymi lub kablowymi, którzy nie mogą ich czytać ani modyfikować. Jednak zarówno twoi dostawcy poczty e-mail, jak i dostawcy poczty e-mail odbiorcy mogą czytać, analizować lub modyfikować twoje wiadomości, w tym wszelkie załączniki.</p>
|
||||
|
||||
<p>Delta Chat domyślnie korzysta ze ścisłego <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">szyfrowania TLS</a>, które zabezpiecza połączenia pomiędzy twoim urządzeniem a dostawcą poczty e-mail. Cała obsługa TLS Delta Chat została poddana niezależnemu <a href="#security-audits">audytowi bezpieczeństwa</a>. Co więcej, połączenie między tobą a dostawcą poczty e-mail odbiorcy będzie zazwyczaj również szyfrowane. Jeśli zaangażowane serwery poczty e-mail obsługują <a href="https://datatracker.ietf.org/doc/html/rfc8461">MTA-STS</a>, pomiędzy dostawcami poczty e-mail będzie egzekwowane szyfrowanie transportu. W takim przypadku komunikacja Delta Chat nigdy nie będzie udostępniana w Internecie w postaci zwykłego tekstu, nawet jeśli wiadomość nie została zaszyfrowana metodą end-to-end.</p>
|
||||
|
||||
<p>Pamiętaj, że <a href="#howtoe2ee">utrzymywanie gwarantowanego szyfrowania typu end-to-end</a> oprócz szyfrowania TLS zapewnia wszechstronne bezpieczeństwo między urządzeniami twoimi i odbiorców. Nawet twój dostawca poczty e-mail lub Internetu nie będzie w stanie przeczytać ani zmodyfikować twoich wiadomości.</p>
|
||||
|
||||
<h3 id="message-metadata">
|
||||
|
||||
@@ -719,20 +764,23 @@ nadal będziesz mógł pisać, ale nie będziesz już powiadamiany o żadnych no
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat chroni większość metadanych wiadomości, umieszczając następujące informacje w całkowicie zaszyfrowanej części wiadomości:</p>
|
||||
<p>W przeciwieństwie do większości innych komunikatorów, aplikacje Delta Chat nie przechowują żadnych metadanych dotyczących kontaktów ani grup na serwerach, również w formie zaszyfrowanej. Zamiast tego wszystkie metadane grup są szyfrowane metodą end-to-end i przechowywane wyłącznie na urządzeniach użytkowników końcowych.</p>
|
||||
|
||||
<p>Serwery poczty e-mail widzą zatem tylko:</p>
|
||||
|
||||
<ul>
|
||||
<li>Wiersz tematu</li>
|
||||
<li>Awatar i nazwa grupy</li>
|
||||
<li>Żądania MDN (potwierdzenie odczytu) (<code class="language-plaintext highlighter-rouge">Chat-Disposition-Notification-To</code>)</li>
|
||||
<li>Licznik czasu znikania wiadomości (<code class="language-plaintext highlighter-rouge">Ephemeral-Timer</code>)</li>
|
||||
<li><code class="language-plaintext highlighter-rouge">Chat-Group-Member-Removed</code>, <code class="language-plaintext highlighter-rouge">Chat-Group-Member-Added</code></li>
|
||||
<li>Nagłówek <code class="language-plaintext highlighter-rouge">Secure-Join</code> zawierający polecenia bezpiecznego łączenia</li>
|
||||
<li>Powiadomienie o włączeniu przesyłania strumieniowego lokalizacji</li>
|
||||
<li>Adres URL pokoju WebRTC</li>
|
||||
<li>
|
||||
<p>datę wysłania wiadomości,</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>adresy nadawcy i odbiorcy,</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>oraz rozmiar wiadomości.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Serwery poczty e-mail nie mają dostępu do chronionych metadanych, ale widzą datę wiadomości, jej rozmiar oraz, co ważniejsze, adresy nadawcy i odbiorcy. Serwery poczty e-mail potrzebują adresów odbiorców, aby kierować i dostarczać wiadomości do urządzeń odbiorców.</p>
|
||||
<p>Wszystkie pozostałe metadane dotyczące wiadomości, kontaktów i grup znajdują się w zaszyfrowanej metodą end-to-end części wiadomości.</p>
|
||||
|
||||
<h3 id="device-seizure">
|
||||
|
||||
@@ -742,61 +790,21 @@ nadal będziesz mógł pisać, ale nie będziesz już powiadamiany o żadnych no
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Zarówno w celu ochrony przed serwerami poczty e-mail gromadzącymi metadane, jak i przed zagrożeniem przejęcia urządzenia, zalecamy użycie zoptymalizowanej <a href="https://delta.chat/pl/serverguide">instancji serwera poczty e-mail</a> Delta Chat w celu tworzenia pseudonimowych kont tymczasowych poprzez skanowanie kodów QR. Pamiętaj, że aplikacje Delta Chat na wszystkich platformach obsługują wiele kont, dzięki czemu możesz z łatwością korzystać z kont „1-tygodniowych” lub „1-miesięcznych” związanych z konkretnymi działaniami obok konta „głównego”, wiedząc, że wszystkie tymczasowe dane konta wraz ze wszystkimi metadanymi zostaną usunięte. Co więcej, jeśli urządzenie zostanie przejęte, nie będzie można łatwo zidentyfikować kontaktów korzystających z tymczasowych kont e-mail, w porównaniu z komunikatorami, które ujawniają numery telefonów w grupach czatów, które z kolei często są powiązane z tożsamością prawną.</p>
|
||||
<p>Zarówno w celu ochrony przed serwerami poczty e-mail gromadzącymi metadane, jak i przed zagrożeniem przejęcia urządzenia, zalecamy użycie <a href="https://chatmail.at/relays">przełącznika chatmail</a> do tworzenia profili czatu z losowymi adresami e-mail do przesyłania. Pamiętaj, że aplikacje Delta Chat na wszystkich platformach obsługują wiele kont, dzięki czemu możesz z łatwością korzystać z kont „1-tygodniowych” lub „1-miesięcznych” związanych z konkretnymi działaniami obok konta „głównego”, wiedząc, że wszystkie tymczasowe dane konta wraz ze wszystkimi metadanymi zostaną usunięte. Co więcej, jeśli urządzenie zostanie przejęte, kontakty czatu korzystające z profili krótkotrwałych nie będą mogły zostać łatwo zidentyfikowane.</p>
|
||||
|
||||
<h3 id="jak-mogę-sprawdzić-informacje-o-szyfrowaniu">
|
||||
<h3 id="sealedsender">
|
||||
|
||||
|
||||
Jak mogę sprawdzić informacje o szyfrowaniu? <a href="#jak-mogę-sprawdzić-informacje-o-szyfrowaniu" class="anchor"></a>
|
||||
Czy Delta Chat obsługuje funkcję „Sealed Sender”? <a href="#sealedsender" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Możesz sprawdzić stan szyfrowania end-to-end ręcznie w oknie dialogowym „Szyfrowanie” (profil użytkownika w systemie Android/iOS lub kliknij prawym przyciskiem myszy element listy czatu użytkownika na komputerze). Delta Chat pokazuje tam dwa odciski palców. Jeśli te same odciski palców pojawią się u ciebie i urządzeniu twojego kontaktu, połączenie jest bezpieczne.</p>
|
||||
|
||||
<h3 id="jak-mogę-sprawdzić-stan-szyfrowania-wiadomości">
|
||||
|
||||
|
||||
Jak mogę sprawdzić stan szyfrowania wiadomości? <a href="#jak-mogę-sprawdzić-stan-szyfrowania-wiadomości" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
<p>Nie, jeszcze nie.</p>
|
||||
|
||||
<p>Mała <strong>kłódka</strong> w dymku wiadomości oznacza, że wiadomość od danego nadawcy została prawidłowo zaszyfrowana. Jeśli <strong>nie ma kłódki</strong>, wiadomość nie została prawidłowo zaszyfrowana metodą end-to-end, najprawdopodobniej dlatego, że nadawca korzysta z aplikacji lub interfejsu poczty internetowej bez obsługi szyfrowania typu end-to-end.</p>
|
||||
|
||||
<h3 id="dlaczego-widzę-niezaszyfrowane-wiadomości">
|
||||
|
||||
|
||||
Dlaczego widzę niezaszyfrowane wiadomości? <a href="#dlaczego-widzę-niezaszyfrowane-wiadomości" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
<p>Komunikator Signal wprowadził funkcję <a href="https://signal.org/blog/sealed-sender/">„Sealed Sender”</a> w 2018 roku, aby infrastruktura serwerowa nie wiedziała, kto wysyła wiadomość do grupy odbiorców. Jest to szczególnie ważne, ponieważ serwer Signal zna numer telefonu komórkowego każdego konta, który zazwyczaj jest powiązany z identyfikatorem paszportu.</p>
|
||||
|
||||
<p>Jeśli kontakt korzysta z aplikacji e-mail bez Autocrypt, wszystkie wiadomości od tego kontaktu (w czacie grupowym lub 1:1) nie będą w pełni szyfrowane, w związku z czym nie będą wyświetlane z „kłódką” przy wiadomościach. Pamiętaj, że nawet jeśli twoje kontakty korzystają z Delta Chat na swoim koncie, mogą również używać na tym koncie aplikacji e-mail bez Autocrypt, co może powodować sporadyczne wysyłanie niezaszyfrowanych wiadomości. Odpowiadanie niezaszyfrowanych wiadomości na niezaszyfrowane wiadomości jest wymagane przez funkcję Autocrypt, aby zapobiec nieczytelnym wiadomościom po stronie twoich kontaktów i ich aplikacji e-mail bez Autocrypt.</p>
|
||||
|
||||
<h3 id="jak-mogę-uzyskać-zaszyfrowany-ened-to-end-czat-z-kontaktem-delta-chat-który-czasami-korzysta-z-poczty-internetowej-lub-innej-aplikacji-e-mail-bez-autocrypt">
|
||||
|
||||
|
||||
Jak mogę uzyskać zaszyfrowany ened-to-end czat z kontaktem Delta Chat, który czasami korzysta z poczty internetowej lub innej aplikacji e-mail bez Autocrypt? <a href="#jak-mogę-uzyskać-zaszyfrowany-ened-to-end-czat-z-kontaktem-delta-chat-który-czasami-korzysta-z-poczty-internetowej-lub-innej-aplikacji-e-mail-bez-autocrypt" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Jeśli potrzebujesz bezpiecznie, kompleksowo zaszyfrowanego czatu z kontaktem, który korzysta ze swojego konta zarówno w Delta Chat, jak i w aplikacjach bez Autocrypt (np. poczta internetowa), najlepiej skonfigurować z nim gwarantowane szyfrowanie typu end-to-end, a następnie utworzyć <a href="#howtoe2ee">gwarantowany zaszyfrowany end-to-end</a> czat grupowy z wami dwoma jako członkami. W tym czacie grupowym wszystkie wiadomości będą w pełni szyfrowane, nawet jeśli bezpośredni czat między wami zawiera ostrzeżenie <a href="#nocryptanymore">„…wysłano wiadomość z innego urządzenia”</a>.</p>
|
||||
|
||||
<h3 id="jak-mogę-zapewnić-kompleksowe-szyfrowanie-i-usuwanie-wiadomości">
|
||||
|
||||
|
||||
Jak mogę zapewnić kompleksowe szyfrowanie i usuwanie wiadomości? <a href="#jak-mogę-zapewnić-kompleksowe-szyfrowanie-i-usuwanie-wiadomości" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Najlepszym sposobem, aby mieć pewność, że każda wiadomość jest kompleksowo zaszyfrowana, a metadane usunięte tak szybko, jak to możliwe, jest <a href="#howtoe2ee">korzystanie z czatów z gwarantowanym szyfrowaniem end-to-end</a> i włączonymi <a href="#ephemeralmsgs">znikającymi wiadomościami</a>.</p>
|
||||
|
||||
<p>Gwarantowane szyfrowane metodą end-to-end czaty chronią przed atakami <a href="https://en.wikipedia.org/wiki/Man-in-the-middle_attack">MITM attacks</a>, a włączenie znikających wiadomości powoduje usunięcie wiadomości z serwera po upływie czasu skonfigurowanego przez użytkownika.</p>
|
||||
|
||||
<p>Jeśli nie potrzebujesz dłuższej kopii wiadomości na serwerze, możesz także włączyć opcję <a href="#delold">„automatycznie usuń wiadomości z serwera”</a>.</p>
|
||||
<p>Nawet jeśli <a href="https://chatmail.at/relays">przekaźniki chatmail</a> nie proszą o żadne prywatne dane (w tym numery telefonów), warto chronić metadane relacji między adresami. Nie przewidujemy poważniejszych problemów w korzystaniu z losowych, jednorazowych adresów e-mail do wysyłania wiadomości w tajemnicy, ale wdrożenie tej funkcji nie zostało jeszcze uznane za priorytetowe.</p>
|
||||
|
||||
<h3 id="pfs">
|
||||
|
||||
@@ -806,23 +814,33 @@ nadal będziesz mógł pisać, ale nie będziesz już powiadamiany o żadnych no
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Nie, Delta Chat nie obsługuje funkcji Perfect Forward Secrecy (PFS). Oznacza to, że jeśli twój prywatny klucz deszyfrujący Delta Chat wycieknie i ktoś zdobędzie twoje wcześniejsze wiadomości w trakcie przesyłania, będzie mógł je odszyfrować i odczytać przy użyciu ujawnionego klucza deszyfrowania.</p>
|
||||
<p>Nie, jeszcze nie.</p>
|
||||
|
||||
<p>Należy jednak pamiętać, że jeśli ktoś zdobędzie twoje klucze deszyfrujące, zazwyczaj będzie mógł również uzyskać twoje wiadomości, niezależnie od tego, czy obowiązuje zasada Perfect Forward Secrecy, czy nie. Typową sytuacją w świecie rzeczywistym w przypadku wycieku kluczy deszyfrujących jest przejęcie urządzenia, które omawiamy w naszej odpowiedzi na temat <a href="#device-seizure">przejęcia metadanych i urządzenia</a>.</p>
|
||||
<p>Delta Chat obecnie nie obsługuje mechanizmu Perfect Forward Secrecy (PFS). Oznacza to, że jeśli twój prywatny klucz deszyfrujący zostanie ujawniony, a ktoś zdobędzie twoje wcześniejsze wiadomości w trakcie transmisji, będzie mógł je odszyfrować i odczytać za pomocą ujawnionego klucza deszyfrującego. Należy pamiętać, że mechanizm Forward Secrecy zwiększa bezpieczeństwo tylko w przypadku usuwania wiadomości. W przeciwnym razie osoba, która uzyska twoje klucze deszyfrujące, zazwyczaj będzie mogła uzyskać dostęp do wszystkich nieusuniętych wiadomości i nie będzie musiała odszyfrowywać żadnych wcześniej zebranych wiadomości.</p>
|
||||
|
||||
<p>Możliwe, że Delta Chat ewoluuje, aby wspierać Perfect Forward Secrecy, ponieważ OpenPGP to tylko kontener na zaszyfrowane wiadomości, ale zarządzanie kluczami szyfrowania (a tym samym rotacja kluczy lub „zapadanie kluczy”) mogłoby być zorganizowane w elastyczny sposób. Zobacz <a href="https://gitlab.com/sequoia-pgp/openpgp-dr">prototyp PFS firmy Seqouia</a>, aby zapoznać się z istniejącymi eksperymentami w społeczności implementatorów OpenPGP.</p>
|
||||
<p>Opracowaliśmy metodę Forward Secrecy, która przeszła wstępną analizę niektórych kryptografów i ekspertów ds. wdrożeń, ale oczekuje na bardziej formalne opracowanie, które potwierdzi jej niezawodne działanie w federacyjnym przesyłaniu wiadomości i w przypadku korzystania z wielu urządzeń, zanim zostanie zaimplementowana w <a href="https://github.com/chatmail/core">rdzeniu chatmail</a>, co uczyniłoby ją dostępną we wszystkich <a href="https://chatmail.at/clients">klientach chatmail</a>.</p>
|
||||
|
||||
<h3 id="czy-szyfrowanie-end-to-end-delta-chat-jest-tak-samo-bezpieczne-jak-signal">
|
||||
<h3 id="pqc">
|
||||
|
||||
|
||||
Czy szyfrowanie end-to-end Delta Chat jest tak samo bezpieczne jak Signal? <a href="#czy-szyfrowanie-end-to-end-delta-chat-jest-tak-samo-bezpieczne-jak-signal" class="anchor"></a>
|
||||
Czy Delta Chat obsługuje kryptografię postkwantową? <a href="#pqc" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>To zależy od tego, co jest dla ciebie ważne. Delta Chat <a href="#pfs">nie obsługuje PFS</a> tak jak Signal, ale zapewnia <a href="#e2eeguarantee">gwarantowane szyfrowane end-to-end czatów</a>, które jest zabezpieczeniem przed zaatakowanymi serwerami lub uszkodzonymi sieciami. Signal i większość innych komunikatorów obsługujących PFS nie zapewniają praktycznego schematu ochrony czatów grupowych przed atakami sieciowymi, które są prawdopodobnie bardziej niepokojące niż potencjalny atakujący, który przejmuje twój telefon i prywatne ustawienia szyfrowania i w ten sposób nie twoje wiadomości i mimo to ma pełny zapis wszystkich poprzednio zaszyfrowanych wiadomości.</p>
|
||||
<p>Nie, jeszcze nie.</p>
|
||||
|
||||
<p>W każdym razie szyfrowanie end-to-end Delta Chat wykorzystuje <a href="#openpgp-secure">bezpieczny podzbiór OpenPGP</a>, który został poddany <a href="https://delta.chat/assets/blog/2019-first-security-review.pdf">niezależnemu audytowi bezpieczeństwa</a>.</p>
|
||||
<p>Delta Chat korzysta z biblioteki Rust OpenPGP <a href="https://github.com/rpgp/rpgp">rPGP</a>, która obsługuje najnowszy <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">projekt OpenPGP IETF Post-Quantum-Cryptography</a>. Planujemy dodać obsługę PQC do <a href="https://github.com/chatmail/core">rdzenia chatmail</a> po sfinalizowaniu projektu w IETF we współpracy z innymi implementatorami OpenPGP.</p>
|
||||
|
||||
<h3 id="jak-mogę-ręcznie-sprawdzić-informacje-o-szyfrowaniu">
|
||||
|
||||
|
||||
Jak mogę ręcznie sprawdzić informacje o szyfrowaniu? <a href="#jak-mogę-ręcznie-sprawdzić-informacje-o-szyfrowaniu" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Możesz sprawdzić stan szyfrowania end-to-end ręcznie w oknie dialogowym „Szyfrowanie” (profil użytkownika w systemie Android/iOS lub kliknij prawym przyciskiem myszy element listy czatu użytkownika na komputerze). Delta Chat pokazuje tam dwa odciski palców. Jeśli te same odciski palców pojawią się u ciebie i urządzeniu twojego kontaktu, połączenie jest bezpieczne.</p>
|
||||
|
||||
<h3 id="importkey">
|
||||
|
||||
@@ -832,33 +850,11 @@ nadal będziesz mógł pisać, ale nie będziesz już powiadamiany o żadnych no
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Tak.
|
||||
The best way is to send an Autocrypt Setup Message from the other e-mail client.
|
||||
Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the settings of the other client and follow the instructions shown there.</p>
|
||||
<p>Nie.</p>
|
||||
|
||||
<p>Alternatywnie możesz zaimportować klucz ręcznie w „Ustawienia » Zaawansowane » Importuj tajne klucze”. Uwaga: Upewnij się, że klucz nie jest chroniony hasłem lub wcześniej usuń hasło.</p>
|
||||
<p>Delta Chat generuje bezpieczne klucze OpenPGP zgodnie ze specyfikacją Autocrypt 1.1. Możesz wyeksportować swój prywatny klucz, ale nie możesz zaimportować dodatkowych kluczy prywatnych.</p>
|
||||
|
||||
<p>Jeśli nie masz klucza lub nawet nie wiesz, że go potrzebujesz nie przejmuj się: Delta Chat generuje go w razie potrzeby, nie trzeba naciskać przycisku, aby to zrobić.</p>
|
||||
|
||||
<h3 id="nie-mogę-zaimportować-istniejącego-klucza-pgp-do-delta-chat">
|
||||
|
||||
|
||||
Nie mogę zaimportować istniejącego klucza PGP do Delta Chat. <a href="#nie-mogę-zaimportować-istniejącego-klucza-pgp-do-delta-chat" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Jest wielce prawdopodobne, że problemem jest klucz, który jest zaszyfrowany i/lub używa hasła. Takie klucze nie są obsługiwane przez Delta Chat. Usuń szyfrowanie i hasło i spróbować ponownie zaimportować klucz.</p>
|
||||
|
||||
<p>Innym częstym błędem jest nieprawidłowe zakończenie pliku.
|
||||
Użyj opancerzonego formatu ASCII i końcówki pliku <code class="language-plaintext highlighter-rouge">.asc</code>.</p>
|
||||
|
||||
<p>Rozsądny format, Delta Chat obsługuje popularne formaty kluczy prywatnych OpenPGP, jednak jest mało prawdopodobne, że będziemy obsługiwać 100% wszystkich kluczy prywatnych z dowolnych źródeł. To również nie jest głównym celem Delta Chat (w rzeczywistości, znaczna większość użytkowników Delta Chat nie będzie miała żadnego klucza przed rozpoczęciem korzystania z Delta). Jednak staramy się wspierać klucze prywatne z innych źródeł tak dobrze jak to tylko możliwe.</p>
|
||||
|
||||
<p>Usunięcie hasła z klucza prywatnego będzie się różnić w zależności od
|
||||
oprogramowania używanego do zarządzania kluczami PGP. Za pomocą programu Enigmail można ustawić
|
||||
hasło na pustą wartość w oknie zarządzania kluczami. Z GnuPG możesz ustawić je <a href="https://github.com/deltachat/deltachat-android/issues/98#issuecomment-378383429">z linii poleceń</a>.
|
||||
W przypadku innych programów można znaleźć rozwiązanie online.</p>
|
||||
<p>Generalnie nie zalecamy ani nie oferujemy użytkownikom wykonywania ręcznego zarządzania kluczami. Chcemy się upewnić, że audyty bezpieczeństwa mogą skupić się na kilku sprawdzonych algorytmach kryptograficznych, a nie na pełnym zakresie możliwych algorytmów dozwolonych w OpenPGP.</p>
|
||||
|
||||
<h3 id="security-audits">
|
||||
|
||||
@@ -868,7 +864,18 @@ W przypadku innych programów można znaleźć rozwiązanie online.</p>
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Projekt Delta Chat przeszedł cztery niezależne audyty bezpieczeństwa i jedną niezależną analizę bezpieczeństwa, od najnowszego do najstarszego:</p>
|
||||
<p>Tak, wielokrotnie.
|
||||
Projekt Delta Chat stale przechodzi niezależne audyty bezpieczeństwa i analizy,
|
||||
od najnowszych do najstarszych:</p>
|
||||
|
||||
<p>W grudniu 2024 r. <a href="https://github.com/rpgp/docs/blob/main/audits/NGI%20Core%20rPGP%20penetration%20test%20report%202024%201.0.pdf">NLNET</a> wykonała ocenę rPGP zleconą przez <a href="https://www.radicallyopensecurity.com/">Radically Open Security</a>. rPGP służy jako kompleksowy silnik szyfrowania <a href="https://openpgp.org">OpenPGP</a> w Delta Chat. Wydano dwa ostrzeżenia związane z wynikami tego audytu:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="https://github.com/rpgp/rpgp/security/advisories/GHSA-9rmp-2568-59rv">„Paniki przy nieprawidłowym wprowadzaniu niezaufanych danych wejściowych”</a> CVE-2024-53856</li>
|
||||
<li><a href="https://github.com/rpgp/rpgp/security/advisories/GHSA-4grw-m28r-q285">„Potencjalne wyczerpanie zasobów podczas obsługi niezaufanych wiadomości”</a> CVE-2024-53857</li>
|
||||
</ul>
|
||||
|
||||
<p>Problemy opisane w tych ostrzeżeniach zostały naprawione i są częścią wydań Delta Chat we wszystkich sklepach z aplikacjami od grudnia 2024 r.</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
@@ -904,7 +911,7 @@ W przypadku innych programów można znaleźć rozwiązanie online.</p>
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Tak. Delta Chat 1.36 comes with a new, experimental function for using the same profile on different devices:</p>
|
||||
<p>Tak. You can use the same profile on different devices:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
@@ -954,6 +961,9 @@ W przypadku innych programów można znaleźć rozwiązanie online.</p>
|
||||
<li>
|
||||
<p><strong>Sieci dla gości</strong> mogą nie pozwalać urządzeniom na komunikację między sobą. Jeśli to możliwe, korzystaj z sieci innej niż gość.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Jeśli nadal masz problemy z korzystaniem z tej samej sieci, spróbuj otworzyć <strong>Hotspot</strong> na jednym urządzeniu i połączyć się z tą siecią Wi-Fi z drugiego urządzenia</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Upewnij się, że na urządzeniu docelowym jest <strong>wystarczająca ilość miejsca</strong></p>
|
||||
</li>
|
||||
@@ -988,6 +998,20 @@ W przypadku innych programów można znaleźć rozwiązanie online.</p>
|
||||
<li>Jesteś teraz zsynchronizowany i możesz używać obu urządzeń do wysyłania i odbierania wiadomości zaszyfrowanych end-to-end w komunikacji ze swoimi partnerami.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="czy-usuwanie-przypinanie-archiwizowanie-zapisywanie-wyciszanie-itp-jest-synchronizowane-ze-wszystkimi-urządzeniami">
|
||||
|
||||
|
||||
Czy usuwanie, przypinanie, archiwizowanie, zapisywanie, wyciszanie itp. jest synchronizowane ze wszystkimi urządzeniami? <a href="#czy-usuwanie-przypinanie-archiwizowanie-zapisywanie-wyciszanie-itp-jest-synchronizowane-ze-wszystkimi-urządzeniami" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Tak.</p>
|
||||
|
||||
<p>Jeśli np. usuniesz czat lub wiadomość z telefonu, zostaną one usunięte z aplikacji na komputer, gdy tylko ponownie będą online.</p>
|
||||
|
||||
<p>Wyjątkiem jest <a href="#delold">usuwanie starych wiadomości z urządzenia</a>, ponieważ różne urządzenia mogą mieć różne pojemności pamięci i ustawienia specyficzne dla systemu, takie jak dźwięki powiadomień.</p>
|
||||
|
||||
<h3 id="czy-są-jakieś-plany-wprowadzenia-klienta-web-delta-chat">
|
||||
|
||||
|
||||
@@ -1024,7 +1048,7 @@ W przypadku innych programów można znaleźć rozwiązanie online.</p>
|
||||
<li>Aplikacje webxdc nie mogą wysyłać danych do Internetu ani niczego pobierać.</li>
|
||||
<li>Aplikacja webxdc może wymieniać dane wyłącznie w ramach czatu Delta Chat, a ich kopie znajdują się na urządzeniach twoich partnerów czatu. Poza tym jest całkowicie odizolowana od Internetu.</li>
|
||||
<li>Prywatność, jaką oferuje aplikacja webxdc, to prywatność twojego czatu – jeśli ufasz osobom, z którymi rozmawiasz, możesz ufać także aplikacji webxdc.</li>
|
||||
<li>Oznacza to również: otwieranie aplikacji webxdc na czatach, w przypadku których nie ufa się członkom, może stanowić ryzyko dla prywatności – tak jak załączniki do wiadomości e-mail, gdzie otwiera się tylko załączniki od nadawców, którym ufasz, a nie od spamerów.</li>
|
||||
<li>Oznacza to również: otwieranie aplikacji w czatach z niezaufanymi członkami, co może stanowić ryzyko dla prywatności. Podobnie jak w przypadku załączników do wiadomości e-mail, połączeń wideo lub zwykłych linków: otwieraj je tylko od nadawców, którym ufasz, a nie od spamerów. Spamerzy mogą poznać wszelkie dane, które im wysyłasz, a także twój adres IP.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="gdzie-mogę-zdobyć-aplikacje-webxdc">
|
||||
@@ -1091,13 +1115,8 @@ W przypadku innych programów można znaleźć rozwiązanie online.</p>
|
||||
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>Dzięki liście rozgłoszeniowej możesz wysłać wiadomość do wielu odbiorców jednocześnie; kiedy ci odpowiedzą, otrzymasz odpowiedź w bezpośrednim czacie 1: 1 z nimi. Odbiorcy nie widzą siebie nawzajem.</li>
|
||||
<li>Technicznie, jest to e-mail z wieloma odbiorcami w „ukrytej kopii”.</li>
|
||||
<li>Możesz włączyć tę funkcję w sekcji „Funkcje eksperymentalne” w ustawieniach zaawansowanych. Następnie możesz utworzyć listę rozgłoszeniową z okna dialogowego „Nowy czat”.</li>
|
||||
<li>Jeśli używasz więcej niż jednego urządzenia, listy rozgłoszeniowe nie są obecnie synchronizowane między nimi.</li>
|
||||
<li>Wiadomości wysyłane do list rozgłoszeniowych nie są szyfrowane. Szyfrowanie złamałoby anonimowość, ponieważ wtedy wszyscy odbiorcy wiedzieliby, kto jeszcze je otrzymał (wysyłanie indywidualnych wiadomości do wszystkich byłoby gorsze z powodu limitu prędkości i zużycia sieci).</li>
|
||||
</ul>
|
||||
<p>Za pomocą listy rozgłoszeniowej możesz wysłać wiadomość do wielu odbiorców naraz; odbiorcy nie mogą odpowiedzieć na tej liście.
|
||||
Listy rozgłoszeniowe są nadal wysoce eksperymentalne i bardzo prawdopodobne, że zostaną zastąpione czymś innym, bądźcie czujni :)</p>
|
||||
|
||||
<h3 id="jak-mogę-udostępnić-swoją-lokalizację-moim-rozmówcom">
|
||||
|
||||
@@ -1143,6 +1162,10 @@ W przypadku innych programów można znaleźć rozwiązanie online.</p>
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Uwaga: Zmiana adresów e-mail jest tymczasowo wyłączona
|
||||
z powodu trwających zmian w rdzeniu DeltaChat.
|
||||
Powinna być ponownie dostępna za kilka miesięcy.</p>
|
||||
|
||||
<ol>
|
||||
<li>
|
||||
<p>Zmień swój adres w „Ustawienia → Zaawansowane → Hasło i konto” i wprowadź hasło do swojego nowego konta (i jeśli to konieczne, ustawienia serwera). Otrzymasz informację o przeniesieniu na nowy adres. Dodatkowe powiadomienie pojawi się również na czacie „Komunikaty urządzenia”.</p>
|
||||
@@ -1216,17 +1239,40 @@ Oto, co robi Delta Chat z tymi uprawnieniami:</p>
|
||||
Jednak niektórzy dostawcy potrzebują specjalnych opcji, aby działać poprawnie, zobacz <a href="https://providers.delta.chat">Przegląd dostawców</a></li>
|
||||
</ul>
|
||||
|
||||
<h3 id="chcę-zarządzać-własnym-serwerem-e-mail-dla-delta-chat-co-polecacie">
|
||||
<h3 id="can-i-use-delta-chat-in-parallel-with-other-e-mail-apps">
|
||||
|
||||
|
||||
Chcę zarządzać własnym serwerem e-mail dla Delta Chat. Co polecacie? <a href="#chcę-zarządzać-własnym-serwerem-e-mail-dla-delta-chat-co-polecacie" class="anchor"></a>
|
||||
Can I use Delta Chat in parallel with other E-Mail apps? <a href="#can-i-use-delta-chat-in-parallel-with-other-e-mail-apps" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Yes, but it is not recommended.</p>
|
||||
|
||||
<p>Parallel usage with the same E-Mail address might lead to the following inconveniences:</p>
|
||||
|
||||
<ul>
|
||||
<li>Next to your Delta Chat notifications,
|
||||
you might also get notifications for (encrypted, so unreadable) chat mail messages
|
||||
in your other email app</li>
|
||||
<li>If Delta Chat is not running
|
||||
or “Advanced > Move automatically to the DeltaChat Folder” is disabled,
|
||||
encrypted Delta Chat messages might clutter the Inbox in your other E-Mail apps.</li>
|
||||
</ul>
|
||||
|
||||
<p><a href="https://support.delta.chat/t/sieve-rule-to-move-deltachat-mails-to-deltachat-folder/288/10">Sieve rules</a> can solve these problems.</p>
|
||||
|
||||
<h3 id="chcę-zarządzać-własnym-serwerem-dla-delta-chat-co-polecacie">
|
||||
|
||||
|
||||
Chcę zarządzać własnym serwerem dla Delta Chat. Co polecacie? <a href="#chcę-zarządzać-własnym-serwerem-dla-delta-chat-co-polecacie" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>Większość serwerów pocztowych będzie działać dobrze. Ale to, co osobiście polecamy, to połączenie mailcow i mailadm, jak opisano <a href="https://delta.chat/en/2023-01-27-upcoming-mail-server-workshops">w tym poście na blogu</a>.</li>
|
||||
<li>Instrukcję instalacji można znaleźć <a href="https://delta.chat/pl/serverguide">na naszej stronie internetowej</a>.</li>
|
||||
<li>Większość serwerów pocztowych będzie działać dobrze. Ale my osobiście polecamy serwer przekaźnikowy chatmail, jak opisano <a href="https://delta.chat/en/2023-12-13-chatmail">w tym wpisie na blogu</a>.</li>
|
||||
<li>Możesz znaleźć <a href="https://github.com/chatmail/relay">przewodnik instalacji na GitHub</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="dlaczego-muszę-wpisać-moje-hasło-e-mail-do-delta-chat-czy-to-jest-bezpieczne">
|
||||
@@ -1239,7 +1285,7 @@ Jednak niektórzy dostawcy potrzebują specjalnych opcji, aby działać poprawni
|
||||
|
||||
<p>Podobnie jak w przypadku innych programów pocztowych, takich jak Thunderbird, K9-Mail lub Outlook, program potrzebuje hasła, aby można było go używać do wysyłania i odbierania e-maili. Oczywiście hasło jest przechowywane tylko na Twoim urządzeniu. Hasło jest przesyłane tylko do Twojego dostawcy poczty e-mail (po zalogowaniu), który i tak ma dostęp do Twojej poczty</p>
|
||||
|
||||
<p>Ponieważ Delta Chat jest Open Source, możesz sprawdzić <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/src/login_param.rs">Kod źródłowy</a>
|
||||
<p>Ponieważ Delta Chat jest Open Source, możesz sprawdzić <a href="https://github.com/chatmail/core/blob/main/src/login_param.rs">Kod źródłowy</a>
|
||||
jeśli chcesz sprawdzić, czy Twoje poświadczenia są przetwarzane w bezpieczny sposób. Cieszymy się z opinii, które sprawiają, że aplikacja jest bezpieczniejsza dla wszystkich naszych użytkowników.</p>
|
||||
|
||||
<h3 id="jakie-wiadomości-pojawiają-się-w-delta-chat">
|
||||
@@ -1318,17 +1364,17 @@ jeśli chcesz sprawdzić, czy Twoje poświadczenia są przetwarzane w bezpieczny
|
||||
|
||||
<p>Niektórzy używają Delta Chat jako zwykłego klienta poczty e-mail i chcą używać folderu Skrzynka odbiorcza dla swojej poczty zamiast folderu DeltaChat. Jeśli wyłączysz opcję „Oglądaj folder DeltaChat”, powinieneś również wyłączyć opcję „Automatyczne przenoszenie do folderu DeltaChat”. W przeciwnym razie usuwanie wiadomości lub konfiguracja wielu urządzeń może nie działać poprawnie.</p>
|
||||
|
||||
<h3 id="czy-delta-chat-jest-kompatybilny-z-protonmail--tutanota--criptext">
|
||||
<h3 id="czy-delta-chat-jest-kompatybilny-z-proton-mail--tutanota--criptext">
|
||||
|
||||
|
||||
Czy Delta Chat jest kompatybilny z Protonmail / Tutanota / Criptext? <a href="#czy-delta-chat-jest-kompatybilny-z-protonmail--tutanota--criptext" class="anchor"></a>
|
||||
Czy Delta Chat jest kompatybilny z Proton Mail / Tutanota / Criptext? <a href="#czy-delta-chat-jest-kompatybilny-z-proton-mail--tutanota--criptext" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>I tak i nie.</li>
|
||||
<li>Nie, nie możesz używać swojego konta Protonmail, Tutanota lub Criptext z Delta Chat; nie oferują one odbierania poczty przez IMAP.</li>
|
||||
<li>Nie, nie możesz używać swojego konta Proton Mail, Tutanota lub Criptext z Delta Chat; nie oferują one odbierania poczty przez IMAP.</li>
|
||||
<li>W każdym przypadku możesz użyć Delta Chat do wysyłania wiadomości do osób, które używają Protonmail, Tutanota lub Criptext. Te wiadomości nie będą jednak szyfrowane metodą end-to-end. Szyfrowanie typu end-to-end, które oferują ci dostawcy, nie jest kompatybilne z <a href="https://autocrypt.org/">Autocrypt</a> stosowanym standardowo w Delta Chat.</li>
|
||||
<li>Delta Chat może zaszyfrować metodą end-to-end za pośrednictwem dowolnego dostawcy poczty e-mail z dowolną <a href="https://autocrypt.org/dev-status.html">aplikacją e-mail z włączoną funkcją Autocrypt</a>.</li>
|
||||
</ul>
|
||||
@@ -1360,7 +1406,7 @@ jeśli chcesz sprawdzić, czy Twoje poświadczenia są przetwarzane w bezpieczny
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>Zobacz <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">Standardy używane w Delta Chat</a>.</li>
|
||||
<li>Zobacz <a href="https://github.com/chatmail/core/blob/main/standards.md#standards-used-in-delta-chat">Standardy używane w Delta Chat</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="gdzie-moi-znajomi-mogą-znaleźć-delta-chat">
|
||||
@@ -1445,4 +1491,4 @@ Fundusze te wspierają nasze długofalowe cele, aby uczynić Delta Chat bardziej
|
||||
|
||||
|
||||
|
||||
</body></html>
|
||||
</body></html>
|
||||
|
After Width: | Height: | Size: 145 B |
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
|
||||
<html lang="uk"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
|
||||
<li><a href="#що-таке-delta-chat">Що таке Delta Chat?</a>
|
||||
<ul>
|
||||
<li><a href="#як-мені-знайти-людей-для-спілкування">Як мені знайти людей для спілкування?</a></li>
|
||||
@@ -10,7 +10,9 @@
|
||||
<li><a href="#хто-бачить-моє-зображення-профілю">Хто бачить моє зображення профілю?</a></li>
|
||||
<li><a href="#signature">Чи можу я встановити текст підпису/статусу/девізу у Delta Chat?</a></li>
|
||||
<li><a href="#що-значить-закріплення-приглушення-архівування">Що значить Закріплення, Приглушення, Архівування?</a></li>
|
||||
<li><a href="#save">Як працюють “Збережені повідомлення”?</a></li>
|
||||
<li><a href="#що-означає-зелена-точка">Що означає зелена точка?</a></li>
|
||||
<li><a href="#edit">Виправлення помилок та видалення повідомлень після надсилання</a></li>
|
||||
<li><a href="#ephemeralmsgs">Як працюють повідомлення, що зникають?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -34,29 +36,25 @@
|
||||
<li><a href="#чому-delta-chat-інтегрується-з-централізованими-пропрієтарними-push-сервісами-applegoogle">Чому Delta Chat інтегрується з централізованими пропрієтарними push-сервісами Apple/Google?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#шифрування-та-безпека">Шифрування та безпека</a>
|
||||
<li><a href="#e2ee">Шифрування та безпека</a>
|
||||
<ul>
|
||||
<li><a href="#які-стандарти-використовуються-для-наскрізного-шифрування">Які стандарти використовуються для наскрізного шифрування?</a></li>
|
||||
<li><a href="#whene2e">Як дізнатися, чи повідомлення зашифровано наскрізним шифруванням?</a></li>
|
||||
<li><a href="#howtoe2ee">Як отримати гарантоване наскрізне шифрування та зелені галочки?</a></li>
|
||||
<li><a href="#e2eeguarantee">Що означає зелена галочка і “гарантоване наскрізне шифрування”?</a></li>
|
||||
<li><a href="#nocryptanymore">Контакт “надіслав повідомлення з іншого пристрою”, що робити?</a></li>
|
||||
<li><a href="#чи-можу-я-отримувати-та-надсилати-пошту-без-наскрізного-шифрування">Чи можу я отримувати та надсилати пошту без наскрізного шифрування?</a></li>
|
||||
<li><a href="#howtoe2ee">Як створити чат з новим контактом?</a></li>
|
||||
<li><a href="#e2eeguarantee">Що означає зелена галочка в профілі контакту?</a></li>
|
||||
<li><a href="#чи-зашифровані-наскрізно-вкладення-зображення-файли-аудіо-тощо">Чи зашифровані наскрізно вкладення (зображення, файли, аудіо тощо)?</a></li>
|
||||
<li><a href="#openpgp-secure">Чи безпечний OpenPGP?</a></li>
|
||||
<li><a href="#openpgp-alternatives">Чи розглядали ви можливість використання альтернатив OpenPGP для наскрізного шифрування?</a></li>
|
||||
<li><a href="#чи-вразливий-delta-chat-до-efail">Чи вразливий Delta Chat до EFAIL?</a></li>
|
||||
<li><a href="#tls">Чи буде показано повідомлення відкритим текстом, якщо наскрізне шифрування недоступне?</a></li>
|
||||
<li><a href="#чи-повідомлення-позначені-значком-пошти-доступні-в-інтернетіtls">Чи повідомлення, позначені значком пошти, доступні в Інтернеті?{#tls}</a></li>
|
||||
<li><a href="#message-metadata">Як Delta Chat захищає метадані у повідомленнях?</a></li>
|
||||
<li><a href="#device-seizure">Як захистити метадані та контакти якщо пристрій вилучено?</a></li>
|
||||
<li><a href="#як-перевірити-інформацію-про-шифрування">Як перевірити інформацію про шифрування?</a></li>
|
||||
<li><a href="#як-перевірити-стан-шифрування-повідомлень">Як перевірити стан шифрування повідомлень?</a></li>
|
||||
<li><a href="#чому-я-бачу-незашифровані-повідомлення">Чому я бачу незашифровані повідомлення?</a></li>
|
||||
<li><a href="#як-я-можу-отримати-наскрізно-зашифрований-чат-з-контактом-delta-chat-який-іноді-користується-веб-поштою-або-іншим-додатком-електронної-пошти-що-не-підтримує-autocrypt">Як я можу отримати наскрізно зашифрований чат з контактом Delta Chat, який іноді користується веб-поштою або іншим додатком електронної пошти, що не підтримує Autocrypt?</a></li>
|
||||
<li><a href="#як-забезпечити-наскрізне-шифрування-та-видалення-повідомлень">Як забезпечити наскрізне шифрування та видалення повідомлень?</a></li>
|
||||
<li><a href="#sealedsender">Чи підтримує Delta Chat функцію “Запечатаний відправник”?</a></li>
|
||||
<li><a href="#pfs">Чи підтримує Delta Chat цілковиту пряму секретність (Perfect Forward Secrecy)?</a></li>
|
||||
<li><a href="#чи-є-наскрізне-шифрування-delta-chat-таким-же-безпечним-як-signal">Чи є наскрізне шифрування Delta Chat таким же безпечним, як Signal?</a></li>
|
||||
<li><a href="#pqc">Чи підтримує Delta Chat пост-квантову криптографію?</a></li>
|
||||
<li><a href="#як-я-можу-вручну-перевірити-інформацію-про-шифрування">Як я можу вручну перевірити інформацію про шифрування?</a></li>
|
||||
<li><a href="#importkey">Чи можна повторно використовувати існуючий закритий ключ?</a></li>
|
||||
<li><a href="#я-не-можу-імпортувати-свій-існуючий-pgp-ключ-у-delta-chat">Я не можу імпортувати свій існуючий PGP ключ у Delta Chat.</a></li>
|
||||
<li><a href="#security-audits">Чи проходив Delta Chat незалежний аудит на наявність вразливостей у безпеці?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -65,6 +63,7 @@
|
||||
<li><a href="#чи-можна-використовувати-delta-chat-на-декількох-пристроях-одночасно">Чи можна використовувати Delta Chat на декількох пристроях одночасно?</a></li>
|
||||
<li><a href="#вирішення-проблем">Вирішення проблем</a></li>
|
||||
<li><a href="#backup">Ручне перенесення</a></li>
|
||||
<li><a href="#чи-синхронізовані-видалення-закріплення-архівування-збереження-вимкнення-сповіщень-тощо-з-усіма-пристроями">Чи синхронізовані видалення, закріплення, архівування, збереження, вимкнення сповіщень тощо з усіма пристроями?</a></li>
|
||||
<li><a href="#чи-планується-впровадження-веб-клієнта-delta-chat">Чи планується впровадження веб-клієнта Delta Chat?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -81,14 +80,15 @@
|
||||
<li><a href="#що-таке-списки-широкомовлення-та-як-ними-користуватися">Що таке списки широкомовлення та як ними користуватися?</a></li>
|
||||
<li><a href="#як-я-можу-поділитися-своїм-місцезнаходженням-зі-своїми-співрозмовниками-в-чаті">Як я можу поділитися своїм місцезнаходженням зі своїми співрозмовниками в чаті?</a></li>
|
||||
<li><a href="#чому-я-можу-вибрати-лише-стеження-за-папкою-deltachat">Чому я можу вибрати лише стеження за папкою DeltaChat?</a></li>
|
||||
<li><a href="#як-я-можу-змінити-мій-обліковий-запис-на-іншу-адресу-електронної-пошти">Як я можу змінити мій обліковий запис на іншу адресу електронної пошти?</a></li>
|
||||
<li><a href="#як-я-можу-використовувати-іншу-адресу-електронної-пошти-у-своєму-профілі">Як я можу використовувати іншу адресу електронної пошти у своєму профілі?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#інше">Інше</a>
|
||||
<ul>
|
||||
<li><a href="#яких-дозволів-потребує-delta-chat">Яких дозволів потребує Delta Chat?</a></li>
|
||||
<li><a href="#чи-підтримує-delta-chat-роботу-з-моїм-провайдером-електронної-пошти">Чи підтримує Delta Chat роботу з <em>моїм</em> провайдером електронної пошти?</a></li>
|
||||
<li><a href="#я-хочу-керувати-власним-сервером-електронної-пошти-для-delta-chat-що-ви-порадите">Я хочу керувати власним сервером електронної пошти для Delta Chat. Що ви порадите?</a></li>
|
||||
<li><a href="#can-i-use-delta-chat-in-parallel-with-other-e-mail-apps">Can I use Delta Chat in parallel with other E-Mail apps?</a></li>
|
||||
<li><a href="#я-хочу-керувати-власним-сервером-для-delta-chat-що-ви-порекомендуєте">Я хочу керувати власним сервером для Delta Chat. Що ви порекомендуєте?</a></li>
|
||||
<li><a href="#чому-я-маю-вводити-пароль-до-моєї-електронної-пошти-у-delta-chat-чи-це-безпечно">Чому я маю вводити пароль до моєї електронної пошти у Delta Chat? Чи це безпечно?</a></li>
|
||||
<li><a href="#які-повідомлення-відображаються-у-delta-chat">Які повідомлення відображаються у Delta Chat?</a></li>
|
||||
<li><a href="#чи-підтримує-delta-chat-html-листи">Чи підтримує Delta Chat HTML-листи?</a></li>
|
||||
@@ -96,7 +96,7 @@
|
||||
<li><a href="#для-чого-потрібне-налаштування-відправити-копію-собі">Для чого потрібне налаштування “Відправити копію собі”?</a></li>
|
||||
<li><a href="#чому-я-можу-обрати-стеження-за--папкою-надіслані">Чому я можу обрати стеження за папкою “Надіслані”?</a></li>
|
||||
<li><a href="#чому-я-можу-відмовитись-від-стеження-за--папкою-deltachat">Чому я можу відмовитись від стеження за папкою DeltaChat?</a></li>
|
||||
<li><a href="#чи-сумісний-delta-chat-із-protonmail--tutanota--criptext">Чи сумісний Delta Chat із Protonmail / Tutanota / Criptext?</a></li>
|
||||
<li><a href="#чи-сумісний-delta-chat-із-proton-mail--tutanota--criptext">Чи сумісний Delta Chat із Proton Mail / Tutanota / Criptext?</a></li>
|
||||
<li><a href="#remove-account">Як мені видалити свій обліковий запис?</a></li>
|
||||
<li><a href="#мене-цікавлять-технічні-деталі-можете-розповісти-більше">Мене цікавлять технічні деталі. Можете розповісти більше?</a></li>
|
||||
<li><a href="#де-мої-друзі-можуть-знайти-delta-chat">Де мої друзі можуть знайти Delta Chat?</a></li>
|
||||
@@ -117,7 +117,7 @@
|
||||
|
||||
<p>Delta Chat - це надійний, децентралізований та безпечний додаток для обміну повідомленнями, доступний для мобільних і десктопних платформ.</p>
|
||||
|
||||
<p>Delta Chat схожий на Whatsapp або Telegram, але ви також можете використовувати його як додаток електронної пошти. Ви можете анонімно зареєструватися на різних <a href="https://delta.chat/chatmail">сумісних chatmail-серверах</a> які є мінімальними поштовими серверами, оптимізованими для швидкої та безпечної роботи. Або ви можете використовувати класичні поштові сервери та існуючий обліковий запис електронної пошти в цьому випадку Delta Chat буде працювати як поштовий додаток.</p>
|
||||
<p>Delta Chat схожий на Whatsapp або Telegram, але ви також можете використовувати його як додаток електронної пошти. Ви можете анонімно зареєструватися на різних <a href="https://delta.chat/chatmail">сумісних chatmail-серверах</a> які є мінімальними поштовими серверами, оптимізованими для швидкої та безпечної роботи. Або ви можете використовувати класичні поштові сервери та існуючий профіль електронної пошти в цьому випадку Delta Chat буде працювати як поштовий додаток.</p>
|
||||
|
||||
<p><img style="float:right; width:50%; max-width:360%; margin:1em;" src="../delta-what-optim.png" /></p>
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
<p><a href="#security-audits">Аудитоване наскрізне шифрування</a> захищене від мережевих та серверних атак.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Вільне програмне забезпечення з відкритим вихідним кодом, як для додатків, так і для сервера. Побудовано на основі <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md">Стандартів електронної пошти та інтернету</a>, <a href="https://xkcd.com/927/">щоб уникнути “синдрому ще одного стандарту (xkcd 927)”</a></p>
|
||||
<p>Вільне програмне забезпечення з відкритим вихідним кодом, як для додатків, так і для сервера. Побудовано на основі <a href="https://github.com/chatmail/core/blob/main/standards.md#standards-used-in-delta-chat">Стандартів електронної пошти та інтернету</a>, <a href="https://xkcd.com/927/">щоб уникнути “синдрому ще одного стандарту (xkcd 927)”</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -167,10 +167,18 @@
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Якщо повідомлення приходить від невідомого контакту, воно відображається як <strong>запит</strong>.</p>
|
||||
|
||||
<ul>
|
||||
<li>Якщо повідомлення приходить від невідомого контакту, воно відображається як <strong>запит</strong>. ви потрібно прийняти запит, перш ніж ви зможете відповісти.</li>
|
||||
<li>Ви також можете “видалити” його, якщо ви не хочете зараз спілкуватися з ними. Це <em>не</em> видаляє повідомлення на сервері, лише на вашому пристрої. Отже, ви можете як і раніше обробляти повідомлення в іншій поштовій програмі.</li>
|
||||
<li>Якщо ви видалите запит, майбутні повідомлення від цього контакту все одно відображатимуться як запит на повідомлення, щоб ви могли змінити свою думку. Якщо дуже не хочеться отримувати повідомлення від цієї особи, подумайте про її <em>блокування</em>.</li>
|
||||
<li>
|
||||
<p>ви потрібно прийняти запит, перш ніж ви зможете відповісти.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Ви також можете “видалити” його, якщо ви не хочете зараз спілкуватися з ними.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Якщо ви видалите запит, майбутні повідомлення від цього контакту все одно відображатимуться як запит на повідомлення, щоб ви могли змінити свою думку. Якщо дуже не хочеться отримувати повідомлення від цієї особи, подумайте про її <em>блокування</em>.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="чи-підтримує-delta-chat-вкладення-у-вигляді-фото-відео-тощо">
|
||||
@@ -270,6 +278,36 @@
|
||||
|
||||
<p>Щоб скористатися функціями, утримуйте натиснутою клавішу або клацніть правою кнопкою миші на чаті у списку чатів.</p>
|
||||
|
||||
<h3 id="save">
|
||||
|
||||
|
||||
Як працюють “Збережені повідомлення”? <a href="#save" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p><strong>Збережені повідомлення</strong> - це чат, за допомогою якого ви можете легко запам’ятовувати та знаходити повідомлення.</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>У будь-якому чаті натисніть і утримуйте повідомлення або клацніть правою кнопкою миші та виберіть <strong>Зберегти</strong>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Збережені повідомлення позначені символом <img style="vertical-align:middle; width:1.2em; margin:1px" src="../saved-icon.png" alt="Saved icon" /> поруч з міткою часу</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Пізніше відкрийте чат “Збережені повідомлення” - і ви побачите там збережені повідомлення. Натиснувши <img style="vertical-align:middle; width:1.2em; margin:1px" src="../go-to-original.png" alt="Arrow-right icon" />, ви можете повернутися до початкового повідомлення в оригінальному чаті</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Нарешті, ви також можете використовувати “Зберегти повідомлення”, щоб робити <strong>особисті нотатки</strong> - відкрити чат, набрати щось, додати фотографію або голосове повідомлення тощо.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Оскільки “Збережені повідомлення” синхронізуються, вони можуть стати дуже зручними для передачі даних між пристроями</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Повідомлення залишаються збереженими, навіть якщо вони були відредаговані або видалені - чи то через <a href="#edit">відправника</a>, чи то через <a href="#delold">очищення пристрою</a>, чи то через <a href="#ephemeralmsgs">зникнення повідомлень інших чатів</a>.</p>
|
||||
|
||||
<h3 id="що-означає-зелена-точка">
|
||||
|
||||
|
||||
@@ -278,16 +316,40 @@
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Іноді ви можете бачити <strong>зелену крапку</strong> <img style="vertical-align:middle; width:1.2em; margin:1px" src="../green-dot.png" alt="" /> поруч з аватаркою контакту. Це означає, що його <strong>недавно бачили</strong> протягом останніх 10 хвилин:</p>
|
||||
|
||||
<ul>
|
||||
<li>Починаючи з Delta Chat 1.34, ви іноді можете бачити “зелену крапку” поруч із аватаром контакту. Це означає, що контакт «нещодавно бачили».</li>
|
||||
<li>Детально: це означає, що за останні 10 хвилин Delta Chat бачив їх:</li>
|
||||
<li>або тому, що вони надіслали вам повідомлення безпосередньо,</li>
|
||||
<li>тому що вони написали щось групі, учасником якої є ви обоє,</li>
|
||||
<li>тому що вони надіслали вам сповіщення про прочитання повідомлення, яке ви написали,</li>
|
||||
<li>або тому, що вони надіслали дані до вашої програми Delta Chat за допомогою a <a href="#webxdc">приватна програма</a>.
|
||||
– Отже, це не онлайн-статус у реальному часі – і якщо хтось не відповідає відразу, навіть якщо вони, здаються, онлайн, не хвилюйтесь і дайте їм трохи простору ;-)
|
||||
– З іншого боку, інші не завжди «побачать, що ви онлайн». Якщо ви вимкнули сповіщення про прочитання, вони не побачать зелену крапку, доки ви не надішлете їм повідомлення або напишете у групу, до якої вони входять.</li>
|
||||
<li>або тому, що вони написали вам безпосередньо,</li>
|
||||
<li>тому що вони написали щось у групі, в якій ви обидва є учасниками,</li>
|
||||
<li>тому що вони надіслали вам квитанцію про прочитання повідомлення, яке ви написали,</li>
|
||||
<li>тому що вони відредагували або видалили повідомлення у спільному з вами чаті,</li>
|
||||
<li>або тому, що вони використовують <a href="#webxdc">app</a> у спільному з вами чаті.</li>
|
||||
</ul>
|
||||
|
||||
<p>Отже, це не онлайн статус в реальному часі - і якщо хтось не відповідає одразу, хоча здається, що вони онлайн, не хвилюйтеся і дайте їм трохи часу :)</p>
|
||||
|
||||
<p>З іншого боку, інші не завжди “бачать, що ви онлайн”. Якщо ви вимкнули звіти про прочитання, вони не побачать зелену крапку доки не буде виконано одну з вищезазначених умов.</p>
|
||||
|
||||
<h3 id="edit">
|
||||
|
||||
|
||||
Виправлення помилок та видалення повідомлень після надсилання <a href="#edit" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>Ви можете редагувати текст ваших повідомлень після відправлення. Для цього натисніть на повідомлення довгим натисканням або правою кнопкою миші і виберіть <strong>Редагувати</strong> або <img style="vertical-align:middle; width:1.2em; margin:1px" src="../edit-icon.png" alt="Edit icon" />.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Якщо ви випадково надіслали повідомлення, в тому ж меню виберіть <strong>Видалити</strong>, а потім <strong>Видалити для всіх</strong>.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Відредаговані повідомлення матимуть позначку “Відредаговано” поруч з міткою часу, видалені повідомлення будуть видалені без позначки в чаті. Сповіщення не надсилаються і відсутнє часове обмеження.</p>
|
||||
|
||||
<p>Зауважте, що початкове повідомлення все ще може бути отримане учасниками чату які могли вже відповісти, переслати, зберегти, зробити знімок екрану або іншим чином скопіювати повідомлення.</p>
|
||||
|
||||
<h3 id="ephemeralmsgs">
|
||||
|
||||
@@ -297,7 +359,11 @@
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Увімкнути “повідомлення, що зникають” можна в налаштуваннях чату, у верхньому правому куті вікна чату, вибравши проміжок часу від 1 хвилини до 5 тижнів.</p>
|
||||
<p>You can turn on “disappearing messages”
|
||||
in the settings of a chat,
|
||||
at the top right of the chat window,
|
||||
by selecting a time span
|
||||
between 5 minutes and 1 year.</p>
|
||||
|
||||
<p>Доки налаштування не буде знову вимкнено, додаток Delta Chat кожного учасника чату піклується про видалення повідомлень через вибраний проміжок часу. Проміжок часу починається коли одержувач вперше побачив повідомлення в Delta Chat. Повідомлення видаляються як з кожної поштової скриньки на сервері, так і в самому додатку.</p>
|
||||
|
||||
@@ -400,7 +466,7 @@
|
||||
<p>Щоб увімкнути його, перейдіть до <strong>Видалити старі повідомлення → Видалити повідомлення з сервера</strong> в налаштуваннях «Чати та медіа». Ви можете встановити часові рамки між «Одразу» та «Через 1 рік». Усі електронні листи, отримані Delta Chat, будуть видалені з сервера після закінчення цього терміну.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Зауважте, що якщо ви використовуєте Delta Chat на кількох пристроях, вам слід залишити повідомлення на сервері, доки інший пристрій не зможе завантажити їх. У цьому випадку вам слід встановити для автоматичного видалення значення «через день» або щось подібне, залежно від того, як часто ви вмикаєте інший пристрій.</p>
|
||||
<p>Зверніть увагу, що якщо ви використовуєте Delta Chat на декількох пристроях, вам потрібно залишити повідомлення на сервері з достатнім проміжком часу щоб інші пристрої також могли їх завантажити.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -525,14 +591,14 @@ Push-сповіщення автоматично активуються для
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat - це безкоштовний децентралізований месенджер з відкритим вихідним кодом і вільним вибором сервера, але ми хочемо, щоб користувачі отримували надійну “миттєву доставку” повідомлень, як у додатках Whatsapp, Signal або Telegram, без попередніх запитань, які більше підходять для досвідчених користувачів або розробників.</p>
|
||||
<p>Delta Chat - це безкоштовний децентралізований месенджер з відкритим вихідним кодом і вільним вибором сервера, але ми хочемо, щоб користувачі отримували надійну “миттєву доставку” повідомлень, як у додатках WhatsApp, Signal або Telegram, без попередніх запитань, які більше підходять для досвідчених користувачів або розробників.</p>
|
||||
|
||||
<p>Зверніть увагу, що Delta Chat має <a href="#privacy-notifications">невелику систему push-повідомлень, що зберігає конфіденційність</a> яка забезпечує “миттєву доставку” повідомлень на всі chatmail-сервери включаючи потенційний <a href="https://delta.chat/chatmail#selfhosted">який ви можете налаштувати самостійно без нашого дозволу</a>. Ласкаво просимо до потужності сумісної та масової системи чат-пошти та електронної пошти :)</p>
|
||||
|
||||
<h2 id="шифрування-та-безпека">
|
||||
<h2 id="e2ee">
|
||||
|
||||
|
||||
Шифрування та безпека <a href="#шифрування-та-безпека" class="anchor"></a>
|
||||
Шифрування та безпека <a href="#e2ee" class="anchor"></a>
|
||||
|
||||
|
||||
</h2>
|
||||
@@ -545,10 +611,21 @@ Push-сповіщення автоматично активуються для
|
||||
|
||||
</h3>
|
||||
|
||||
<p><a href="https://autocrypt.org">Autocrypt</a> використовується для автоматичного встановлення наскрізного шифрування з контактами та груповими чатами.
|
||||
Autocrypt використовує обмежену і <a href="#openpgp-secure">безпечну підмножину стандарту OpenPGP</a>. Зашифровані наскрізним шифруванням повідомлення позначаються замком <img style="vertical-align:middle; width:1.2em; margin:1px" src="../lock-icon.png" alt="padlock" />.</p>
|
||||
<p>Delta Chat використовує <a href="#openpgp-secure">безпечну підмножину стандарту OpenPGP</a> для забезпечення автоматичного наскрізного шифрування за допомогою цих протоколів:</p>
|
||||
|
||||
<p><a href="https://securejoin.delta.chat/en/latest/new.html">Протоколи Secure-Join</a> використовуються для створення чатів з гарантованим наскрізним шифруванням що захищає від мережевих атак і скомпрометованих серверів. Чати, відмічені зеленою галочкою <img style="vertical-align:middle; width:1.5em; margin:1px" src="../green-checkmark.png" alt="green checkmark" /> гарантують наскрізне шифрування повідомлень.</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Використовуйте <a href="https://securejoin.delta.chat/en/latest/new.html">Secure-Join</a> щоб обмінюватися інформацією про налаштування шифрування, через сканування QR-коду або “посилання-запрошення”.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="https://autocrypt.org">Autocrypt</a> використовується для автоматичного встановлення наскрізного шифрування між контактами і всіма учасниками групового чату.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">Поширення контакту в чаті</a> дозволяє отримувачам використовувати наскрізне шифрування з контактом.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Delta Chat не запитує, не публікує і не взаємодіє з будь-якими серверами ключів OpenPGP.</p>
|
||||
|
||||
<h3 id="whene2e">
|
||||
|
||||
@@ -558,102 +635,70 @@ Autocrypt використовує обмежену і <a href="#openpgp-secure"
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Всі наскрізні зашифровані повідомлення мають навісний замок:</p>
|
||||
|
||||
<p><img style="width:160px; margin:1px" src="../lock-screenshot.png" alt="padlock in bubble" /></p>
|
||||
|
||||
<p>Наскрізне шифрування гарантується, якщо поруч із заголовком чату стоїть зелена галочка:</p>
|
||||
|
||||
<p><img style="width:211px; margin:1px" src="../green-checkmark-screenshot.png" alt="green checkmark in title" /></p>
|
||||
<p>Всі повідомлення в Delta Chat за замовчуванням <strong>наскрізно зашифровані</strong>. Починаючи з версії 2 Delta Chat (липень 2025 року) на наскрізних зашифрованих повідомленнях більше немає замків або інших подібних маркерів.</p>
|
||||
|
||||
<h3 id="howtoe2ee">
|
||||
<h3 id="чи-можу-я-отримувати-та-надсилати-пошту-без-наскрізного-шифрування">
|
||||
|
||||
|
||||
Як отримати гарантоване наскрізне шифрування та зелені галочки? <a href="#howtoe2ee" class="anchor"></a>
|
||||
Чи можу я отримувати та надсилати пошту без наскрізного шифрування? <a href="#чи-можу-я-отримувати-та-надсилати-пошту-без-наскрізного-шифрування" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Якщо у вас є другий канал зв’язку з вашим партнером по чату, наприклад, відеочат або інший месенджер, ви можете згенерувати посилання-запрошення.</p>
|
||||
<p>Якщо ви використовуєте стандартні <a href="https://chatmail.at/relays">ретранслятори чату</a>, неможливо отримувати або надсилати повідомлення без наскрізного шифрування.</p>
|
||||
|
||||
<p>Якщо ви разом особисто, ви можете показати QR-код своєму співрозмовнику.</p>
|
||||
<p>If you instead create a profile using a classic e-mail server,
|
||||
you can send and receive messages with or without end-to-end encryption.
|
||||
Messages lacking end-to-end encryption are marked with an e-mail icon
|
||||
<img style="vertical-align:middle; width:1.2em; margin:1px" src="../email-icon.png" alt="email" />.</p>
|
||||
|
||||
<h3 id="howtoe2ee">
|
||||
|
||||
|
||||
Як створити чат з новим контактом? <a href="#howtoe2ee" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Ви можете надіслати посилання-запрошення через інший приватний чат, показати QR-код запрошення, коли ви знаходитесь поруч один з одним або під час відеодзвінка, або натиснути на “контакт”, яким ви поділилися в чаті.</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>Для <strong>Запрошення до групи</strong>, торкніться назви групи чату, щоб побачити список її учасників, і виберіть “QR-код запрошення”.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Для <strong>прямих запрошень у чат 1:1</strong>, торкніться іконки QR-коду <img style="vertical-align:middle; width:1.8em; margin:1px" src="../qr-icon.png" /> на головному екрані програми Delta Chat.</p>
|
||||
<p>Для <strong>прямих запрошень у чат один-на-один</strong>, торкніться іконки QR-коду <img style="vertical-align:middle; width:1.8em; margin:1px" src="../qr-icon.png" /> на головному екрані програми Delta Chat.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Дозвольте вашому партнеру по чату відсканувати QR-зображення
|
||||
у своєму додатку Delta Chat, або натисніть “Копіювати” чи “Поділитися”, щоб створити посилання-запрошення і поділитися ним з вашим співрозмовником.</p>
|
||||
<p>Попросіть вашого партнера по чату відсканувати QR-зображення у своєму додатку Delta Chat, або натисніть “Копіювати” чи “Поділитися”, щоб створити посилання-запрошення і поділитися ним з вашим партнером по чату.</p>
|
||||
|
||||
<p>Тепер зачекайте, поки між обома пристроями <a href="https://securejoin.delta.chat/en/latest/new.html#setup-contact-protocol">відбудеться обмін мережевими повідомленнями Secure-Join</a>.</p>
|
||||
<p>Тепер зачекайте, поки встановиться <a href="https://securejoin.delta.chat/en/latest/new.html#setup-contact-protocol">наскрізне шифрування</a>.</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>Якщо обидва пристрої онлайн, обидві сторони зрештою побачать (груповий або прямий) чат із зеленою галочкою <img style="vertical-align:middle; width:1.5em; margin:1px" src="../green-checkmark.png" alt="green checkmark" /> поруч із заголовком.</p>
|
||||
<p>Якщо обидві сторони онлайн, вони незабаром побачать (груповий або прямий) чат і можуть почати безпечно обмінюватися повідомленнями.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Якщо один з пристроїв не в мережі, зелені галочки з’являться пізніше, коли пристрій знову буде підключено до Інтернету а мережевий протокол Secure-Join буде завершено.</p>
|
||||
<p>Якщо одна зі сторін перебуває в офлайні або в поганій мережі, можливість спілкуватися в чаті затримується до відновлення з’єднання.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Вітаємо! Тепер ви автоматично використовуватимете гарантоване наскрізне шифрування з цим контактом, і ви обидва можете додавати один одного в групи, позначені зеленою галочкою <img style="vertical-align:middle; width:1.5em; margin:1px" src="../green-checkmark.png" alt="green checkmark" />, таким чином автоматично поширюючи гарантоване наскрізне шифрування серед її членів.</p>
|
||||
<p>Вітаємо!
|
||||
Тепер ви автоматично використовуватимете наскрізне шифрування з цим контактом. Якщо ви додасте один одного в групи чату, наскрізне шифрування буде встановлено між усіма учасниками.</p>
|
||||
|
||||
<h3 id="e2eeguarantee">
|
||||
|
||||
|
||||
Що означає зелена галочка і “гарантоване наскрізне шифрування”? <a href="#e2eeguarantee" class="anchor"></a>
|
||||
Що означає зелена галочка в профілі контакту? <a href="#e2eeguarantee" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Назви чатів із зеленими галочками <img style="vertical-align:middle; width:1.5em; margin:1px" src="../green-checkmark.png" alt="green checkmark" /> означають, що всі повідомлення в чаті будуть наскрізно зашифровані і не можуть бути прочитані або змінені скомпрометованими поштовими серверами або інтернет-провайдерами.
|
||||
Приєднання до групових чатів із зеленими галочками безпечно поширює інформацію про шифрування (і зелені галочки) всіх учасників таким чином, що гарантує наскрізне шифрування в групі та між її учасниками.</p>
|
||||
|
||||
<p>Профілі контактів із зеленими галочками <img style="vertical-align:middle; width:1.5em; margin:1px" src="../green-checkmark.png" alt="green checkmark" /> означають, що повідомлення контакту наразі гарантовано зашифровані наскрізним шифруванням. Кожен контакт із зеленою галочкою або зробив пряме <a href="#howtoe2ee">QR-сканування</a> з вами або був представлений іншим контактом, позначеним зеленою галочкою. Знайомство відбувається автоматично під час додавання учасників до груп. Той, хто додає контакт до групи, позначеної зеленою галочкою, стає представником для тих учасників, які ще не знали про доданий контакт. У профілі контакту ви можете кілька разів натиснути на текст “Представлений …” поки не потрапите до того, з ким ви безпосередньо зробили <a href="#howtoe2ee">QR-сканування</a>.</p>
|
||||
|
||||
<p>Зверніть увагу, що в профілі контакту ви можете бачити і натискати рекомендувачів але в заголовку профілю немає зеленої галочки. Зазвичай це означає, що контакт <a href="#nocryptanymore">“надіслав повідомлення з іншого пристрою”</a>.</p>
|
||||
<p>У профілі контакту може відображатися зелена галочка <img style="vertical-align:middle; width:1.5em; margin:1px" src="../green-checkmark.png" alt="green checkmark" /> і рядок “Представлений”. Кожен контакт із зеленою галочкою або зробив пряме <a href="#howtoe2ee">QR-сканування</a> з вами або був представлений іншим контактом, позначеним зеленою галочкою. Знайомство відбувається автоматично під час додавання учасників до груп. Той, хто додає контакт із зеленою галочкою до групи, в якій є лише учасники із зеленою галочкою стає представником. У профілі контакту ви можете кілька разів натиснути на текст “Представлений …” поки не потрапите до того, з ким ви безпосередньо зробили <a href="#howtoe2ee">QR-сканування</a>.</p>
|
||||
|
||||
<p>Для більш детального обговорення “гарантованого наскрізного шифрування” будь ласка, перегляньте <a href="https://securejoin.delta.chat/en/latest/new.html">Протоколи безпечного приєднання</a> і, зокрема, прочитайте про “Перевірені групи”, технічний термін того, що тут називається чатами з “зеленою галочкою” або “гарантованим наскрізним шифруванням”.</p>
|
||||
|
||||
<h3 id="nocryptanymore">
|
||||
|
||||
|
||||
Контакт “надіслав повідомлення з іншого пристрою”, що робити? <a href="#nocryptanymore" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Ваш чат з контактом втратив гарантоване наскрізне шифрування. Зелену галочку було знято для цього чату та контакту, коли ви побачили це попередження. **Якщо ви вважаєте раптове зникнення гарантованого наскрізного шифрування несподіваним для цього контакту, не приймайте попередження! Замість цього зв’яжіться з контактом через другий канал наприклад, відеодзвінок, інший месенджер або телефонний дзвінок, щоб з’ясувати, що сталося.</p>
|
||||
|
||||
<p>Якщо ваш контакт дійсно спричинив втрату гарантованого наскрізного шифрування будь ласка, зверніться до наступних параграфів, щоб дізнатися про типові причини та способи їх усунення. Незважаючи на це, всі інші чати, позначені зеленою галочкою, залишаються гарантовано зашифрованими з кінця в кінець навіть якщо контакт є їхнім учасником.</p>
|
||||
|
||||
<p><strong>Ваш контакт використовує Delta Chat на другому пристрої (телефоні або ноутбуці)</strong>.</p>
|
||||
|
||||
<p>Якщо у них є інший пристрій із запущеним додатком Delta Chat, вони повинні видалити обліковий запис з нового пристрою і додати його <a href="#multiclient">як другий пристрій, як описано тут</a>. Як тільки вони напишуть вам повідомлення, попередження зникне і гарантоване шифрування буде встановлено на обох пристроях вашого контакту.</p>
|
||||
|
||||
<p><strong>Ваш контакт перевстановив Delta Chat, використовуючи свій старий логін</strong></p>
|
||||
|
||||
<p>Якщо у них є <a href="#backup">файл резервної копії</a>, вони повинні видалити обліковий запис з нового пристрою і, замість цього, імпортувати файл резервної копії для відновлення свого облікового запису. Як тільки вони напишуть вам повідомлення, попередження зникне і гарантоване шифрування буде відновлено для цього контакту.</p>
|
||||
|
||||
<p>Якщо у них немає резервної копії файлу, найкраще виконати <a href="#howtoe2ee">QR-сканування</a> з вашим співрозмовником, щоб відновити гарантоване кінцеве шифрування.</p>
|
||||
|
||||
<p><strong>Ваш контакт надіслав листа через веб-інтерфейс або іншу поштову програму і незабаром повернеться до використання Delta Chat.</strong></p>
|
||||
|
||||
<p>Якщо ви впевнені, що контакт іноді користується веб-поштою, або іншу поштову програму без наскрізного шифрування, ви можете прийняти попередження. Як тільки ваш контакт знову скористається Delta Chat, гарантоване наскрізне шифрування буде автоматично відновлено.</p>
|
||||
|
||||
<p><strong>Ваш контакт повністю припинив користуватися Delta Chat</strong>.</p>
|
||||
|
||||
<p>Іноді залишатися на зв’язку важливіше, ніж наскрізне шифрування. <a href="#tls">“Шифрування на транспортному рівні” (TLS)</a> все ще може ефективно захищати конфіденційність ваших повідомлень між вашим пристроєм і сервером електронної пошти. Але без наскрізного шифрування ви та ваш контакт довіряєте серверу електронної пошти не читати і не маніпулювати вашими повідомленнями, а також не передавати їх третім особам.</p>
|
||||
|
||||
<p>У будь-якому випадку, ви не можете зробити нічого іншого, окрім як прийняти попередження. Будь ласка, також видаліть контакт з будь-якої активної групи, позначеної зеленою галочкою які ви можете знайти в розділі “Загальні чати” в профілі контакту. Це позбавить ваш контакт від отримання “нечитабельних” повідомлень.</p>
|
||||
|
||||
<p>Якщо контакт видалив Delta Chat через помилки або небажану поведінку, будь ласка, розгляньте можливість написати повідомлення на нашому <a href="https://support.delta.chat">форумі підтримки</a> щоб допомогти нам виявити та вирішити загальні проблеми. Дякуємо!</p>
|
||||
|
||||
<h3 id="чи-зашифровані-наскрізно-вкладення-зображення-файли-аудіо-тощо">
|
||||
|
||||
|
||||
@@ -674,9 +719,12 @@ Autocrypt використовує обмежену і <a href="#openpgp-secure"
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Так, Delta Chat використовує безпечну підмножину OpenPGP і відображає індикатор безпеки у вигляді замка в повідомленні лише тоді, коли все повідомлення належним чином зашифровано і підписано. Наприклад, “Відокремлені підписи” не вважаються безпечними.</p>
|
||||
<p>Так, Delta Chat використовує безпечну підмножину OpenPGP яка вимагає, щоб все повідомлення було належним чином зашифровано і підписано. Наприклад, “відокремлені підписи” не вважаються безпечними.</p>
|
||||
|
||||
<p>OpenPGP не є небезпечним сам по собі. Більшість публічно обговорюваних проблем безпеки OpenPGP насправді виникають через погане використання або погану реалізацію інструментів або програм (або і те, і інше). Особливо важливо розрізняти OpenPGP, стандарт шифрування IETF, і GnuPG (GPG), інструментом командного рядка, що реалізує OpenPGP. Багато публічних критиків OpenPGP насправді обговорюють GnuPG, який Delta Chat ніколи не використовував. Натомість Delta Chat використовує реалізацію OpenPGP на Rust <a href="https://github.com/rpgp/rpgp">rPGP</a>, доступну як <a href="https://crates.io/crates/pgp">незалежний пакет “pgp”</a>, і <a href="https://delta.chat/assets/blog/2019-first-security-review.pdf">пройшов аудит безпеки у 2019 році</a>.</p>
|
||||
<p>OpenPGP сам по собі не є небезпечним. Більшість публічно обговорюваних проблем безпеки OpenPGP насправді виникають через недостатню зручність використання або погану реалізацію інструментів чи програм (або обох).
|
||||
Особливо важливо розрізняти OpenPGP, стандарт шифрування IETF, і GnuPG (GPG), інструмент командного рядка, що реалізує OpenPGP.
|
||||
Багато публічних критиків OpenPGP насправді обговорюють GnuPG, який Delta Chat ніколи не використовував.
|
||||
Delta Chat скоріше використовує реалізацію OpenPGP Rust <a href="https://github.com/rpgp/rpgp">rPGP</a>, доступну як <a href="https://crates.io/crates/pgp">незалежний пакет «pgp»</a> і <a href="#security-audits">перевірку безпеки в 2019 і 2024 роках</a>.</p>
|
||||
|
||||
<p>Ми прагнемо, разом з іншими розробниками OpenPGP, подальше покращення характеристик безпеки шляхом впровадження <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/">нового IETF OpenPGP Crypto-Refresh</a> який, на щастя, був прийнятий влітку 2023 року.</p>
|
||||
|
||||
@@ -704,19 +752,17 @@ Autocrypt використовує обмежену і <a href="#openpgp-secure"
|
||||
|
||||
<p>Delta Chat також ніколи не був вразливим до EFAIL-атаки “Пряма ексфільтрація” тому що він розшифровує лише “багатокомпонентні/зашифровані” повідомлення, які містять рівно одну зашифровану і підписану частину, як визначено специфікацією Autocrypt Level 1.</p>
|
||||
|
||||
<h3 id="tls">
|
||||
<h3 id="чи-повідомлення-позначені-значком-пошти-доступні-в-інтернетіtls">
|
||||
|
||||
|
||||
Чи буде показано повідомлення відкритим текстом, якщо наскрізне шифрування недоступне? <a href="#tls" class="anchor"></a>
|
||||
Чи повідомлення, позначені значком пошти, доступні в Інтернеті?{#tls} <a href="#чи-повідомлення-позначені-значком-пошти-доступні-в-інтернетіtls" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Навіть якщо ваші повідомлення не гарантовано зашифровані наскрізним шифруванням, вони все одно захищені від інтернет-провайдерів, таких як мобільні або кабельні компанії. Однак, ваші провайдери та провайдери електронної пошти одержувача можуть читати, аналізувати або навіть змінювати ваші повідомлення, включаючи будь-які вкладення, якщо вони не зашифровані наскрізним шифруванням.</p>
|
||||
<p>Якщо ви надсилаєте або отримуєте електронні листи без наскрізного шифрування (використовуючи класичний сервер електронної пошти), вони все одно захищені від мобільних або кабельних компаній, які не можуть читати чи змінювати ваші повідомлення. Однак як ваш, так і поштовий провайдер одержувача можуть читати, аналізувати або змінювати ваші листи, включаючи будь-які вкладення.</p>
|
||||
|
||||
<p>За замовчуванням Delta Chat використовує суворе <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS-шифрування</a> яке захищає з’єднання між вашим пристроєм і провайдером електронної пошти. Вся робота з TLS-шифруванням Delta Chat пройшла незалежний <a href="#security-audits">аудит безпеки</a>. Крім того, з’єднання між вашим провайдером та провайдером електронної пошти одержувача зазвичай також шифрується при передачі даних. Якщо задіяні поштові сервери підтримують <a href="https://datatracker.ietf.org/doc/html/rfc8461">MTA-STS</a> то між провайдерами електронної пошти буде застосовуватися транспортне шифрування в цьому випадку повідомлення Delta Chat ніколи не будуть доступні в Інтернеті у вигляді відкритого тексту навіть якщо повідомлення не було наскрізь зашифровано.</p>
|
||||
|
||||
<p>Зверніть увагу, що <a href="#howtoe2ee">підтримка гарантованого наскрізного шифрування</a>, на додаток до шифрування TLS, забезпечує повну безпеку між вашим пристроєм і пристроєм одержувача. Навіть ваш провайдер електронної пошти або інтернет-провайдер не зможуть прочитати або змінити ваші повідомлення.</p>
|
||||
|
||||
<h3 id="message-metadata">
|
||||
|
||||
@@ -726,20 +772,23 @@ Autocrypt використовує обмежену і <a href="#openpgp-secure"
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat захищає більшість метаданих повідомлень, поміщаючи наступну інформацію в наскрізно зашифровану частину повідомлень:</p>
|
||||
<p>На відміну від більшості інших месенджерів, додатки Delta Chat не зберігають жодних метаданих про контакти чи групи на серверах, навіть у зашифрованому вигляді. Натомість усі метадані груп наскрізно зашифровані та зберігаються виключно на пристроях користувачів.</p>
|
||||
|
||||
<p>Тому поштові сервери можуть бачити лише</p>
|
||||
|
||||
<ul>
|
||||
<li>Тема повідомлення</li>
|
||||
<li>Аватар та назва групи</li>
|
||||
<li>Запити MDN (підтвердження прочитання) (<code class="language-plaintext highlighter-rouge">Chat-Disposition-Notification-To</code>)</li>
|
||||
<li>Таймер зникнення повідомлень (<code class="language-plaintext highlighter-rouge">Ephemeral-Timer</code>)</li>
|
||||
<li><code class="language-plaintext highlighter-rouge">Chat-Group-Member-Removed</code>, <code class="language-plaintext highlighter-rouge">Chat-Group-Member-Added</code>, <code class="language-plaintext highlighter-rouge">Chat-Group-Member-Added</code>, <code class="language-plaintext highlighter-rouge">Chat-Group-Member-Removed-Member-Added</code>.</li>
|
||||
<li>Заголовок <code class="language-plaintext highlighter-rouge">Secure-Join</code>, що містить команди безпечного приєднання</li>
|
||||
<li>Сповіщення про увімкнення потокового передавання місцезнаходження</li>
|
||||
<li>URL-адреса кімнати WebRTC</li>
|
||||
<li>
|
||||
<p>дату повідомлення,</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>адреси відправника та одержувача</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>і розмір повідомлення.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Поштові сервери не мають доступу до цих захищених метаданих але вони бачать дату і розмір повідомлення, і, що важливіше, адреси відправника та отримувача. Поштовим серверам потрібні адреси одержувачів, щоб маршрутизувати і доставляти повідомлення на пристрої одержувача.</p>
|
||||
<p>Усі інші метадані повідомлень, контактів і груп містяться в наскрізно зашифрованій частині повідомлень.</p>
|
||||
|
||||
<h3 id="device-seizure">
|
||||
|
||||
@@ -749,62 +798,21 @@ Autocrypt використовує обмежену і <a href="#openpgp-secure"
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Як для захисту від поштових серверів, що збирають метадані, так і для захисту від загрози вилучення пристрою ми рекомендуємо використовувати оптимізований для Delta Chat <a href="https://delta.chat/serverguide">екземпляр поштового сервера</a> для створення псевдонімних тимчасових акаунтів за допомогою сканування QR-коду. Зверніть увагу, що додатки Delta Chat на всіх платформах підтримують кілька облікових записів тому ви можете легко використовувати “1-тижневі” або “1-місячні” акаунти поруч з вашим “основним” акаунтом знаючи, що всі дані тимчасових акаунтів разом з усіма метаданими будуть видалені. Більше того, якщо пристрій вилучається, то контакти, які використовують тимчасові електронні скриньки, не можуть бути легко ідентифіковані, на відміну від месенджерів, які розкривають телефонні номери в групах чату, які, в свою чергу, часто пов’язані з юридичними особами.</p>
|
||||
<p>Для захисту як від серверів електронної пошти, що збирають метадані, так і від загрози вилучення пристрою, ми рекомендуємо використовувати <a href="https://chatmail.at/relays">чатмейл-релей</a> для створення чат-профілів із випадковими адресами електронної пошти для передавання повідомлень. Зверніть увагу, що додатки Delta Chat на всіх платформах підтримують кілька профілів, тому ви можете легко користуватися профілями, створеними для конкретних ситуацій, поруч із вашим «основним» профілем, з упевненістю, що всі їхні дані, включно з усіма метаданими, буде видалено. Більше того, у разі вилучення пристрою ідентифікувати чат-контакти, які використовують короткочасні профілі, буде вкрай складно.</p>
|
||||
|
||||
<h3 id="як-перевірити-інформацію-про-шифрування">
|
||||
<h3 id="sealedsender">
|
||||
|
||||
|
||||
Як перевірити інформацію про шифрування? <a href="#як-перевірити-інформацію-про-шифрування" class="anchor"></a>
|
||||
Чи підтримує Delta Chat функцію “Запечатаний відправник”? <a href="#sealedsender" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Ви можете перевірити стан наскрізного шифрування вручну в діалоговому вікні “Шифрування” (профіль користувача на Android/iOS або клацніть правою кнопкою миші на елементі списку чату користувача на робочому столі). Delta Chat показує там два відбитки. Якщо на вашому пристрої та пристрої вашого співрозмовника з’являються однакові відбитки, з’єднання безпечне.</p>
|
||||
|
||||
<h3 id="як-перевірити-стан-шифрування-повідомлень">
|
||||
|
||||
|
||||
Як перевірити стан шифрування повідомлень? <a href="#як-перевірити-стан-шифрування-повідомлень" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
<p>Ні, поки ще ні.</p>
|
||||
|
||||
<p>Маленький <strong>замок</strong> в бульбашці повідомлення позначає, що повідомлення було належним чином наскрізно зашифроване від відправника. Якщо <strong>немає замка</strong>, повідомлення не було належним чином наскрізно зашифровано, найімовірніше, тому що відправник використовує додаток або інтерфейс веб-пошти без підтримки кінцевого шифрування.</p>
|
||||
|
||||
<h3 id="чому-я-бачу-незашифровані-повідомлення">
|
||||
|
||||
|
||||
Чому я бачу незашифровані повідомлення? <a href="#чому-я-бачу-незашифровані-повідомлення" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
<p>Месенджер Signal запровадив <a href="https://signal.org/blog/sealed-sender/">“Запечатаного відправника” у 2018 році</a> щоб їхня серверна інфраструктура не знала, хто надсилає повідомлення певній групі одержувачів. Це особливо важливо, оскільки сервер Signal знає номер мобільного телефону кожного акаунта, який зазвичай асоціюється з паспортними даними.</p>
|
||||
|
||||
<p>Якщо контакт використовує поштову програму, яка не підтримує Autocrypt всі повідомлення за участю цього контакту (в групі або чаті 1:1) не будуть наскрізно зашифровані, а отже, не показуватимуть “висячого замка” з повідомленнями. Зверніть увагу, що навіть якщо ваші контакти використовують Delta Chat у своєму акаунті, вони також можуть використовувати програму електронної пошти без функції шифрування, що може спричинити періодичні незашифровані повідомлення. Відповідати незашифрованими на незашифровані повідомлення вимагає Autocrypt щоб запобігти отриманню нечитабельних повідомлень на стороні ваших контактів та їхніх поштових програм, які не підтримують Autocrypt.</p>
|
||||
|
||||
<h3 id="як-я-можу-отримати-наскрізно-зашифрований-чат-з-контактом-delta-chat-який-іноді-користується-веб-поштою-або-іншим-додатком-електронної-пошти-що-не-підтримує-autocrypt">
|
||||
|
||||
|
||||
Як я можу отримати наскрізно зашифрований чат з контактом Delta Chat, який іноді користується веб-поштою або іншим додатком електронної пошти, що не підтримує Autocrypt? <a href="#як-я-можу-отримати-наскрізно-зашифрований-чат-з-контактом-delta-chat-який-іноді-користується-веб-поштою-або-іншим-додатком-електронної-пошти-що-не-підтримує-autocrypt" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Якщо вам потрібен безпечний наскрізно зашифрований чат з контактом який використовує свій акаунт як у Delta Chat, так і в інших додатках (наприклад, веб-пошті), що не підтримують автошифрування, найкраще налаштувати <a href="#howtoe2ee">гарантоване наскрізне шифрування з ними</a>, а потім створити груповий чат з гарантованим наскрізним шифруванням, учасниками якого будете ви двоє. У цьому груповому чаті всі повідомлення будуть наскрізно зашифровані навіть якщо в прямому чаті між вами буде <a href="#nocryptanymore">“… надіслано повідомлення з іншого пристрою”</a>.</p>
|
||||
|
||||
<h3 id="як-забезпечити-наскрізне-шифрування-та-видалення-повідомлень">
|
||||
|
||||
|
||||
Як забезпечити наскрізне шифрування та видалення повідомлень? <a href="#як-забезпечити-наскрізне-шифрування-та-видалення-повідомлень" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Найкращий спосіб забезпечити наскрізне шифрування кожного повідомлення, а метадані видалялися якнайшвидше, це <a href="#howtoe2ee">використання чатів з гарантованим наскрізним шифруванням</a>
|
||||
та увімкнення <a href="#ephemeralmsgs">зникаючих повідомлень</a>.</p>
|
||||
|
||||
<p>Гарантований наскрізно шифрований чат захищає від <a href="https://en.wikipedia.org/wiki/Man-in-the-middle_attack">MITM-атак</a>, а увімкнення функції зникнення повідомлень видаляє повідомлення на сервері через певний час, налаштований користувачем.</p>
|
||||
|
||||
<p>Якщо вам не потрібне більш довготривале зберігання копій ваших повідомлень на сервері, ви також можете увімкнути <a href="#delold">“автоматично видаляти повідомлення з сервера”</a>.</p>
|
||||
<p>Навіть якщо <a href="https://chatmail.at/relays">чат-мейл релей</a> не запитує ніяких приватних даних (в тому числі номерів телефонів), все одно може мати сенс захистити реляційні метадані між адресами. Ми не передбачаємо великих проблем у використанні випадкових одноразових адрес електронної пошти для запечатаних відправлень але реалізація такої можливості ще не є пріоритетною.</p>
|
||||
|
||||
<h3 id="pfs">
|
||||
|
||||
@@ -814,23 +822,33 @@ Autocrypt використовує обмежену і <a href="#openpgp-secure"
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Ні, Delta Chat не підтримує ідеальну пряму секретність (PFS). Це означає, що якщо ваш приватний ключ дешифрування Delta Chat витік, і хтось зібрав ваші попередні транзитні повідомлення, він зможе розшифрувати і прочитати їх за допомогою витоку ключа розшифрування.</p>
|
||||
<p>Ні, поки ще ні.</p>
|
||||
|
||||
<p>Зауважте, однак, що якщо хтось отримає ваші ключі розшифрування, вони, як правило, також зможуть отримати ваші повідомлення, незалежно від того, чи працює Perfect Forward Secrecy чи ні. Типовою реальною ситуацією для витоку ключів дешифрування є вилучення пристрою яку ми обговорюємо в нашій відповіді <a href="#device-seizure">про метадані та вилучення пристрою</a>.</p>
|
||||
<p>Delta Chat наразі не підтримує ідеальну пряму секретність (Perfect Forward Secrecy, PFS). Це означає, що якщо ваш приватний ключ для розшифрування буде скомпрометовано, а хтось заздалегідь зібрав ваші повідомлення під час передачі, він зможе розшифрувати та прочитати їх, використовуючи зламаний ключ. Зверніть увагу, що пряма секретність підвищує рівень безпеки лише в тому разі, якщо ви видаляєте повідомлення. Інакше, якщо хтось отримує доступ до ваших ключів розшифрування, він зазвичай також має доступ до всіх ваших невидалених повідомлень і навіть не потребує розшифровувати заздалегідь перехоплені дані.</p>
|
||||
|
||||
<p>Можливо, що Delta Chat розвивається для підтримки Perfect Forward Secrecy, тому що OpenPGP - це лише контейнер для зашифрованих повідомлень, але керування ключами шифрування (і, відповідно, ротація ключів або “храповик” ключів) може бути організоване у гнучкий спосіб. Дивіться <a href="https://gitlab.com/sequoia-pgp/openpgp-dr">Прототип PFS від Seqouia</a> щодо існуючих експериментів у спільноті реалізаторів OpenPGP.</p>
|
||||
<p>Ми розробили підхід Forward Secrecy, який витримав початкову експертизу від деяких криптографів та експертів з реалізації але чекає на більш офіційний звіт щоб переконатися, що він надійно працює в об’єднаних системах обміну повідомленнями та при використанні декількох пристроїв, перш ніж його можна буде реалізувати в <a href="https://github.com/chatmail/core">ядрі чату</a>, що зробить його доступним у всіх <a href="https://chatmail.at/clients">клієнтах чату</a>.</p>
|
||||
|
||||
<h3 id="чи-є-наскрізне-шифрування-delta-chat-таким-же-безпечним-як-signal">
|
||||
<h3 id="pqc">
|
||||
|
||||
|
||||
Чи є наскрізне шифрування Delta Chat таким же безпечним, як Signal? <a href="#чи-є-наскрізне-шифрування-delta-chat-таким-же-безпечним-як-signal" class="anchor"></a>
|
||||
Чи підтримує Delta Chat пост-квантову криптографію? <a href="#pqc" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Це залежить від того, що для вас важливо. Delta Chat <a href="#pfs">не підтримує PFS</a>, як Signal але він забезпечує <a href="#e2eeguarantee">гарантовано наскрізні зашифровані чати</a> які захищені від скомпрометованих серверів або пошкоджених мереж. Signal та більшість інших месенджерів, що підтримують PFS, не надають практичної схеми захисту чат-груп від мережевих атак які, можливо, викликають більше занепокоєння ніж потенційний зловмисник, який заволодіє вашим телефоном і приватними налаштуваннями шифрування, але чомусь не ваші повідомлення, але має повний запис усіх минулих зашифрованих повідомлень.</p>
|
||||
<p>Ні, поки ще ні.</p>
|
||||
|
||||
<p>У будь-якому випадку, наскрізне шифрування Delta Chat використовує <a href="#openpgp-secure">безпечну підмножину OpenPGP</a> який пройшов <a href="https://delta.chat/assets/blog/2019-first-security-review.pdf">незалежний аудит безпеки</a>.</p>
|
||||
<p>Delta Chat використовує бібліотеку Rust OpenPGP <a href="https://github.com/rpgp/rpgp">rPGP</a> яка підтримує останню версію <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>. Ми плануємо додати підтримку PQC у <a href="https://github.com/chatmail/core">chatmail core</a> після того, як проект буде завершено у IETF у співпраці з іншими розробниками OpenPGP.</p>
|
||||
|
||||
<h3 id="як-я-можу-вручну-перевірити-інформацію-про-шифрування">
|
||||
|
||||
|
||||
Як я можу вручну перевірити інформацію про шифрування? <a href="#як-я-можу-вручну-перевірити-інформацію-про-шифрування" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Ви можете перевірити стан наскрізного шифрування вручну в діалоговому вікні “Шифрування” (профіль користувача на Android/iOS або клацніть правою кнопкою миші на елементі списку чату користувача на робочому столі). Delta Chat показує там два відбитки. Якщо на вашому пристрої та пристрої вашого співрозмовника з’являються однакові відбитки, з’єднання безпечне.</p>
|
||||
|
||||
<h3 id="importkey">
|
||||
|
||||
@@ -840,29 +858,11 @@ Autocrypt використовує обмежену і <a href="#openpgp-secure"
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Так.
|
||||
The best way is to send an Autocrypt Setup Message from the other e-mail client.
|
||||
Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the settings of the other client and follow the instructions shown there.</p>
|
||||
<p>Ні.</p>
|
||||
|
||||
<p>Крім того, ви можете імпортувати ключ вручну в “Налаштування -> Додаткові налаштування -> Імпорт секретних ключів”. Застереження: Переконайтеся, що ключ не захищено паролем, або видаліть його заздалегідь.</p>
|
||||
<p>Delta Chat генерує безпечні ключі OpenPGP відповідно до специфікації Autocrypt 1.1. Ви можете експортувати свій приватний ключ, але не можете імпортувати додаткові приватні ключі.</p>
|
||||
|
||||
<p>Якщо у вас немає ключа або ви навіть не знаєте, що він вам знадобиться – не хвилюйтеся: Delta Chat генерує ключі в міру необхідності, вам не потрібно натискати кнопку для цього.</p>
|
||||
|
||||
<h3 id="я-не-можу-імпортувати-свій-існуючий-pgp-ключ-у-delta-chat">
|
||||
|
||||
|
||||
Я не можу імпортувати свій існуючий PGP ключ у Delta Chat. <a href="#я-не-можу-імпортувати-свій-існуючий-pgp-ключ-у-delta-chat" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Найімовірнішою причиною є те, що ваш ключ зашифрований та/або використовується пароль. Такі ключі не підтримуються Delta Chat. Ви можете видалити шифрування парольної фрази та пароль і спробувати імпорт знову.</p>
|
||||
|
||||
<p>Ще одна поширена помилка – неправильне закінчення файлу. Використовуйте захищений формат ASCII і закінчення файлу <code class="language-plaintext highlighter-rouge">.asc</code>.</p>
|
||||
|
||||
<p>Delta Chat підтримує поширені формати приватних ключів OpenPGP, однак навряд чи приватні ключі з усіх джерел будуть повністю підтримуватися. Це не головна мета Delta Chat. Фактично, більшість нових користувачів не матимуть жодного ключа до використання Delta Chat. Однак ми намагаємось підтримувати приватні ключі з якомога більшої кількості джерел.</p>
|
||||
|
||||
<p>Видалення пароля з приватного ключа буде залежати від програмного забезпечення, яке використовується для управління ключами PGP. За допомогою Enigmail ви можете встановити для порожній пароль у вікні Керування ключами. За допомогою GnuPG ви можете встановити його <a href="https://github.com/deltachat/deltachat-android/issues/98#issuecomment-378383429">через командний рядок</a>. Для інших програм ви зможете знайти рішення в інтернеті.</p>
|
||||
<p>Загалом, ми не рекомендуємо і не пропонуємо користувачам керувати ключами вручну. Ми хочемо гарантувати, що аудит безпеки може зосередитися на декількох перевірених криптографічних алгоритмах а не на всій широті можливих алгоритмів, дозволених у OpenPGP.</p>
|
||||
|
||||
<h3 id="security-audits">
|
||||
|
||||
@@ -872,7 +872,17 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Проект Delta Chat пройшов чотири незалежні аудити безпеки та один незалежний аналіз безпеки, від найновіших до найстаріших:</p>
|
||||
<p>Так, багаторазово. Проект Delta Chat постійно проходить незалежні перевірки та аналіз безпеки, а саме, від останніх і закінчуючи старішими:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>2024 грудня відбулася <a href="https://github.com/rpgp/docs/blob/main/audits/NGI%20Core%20rPGP%20penetration%20test%20report%202024%201.0.pdf">оцінка rPGP на замовлення NLNET</a> від <a href="https://www.radicallyopensecurity.com/">Radically Open Security</a>. rPGP слугує рушієм наскрізного шифрування <a href="https://openpgp.org">OpenPGP</a> для Delta Chat. За результатами цього аудиту було випущено дві рекомендації:</p>
|
||||
</li>
|
||||
<li><a href="https://github.com/rpgp/rpgp/security/advisories/GHSA-9rmp-2568-59rv">“Паніка через неправильне ненадійне введення”</a> CVE-2024-53856</li>
|
||||
<li><a href="https://github.com/rpgp/rpgp/security/advisories/GHSA-4grw-m28r-q285">“Потенційне виснаження ресурсів під час обробки ненадійних повідомлень”</a> CVE-2024-53857</li>
|
||||
</ul>
|
||||
|
||||
<p>Проблеми, описані в цих рекомендаціях, виправлено, і вони є частиною випусків Delta Chat у всіх магазинах додатків з грудня 2024 року.</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
@@ -908,7 +918,7 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Так. Delta Chat 1.36 comes with a new, experimental function for using the same profile on different devices:</p>
|
||||
<p>Так. You can use the same profile on different devices:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
@@ -957,6 +967,9 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
<li>
|
||||
<p><strong>Гостьові мережі</strong> можуть не дозволяти пристроям зв’язуватися один з одним. Якщо можливо, використовуйте негостьову мережу.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Якщо у вас все ще виникають проблеми з використанням однієї мережі, спробуйте відкрити <strong>Мобільну точку доступу</strong> на одному пристрої та приєднатися до цього Wi-Fi з іншого</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Переконайтеся, що на цільовому пристрої <strong>достатньо пам’яті</strong></p>
|
||||
</li>
|
||||
@@ -964,7 +977,7 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
<p>Якщо передача почалася, переконайтеся, що пристрої <strong>залишаються активними</strong> і не засинають. Не виходьте з Delta Chat. (ми докладаємо всіх зусиль, щоб програма працювала у фоновому режимі, але <a href="https://dontkillmyapp.com">системи, як правило, вбивають програми</a>, на жаль)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Delta Chat <strong>уже ввійшов</strong> на цільовому пристрої? Ви можете використовувати кілька облікових записів на одному пристрої, просто <a href="#multiple-accounts">додайте інший обліковий запис</a></p>
|
||||
<p>Delta Chat <strong>вже зареєстрований</strong> на цільовому пристрої? Ви можете використовувати кілька профілів на одному пристрої, просто <a href="#multiple-accounts">додайте ще один профіль</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Якщо у вас усе ще виникають проблеми або якщо ви <strong>не можете відсканувати QR-код</strong> спробуйте <strong>перенесення вручну</strong>, описане нижче</p>
|
||||
@@ -982,15 +995,26 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
<p>Цей спосіб рекомендований, лише якщо «Додати другий пристрій», як описано вище, не працює.</p>
|
||||
|
||||
<ul>
|
||||
<li>На старому пристрої перейдіть до “Налаштування -> Чати та медіа -> Експортувати резервну копію”. Введіть свій PIN-код, графічний ключ або пароль розблокування екрана. Потім ви можете натиснути на “Почати Резервне копіювання”. Це збереже файл резервної копії на вашому пристрої. Тепер вам потрібно якось перенести його на інший пристрій.</li>
|
||||
<li>На новому пристрої на екрані входу замість того, щоб увійти до свого облікового запису електронної пошти, виберіть “Імпортувати резервну копію” виберіть “Імпортувати резервну копію”. Після імпорту ваші листування, ключі шифрування ключі шифрування та медіа повинні бути скопійовані на новий пристрій.
|
||||
<ul>
|
||||
<li>Якщо ви користуєтеся iOS:** і у вас виникли труднощі, можливо <a href="https://support.delta.chat/t/import-backup-to-ios/1628">цей посібник</a> допоможе вам.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Тепер ви синхронізовані і можете використовувати обидва пристрої для надсилання та отримання наскрізно зашифрованих повідомлень зі своїми партнерами по спілкуванню.</li>
|
||||
<li>На старому пристрої перейдіть до “Налаштування -> Чати та медіа -> Експортувати резервну копію”. Введіть свій PIN-код, графічний ключ або пароль розблокування екрана. Потім ви можете натиснути на “Почати резервне копіювання”. Це збереже файл резервної копії на вашому пристрої. Тепер вам потрібно якось перенести його на інший пристрій.</li>
|
||||
<li>На новому пристрої в меню “У мене вже є профіль” виберіть “Відновити з резервної копії”. Після імпорту ваші розмови, ключі шифрування ключі шифрування та медіа повинні бути скопійовані на новий пристрій.</li>
|
||||
<li>Якщо ви користуєтеся iOS:** і у вас виникли труднощі, можливо <a href="https://support.delta.chat/t/import-backup-to-ios/1628">цей посібник</a> допоможе вам.</li>
|
||||
<li>Тепер ви синхронізовані і можете використовувати обидва пристрої для надсилання та отримання наскрізних зашифрованих повідомлень зі своїми партнерами по спілкуванню.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="чи-синхронізовані-видалення-закріплення-архівування-збереження-вимкнення-сповіщень-тощо-з-усіма-пристроями">
|
||||
|
||||
|
||||
Чи синхронізовані видалення, закріплення, архівування, збереження, вимкнення сповіщень тощо з усіма пристроями? <a href="#чи-синхронізовані-видалення-закріплення-архівування-збереження-вимкнення-сповіщень-тощо-з-усіма-пристроями" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Так.</p>
|
||||
|
||||
<p>Якщо ви, наприклад, видалите чат або повідомлення з телефону, воно буде видалено на вашому робочому столі, щойно ви знову з’явитесь в мережі.</p>
|
||||
|
||||
<p>Винятком є <a href="#delold">Видалення старих повідомлень з пристрою</a>, оскільки різні пристрої можуть мати різний обсяг пам’яті, а також системні налаштування звуків сповіщень.</p>
|
||||
|
||||
<h3 id="чи-планується-впровадження-веб-клієнта-delta-chat">
|
||||
|
||||
|
||||
@@ -1025,9 +1049,12 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
|
||||
<ul>
|
||||
<li>Додатки webxdc не можуть надсилати дані в Інтернет або завантажувати щось.</li>
|
||||
<li>Програма webxdc може обмінюватися даними лише в межах чату Delta Chat з її копіями на пристроях ваших партнерів по чату. В іншому, вони повністю ізольовані від Інтернету.</li>
|
||||
<li>Програма webxdc може обмінюватися даними лише в межах чату Delta Chat з її копіями на пристроях ваших партнерів по чату. В іншому, вона повністю ізольована від Інтернету.</li>
|
||||
<li>Конфіденційність, яку пропонує додаток webxdc, є конфіденційністю вашого чату - доки ви довіряєте людям, з якими ви спілкуєтеся, ви можете довіряти і додатку webxdc.</li>
|
||||
<li>Це також означає, що відкриття додатків webxdc в чатах, де ви не довіряєте учасникам, як ви знаєте з вкладень в електронній пошті, де ви відкриваєте вкладення тільки від відправників, яким довіряєте, а не від спамерів.</li>
|
||||
<li>Це також означає, що відкриття додатків у чатах з користувачами, яким ви не довіряєте, може становити ризик для конфіденційності.
|
||||
Так само, як і з вкладеннями електронної пошти, відеодзвінками або звичайними посиланнями:
|
||||
відкривайте їх лише від відправників, яким ви довіряєте, а не від спамерів.
|
||||
Спамери можуть дізнатися будь-які дані, які ви їм надсилаєте, а також вашу IP-адресу.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="де-я-можу-отримати-webxdc-додатки">
|
||||
@@ -1095,13 +1122,9 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>За допомогою списку розсилки ви можете надіслати повідомлення багатьом одержувачам одночасно; коли вони відповідають вам, ви отримуєте відповідь у своєму прямому чаті 1:1 з ними. Одержувачі не бачать один одного.</li>
|
||||
<li>Технічно це електронний лист із багатьма одержувачами в BCC.</li>
|
||||
<li>Ви можете ввімкнути функцію в розділі «експериментальні функції» в розширених налаштуваннях. Потім ви можете створити список трансляцій у діалоговому вікні «Новий чат».</li>
|
||||
<li>Якщо ви використовуєте більше ніж один пристрій, списки трансляцій наразі не синхронізуються між ними.</li>
|
||||
<li>Повідомлення, надіслані до списків розсилки, не шифруються. Шифрування порушило б анонімність, оскільки тоді всі одержувачі знали б, хто ще його отримав (надсилання окремих листів усім було б гіршим через обмеження швидкості та споживання мережі).</li>
|
||||
</ul>
|
||||
<p>За допомогою списку розсилки ви можете надіслати повідомлення одразу багатьом одержувачам;
|
||||
одержувачі не можуть відповідати у цьому списку.
|
||||
Списки розсилки все ще дуже експериментальні і, ймовірно, будуть замінені чимось іншим, слідкуйте за новинами :)</p>
|
||||
|
||||
<h3 id="як-я-можу-поділитися-своїм-місцезнаходженням-зі-своїми-співрозмовниками-в-чаті">
|
||||
|
||||
@@ -1139,17 +1162,22 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
|
||||
<p>У цьому випадку Delta Chat не потрібно стежити за папкою «Вхідні», а достатньо лише стежити за папкою DeltaChat.</p>
|
||||
|
||||
<h3 id="як-я-можу-змінити-мій-обліковий-запис-на-іншу-адресу-електронної-пошти">
|
||||
<h3 id="як-я-можу-використовувати-іншу-адресу-електронної-пошти-у-своєму-профілі">
|
||||
|
||||
|
||||
Як я можу змінити мій обліковий запис на іншу адресу електронної пошти? <a href="#як-я-можу-змінити-мій-обліковий-запис-на-іншу-адресу-електронної-пошти" class="anchor"></a>
|
||||
Як я можу використовувати іншу адресу електронної пошти у своєму профілі? <a href="#як-я-можу-використовувати-іншу-адресу-електронної-пошти-у-своєму-профілі" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Примітка:
|
||||
Зміна адреси електронної пошти тимчасово відключена
|
||||
через постійні зміни в ядрі DeltaChat.
|
||||
Ця функція буде знову доступна через кілька місяців.</p>
|
||||
|
||||
<ol>
|
||||
<li>
|
||||
<p>Змініть свою адресу в розділі “Налаштування → Додатково → Пароль та обліковий запис” та введіть пароль вашого нового облікового запису (і, за необхідності, налаштування сервера). Ви отримаєте інформаційне повідомлення про те, що ви переїжджаєте на нову адресу. Додаткове повідомлення також з’явиться у вашому чаті “Повідомлення пристрою”.</p>
|
||||
<p>Змініть свою адресу в розділі “Налаштування → Додатково → Пароль та обліковий запис” та введіть пароль вашого нового поштового акаунта (і, за необхідності, налаштування сервера). Ви отримаєте інформаційне повідомлення про те, що ви переїжджаєте на нову адресу. Додаткове повідомлення також з’явиться у вашому чаті “Повідомлення пристрою”.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Якщо можливо, нехай ваш старий провайдер електронної пошти пересилає всі повідомлення на вашу нову адресу.</p>
|
||||
@@ -1236,17 +1264,40 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
Проте для коректної роботи деяких провайдерів потрібні специфічні налаштування. Дивіться наш <a href="https://providers.delta.chat">огляд провайдерів</a></li>
|
||||
</ul>
|
||||
|
||||
<h3 id="я-хочу-керувати-власним-сервером-електронної-пошти-для-delta-chat-що-ви-порадите">
|
||||
<h3 id="can-i-use-delta-chat-in-parallel-with-other-e-mail-apps">
|
||||
|
||||
|
||||
Я хочу керувати власним сервером електронної пошти для Delta Chat. Що ви порадите? <a href="#я-хочу-керувати-власним-сервером-електронної-пошти-для-delta-chat-що-ви-порадите" class="anchor"></a>
|
||||
Can I use Delta Chat in parallel with other E-Mail apps? <a href="#can-i-use-delta-chat-in-parallel-with-other-e-mail-apps" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Yes, but it is not recommended.</p>
|
||||
|
||||
<p>Parallel usage with the same E-Mail address might lead to the following inconveniences:</p>
|
||||
|
||||
<ul>
|
||||
<li>Next to your Delta Chat notifications,
|
||||
you might also get notifications for (encrypted, so unreadable) chat mail messages
|
||||
in your other email app</li>
|
||||
<li>If Delta Chat is not running
|
||||
or “Advanced > Move automatically to the DeltaChat Folder” is disabled,
|
||||
encrypted Delta Chat messages might clutter the Inbox in your other E-Mail apps.</li>
|
||||
</ul>
|
||||
|
||||
<p><a href="https://support.delta.chat/t/sieve-rule-to-move-deltachat-mails-to-deltachat-folder/288/10">Sieve rules</a> can solve these problems.</p>
|
||||
|
||||
<h3 id="я-хочу-керувати-власним-сервером-для-delta-chat-що-ви-порекомендуєте">
|
||||
|
||||
|
||||
Я хочу керувати власним сервером для Delta Chat. Що ви порекомендуєте? <a href="#я-хочу-керувати-власним-сервером-для-delta-chat-що-ви-порекомендуєте" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>Більшість поштових серверів працюватимуть добре. Але ми особисто рекомендуємо поєднання mailcow і mailadm, як описано <a href="https://delta.chat/en/2023-01-27-upcoming-mail-server-workshops">у цьому пості</a>.</li>
|
||||
<li>Ви можете знайти <a href="https://delta.chat/uk/serverguide">посібник із встановлення на нашому веб-сайті</a>.</li>
|
||||
<li>Більшість поштових серверів будуть працювати добре. Але ми особисто рекомендуємо використовувати сервер ретрансляції чатової пошти, як описано <a href="https://delta.chat/en/2023-12-13-chatmail">в цій статті</a>.</li>
|
||||
<li>Ви можете знайти <a href="https://github.com/chatmail/relay">посібник зі встановлення на GitHub</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="чому-я-маю-вводити-пароль-до-моєї-електронної-пошти-у-delta-chat-чи-це-безпечно">
|
||||
@@ -1259,7 +1310,7 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
|
||||
<p>Як і інші поштові програми, такі як Thunderbird, K9-Mail або Outlook, програма програма потребує пароля, щоб ви могли використовувати її для надсилання листів. Звичайно, пароль пароль зберігається лише на вашому пристрої. Пароль передається лише вашому поштовому провайдеру (коли ви входите в систему), який і так має доступ до вашої пошти.</p>
|
||||
|
||||
<p>Оскільки Delta Chat має відкритий код, ви можете ознайомитись із <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/src/login_param.rs">початковим кодом</a> якщо бажаєте перевірити що ваші дані для авторизації надійно зберігаються. Ми будемо раді вашим пропозиціям, які зроблять додаток більш безпечним для всіх користувачів.</p>
|
||||
<p>Оскільки Delta Chat має відкритий код, ви можете ознайомитись із <a href="https://github.com/chatmail/core/blob/main/src/login_param.rs">початковим кодом</a> якщо бажаєте перевірити що ваші дані для авторизації надійно зберігаються. Ми будемо раді вашим пропозиціям, які зроблять додаток більш безпечним для всіх користувачів.</p>
|
||||
|
||||
<h3 id="які-повідомлення-відображаються-у-delta-chat">
|
||||
|
||||
@@ -1338,17 +1389,17 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
<p>Деякі люди використовують Delta Chat як звичайний поштовий клієнт і хочуть використовувати папку Вхідні для своєї пошти замість папки DeltaChat. Якщо ви вимкнете “Перегляд папки DeltaChat”, вам слід також вимкнути “переміщення повідомлень чату до DeltaChat”.
|
||||
В іншому випадку видалення повідомлень або налаштування декількох пристроїв може не працювати належним чином.</p>
|
||||
|
||||
<h3 id="чи-сумісний-delta-chat-із-protonmail--tutanota--criptext">
|
||||
<h3 id="чи-сумісний-delta-chat-із-proton-mail--tutanota--criptext">
|
||||
|
||||
|
||||
Чи сумісний Delta Chat із Protonmail / Tutanota / Criptext? <a href="#чи-сумісний-delta-chat-із-protonmail--tutanota--criptext" class="anchor"></a>
|
||||
Чи сумісний Delta Chat із Proton Mail / Tutanota / Criptext? <a href="#чи-сумісний-delta-chat-із-proton-mail--tutanota--criptext" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>Так і ні.</li>
|
||||
<li>Ні, ви не можете використовувати свій обліковий запис Protonmail, Tutanota або Criptext у Delta Chat; вони не пропонують отримання листів через IMAP.</li>
|
||||
<li>Ні, ви не можете використовувати свій обліковий запис Proton Mail, Tutanota або Criptext у Delta Chat; вони не пропонують отримання листів через IMAP.</li>
|
||||
<li>У будь-якому випадку, ви можете використовувати Delta Chat для надсилання повідомлень людям, які користуються Protonmail, Tutanota або Criptext. Ці повідомлення не будуть наскрізно зашифрованими. Наскрізне шифрування, яке пропонують ці провайдери, не є сумісним з <a href="https://autocrypt.org/">Autocrypt</a>, стандартним шифруванням, яке використовує Delta Chat.</li>
|
||||
<li>Delta Chat може наскрізно шифрувати через будь-якого провайдера електронної пошти з будь-якою <a href="https://autocrypt.org/dev-status.html">поштовою програмою з підтримкою автошифрування</a>.</li>
|
||||
</ul>
|
||||
@@ -1380,7 +1431,7 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>Дивіться <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">Стандарти, що використовуються у Delta Chat</a>.</li>
|
||||
<li>Дивіться <a href="https://github.com/chatmail/core/blob/main/standards.md#standards-used-in-delta-chat">Стандарти, що використовуються у Delta Chat</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="де-мої-друзі-можуть-знайти-delta-chat">
|
||||
@@ -1442,7 +1493,7 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
завершення прив’язок Rust/Python та запуск екосистеми чат-ботів.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>У 2021 році ми отримали подальше фінансування ЄС для двох Інтернет-технологій наступного покоління пропозиції, а саме для <a href="https://dapsi.ngi.eu/hall-of-fame/eppd/">EPPD - каталог перенесення електронної пошти</a> (~97 тис. євро) та <a href="https://nlnet.nl/project/EmailPorting/">AEAP - перенесення електронної адреси</a> (~90 тис. євро), що призвело до кращої підтримки кількох облікових записів, покращеного QR-коду контактів і налаштувань груп, а також багатьох покращень роботи мережі на всіх платформах.</p>
|
||||
<p>У 2021 році ми отримали подальше фінансування від ЄС на дві пропозиції щодо Інтернету наступного покоління а саме на <a href="https://dapsi.ngi.eu/hall-of-fame/eppd/">EPPD - каталог перенесення провайдерів електронної пошти</a> (~97 тис. євро) та <a href="https://nlnet.nl/project/EmailPorting/">AEAP - перенесення адрес електронної пошти</a> (~90 тис. євро), що дозволило нам покращити багатопрофільну підтримку, вдосконалити налаштування контактів та груп за допомогою QR-коду та багато інших мережевих покращень на всіх платформах.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>З кінця 2021 до березня 2023 року ми отримували фінансування <em>Internet Freedom</em> (500 тис. дол. США) від Бюро США з питань демократії, прав людини та праці (DRL). Це фінансування підтримало наші довгострокові цілі зробити Delta Chat більш зручним для використання і сумісним з широким спектром серверів електронної пошти по всьому світу, більш стійким і безпечним в місцях, які постраждали від інтернет-цензури та відключення інтернету.</p>
|
||||
@@ -1470,4 +1521,4 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
|
||||
|
||||
|
||||
</body></html>
|
||||
</body></html>
|
||||
@@ -0,0 +1,124 @@
|
||||
/* Autogenerated file, do not edit manually */
|
||||
package chat.delta.rpc;
|
||||
|
||||
import chat.delta.util.SettableFuture;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/* Basic RPC Transport implementation */
|
||||
public abstract class BaseTransport implements Rpc.Transport {
|
||||
private final Map<Integer, SettableFuture<JsonNode>> requestFutures = new ConcurrentHashMap<>();
|
||||
private int requestId = 0;
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
private Thread worker;
|
||||
|
||||
/* Send a Request as raw JSON String to the RPC server */
|
||||
protected abstract void sendRequest(String jsonRequest);
|
||||
|
||||
/* Get next Response as raw JSON String from the RPC server */
|
||||
protected abstract String getResponse();
|
||||
|
||||
public ObjectMapper getObjectMapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
public void call(String method, JsonNode... params) throws RpcException {
|
||||
innerCall(method, params);
|
||||
}
|
||||
|
||||
public <T> T callForResult(TypeReference<T> resultType, String method, JsonNode... params) throws RpcException {
|
||||
try {
|
||||
JsonNode node = innerCall(method, params);
|
||||
if (node.isNull()) return null;
|
||||
return mapper.readValue(node.traverse(), resultType);
|
||||
} catch (IOException e) {
|
||||
throw new RpcException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private JsonNode innerCall(String method, JsonNode... params) throws RpcException {
|
||||
int id;
|
||||
synchronized (this) {
|
||||
id = ++requestId;
|
||||
ensureWorkerThread();
|
||||
}
|
||||
try {
|
||||
String jsonRequest = mapper.writeValueAsString(new Request(method, params, id));
|
||||
SettableFuture<JsonNode> future = new SettableFuture<>();
|
||||
requestFutures.put(id, future);
|
||||
sendRequest(jsonRequest);
|
||||
return future.get();
|
||||
} catch (ExecutionException e) {
|
||||
throw (RpcException)e.getCause();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RpcException(e.getMessage());
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RpcException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureWorkerThread() {
|
||||
if (worker != null) return;
|
||||
|
||||
worker = new Thread(() -> {
|
||||
while (true) {
|
||||
try {
|
||||
processResponse();
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, "jsonrpcThread");
|
||||
worker.start();
|
||||
}
|
||||
|
||||
private void processResponse() throws JsonProcessingException {
|
||||
String jsonResponse = getResponse();
|
||||
Response response = mapper.readValue(jsonResponse, Response.class);
|
||||
|
||||
if (response.id == 0) { // Got JSON-RPC notification/event, ignore
|
||||
return;
|
||||
}
|
||||
|
||||
SettableFuture<JsonNode> future = requestFutures.remove(response.id);
|
||||
if (future == null) { // Got a response with unknown ID, ignore
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.error != null) {
|
||||
future.setException(new RpcException(response.error.toString()));
|
||||
} else if (response.result != null) {
|
||||
future.set(response.result);
|
||||
} else {
|
||||
future.setException(new RpcException("Got JSON-RPC response without result or error: " + jsonResponse));
|
||||
}
|
||||
}
|
||||
|
||||
private static class Request {
|
||||
private final String jsonrpc = "2.0";
|
||||
public final String method;
|
||||
public final JsonNode[] params;
|
||||
public final int id;
|
||||
|
||||
public Request(String method, JsonNode[] params, int id) {
|
||||
this.method = method;
|
||||
this.params = params;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Response {
|
||||
public String jsonrpc;
|
||||
public int id;
|
||||
public JsonNode result;
|
||||
public JsonNode error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
/* Autogenerated file, do not edit manually */
|
||||
package chat.delta.rpc;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import chat.delta.rpc.types.*;
|
||||
|
||||
public class Rpc {
|
||||
|
||||
public interface Transport {
|
||||
void call(String method, JsonNode... params) throws RpcException;
|
||||
<T> T callForResult(TypeReference<T> resultType, String method, JsonNode... params) throws RpcException;
|
||||
ObjectMapper getObjectMapper();
|
||||
}
|
||||
|
||||
public final Transport transport;
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
public Rpc(Transport transport) {
|
||||
this.transport = transport;
|
||||
this.mapper = transport.getObjectMapper();
|
||||
}
|
||||
|
||||
public Integer addAccount() throws RpcException {
|
||||
return transport.callForResult(new TypeReference<Integer>(){}, "add_account");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the order of accounts.
|
||||
* The provided list should contain all account IDs in the desired order.
|
||||
* If an account ID is missing from the list, it will be appended at the end.
|
||||
* If the list contains non-existent account IDs, they will be ignored.
|
||||
*/
|
||||
public void setAccountsOrder(java.util.List<Integer> order) throws RpcException {
|
||||
transport.call("set_accounts_order", mapper.valueToTree(order));
|
||||
}
|
||||
|
||||
/* Get the combined filesize of an account in bytes */
|
||||
public Integer getAccountFileSize(Integer accountId) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<Integer>(){}, "get_account_file_size", mapper.valueToTree(accountId));
|
||||
}
|
||||
|
||||
/**
|
||||
* If there was an error while the account was opened
|
||||
* and migrated to the current version,
|
||||
* then this function returns it.
|
||||
* <p>
|
||||
* This function is useful because the key-contacts migration could fail due to bugs
|
||||
* and then the account will not work properly.
|
||||
* <p>
|
||||
* After opening an account, the UI should call this function
|
||||
* and show the error string if one is returned.
|
||||
*/
|
||||
public String getMigrationError(Integer accountId) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<String>(){}, "get_migration_error", mapper.valueToTree(accountId));
|
||||
}
|
||||
|
||||
public Integer draftSelfReport(Integer accountId) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<Integer>(){}, "draft_self_report", mapper.valueToTree(accountId));
|
||||
}
|
||||
|
||||
/* Returns configuration value for the given key. */
|
||||
public String getConfig(Integer accountId, String key) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<String>(){}, "get_config", mapper.valueToTree(accountId), mapper.valueToTree(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a new email account using the provided parameters
|
||||
* and adds it as a transport.
|
||||
* <p>
|
||||
* If the email address is the same as an existing transport,
|
||||
* then this existing account will be reconfigured instead of a new one being added.
|
||||
* <p>
|
||||
* This function stops and starts IO as needed.
|
||||
* <p>
|
||||
* Usually it will be enough to only set `addr` and `password`,
|
||||
* and all the other settings will be autoconfigured.
|
||||
* <p>
|
||||
* During configuration, ConfigureProgress events are emitted;
|
||||
* they indicate a successful configuration as well as errors
|
||||
* and may be used to create a progress bar.
|
||||
* This function will return after configuration is finished.
|
||||
* <p>
|
||||
* If configuration is successful,
|
||||
* the working server parameters will be saved
|
||||
* and used for connecting to the server.
|
||||
* The parameters entered by the user will be saved separately
|
||||
* so that they can be prefilled when the user opens the server-configuration screen again.
|
||||
* <p>
|
||||
* See also:
|
||||
* - [Self::is_configured()] to check whether there is
|
||||
* at least one working transport.
|
||||
* - [Self::add_transport_from_qr()] to add a transport
|
||||
* from a server encoded in a QR code.
|
||||
* - [Self::list_transports()] to get a list of all configured transports.
|
||||
* - [Self::delete_transport()] to remove a transport.
|
||||
*/
|
||||
public void addOrUpdateTransport(Integer accountId, EnteredLoginParam param) throws RpcException {
|
||||
transport.call("add_or_update_transport", mapper.valueToTree(accountId), mapper.valueToTree(param));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new email account as a transport
|
||||
* using the server encoded in the QR code.
|
||||
* See [Self::add_or_update_transport].
|
||||
*/
|
||||
public void addTransportFromQr(Integer accountId, String qr) throws RpcException {
|
||||
transport.call("add_transport_from_qr", mapper.valueToTree(accountId), mapper.valueToTree(qr));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new unencrypted group chat.
|
||||
* <p>
|
||||
* Same as [`Self::create_group_chat`], but the chat is unencrypted and can only have
|
||||
* address-contacts.
|
||||
*/
|
||||
public Integer createGroupChatUnencrypted(Integer accountId, String name) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<Integer>(){}, "create_group_chat_unencrypted", mapper.valueToTree(accountId), mapper.valueToTree(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new **broadcast channel**
|
||||
* (called "Channel" in the UI).
|
||||
* <p>
|
||||
* Broadcast channels are similar to groups on the sending device,
|
||||
* however, recipients get the messages in a read-only chat
|
||||
* and will not see who the other members are.
|
||||
* <p>
|
||||
* Called `broadcast` here rather than `channel`,
|
||||
* because the word "channel" already appears a lot in the code,
|
||||
* which would make it hard to grep for it.
|
||||
* <p>
|
||||
* After creation, the chat contains no recipients and is in _unpromoted_ state;
|
||||
* see [`CommandApi::create_group_chat`] for more information on the unpromoted state.
|
||||
* <p>
|
||||
* Returns the created chat's id.
|
||||
*/
|
||||
public Integer createBroadcast(Integer accountId, String chatName) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<Integer>(){}, "create_broadcast", mapper.valueToTree(accountId), mapper.valueToTree(chatName));
|
||||
}
|
||||
|
||||
/* Returns contact id of the created or existing DM chat with that contact */
|
||||
public Integer createChatByContactId(Integer accountId, Integer contactId) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<Integer>(){}, "create_chat_by_contact_id", mapper.valueToTree(accountId), mapper.valueToTree(contactId));
|
||||
}
|
||||
|
||||
/* Sets display name for existing contact. */
|
||||
public void changeContactName(Integer accountId, Integer contactId, String name) throws RpcException {
|
||||
transport.call("change_contact_name", mapper.valueToTree(accountId), mapper.valueToTree(contactId), mapper.valueToTree(name));
|
||||
}
|
||||
|
||||
|
||||
/* Parses a vCard file located at the given path. Returns contacts in their original order. */
|
||||
public java.util.List<VcardContact> parseVcard(String path) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<java.util.List<VcardContact>>(){}, "parse_vcard", mapper.valueToTree(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports contacts from a vCard file located at the given path.
|
||||
* <p>
|
||||
* Returns the ids of created/modified contacts in the order they appear in the vCard.
|
||||
*/
|
||||
public java.util.List<Integer> importVcard(Integer accountId, String path) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<java.util.List<Integer>>(){}, "import_vcard", mapper.valueToTree(accountId), mapper.valueToTree(path));
|
||||
}
|
||||
|
||||
/* Returns a vCard containing contacts with the given ids. */
|
||||
public String makeVcard(Integer accountId, java.util.List<Integer> contacts) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<String>(){}, "make_vcard", mapper.valueToTree(accountId), mapper.valueToTree(contacts));
|
||||
}
|
||||
|
||||
public void sendWebxdcRealtimeData(Integer accountId, Integer instanceMsgId, java.util.List<Integer> data) throws RpcException {
|
||||
transport.call("send_webxdc_realtime_data", mapper.valueToTree(accountId), mapper.valueToTree(instanceMsgId), mapper.valueToTree(data));
|
||||
}
|
||||
|
||||
public void sendWebxdcRealtimeAdvertisement(Integer accountId, Integer instanceMsgId) throws RpcException {
|
||||
transport.call("send_webxdc_realtime_advertisement", mapper.valueToTree(accountId), mapper.valueToTree(instanceMsgId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Leaves the gossip of the webxdc with the given message id.
|
||||
* <p>
|
||||
* NB: When this is called before closing a webxdc app in UIs, it must be guaranteed that
|
||||
* `send_webxdc_realtime_*()` functions aren't called for the given `instance_message_id`
|
||||
* anymore until the app is open again.
|
||||
*/
|
||||
public void leaveWebxdcRealtime(Integer accountId, Integer instanceMessageId) throws RpcException {
|
||||
transport.call("leave_webxdc_realtime", mapper.valueToTree(accountId), mapper.valueToTree(instanceMessageId));
|
||||
}
|
||||
|
||||
/* Starts an outgoing call. */
|
||||
public Integer placeOutgoingCall(Integer accountId, Integer chatId, String placeCallInfo) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<Integer>(){}, "place_outgoing_call", mapper.valueToTree(accountId), mapper.valueToTree(chatId), mapper.valueToTree(placeCallInfo));
|
||||
}
|
||||
|
||||
/* Accepts an incoming call. */
|
||||
public void acceptIncomingCall(Integer accountId, Integer msgId, String acceptCallInfo) throws RpcException {
|
||||
transport.call("accept_incoming_call", mapper.valueToTree(accountId), mapper.valueToTree(msgId), mapper.valueToTree(acceptCallInfo));
|
||||
}
|
||||
|
||||
/* Ends incoming or outgoing call. */
|
||||
public void endCall(Integer accountId, Integer msgId) throws RpcException {
|
||||
transport.call("end_call", mapper.valueToTree(accountId), mapper.valueToTree(msgId));
|
||||
}
|
||||
|
||||
/* Returns information about the call. */
|
||||
public CallInfo callInfo(Integer accountId, Integer msgId) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<CallInfo>(){}, "call_info", mapper.valueToTree(accountId), mapper.valueToTree(msgId));
|
||||
}
|
||||
|
||||
/* Returns JSON with ICE servers, to be used for WebRTC video calls. */
|
||||
public String iceServers(Integer accountId) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<String>(){}, "ice_servers", mapper.valueToTree(accountId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes an HTTP GET request and returns a response.
|
||||
* <p>
|
||||
* `url` is the HTTP or HTTPS URL.
|
||||
*/
|
||||
public HttpResponse getHttpResponse(Integer accountId, String url) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<HttpResponse>(){}, "get_http_response", mapper.valueToTree(accountId), mapper.valueToTree(url));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a reaction to message.
|
||||
* <p>
|
||||
* Reaction is a string of emojis separated by spaces. Reaction to a
|
||||
* single message can be sent multiple times. The last reaction
|
||||
* received overrides all previously received reactions. It is
|
||||
* possible to remove all reactions by sending an empty string.
|
||||
*/
|
||||
public Integer sendReaction(Integer accountId, Integer messageId, java.util.List<String> reaction) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<Integer>(){}, "send_reaction", mapper.valueToTree(accountId), mapper.valueToTree(messageId), mapper.valueToTree(reaction));
|
||||
}
|
||||
|
||||
/* Returns reactions to the message. */
|
||||
public Reactions getMessageReactions(Integer accountId, Integer messageId) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<Reactions>(){}, "get_message_reactions", mapper.valueToTree(accountId), mapper.valueToTree(messageId));
|
||||
}
|
||||
|
||||
/* Checks if messages can be sent to a given chat. */
|
||||
public Boolean canSend(Integer accountId, Integer chatId) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<Boolean>(){}, "can_send", mapper.valueToTree(accountId), mapper.valueToTree(chatId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/* Autogenerated file, do not edit manually */
|
||||
package chat.delta.rpc;
|
||||
|
||||
public class RpcException extends Exception {
|
||||
|
||||
public RpcException(String message) { super(message); }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/* Autogenerated file, do not edit manually */
|
||||
package chat.delta.rpc.types;
|
||||
|
||||
public class CallInfo {
|
||||
/* True if SDP offer has a video. */
|
||||
public Boolean hasVideo;
|
||||
/**
|
||||
* SDP offer.
|
||||
* <p>
|
||||
* Can be used to manually answer the call even if incoming call event was missed.
|
||||
*/
|
||||
public String sdpOffer;
|
||||
/**
|
||||
* Call state.
|
||||
* <p>
|
||||
* For example, if the call is accepted, active, canceled, declined etc.
|
||||
*/
|
||||
public CallState state;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/* Autogenerated file, do not edit manually */
|
||||
package chat.delta.rpc.types;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
|
||||
|
||||
@JsonTypeInfo(use=Id.NAME, include=As.PROPERTY, property="kind")
|
||||
@JsonSubTypes({@Type(value = CallState.Alerting.class, name="Alerting"), @Type(value = CallState.Active.class, name="Active"), @Type(value = CallState.Completed.class, name="Completed"), @Type(value = CallState.Missed.class, name="Missed"), @Type(value = CallState.Declined.class, name="Declined"), @Type(value = CallState.Canceled.class, name="Canceled")})
|
||||
public abstract class CallState {
|
||||
|
||||
/**
|
||||
* Fresh incoming or outgoing call that is still ringing.
|
||||
* <p>
|
||||
* There is no separate state for outgoing call that has been dialled but not ringing on the other side yet as we don't know whether the other side received our call.
|
||||
*/
|
||||
public static class Alerting extends CallState {
|
||||
}
|
||||
|
||||
/* Active call. */
|
||||
public static class Active extends CallState {
|
||||
}
|
||||
|
||||
/* Completed call that was once active and then was terminated for any reason. */
|
||||
public static class Completed extends CallState {
|
||||
/* Call duration in seconds. */
|
||||
public Integer duration;
|
||||
}
|
||||
|
||||
/* Incoming call that was not picked up within a timeout or was explicitly ended by the caller before we picked up. */
|
||||
public static class Missed extends CallState {
|
||||
}
|
||||
|
||||
/* Incoming call that was explicitly ended on our side before picking up or outgoing call that was declined before the timeout. */
|
||||
public static class Declined extends CallState {
|
||||
}
|
||||
|
||||
/**
|
||||
* Outgoing call that has been canceled on our side before receiving a response.
|
||||
* <p>
|
||||
* Incoming calls cannot be canceled, on the receiver side canceled calls usually result in missed calls.
|
||||
*/
|
||||
public static class Canceled extends CallState {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/* Autogenerated file, do not edit manually */
|
||||
package chat.delta.rpc.types;
|
||||
|
||||
public enum EnteredCertificateChecks {
|
||||
/* `Automatic` means that provider database setting should be taken. If there is no provider database setting for certificate checks, check certificates strictly. */
|
||||
automatic,
|
||||
|
||||
/* Ensure that TLS certificate is valid for the server hostname. */
|
||||
strict,
|
||||
|
||||
/* Accept certificates that are expired, self-signed or otherwise not valid for the server hostname. */
|
||||
acceptInvalidCertificates,
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/* Autogenerated file, do not edit manually */
|
||||
package chat.delta.rpc.types;
|
||||
|
||||
/**
|
||||
* Login parameters entered by the user.
|
||||
* <p>
|
||||
* Usually it will be enough to only set `addr` and `password`, and all the other settings will be autoconfigured.
|
||||
*/
|
||||
public class EnteredLoginParam {
|
||||
/* Email address. */
|
||||
public String addr;
|
||||
/* TLS options: whether to allow invalid certificates and/or invalid hostnames. Default: Automatic */
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public EnteredCertificateChecks certificateChecks;
|
||||
/* Imap server port. */
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public Integer imapPort;
|
||||
/* Imap socket security. */
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public Socket imapSecurity;
|
||||
/* Imap server hostname or IP address. */
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public String imapServer;
|
||||
/* Imap username. */
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public String imapUser;
|
||||
/* If true, login via OAUTH2 (not recommended anymore). Default: false */
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public Boolean oauth2;
|
||||
/* Password. */
|
||||
public String password;
|
||||
/**
|
||||
* SMTP Password.
|
||||
* <p>
|
||||
* Only needs to be specified if different than IMAP password.
|
||||
*/
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public String smtpPassword;
|
||||
/* SMTP server port. */
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public Integer smtpPort;
|
||||
/* SMTP socket security. */
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public Socket smtpSecurity;
|
||||
/* SMTP server hostname or IP address. */
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public String smtpServer;
|
||||
/* SMTP username. */
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public String smtpUser;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/* Autogenerated file, do not edit manually */
|
||||
package chat.delta.rpc.types;
|
||||
|
||||
public class HttpResponse {
|
||||
/* base64-encoded response body. */
|
||||
public String blob;
|
||||
/* Encoding, e.g. "utf-8". */
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public String encoding;
|
||||
/* MIME type, e.g. "text/plain" or "text/html". */
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public String mimetype;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/* Autogenerated file, do not edit manually */
|
||||
package chat.delta.rpc.types;
|
||||
|
||||
/* A single reaction emoji. */
|
||||
public class Reaction {
|
||||
/* Emoji frequency. */
|
||||
public Integer count;
|
||||
/* Emoji. */
|
||||
public String emoji;
|
||||
/* True if we reacted with this emoji. */
|
||||
public Boolean isFromSelf;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/* Autogenerated file, do not edit manually */
|
||||
package chat.delta.rpc.types;
|
||||
|
||||
/* Structure representing all reactions to a particular message. */
|
||||
public class Reactions {
|
||||
/* Unique reactions and their count, sorted in descending order. */
|
||||
public java.util.List<Reaction> reactions;
|
||||
/* Map from a contact to it's reaction to message. */
|
||||
public java.util.Map<String, java.util.List<String>> reactionsByContact;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/* Autogenerated file, do not edit manually */
|
||||
package chat.delta.rpc.types;
|
||||
|
||||
public enum Socket {
|
||||
/* Unspecified socket security, select automatically. */
|
||||
automatic,
|
||||
|
||||
/* TLS connection. */
|
||||
ssl,
|
||||
|
||||
/* STARTTLS connection. */
|
||||
starttls,
|
||||
|
||||
/* No TLS, plaintext connection. */
|
||||
plain,
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/* Autogenerated file, do not edit manually */
|
||||
package chat.delta.rpc.types;
|
||||
|
||||
public class VcardContact {
|
||||
/* Email address. */
|
||||
public String addr;
|
||||
/* Contact color as hex string. */
|
||||
public String color;
|
||||
/* The contact's name, or the email address if no name was given. */
|
||||
public String displayName;
|
||||
/* Public PGP key in Base64. */
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public String key;
|
||||
/* Profile image in Base64. */
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public String profileImage;
|
||||
/* Last update timestamp. */
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public Integer timestamp;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/* Autogenerated file, do not edit manually */
|
||||
package chat.delta.util;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public interface ListenableFuture<T> extends Future<T> {
|
||||
void addListener(Listener<T> listener);
|
||||
|
||||
public interface Listener<T> {
|
||||
public void onSuccess(T result);
|
||||
public void onFailure(ExecutionException e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/* Autogenerated file, do not edit manually */
|
||||
package chat.delta.util;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class SettableFuture<T> implements ListenableFuture<T> {
|
||||
|
||||
private final List<Listener<T>> listeners = new LinkedList<>();
|
||||
|
||||
private boolean completed;
|
||||
private boolean canceled;
|
||||
private volatile T result;
|
||||
private volatile Throwable exception;
|
||||
|
||||
public SettableFuture() { }
|
||||
|
||||
public SettableFuture(T value) {
|
||||
this.result = value;
|
||||
this.completed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean cancel(boolean mayInterruptIfRunning) {
|
||||
if (!completed && !canceled) {
|
||||
canceled = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isCancelled() {
|
||||
return canceled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isDone() {
|
||||
return completed;
|
||||
}
|
||||
|
||||
public boolean set(T result) {
|
||||
synchronized (this) {
|
||||
if (completed || canceled) return false;
|
||||
|
||||
this.result = result;
|
||||
this.completed = true;
|
||||
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
notifyAllListeners();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean setException(Throwable throwable) {
|
||||
synchronized (this) {
|
||||
if (completed || canceled) return false;
|
||||
|
||||
this.exception = throwable;
|
||||
this.completed = true;
|
||||
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
notifyAllListeners();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void deferTo(ListenableFuture<T> other) {
|
||||
other.addListener(new Listener<T>() {
|
||||
@Override
|
||||
public void onSuccess(T result) {
|
||||
SettableFuture.this.set(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(ExecutionException e) {
|
||||
SettableFuture.this.setException(e.getCause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized T get() throws InterruptedException, ExecutionException {
|
||||
while (!completed) wait();
|
||||
|
||||
if (exception != null) throw new ExecutionException(exception);
|
||||
else return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized T get(long timeout, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
while (!completed && System.currentTimeMillis() - startTime > unit.toMillis(timeout)) {
|
||||
wait(unit.toMillis(timeout));
|
||||
}
|
||||
|
||||
if (!completed) throw new TimeoutException();
|
||||
else return get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Listener<T> listener) {
|
||||
synchronized (this) {
|
||||
listeners.add(listener);
|
||||
|
||||
if (!completed) return;
|
||||
}
|
||||
|
||||
notifyListener(listener);
|
||||
}
|
||||
|
||||
private void notifyAllListeners() {
|
||||
List<Listener<T>> localListeners;
|
||||
|
||||
synchronized (this) {
|
||||
localListeners = new LinkedList<>(listeners);
|
||||
}
|
||||
|
||||
for (Listener<T> listener : localListeners) {
|
||||
notifyListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyListener(Listener<T> listener) {
|
||||
if (exception != null) listener.onFailure(new ExecutionException(exception));
|
||||
else listener.onSuccess(result);
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,6 @@ public class DcAccounts {
|
||||
public native void setPushDeviceToken (String token);
|
||||
public native boolean backgroundFetch (int timeoutSeconds);
|
||||
|
||||
public native int addAccount ();
|
||||
public native int migrateAccount (String dbfile);
|
||||
public native boolean removeAccount (int accountId);
|
||||
public native int[] getAll ();
|
||||
@@ -51,4 +50,14 @@ public class DcAccounts {
|
||||
private native long getJsonrpcInstanceCPtr ();
|
||||
private native long getAccountCPtr (int accountId);
|
||||
private native long getSelectedAccountCPtr ();
|
||||
|
||||
public boolean isAllChatmail() {
|
||||
for (int accountId : getAll()) {
|
||||
DcContext dcContext = getAccount(accountId);
|
||||
if (!dcContext.isChatmail()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ public class DcChat {
|
||||
public static final int DC_CHAT_TYPE_SINGLE = 100;
|
||||
public static final int DC_CHAT_TYPE_GROUP = 120;
|
||||
public static final int DC_CHAT_TYPE_MAILINGLIST = 140;
|
||||
public static final int DC_CHAT_TYPE_BROADCAST = 160;
|
||||
public static final int DC_CHAT_TYPE_OUT_BROADCAST = 160;
|
||||
public static final int DC_CHAT_TYPE_IN_BROADCAST = 165;
|
||||
|
||||
public static final int DC_CHAT_NO_CHAT = 0;
|
||||
public final static int DC_CHAT_ID_ARCHIVED_LINK = 6;
|
||||
@@ -38,12 +39,12 @@ public class DcChat {
|
||||
public native String getMailinglistAddr();
|
||||
public native String getProfileImage ();
|
||||
public native int getColor ();
|
||||
public native boolean isEncrypted ();
|
||||
public native boolean isUnpromoted ();
|
||||
public native boolean isSelfTalk ();
|
||||
public native boolean isDeviceTalk ();
|
||||
public native boolean canSend ();
|
||||
public native boolean isProtected ();
|
||||
public native boolean isProtectionBroken();
|
||||
public native boolean isSendingLocations();
|
||||
public native boolean isMuted ();
|
||||
public native boolean isContactRequest ();
|
||||
@@ -53,19 +54,18 @@ public class DcChat {
|
||||
|
||||
public boolean isMultiUser() {
|
||||
int type = getType();
|
||||
return type == DC_CHAT_TYPE_GROUP || type == DC_CHAT_TYPE_MAILINGLIST || type == DC_CHAT_TYPE_BROADCAST;
|
||||
return type != DC_CHAT_TYPE_SINGLE;
|
||||
}
|
||||
|
||||
public boolean isMailingList() {
|
||||
return getType() == DC_CHAT_TYPE_MAILINGLIST;
|
||||
}
|
||||
|
||||
public boolean isBroadcast() {
|
||||
return getType() == DC_CHAT_TYPE_BROADCAST;
|
||||
public boolean isInBroadcast() {
|
||||
return getType() == DC_CHAT_TYPE_IN_BROADCAST;
|
||||
}
|
||||
|
||||
public boolean isHalfBlocked() {
|
||||
return isProtectionBroken() || isContactRequest();
|
||||
public boolean isOutBroadcast() {
|
||||
return getType() == DC_CHAT_TYPE_OUT_BROADCAST;
|
||||
}
|
||||
|
||||
// working with raw c-data
|
||||
|
||||
@@ -10,8 +10,9 @@ public class DcContact {
|
||||
public final static int DC_CONTACT_ID_NEW_GROUP = -2; // - " -
|
||||
public final static int DC_CONTACT_ID_ADD_MEMBER = -3; // - " -
|
||||
public final static int DC_CONTACT_ID_QR_INVITE = -4; // - " -
|
||||
public final static int DC_CONTACT_ID_NEW_BROADCAST_LIST = -5; // - " -
|
||||
public final static int DC_CONTACT_ID_NEW_BROADCAST = -5; // - " -
|
||||
public final static int DC_CONTACT_ID_ADD_ACCOUNT = -6; // - " -
|
||||
public final static int DC_CONTACT_ID_NEW_UNENCRYPTED_GROUP = -7; // - " -
|
||||
|
||||
public DcContact(long contactCPtr) {
|
||||
this.contactCPtr = contactCPtr;
|
||||
@@ -50,7 +51,6 @@ public class DcContact {
|
||||
public native String getAuthName ();
|
||||
public native String getDisplayName ();
|
||||
public native String getAddr ();
|
||||
public native String getNameNAddr ();
|
||||
public native String getProfileImage();
|
||||
public native int getColor ();
|
||||
public native String getStatus ();
|
||||
@@ -58,6 +58,7 @@ public class DcContact {
|
||||
public native boolean wasSeenRecently();
|
||||
public native boolean isBlocked ();
|
||||
public native boolean isVerified ();
|
||||
public native boolean isKeyContact ();
|
||||
public native int getVerifierId ();
|
||||
public native boolean isBot ();
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ public class DcContext {
|
||||
public final static int DC_EVENT_MSG_READ = 2015;
|
||||
public final static int DC_EVENT_CHAT_MODIFIED = 2020;
|
||||
public final static int DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED = 2021;
|
||||
public final static int DC_EVENT_CHAT_DELETED = 2023;
|
||||
public final static int DC_EVENT_CONTACTS_CHANGED = 2030;
|
||||
public final static int DC_EVENT_LOCATION_CHANGED = 2035;
|
||||
public final static int DC_EVENT_CONFIGURE_PROGRESS = 2041;
|
||||
@@ -30,6 +31,10 @@ public class DcContext {
|
||||
public final static int DC_EVENT_WEBXDC_INSTANCE_DELETED = 2121;
|
||||
public final static int DC_EVENT_WEBXDC_REALTIME_DATA = 2150;
|
||||
public final static int DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE = 2200;
|
||||
public final static int DC_EVENT_INCOMING_CALL = 2550;
|
||||
public final static int DC_EVENT_INCOMING_CALL_ACCEPTED = 2560;
|
||||
public final static int DC_EVENT_OUTGOING_CALL_ACCEPTED = 2570;
|
||||
public final static int DC_EVENT_CALL_ENDED = 2580;
|
||||
|
||||
public final static int DC_IMEX_EXPORT_SELF_KEYS = 1;
|
||||
public final static int DC_IMEX_IMPORT_SELF_KEYS = 2;
|
||||
@@ -38,6 +43,7 @@ public class DcContext {
|
||||
|
||||
public final static int DC_GCL_VERIFIED_ONLY = 1;
|
||||
public final static int DC_GCL_ADD_SELF = 2;
|
||||
public final static int DC_GCL_ADDRESS = 0x04;
|
||||
public final static int DC_GCL_ARCHIVED_ONLY = 0x01;
|
||||
public final static int DC_GCL_NO_SPECIALS = 0x02;
|
||||
public final static int DC_GCL_ADD_ALLDONE_HINT = 0x04;
|
||||
@@ -51,8 +57,8 @@ public class DcContext {
|
||||
public final static int DC_QR_FPR_MISMATCH = 220;
|
||||
public final static int DC_QR_FPR_WITHOUT_ADDR = 230;
|
||||
public final static int DC_QR_ACCOUNT = 250;
|
||||
public final static int DC_QR_BACKUP = 251;
|
||||
public final static int DC_QR_BACKUP2 = 252;
|
||||
public final static int DC_QR_BACKUP_TOO_NEW = 255;
|
||||
public final static int DC_QR_WEBRTC = 260;
|
||||
public final static int DC_QR_PROXY = 271;
|
||||
public final static int DC_QR_ADDR = 320;
|
||||
@@ -88,7 +94,7 @@ public class DcContext {
|
||||
private static final String CONFIG_ACCOUNT_ENABLED = "ui.enabled";
|
||||
private static final String CONFIG_MUTE_MENTIONS_IF_MUTED = "ui.mute_mentions_if_muted";
|
||||
|
||||
// when using DcAccounts, use DcAccounts.addAccount() instead
|
||||
// when using DcAccounts, use Rpc.addAccount() instead
|
||||
public DcContext(String osName, String dbfile) {
|
||||
contextCPtr = createContextCPtr(osName, dbfile);
|
||||
}
|
||||
@@ -118,7 +124,6 @@ public class DcContext {
|
||||
public native void setStockTranslation (int stockId, String translation);
|
||||
public native String getBlobdir ();
|
||||
public native String getLastError ();
|
||||
public native void configure ();
|
||||
public native void stopOngoingProcess ();
|
||||
public native int isConfigured ();
|
||||
public native boolean open (String passphrase);
|
||||
@@ -137,13 +142,13 @@ public class DcContext {
|
||||
public void setConfigInt (String key, int value) { setConfig(key, Integer.toString(value)); }
|
||||
public native boolean setConfigFromQr (String qr);
|
||||
public native String getConfig (String key);
|
||||
public int getConfigInt (String key) { try{return Integer.parseInt(getConfig(key));} catch(Exception e) {} return 0; }
|
||||
public int getConfigInt (String key) { return getConfigInt(key, 0); }
|
||||
public int getConfigInt (String key, int defValue) { try{return Integer.parseInt(getConfig(key));} catch(Exception e) {} return defValue; }
|
||||
public native String getInfo ();
|
||||
public native int getConnectivity ();
|
||||
public native String getConnectivityHtml ();
|
||||
public native String getOauth2Url (String addr, String redirectUrl);
|
||||
public native String initiateKeyTransfer ();
|
||||
public native boolean continueKeyTransfer (int msg_id, String setup_code);
|
||||
public native void imex (int what, String dir);
|
||||
public native String imexHasBackup (String dir);
|
||||
public DcBackupProvider newBackupProvider () { return new DcBackupProvider(newBackupProviderCPtr()); }
|
||||
@@ -187,17 +192,19 @@ public class DcContext {
|
||||
public native void blockChat (int chat_id);
|
||||
public native void acceptChat (int chat_id);
|
||||
public DcMsg getMsg (int msg_id) { return new DcMsg(getMsgCPtr(msg_id)); }
|
||||
public native void sendEditRequest (int msg_id, String text);
|
||||
public native String getMsgInfo (int id);
|
||||
public native String getMsgHtml (int msg_id);
|
||||
public native void downloadFullMsg (int msg_id);
|
||||
public native int getFreshMsgCount (int chat_id);
|
||||
public native int estimateDeletionCount(boolean from_server, long seconds);
|
||||
public native void deleteMsgs (int msg_ids[]);
|
||||
public native void sendDeleteRequest (int msg_ids[]);
|
||||
public native void forwardMsgs (int msg_ids[], int chat_id);
|
||||
public native void saveMsgs (int msg_ids[]);
|
||||
public native boolean resendMsgs (int msg_ids[]);
|
||||
public native int sendMsg (int chat_id, DcMsg msg);
|
||||
public native int sendTextMsg (int chat_id, String text);
|
||||
public native int sendVideochatInvitation(int chat_id);
|
||||
public native boolean sendWebxdcStatusUpdate(int msg_id, String payload);
|
||||
public native String getWebxdcStatusUpdates(int msg_id, int last_known_serial);
|
||||
public native void setWebxdcIntegration (String file);
|
||||
@@ -242,23 +249,6 @@ public class DcContext {
|
||||
return displayname;
|
||||
}
|
||||
|
||||
public boolean isCommunity() {
|
||||
return getConfigInt("is_community") == 1;
|
||||
}
|
||||
|
||||
public void setCommunityMode(boolean enable) {
|
||||
setConfig("is_community", enable? "1" : null);
|
||||
}
|
||||
|
||||
public String getCommunityUser() {
|
||||
String name = getConfig("ui.community.selfname");
|
||||
return name == null? "" : name;
|
||||
}
|
||||
|
||||
public void setCommunityUser(String name) {
|
||||
setConfig("ui.community.selfname", name);
|
||||
}
|
||||
|
||||
public boolean isChatmail() {
|
||||
return getConfigInt("is_chatmail") == 1;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ public class DcMsg {
|
||||
public final static int DC_MSG_VOICE = 41;
|
||||
public final static int DC_MSG_VIDEO = 50;
|
||||
public final static int DC_MSG_FILE = 60;
|
||||
public final static int DC_MSG_VIDEOCHAT_INVITATION = 70;
|
||||
public final static int DC_MSG_CALL = 71;
|
||||
public final static int DC_MSG_WEBXDC = 80;
|
||||
public final static int DC_MSG_VCARD = 90;
|
||||
|
||||
@@ -33,9 +33,9 @@ public class DcMsg {
|
||||
public final static int DC_INFO_LOCATION_ONLY = 9;
|
||||
public final static int DC_INFO_EPHEMERAL_TIMER_CHANGED = 10;
|
||||
public final static int DC_INFO_PROTECTION_ENABLED = 11;
|
||||
public final static int DC_INFO_PROTECTION_DISABLED = 12;
|
||||
public final static int DC_INFO_INVALID_UNENCRYPTED_MAIL = 13;
|
||||
public final static int DC_INFO_WEBXDC_INFO_MESSAGE = 32;
|
||||
public final static int DC_INFO_CHAT_E2EE = 50;
|
||||
|
||||
public final static int DC_STATE_UNDEFINED = 0;
|
||||
public final static int DC_STATE_IN_FRESH = 10;
|
||||
@@ -123,6 +123,7 @@ public class DcMsg {
|
||||
private native int getViewType ();
|
||||
public int getType () { return getDownloadState()==DC_DOWNLOAD_DONE? getViewType() : DC_MSG_TEXT; }
|
||||
public native int getInfoType ();
|
||||
public native int getInfoContactId ();
|
||||
public native int getState ();
|
||||
public native int getDownloadState ();
|
||||
public native int getChatId ();
|
||||
@@ -142,24 +143,21 @@ public class DcMsg {
|
||||
public native byte[] getWebxdcBlob (String filename);
|
||||
public JSONObject getWebxdcInfo () {
|
||||
try {
|
||||
return new JSONObject(getWebxdcInfoJson());
|
||||
String json = getWebxdcInfoJson();
|
||||
if (json != null && !json.isEmpty()) return new JSONObject(json);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
return new JSONObject();
|
||||
}
|
||||
return new JSONObject();
|
||||
}
|
||||
public native String getWebxdcHref ();
|
||||
public native boolean isForwarded ();
|
||||
public native boolean isInfo ();
|
||||
public native boolean isSetupMessage ();
|
||||
public native boolean hasHtml ();
|
||||
public native String getSetupCodeBegin ();
|
||||
public native String getVideochatUrl ();
|
||||
public native int getVideochatType ();
|
||||
public native void setText (String text);
|
||||
public native void setSubject (String text);
|
||||
public native void setHtml (String text);
|
||||
public native void setFile (String file, String filemime);
|
||||
public native void forceSticker ();
|
||||
public native void setFileAndDeduplicate(String file, String name, String filemime);
|
||||
public native void setDimension (int width, int height);
|
||||
@@ -170,7 +168,7 @@ public class DcMsg {
|
||||
public native String getQuotedText ();
|
||||
public native String getError ();
|
||||
public native String getOverrideSenderName();
|
||||
public native int getSenderColor();
|
||||
public native boolean isEdited ();
|
||||
|
||||
public String getSenderName(DcContact dcContact) {
|
||||
String overrideName = getOverrideSenderName();
|
||||
@@ -191,6 +189,14 @@ public class DcMsg {
|
||||
return cPtr != 0 ? new DcMsg(cPtr) : null;
|
||||
}
|
||||
|
||||
public native int getOriginalMsgId ();
|
||||
public native int getSavedMsgId ();
|
||||
|
||||
public boolean canSave() {
|
||||
// saving info-messages out of context results in confusion, see https://github.com/deltachat/deltachat-ios/issues/2567
|
||||
return !isInfo();
|
||||
}
|
||||
|
||||
public File getFileAsFile() {
|
||||
if(getFile()==null)
|
||||
throw new AssertionError("expected a file to be present.");
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.b44t.messenger;
|
||||
|
||||
import chat.delta.rpc.BaseTransport;
|
||||
|
||||
/* RPC transport over C FFI */
|
||||
public class FFITransport extends BaseTransport {
|
||||
private final DcJsonrpcInstance dcJsonrpcInstance;
|
||||
|
||||
public FFITransport(DcJsonrpcInstance dcJsonrpcInstance) {
|
||||
this.dcJsonrpcInstance = dcJsonrpcInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendRequest(String jsonRequest) {
|
||||
dcJsonrpcInstance.request(jsonRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getResponse() {
|
||||
return dcJsonrpcInstance.getNextResponse();
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.b44t.messenger.rpc;
|
||||
|
||||
import android.util.Base64;
|
||||
|
||||
public class HttpResponse {
|
||||
// base64-encoded response body.
|
||||
private final String blob;
|
||||
// MIME type, e.g. "text/plain" or "text/html".
|
||||
private final String mimetype;
|
||||
// Encoding, e.g. "utf-8".
|
||||
private final String encoding;
|
||||
|
||||
public HttpResponse(String blob, String mimetype, String encoding) {
|
||||
this.blob = blob;
|
||||
this.mimetype = mimetype;
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
public byte[] getBlob() {
|
||||
if (blob == null) {
|
||||
return null;
|
||||
}
|
||||
return Base64.decode(blob, Base64.NO_WRAP | Base64.NO_PADDING);
|
||||
}
|
||||
|
||||
public String getMimetype() {
|
||||
return mimetype;
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return encoding;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.b44t.messenger.rpc;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class Reaction {
|
||||
// The reaction emoji string.
|
||||
private final String emoji;
|
||||
// The count of users that have reacted with this reaction.
|
||||
private final int count;
|
||||
// true if self-account reacted with this reaction, false otherwise.
|
||||
private final boolean isFromSelf;
|
||||
|
||||
public Reaction(String emoji, int count, boolean isFromSelf) {
|
||||
this.emoji = emoji;
|
||||
this.count = count;
|
||||
this.isFromSelf = isFromSelf;
|
||||
}
|
||||
|
||||
public String getEmoji() {
|
||||
return emoji;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public boolean isFromSelf() {
|
||||
return isFromSelf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (obj instanceof Reaction) {
|
||||
Reaction reaction = (Reaction) obj;
|
||||
return emoji.equals(reaction.getEmoji()) && count == reaction.getCount() && isFromSelf == reaction.isFromSelf();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.b44t.messenger.rpc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Reactions {
|
||||
// Map from a contact to it's reaction to message.
|
||||
private final HashMap<Integer, String[]> reactionsByContact;
|
||||
// Unique reactions, sorted in descending order.
|
||||
private final ArrayList<Reaction> reactions;
|
||||
|
||||
public Reactions(HashMap<Integer, String[]> reactionsByContact, ArrayList<Reaction> reactions) {
|
||||
this.reactionsByContact = reactionsByContact;
|
||||
this.reactions = reactions;
|
||||
}
|
||||
|
||||
public Map<Integer, String[]> getReactionsByContact() {
|
||||
return reactionsByContact;
|
||||
}
|
||||
|
||||
public List<Reaction> getReactions() {
|
||||
return reactions;
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
package com.b44t.messenger.rpc;
|
||||
|
||||
import com.b44t.messenger.DcJsonrpcInstance;
|
||||
import com.b44t.messenger.util.concurrent.SettableFuture;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class Rpc {
|
||||
private final Map<Integer, SettableFuture<JsonElement>> requestFutures = new ConcurrentHashMap<>();
|
||||
private final DcJsonrpcInstance dcJsonrpcInstance;
|
||||
private int requestId = 0;
|
||||
private final Gson gson = new GsonBuilder().serializeNulls().create();
|
||||
|
||||
public Rpc(DcJsonrpcInstance dcJsonrpcInstance) {
|
||||
this.dcJsonrpcInstance = dcJsonrpcInstance;
|
||||
}
|
||||
|
||||
private void processResponse() throws JsonSyntaxException {
|
||||
String jsonResponse = dcJsonrpcInstance.getNextResponse();
|
||||
Response response = gson.fromJson(jsonResponse, Response.class);
|
||||
|
||||
if (response.id == 0) { // Got JSON-RPC notification/event, ignore
|
||||
return;
|
||||
}
|
||||
|
||||
SettableFuture<JsonElement> future = requestFutures.remove(response.id);
|
||||
if (future == null) { // Got a response with unknown ID, ignore
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.error != null) {
|
||||
future.setException(new RpcException(response.error.toString()));
|
||||
} else if (response.result != null) {
|
||||
future.set(response.result);
|
||||
} else {
|
||||
future.setException(new RpcException("Got JSON-RPC response without result or error: " + jsonResponse));
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
new Thread(() -> {
|
||||
while (true) {
|
||||
try {
|
||||
processResponse();
|
||||
} catch (JsonSyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, "jsonrpcThread").start();
|
||||
}
|
||||
|
||||
public SettableFuture<JsonElement> call(String method, Object... params) {
|
||||
int id;
|
||||
synchronized (this) {
|
||||
id = ++requestId;
|
||||
}
|
||||
String jsonRequest = gson.toJson(new Request(method, params, id));
|
||||
SettableFuture<JsonElement> future = new SettableFuture<>();
|
||||
requestFutures.put(id, future);
|
||||
dcJsonrpcInstance.request(jsonRequest);
|
||||
return future;
|
||||
}
|
||||
|
||||
public JsonElement getResult(String method, Object... params) throws RpcException {
|
||||
try {
|
||||
return call(method, params).get();
|
||||
} catch (ExecutionException e) {
|
||||
throw (RpcException)e.getCause();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RpcException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public List<VcardContact> parseVcard(String path) throws RpcException {
|
||||
TypeToken<List<VcardContact>> listType = new TypeToken<List<VcardContact>>(){};
|
||||
return gson.fromJson(getResult("parse_vcard", path), listType.getType());
|
||||
}
|
||||
|
||||
public String makeVcard(int accountId, int... contacts) throws RpcException {
|
||||
return gson.fromJson(getResult("make_vcard", accountId, contacts), String.class);
|
||||
}
|
||||
|
||||
public List<Integer> importVcard(int accountId, String path) throws RpcException {
|
||||
TypeToken<List<Integer>> listType = new TypeToken<List<Integer>>(){};
|
||||
return gson.fromJson(getResult("import_vcard", accountId, path), listType.getType());
|
||||
}
|
||||
|
||||
public HttpResponse getHttpResponse(int accountId, String url) throws RpcException {
|
||||
return gson.fromJson(getResult("get_http_response", accountId, url), HttpResponse.class);
|
||||
}
|
||||
|
||||
public Reactions getMsgReactions(int accountId, int msgId) throws RpcException {
|
||||
return gson.fromJson(getResult("get_message_reactions", accountId, msgId), Reactions.class);
|
||||
}
|
||||
|
||||
public int sendReaction(int accountId, int msgId, String... reaction) throws RpcException {
|
||||
return getResult("send_reaction", accountId, msgId, reaction).getAsInt();
|
||||
}
|
||||
|
||||
public int draftSelfReport(int accountId) throws RpcException {
|
||||
return getResult("draft_self_report", accountId).getAsInt();
|
||||
}
|
||||
|
||||
public void sendWebxdcRealtimeData(Integer accountId, Integer instanceMsgId, List<Integer> data) throws RpcException {
|
||||
getResult("send_webxdc_realtime_data", accountId, instanceMsgId, data);
|
||||
}
|
||||
|
||||
public void sendWebxdcRealtimeAdvertisement(Integer accountId, Integer instanceMsgId) throws RpcException {
|
||||
getResult("send_webxdc_realtime_advertisement", accountId, instanceMsgId);
|
||||
}
|
||||
|
||||
public void leaveWebxdcRealtime(Integer accountId, Integer instanceMessageId) throws RpcException {
|
||||
getResult("leave_webxdc_realtime", accountId, instanceMessageId);
|
||||
}
|
||||
|
||||
public int getAccountFileSize(int accountId) throws RpcException {
|
||||
return getResult("get_account_file_size", accountId).getAsInt();
|
||||
}
|
||||
|
||||
private static class Request {
|
||||
private final String jsonrpc = "2.0";
|
||||
public final String method;
|
||||
public final Object[] params;
|
||||
public final int id;
|
||||
|
||||
public Request(String method, Object[] params, int id) {
|
||||
this.method = method;
|
||||
this.params = params;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Response {
|
||||
public final int id;
|
||||
public final JsonElement result;
|
||||
public final JsonElement error;
|
||||
|
||||
public Response(int id, JsonElement result, JsonElement error) {
|
||||
this.id = id;
|
||||
this.result = result;
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.b44t.messenger.rpc;
|
||||
|
||||
/**
|
||||
* An exception occurred while processing a request in ArcaneChat core.
|
||||
**/
|
||||
public class RpcException extends Exception {
|
||||
|
||||
public RpcException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package com.b44t.messenger.rpc;
|
||||
|
||||
import android.util.Base64;
|
||||
|
||||
public class VcardContact {
|
||||
// Email address.
|
||||
private final String addr;
|
||||
|
||||
// The contact's name, or the email address if no name was given.
|
||||
private final String displayName;
|
||||
|
||||
// Public PGP key in Base64.
|
||||
private final String key;
|
||||
|
||||
// Profile image in Base64.
|
||||
private final String profileImage;
|
||||
|
||||
// Contact color in HTML color format.
|
||||
private final String color;
|
||||
|
||||
// Last update timestamp.
|
||||
private final int timestamp;
|
||||
|
||||
public VcardContact(String addr, String displayName, String key, String profileImage, String color, int timestamp) {
|
||||
this.addr = addr;
|
||||
this.displayName = displayName;
|
||||
this.key = key;
|
||||
this.profileImage = profileImage;
|
||||
this.color = color;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public String getAddr() {
|
||||
return addr;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public byte[] getKey() {
|
||||
return key == null? null : Base64.decode(key, Base64.NO_WRAP | Base64.NO_PADDING);
|
||||
}
|
||||
|
||||
public boolean hasProfileImage() {
|
||||
return profileImage != null;
|
||||
}
|
||||
|
||||
public byte[] getProfileImage() {
|
||||
return profileImage == null? null : Base64.decode(profileImage, Base64.NO_WRAP | Base64.NO_PADDING);
|
||||
}
|
||||
|
||||
public String getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcEvent;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import org.thoughtcrime.securesms.connect.DcEventCenter;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class AllMediaActivity extends PassphraseRequiredActionBarActivity
|
||||
implements DcEventCenter.DcEventDelegate
|
||||
{
|
||||
|
||||
public static final String CHAT_ID_EXTRA = "chat_id";
|
||||
public static final String CONTACT_ID_EXTRA = "contact_id";
|
||||
public static final String FORCE_GALLERY = "force_gallery";
|
||||
|
||||
static class TabData {
|
||||
final int title;
|
||||
final int type1;
|
||||
final int type2;
|
||||
final int type3;
|
||||
TabData(int title, int type1, int type2, int type3) {
|
||||
this.title = title;
|
||||
this.type1 = type1;
|
||||
this.type2 = type2;
|
||||
this.type3 = type3;
|
||||
}
|
||||
};
|
||||
|
||||
private DcContext dcContext;
|
||||
private int chatId;
|
||||
private int contactId;
|
||||
|
||||
private final ArrayList<TabData> tabs = new ArrayList<>();
|
||||
private Toolbar toolbar;
|
||||
private TabLayout tabLayout;
|
||||
private ViewPager viewPager;
|
||||
|
||||
@Override
|
||||
protected void onPreCreate() {
|
||||
dynamicTheme = new DynamicNoActionBarTheme();
|
||||
super.onPreCreate();
|
||||
dcContext = DcHelper.getContext(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle, boolean ready) {
|
||||
tabs.add(new TabData(R.string.webxdc_apps, DcMsg.DC_MSG_WEBXDC, 0, 0));
|
||||
tabs.add(new TabData(R.string.tab_gallery, DcMsg.DC_MSG_IMAGE, DcMsg.DC_MSG_GIF, DcMsg.DC_MSG_VIDEO));
|
||||
tabs.add(new TabData(R.string.files, DcMsg.DC_MSG_FILE, 0, 0));
|
||||
tabs.add(new TabData(R.string.audio, DcMsg.DC_MSG_AUDIO, DcMsg.DC_MSG_VOICE, 0));
|
||||
|
||||
setContentView(R.layout.all_media_activity);
|
||||
|
||||
initializeResources();
|
||||
|
||||
setSupportActionBar(this.toolbar);
|
||||
ActionBar supportActionBar = getSupportActionBar();
|
||||
if (supportActionBar != null) {
|
||||
supportActionBar.setDisplayHomeAsUpEnabled(true);
|
||||
supportActionBar.setTitle(isGlobalGallery() ? R.string.menu_all_media : R.string.apps_and_media);
|
||||
}
|
||||
|
||||
this.tabLayout.setupWithViewPager(viewPager);
|
||||
this.viewPager.setAdapter(new AllMediaPagerAdapter(getSupportFragmentManager()));
|
||||
if (getIntent().getBooleanExtra(FORCE_GALLERY, false)) {
|
||||
this.viewPager.setCurrentItem(1, false);
|
||||
}
|
||||
|
||||
DcEventCenter eventCenter = DcHelper.getEventCenter(this);
|
||||
eventCenter.addObserver(DcContext.DC_EVENT_CHAT_MODIFIED, this);
|
||||
eventCenter.addObserver(DcContext.DC_EVENT_CONTACTS_CHANGED, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
DcHelper.getEventCenter(this).removeObservers(this);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEvent(@NonNull DcEvent event) {
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
chatId = getIntent().getIntExtra(CHAT_ID_EXTRA, 0);
|
||||
contactId = getIntent().getIntExtra(CONTACT_ID_EXTRA, 0);
|
||||
|
||||
if (contactId!=0) {
|
||||
chatId = dcContext.getChatIdByContactId(contactId);
|
||||
}
|
||||
|
||||
if(chatId!=0) {
|
||||
DcChat dcChat = dcContext.getChat(chatId);
|
||||
if(!dcChat.isMultiUser()) {
|
||||
final int[] members = dcContext.getChatContacts(chatId);
|
||||
contactId = members.length>=1? members[0] : 0;
|
||||
}
|
||||
}
|
||||
|
||||
this.viewPager = ViewUtil.findById(this, R.id.pager);
|
||||
this.toolbar = ViewUtil.findById(this, R.id.toolbar);
|
||||
this.tabLayout = ViewUtil.findById(this, R.id.tab_layout);
|
||||
}
|
||||
|
||||
private boolean isGlobalGallery() {
|
||||
return contactId==0 && chatId==0;
|
||||
}
|
||||
|
||||
private class AllMediaPagerAdapter extends FragmentStatePagerAdapter {
|
||||
private Object currentFragment = null;
|
||||
|
||||
AllMediaPagerAdapter(FragmentManager fragmentManager) {
|
||||
super(fragmentManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
|
||||
super.setPrimaryItem(container, position, object);
|
||||
if (currentFragment != null && currentFragment != object) {
|
||||
ActionMode action = null;
|
||||
if (currentFragment instanceof MessageSelectorFragment) {
|
||||
action = ((MessageSelectorFragment) currentFragment).getActionMode();
|
||||
}
|
||||
if (action != null) {
|
||||
action.finish();
|
||||
}
|
||||
}
|
||||
currentFragment = object;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
TabData data = tabs.get(position);
|
||||
Fragment fragment;
|
||||
Bundle args = new Bundle();
|
||||
|
||||
if (data.type1 == DcMsg.DC_MSG_IMAGE) {
|
||||
fragment = new AllMediaGalleryFragment();
|
||||
args.putInt(AllMediaGalleryFragment.CHAT_ID_EXTRA, (chatId==0&&!isGlobalGallery())? -1 : chatId);
|
||||
} else {
|
||||
fragment = new AllMediaDocumentsFragment();
|
||||
args.putInt(AllMediaDocumentsFragment.CHAT_ID_EXTRA, (chatId==0&&!isGlobalGallery())? -1 : chatId);
|
||||
args.putInt(AllMediaDocumentsFragment.VIEWTYPE1, data.type1);
|
||||
args.putInt(AllMediaDocumentsFragment.VIEWTYPE2, data.type2);
|
||||
}
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return tabs.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return getString(tabs.get(position).title);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == android.R.id.home) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.b44t.messenger.DcMsg;
|
||||
import com.codewaves.stickyheadergrid.StickyHeaderGridAdapter;
|
||||
|
||||
@@ -24,7 +25,7 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
class ProfileDocumentsAdapter extends StickyHeaderGridAdapter {
|
||||
class AllMediaDocumentsAdapter extends StickyHeaderGridAdapter {
|
||||
|
||||
private final Context context;
|
||||
private final ItemClickListener itemClickListener;
|
||||
@@ -56,9 +57,9 @@ class ProfileDocumentsAdapter extends StickyHeaderGridAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
ProfileDocumentsAdapter(@NonNull Context context,
|
||||
BucketedThreadMedia media,
|
||||
ItemClickListener clickListener)
|
||||
AllMediaDocumentsAdapter(@NonNull Context context,
|
||||
BucketedThreadMedia media,
|
||||
ItemClickListener clickListener)
|
||||
{
|
||||
this.context = context;
|
||||
this.media = media;
|
||||
@@ -101,16 +102,18 @@ class ProfileDocumentsAdapter extends StickyHeaderGridAdapter {
|
||||
viewHolder.audioView.setOnLongClickListener(view -> { itemClickListener.onMediaLongClicked(dcMsg); return true; });
|
||||
viewHolder.audioView.disablePlayer(!selected.isEmpty());
|
||||
viewHolder.itemView.setOnClickListener(view -> itemClickListener.onMediaClicked(dcMsg));
|
||||
viewHolder.date.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else if (slide != null && slide.isWebxdcDocument()) {
|
||||
viewHolder.audioView.setVisibility(View.GONE);
|
||||
viewHolder.documentView.setVisibility(View.GONE);
|
||||
|
||||
viewHolder.webxdcView.setVisibility(View.VISIBLE);
|
||||
viewHolder.webxdcView.setWebxdc(dcMsg, context.getString(R.string.webxdc_app));
|
||||
viewHolder.webxdcView.setWebxdc(dcMsg, "");
|
||||
viewHolder.webxdcView.setOnClickListener(view -> itemClickListener.onMediaClicked(dcMsg));
|
||||
viewHolder.webxdcView.setOnLongClickListener(view -> { itemClickListener.onMediaLongClicked(dcMsg); return true; });
|
||||
viewHolder.itemView.setOnClickListener(view -> itemClickListener.onMediaClicked(dcMsg));
|
||||
viewHolder.date.setVisibility(View.GONE);
|
||||
}
|
||||
else if (slide != null && slide.hasDocument()) {
|
||||
viewHolder.audioView.setVisibility(View.GONE);
|
||||
@@ -121,11 +124,13 @@ class ProfileDocumentsAdapter extends StickyHeaderGridAdapter {
|
||||
viewHolder.documentView.setOnClickListener(view -> itemClickListener.onMediaClicked(dcMsg));
|
||||
viewHolder.documentView.setOnLongClickListener(view -> { itemClickListener.onMediaLongClicked(dcMsg); return true; });
|
||||
viewHolder.itemView.setOnClickListener(view -> itemClickListener.onMediaClicked(dcMsg));
|
||||
viewHolder.date.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else {
|
||||
viewHolder.documentView.setVisibility(View.GONE);
|
||||
viewHolder.audioView.setVisibility(View.GONE);
|
||||
viewHolder.webxdcView.setVisibility(View.GONE);
|
||||
viewHolder.date.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
viewHolder.itemView.setOnLongClickListener(view -> { itemClickListener.onMediaLongClicked(dcMsg); return true; });
|
||||
@@ -5,14 +5,12 @@ import static com.b44t.messenger.DcChat.DC_CHAT_NO_CHAT;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -34,21 +32,21 @@ import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class ProfileDocumentsFragment
|
||||
public class AllMediaDocumentsFragment
|
||||
extends MessageSelectorFragment
|
||||
implements LoaderManager.LoaderCallbacks<BucketedThreadMediaLoader.BucketedThreadMedia>,
|
||||
ProfileDocumentsAdapter.ItemClickListener
|
||||
AllMediaDocumentsAdapter.ItemClickListener
|
||||
{
|
||||
public static final String CHAT_ID_EXTRA = "chat_id";
|
||||
public static final String SHOW_AUDIO_EXTRA = "show_audio";
|
||||
public static final String SHOW_WEBXDC_EXTRA = "show_webxdc";
|
||||
public static final String VIEWTYPE1 = "viewtype1";
|
||||
public static final String VIEWTYPE2 = "viewtype2";
|
||||
|
||||
protected TextView noMedia;
|
||||
protected RecyclerView recyclerView;
|
||||
private StickyHeaderGridLayoutManager gridManager;
|
||||
private final ActionModeCallback actionModeCallback = new ActionModeCallback();
|
||||
private boolean showAudio;
|
||||
private boolean showWebxdc;
|
||||
private int viewtype1;
|
||||
private int viewtype2;
|
||||
|
||||
protected int chatId;
|
||||
|
||||
@@ -58,8 +56,8 @@ public class ProfileDocumentsFragment
|
||||
|
||||
dcContext = DcHelper.getContext(getContext());
|
||||
chatId = getArguments().getInt(CHAT_ID_EXTRA, -1);
|
||||
showAudio = getArguments().getBoolean(SHOW_AUDIO_EXTRA, false);
|
||||
showWebxdc = getArguments().getBoolean(SHOW_WEBXDC_EXTRA, false);
|
||||
viewtype1 = getArguments().getInt(VIEWTYPE1, 0);
|
||||
viewtype2 = getArguments().getInt(VIEWTYPE2, 0);
|
||||
|
||||
getLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
@@ -72,7 +70,7 @@ public class ProfileDocumentsFragment
|
||||
this.noMedia = ViewUtil.findById(view, R.id.no_documents);
|
||||
this.gridManager = new StickyHeaderGridLayoutManager(1);
|
||||
|
||||
this.recyclerView.setAdapter(new ProfileDocumentsAdapter(getContext(),
|
||||
this.recyclerView.setAdapter(new AllMediaDocumentsAdapter(getContext(),
|
||||
new BucketedThreadMediaLoader.BucketedThreadMedia(getContext()),
|
||||
this));
|
||||
this.recyclerView.setLayoutManager(gridManager);
|
||||
@@ -106,32 +104,26 @@ public class ProfileDocumentsFragment
|
||||
|
||||
@Override
|
||||
public Loader<BucketedThreadMediaLoader.BucketedThreadMedia> onCreateLoader(int i, Bundle bundle) {
|
||||
if (showAudio) {
|
||||
return new BucketedThreadMediaLoader(getContext(), chatId, DcMsg.DC_MSG_AUDIO, DcMsg.DC_MSG_VOICE, 0);
|
||||
} else if (showWebxdc) {
|
||||
return new BucketedThreadMediaLoader(getContext(), chatId, DcMsg.DC_MSG_WEBXDC, 0, 0);
|
||||
} else {
|
||||
return new BucketedThreadMediaLoader(getContext(), chatId, DcMsg.DC_MSG_FILE, 0, 0);
|
||||
}
|
||||
return new BucketedThreadMediaLoader(getContext(), chatId, viewtype1, viewtype2, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<BucketedThreadMediaLoader.BucketedThreadMedia> loader, BucketedThreadMediaLoader.BucketedThreadMedia bucketedThreadMedia) {
|
||||
((ProfileDocumentsAdapter) recyclerView.getAdapter()).setMedia(bucketedThreadMedia);
|
||||
((ProfileDocumentsAdapter) recyclerView.getAdapter()).notifyAllSectionsDataSetChanged();
|
||||
((AllMediaDocumentsAdapter) recyclerView.getAdapter()).setMedia(bucketedThreadMedia);
|
||||
((AllMediaDocumentsAdapter) recyclerView.getAdapter()).notifyAllSectionsDataSetChanged();
|
||||
|
||||
noMedia.setVisibility(recyclerView.getAdapter().getItemCount() > 0 ? View.GONE : View.VISIBLE);
|
||||
if (chatId == DC_CHAT_NO_CHAT) {
|
||||
if (showWebxdc) {
|
||||
if (viewtype1 == DcMsg.DC_MSG_WEBXDC) {
|
||||
noMedia.setText(R.string.all_apps_empty_hint);
|
||||
} else if (!showAudio){
|
||||
} else if (viewtype1 == DcMsg.DC_MSG_FILE){
|
||||
noMedia.setText(R.string.all_files_empty_hint);
|
||||
} else {
|
||||
noMedia.setText(R.string.tab_all_media_empty_hint);
|
||||
}
|
||||
} else if (showAudio) {
|
||||
} else if (viewtype1 == DcMsg.DC_MSG_AUDIO) {
|
||||
noMedia.setText(R.string.tab_audio_empty_hint);
|
||||
} else if (showWebxdc) {
|
||||
} else if (viewtype1 == DcMsg.DC_MSG_WEBXDC) {
|
||||
noMedia.setText(R.string.tab_webxdc_empty_hint);
|
||||
}
|
||||
getActivity().invalidateOptionsMenu();
|
||||
@@ -139,7 +131,7 @@ public class ProfileDocumentsFragment
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<BucketedThreadMediaLoader.BucketedThreadMedia> cursorLoader) {
|
||||
((ProfileDocumentsAdapter) recyclerView.getAdapter()).setMedia(new BucketedThreadMediaLoader.BucketedThreadMedia(getContext()));
|
||||
((AllMediaDocumentsAdapter) recyclerView.getAdapter()).setMedia(new BucketedThreadMediaLoader.BucketedThreadMedia(getContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -157,7 +149,7 @@ public class ProfileDocumentsFragment
|
||||
}
|
||||
|
||||
private void handleMediaMultiSelectClick(@NonNull DcMsg mediaRecord) {
|
||||
ProfileDocumentsAdapter adapter = getListAdapter();
|
||||
AllMediaDocumentsAdapter adapter = getListAdapter();
|
||||
|
||||
adapter.toggleSelection(mediaRecord);
|
||||
if (adapter.getSelectedMediaCount() == 0) {
|
||||
@@ -180,7 +172,7 @@ public class ProfileDocumentsFragment
|
||||
}
|
||||
|
||||
if (dcMsg.getType() == DcMsg.DC_MSG_WEBXDC) {
|
||||
DcHelper.openWebxdc(context, dcMsg);
|
||||
WebxdcActivity.openWebxdcActivity(context, dcMsg);
|
||||
} else {
|
||||
DcHelper.openForViewOrShare(getActivity(), dcMsg.getId(), Intent.ACTION_VIEW);
|
||||
}
|
||||
@@ -189,7 +181,7 @@ public class ProfileDocumentsFragment
|
||||
@Override
|
||||
public void onMediaLongClicked(DcMsg mediaRecord) {
|
||||
if (actionMode == null) {
|
||||
((ProfileDocumentsAdapter) recyclerView.getAdapter()).toggleSelection(mediaRecord);
|
||||
((AllMediaDocumentsAdapter) recyclerView.getAdapter()).toggleSelection(mediaRecord);
|
||||
|
||||
actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(actionModeCallback);
|
||||
}
|
||||
@@ -222,22 +214,17 @@ public class ProfileDocumentsFragment
|
||||
menu.findItem(R.id.menu_add_to_home_screen).setVisible(webxdcApp);
|
||||
}
|
||||
|
||||
private ProfileDocumentsAdapter getListAdapter() {
|
||||
return (ProfileDocumentsAdapter) recyclerView.getAdapter();
|
||||
private AllMediaDocumentsAdapter getListAdapter() {
|
||||
return (AllMediaDocumentsAdapter) recyclerView.getAdapter();
|
||||
}
|
||||
|
||||
private class ActionModeCallback implements ActionMode.Callback {
|
||||
|
||||
private int originalStatusBarColor;
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
mode.getMenuInflater().inflate(R.menu.profile_context, menu);
|
||||
mode.setTitle("1");
|
||||
|
||||
Window window = getActivity().getWindow();
|
||||
originalStatusBarColor = window.getStatusBarColor();
|
||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
setCorrectMenuVisibility(menu);
|
||||
return true;
|
||||
}
|
||||
@@ -249,35 +236,35 @@ public class ProfileDocumentsFragment
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem) {
|
||||
switch (menuItem.getItemId()) {
|
||||
case R.id.details:
|
||||
handleDisplayDetails(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
|
||||
mode.finish();
|
||||
return true;
|
||||
case R.id.delete:
|
||||
handleDeleteMessages(chatId, getListAdapter().getSelectedMedia());
|
||||
mode.finish();
|
||||
return true;
|
||||
case R.id.share:
|
||||
handleShare(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
|
||||
return true;
|
||||
case R.id.menu_add_to_home_screen:
|
||||
WebxdcActivity.addToHomeScreen(getActivity(), getSelectedMessageRecord(getListAdapter().getSelectedMedia()).getId());
|
||||
mode.finish();
|
||||
return true;
|
||||
case R.id.show_in_chat:
|
||||
handleShowInChat(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
|
||||
return true;
|
||||
case R.id.save:
|
||||
handleSaveAttachment(getListAdapter().getSelectedMedia());
|
||||
return true;
|
||||
case R.id.menu_resend:
|
||||
handleResendMessage(getListAdapter().getSelectedMedia());
|
||||
return true;
|
||||
case R.id.menu_select_all:
|
||||
getListAdapter().selectAll();
|
||||
updateActionModeBar();
|
||||
return true;
|
||||
int itemId = menuItem.getItemId();
|
||||
if (itemId == R.id.details) {
|
||||
handleDisplayDetails(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
|
||||
mode.finish();
|
||||
return true;
|
||||
} else if (itemId == R.id.delete) {
|
||||
handleDeleteMessages(chatId, getListAdapter().getSelectedMedia());
|
||||
mode.finish();
|
||||
return true;
|
||||
} else if (itemId == R.id.share) {
|
||||
handleShare(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_add_to_home_screen) {
|
||||
WebxdcActivity.addToHomeScreen(getActivity(), getSelectedMessageRecord(getListAdapter().getSelectedMedia()).getId());
|
||||
mode.finish();
|
||||
return true;
|
||||
} else if (itemId == R.id.show_in_chat) {
|
||||
handleShowInChat(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
|
||||
return true;
|
||||
} else if (itemId == R.id.save) {
|
||||
handleSaveAttachment(getListAdapter().getSelectedMedia());
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_resend) {
|
||||
handleResendMessage(getListAdapter().getSelectedMedia());
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_select_all) {
|
||||
getListAdapter().selectAll();
|
||||
updateActionModeBar();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -286,8 +273,6 @@ public class ProfileDocumentsFragment
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
actionMode = null;
|
||||
getListAdapter().clearSelection();
|
||||
|
||||
getActivity().getWindow().setStatusBarColor(originalStatusBarColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.b44t.messenger.DcMsg;
|
||||
import com.codewaves.stickyheadergrid.StickyHeaderGridAdapter;
|
||||
|
||||
@@ -20,7 +21,7 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
class ProfileGalleryAdapter extends StickyHeaderGridAdapter {
|
||||
class AllMediaGalleryAdapter extends StickyHeaderGridAdapter {
|
||||
|
||||
private final Context context;
|
||||
private final GlideRequests glideRequests;
|
||||
@@ -49,10 +50,10 @@ class ProfileGalleryAdapter extends StickyHeaderGridAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
ProfileGalleryAdapter(@NonNull Context context,
|
||||
@NonNull GlideRequests glideRequests,
|
||||
BucketedThreadMedia media,
|
||||
ItemClickListener clickListener)
|
||||
AllMediaGalleryAdapter(@NonNull Context context,
|
||||
@NonNull GlideRequests glideRequests,
|
||||
BucketedThreadMedia media,
|
||||
ItemClickListener clickListener)
|
||||
{
|
||||
this.context = context;
|
||||
this.glideRequests = glideRequests;
|
||||
@@ -5,22 +5,21 @@ import static com.b44t.messenger.DcChat.DC_CHAT_NO_CHAT;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcEvent;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
@@ -35,10 +34,10 @@ import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class ProfileGalleryFragment
|
||||
public class AllMediaGalleryFragment
|
||||
extends MessageSelectorFragment
|
||||
implements LoaderManager.LoaderCallbacks<BucketedThreadMediaLoader.BucketedThreadMedia>,
|
||||
ProfileGalleryAdapter.ItemClickListener
|
||||
AllMediaGalleryAdapter.ItemClickListener
|
||||
{
|
||||
public static final String CHAT_ID_EXTRA = "chat_id";
|
||||
|
||||
@@ -67,7 +66,7 @@ public class ProfileGalleryFragment
|
||||
this.noMedia = ViewUtil.findById(view, R.id.no_images);
|
||||
this.gridManager = new StickyHeaderGridLayoutManager(getCols());
|
||||
|
||||
this.recyclerView.setAdapter(new ProfileGalleryAdapter(getContext(),
|
||||
this.recyclerView.setAdapter(new AllMediaGalleryAdapter(getContext(),
|
||||
GlideApp.with(this),
|
||||
new BucketedThreadMediaLoader.BucketedThreadMedia(getContext()),
|
||||
this));
|
||||
@@ -112,8 +111,8 @@ public class ProfileGalleryFragment
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<BucketedThreadMediaLoader.BucketedThreadMedia> loader, BucketedThreadMediaLoader.BucketedThreadMedia bucketedThreadMedia) {
|
||||
((ProfileGalleryAdapter) recyclerView.getAdapter()).setMedia(bucketedThreadMedia);
|
||||
((ProfileGalleryAdapter) recyclerView.getAdapter()).notifyAllSectionsDataSetChanged();
|
||||
((AllMediaGalleryAdapter) recyclerView.getAdapter()).setMedia(bucketedThreadMedia);
|
||||
((AllMediaGalleryAdapter) recyclerView.getAdapter()).notifyAllSectionsDataSetChanged();
|
||||
|
||||
noMedia.setVisibility(recyclerView.getAdapter().getItemCount() > 0 ? View.GONE : View.VISIBLE);
|
||||
if (chatId == DC_CHAT_NO_CHAT) {
|
||||
@@ -124,7 +123,7 @@ public class ProfileGalleryFragment
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<BucketedThreadMediaLoader.BucketedThreadMedia> cursorLoader) {
|
||||
((ProfileGalleryAdapter) recyclerView.getAdapter()).setMedia(new BucketedThreadMediaLoader.BucketedThreadMedia(getContext()));
|
||||
((AllMediaGalleryAdapter) recyclerView.getAdapter()).setMedia(new BucketedThreadMediaLoader.BucketedThreadMedia(getContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -142,7 +141,7 @@ public class ProfileGalleryFragment
|
||||
}
|
||||
|
||||
private void handleMediaMultiSelectClick(@NonNull DcMsg mediaRecord) {
|
||||
ProfileGalleryAdapter adapter = getListAdapter();
|
||||
AllMediaGalleryAdapter adapter = getListAdapter();
|
||||
|
||||
adapter.toggleSelection(mediaRecord);
|
||||
if (adapter.getSelectedMediaCount() == 0) {
|
||||
@@ -175,7 +174,7 @@ public class ProfileGalleryFragment
|
||||
@Override
|
||||
public void onMediaLongClicked(DcMsg mediaRecord) {
|
||||
if (actionMode == null) {
|
||||
((ProfileGalleryAdapter) recyclerView.getAdapter()).toggleSelection(mediaRecord);
|
||||
((AllMediaGalleryAdapter) recyclerView.getAdapter()).toggleSelection(mediaRecord);
|
||||
recyclerView.getAdapter().notifyDataSetChanged();
|
||||
|
||||
actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(actionModeCallback);
|
||||
@@ -206,22 +205,17 @@ public class ProfileGalleryFragment
|
||||
menu.findItem(R.id.menu_resend).setVisible(canResend);
|
||||
}
|
||||
|
||||
private ProfileGalleryAdapter getListAdapter() {
|
||||
return (ProfileGalleryAdapter) recyclerView.getAdapter();
|
||||
private AllMediaGalleryAdapter getListAdapter() {
|
||||
return (AllMediaGalleryAdapter) recyclerView.getAdapter();
|
||||
}
|
||||
|
||||
private class ActionModeCallback implements ActionMode.Callback {
|
||||
|
||||
private int originalStatusBarColor;
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
mode.getMenuInflater().inflate(R.menu.profile_context, menu);
|
||||
mode.setTitle("1");
|
||||
|
||||
Window window = getActivity().getWindow();
|
||||
originalStatusBarColor = window.getStatusBarColor();
|
||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
setCorrectMenuVisibility(menu);
|
||||
return true;
|
||||
}
|
||||
@@ -233,31 +227,31 @@ public class ProfileGalleryFragment
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem) {
|
||||
switch (menuItem.getItemId()) {
|
||||
case R.id.details:
|
||||
handleDisplayDetails(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
|
||||
mode.finish();
|
||||
return true;
|
||||
case R.id.delete:
|
||||
handleDeleteMessages(chatId, getListAdapter().getSelectedMedia());
|
||||
mode.finish();
|
||||
return true;
|
||||
case R.id.share:
|
||||
handleShare(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
|
||||
return true;
|
||||
case R.id.show_in_chat:
|
||||
handleShowInChat(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
|
||||
return true;
|
||||
case R.id.save:
|
||||
handleSaveAttachment(getListAdapter().getSelectedMedia());
|
||||
return true;
|
||||
case R.id.menu_resend:
|
||||
handleResendMessage(getListAdapter().getSelectedMedia());
|
||||
return true;
|
||||
case R.id.menu_select_all:
|
||||
getListAdapter().selectAll();
|
||||
updateActionModeBar();
|
||||
return true;
|
||||
int itemId = menuItem.getItemId();
|
||||
if (itemId == R.id.details) {
|
||||
handleDisplayDetails(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
|
||||
mode.finish();
|
||||
return true;
|
||||
} else if (itemId == R.id.delete) {
|
||||
handleDeleteMessages(chatId, getListAdapter().getSelectedMedia());
|
||||
mode.finish();
|
||||
return true;
|
||||
} else if (itemId == R.id.share) {
|
||||
handleShare(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
|
||||
return true;
|
||||
} else if (itemId == R.id.show_in_chat) {
|
||||
handleShowInChat(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
|
||||
return true;
|
||||
} else if (itemId == R.id.save) {
|
||||
handleSaveAttachment(getListAdapter().getSelectedMedia());
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_resend) {
|
||||
handleResendMessage(getListAdapter().getSelectedMedia());
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_select_all) {
|
||||
getListAdapter().selectAll();
|
||||
updateActionModeBar();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -266,8 +260,6 @@ public class ProfileGalleryFragment
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
actionMode = null;
|
||||
getListAdapter().clearSelection();
|
||||
|
||||
getActivity().getWindow().setStatusBarColor(originalStatusBarColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import com.b44t.messenger.DcAccounts;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcEvent;
|
||||
import com.b44t.messenger.DcEventEmitter;
|
||||
import com.b44t.messenger.rpc.Rpc;
|
||||
import com.b44t.messenger.FFITransport;
|
||||
|
||||
import org.thoughtcrime.securesms.connect.AccountManager;
|
||||
import org.thoughtcrime.securesms.connect.DcEventCenter;
|
||||
@@ -52,6 +52,9 @@ import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import chat.delta.rpc.Rpc;
|
||||
import chat.delta.rpc.RpcException;
|
||||
|
||||
public class ApplicationContext extends MultiDexApplication {
|
||||
private static final String TAG = ApplicationContext.class.getSimpleName();
|
||||
|
||||
@@ -108,7 +111,7 @@ public class ApplicationContext extends MultiDexApplication {
|
||||
System.loadLibrary("native-utils");
|
||||
|
||||
dcAccounts = new DcAccounts(new File(getFilesDir(), "accounts").getAbsolutePath());
|
||||
rpc = new Rpc(dcAccounts.getJsonrpcInstance());
|
||||
rpc = new Rpc(new FFITransport(dcAccounts.getJsonrpcInstance()));
|
||||
AccountManager.getInstance().migrateToDcAccounts(this);
|
||||
int[] allAccounts = dcAccounts.getAll();
|
||||
for (int accountId : allAccounts) {
|
||||
@@ -126,7 +129,11 @@ public class ApplicationContext extends MultiDexApplication {
|
||||
}
|
||||
}
|
||||
if (allAccounts.length == 0) {
|
||||
dcAccounts.addAccount();
|
||||
try {
|
||||
rpc.addAccount();
|
||||
} catch (RpcException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
dcContext = dcAccounts.getSelectedAccount();
|
||||
notificationCenter = new NotificationCenter(this);
|
||||
@@ -143,8 +150,6 @@ public class ApplicationContext extends MultiDexApplication {
|
||||
Log.i("DeltaChat", "shutting down event handler");
|
||||
}, "eventThread").start();
|
||||
|
||||
rpc.start();
|
||||
|
||||
// migrating global notifications pref. to per-account config, added 10/July/24
|
||||
final String NOTIFICATION_PREF = "pref_key_enable_notifications";
|
||||
boolean isMuted = !Prefs.getBooleanPreference(this, NOTIFICATION_PREF, true);
|
||||
@@ -215,30 +220,30 @@ public class ApplicationContext extends MultiDexApplication {
|
||||
}
|
||||
}, filter);
|
||||
|
||||
// MAYBE TODO: i think the ApplicationContext is also created
|
||||
// when the app is stated by FetchWorker timeouts.
|
||||
// in this case, the normal threads shall not be started.
|
||||
Constraints constraints = new Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build();
|
||||
PeriodicWorkRequest fetchWorkRequest = new PeriodicWorkRequest.Builder(
|
||||
FetchWorker.class,
|
||||
PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS, // usually 15 minutes
|
||||
TimeUnit.MILLISECONDS,
|
||||
PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS, // the start may be preferred by up to 5 minutes, so we run every 10-15 minutes
|
||||
TimeUnit.MILLISECONDS)
|
||||
.setConstraints(constraints)
|
||||
.build();
|
||||
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
|
||||
"FetchWorker",
|
||||
ExistingPeriodicWorkPolicy.KEEP,
|
||||
fetchWorkRequest);
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
|
||||
|
||||
if (Prefs.isPushEnabled(this)) {
|
||||
FcmReceiveService.register(this);
|
||||
} else {
|
||||
Log.i(TAG, "FCM disabled at build time");
|
||||
// MAYBE TODO: i think the ApplicationContext is also created
|
||||
// when the app is stated by FetchWorker timeouts.
|
||||
// in this case, the normal threads shall not be started.
|
||||
Constraints constraints = new Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build();
|
||||
PeriodicWorkRequest fetchWorkRequest = new PeriodicWorkRequest.Builder(
|
||||
FetchWorker.class,
|
||||
PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS, // usually 15 minutes
|
||||
TimeUnit.MILLISECONDS,
|
||||
PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS, // the start may be preferred by up to 5 minutes, so we run every 10-15 minutes
|
||||
TimeUnit.MILLISECONDS)
|
||||
.setConstraints(constraints)
|
||||
.build();
|
||||
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
|
||||
"FetchWorker",
|
||||
ExistingPeriodicWorkPolicy.KEEP,
|
||||
fetchWorkRequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,22 +17,15 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
@@ -51,9 +44,10 @@ import org.thoughtcrime.securesms.preferences.PrivacyPreferenceFragment;
|
||||
import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment;
|
||||
import org.thoughtcrime.securesms.preferences.NotificationsPreferenceFragment;
|
||||
import org.thoughtcrime.securesms.preferences.widgets.ProfilePreference;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.Prefs;
|
||||
import org.thoughtcrime.securesms.qr.BackupTransferActivity;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.IntentUtils;
|
||||
import org.thoughtcrime.securesms.util.Prefs;
|
||||
import org.thoughtcrime.securesms.util.ScreenLockUtil;
|
||||
|
||||
/**
|
||||
@@ -74,17 +68,21 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
private static final String PREFERENCE_CATEGORY_MULTIDEVICE = "preference_category_multidevice";
|
||||
private static final String PREFERENCE_CATEGORY_ADVANCED = "preference_category_advanced";
|
||||
private static final String PREFERENCE_CATEGORY_CONNECTIVITY = "preference_category_connectivity";
|
||||
private static final String PREFERENCE_CATEGORY_DONATE = "preference_category_donate";
|
||||
private static final String PREFERENCE_CATEGORY_HELP = "preference_category_help";
|
||||
|
||||
public static final int REQUEST_CODE_SET_BACKGROUND = 11;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle, boolean ready) {
|
||||
setContentView(R.layout.activity_application_preferences);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
this.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setElevation(0); // TODO: use custom toolbar instead
|
||||
|
||||
if (icicle == null) {
|
||||
initFragment(android.R.id.content, new ApplicationPreferenceFragment());
|
||||
initFragment(R.id.fragment, new ApplicationPreferenceFragment());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +94,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
showBackupProvider();
|
||||
return;
|
||||
}
|
||||
Fragment fragment = getSupportFragmentManager().findFragmentById(android.R.id.content);
|
||||
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment);
|
||||
fragment.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@@ -153,6 +151,9 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
this.findPreference(PREFERENCE_CATEGORY_ADVANCED)
|
||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_ADVANCED));
|
||||
|
||||
this.findPreference(PREFERENCE_CATEGORY_DONATE)
|
||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_DONATE));
|
||||
|
||||
this.findPreference(PREFERENCE_CATEGORY_HELP)
|
||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_HELP));
|
||||
|
||||
@@ -203,38 +204,6 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
.setSummary(AdvancedPreferenceFragment.getVersion(getActivity()));
|
||||
}
|
||||
|
||||
@TargetApi(11)
|
||||
private void tintIcons(Context context) {
|
||||
Drawable notifications = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_notifications_white_24dp));
|
||||
Drawable swap = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_swap_vert_white_24dp));
|
||||
Drawable appearance = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_brightness_6_white_24dp));
|
||||
Drawable chats = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_forum_white_24dp));
|
||||
Drawable privacy = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_lock_white_24dp));
|
||||
Drawable advanced = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_advanced_white_24dp));
|
||||
Drawable help = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_help_white_24dp));
|
||||
|
||||
int[] tintAttr = new int[]{R.attr.pref_icon_tint};
|
||||
TypedArray typedArray = context.obtainStyledAttributes(tintAttr);
|
||||
int color = typedArray.getColor(0, 0x0);
|
||||
typedArray.recycle();
|
||||
|
||||
DrawableCompat.setTint(notifications, color);
|
||||
DrawableCompat.setTint(swap, color);
|
||||
DrawableCompat.setTint(appearance, color);
|
||||
DrawableCompat.setTint(chats, color);
|
||||
DrawableCompat.setTint(privacy, color);
|
||||
DrawableCompat.setTint(advanced, color);
|
||||
DrawableCompat.setTint(help, color);
|
||||
|
||||
this.findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS).setIcon(notifications);
|
||||
this.findPreference(PREFERENCE_CATEGORY_CONNECTIVITY).setIcon(swap);
|
||||
this.findPreference(PREFERENCE_CATEGORY_APPEARANCE).setIcon(appearance);
|
||||
this.findPreference(PREFERENCE_CATEGORY_CHATS).setIcon(chats);
|
||||
this.findPreference(PREFERENCE_CATEGORY_PRIVACY).setIcon(privacy);
|
||||
this.findPreference(PREFERENCE_CATEGORY_ADVANCED).setIcon(advanced);
|
||||
this.findPreference(PREFERENCE_CATEGORY_HELP).setIcon(help);
|
||||
}
|
||||
|
||||
private class CategoryClickListener implements Preference.OnPreferenceClickListener {
|
||||
private final String category;
|
||||
|
||||
@@ -289,6 +258,9 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
case PREFERENCE_CATEGORY_ADVANCED:
|
||||
fragment = new AdvancedPreferenceFragment();
|
||||
break;
|
||||
case PREFERENCE_CATEGORY_DONATE:
|
||||
IntentUtils.showInBrowser(requireActivity(), "https://arcanechat.me/#contribute");
|
||||
break;
|
||||
case PREFERENCE_CATEGORY_HELP:
|
||||
startActivity(new Intent(getActivity(), LocalHelpActivity.class));
|
||||
break;
|
||||
@@ -302,7 +274,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
|
||||
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
|
||||
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
|
||||
fragmentTransaction.replace(android.R.id.content, fragment);
|
||||
fragmentTransaction.replace(R.id.fragment, fragment);
|
||||
fragmentTransaction.addToBackStack(null);
|
||||
fragmentTransaction.commit();
|
||||
}
|
||||
|
||||
@@ -9,9 +9,8 @@ public class AttachContactActivity extends ContactSelectionActivity {
|
||||
public static final String CONTACT_ID_EXTRA = "contact_id_extra";
|
||||
|
||||
@Override
|
||||
public void onContactSelected(int specialId, String addr) {
|
||||
public void onContactSelected(int contactId) {
|
||||
Intent intent = new Intent();
|
||||
int contactId = DcHelper.getContext(this).lookupContactIdByAddr(addr);
|
||||
intent.putExtra(CONTACT_ID_EXTRA, contactId);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.view.WindowCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
@@ -33,7 +33,9 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
onPreCreate();
|
||||
EdgeToEdge.enable(this); // docs says to use: WindowCompat.enableEdgeToEdge(getWindow()); but it is not available
|
||||
super.onCreate(savedInstanceState);
|
||||
WindowCompat.getInsetsController(getWindow(), getWindow().getDecorView()).setAppearanceLightStatusBars(false); // force white text in status bar
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -14,17 +14,17 @@ import androidx.appcompat.app.AlertDialog;
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
import com.b44t.messenger.rpc.Rpc;
|
||||
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import chat.delta.rpc.Rpc;
|
||||
|
||||
public abstract class BaseConversationItem extends LinearLayout
|
||||
implements BindableConversationItem
|
||||
{
|
||||
@@ -93,7 +93,7 @@ public abstract class BaseConversationItem extends LinearLayout
|
||||
protected boolean shouldInterceptClicks(DcMsg messageRecord) {
|
||||
return batchSelected.isEmpty()
|
||||
&& (messageRecord.isFailed()
|
||||
|| messageRecord.getInfoType() == DcMsg.DC_INFO_PROTECTION_DISABLED
|
||||
|| messageRecord.getInfoType() == DcMsg.DC_INFO_CHAT_E2EE
|
||||
|| messageRecord.getInfoType() == DcMsg.DC_INFO_PROTECTION_ENABLED
|
||||
|| messageRecord.getInfoType() == DcMsg.DC_INFO_INVALID_UNENCRYPTED_MAIL);
|
||||
}
|
||||
@@ -141,9 +141,7 @@ public abstract class BaseConversationItem extends LinearLayout
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.create();
|
||||
d.show();
|
||||
} else if (messageRecord.getInfoType() == DcMsg.DC_INFO_PROTECTION_DISABLED) {
|
||||
DcHelper.showVerificationBrokenDialog(context, conversationRecipient.getName());
|
||||
} else if (messageRecord.getInfoType() == DcMsg.DC_INFO_PROTECTION_ENABLED) {
|
||||
} else if (messageRecord.getInfoType() == DcMsg.DC_INFO_CHAT_E2EE || messageRecord.getInfoType() == DcMsg.DC_INFO_PROTECTION_ENABLED) {
|
||||
DcHelper.showProtectionEnabledDialog(context);
|
||||
} else if (messageRecord.getInfoType() == DcMsg.DC_INFO_INVALID_UNENCRYPTED_MAIL) {
|
||||
DcHelper.showInvalidUnencryptedDialog(context);
|
||||
|
||||
@@ -10,20 +10,22 @@ import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.content.pm.ShortcutInfoCompat;
|
||||
import androidx.core.content.pm.ShortcutManagerCompat;
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.b44t.messenger.DcChat;
|
||||
@@ -33,6 +35,7 @@ import com.google.android.material.snackbar.Snackbar;
|
||||
import org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.connect.DirectShareUtil;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.RelayUtil;
|
||||
import org.thoughtcrime.securesms.util.SendRelayedMessageUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
@@ -316,9 +319,41 @@ public abstract class BaseConversationListFragment extends Fragment implements A
|
||||
updateActionModeItems(actionMode.getMenu());
|
||||
}
|
||||
|
||||
private void handleAddToHomeScreen() {
|
||||
final Activity activity = requireActivity();
|
||||
final DcContext dcContext = DcHelper.getContext(activity);
|
||||
final Set<Long> selectedChats = getListAdapter().getBatchSelections();
|
||||
final DcChat chat = dcContext.getChat(selectedChats.iterator().next().intValue());
|
||||
|
||||
Intent intent = new Intent(activity, ShareActivity.class);
|
||||
intent.setAction(Intent.ACTION_SEND);
|
||||
intent.putExtra(ShareActivity.EXTRA_ACC_ID, dcContext.getAccountId());
|
||||
intent.putExtra(ShareActivity.EXTRA_CHAT_ID, chat.getId());
|
||||
|
||||
Recipient recipient = new Recipient(activity, chat);
|
||||
Util.runOnAnyBackgroundThread(() -> {
|
||||
Bitmap avatar = DirectShareUtil.getIconForShortcut(activity, recipient);
|
||||
ShortcutInfoCompat shortcutInfoCompat = new ShortcutInfoCompat.Builder(activity, "chat-" + dcContext.getAccountId() + "-" + chat.getId())
|
||||
.setShortLabel(chat.getName())
|
||||
.setIcon(IconCompat.createWithAdaptiveBitmap(avatar))
|
||||
.setIntent(intent)
|
||||
.build();
|
||||
Util.runOnMain(() -> {
|
||||
if (!ShortcutManagerCompat.requestPinShortcut(activity, shortcutInfoCompat, null)) {
|
||||
Toast.makeText(activity, "ErrAddToHomescreen: requestPinShortcut() failed", Toast.LENGTH_LONG).show();
|
||||
} else if (actionMode != null) {
|
||||
actionMode.finish();
|
||||
actionMode = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void updateActionModeItems(Menu menu) {
|
||||
// We do not show action mode icons when relaying (= sharing or forwarding).
|
||||
if (!isRelayingMessageContent(requireActivity())) {
|
||||
final int selectedCount = getListAdapter().getBatchSelections().size();
|
||||
menu.findItem(R.id.menu_add_to_home_screen).setVisible(selectedCount == 1);
|
||||
MenuItem archiveItem = menu.findItem(R.id.menu_archive_selected);
|
||||
if (offerToArchive()) {
|
||||
archiveItem.setIcon(R.drawable.ic_archive_white_24dp);
|
||||
@@ -364,8 +399,6 @@ public abstract class BaseConversationListFragment extends Fragment implements A
|
||||
|
||||
mode.setTitle("1");
|
||||
|
||||
requireActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -376,13 +409,28 @@ public abstract class BaseConversationListFragment extends Fragment implements A
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_select_all: handleSelectAllThreads(); return true;
|
||||
case R.id.menu_delete_selected: handleDeleteAllSelected(); return true;
|
||||
case R.id.menu_pin_selected: handlePinAllSelected(); return true;
|
||||
case R.id.menu_archive_selected: handleArchiveAllSelected(); return true;
|
||||
case R.id.menu_mute_selected: handleMuteAllSelected(); return true;
|
||||
case R.id.menu_marknoticed_selected: handleMarknoticedSelected(); return true;
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == R.id.menu_select_all) {
|
||||
handleSelectAllThreads();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_delete_selected) {
|
||||
handleDeleteAllSelected();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_pin_selected) {
|
||||
handlePinAllSelected();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_archive_selected) {
|
||||
handleArchiveAllSelected();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_mute_selected) {
|
||||
handleMuteAllSelected();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_marknoticed_selected) {
|
||||
handleMarknoticedSelected();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_add_to_home_screen) {
|
||||
handleAddToHomeScreen();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -393,10 +441,6 @@ public abstract class BaseConversationListFragment extends Fragment implements A
|
||||
actionMode = null;
|
||||
getListAdapter().initializeBatchMode(false);
|
||||
|
||||
TypedArray color = requireActivity().getTheme().obtainStyledAttributes(new int[]{android.R.attr.statusBarColor});
|
||||
requireActivity().getWindow().setStatusBarColor(color.getColor(0, Color.BLACK));
|
||||
color.recycle();
|
||||
|
||||
Context context = getContext();
|
||||
if (context != null) {
|
||||
fab.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_add_white_24dp));
|
||||
|
||||
@@ -25,6 +25,7 @@ public interface BindableConversationItem extends Unbindable {
|
||||
|
||||
interface EventListener {
|
||||
void onQuoteClicked(DcMsg messageRecord);
|
||||
void onJumpToOriginalClicked(DcMsg messageRecord);
|
||||
void onShowFullClicked(DcMsg messageRecord);
|
||||
void onDownloadClicked(DcMsg messageRecord);
|
||||
void onReactionClicked(DcMsg messageRecord);
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcEvent;
|
||||
|
||||
@@ -30,9 +30,11 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle, boolean ready) {
|
||||
setContentView(R.layout.activity_blocked_contacts);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setTitle(R.string.pref_blocked_contacts);
|
||||
initFragment(android.R.id.content, new BlockedAndShareContactsFragment(), getIntent().getExtras());
|
||||
getSupportActionBar().setElevation(0); // TODO: use custom toolbar instead
|
||||
initFragment(R.id.fragment, new BlockedAndShareContactsFragment(), getIntent().getExtras());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,6 +33,8 @@ import java.util.List;
|
||||
*/
|
||||
public class ContactMultiSelectionActivity extends ContactSelectionActivity {
|
||||
|
||||
public static final String CONTACTS_EXTRA = "contacts_extra";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle, boolean ready) {
|
||||
getIntent().putExtra(ContactSelectionListFragment.MULTI_SELECT, true);
|
||||
@@ -57,11 +59,10 @@ public class ContactMultiSelectionActivity extends ContactSelectionActivity {
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_add_members:
|
||||
saveSelection();
|
||||
finish();
|
||||
return true;
|
||||
if (item.getItemId() == R.id.menu_add_members) {
|
||||
saveSelection();
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -69,12 +70,8 @@ public class ContactMultiSelectionActivity extends ContactSelectionActivity {
|
||||
|
||||
private void saveSelection() {
|
||||
Intent resultIntent = getIntent();
|
||||
List<String> selectedContacts = contactsFragment.getSelectedContacts();
|
||||
|
||||
if (selectedContacts != null) {
|
||||
resultIntent.putStringArrayListExtra("contacts", new ArrayList<>(selectedContacts));
|
||||
}
|
||||
|
||||
List<Integer> selectedContacts = contactsFragment.getSelectedContacts();
|
||||
resultIntent.putIntegerArrayListExtra(CONTACTS_EXTRA, new ArrayList<>(selectedContacts));
|
||||
setResult(RESULT_OK, resultIntent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,8 +89,8 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactSelected(int specialId, String number) {}
|
||||
public void onContactSelected(int contactId) {}
|
||||
|
||||
@Override
|
||||
public void onContactDeselected(int specialId, String number) {}
|
||||
public void onContactDeselected(int contactId) {}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,7 @@ import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.util.SparseIntArray;
|
||||
@@ -36,7 +33,6 @@ import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
@@ -84,13 +80,14 @@ public class ContactSelectionListFragment extends Fragment
|
||||
|
||||
public static final String MULTI_SELECT = "multi_select";
|
||||
public static final String SELECT_VERIFIED_EXTRA = "select_verified";
|
||||
public static final String SELECT_UNENCRYPTED_EXTRA = "select_unencrypted_extra";
|
||||
public static final String ALLOW_CREATION = "allow_creation";
|
||||
public static final String PRESELECTED_CONTACTS = "preselected_contacts";
|
||||
public static final int CONTACT_ADDR_RESULT_CODE = 61123;
|
||||
|
||||
private DcContext dcContext;
|
||||
|
||||
private Set<String> selectedContacts;
|
||||
private Set<Integer> selectedContacts;
|
||||
private OnContactSelectedListener onContactSelectedListener;
|
||||
private String cursorFilter;
|
||||
private RecyclerView recyclerView;
|
||||
@@ -136,7 +133,6 @@ public class ContactSelectionListFragment extends Fragment
|
||||
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
|
||||
MenuInflater inflater = getActivity().getMenuInflater();
|
||||
inflater.inflate(R.menu.contact_list, menu);
|
||||
getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
setCorrectMenuVisibility(menu);
|
||||
actionMode.setTitle("1");
|
||||
return true;
|
||||
@@ -149,16 +145,16 @@ public class ContactSelectionListFragment extends Fragment
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
|
||||
switch (menuItem.getItemId()) {
|
||||
case R.id.menu_select_all:
|
||||
handleSelectAll();
|
||||
return true;
|
||||
case R.id.menu_view_profile:
|
||||
handleViewProfile();
|
||||
return true;
|
||||
case R.id.menu_delete_selected:
|
||||
handleDeleteSelected();
|
||||
return true;
|
||||
int itemId = menuItem.getItemId();
|
||||
if (itemId == R.id.menu_select_all) {
|
||||
handleSelectAll();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_view_profile) {
|
||||
handleViewProfile();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_delete_selected) {
|
||||
handleDeleteSelected();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -167,10 +163,6 @@ public class ContactSelectionListFragment extends Fragment
|
||||
public void onDestroyActionMode(ActionMode actionMode) {
|
||||
ContactSelectionListFragment.this.actionMode = null;
|
||||
getContactSelectionListAdapter().resetActionModeSelection();
|
||||
|
||||
TypedArray color = getActivity().getTheme().obtainStyledAttributes(new int[]{android.R.attr.statusBarColor});
|
||||
getActivity().getWindow().setStatusBarColor(color.getColor(0, Color.BLACK));
|
||||
color.recycle();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -213,16 +205,9 @@ public class ContactSelectionListFragment extends Fragment
|
||||
ContactSelectionListAdapter adapter = getContactSelectionListAdapter();
|
||||
final SparseIntArray actionModeSelection = adapter.getActionModeSelection().clone();
|
||||
new Thread(() -> {
|
||||
boolean failed = false;
|
||||
for (int index = 0; index < actionModeSelection.size(); index++) {
|
||||
int contactId = actionModeSelection.valueAt(index);
|
||||
boolean currentFailed = !dcContext.deleteContact(contactId);
|
||||
failed = currentFailed || failed;
|
||||
}
|
||||
if (failed) {
|
||||
Util.runOnMain(()-> {
|
||||
Toast.makeText(getActivity(), R.string.cannot_delete_contacts_in_use, Toast.LENGTH_LONG).show();
|
||||
});
|
||||
dcContext.deleteContact(contactId);
|
||||
}
|
||||
}).start();
|
||||
adapter.resetActionModeSelection();
|
||||
@@ -242,8 +227,8 @@ public class ContactSelectionListFragment extends Fragment
|
||||
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
public @NonNull List<String> getSelectedContacts() {
|
||||
List<String> selected = new LinkedList<>();
|
||||
public @NonNull List<Integer> getSelectedContacts() {
|
||||
List<Integer> selected = new LinkedList<>();
|
||||
if (selectedContacts != null) {
|
||||
selected.addAll(selectedContacts);
|
||||
}
|
||||
@@ -259,6 +244,10 @@ public class ContactSelectionListFragment extends Fragment
|
||||
return getActivity().getIntent().getBooleanExtra(SELECT_VERIFIED_EXTRA, false);
|
||||
}
|
||||
|
||||
private boolean isUnencrypted() {
|
||||
return getActivity().getIntent().getBooleanExtra(SELECT_UNENCRYPTED_EXTRA, false);
|
||||
}
|
||||
|
||||
private void initializeCursor() {
|
||||
ContactSelectionListAdapter adapter = new ContactSelectionListAdapter(getActivity(),
|
||||
GlideApp.with(this),
|
||||
@@ -266,7 +255,7 @@ public class ContactSelectionListFragment extends Fragment
|
||||
isMulti(),
|
||||
true);
|
||||
selectedContacts = adapter.getSelectedContacts();
|
||||
ArrayList<String> preselectedContacts = getActivity().getIntent().getStringArrayListExtra(PRESELECTED_CONTACTS);
|
||||
ArrayList<Integer> preselectedContacts = getActivity().getIntent().getIntegerArrayListExtra(PRESELECTED_CONTACTS);
|
||||
if(preselectedContacts!=null) {
|
||||
selectedContacts.addAll(preselectedContacts);
|
||||
}
|
||||
@@ -281,11 +270,11 @@ public class ContactSelectionListFragment extends Fragment
|
||||
@Override
|
||||
public Loader<DcContactsLoader.Ret> onCreateLoader(int id, Bundle args) {
|
||||
final boolean allowCreation = getActivity().getIntent().getBooleanExtra(ALLOW_CREATION, true);
|
||||
final boolean addCreateContactLink = allowCreation && !isSelectVerfied();
|
||||
final boolean addCreateContactLink = allowCreation && isUnencrypted();
|
||||
final boolean addCreateGroupLinks = allowCreation && !isRelayingMessageContent(getActivity()) && !isMulti();
|
||||
final boolean addScanQRLink = allowCreation && !isMulti();
|
||||
|
||||
final int listflags = DcContext.DC_GCL_ADD_SELF;
|
||||
final int listflags = DcContext.DC_GCL_ADD_SELF | (isUnencrypted()? DcContext.DC_GCL_ADDRESS : 0);
|
||||
return new DcContactsLoader(getActivity(), listflags, cursorFilter, addCreateGroupLinks, addCreateContactLink, addScanQRLink, false);
|
||||
}
|
||||
|
||||
@@ -344,10 +333,9 @@ public class ContactSelectionListFragment extends Fragment
|
||||
}
|
||||
return;
|
||||
}
|
||||
int specialId = contact.getSpecialId();
|
||||
String addr = contact.getNumber();
|
||||
if (!isMulti() || !selectedContacts.contains(addr)) {
|
||||
if (specialId == DcContact.DC_CONTACT_ID_NEW_CLASSIC_CONTACT) {
|
||||
int contactId = contact.getSpecialId();
|
||||
if (!isMulti() || !selectedContacts.contains(contactId)) {
|
||||
if (contactId == DcContact.DC_CONTACT_ID_NEW_CLASSIC_CONTACT) {
|
||||
Intent intent = new Intent(getContext(), NewContactActivity.class);
|
||||
if (dcContext.mayBeValidAddr(cursorFilter)) {
|
||||
intent.putExtra(NewContactActivity.ADDR_EXTRA, cursorFilter);
|
||||
@@ -371,16 +359,16 @@ public class ContactSelectionListFragment extends Fragment
|
||||
return;
|
||||
}
|
||||
|
||||
selectedContacts.add(addr);
|
||||
selectedContacts.add(contactId);
|
||||
contact.setChecked(true);
|
||||
if (onContactSelectedListener != null) {
|
||||
onContactSelectedListener.onContactSelected(specialId, addr);
|
||||
onContactSelectedListener.onContactSelected(contactId);
|
||||
}
|
||||
} else {
|
||||
selectedContacts.remove(addr);
|
||||
selectedContacts.remove(contactId);
|
||||
contact.setChecked(false);
|
||||
if (onContactSelectedListener != null) {
|
||||
onContactSelectedListener.onContactDeselected(specialId, addr);
|
||||
onContactSelectedListener.onContactDeselected(contactId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -406,8 +394,8 @@ public class ContactSelectionListFragment extends Fragment
|
||||
}
|
||||
|
||||
public interface OnContactSelectedListener {
|
||||
void onContactSelected(int specialId, String number);
|
||||
void onContactDeselected(int specialId, String number);
|
||||
void onContactSelected(int contactId);
|
||||
void onContactDeselected(int contactId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -421,7 +409,10 @@ public class ContactSelectionListFragment extends Fragment
|
||||
public void onActivityResult(int reqCode, int resultCode, final Intent data) {
|
||||
super.onActivityResult(reqCode, resultCode, data);
|
||||
if (resultCode == Activity.RESULT_OK && reqCode == CONTACT_ADDR_RESULT_CODE) {
|
||||
selectedContacts.add(data.getStringExtra(NewContactActivity.ADDR_EXTRA));
|
||||
int contactId = data.getIntExtra(NewContactActivity.CONTACT_ID_EXTRA, 0);
|
||||
if (contactId != 0) {
|
||||
selectedContacts.add(contactId);
|
||||
}
|
||||
getLoaderManager().restartLoader(0, null, ContactSelectionListFragment.this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
@@ -73,8 +74,6 @@ import com.b44t.messenger.DcContact;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcEvent;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
import com.b44t.messenger.rpc.Rpc;
|
||||
import com.b44t.messenger.rpc.RpcException;
|
||||
import com.b44t.messenger.util.concurrent.ListenableFuture;
|
||||
import com.b44t.messenger.util.concurrent.SettableFuture;
|
||||
|
||||
@@ -121,13 +120,17 @@ import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener;
|
||||
import org.thoughtcrime.securesms.util.guava.Optional;
|
||||
import org.thoughtcrime.securesms.util.views.ProgressDialog;
|
||||
import org.thoughtcrime.securesms.video.recode.VideoRecoder;
|
||||
import org.thoughtcrime.securesms.videochat.VideochatUtil;
|
||||
import org.thoughtcrime.securesms.calls.CallUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import chat.delta.rpc.Rpc;
|
||||
import chat.delta.rpc.RpcException;
|
||||
|
||||
/**
|
||||
* Activity for displaying a message thread, as well as
|
||||
* composing/sending a new message into that thread.
|
||||
@@ -195,6 +198,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
private boolean isDefaultSms = true;
|
||||
private boolean isSecurityInitialized = false;
|
||||
private boolean successfulForwardingAttempt = false;
|
||||
private boolean isEditing = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle state, boolean ready) {
|
||||
@@ -436,7 +440,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
getMenuInflater().inflate(R.menu.conversation, menu);
|
||||
|
||||
if (dcChat.isSelfTalk() || dcChat.isBroadcast()) {
|
||||
if (dcChat.isSelfTalk() || dcChat.isOutBroadcast()) {
|
||||
menu.findItem(R.id.menu_mute_notifications).setVisible(false);
|
||||
} else if(dcChat.isMuted()) {
|
||||
menu.findItem(R.id.menu_mute_notifications).setTitle(R.string.menu_unmute);
|
||||
@@ -446,12 +450,25 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
menu.findItem(R.id.menu_show_map).setVisible(false);
|
||||
}
|
||||
|
||||
if (!dcChat.canSend() || dcChat.isBroadcast() || dcChat.isMailingList()) {
|
||||
menu.findItem(R.id.menu_start_call).setVisible(
|
||||
Prefs.isCallsEnabled(this)
|
||||
&& dcChat.canSend()
|
||||
&& dcChat.isEncrypted()
|
||||
&& !dcChat.isSelfTalk()
|
||||
&& !dcChat.isMultiUser()
|
||||
);
|
||||
|
||||
if (!dcChat.isEncrypted() || !dcChat.canSend() || dcChat.isMailingList() ) {
|
||||
menu.findItem(R.id.menu_ephemeral_messages).setVisible(false);
|
||||
}
|
||||
|
||||
if (isMultiUser()) {
|
||||
if (dcChat.canSend() && !dcChat.isBroadcast() && !dcChat.isMailingList()) {
|
||||
if (dcChat.isInBroadcast() && !dcChat.isContactRequest()) {
|
||||
menu.findItem(R.id.menu_leave).setTitle(R.string.menu_leave_channel).setVisible(true);
|
||||
} else if (dcChat.isEncrypted()
|
||||
&& dcChat.canSend()
|
||||
&& !dcChat.isOutBroadcast()
|
||||
&& !dcChat.isMailingList()) {
|
||||
menu.findItem(R.id.menu_leave).setVisible(true);
|
||||
}
|
||||
}
|
||||
@@ -495,6 +512,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
Log.e(TAG, "cannot set up in-chat-search: ", e);
|
||||
}
|
||||
|
||||
if (!dcChat.canSend() || isEditing) {
|
||||
MenuItem attachItem = menu.findItem(R.id.menu_add_attachment);
|
||||
if (attachItem!=null) {
|
||||
attachItem.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
return true;
|
||||
}
|
||||
@@ -502,17 +526,46 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_leave: handleLeaveGroup(); return true;
|
||||
case R.id.menu_archive_chat: handleArchiveChat(); return true;
|
||||
case R.id.menu_clear_chat: fragment.handleClearChat(); return true;
|
||||
case R.id.menu_delete_chat: handleDeleteChat(); return true;
|
||||
case R.id.menu_mute_notifications: handleMuteNotifications(); return true;
|
||||
case R.id.menu_show_map: WebxdcActivity.openMaps(this, chatId); return true;
|
||||
case R.id.menu_search_up: handleMenuSearchNext(false); return true;
|
||||
case R.id.menu_search_down: handleMenuSearchNext(true); return true;
|
||||
case android.R.id.home: handleReturnToConversationList(); return true;
|
||||
case R.id.menu_ephemeral_messages: handleEphemeralMessages(); return true;
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == R.id.menu_add_attachment) {
|
||||
handleAddAttachment();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_leave) {
|
||||
handleLeaveGroup();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_archive_chat) {
|
||||
handleArchiveChat();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_clear_chat) {
|
||||
fragment.handleClearChat();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_delete_chat) {
|
||||
handleDeleteChat();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_mute_notifications) {
|
||||
handleMuteNotifications();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_show_map) {
|
||||
WebxdcActivity.openMaps(this, chatId);
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_start_call) {
|
||||
CallUtil.startCall(this, chatId);
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_all_media) {
|
||||
handleAllMedia();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_search_up) {
|
||||
handleMenuSearchNext(false);
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_search_down) {
|
||||
handleMenuSearchNext(true);
|
||||
return true;
|
||||
} else if (itemId == android.R.id.home) {
|
||||
handleReturnToConversationList();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_ephemeral_messages) {
|
||||
handleEphemeralMessages();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -584,15 +637,26 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
private void handleProfile() {
|
||||
Intent intent = new Intent(this, ProfileActivity.class);
|
||||
intent.putExtra(ProfileActivity.CHAT_ID_EXTRA, chatId);
|
||||
intent.putExtra(ProfileActivity.FROM_CHAT, true);
|
||||
startActivity(intent);
|
||||
overridePendingTransition(0, 0);
|
||||
}
|
||||
|
||||
private void handleAllMedia() {
|
||||
Intent intent = new Intent(this, AllMediaActivity.class);
|
||||
intent.putExtra(AllMediaActivity.CHAT_ID_EXTRA, chatId);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void handleLeaveGroup() {
|
||||
@StringRes int leaveLabel;
|
||||
if (dcChat.isInBroadcast()) {
|
||||
leaveLabel = R.string.menu_leave_channel;
|
||||
} else {
|
||||
leaveLabel = R.string.menu_leave_group;
|
||||
}
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(this)
|
||||
.setMessage(getString(R.string.ask_leave_group))
|
||||
.setPositiveButton(R.string.menu_leave_group, (d, which) -> {
|
||||
.setPositiveButton(leaveLabel, (d, which) -> {
|
||||
dcContext.removeContactFromChat(chatId, DcContact.DC_CONTACT_ID_SELF);
|
||||
Toast.makeText(this, getString(R.string.done), Toast.LENGTH_SHORT).show();
|
||||
})
|
||||
@@ -655,7 +719,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
if (!dcChat.isMultiUser()) {
|
||||
int[] contactIds = dcContext.getChatContacts(chatId);
|
||||
if (contactIds.length == 1 || contactIds.length == 2) {
|
||||
name = dcContext.getContact(contactIds[0]).getNameNAddr();
|
||||
name = dcContext.getContact(contactIds[0]).getDisplayName();
|
||||
}
|
||||
}
|
||||
new AlertDialog.Builder(this)
|
||||
@@ -708,6 +772,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
* @return
|
||||
*/
|
||||
private ListenableFuture<Boolean> initializeDraft() {
|
||||
isEditing = false;
|
||||
final SettableFuture<Boolean> future = new SettableFuture<>();
|
||||
DcMsg draft = dcContext.getDraft(chatId);
|
||||
final String sharedText = RelayUtil.getSharedText(this);
|
||||
@@ -875,6 +940,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
supportActionBar.setCustomView(R.layout.conversation_title_view);
|
||||
supportActionBar.setDisplayShowCustomEnabled(true);
|
||||
supportActionBar.setDisplayShowTitleEnabled(false);
|
||||
supportActionBar.setElevation(0); // TODO: use custom toolbar instead
|
||||
|
||||
Toolbar parent = (Toolbar) supportActionBar.getCustomView().getParent();
|
||||
parent.setPadding(0,0,0,0);
|
||||
@@ -919,8 +985,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
AttachmentManager.selectGallery(this, PICK_GALLERY); break;
|
||||
case AttachmentTypeSelector.ADD_DOCUMENT:
|
||||
AttachmentManager.selectDocument(this, PICK_DOCUMENT); break;
|
||||
case AttachmentTypeSelector.INVITE_VIDEO_CHAT:
|
||||
new VideochatUtil().invite(this, chatId); break;
|
||||
case AttachmentTypeSelector.ADD_CONTACT_INFO:
|
||||
startContactChooserActivity(); break;
|
||||
case AttachmentTypeSelector.ADD_LOCATION:
|
||||
@@ -959,7 +1023,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] vcard = rpc.makeVcard(dcContext.getAccountId(), contactId).getBytes();
|
||||
byte[] vcard = rpc.makeVcard(dcContext.getAccountId(), Collections.singletonList(contactId)).getBytes();
|
||||
String mimeType = "application/octet-stream";
|
||||
setMedia(PersistentBlobProvider.getInstance().create(this, vcard, mimeType, "vcard.vcf"), MediaType.DOCUMENT);
|
||||
} catch (RpcException e) {
|
||||
@@ -990,9 +1054,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
final SettableFuture<Integer> future = new SettableFuture<>();
|
||||
|
||||
DcMsg msg = null;
|
||||
Optional<QuoteModel> quote = inputPanel.getQuote();
|
||||
Integer recompress = 0;
|
||||
boolean editing = isEditing;
|
||||
|
||||
// for a quick ui feedback, we clear the related controls immediately on sending messages.
|
||||
// for drafts, however, we do not change the controls, the activity may be resumed.
|
||||
@@ -1001,110 +1064,114 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
inputPanel.clearQuote();
|
||||
}
|
||||
|
||||
if(slideDeck!=null) {
|
||||
Util.runOnAnyBackgroundThread(() -> {
|
||||
DcMsg msg = null;
|
||||
int recompress = 0;
|
||||
|
||||
if (action==ACTION_SEND_OUT) {
|
||||
attachmentManager.clear(glideRequests, false);
|
||||
if (editing) {
|
||||
int msgId = quote.get().getQuotedMsg().getId();
|
||||
if (action == ACTION_SEND_OUT) {
|
||||
dcContext.sendEditRequest(msgId, body);
|
||||
} else {
|
||||
dcContext.setDraft(chatId, null);
|
||||
}
|
||||
future.set(chatId);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (slideDeck.getWebxdctDraftId() != 0) {
|
||||
msg = dcContext.getDraft(chatId);
|
||||
} else {
|
||||
List<Attachment> attachments = slideDeck.asAttachments();
|
||||
for (Attachment attachment : attachments) {
|
||||
String contentType = attachment.getContentType();
|
||||
if (MediaUtil.isImageType(contentType) && slideDeck.getDocumentSlide() == null) {
|
||||
msg = new DcMsg(dcContext,
|
||||
MediaUtil.isGif(contentType) ? DcMsg.DC_MSG_GIF : DcMsg.DC_MSG_IMAGE);
|
||||
msg.setDimension(attachment.getWidth(), attachment.getHeight());
|
||||
} else if (MediaUtil.isAudioType(contentType)) {
|
||||
msg = new DcMsg(dcContext,
|
||||
attachment.isVoiceNote() ? DcMsg.DC_MSG_VOICE : DcMsg.DC_MSG_AUDIO);
|
||||
} else if (MediaUtil.isVideoType(contentType) && slideDeck.getDocumentSlide() == null) {
|
||||
msg = new DcMsg(dcContext, DcMsg.DC_MSG_VIDEO);
|
||||
recompress = DcMsg.DC_MSG_VIDEO;
|
||||
} else {
|
||||
msg = new DcMsg(dcContext, DcMsg.DC_MSG_FILE);
|
||||
if(slideDeck!=null) {
|
||||
if (action==ACTION_SEND_OUT) {
|
||||
Util.runOnMain(() -> attachmentManager.clear(glideRequests, false));
|
||||
}
|
||||
|
||||
try {
|
||||
if (slideDeck.getWebxdctDraftId() != 0) {
|
||||
msg = dcContext.getDraft(chatId);
|
||||
} else {
|
||||
List<Attachment> attachments = slideDeck.asAttachments();
|
||||
for (Attachment attachment : attachments) {
|
||||
String contentType = attachment.getContentType();
|
||||
if (MediaUtil.isImageType(contentType) && slideDeck.getDocumentSlide() == null) {
|
||||
msg = new DcMsg(dcContext,
|
||||
MediaUtil.isGif(contentType) ? DcMsg.DC_MSG_GIF : DcMsg.DC_MSG_IMAGE);
|
||||
msg.setDimension(attachment.getWidth(), attachment.getHeight());
|
||||
} else if (MediaUtil.isAudioType(contentType)) {
|
||||
msg = new DcMsg(dcContext,
|
||||
attachment.isVoiceNote() ? DcMsg.DC_MSG_VOICE : DcMsg.DC_MSG_AUDIO);
|
||||
} else if (MediaUtil.isVideoType(contentType) && slideDeck.getDocumentSlide() == null) {
|
||||
msg = new DcMsg(dcContext, DcMsg.DC_MSG_VIDEO);
|
||||
recompress = DcMsg.DC_MSG_VIDEO;
|
||||
} else {
|
||||
msg = new DcMsg(dcContext, DcMsg.DC_MSG_FILE);
|
||||
}
|
||||
String path = attachment.getRealPath(this);
|
||||
msg.setFileAndDeduplicate(path, attachment.getFileName(), null);
|
||||
}
|
||||
String path = attachment.getRealPath(this);
|
||||
msg.setFileAndDeduplicate(path, attachment.getFileName(), null);
|
||||
}
|
||||
if (msg != null) {
|
||||
msg.setText(body);
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
else if (!body.isEmpty()){
|
||||
msg = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT);
|
||||
msg.setText(body);
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
if (quote.isPresent()) {
|
||||
if (msg == null) msg = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT);
|
||||
msg.setQuote(quote.get().getQuotedMsg());
|
||||
}
|
||||
}
|
||||
else if (!body.isEmpty()){
|
||||
msg = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT);
|
||||
msg.setText(body);
|
||||
}
|
||||
|
||||
if (quote.isPresent()) {
|
||||
if (msg == null) msg = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT);
|
||||
msg.setQuote(quote.get().getQuotedMsg());
|
||||
}
|
||||
if (action==ACTION_SEND_OUT) {
|
||||
|
||||
// msg may still be null to clear drafts
|
||||
new AsyncTask<Object, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Object... param) {
|
||||
DcMsg msg = (DcMsg)param[0];
|
||||
Integer recompress = (Integer)param[1];
|
||||
if (action==ACTION_SEND_OUT) {
|
||||
// for WEBXDC, drafts are just sent out as is.
|
||||
// for preparations and other cases, cleanup draft soon.
|
||||
if (msg == null || msg.getType() != DcMsg.DC_MSG_WEBXDC) {
|
||||
dcContext.setDraft(dcChat.getId(), null);
|
||||
}
|
||||
|
||||
// for WEBXDC, drafts are just sent out as is.
|
||||
// for preparations and other cases, cleanup draft soon.
|
||||
if (msg == null || msg.getType() != DcMsg.DC_MSG_WEBXDC) {
|
||||
dcContext.setDraft(dcChat.getId(), null);
|
||||
}
|
||||
|
||||
if(msg!=null)
|
||||
{
|
||||
boolean doSend = true;
|
||||
if (recompress==DcMsg.DC_MSG_VIDEO) {
|
||||
Util.runOnMain(() -> {
|
||||
progressDialog = ProgressDialog.show(
|
||||
ConversationActivity.this,
|
||||
"",
|
||||
getString(R.string.one_moment),
|
||||
true,
|
||||
false
|
||||
);
|
||||
});
|
||||
doSend = VideoRecoder.prepareVideo(ConversationActivity.this, dcChat.getId(), msg);
|
||||
Util.runOnMain(() -> {
|
||||
try {
|
||||
progressDialog.dismiss();
|
||||
} catch (final IllegalArgumentException e) {
|
||||
// The activity is finishing/destroyed, do nothing.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (doSend) {
|
||||
if (dcContext.sendMsg(dcChat.getId(), msg) == 0) {
|
||||
Util.runOnMain(()-> Toast.makeText(ConversationActivity.this, dcContext.getLastError(), Toast.LENGTH_LONG).show());
|
||||
return null;
|
||||
if(msg!=null) {
|
||||
boolean doSend = true;
|
||||
if (recompress==DcMsg.DC_MSG_VIDEO) {
|
||||
Util.runOnMain(() -> {
|
||||
if (isFinishing()) return;
|
||||
progressDialog = ProgressDialog.show(
|
||||
ConversationActivity.this,
|
||||
"",
|
||||
getString(R.string.one_moment),
|
||||
true,
|
||||
false
|
||||
);
|
||||
});
|
||||
doSend = VideoRecoder.prepareVideo(ConversationActivity.this, dcChat.getId(), msg);
|
||||
Util.runOnMain(() -> {
|
||||
try {
|
||||
if (progressDialog != null) progressDialog.dismiss();
|
||||
} catch (final IllegalArgumentException e) {
|
||||
// The activity is finishing/destroyed, do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
Util.runOnMain(()-> sendComplete(dcChat.getId()));
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
dcContext.setDraft(dcChat.getId(), msg);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
future.set(chatId);
|
||||
if (doSend) {
|
||||
if (dcContext.sendMsg(dcChat.getId(), msg) == 0) {
|
||||
Util.runOnMain(()-> Toast.makeText(ConversationActivity.this, dcContext.getLastError(), Toast.LENGTH_LONG).show());
|
||||
future.set(chatId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Util.runOnMain(() -> sendComplete(dcChat.getId()));
|
||||
}
|
||||
} else {
|
||||
dcContext.setDraft(dcChat.getId(), msg);
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, msg, recompress);
|
||||
future.set(chatId);
|
||||
});
|
||||
|
||||
return future;
|
||||
}
|
||||
@@ -1139,7 +1206,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
return;
|
||||
}
|
||||
|
||||
if (composeText.getText().length() == 0 && !attachmentManager.isAttachmentPresent()) {
|
||||
if (!isEditing && composeText.getText().length() == 0 && !attachmentManager.isAttachmentPresent()) {
|
||||
buttonToggle.display(attachButton);
|
||||
quickAttachmentToggle.show();
|
||||
} else {
|
||||
@@ -1257,9 +1324,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuoteDismissed() {
|
||||
if (isEditing) composeText.setText("");
|
||||
isEditing = false;
|
||||
}
|
||||
|
||||
// media selected by the system keyboard
|
||||
@Override
|
||||
public void onMediaSelected(@NonNull Uri uri, String contentType) {
|
||||
if (isEditing) return;
|
||||
if (MediaUtil.isImageType(contentType)) {
|
||||
sendSticker(uri, contentType);
|
||||
} else if (MediaUtil.isVideoType(contentType)) {
|
||||
@@ -1317,8 +1391,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
else {
|
||||
processComposeControls(ACTION_SEND_OUT);
|
||||
DcHelper.getNotificationCenter(ConversationActivity.this).maybePlaySendSound(dcChat);
|
||||
processComposeControls(ACTION_SEND_OUT).addListener(new AssertedSuccessListener<Integer>() {
|
||||
@Override
|
||||
public void onSuccess(Integer chatId) {
|
||||
DcHelper.getNotificationCenter(ConversationActivity.this).maybePlaySendSound(dcChat);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1391,11 +1469,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
@Override
|
||||
public void handleReplyMessage(DcMsg msg) {
|
||||
if (isEditing) composeText.setText("");
|
||||
isEditing = false;
|
||||
// If you modify these lines you may also want to modify ConversationItem.setQuote():
|
||||
Recipient author = new Recipient(this, dcContext.getContact(msg.getFromId()));
|
||||
|
||||
SlideDeck slideDeck = new SlideDeck();
|
||||
if (msg.getType() != DcMsg.DC_MSG_TEXT) {
|
||||
if (msg.hasFile()) {
|
||||
slideDeck.addSlide(MediaUtil.getSlideForMsg(this, msg));
|
||||
}
|
||||
|
||||
@@ -1406,11 +1486,32 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
msg.getTimestamp(),
|
||||
author,
|
||||
text,
|
||||
slideDeck);
|
||||
slideDeck,
|
||||
false);
|
||||
|
||||
inputPanel.clickOnComposeInput();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEditMessage(DcMsg msg) {
|
||||
isEditing = true;
|
||||
Recipient author = new Recipient(this, dcContext.getContact(msg.getFromId()));
|
||||
|
||||
SlideDeck slideDeck = new SlideDeck();
|
||||
String text = msg.getSummarytext(500);
|
||||
|
||||
inputPanel.setQuote(GlideApp.with(this),
|
||||
msg,
|
||||
msg.getTimestamp(),
|
||||
author,
|
||||
text,
|
||||
slideDeck,
|
||||
true);
|
||||
|
||||
setDraftText(msg.getText());
|
||||
inputPanel.clickOnComposeInput();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachmentChanged() {
|
||||
handleSecurityChange(isSecureText, isDefaultSms);
|
||||
@@ -1529,7 +1630,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
public void initializeContactRequest() {
|
||||
if (!dcChat.isHalfBlocked()) {
|
||||
if (!dcChat.isContactRequest()) {
|
||||
messageRequestBottomView.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
@@ -1542,15 +1643,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
});
|
||||
|
||||
|
||||
if (dcChat.isProtectionBroken()) {
|
||||
messageRequestBottomView.setBlockText(R.string.more_info_desktop);
|
||||
String name = dcContext.getContact(recipient.getDcContact().getId()).getDisplayName();
|
||||
messageRequestBottomView.setBlockOnClickListener(v -> DcHelper.showVerificationBrokenDialog(this, name));
|
||||
|
||||
messageRequestBottomView.setQuestion(getString(R.string.chat_protection_broken, name));
|
||||
messageRequestBottomView.setAcceptText(R.string.ok);
|
||||
|
||||
} else if (dcChat.getType() == DcChat.DC_CHAT_TYPE_GROUP) {
|
||||
if (dcChat.getType() == DcChat.DC_CHAT_TYPE_GROUP) {
|
||||
// We don't support blocking groups yet, so offer to delete it instead
|
||||
messageRequestBottomView.setBlockText(R.string.delete);
|
||||
messageRequestBottomView.setBlockOnClickListener(v -> handleDeleteChat());
|
||||
|
||||
@@ -16,17 +16,19 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import static org.thoughtcrime.securesms.ConversationItem.PULSE_HIGHLIGHT_MILLIS;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
@@ -49,8 +51,6 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.thoughtcrime.securesms.ConversationItem.PULSE_HIGHLIGHT_MILLIS;
|
||||
|
||||
/**
|
||||
* A DC adapter for a conversation thread. Ultimately
|
||||
* used by ConversationActivity to display a conversation
|
||||
@@ -77,9 +77,8 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
private static final int MESSAGE_TYPE_THUMBNAIL_INCOMING = 6;
|
||||
private static final int MESSAGE_TYPE_DOCUMENT_OUTGOING = 7;
|
||||
private static final int MESSAGE_TYPE_DOCUMENT_INCOMING = 8;
|
||||
private static final int MESSAGE_TYPE_VIDEOCHAT_INVITE = 9;
|
||||
private static final int MESSAGE_TYPE_STICKER_INCOMING = 10;
|
||||
private static final int MESSAGE_TYPE_STICKER_OUTGOING = 11;
|
||||
private static final int MESSAGE_TYPE_STICKER_INCOMING = 9;
|
||||
private static final int MESSAGE_TYPE_STICKER_OUTGOING = 10;
|
||||
|
||||
private final Set<DcMsg> batchSelected = Collections.synchronizedSet(new HashSet<DcMsg>());
|
||||
|
||||
@@ -279,7 +278,6 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
case MESSAGE_TYPE_STICKER_INCOMING:
|
||||
case MESSAGE_TYPE_INCOMING: return R.layout.conversation_item_received;
|
||||
case MESSAGE_TYPE_INFO: return R.layout.conversation_item_update;
|
||||
case MESSAGE_TYPE_VIDEOCHAT_INVITE:return R.layout.conversation_item_videochat;
|
||||
default: throw new IllegalArgumentException("unsupported item view type given to ConversationAdapter");
|
||||
}
|
||||
}
|
||||
@@ -294,7 +292,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
else if (type==DcMsg.DC_MSG_AUDIO || type==DcMsg.DC_MSG_VOICE) {
|
||||
return dcMsg.isOutgoing()? MESSAGE_TYPE_AUDIO_OUTGOING : MESSAGE_TYPE_AUDIO_INCOMING;
|
||||
}
|
||||
else if (type==DcMsg.DC_MSG_FILE && !dcMsg.isSetupMessage()) {
|
||||
else if (type==DcMsg.DC_MSG_FILE) {
|
||||
return dcMsg.isOutgoing()? MESSAGE_TYPE_DOCUMENT_OUTGOING : MESSAGE_TYPE_DOCUMENT_INCOMING;
|
||||
}
|
||||
else if (type==DcMsg.DC_MSG_IMAGE || type==DcMsg.DC_MSG_GIF || type==DcMsg.DC_MSG_VIDEO) {
|
||||
@@ -303,9 +301,6 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
else if (type == DcMsg.DC_MSG_STICKER) {
|
||||
return dcMsg.isOutgoing()? MESSAGE_TYPE_STICKER_OUTGOING : MESSAGE_TYPE_STICKER_INCOMING;
|
||||
}
|
||||
else if (type == DcMsg.DC_MSG_VIDEOCHAT_INVITATION) {
|
||||
return MESSAGE_TYPE_VIDEOCHAT_INVITE;
|
||||
}
|
||||
else {
|
||||
return dcMsg.isOutgoing()? MESSAGE_TYPE_OUTGOING : MESSAGE_TYPE_INCOMING;
|
||||
}
|
||||
|
||||
@@ -25,9 +25,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
@@ -35,15 +33,12 @@ import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
@@ -63,15 +58,15 @@ import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.reactions.AddReactionView;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.reactions.ReactionsDetailsFragment;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.AccessibilityUtil;
|
||||
import org.thoughtcrime.securesms.util.Debouncer;
|
||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.views.ConversationAdaptiveActionsToolbar;
|
||||
import org.thoughtcrime.securesms.videochat.VideochatUtil;
|
||||
import org.thoughtcrime.securesms.calls.CallUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
@@ -173,8 +168,8 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
private void setNoMessageText() {
|
||||
DcChat dcChat = getListAdapter().getChat();
|
||||
if(dcChat.isMultiUser()){
|
||||
if (dcChat.isBroadcast()) {
|
||||
noMessageTextView.setText(R.string.chat_new_broadcast_hint);
|
||||
if (dcChat.isInBroadcast() || dcChat.isOutBroadcast()) {
|
||||
noMessageTextView.setText(R.string.chat_new_channel_hint);
|
||||
} else if (dcChat.isUnpromoted()) {
|
||||
noMessageTextView.setText(R.string.chat_new_group_hint);
|
||||
}
|
||||
@@ -321,12 +316,16 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
return;
|
||||
}
|
||||
|
||||
menu.findItem(R.id.menu_toggle_save).setVisible(false);
|
||||
|
||||
if (messageRecords.size() > 1) {
|
||||
menu.findItem(R.id.menu_context_details).setVisible(false);
|
||||
menu.findItem(R.id.menu_context_share).setVisible(false);
|
||||
menu.findItem(R.id.menu_context_reply).setVisible(false);
|
||||
menu.findItem(R.id.menu_context_edit).setVisible(false);
|
||||
menu.findItem(R.id.menu_context_reply_privately).setVisible(false);
|
||||
menu.findItem(R.id.menu_add_to_home_screen).setVisible(false);
|
||||
//menu.findItem(R.id.menu_toggle_save).setVisible(false);
|
||||
} else {
|
||||
DcMsg messageRecord = messageRecords.iterator().next();
|
||||
DcChat chat = getListAdapter().getChat();
|
||||
@@ -334,9 +333,18 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
menu.findItem(R.id.menu_context_share).setVisible(messageRecord.hasFile());
|
||||
boolean canReply = canReplyToMsg(messageRecord);
|
||||
menu.findItem(R.id.menu_context_reply).setVisible(chat.canSend() && canReply);
|
||||
boolean showReplyPrivately = !dcContext.isCommunity() && chat.isMultiUser() && !messageRecord.isOutgoing() && canReply;
|
||||
menu.findItem(R.id.menu_context_edit).setVisible(chat.isEncrypted() && chat.canSend() && canEditMsg(messageRecord));
|
||||
boolean showReplyPrivately = chat.isMultiUser() && !messageRecord.isOutgoing() && canReply;
|
||||
menu.findItem(R.id.menu_context_reply_privately).setVisible(showReplyPrivately);
|
||||
menu.findItem(R.id.menu_add_to_home_screen).setVisible(messageRecord.getType() == DcMsg.DC_MSG_WEBXDC);
|
||||
|
||||
/*
|
||||
boolean saved = messageRecord.getSavedMsgId() != 0;
|
||||
MenuItem toggleSave = menu.findItem(R.id.menu_toggle_save);
|
||||
toggleSave.setVisible(messageRecord.canSave() && !chat.isSelfTalk());
|
||||
toggleSave.setIcon(saved? R.drawable.baseline_bookmark_remove_24 : R.drawable.baseline_bookmark_border_24);
|
||||
toggleSave.setTitle(saved? R.string.unsave : R.string.save);
|
||||
*/
|
||||
}
|
||||
|
||||
// if one of the selected items cannot be saved, disable saving.
|
||||
@@ -359,8 +367,7 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
}
|
||||
|
||||
static boolean canReplyToMsg(DcMsg dcMsg) {
|
||||
boolean canReply = dcMsg.getType() != DcMsg.DC_MSG_VIDEOCHAT_INVITATION;
|
||||
if (canReply && dcMsg.isInfo()) {
|
||||
if (dcMsg.isInfo()) {
|
||||
switch (dcMsg.getInfoType()) {
|
||||
case DcMsg.DC_INFO_GROUP_NAME_CHANGED:
|
||||
case DcMsg.DC_INFO_GROUP_IMAGE_CHANGED:
|
||||
@@ -369,12 +376,16 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
case DcMsg.DC_INFO_LOCATIONSTREAMING_ENABLED:
|
||||
case DcMsg.DC_INFO_EPHEMERAL_TIMER_CHANGED:
|
||||
case DcMsg.DC_INFO_WEBXDC_INFO_MESSAGE:
|
||||
break;
|
||||
return true;
|
||||
default:
|
||||
canReply = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return canReply;
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean canEditMsg(DcMsg dcMsg) {
|
||||
return dcMsg.isOutgoing() && !dcMsg.isInfo() && dcMsg.getType() != DcMsg.DC_MSG_CALL && !dcMsg.hasHtml() && !dcMsg.getText().isEmpty();
|
||||
}
|
||||
|
||||
public void handleClearChat() {
|
||||
@@ -477,6 +488,16 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
listener.handleReplyMessage(message);
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
private void handleEditMessage(final DcMsg message) {
|
||||
if (getActivity() != null) {
|
||||
//noinspection ConstantConditions
|
||||
((AppCompatActivity) getActivity()).getSupportActionBar().collapseActionView();
|
||||
}
|
||||
|
||||
listener.handleEditMessage(message);
|
||||
}
|
||||
|
||||
private void handleReplyMessagePrivately(final DcMsg msg) {
|
||||
|
||||
if (getActivity() != null) {
|
||||
@@ -494,6 +515,15 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
}
|
||||
}
|
||||
|
||||
private void handleToggleSave(final Set<DcMsg> messageRecords) {
|
||||
DcMsg msg = getSelectedMessageRecord(messageRecords);
|
||||
if (msg.getSavedMsgId() != 0) {
|
||||
dcContext.deleteMsgs(new int[]{msg.getSavedMsgId()});
|
||||
} else {
|
||||
dcContext.saveMsgs(new int[]{msg.getId()});
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadList() {
|
||||
reloadList(false);
|
||||
}
|
||||
@@ -607,6 +637,7 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
|
||||
public interface ConversationFragmentListener {
|
||||
void handleReplyMessage(DcMsg messageRecord);
|
||||
void handleEditMessage(DcMsg messageRecord);
|
||||
}
|
||||
|
||||
private class ConversationScrollListener extends OnScrollListener {
|
||||
@@ -707,80 +738,6 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
Util.runOnAnyBackgroundThread(() -> dcContext.markseenMsgs(ids));
|
||||
}
|
||||
|
||||
|
||||
void querySetupCode(final DcMsg dcMsg, String[] preload)
|
||||
{
|
||||
if( !dcMsg.isSetupMessage()) {
|
||||
return;
|
||||
}
|
||||
|
||||
View gl = View.inflate(getActivity(), R.layout.setup_code_grid, null);
|
||||
final EditText[] editTexts = {
|
||||
gl.findViewById(R.id.setupCode0), gl.findViewById(R.id.setupCode1), gl.findViewById(R.id.setupCode2),
|
||||
gl.findViewById(R.id.setupCode3), gl.findViewById(R.id.setupCode4), gl.findViewById(R.id.setupCode5),
|
||||
gl.findViewById(R.id.setupCode6), gl.findViewById(R.id.setupCode7), gl.findViewById(R.id.setupCode8)
|
||||
};
|
||||
AlertDialog.Builder builder1 = new AlertDialog.Builder(getActivity());
|
||||
builder1.setView(gl);
|
||||
editTexts[0].setText(dcMsg.getSetupCodeBegin());
|
||||
editTexts[0].setSelection(editTexts[0].getText().length());
|
||||
|
||||
for( int i = 0; i < 9; i++ ) {
|
||||
if( preload != null && i < preload.length ) {
|
||||
editTexts[i].setText(preload[i]);
|
||||
editTexts[i].setSelection(editTexts[i].getText().length());
|
||||
}
|
||||
editTexts[i].addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
if( s.length()==4 ) {
|
||||
for ( int i = 0; i < 8; i++ ) {
|
||||
if( editTexts[i].hasFocus() && editTexts[i+1].getText().length()<4 ) {
|
||||
editTexts[i+1].requestFocus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
builder1.setTitle(getActivity().getString(R.string.autocrypt_continue_transfer_title));
|
||||
builder1.setMessage(getActivity().getString(R.string.autocrypt_continue_transfer_please_enter_code));
|
||||
builder1.setNegativeButton(android.R.string.cancel, null);
|
||||
builder1.setCancelable(false); // prevent the dialog from being dismissed accidentally (when the dialog is closed, the setup code is gone forever and the user has to create a new setup message)
|
||||
builder1.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
String setup_code = "";
|
||||
final String[] preload1 = new String[9];
|
||||
for ( int i = 0; i < 9; i++ ) {
|
||||
preload1[i] = editTexts[i].getText().toString();
|
||||
setup_code += preload1[i];
|
||||
}
|
||||
boolean success = dcContext.continueKeyTransfer(dcMsg.getId(), setup_code);
|
||||
|
||||
AlertDialog.Builder builder2 = new AlertDialog.Builder(getActivity());
|
||||
builder2.setTitle(getActivity().getString(R.string.autocrypt_continue_transfer_title));
|
||||
builder2.setMessage(getActivity().getString(success? R.string.autocrypt_continue_transfer_succeeded : R.string.autocrypt_bad_setup_code));
|
||||
if( success ) {
|
||||
builder2.setPositiveButton(android.R.string.ok, null);
|
||||
}
|
||||
else {
|
||||
builder2.setNegativeButton(android.R.string.cancel, null);
|
||||
builder2.setPositiveButton(R.string.autocrypt_continue_transfer_retry, (dialog1, which1) -> querySetupCode(dcMsg, preload1));
|
||||
}
|
||||
builder2.show();
|
||||
});
|
||||
builder1.show();
|
||||
}
|
||||
|
||||
private class ConversationFragmentItemClickListener implements ItemClickListener {
|
||||
|
||||
@Override
|
||||
@@ -800,28 +757,33 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
actionMode.setTitleOptionalHint(false); // the title represents important information, also indicating implicitly, more items can be selected
|
||||
}
|
||||
}
|
||||
else if(messageRecord.isSetupMessage()) {
|
||||
querySetupCode(messageRecord,null);
|
||||
}
|
||||
else if (messageRecord.getType()==DcMsg.DC_MSG_VIDEOCHAT_INVITATION) {
|
||||
new VideochatUtil().join(getActivity(), messageRecord.getId());
|
||||
}
|
||||
else if(DozeReminder.isDozeReminderMsg(getContext(), messageRecord)) {
|
||||
DozeReminder.dozeReminderTapped(getContext());
|
||||
}
|
||||
else if(messageRecord.getInfoType() == DcMsg.DC_INFO_WEBXDC_INFO_MESSAGE) {
|
||||
WebxdcActivity.openWebxdcActivity(getContext(), messageRecord.getParent(), messageRecord.getWebxdcHref());
|
||||
if (messageRecord.getParent() != null) {
|
||||
// if the parent webxdc message still exists
|
||||
WebxdcActivity.openWebxdcActivity(getContext(), messageRecord.getParent(), messageRecord.getWebxdcHref());
|
||||
}
|
||||
}
|
||||
else if (!TextUtils.isEmpty(messageRecord.getPOILocation()) && messageRecord.getType() == DcMsg.DC_MSG_TEXT && !messageRecord.hasHtml()) {
|
||||
WebxdcActivity.openMaps(getContext(), getListAdapter().getChat().getId(), "index.html#"+messageRecord.getPOILocation());
|
||||
}
|
||||
else {
|
||||
String self_mail = dcContext.getConfig("configured_mail_user");
|
||||
if (self_mail != null && !self_mail.isEmpty()
|
||||
&& messageRecord.getText().contains(self_mail)
|
||||
&& getListAdapter().getChat().isDeviceTalk()) {
|
||||
// This is a device message informing the user that the password is wrong
|
||||
startActivity(new Intent(getActivity(), RegistrationActivity.class));
|
||||
int infoContactId = messageRecord.getInfoContactId();
|
||||
if (infoContactId != 0 && infoContactId != DC_CONTACT_ID_SELF) {
|
||||
Intent intent = new Intent(getContext(), ProfileActivity.class);
|
||||
intent.putExtra(ProfileActivity.CONTACT_ID_EXTRA, infoContactId);
|
||||
startActivity(intent);
|
||||
}
|
||||
else {
|
||||
String self_mail = dcContext.getConfig("configured_mail_user");
|
||||
if (self_mail != null && !self_mail.isEmpty()
|
||||
&& messageRecord.getText().contains(self_mail)
|
||||
&& getListAdapter().getChat().isDeviceTalk()) {
|
||||
// This is a device message informing the user that the password is wrong
|
||||
startActivity(new Intent(getActivity(), RegistrationActivity.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -833,9 +795,6 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
list.getAdapter().notifyDataSetChanged();
|
||||
|
||||
actionMode = ((AppCompatActivity)getActivity()).startSupportActionMode(actionModeCallback);
|
||||
|
||||
if (dcContext.isCommunity()) return;
|
||||
|
||||
addReactionView.show(messageRecord, view, () -> {
|
||||
if (actionMode != null) {
|
||||
actionMode.finish();
|
||||
@@ -844,20 +803,18 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuoteClicked(DcMsg messageRecord) {
|
||||
DcMsg quoted = messageRecord.getQuotedMsg();
|
||||
if (quoted == null) {
|
||||
Log.i(TAG, "Clicked on a quote whose original message we never had.");
|
||||
private void jumpToOriginal(DcMsg original) {
|
||||
if (original == null) {
|
||||
Log.i(TAG, "Clicked on a quote or jump-to-original whose original message was deleted/non-existing.");
|
||||
Toast.makeText(getContext(), R.string.ConversationFragment_quoted_message_not_found, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
int foreignChatId = quoted.getChatId();
|
||||
int foreignChatId = original.getChatId();
|
||||
if (foreignChatId != 0 && foreignChatId != chatId) {
|
||||
Intent intent = new Intent(getActivity(), ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.CHAT_ID_EXTRA, foreignChatId);
|
||||
int start = DcMsg.getMessagePosition(quoted, dcContext);
|
||||
int start = DcMsg.getMessagePosition(original, dcContext);
|
||||
intent.putExtra(ConversationActivity.STARTING_POSITION_EXTRA, start);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
((ConversationActivity) getActivity()).hideSoftKeyboard();
|
||||
@@ -867,15 +824,25 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
Log.e(TAG, "Activity was null");
|
||||
}
|
||||
} else {
|
||||
scrollMaybeSmoothToMsgId(quoted.getId());
|
||||
scrollMaybeSmoothToMsgId(original.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJumpToOriginalClicked(DcMsg messageRecord) {
|
||||
jumpToOriginal(dcContext.getMsg(messageRecord.getOriginalMsgId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuoteClicked(DcMsg messageRecord) {
|
||||
jumpToOriginal(messageRecord.getQuotedMsg());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowFullClicked(DcMsg messageRecord) {
|
||||
Intent intent = new Intent(getActivity(), FullMsgActivity.class);
|
||||
intent.putExtra(FullMsgActivity.MSG_ID_EXTRA, messageRecord.getId());
|
||||
intent.putExtra(FullMsgActivity.BLOCK_LOADING_REMOTE, getListAdapter().getChat().isHalfBlocked());
|
||||
intent.putExtra(FullMsgActivity.BLOCK_LOADING_REMOTE, getListAdapter().getChat().isContactRequest());
|
||||
startActivity(intent);
|
||||
getActivity().overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out);
|
||||
}
|
||||
@@ -905,8 +872,6 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
|
||||
private class ActionModeCallback implements ActionMode.Callback {
|
||||
|
||||
private int statusBarColor;
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
MenuInflater inflater = mode.getMenuInflater();
|
||||
@@ -914,10 +879,7 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
|
||||
mode.setTitle("1");
|
||||
|
||||
Window window = getActivity().getWindow();
|
||||
statusBarColor = window.getStatusBarColor();
|
||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
|
||||
Util.redMenuItem(menu, R.id.menu_context_delete_message);
|
||||
setCorrectMenuVisibility(menu);
|
||||
ConversationAdaptiveActionsToolbar.adjustMenuActions(menu, 10, requireActivity().getWindow().getDecorView().getMeasuredWidth());
|
||||
return true;
|
||||
@@ -933,8 +895,6 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
((ConversationAdapter)list.getAdapter()).clearSelection();
|
||||
list.getAdapter().notifyDataSetChanged();
|
||||
|
||||
getActivity().getWindow().setStatusBarColor(statusBarColor);
|
||||
|
||||
actionMode = null;
|
||||
hideAddReactionView();
|
||||
}
|
||||
@@ -942,45 +902,52 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
hideAddReactionView();
|
||||
switch(item.getItemId()) {
|
||||
case R.id.menu_context_copy:
|
||||
handleCopyMessage(getListAdapter().getSelectedItems());
|
||||
actionMode.finish();
|
||||
return true;
|
||||
case R.id.menu_context_delete_message:
|
||||
handleDeleteMessages((int) chatId, getListAdapter().getSelectedItems());
|
||||
return true;
|
||||
case R.id.menu_context_share:
|
||||
DcHelper.openForViewOrShare(getContext(), getSelectedMessageRecord(getListAdapter().getSelectedItems()).getId(), Intent.ACTION_SEND);
|
||||
return true;
|
||||
case R.id.menu_context_details:
|
||||
handleDisplayDetails(getSelectedMessageRecord(getListAdapter().getSelectedItems()));
|
||||
actionMode.finish();
|
||||
return true;
|
||||
case R.id.menu_context_forward:
|
||||
handleForwardMessage(getListAdapter().getSelectedItems());
|
||||
actionMode.finish();
|
||||
return true;
|
||||
case R.id.menu_add_to_home_screen:
|
||||
WebxdcActivity.addToHomeScreen(getActivity(), getSelectedMessageRecord(getListAdapter().getSelectedItems()).getId());
|
||||
actionMode.finish();
|
||||
return true;
|
||||
case R.id.menu_context_save_attachment:
|
||||
handleSaveAttachment(getListAdapter().getSelectedItems());
|
||||
return true;
|
||||
case R.id.menu_context_reply:
|
||||
handleReplyMessage(getSelectedMessageRecord(getListAdapter().getSelectedItems()));
|
||||
actionMode.finish();
|
||||
return true;
|
||||
case R.id.menu_context_reply_privately:
|
||||
handleReplyMessagePrivately(getSelectedMessageRecord(getListAdapter().getSelectedItems()));
|
||||
return true;
|
||||
case R.id.menu_resend:
|
||||
handleResendMessage(getListAdapter().getSelectedItems());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == R.id.menu_context_copy) {
|
||||
handleCopyMessage(getListAdapter().getSelectedItems());
|
||||
actionMode.finish();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_context_delete_message) {
|
||||
handleDeleteMessages((int) chatId, getListAdapter().getSelectedItems());
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_context_share) {
|
||||
DcHelper.openForViewOrShare(getContext(), getSelectedMessageRecord(getListAdapter().getSelectedItems()).getId(), Intent.ACTION_SEND);
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_context_details) {
|
||||
handleDisplayDetails(getSelectedMessageRecord(getListAdapter().getSelectedItems()));
|
||||
actionMode.finish();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_context_forward) {
|
||||
handleForwardMessage(getListAdapter().getSelectedItems());
|
||||
actionMode.finish();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_add_to_home_screen) {
|
||||
WebxdcActivity.addToHomeScreen(getActivity(), getSelectedMessageRecord(getListAdapter().getSelectedItems()).getId());
|
||||
actionMode.finish();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_context_save_attachment) {
|
||||
handleSaveAttachment(getListAdapter().getSelectedItems());
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_context_reply) {
|
||||
handleReplyMessage(getSelectedMessageRecord(getListAdapter().getSelectedItems()));
|
||||
actionMode.finish();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_context_edit) {
|
||||
handleEditMessage(getSelectedMessageRecord(getListAdapter().getSelectedItems()));
|
||||
actionMode.finish();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_context_reply_privately) {
|
||||
handleReplyMessagePrivately(getSelectedMessageRecord(getListAdapter().getSelectedItems()));
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_resend) {
|
||||
handleResendMessage(getListAdapter().getSelectedItems());
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_toggle_save) {
|
||||
handleToggleSave(getListAdapter().getSelectedItems());
|
||||
actionMode.finish();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,14 +40,12 @@ import androidx.appcompat.app.AlertDialog;
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcContact;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
import com.b44t.messenger.rpc.Reactions;
|
||||
import com.b44t.messenger.rpc.RpcException;
|
||||
import com.b44t.messenger.rpc.VcardContact;
|
||||
|
||||
import org.thoughtcrime.securesms.audio.AudioSlidePlayer;
|
||||
import org.thoughtcrime.securesms.components.AudioView;
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.components.BorderlessImageView;
|
||||
import org.thoughtcrime.securesms.components.CallItemView;
|
||||
import org.thoughtcrime.securesms.components.ConversationItemFooter;
|
||||
import org.thoughtcrime.securesms.components.ConversationItemThumbnail;
|
||||
import org.thoughtcrime.securesms.components.DocumentView;
|
||||
@@ -73,10 +71,17 @@ import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.views.Stub;
|
||||
import org.thoughtcrime.securesms.calls.CallUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import chat.delta.rpc.RpcException;
|
||||
import chat.delta.rpc.types.CallInfo;
|
||||
import chat.delta.rpc.types.CallState;
|
||||
import chat.delta.rpc.types.Reactions;
|
||||
import chat.delta.rpc.types.VcardContact;
|
||||
|
||||
/**
|
||||
* A view that displays an individual conversation item within a conversation
|
||||
* thread. Used by ComposeMessageActivity's ListActivity via a ConversationAdapter.
|
||||
@@ -101,6 +106,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
protected ViewGroup bodyBubble;
|
||||
protected ReactionsConversationView reactionsView;
|
||||
protected View replyView;
|
||||
protected View jumptoView;
|
||||
@Nullable private QuoteView quoteView;
|
||||
private ConversationItemFooter footer;
|
||||
private TextView groupSender;
|
||||
@@ -117,6 +123,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
private @NonNull Stub<WebxdcView> webxdcViewStub;
|
||||
private Stub<BorderlessImageView> stickerStub;
|
||||
private Stub<VcardView> vcardViewStub;
|
||||
private Stub<CallItemView> callViewStub;
|
||||
private @Nullable EventListener eventListener;
|
||||
|
||||
private int measureCalls;
|
||||
@@ -151,10 +158,12 @@ public class ConversationItem extends BaseConversationItem
|
||||
this.webxdcViewStub = new Stub<>(findViewById(R.id.webxdc_view_stub));
|
||||
this.stickerStub = new Stub<>(findViewById(R.id.sticker_view_stub));
|
||||
this.vcardViewStub = new Stub<>(findViewById(R.id.vcard_view_stub));
|
||||
this.callViewStub = new Stub<>(findViewById(R.id.call_view_stub));
|
||||
this.groupSenderHolder = findViewById(R.id.group_sender_holder);
|
||||
this.quoteView = findViewById(R.id.quote_view);
|
||||
this.container = findViewById(R.id.container);
|
||||
this.replyView = findViewById(R.id.reply_icon);
|
||||
this.jumptoView = findViewById(R.id.jumpto_icon);
|
||||
this.msgActionButton = findViewById(R.id.msg_action_button);
|
||||
this.showFullButton = findViewById(R.id.show_full_button);
|
||||
|
||||
@@ -176,12 +185,23 @@ public class ConversationItem extends BaseConversationItem
|
||||
{
|
||||
bind(messageRecord, dcChat, batchSelected, pulseHighlight, recipients);
|
||||
this.glideRequests = glideRequests;
|
||||
this.showSender = dcContext.isCommunity() || (dcChat.isMultiUser() && !messageRecord.isOutgoing()) || messageRecord.getOverrideSenderName() != null;
|
||||
this.showSender = ((dcChat.isMultiUser() || dcChat.isSelfTalk()) && !messageRecord.isOutgoing()) || messageRecord.getOverrideSenderName() != null;
|
||||
|
||||
if (showSender) {
|
||||
this.dcContact = dcContext.getContact(messageRecord.getFromId());
|
||||
}
|
||||
|
||||
if (dcChat.isSelfTalk() && messageRecord.getOriginalMsgId() != 0) {
|
||||
jumptoView.setVisibility(View.VISIBLE);
|
||||
jumptoView.setOnClickListener(view -> {
|
||||
if (eventListener != null) {
|
||||
eventListener.onJumpToOriginalClicked(messageRecord);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
jumptoView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
setGutterSizes(messageRecord, showSender);
|
||||
setMessageShape(messageRecord);
|
||||
setMediaAttributes(messageRecord, showSender);
|
||||
@@ -257,11 +277,10 @@ public class ConversationItem extends BaseConversationItem
|
||||
R.attr.conversation_item_incoming_bubble_color,
|
||||
R.attr.conversation_item_outgoing_bubble_color,
|
||||
};
|
||||
final TypedArray attrs = context.obtainStyledAttributes(attributes);
|
||||
|
||||
incomingBubbleColor = attrs.getColor(0, Color.WHITE);
|
||||
outgoingBubbleColor = attrs.getColor(1, Color.WHITE);
|
||||
attrs.recycle();
|
||||
try (TypedArray attrs = context.obtainStyledAttributes(attributes)) {
|
||||
incomingBubbleColor = attrs.getColor(0, Color.WHITE);
|
||||
outgoingBubbleColor = attrs.getColor(1, Color.WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -310,6 +329,11 @@ public class ConversationItem extends BaseConversationItem
|
||||
vcardViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty());
|
||||
vcardViewStub.get().setClickable(batchSelected.isEmpty());
|
||||
}
|
||||
|
||||
if (callViewStub.resolved()) {
|
||||
callViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty());
|
||||
callViewStub.get().setClickable(batchSelected.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
private void setContentDescription() {
|
||||
@@ -326,6 +350,8 @@ public class ConversationItem extends BaseConversationItem
|
||||
desc += webxdcViewStub.get().getDescription() + "\n";
|
||||
} else if (vcardViewStub.resolved() && vcardViewStub.get().getVisibility() == View.VISIBLE) {
|
||||
desc += vcardViewStub.get().getDescription() + "\n";
|
||||
} else if (callViewStub.resolved() && callViewStub.get().getVisibility() == View.VISIBLE) {
|
||||
desc += callViewStub.get().getDescription() + "\n";
|
||||
} else if (mediaThumbnailStub.resolved() && mediaThumbnailStub.get().getVisibility() == View.VISIBLE) {
|
||||
desc += mediaThumbnailStub.get().getDescription() + "\n";
|
||||
} else if (stickerStub.resolved() && stickerStub.get().getVisibility() == View.VISIBLE) {
|
||||
@@ -378,7 +404,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
}
|
||||
|
||||
private boolean hasDocument(DcMsg dcMsg) {
|
||||
return dcMsg.getType()==DcMsg.DC_MSG_FILE && !dcMsg.isSetupMessage();
|
||||
return dcMsg.getType()==DcMsg.DC_MSG_FILE;
|
||||
}
|
||||
|
||||
private void setBodyText(DcMsg messageRecord) {
|
||||
@@ -387,11 +413,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
|
||||
String text = messageRecord.getText();
|
||||
|
||||
if (messageRecord.isSetupMessage()) {
|
||||
bodyText.setText(context.getString(R.string.autocrypt_asm_click_body));
|
||||
bodyText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else if (text.isEmpty()) {
|
||||
if (messageRecord.getType() == DcMsg.DC_MSG_CALL || text.isEmpty()) {
|
||||
bodyText.setVisibility(View.GONE);
|
||||
}
|
||||
else {
|
||||
@@ -429,10 +451,10 @@ public class ConversationItem extends BaseConversationItem
|
||||
showFullButton.setVisibility(View.GONE);
|
||||
msgActionButton.setVisibility(View.VISIBLE);
|
||||
msgActionButton.setEnabled(true);
|
||||
msgActionButton.setText(webxdcViewStub.get().isCommunity()? R.string.join: R.string.start_app);
|
||||
msgActionButton.setText(R.string.start_app);
|
||||
msgActionButton.setOnClickListener(view -> {
|
||||
if (batchSelected.isEmpty()) {
|
||||
DcHelper.openWebxdc(getContext(), messageRecord);
|
||||
WebxdcActivity.openWebxdcActivity(getContext(), messageRecord);
|
||||
} else {
|
||||
passthroughClickListener.onClick(view);
|
||||
}
|
||||
@@ -482,6 +504,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
if (webxdcViewStub.resolved()) webxdcViewStub.get().setVisibility(View.GONE);
|
||||
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
|
||||
if (vcardViewStub.resolved()) vcardViewStub.get().setVisibility(View.GONE);
|
||||
if (callViewStub.resolved()) callViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
int duration = messageRecord.getDuration();
|
||||
@@ -507,6 +530,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
if (webxdcViewStub.resolved()) webxdcViewStub.get().setVisibility(View.GONE);
|
||||
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
|
||||
if (vcardViewStub.resolved()) vcardViewStub.get().setVisibility(View.GONE);
|
||||
if (callViewStub.resolved()) callViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
documentViewStub.get().setDocument(new DocumentSlide(context, messageRecord));
|
||||
@@ -525,6 +549,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
|
||||
if (vcardViewStub.resolved()) vcardViewStub.get().setVisibility(View.GONE);
|
||||
if (callViewStub.resolved()) callViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
webxdcViewStub.get().setWebxdc(messageRecord, context.getString(R.string.webxdc_app));
|
||||
webxdcViewStub.get().setWebxdcClickListener(new ThumbnailClickListener());
|
||||
@@ -542,6 +567,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
if (webxdcViewStub.resolved()) webxdcViewStub.get().setVisibility(View.GONE);
|
||||
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
|
||||
if (callViewStub.resolved()) callViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
vcardViewStub.get().setVcard(glideRequests, new VcardSlide(context, messageRecord), rpc);
|
||||
vcardViewStub.get().setVcardClickListener(new ThumbnailClickListener());
|
||||
@@ -553,6 +579,27 @@ public class ConversationItem extends BaseConversationItem
|
||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
footer.setVisibility(VISIBLE);
|
||||
}
|
||||
else if (messageRecord.getType() == DcMsg.DC_MSG_CALL) {
|
||||
callViewStub.get().setVisibility(View.VISIBLE);
|
||||
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
|
||||
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
|
||||
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
if (webxdcViewStub.resolved()) webxdcViewStub.get().setVisibility(View.GONE);
|
||||
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
|
||||
if (vcardViewStub.resolved()) vcardViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
try {
|
||||
callViewStub.get().setCallItem(messageRecord.isOutgoing(), rpc.callInfo(dcContext.getAccountId(), messageRecord.getId()));
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "Error in Rpc.callInfo", e);
|
||||
}
|
||||
callViewStub.get().setCallClickListener(new CallClickListener());
|
||||
callViewStub.get().setOnLongClickListener(passthroughClickListener);
|
||||
|
||||
callViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
|
||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
else if (hasThumbnail(messageRecord)) {
|
||||
mediaThumbnailStub.get().setVisibility(View.VISIBLE);
|
||||
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
|
||||
@@ -560,6 +607,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
if (webxdcViewStub.resolved()) webxdcViewStub.get().setVisibility(View.GONE);
|
||||
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
|
||||
if (vcardViewStub.resolved()) vcardViewStub.get().setVisibility(View.GONE);
|
||||
if (callViewStub.resolved()) callViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
Slide slide = MediaUtil.getSlideForMsg(context, messageRecord);
|
||||
|
||||
@@ -599,6 +647,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
if (webxdcViewStub.resolved()) webxdcViewStub.get().setVisibility(View.GONE);
|
||||
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
|
||||
if (vcardViewStub.resolved()) vcardViewStub.get().setVisibility(View.GONE);
|
||||
if (callViewStub.resolved()) callViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
bodyBubble.setBackgroundColor(Color.TRANSPARENT);
|
||||
|
||||
@@ -619,6 +668,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
if (webxdcViewStub.resolved()) webxdcViewStub.get().setVisibility(View.GONE);
|
||||
if (vcardViewStub.resolved()) vcardViewStub.get().setVisibility(View.GONE);
|
||||
if (callViewStub.resolved()) callViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
@@ -666,9 +716,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
if (!showSender || dcContact ==null) {
|
||||
contactPhoto.setVisibility(View.GONE);
|
||||
} else {
|
||||
int color = messageRecord.getSenderColor();
|
||||
Recipient recipient = new Recipient(context, dcContact, messageRecord.getSenderName(dcContact), color);
|
||||
contactPhoto.setAvatar(glideRequests, recipient, true);
|
||||
contactPhoto.setAvatar(glideRequests, new Recipient(context, dcContact), true);
|
||||
contactPhoto.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
@@ -693,7 +741,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
Recipient author = null;
|
||||
SlideDeck slideDeck = new SlideDeck();
|
||||
if (msg != null) {
|
||||
author = new Recipient(context, dcContext.getContact(msg.getFromId()), msg.getSenderColor());
|
||||
author = new Recipient(context, dcContext.getContact(msg.getFromId()));
|
||||
if (msg.getType() != DcMsg.DC_MSG_TEXT) {
|
||||
Slide slide = MediaUtil.getSlideForMsg(context, msg);
|
||||
if (slide != null) {
|
||||
@@ -707,7 +755,8 @@ public class ConversationItem extends BaseConversationItem
|
||||
author,
|
||||
quoteTxt,
|
||||
slideDeck,
|
||||
current.getType() == DcMsg.DC_MSG_STICKER);
|
||||
current.getType() == DcMsg.DC_MSG_STICKER,
|
||||
false);
|
||||
|
||||
quoteView.setVisibility(View.VISIBLE);
|
||||
quoteView.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
@@ -750,11 +799,11 @@ public class ConversationItem extends BaseConversationItem
|
||||
|
||||
private void setReactions(@NonNull DcMsg current) {
|
||||
try {
|
||||
Reactions reactions = rpc.getMsgReactions(dcContext.getAccountId(), current.getId());
|
||||
Reactions reactions = rpc.getMessageReactions(dcContext.getAccountId(), current.getId());
|
||||
if (reactions == null) {
|
||||
reactionsView.clear();
|
||||
} else {
|
||||
reactionsView.setReactions(reactions.getReactions());
|
||||
reactionsView.setReactions(reactions.reactions);
|
||||
reactionsView.setOnClickListener(view -> {
|
||||
if (eventListener != null && batchSelected.isEmpty()) {
|
||||
eventListener.onReactionClicked(current);
|
||||
@@ -773,6 +822,8 @@ public class ConversationItem extends BaseConversationItem
|
||||
return stickerStub.get().getFooter();
|
||||
} else if (hasOnlyThumbnail(messageRecord) && TextUtils.isEmpty(messageRecord.getText())) {
|
||||
return mediaThumbnailStub.get().getFooter();
|
||||
} else if (messageRecord.getType() == DcMsg.DC_MSG_CALL) {
|
||||
return callViewStub.get().getFooter();
|
||||
} else {
|
||||
return footer;
|
||||
}
|
||||
@@ -800,8 +851,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
}
|
||||
else if (showSender && dcContact !=null) {
|
||||
this.groupSender.setText(messageRecord.getSenderName(dcContact));
|
||||
int color = messageRecord.getSenderColor();
|
||||
this.groupSender.setTextColor(Util.rgbToArgbColor(color!=0? color : dcContact.getColor()));
|
||||
this.groupSender.setTextColor(Util.rgbToArgbColor(dcContact.getColor()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -875,6 +925,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
else if (documentViewStub.resolved()) documentViewStub.get().performClick();
|
||||
else if (webxdcViewStub.resolved()) webxdcViewStub.get().performClick();
|
||||
else if (vcardViewStub.resolved()) vcardViewStub.get().performClick();
|
||||
else if (callViewStub.resolved()) callViewStub.get().performClick();
|
||||
}
|
||||
|
||||
/// Event handlers
|
||||
@@ -884,17 +935,17 @@ public class ConversationItem extends BaseConversationItem
|
||||
if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) {
|
||||
performClick();
|
||||
} else if (slide.isWebxdcDocument()) {
|
||||
msgActionButton.performClick();
|
||||
WebxdcActivity.openWebxdcActivity(context, messageRecord);
|
||||
} else if (slide.isVcard()) {
|
||||
try {
|
||||
String path = slide.asAttachment().getRealPath(context);
|
||||
VcardContact vcardContact = rpc.parseVcard(path).get(0);
|
||||
new AlertDialog.Builder(context)
|
||||
.setMessage(context.getString(R.string.ask_start_chat_with, vcardContact.getDisplayName()))
|
||||
.setMessage(context.getString(R.string.ask_start_chat_with, vcardContact.displayName))
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
try {
|
||||
List<Integer> contactIds = rpc.importVcard(dcContext.getAccountId(), path);
|
||||
if (contactIds.size() > 0) {
|
||||
if (!contactIds.isEmpty()) {
|
||||
int chatId = dcContext.createChatByContactId(contactIds.get(0));
|
||||
if (chatId != 0) {
|
||||
Intent intent = new Intent(context, ConversationActivity.class);
|
||||
@@ -936,4 +987,21 @@ public class ConversationItem extends BaseConversationItem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CallClickListener implements CallItemView.CallClickListener {
|
||||
public void onClick(final View v, final CallInfo callInfo) {
|
||||
if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) {
|
||||
performClick();
|
||||
} else {
|
||||
int accId = dcContext.getAccountId();
|
||||
int chatId = messageRecord.getChatId();
|
||||
if (!messageRecord.isOutgoing() && callInfo.state instanceof CallState.Alerting) {
|
||||
int callId = messageRecord.getId();
|
||||
CallUtil.openCall(getContext(), accId, chatId, callId, callInfo.sdpOffer);
|
||||
} else {
|
||||
CallUtil.startCall(getContext(), accId, chatId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.os.Build;
|
||||
import android.os.Vibrator;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||