Compare commits

...

479 Commits

Author SHA1 Message Date
tsu-gu a65c0b6efc add slovak translation 2023-02-05 21:13:03 +00:00
tsu-gu a8de1f8467 Delete 'lib/l10n/cwtch.arb'
wrong file name
2023-02-05 21:12:26 +00:00
tsu-gu a97b6e3dd8 add slovak translation 2023-02-05 21:11:25 +00:00
Sarah Jamie Lewis 8417204a24 Update UI Tests to Run with Flutter 3 / In Drone (#621)
Reviewed-on: cwtch.im/cwtch-ui#621
Co-authored-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
Co-committed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2023-02-01 22:48:36 +00:00
Sarah Jamie Lewis bf05a52d63 Fix debug layout errors + Fix Quoted File Message Layout 2022-12-30 23:17:18 +00:00
Sarah Jamie Lewis 23ae1ac0bb Allow Links to be Selectable / Fix Performance of Message Row to prevent Spurious Renders 2022-12-30 23:17:18 +00:00
Dan Ballard 7bcde5a1fa Merge pull request 'Ensure all Scrollbars have dedicated Controllers' (#613) from scrollbars into trunk
Reviewed-on: cwtch.im/cwtch-ui#613
2022-12-30 23:16:00 +00:00
Sarah Jamie Lewis 723b0eb04d Ensure all Scrollbars have dedicated Controllers
Fix: #602
2022-12-30 23:16:00 +00:00
Dan Ballard 119e683d3d Merge pull request 'Error fetch lcg scripts on failure' (#612) from failonfetch into trunk
Reviewed-on: cwtch.im/cwtch-ui#612
2022-12-30 23:15:29 +00:00
Sarah Jamie Lewis 6abf506f18 Error fetch lcg scripts on failure 2022-12-28 18:55:06 -08:00
Sarah Jamie Lewis fd8790fab6 Fix Mac Builds 2022-12-16 20:54:30 +00:00
Sarah Jamie Lewis 00c385bb91 Merge pull request 'fix ffi DeactivatePeerEngine call - hide it - bump pubspec version' (#599) from fixDeactivate into trunk
Reviewed-on: cwtch.im/cwtch-ui#599
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-12-15 03:58:17 +00:00
Dan Ballard c2f46a0117 Merge branch 'noDisable' into fixDeactivate 2022-12-14 16:16:01 -08:00
Dan Ballard f7da7b4bb7 bump pubspec for 1.10 release 2022-12-14 16:15:30 -08:00
Dan Ballard 448900b48e no isable profiles till it works 2022-12-14 16:14:58 -08:00
Dan Ballard 4a11968567 update flutter-desktop linux container to use newer build with older glibc for compat 2022-12-13 09:54:36 -08:00
Dan Ballard abec0f3ad5 Merge pull request 'Fix Android File Sharing' (#600) from newjson into trunk
Reviewed-on: cwtch.im/cwtch-ui#600
2022-12-13 00:37:17 +00:00
Sarah Jamie Lewis 28ce08637b Fix Android File Sharing 2022-12-12 15:13:32 -08:00
Sarah Jamie Lewis c8bdc56507 Merge pull request 'save profile.autostart on create' (#598) from saveAutostart into trunk
Reviewed-on: cwtch.im/cwtch-ui#598
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-12-12 02:54:32 +00:00
Dan Ballard 4e895723e2 Merge branch 'trunk' into saveAutostart 2022-12-12 00:00:20 +00:00
Dan Ballard 9958c72d5d fix ffi DeactivatePeerEngine call 2022-12-11 15:59:37 -08:00
Dan Ballard c498a0c86a fix kotlin mainactivity createPeer 2022-12-11 15:14:05 -08:00
Sarah Jamie Lewis 596b65f12d Merge pull request 'add offline icon to server rows' (#597) from serverOfflineIcon into trunk
Reviewed-on: cwtch.im/cwtch-ui#597
2022-12-11 18:35:16 +00:00
Sarah Jamie Lewis 16eda0ce8a Merge branch 'trunk' into serverOfflineIcon 2022-12-11 18:35:09 +00:00
Dan Ballard ff332dee9c save profile.autostart on create 2022-12-11 10:16:08 -08:00
Dan Ballard 8eb2e73b10 add offline icon to server rows 2022-12-11 10:13:07 -08:00
Sarah Jamie Lewis 469624c46c Merge pull request 'add windowManager.focus to notification click response' (#595) from notiFocus into trunk
Reviewed-on: cwtch.im/cwtch-ui#595
2022-12-11 17:25:34 +00:00
Dan Ballard ffa52f697a add windowManager.focus to notification click response 2022-12-11 17:25:29 +00:00
Sarah Jamie Lewis f585122f57 Fix NPE Exception in Double Col View During Notification Swtiching 2022-12-11 09:18:18 -08:00
Sarah Jamie Lewis be26d40176 Update Splash Screen Animation with new Cwtch Logo (by Marcia) 2022-12-09 19:45:50 -08:00
Sarah Jamie Lewis 9a67008ece Update LCG + Fuzzbot Fix
Fix: #584
Fix: #575
2022-12-07 15:02:53 -08:00
Sarah Jamie Lewis 27a729d09a Fix Change Password for Unencrypted Profiles / Handle TokenUpdates with No Tokens / Format 2022-12-06 12:11:55 -08:00
Sarah Jamie Lewis 7ee619f1a6 Merge pull request 'lcgbump' (#591) from lcgBump into trunk
Reviewed-on: cwtch.im/cwtch-ui#591
2022-12-06 18:05:26 +00:00
Dan Ballard 993f42113e lcgbump 2022-12-06 09:35:37 -08:00
Sarah Jamie Lewis 6418170b2e Merge pull request '69 nice theme fixes' (#590) from 69nicefixes into trunk
Reviewed-on: cwtch.im/cwtch-ui#590
2022-12-06 00:33:23 +00:00
erinn 91f44d631c Merge branch 'trunk' into 69nicefixes 2022-12-06 00:27:58 +00:00
erinn 928a201a3b missing contactsview.dart from last commit 2022-12-05 16:16:08 -08:00
erinn 007fb02cba another double toast fix, more comment cleanup 2022-12-05 16:14:21 -08:00
erinn 65b0ecc0c3 many theme consistency updates per marcia 2022-12-05 16:06:33 -08:00
Sarah Jamie Lewis faba13d435 Merge pull request 'fixes' (#588) from fixes into trunk
Reviewed-on: cwtch.im/cwtch-ui#588
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-12-06 00:06:19 +00:00
Dan Ballard 6746abacd7 fix translations for new feature, use icon 2022-12-05 12:26:42 -08:00
Dan Ballard d4546199e4 support (de)activatePeerEngine; turning peers on/off; autostart peers; new UI update for offline contacts/profiles 2022-12-05 12:26:42 -08:00
Dan Ballard f29e926d28 fix lunux scripts LD_LIBRARY_PATH to include Tor (for ssl etc) and fix color of add/edit profile button 2022-12-05 12:26:42 -08:00
Sarah Jamie Lewis ada351f778 Set home directory in linux setup
Fix: #586
2022-12-04 09:33:01 -08:00
Sarah Jamie Lewis 65a1280b35 Update LCG
Fix: #560
2022-11-30 11:41:47 -08:00
Sarah Jamie Lewis 3b60bf085a Fix context listen issues in debug 2022-11-28 13:59:43 -08:00
Sarah Jamie Lewis 3784ec04e3 Asset TextCaptialization.sentences on Android
Fix: #21
2022-11-28 13:46:59 -08:00
Sarah Jamie Lewis 17ffe03dba Fix Android Profile Export
Fix #512.
2022-11-28 13:35:12 -08:00
Sarah Jamie Lewis 059d32718b Enable Formatting on Quoted Messages
Fix: #556
2022-11-28 10:33:04 -08:00
Sarah Jamie Lewis f6710484a2 Add replying to 2022-11-24 13:28:25 -08:00
Sarah Jamie Lewis b0668812e4 Juniper Theme Intro 2022-11-23 12:20:02 -08:00
Sarah Jamie Lewis cfe5f29213 Juniper Theme
A theme dedicated to Juniper.
2022-11-23 12:19:01 -08:00
Sarah Jamie Lewis b866124147 Hide Address Copy in Peer Settings when Streamer Mode is Enabled
Fix: #557
2022-11-23 11:58:49 -08:00
Sarah Jamie Lewis ed4bb99fde Add More Prominant Unlock Profile Button
+ add buttons to onboarding flow

Fix: #561
2022-11-23 10:05:54 -08:00
Sarah Jamie Lewis b282ace9c3 Allow Message formatting to be turned off when experiments are disabled
Fix: #549
2022-11-23 08:34:14 -08:00
Sarah Jamie Lewis daa0e65070 Update 2022-11-18 06:24:24 -08:00
Sarah Jamie Lewis 25e1300b2b Support for Brazilian Portuguese (#564) 2022-11-18 06:21:32 -08:00
Kaio Duarte Costa d4a87cd416 language localization corrections and line inclusion
Added: 'LocalePtBr' 
Correction: 'locale'
2022-11-18 13:28:36 +00:00
Kaio Duarte Costa 0455ed15d7 Brazilian Portuguese translation - 99% complete 2022-11-18 13:23:29 +00:00
Kaio Duarte Costa 1f15e8af39 Translation Update - 80% 2022-11-17 21:23:50 +00:00
Kaio Duarte Costa 32b4ad2576 File with Brazilian Portuguese translation 2022-11-17 00:45:01 +00:00
Sarah Jamie Lewis 08d337401f Update Flutter Build Images 2022-11-16 17:54:23 +00:00
Sarah Jamie Lewis 91eca10f12 Update test/crypto deps 2022-11-16 17:54:23 +00:00
Sarah Jamie Lewis 870e7338ae Copyright notice 2022-11-16 17:54:23 +00:00
Sarah Jamie Lewis af4aab3a47 New Cwtch Icons, Dutch / Turkish Translations 2022-11-16 17:54:23 +00:00
Dan Ballard 8972d0eef5 Merge pull request 'Fix Mac Builds' (#541) from fix-mac into trunk
Reviewed-on: cwtch.im/cwtch-ui#541
2022-09-10 21:34:51 +00:00
Sarah Jamie Lewis 881cfbd0a3 Merge branch 'trunk' into fix-mac 2022-09-10 21:34:19 +00:00
Sarah Jamie Lewis c5f684e42e Fix Mac Builds 2022-09-10 14:33:33 -07:00
Dan Ballard 2d89b30881 Merge pull request 'update-cwtch' (#540) from update-cwtch into trunk
Reviewed-on: cwtch.im/cwtch-ui#540
2022-09-10 19:47:23 +00:00
Sarah Jamie Lewis e51c30ecc9 Update Cwtch 2022-09-10 12:45:21 -07:00
Sarah Jamie Lewis fb4c438e1c Dutch Integration 2022-09-10 08:11:08 -07:00
Sarah Jamie Lewis 2eca5058a8 Merge pull request 'fix antispam tooltips/message blocking for p2p contacts' (#538) from fixAntispam into trunk
Reviewed-on: cwtch.im/cwtch-ui#538
2022-09-10 00:26:48 +00:00
Dan Ballard 94297ee85f fix antispam tooltips/message blocking for p2p contacts 2022-09-09 17:20:34 -07:00
Sarah Jamie Lewis 3fe732809d Merge pull request 'bump tor versions, move linux to harvested tor, bump lcg to 1.9.0' (#537) from torBump into trunk
Reviewed-on: cwtch.im/cwtch-ui#537
2022-09-09 23:29:33 +00:00
Dan Ballard 52b1f28252 lcg 1.9.0 2022-09-09 16:03:39 -07:00
Dan Ballard 16f413177f bump tor versions, move linux to harvested tor 2022-09-09 15:20:09 -07:00
Dan Ballard ea7b307de2 Merge pull request 'allow sharing proifile via qrcode' (#536) from qrcode into trunk
Reviewed-on: cwtch.im/cwtch-ui#536
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-09-09 22:19:55 +00:00
Dan Ballard 4d4901838e qrcode experiment; dont sent when doing antispam on enter 2022-09-09 14:55:58 -07:00
Dan Ballard cfa4b4f95b allow sharing proifile via qrcode 2022-09-09 14:02:28 -07:00
Dan Ballard 2defc7ea2c Merge pull request 'Update translations' (#535) from update-translations into trunk
Reviewed-on: cwtch.im/cwtch-ui#535
2022-09-09 19:58:13 +00:00
Sarah Jamie Lewis cfb32bc84a Update translations 2022-09-09 12:50:10 -07:00
Dan Ballard e570f6941b Merge pull request 'Expose antispam status in UI' (#534) from antispam into trunk
Reviewed-on: cwtch.im/cwtch-ui#534
2022-09-09 19:33:44 +00:00
Sarah Jamie Lewis 37e18d03a1 Expose antispam status in UI 2022-09-09 12:24:20 -07:00
Sarah Jamie Lewis 76c925d874 Merge pull request 'lcg bump; selected conversation sanity null;' (#533) from lcgBump into trunk
Reviewed-on: cwtch.im/cwtch-ui#533
2022-09-09 19:22:31 +00:00
Dan Ballard 975983be3c lcg bump; selected conversation sanity null; 2022-09-09 12:03:32 -07:00
Sarah Jamie Lewis 7edc46743f Merge pull request 'save message drafts (until cwtch close)' (#529) from savedrafts into trunk
Reviewed-on: cwtch.im/cwtch-ui#529
2022-08-26 21:58:18 +00:00
erinn 74ab39067e provider.listen false, revert final 2022-08-26 14:44:13 -07:00
erinn cac2064731 save message drafts (until cwtch close) 2022-08-26 14:34:33 -07:00
Dan Ballard 93284708e0 Merge pull request 'Load Messages when Syncing a Group' (#527) from load_messages_sync into trunk
Reviewed-on: cwtch.im/cwtch-ui#527
2022-08-19 02:45:58 +00:00
Sarah Jamie Lewis 521c0600a2 Load Messages when Syncing a Group
Fixes: #306
2022-08-18 14:26:42 -07:00
Dan Ballard 7f2a8d649d Merge pull request 'fix importBundle error handling and dupping of events' (#523) from noAddContactDup into trunk
Reviewed-on: cwtch.im/cwtch-ui#523
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-08-16 18:00:34 +00:00
Dan Ballard d550c23fbd importBundle returns result 2022-08-16 10:41:36 -07:00
Dan Ballard 5d09341277 fix importBundle error handling and dupping of events 2022-08-14 20:43:34 -07:00
Dan Ballard 191065f51c Merge pull request 'Initial Commit of Fastlane + Debug Accessibiltiy Views' (#521) from debug_access into trunk
Reviewed-on: cwtch.im/cwtch-ui#521
2022-08-12 14:51:34 +00:00
Sarah Jamie Lewis 47e26f18fc Initial Commit of Fastlane + Debug Accessibiltiy Views 2022-08-11 13:17:38 -07:00
Dan Ballard 2bf28e2c6a Merge pull request 'Initial Support for Turkish' (#519) from locale_tr into trunk
Reviewed-on: cwtch.im/cwtch-ui#519
2022-07-29 22:53:47 +00:00
Sarah Jamie Lewis af3c6940bd Initial Support for Turkish 2022-07-29 15:20:16 -07:00
Dan Ballard c3fa6735f5 Merge pull request 'Fix Quoting of Large Messages' (#517) from fixquote into trunk
Reviewed-on: cwtch.im/cwtch-ui#517
2022-07-27 19:38:46 +00:00
Sarah Jamie Lewis 8cfbc39988 Merge branch 'trunk' into fixquote 2022-07-27 17:38:21 +00:00
Sarah Jamie Lewis fc29b10f12 Fix Quoting of Large Messages 2022-07-27 10:35:54 -07:00
Dan Ballard a49ad07b40 Merge pull request 'Fix indexing error in conversation list search.' (#516) from pinned into trunk
Reviewed-on: cwtch.im/cwtch-ui#516
2022-07-25 18:09:09 +00:00
Sarah Jamie Lewis 34e296959a Fix indexing error in conversation list search.
Only allow pinning of accepted contacts
2022-07-25 09:31:56 -07:00
Sarah Jamie Lewis d4d7a54af1 Merge pull request 'docs on making a self signed cert' (#491) from winInstructions into trunk
Reviewed-on: cwtch.im/cwtch-ui#491
2022-07-25 16:27:37 +00:00
Sarah Jamie Lewis 5139846f31 Merge branch 'trunk' into winInstructions 2022-07-25 16:27:28 +00:00
Sarah Jamie Lewis 483213c63b Merge pull request 'Pinning Conversations' (#515) from pinned into trunk
Reviewed-on: cwtch.im/cwtch-ui#515
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-07-25 16:27:20 +00:00
Sarah Jamie Lewis 546ac6c23d Pinning Conversations 2022-07-22 10:18:55 -07:00
Dan Ballard 3a752b7397 Merge pull request 'Implement View Replies' (#507) from viewreplies into trunk
Reviewed-on: cwtch.im/cwtch-ui#507
2022-07-07 22:13:10 +00:00
Sarah Jamie Lewis 7540aed701 Handle viewing replies to images correctly 2022-07-07 13:51:59 -07:00
Sarah Jamie Lewis ad52f2e0c8 Fixup Padding and add message about no replies 2022-07-07 13:40:23 -07:00
Sarah Jamie Lewis 337f6dc5d9 Use Long Press on Android to access replies 2022-07-07 12:58:44 -07:00
Sarah Jamie Lewis 814e6df6f6 Implement View Replies 2022-07-07 12:58:44 -07:00
Dan Ballard 62ea8278f3 Merge pull request 'Prevent a new line in the middle of a sentance from sending a message' (#506) from filesharing-persist into trunk
Reviewed-on: cwtch.im/cwtch-ui#506
2022-07-06 22:02:03 +00:00
Sarah Jamie Lewis e8a638ed29 Prevent a new line in the middle of a sentance from sending a message 2022-07-06 15:00:34 -07:00
Sarah Jamie Lewis 44fba12d21 Merge pull request 'File sharing Manager + File Sharing Fixes' (#505) from filesharing-persist into trunk
Reviewed-on: cwtch.im/cwtch-ui#505
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-07-06 21:19:20 +00:00
Sarah Jamie Lewis 7516232bd4 Merge branch 'trunk' into filesharing-persist 2022-07-06 21:19:12 +00:00
Sarah Jamie Lewis 5be25b87c4 Android 2022-07-06 13:57:35 -07:00
Sarah Jamie Lewis e13ad5d218 Update lcg 2022-07-06 13:11:49 -07:00
Sarah Jamie Lewis c397a9cdb7 Update File Sharing tooltip 2022-07-06 12:25:04 -07:00
Sarah Jamie Lewis 60e822cf12 Android + Time out Resume Logic so files can be requested again 2022-07-06 12:14:40 -07:00
Sarah Jamie Lewis 0ea2a2116e File Sharing Manager 2022-07-06 11:57:04 -07:00
Dan Ballard 9fb9759e6a Merge pull request 'Fix bugs in Invite Modal' (#504) from invite-fix into trunk
Reviewed-on: cwtch.im/cwtch-ui#504
2022-07-04 22:12:13 +00:00
Sarah Jamie Lewis 62b87f2939 Fix #498 2022-07-04 14:39:58 -07:00
Sarah Jamie Lewis da58555104 Fix bugs in Invite Modal 2022-07-04 14:34:32 -07:00
Sarah Jamie Lewis d8cfb5c730 Merge pull request 'android-modal' (#500) from android-modal into trunk
Reviewed-on: cwtch.im/cwtch-ui#500
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-07-04 19:17:05 +00:00
Sarah Jamie Lewis 0dd9ecedac update pubspec version 2022-07-04 12:16:38 -07:00
Sarah Jamie Lewis 61ee9491ab Fix Cut off Buttons in Android 2022-07-04 12:16:38 -07:00
Sarah Jamie Lewis 6b9cf1f164 Fix Modal for New/Import and Unlock on Android 2022-07-04 12:16:38 -07:00
Sarah Jamie Lewis b8326762bf Fix Android Modal Menu 2022-07-04 12:16:38 -07:00
Dan Ballard af9a386ae8 Merge pull request 'new russian translations from @RuLang' (#499) from ru-update into trunk
Reviewed-on: cwtch.im/cwtch-ui#499
2022-07-04 18:56:12 +00:00
Sarah Jamie Lewis da5925c7b3 new russian translations from @RuLang 2022-07-04 10:43:30 -07:00
Dan Ballard eabee61687 Merge pull request 'pubspec andro ver bump' (#497) from androidExported into trunk
Reviewed-on: cwtch.im/cwtch-ui#497
2022-06-29 17:38:40 +00:00
Dan Ballard 102341f931 pubspec andro ver bump 2022-06-29 10:38:07 -07:00
Dan Ballard e36c5bf2f9 Merge pull request 'android 12 sdk 31 fixes' (#496) from androidExported into trunk
Reviewed-on: cwtch.im/cwtch-ui#496
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-06-29 17:37:07 +00:00
Dan Ballard 629c9152ca Merge branch 'trunk' into androidExported 2022-06-29 17:22:40 +00:00
Dan Ballard 9298be0a61 android 12 sdk 31 fixes 2022-06-29 10:21:54 -07:00
Sarah Jamie Lewis fc4a87e3aa Merge pull request 'upgrade android kotlin + work package for android 12' (#495) from androidExported into trunk
Reviewed-on: cwtch.im/cwtch-ui#495
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-06-29 03:03:12 +00:00
Dan Ballard bef8ca083b Merge branch 'trunk' into androidExported 2022-06-29 00:29:28 +00:00
Dan Ballard 8d0b277731 upgrade android kotlin + work package for android 12 2022-06-28 17:28:55 -07:00
Sarah Jamie Lewis 7badbca926 Merge pull request 'android exported activity' (#494) from androidExported into trunk
Reviewed-on: cwtch.im/cwtch-ui#494
2022-06-28 20:48:10 +00:00
Dan Ballard 708f00f678 android exported activity 2022-06-28 13:20:29 -07:00
Dan Ballard 4404977128 Merge pull request '1.8.0' (#493) from 1.8.0 into trunk
Reviewed-on: cwtch.im/cwtch-ui#493
2022-06-27 19:51:28 +00:00
Sarah Jamie Lewis e29366cb49 Merge branch 'trunk' into 1.8.0 2022-06-27 19:21:01 +00:00
Sarah Jamie Lewis 3a12a94a85 1.8.0 2022-06-27 12:20:24 -07:00
Sarah Jamie Lewis 8da9db87de Merge pull request 'newwincert' (#492) from newwincert into trunk
Reviewed-on: cwtch.im/cwtch-ui#492
2022-06-24 05:33:56 +00:00
Sarah Jamie Lewis 93adb32ca5 Merge branch 'trunk' into newwincert 2022-06-24 05:33:49 +00:00
Dan Ballard ee9af54917 adjust drone to handle new windows cert and sign more dlls 2022-06-23 21:46:46 -07:00
Sarah Jamie Lewis 453feae88a Merge pull request 'Fix Image Width Handling' (#490) from fuzzbot-fixes into trunk
Reviewed-on: cwtch.im/cwtch-ui#490
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-06-23 23:38:15 +00:00
Dan Ballard e32e32ed27 docs on making a self signed cert 2022-06-23 16:10:05 -07:00
Sarah Jamie Lewis bf1eece1e2 increase max image size 2022-06-23 16:00:54 -07:00
Sarah Jamie Lewis 9c9916e7c9 max height on images 2022-06-23 15:59:25 -07:00
Sarah Jamie Lewis b3788b4f05 Fix Image Width Handling 2022-06-23 15:51:16 -07:00
Dan Ballard 5f67f626e5 Merge pull request 'fuzzbot-fixes' (#489) from fuzzbot-fixes into trunk
Reviewed-on: cwtch.im/cwtch-ui#489
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-06-23 19:26:46 +00:00
Sarah Jamie Lewis 00ca54a6a3 Merge branch 'trunk' into fuzzbot-fixes 2022-06-23 19:08:54 +00:00
Sarah Jamie Lewis 5770eb4b66 Fix File Bubble Preview Cache Issues + Wrap Quoted Message Malformed Bubbles as Rows 2022-06-23 12:07:39 -07:00
Sarah Jamie Lewis ab77ad80d1 Fix Bugs in Quoted Message Selection found by Fuzzbot 2022-06-23 11:25:26 -07:00
Dan Ballard 0c426a129a Merge pull request 'Turn on Message Formatting by Default' (#488) from default_message_formatting into trunk
Reviewed-on: cwtch.im/cwtch-ui#488
2022-06-22 21:34:22 +00:00
Sarah Jamie Lewis 0aa0d286ef move formatting out of experiments in settings 2022-06-22 14:22:52 -07:00
Sarah Jamie Lewis 1483ddcc94 Turn on Message Formatting by Default 2022-06-22 12:56:35 -07:00
Dan Ballard 405160947b Merge pull request 'add support for diff arch macos libcwtch-go' (#487) from macarm into trunk
Reviewed-on: cwtch.im/cwtch-ui#487
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-06-22 19:45:13 +00:00
Dan Ballard 3a5668734e mac arm64 2022-06-22 12:40:06 -07:00
Dan Ballard a6406e9068 add support for diff arch macos libcwtch-go 2022-06-22 12:05:46 -07:00
Sarah Jamie Lewis a4e1a7ede1 Merge pull request 'New Translations + Update LCG' (#486) from intl_fix into trunk
Reviewed-on: cwtch.im/cwtch-ui#486
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-06-21 23:11:54 +00:00
Sarah Jamie Lewis 77227111fd Merge branch 'trunk' into intl_fix 2022-06-21 23:09:52 +00:00
Sarah Jamie Lewis 9d2654459c Merge pull request 'fix first sync' (#485) from fixFirstSync into trunk
Reviewed-on: cwtch.im/cwtch-ui#485
2022-06-21 23:09:46 +00:00
Sarah Jamie Lewis 6dca8e80e6 New Translations + Update LCG 2022-06-21 16:09:09 -07:00
Dan Ballard 3f4530f299 fix first sync 2022-06-21 16:03:04 -07:00
Dan Ballard 40b3207e2d Merge pull request 'Remove Group "Address" - it is badly named, and no longer relavent' (#482) from fix_308 into trunk
Reviewed-on: cwtch.im/cwtch-ui#482
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-06-21 22:12:14 +00:00
Sarah Jamie Lewis fc1f910486 Remove Awful Flutter 3 Screen Transitions 2022-06-21 14:58:09 -07:00
Sarah Jamie Lewis f71bce5b71 Remove Group "Address" - it is badly named, and no longer relavent 2022-06-21 14:08:54 -07:00
Sarah Jamie Lewis c01860f1de Merge pull request 'Flutter3, local_desktop_notifications on linux again' (#484) from flutter3_notify into trunk
Reviewed-on: cwtch.im/cwtch-ui#484
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-06-21 21:08:47 +00:00
Dan Ballard c7e6cfcbc1 macos podfile lock update for flutter 3 2022-06-21 16:58:02 -04:00
Dan Ballard d9acca7b1b drone flutter 3.0.1 windows 2022-06-21 13:06:38 -07:00
Dan Ballard 914fe9c300 updated widget goldens, cwtch label flutter 3 minute change 2022-06-21 13:06:38 -07:00
Dan Ballard ce5499419f drone test 2022-06-21 13:06:38 -07:00
Dan Ballard 531595e9e9 remove notification debugging and linux notifier 2022-06-21 13:06:38 -07:00
Dan Ballard 9857dff9a3 upgraded to flutter 3, upgraded kotlin and gradle, some deps 2022-06-21 13:06:38 -07:00
Dan Ballard 03b3d86a41 translate lux material stuff; upgrade to fixed local notification package; upgrade provide, scrollable_position 2022-06-21 13:06:38 -07:00
Dan Ballard a5040b7236 start of flutter3 migration, update l10n/custom_material_delegate, update cwtchNotifier to user local_notifier 2022-06-21 13:06:38 -07:00
Dan Ballard a83b357f0f Merge pull request 'Fix Debug Errors + Implement Scroll To on DM' (#481) from fix_308 into trunk
Reviewed-on: cwtch.im/cwtch-ui#481
2022-06-20 19:10:32 +00:00
Sarah Jamie Lewis b425175fff Attempt to fix Scroll-to Bug 2022-06-20 11:41:31 -07:00
Sarah Jamie Lewis 8570199196 Fix: #308 - Scroll to Contact
Also fixes a bunch of debug-build issues (overflows / resizes).
2022-06-20 10:54:06 -07:00
Dan Ballard 1122c818f5 Merge pull request 'Add Snackbar Notifications for all Copies' (#478) from snackbars into trunk
Reviewed-on: cwtch.im/cwtch-ui#478
2022-06-16 17:54:30 +00:00
Dan Ballard 6714b0d8a0 Merge pull request 'Message Formatting Toolbar Tooltips' (#477) from formatting_toolbar_tooltip into trunk
Reviewed-on: cwtch.im/cwtch-ui#477
2022-06-16 17:53:54 +00:00
Sarah Jamie Lewis 0d90219c87 Add Snackbar Notifications for all Copies 2022-06-16 09:40:55 -07:00
Sarah Jamie Lewis 8ab82569e3 Message Formatting Toolbar Tooltips 2022-06-16 09:23:10 -07:00
Dan Ballard a7861681e1 Merge pull request 'Update Translations (including RU from @RuLang)' (#476) from intl into trunk
Reviewed-on: cwtch.im/cwtch-ui#476
2022-06-15 18:06:56 +00:00
Sarah Jamie Lewis 106b45c758 Update Translations (including RU from @RuLang) 2022-06-15 10:46:05 -07:00
Sarah Jamie Lewis 644ae502e5 Merge pull request 'formatting_toolbar' (#475) from formatting_toolbar into trunk
Reviewed-on: cwtch.im/cwtch-ui#475
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-06-15 03:14:17 +00:00
Sarah Jamie Lewis 7bae6485f7 Fixup Formatting PR (Dans Comments) 2022-06-14 18:44:24 -07:00
Sarah Jamie Lewis 04c335e7a4 formatting toolbar 2022-06-14 18:30:04 -07:00
Sarah Jamie Lewis 3961692817 Nicer Quoted Messages 2022-06-13 10:06:06 -07:00
Sarah Jamie Lewis d703a9636f Fix Contact Message Date not displaying date for day old messages 2022-06-13 09:31:25 -07:00
Dan Ballard e4419366a4 Merge pull request 'Click to scroll on Quoted Message / Shorten Text' (#469) from marcia_fixes into trunk
Reviewed-on: cwtch.im/cwtch-ui#469
2022-06-10 23:43:09 +00:00
Sarah Jamie Lewis f848316db9 Fix bug preventing scrolling to unread messages 2022-06-10 15:42:54 -07:00
Sarah Jamie Lewis a5b253f185 Merge pull request 'reply_links' (#470) from reply_links into marcia_fixes
Reviewed-on: cwtch.im/cwtch-ui#470
2022-06-10 21:32:13 +00:00
Sarah Jamie Lewis e7c19c7477 Merge pull request 'show down button in messageview when ever scrolling up' (#471) from show_down into marcia_fixes
Reviewed-on: cwtch.im/cwtch-ui#471
2022-06-10 21:31:10 +00:00
Dan Ballard 59df024867 show down button in messageview when ever scrolling up 2022-06-10 14:28:16 -07:00
Sarah Jamie Lewis 65ff084952 make links in replies clickable 2022-06-10 14:21:40 -07:00
Sarah Jamie Lewis b3e11cfffd remove scroll controller from message view 2022-06-10 12:24:38 -07:00
Sarah Jamie Lewis 0c9be47e17 Click to scroll on Quoted Message / Shorten Text 2022-06-10 12:12:43 -07:00
Dan Ballard 3bb3a8736c Merge pull request 'Fix message view title padding in doublecol view' (#468) from marcia_fixes into trunk
Reviewed-on: cwtch.im/cwtch-ui#468
2022-06-10 18:16:31 +00:00
Sarah Jamie Lewis 67850e8e4b Fix message view title padding in doublecol view 2022-06-10 10:40:39 -07:00
Dan Ballard c8e896fa51 Merge pull request 'Modal Menu UI Fixes' (#467) from marcia_fixes into trunk
Reviewed-on: cwtch.im/cwtch-ui#467
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-06-09 23:19:13 +00:00
Sarah Jamie Lewis d1e8f71290 fixes for profile buttons 2022-06-09 14:54:48 -07:00
Sarah Jamie Lewis be8646e805 fix padding 2022-06-09 14:30:38 -07:00
Sarah Jamie Lewis 6d42f2c76c make text bold and add additional padding to contacts modal 2022-06-09 14:28:24 -07:00
Sarah Jamie Lewis 8429907650 modal menus design fixes 2022-06-09 14:26:02 -07:00
Sarah Jamie Lewis c3848553d7 Bugfix when resizing app when menu is open 2022-06-09 13:49:38 -07:00
Sarah Jamie Lewis 3c85b8f59e Merge pull request 'Column-wise contact row (marcia feedback)' (#466) from marcia_fixes into trunk
Reviewed-on: cwtch.im/cwtch-ui#466
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-06-09 20:43:10 +00:00
Sarah Jamie Lewis d0e7e6703b Different buttons 2022-06-09 13:40:42 -07:00
Sarah Jamie Lewis 2bc47173f9 more clear contact request 2022-06-09 13:36:59 -07:00
Sarah Jamie Lewis 15c68d8812 remove padding 2022-06-09 13:20:01 -07:00
Sarah Jamie Lewis e76f2883c6 Column-wise contact row (marcia feedback) 2022-06-09 13:10:27 -07:00
Dan Ballard 439b9b874f Merge pull request 'marcia settings fixes' (#462) from marcia_fixes into trunk
Reviewed-on: cwtch.im/cwtch-ui#462
2022-05-31 23:31:27 +00:00
Sarah Jamie Lewis f5393cdb79 Merge branch 'trunk' into marcia_fixes 2022-05-31 23:19:49 +00:00
Sarah Jamie Lewis c0f1b674aa marcia settings fixes 2022-05-31 13:37:32 -07:00
Dan Ballard 630713a5e4 Merge pull request 'New Polish Translations' (#460) from pl_intl into trunk
Reviewed-on: cwtch.im/cwtch-ui#460
2022-05-24 18:53:52 +00:00
Sarah Jamie Lewis d10a6df872 Merge branch 'trunk' into pl_intl 2022-05-24 18:16:16 +00:00
Sarah Jamie Lewis 2723a35d44 New Polish Translations 2022-05-24 11:14:42 -07:00
Dan Ballard 427081c937 Merge pull request 'Fix #457 + Formatting' (#459) from fix457 into trunk
Reviewed-on: cwtch.im/cwtch-ui#459
2022-05-11 19:56:11 +00:00
Sarah Jamie Lewis 9d4abc3725 Fix #457 + Formatting 2022-05-11 12:44:24 -07:00
Sarah Jamie Lewis fa52b741bf Merge pull request 'v1.7.1 29' (#454) from pubspecBump into trunk
Reviewed-on: cwtch.im/cwtch-ui#454
2022-05-02 22:12:25 +00:00
Dan Ballard fb86fb6eae v1.7.1 29 2022-05-02 15:07:55 -07:00
Sarah Jamie Lewis 8dd696b6ab Merge pull request 'dont start 'new messages' when convo selected' (#453) from cache3.0 into trunk
Reviewed-on: cwtch.im/cwtch-ui#453
2022-05-01 17:32:16 +00:00
Dan Ballard 001ad854c7 dont start 'new messages' when convo selected 2022-04-30 14:43:45 -07:00
Dan Ballard af5fb678fc Merge pull request 'caching fixes for stability and android' (#450) from cache3.0 into trunk
Reviewed-on: cwtch.im/cwtch-ui#450
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-04-29 23:37:20 +00:00
Dan Ballard ffa51e83a1 new message marker moved from id to index and now works on old messages 2022-04-29 16:07:52 -07:00
Dan Ballard 441845ed49 Merge pull request 'Fix maximum width of dropdown boxes in settings' (#452) from fix-settings into trunk
Reviewed-on: cwtch.im/cwtch-ui#452
2022-04-29 17:50:18 +00:00
Sarah Jamie Lewis 0146436cb3 Fix maximum width of dropdown boxes in settings 2022-04-29 09:57:26 -07:00
Dan Ballard 0647a2d98d android pre load unsynced messages 2022-04-28 21:28:12 -07:00
Dan Ballard 0bcfe75a63 rework cache android resume based off message count totals, force pre fetch on load message list, tweak new messages bubble behaviour 2022-04-28 08:57:31 -07:00
Dan Ballard ecdcef2192 Merge pull request 'GetMessage* on android; make reply to use message cache; New Messages bubble doesn't reup' (#448) from replyFix into trunk
Reviewed-on: cwtch.im/cwtch-ui#448
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-04-27 04:51:20 +00:00
Dan Ballard e6c9f7becb GetMessage* on android; make reply to use message cache; New Messages bubble doesn't reup 2022-04-26 21:34:16 -07:00
Sarah Jamie Lewis 9d8f73ac00 Merge pull request 'Format, Context Binding and Check if File Exists in File Bubble' (#447) from file-fixes into trunk
Reviewed-on: cwtch.im/cwtch-ui#447
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-04-26 19:38:28 +00:00
Sarah Jamie Lewis dc78117e1a Format, Context Binding and Check if File Exists in File Bubble 2022-04-26 12:16:48 -07:00
Dan Ballard 59e3220bce Merge pull request 'Debug Info Fix and Dependency Upgrades' (#433) from perf into trunk
Reviewed-on: cwtch.im/cwtch-ui#433
2022-04-21 23:56:32 +00:00
Sarah Jamie Lewis 653ba199bc Merge branch 'trunk' into perf 2022-04-21 23:33:26 +00:00
Sarah Jamie Lewis 1b45205c48 Merge pull request 'nsis uninstall typo reg key' (#438) from winUninstall into trunk
Reviewed-on: cwtch.im/cwtch-ui#438
2022-04-21 23:33:17 +00:00
Dan Ballard 85186b2565 nsis uninstall typo reg key 2022-04-21 16:32:22 -07:00
Sarah Jamie Lewis 3287fa79ff Merge branch 'trunk' into perf 2022-04-21 23:32:07 +00:00
Sarah Jamie Lewis 111d522484 Upgrade lcg to 1.7.1 2022-04-21 16:31:17 -07:00
Sarah Jamie Lewis 20c854bafb Update Translations 2022-04-21 16:14:03 -07:00
Dan Ballard ffdc7b3262 Merge pull request 'winUninstall' (#434) from winUninstall into trunk
Reviewed-on: cwtch.im/cwtch-ui#434
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-04-21 18:40:31 +00:00
Dan Ballard a3d986d9d6 ffi on windows more options to detect tor; nsis installer warn about cwtch needing exiting 2022-04-20 18:20:44 -07:00
Sarah Jamie Lewis 5e3387ec8a Debug Info Fix an Dependency Upgrades 2022-04-20 17:28:38 -07:00
Dan Ballard a6c7682c84 nsis windows installer detect running, ask to not, abort 2022-04-20 14:25:36 -07:00
Dan Ballard b29836ed3b register uninstaller with windows add/remove programs 2022-04-20 12:26:28 -07:00
Sarah Jamie Lewis e0bf47b6ab Merge pull request 'a bunch of cache logic fixes and futher support for reconnect on android' (#431) from cachefixes into trunk
Reviewed-on: cwtch.im/cwtch-ui#431
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-04-20 18:16:47 +00:00
Dan Ballard 4bd92d854f comments 2022-04-19 20:46:59 -07:00
Dan Ballard 82d1bf873f lcg bump 2022-04-19 20:46:59 -07:00
Dan Ballard 5959981fe4 a bunch of cache logic fixes and futher support for reconnect on android 2022-04-19 20:46:59 -07:00
Dan Ballard ab315e289a Merge pull request 'MainActivity return result to not leave dart calls hanging' (#432) from kotlinResult into trunk
Reviewed-on: cwtch.im/cwtch-ui#432
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-04-20 03:36:20 +00:00
Dan Ballard 6392d67332 MainActivity return result to not leave dart calls hanging 2022-04-19 18:34:22 -07:00
Dan Ballard 8f0b73af2a Merge pull request 'fix linux notification icon (rever to old linux notification manager) and light theme fixes' (#429) from linuxNotif into trunk
Reviewed-on: cwtch.im/cwtch-ui#429
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-04-15 01:02:30 +00:00
Dan Ballard 4e2f83ccd9 light theme fixes + message cache ! fix 2022-04-14 17:50:53 -07:00
Dan Ballard dc5ba7b392 readd linux notification manager so it handles notification icon in different linux style installs 2022-04-14 17:02:24 -07:00
Sarah Jamie Lewis 3595f5d8d1 Merge pull request 'Debug Info Pane for Desktop' (#428) from debuginfo into trunk
Reviewed-on: cwtch.im/cwtch-ui#428
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-04-14 22:51:35 +00:00
Sarah Jamie Lewis 1df348c0c1 Debug Info Pane for Desktop 2022-04-14 15:34:36 -07:00
Sarah Jamie Lewis 548e7f4925 Merge pull request 'add android flag secure, pubspec vewrsion bump, and stubs for sdk31 hide overlay' (#427) from androidFlags into trunk
Reviewed-on: cwtch.im/cwtch-ui#427
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-04-13 21:57:52 +00:00
Dan Ballard a20d2dffc4 add android flag secure, pubspec vewrsion bump, and stubs for sdk31 hide overlay 2022-04-13 14:53:44 -07:00
Dan Ballard 2a712565e9 Merge pull request 'andoird settings / request for power optimization exemption' (#426) from power into trunk
Reviewed-on: cwtch.im/cwtch-ui#426
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-04-13 21:23:41 +00:00
Dan Ballard a94fd3547b add popup about disable battery unoptimized; fix mute policy loading 2022-04-13 14:09:33 -07:00
Dan Ballard c377a09748 add setting that reports / triggers android power optimization exemption 2022-04-13 12:57:15 -07:00
Dan Ballard d261fbd4c0 kotlin powermanagement info and exemption request 2022-04-13 12:53:32 -07:00
Dan Ballard 933ca74fbc Merge pull request 'Themeing Updates including Nicer Code Formatting' (#425) from theme-updates into trunk
Reviewed-on: cwtch.im/cwtch-ui#425
2022-04-12 22:08:20 +00:00
Sarah Jamie Lewis 38f317194d Merge branch 'trunk' into theme-updates 2022-04-12 21:18:58 +00:00
Sarah Jamie Lewis a4ab2ec060 Themeing Updates including Nicer Code Formatting 2022-04-12 14:15:58 -07:00
Dan Ballard 47795094a0 Merge pull request 'Add Hook into Add Contact Flow to better Gauge Intent' (#424) from add_contact_hook into trunk
Reviewed-on: cwtch.im/cwtch-ui#424
2022-04-12 19:27:18 +00:00
Sarah Jamie Lewis 0d1e7bb5a0 Add Hook into Add Contact Flow to better Gauge Intent
(This the future we can expand this, use this information to better guide people)
2022-04-12 12:15:39 -07:00
Sarah Jamie Lewis 987b80c92b Merge pull request 'Message Formatting Experiment Initial Commit' (#413) from message-formatting into trunk
Reviewed-on: cwtch.im/cwtch-ui#413
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-04-06 22:15:44 +00:00
Sarah Jamie Lewis e718adad8a Add Roboto Fonts License 2022-04-06 15:04:17 -07:00
Sarah Jamie Lewis 0b9c159e85 Icon and Log Error 2022-04-06 14:54:53 -07:00
Sarah Jamie Lewis a4a2af08b4 Message Formatting Experiment Initial Commit 2022-04-06 14:36:34 -07:00
Sarah Jamie Lewis 471a729d46 Merge pull request 'port most gomobile FlwtchWorker calls to lcg to MainActivity; sendmessage sets lastSeen time' (#412) from mainActivityPort into trunk
Reviewed-on: cwtch.im/cwtch-ui#412
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-04-06 19:01:40 +00:00
Dan Ballard 1cffea5c1a port most gomobile FlwtchWorker calls to lcg to MainActivity; sendmessage sets lastSeen time 2022-04-05 18:38:59 -07:00
Sarah Jamie Lewis e7c5b2cfa5 Merge pull request 'store last seen time in lcg and handle unread counts' (#411) from unreadSync into trunk
Reviewed-on: cwtch.im/cwtch-ui#411
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-04-04 22:17:59 +00:00
Dan Ballard e08114881c store last seen time in lcg and handle unread counts 2022-04-04 15:02:37 -07:00
Sarah Jamie Lewis 6eaf95a33b Merge pull request 'only send message on Enter Up event' (#408) from androMessage into trunk
Reviewed-on: cwtch.im/cwtch-ui#408
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-03-24 23:39:48 +00:00
Dan Ballard 0db68bcdbb Merge branch 'trunk' into androMessage 2022-03-24 23:37:31 +00:00
Dan Ballard f64559191b only send message on Enter Up event 2022-03-24 16:25:08 -07:00
Dan Ballard b8c1c7682b Merge pull request 'message cache allows index locking, rework messageHandler to use bulk fetching, sendMessage flow with no sleep; move some core getMessages/SendMessage handlers from FlwtchWorker to MainActivity' (#407) from androMessage into trunk
Reviewed-on: cwtch.im/cwtch-ui#407
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-03-24 19:41:16 +00:00
Dan Ballard 9812111041 comments, organizing logic 2022-03-24 12:04:09 -07:00
Dan Ballard ecc9a3a48c comments, fix new messages marker logic 2022-03-23 18:02:26 -07:00
Dan Ballard 523531e6be new lcg version 2022-03-23 18:02:26 -07:00
Dan Ballard ff3e60a750 message cache allows index locking, rework messageHandler to use bulk fetching, sendMessage flow with no sleep; move some core getMessages/SendMessage handlers from FlwtchWorker to MainActivity 2022-03-23 18:01:43 -07:00
Dan Ballard 5a1c66bc25 Merge pull request 'Renable Network Status Updates, Display Status in Contacts View' (#406) from ns into trunk
Reviewed-on: cwtch.im/cwtch-ui#406
2022-03-23 21:42:28 +00:00
Sarah Jamie Lewis 10780ac8cb Merge branch 'trunk' into ns 2022-03-23 21:24:14 +00:00
Sarah Jamie Lewis 0857d46809 Renable Network Status Updates, Display Status in Contacts View 2022-03-23 14:23:23 -07:00
Dan Ballard d7d3b2ef97 Merge pull request 'Fix: #344 - Folder Selection on Settings can be Opened Multiple times' (#402) from small_edits into trunk
Reviewed-on: cwtch.im/cwtch-ui#402
2022-03-22 02:17:14 +00:00
Sarah Jamie Lewis 65d5e9777d Fix: 344 - Folder Selection on Settings can be Opened Multiple times 2022-03-21 10:49:53 -07:00
Dan Ballard 27f4c5f00e Merge pull request 'Localizations + Allow Editing when Contact is Offline' (#400) from small_edits into trunk
Reviewed-on: cwtch.im/cwtch-ui#400
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-03-21 16:37:26 +00:00
Sarah Jamie Lewis f48b6af3dd Localizations + Allow Editing 2022-03-21 09:23:16 -07:00
Dan Ballard d8e19de5b1 Merge pull request 'Import and Export Profile' (#397) from import_export into trunk
Reviewed-on: cwtch.im/cwtch-ui#397
2022-03-11 21:10:03 +00:00
Sarah Jamie Lewis af03dd30cc Upgrade libcwtch 2022-03-11 12:27:44 -08:00
Sarah Jamie Lewis 8a3867b5b3 Import / Export for Android 2022-03-11 12:26:23 -08:00
Sarah Jamie Lewis 6237032716 Import and Export Profile 2022-03-11 12:26:21 -08:00
Dan Ballard 915cf1a6d8 Merge pull request 'splash on shutdown; android stability: check if lcg started' (#399) from splashExit into trunk
Reviewed-on: cwtch.im/cwtch-ui#399
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-03-11 00:45:45 +00:00
Dan Ballard c4ebed0a71 splash on shutdown; android stability: check if lcg started 2022-03-10 16:45:18 -08:00
Dan Ballard 3c71bb8184 Merge pull request 'Using build context instead of inherited context when building file bubble popout' (#395) from file_bubble_pop_fix into trunk
Reviewed-on: cwtch.im/cwtch-ui#395
2022-03-08 19:08:12 +00:00
Sarah Jamie Lewis c3661d4caa Using build context instead of inherited context when building file bubble popout
Fix: #394
2022-03-08 10:56:20 -08:00
Sarah Jamie Lewis 62a99797ca Merge pull request 'assorted android and sync progress fixes' (#391) from state into trunk
Reviewed-on: cwtch.im/cwtch-ui#391
2022-03-04 21:17:41 +00:00
Dan Ballard 7cfa9432c8 unbreak notifications on android ([Pp]ictures) + asset dup; fix sync progres resume logic 2022-03-04 13:15:08 -08:00
Dan Ballard 1d0cb785c1 fix android segfault in flwtch worker; try/catch to catch future bugs in flwtch worker; resume servers load sync status from cwtch; add bg color to sync progress bar; showdown now synchronous so completes 2022-03-04 13:15:08 -08:00
Dan Ballard 8eaa3974c9 Merge pull request 'Stop using key-based lookups for messages, use the message cache instead.' (#392) from message_row_fixes into trunk
Reviewed-on: cwtch.im/cwtch-ui#392
2022-03-04 21:14:34 +00:00
Sarah Jamie Lewis 6cc5146744 Readd-widget key cache 2022-03-04 12:14:43 -08:00
Sarah Jamie Lewis 1fea540f9d Stop using key-based lookups for messages, use the message cache instead.
Always update alignment to allow for message row objects to be reused
2022-03-04 12:14:43 -08:00
Dan Ballard 7457246a01 Merge pull request 'Finish up Danish translation' (#390) from da into trunk
Reviewed-on: cwtch.im/cwtch-ui#390
2022-03-03 18:52:02 +00:00
Sarah Jamie Lewis 0a26a1899b Finidh up Danish translation 2022-03-03 10:19:42 -08:00
Sarah Jamie Lewis 8183fbd987 Merge pull request 'danish-localization' (#388) from christofal/cwtch-ui:danish-localization into trunk
Reviewed-on: cwtch.im/cwtch-ui#388
2022-03-03 17:57:12 +00:00
Sarah Jamie Lewis f3f5f65e22 Merge branch 'trunk' into danish-localization 2022-03-03 17:56:26 +00:00
Dan Ballard c565089578 Merge pull request 'Spanish and Welsh complete' (#387) from es into trunk
Reviewed-on: cwtch.im/cwtch-ui#387
2022-03-02 23:04:32 +00:00
Sarah Jamie Lewis 009f99e0f5 Merge branch 'trunk' into es 2022-03-02 23:01:35 +00:00
Sarah Jamie Lewis 0894fc577b Spanish and Welsh complete 2022-03-02 15:00:34 -08:00
Dan Ballard b0977b31a5 Merge pull request 'Support Welsh, Update Translations for other Languages' (#385) from cy into trunk
Reviewed-on: cwtch.im/cwtch-ui#385
2022-03-02 19:31:46 +00:00
Sarah Jamie Lewis 6df922d64e Merge branch 'trunk' into cy 2022-03-02 19:29:54 +00:00
Sarah Jamie Lewis b70de4052d Support Welsh, Update Translations for other Languages 2022-03-02 11:28:43 -08:00
Allan Christoffersen 453558f034 Full Danish localization 2022-03-02 15:32:47 +01:00
Allan Christoffersen 481890b55f Initial commit of Danish localization 2022-03-02 14:30:53 +01:00
Dan Ballard 7122db0388 Merge pull request 'Move messageDate format handling to MessageBubbleDecoration' (#384) from messagedate into trunk
Reviewed-on: cwtch.im/cwtch-ui#384
2022-03-01 17:39:23 +00:00
Sarah Jamie Lewis c56f40c090 Merge branch 'trunk' into messagedate 2022-03-01 06:36:51 +00:00
Sarah Jamie Lewis c4c693144d Move messageDate format handling to MessageBubbleDecoration 2022-02-28 22:35:09 -08:00
Dan Ballard 891bf51a70 Merge pull request 'harmonize translations from lokalize' (#383) from i18n into trunk
Reviewed-on: cwtch.im/cwtch-ui#383
2022-03-01 00:02:33 +00:00
Sarah Jamie Lewis a559b0caf8 Update No with clobbered 2022-02-28 16:01:10 -08:00
Sarah Jamie Lewis 054e5fca84 harmonize translations from lokalize 2022-02-28 15:43:33 -08:00
Dan Ballard 6b5f4febe7 Merge pull request 'Full Norwegian Translation' (#382) from no into trunk
Reviewed-on: cwtch.im/cwtch-ui#382
2022-02-28 23:39:23 +00:00
Sarah Jamie Lewis 2c55f78913 Small updates 2022-02-28 15:19:25 -08:00
Henrik Austad f1cfd2c30f Norwegian localization 2022-02-28 15:18:39 -08:00
Sarah Jamie Lewis b36e76b818 remove norwya to add better 2022-02-28 15:18:21 -08:00
Dan Ballard 2aadea0cea Merge pull request 'Complete German Translation.' (#381) from de into trunk
Reviewed-on: cwtch.im/cwtch-ui#381
2022-02-28 20:49:44 +00:00
Dan Ballard 423a2bce5e Merge branch 'trunk' into de 2022-02-28 20:49:23 +00:00
Dan Ballard eef40f76f9 Merge pull request 'Add support for many prospective new languages' (#380) from lb into trunk
Reviewed-on: cwtch.im/cwtch-ui#380
2022-02-28 20:49:04 +00:00
Sarah Jamie Lewis 385f86be02 Complete German Translation 2022-02-28 12:29:20 -08:00
Sarah Jamie Lewis 193a9d6f89 Add support for many prospective new languages 2022-02-28 12:05:22 -08:00
Dan Ballard 2ade7e8e4f Merge pull request 'Initial support for Romanian localization' (#379) from ro into trunk
Reviewed-on: cwtch.im/cwtch-ui#379
2022-02-27 20:04:50 +00:00
Sarah Jamie Lewis 12a0fc1059 Update French 2022-02-27 12:02:17 -08:00
Sarah Jamie Lewis 82542664ad Merge branch 'trunk' into ro 2022-02-27 19:37:49 +00:00
Sarah Jamie Lewis 670d8bc343 Initial support for Romanian localization 2022-02-27 11:36:51 -08:00
Dan Ballard ce1db17148 Merge pull request 'WIP make NixNotificationManager using flutter_local_notification' (#375) from macNotifications into trunk
Reviewed-on: cwtch.im/cwtch-ui#375
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-02-24 00:22:06 +00:00
Dan Ballard 018a51b76e Merge branch 'macNotifications' of git.openprivacy.ca:cwtch.im/cwtch-ui into macNotifications 2022-02-23 16:21:50 -08:00
Dan Ballard 61cdb37226 lcg bump 2022-02-23 16:21:42 -08:00
Dan Ballard 5b4778dd78 Merge branch 'trunk' into macNotifications 2022-02-23 19:33:29 +00:00
Dan Ballard 152f5fbc96 garuntee no notifications while using app for nix 2022-02-23 11:27:15 -08:00
Dan Ballard 5e7272b15a nix notification manager only use title, body is unsafe 2022-02-23 11:13:03 -08:00
Dan Ballard 9473acd438 Merge pull request 'Add Message Status Widget to File Bubble' (#376) from fix297 into trunk
Reviewed-on: cwtch.im/cwtch-ui#376
2022-02-22 20:49:34 +00:00
Sarah Jamie Lewis 4fd8075497 Merge branch 'trunk' into fix297 2022-02-22 19:14:25 +00:00
Sarah Jamie Lewis 70eb160abc Add Message Status Widget to File Bubble
Also fix bug in peer settings
2022-02-22 11:09:44 -08:00
Dan Ballard 1a4dccf44a disable sound until we make it a setting 2022-02-20 09:53:15 -08:00
Dan Ballard 7509c20a62 make NixNotificationManager using flutter_local_notification 2022-02-18 15:50:34 -08:00
Dan Ballard 68c2e1547a Merge pull request 'Check WorkInfo is not Null' (#374) from cwtch1.6.1-fixes into trunk
Reviewed-on: cwtch.im/cwtch-ui#374
2022-02-16 23:58:45 +00:00
Sarah Jamie Lewis 705b6e02c9 Fix overlap in debug settings 2022-02-16 13:49:29 -08:00
Sarah Jamie Lewis 137de57e83 Check WorkInfo is Null
This shouldn't happen in normal use, but can happen in debug builds
2022-02-16 13:44:32 -08:00
Dan Ballard 6859780873 Merge pull request 'cwtch1.6.1-fixes' (#373) from cwtch1.6.1-fixes into trunk
Reviewed-on: cwtch.im/cwtch-ui#373
2022-02-16 18:34:44 +00:00
Sarah Jamie Lewis dab09c6acb Fix #314 - Numpad enter should send message 2022-02-14 12:20:25 -08:00
Sarah Jamie Lewis 7bf2e15009 Formatting 2022-02-14 11:03:28 -08:00
Sarah Jamie Lewis a0f8be2d53 Bump Android Version 2022-02-14 11:02:19 -08:00
Sarah Jamie Lewis 02407c5abe Fix #371
Fix #368
Fix #16
2022-02-14 11:01:58 -08:00
Sarah Jamie Lewis 635e383f65 Merge pull request 'pubspec version bump for android' (#369) from ps160 into trunk
Reviewed-on: cwtch.im/cwtch-ui#369
2022-02-11 18:59:16 +00:00
Dan Ballard 1ec9be3d9a pubspec version bump for android 2022-02-11 13:57:25 -05:00
Sarah Jamie Lewis fd886e7315 Merge pull request 'libcwtch go 1.6.0' (#367) from lcg160 into trunk
Reviewed-on: cwtch.im/cwtch-ui#367
2022-02-10 21:18:14 +00:00
Dan Ballard 387816ea0f libcwtch go 1.6.0 2022-02-10 15:58:09 -05:00
Dan Ballard 3cb6c9d9f4 Merge pull request 'Allow cwtch: prefix' (#366) from cwtch_prefix into trunk
Reviewed-on: cwtch.im/cwtch-ui#366
2022-02-09 22:37:27 +00:00
Sarah Jamie Lewis f1688c5f8f upgrade lcg 2022-02-09 14:30:41 -08:00
Sarah Jamie Lewis d5296d2211 Merge branch 'trunk' into cwtch_prefix 2022-02-09 21:59:31 +00:00
Sarah Jamie Lewis 953971980f Allow cwtch: prefix 2022-02-09 13:58:40 -08:00
Sarah Jamie Lewis 7e59d1a526 Merge pull request 'mac lcg' (#360) from maclcg into trunk
Reviewed-on: cwtch.im/cwtch-ui#360
2022-02-09 02:42:28 +00:00
Dan Ballard 783d666486 mac lcg 2022-02-08 21:41:19 -05:00
Dan Ballard 040ba80480 Merge pull request 'notificationSettings' (#354) from notificationSettings into trunk
Reviewed-on: cwtch.im/cwtch-ui#354
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-02-08 23:21:28 +00:00
Dan Ballard 8ba54469eb comment/format fix 2022-02-08 18:21:06 -05:00
Dan Ballard 706d1da518 new lcg version 2022-02-08 17:50:32 -05:00
Dan Ballard b5511ae723 Merge branch 'trunk' into notificationSettings 2022-02-08 17:11:29 -05:00
Dan Ballard 4c47198977 notification policy work, translations, even for notifications 2022-02-08 17:07:39 -05:00
erinn 9a17852533 Merge pull request 'Add Profile Image Preview to Peer Settings + other UI Profile Image Fixups' (#359) from custom_profile_fixes into trunk
Reviewed-on: cwtch.im/cwtch-ui#359
Reviewed-by: erinn <erinn@openprivacy.ca>
2022-02-08 22:07:32 +00:00
Sarah Jamie Lewis 2a07ba8ed7 revert message row image size change 2022-02-08 14:05:23 -08:00
Sarah Jamie Lewis 2e5ee796fa Add Profile Image Preview to Peer Settings + other UI Profile Image Fixups 2022-02-08 14:05:09 -08:00
Dan Ballard d1d3f23f82 android notification policy/content 2022-02-08 12:35:25 -05:00
Dan Ballard fa6e399aab android work 2022-02-08 11:44:00 -05:00
Dan Ballard ddefcb8ff2 rejig notification policy globally and conversationally 2022-02-08 11:40:15 -05:00
Dan Ballard b382c3d349 desktop support for notification settings 2022-02-08 11:34:05 -05:00
Dan Ballard c550437aa5 settings pane headers, and notification settings options 2022-02-08 11:34:05 -05:00
erinn e6246cf44a Merge pull request 'Update Goldens' (#358) from custom_profile_images into trunk
Reviewed-on: cwtch.im/cwtch-ui#358
Reviewed-by: erinn <erinn@openprivacy.ca>
2022-02-07 23:38:50 +00:00
Sarah Jamie Lewis d71574a831 Merge branch 'trunk' into custom_profile_images 2022-02-07 23:35:43 +00:00
Sarah Jamie Lewis 62bca86c19 Update Goldens 2022-02-07 15:35:01 -08:00
erinn 729ff6811e Merge pull request 'Profile Images' (#355) from custom_profile_images into trunk
Reviewed-on: cwtch.im/cwtch-ui#355
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
Reviewed-by: erinn <erinn@openprivacy.ca>
2022-02-07 23:16:46 +00:00
Sarah Jamie Lewis bf4cfde7df Fixup Context Listen 2022-02-07 15:16:02 -08:00
Sarah Jamie Lewis 403454d6b8 Add Edit Badge 2022-02-07 15:12:36 -08:00
Sarah Jamie Lewis d902ba5cce Rename Constant 2022-02-07 14:59:09 -08:00
Sarah Jamie Lewis 5b5fe586e8 Update Lib Cwtch, Allow Deleting P2P contacts, Formatting 2022-02-07 14:53:33 -08:00
Sarah Jamie Lewis b280765631 Fallback to Default Profile Images when Image Previews are Disabled 2022-02-07 14:26:14 -08:00
Sarah Jamie Lewis 2a2d808b60 Disable image previews when file sharing is disables 2022-02-07 12:23:26 -08:00
Sarah Jamie Lewis d158d7d619 Select Profile Image tooltip + restrict selection only when image previews are enabled 2022-02-07 12:20:54 -08:00
Sarah Jamie Lewis c6192ef736 Factor out showFilePicker into a generic controller 2022-02-07 11:30:17 -08:00
Sarah Jamie Lewis 3d85883f8e Profile Images 2022-02-04 16:57:31 -08:00
erinn e22db92dc1 add gherkin tests (#353)
* not all tests are complete
* language and theme tests fail due to upstream issue with dropdown boxes
* not yet autorun or reported by drone

Co-authored-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
Reviewed-on: cwtch.im/cwtch-ui#353
Co-authored-by: erinn <erinn@openprivacy.ca>
Co-committed-by: erinn <erinn@openprivacy.ca>
2022-02-05 00:37:25 +00:00
Sarah Jamie Lewis dd69afc98b Merge pull request 'profile level unread notifications and in profile other profile unread notifications' (#337) from profileNotify into trunk
Reviewed-on: cwtch.im/cwtch-ui#337
2022-02-04 02:01:35 +00:00
Sarah Jamie Lewis ab9d6929be Merge branch 'trunk' into profileNotify 2022-02-04 02:01:30 +00:00
Sarah Jamie Lewis cd4c778b71 Merge pull request 'windows notifications: switch from desktoats to win_toats' (#350) from wintoasts into trunk
Reviewed-on: cwtch.im/cwtch-ui#350
2022-02-04 02:01:22 +00:00
Dan Ballard 19a202a04c formating, and moving active=true up to be better 2022-02-03 13:58:51 -08:00
Dan Ballard be65417f27 new plugin generated code 2022-02-03 08:43:29 -05:00
Dan Ballard 8a9ee402bf recalculate unrad count incases some not read 2022-02-03 08:43:02 -05:00
Dan Ballard 1a9f0763d7 update goldens with new profile unread widgetry 2022-02-02 19:20:28 -05:00
Dan Ballard a82ade8663 profile level unread notifications and in profile other profile unread notifications 2022-02-02 19:20:28 -05:00
Dan Ballard 715b2c6876 windows notifications: switch from desktoats to win_toats 2022-02-02 12:27:42 -08:00
Dan Ballard ca03ddbc53 Merge pull request 'calculate server progress based on last message, nowtime, and message flow; display progress on group contact and remote server' (#340) from serverProgress into trunk
Reviewed-on: cwtch.im/cwtch-ui#340
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-02-02 18:05:49 +00:00
Dan Ballard 0853832a38 Merge branch 'trunk' into serverProgress 2022-02-02 18:05:40 +00:00
Dan Ballard f818d4f2f8 remove syncProgress from contact and wire contact row to search server's list 2022-01-27 18:41:17 -05:00
Sarah Jamie Lewis 6814515186 Merge pull request 'Fixup Length Display so it counts Bytes not Chars' (#346) from fastercwtch into trunk
Reviewed-on: cwtch.im/cwtch-ui#346
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-01-27 21:11:38 +00:00
Sarah Jamie Lewis d84850af49 Fixup Length Display so it counts Bytes not Chars 2022-01-27 13:02:18 -08:00
Dan Ballard a4ce168aec calculate server progress based on last message, nowtime, and message flow; display progress on group contact and remote server 2022-01-26 20:17:13 -05:00
Sarah Jamie Lewis 2bff77983b Merge pull request 'add window_manager plug in to get desktop active state to gate windows notifications; also add spam prevention to windows notifications' (#338) from winFocus into trunk
Reviewed-on: cwtch.im/cwtch-ui#338
2022-01-26 21:41:37 +00:00
Sarah Jamie Lewis 35ae5773f7 Merge branch 'trunk' into winFocus 2022-01-26 21:41:30 +00:00
Sarah Jamie Lewis 6276b022dc Merge pull request 'Upgrade Cwtch and Display Message Limits' (#339) from fastercwtch into trunk
Reviewed-on: cwtch.im/cwtch-ui#339
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-01-26 20:48:22 +00:00
Sarah Jamie Lewis 5c76628578 Upgrade Cwtch and Display Message Limits 2022-01-26 12:28:50 -08:00
Dan Ballard dc587f95f0 remove prints, add comments 2022-01-26 08:48:35 -08:00
Dan Ballard 04cf1e16c2 pubspec for windows_manager 2022-01-26 08:42:25 -08:00
Dan Ballard d96e251650 Merge branch 'trunk' into winFocus 2022-01-26 16:34:25 +00:00
Dan Ballard c3bc961a47 add window_manager plug in to get desktop active state to gate windows notifications; also add spam prevention to windows notifications 2022-01-26 08:31:07 -08:00
Sarah Jamie Lewis c672574bb2 Merge pull request 'add desktoasts windows notifications' (#336) from winNotify into trunk
Reviewed-on: cwtch.im/cwtch-ui#336
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-01-25 22:56:20 +00:00
Sarah Jamie Lewis 05f3cacdbd Merge branch 'trunk' into winNotify 2022-01-25 22:56:03 +00:00
Sarah Jamie Lewis 2e3d02bbe9 Merge pull request 'bump to tor 0.4.6.9' (#332) from androTor into trunk
Reviewed-on: cwtch.im/cwtch-ui#332
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-01-25 22:55:57 +00:00
Dan Ballard 23b6eddf6a bump windows tor 2022-01-25 10:52:18 -05:00
Dan Ballard c838176e3b add desktoasts windows notifications 2022-01-25 04:03:05 -08:00
Dan Ballard a9d272e414 bump android or to 0.4.6.9 2022-01-21 18:48:34 -05:00
Dan Ballard 598251a624 Merge pull request 'Fix #330 - Multiple file browser windows are opened.' (#331) from sender_size into trunk
Reviewed-on: cwtch.im/cwtch-ui#331
2022-01-21 21:42:41 +00:00
Sarah Jamie Lewis def222a8ab upgrade flutter file picker 2022-01-21 13:40:23 -08:00
Sarah Jamie Lewis 748326e13f Fix #330 - Multiple file browser windows are opened. 2022-01-21 13:19:00 -08:00
erinn 508592f80c Merge pull request 'Only override path for Sender, not any other attributes.' (#329) from sender_size into trunk
Reviewed-on: cwtch.im/cwtch-ui#329
Reviewed-by: erinn <erinn@openprivacy.ca>
2022-01-21 20:12:54 +00:00
Sarah Jamie Lewis d27cc0e64e More notify listeners 2022-01-21 12:09:58 -08:00
Sarah Jamie Lewis e359afbdab notify listeners 2022-01-21 12:08:23 -08:00
Sarah Jamie Lewis 92374ad112 Only override path for Sender, not any other attributes.
For auto-downloads both the sender and receiver set the path before
the UI can set download state. As such we need to be careful about how
we let the sender know about the filekey/path.
2022-01-21 10:16:30 -08:00
erinn 0a3837c8b5 Merge pull request 'Clean up sender side image preview' (#328) from sender_size into trunk
Reviewed-on: cwtch.im/cwtch-ui#328
Reviewed-by: erinn <erinn@openprivacy.ca>
2022-01-20 22:55:20 +00:00
Sarah Jamie Lewis 19777afb79 Merge branch 'trunk' into sender_size 2022-01-20 22:53:03 +00:00
Sarah Jamie Lewis 9931521910 Clean up sender side image preview 2022-01-20 14:52:31 -08:00
erinn 7dcc1c863a Merge pull request 'Enable Sender Side Image Previews' (#327) from sender_size into trunk
Reviewed-on: cwtch.im/cwtch-ui#327
Reviewed-by: erinn <erinn@openprivacy.ca>
2022-01-20 22:44:19 +00:00
Sarah Jamie Lewis 13c1a52442 Only allow path override for senders 2022-01-20 14:42:45 -08:00
Sarah Jamie Lewis 6364ebffc6 Update lcg 2022-01-20 14:32:35 -08:00
Sarah Jamie Lewis d095971cb3 Sender side previews - fixing up PR comments 2022-01-20 14:19:06 -08:00
Sarah Jamie Lewis 797279d6d7 Enable Sender Side Image Previews 2022-01-20 14:00:15 -08:00
Sarah Jamie Lewis d0fecbd545 Merge pull request 'message caching' (#326) from cache into trunk
Reviewed-on: cwtch.im/cwtch-ui#326
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-01-20 21:22:50 +00:00
Dan Ballard ccdd7d0e27 remove byType bools and replace with interface and structs for type safety 2022-01-20 15:58:14 -05:00
Dan Ballard 889d398343 add notifyListen to newMessage in contact; format 2022-01-20 13:37:09 -05:00
Dan Ballard 589bc4c36c new lcg; cleanup 2022-01-20 13:05:11 -05:00
Dan Ballard 793b6e2e1a message cache expansion: stores all messages fetched, indexed by hash and id where possible 2022-01-20 09:24:06 -05:00
Dan Ballard d5cb37ed9c stub of new cache 2022-01-20 09:24:06 -05:00
Dan Ballard e7b9f5bb96 move all classes in model.dart to their own models/X.dart 2022-01-20 09:24:06 -05:00
Dan Ballard 5e8f712a90 Merge pull request 'Fixup Widths on Small Screens' (#323) from torcache into trunk
Reviewed-on: cwtch.im/cwtch-ui#323
2022-01-19 22:23:46 +00:00
Sarah Jamie Lewis 2495814869 Fixup Widths on Small Screens 2022-01-19 14:09:19 -08:00
Dan Ballard 08b9dfed5f Merge pull request 'Formatting' (#322) from torcache into trunk
Reviewed-on: cwtch.im/cwtch-ui#322
2022-01-19 22:01:26 +00:00
Sarah Jamie Lewis 19f73eb075 Formatting 2022-01-19 13:59:04 -08:00
Dan Ballard 0fe6f21a75 Merge pull request 'Update lcg' (#320) from torcache into trunk
Reviewed-on: cwtch.im/cwtch-ui#320
2022-01-19 21:30:25 +00:00
Sarah Jamie Lewis 52e22c085f Update lcg 2022-01-19 13:29:13 -08:00
Sarah Jamie Lewis 47348f3ad7 Merge pull request 'move all classes in model.dart to their own models/X.dart' (#317) from models into trunk
Reviewed-on: cwtch.im/cwtch-ui#317
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-01-19 20:35:26 +00:00
Dan Ballard 706c1fb354 move all classes in model.dart to their own models/X.dart 2022-01-18 18:32:00 -05:00
erinn e99fc45a28 Merge pull request 'Allow Tor Caching + Our Own Linkify' (#316) from torcache into trunk
Reviewed-on: cwtch.im/cwtch-ui#316
Reviewed-by: erinn <erinn@openprivacy.ca>
2022-01-18 23:06:25 +00:00
Sarah Jamie Lewis ca44fd798c Show tooltip for links 2022-01-18 15:03:54 -08:00
Sarah Jamie Lewis 1700306c78 Link to specific commit hashes 2022-01-18 14:48:18 -08:00
Sarah Jamie Lewis da3234e3e4 Formatting 2022-01-18 14:44:19 -08:00
Sarah Jamie Lewis 303b70d751 Fixup displayed link + add linkify to licenses.dart 2022-01-18 14:43:49 -08:00
Sarah Jamie Lewis cd1bf07fba Responding to @errorinn PR Comments 2022-01-18 14:32:45 -08:00
Sarah Jamie Lewis b3f06d6765 Update lcg 2022-01-18 13:47:47 -08:00
Sarah Jamie Lewis c6e64a3a5f Allow Tor Caching + Our Own Linkify 2022-01-18 13:17:52 -08:00
erinn 9d10b9ea8d Merge pull request 'Tor Circuit Info Display for P2P + Many Minor UI Fixups.' (#304) from getinfo into trunk
Reviewed-on: cwtch.im/cwtch-ui#304
Reviewed-by: erinn <erinn@openprivacy.ca>
2022-01-17 23:05:11 +00:00
Sarah Jamie Lewis 7257e2bca0 Update Goldens 2022-01-17 14:55:37 -08:00
Sarah Jamie Lewis 5494cb5de0 Upgrade LibCwtch-Go 2022-01-17 14:52:15 -08:00
Sarah Jamie Lewis d6ecf87255 PR Fixups 2022-01-17 12:29:13 -08:00
Sarah Jamie Lewis ae6f0dd456 Update Translations + notifylisteners 2022-01-17 12:24:48 -08:00
Sarah Jamie Lewis ed671d32bc Padding / Margin Changes + Tor Circuit Info 2022-01-14 14:24:41 -08:00
Sarah Jamie Lewis daa89bf6e7 Add Circuit Info To Peer Settings 2022-01-14 14:24:41 -08:00
erinn 24787adc9c Merge pull request 'android-fix' (#302) from android-fix into trunk
Reviewed-on: cwtch.im/cwtch-ui#302
Reviewed-by: erinn <erinn@openprivacy.ca>
2022-01-13 00:55:20 +00:00
Sarah Jamie Lewis a3e2da8469 update text field golden 2022-01-12 16:05:58 -08:00
Sarah Jamie Lewis 9d3d5b06e5 Upgrade Android Dependencies. Remove references to jCenter 2022-01-12 15:54:07 -08:00
Sarah Jamie Lewis bee3ae6e7b Fix Debug Layout Issue in AddContact 2022-01-12 15:28:51 -08:00
erinn 1bd2195be4 Merge pull request 'Support Custom Tor Configuration' (#300) from custom_tor_config into trunk
Reviewed-on: cwtch.im/cwtch-ui#300
Reviewed-by: erinn <erinn@openprivacy.ca>
2022-01-12 23:19:15 +00:00
Sarah Jamie Lewis 26f32a0790 Update Translations + Error Reporting 2022-01-12 15:15:58 -08:00
Sarah Jamie Lewis 958be3e8f7 Upgade lcg 2022-01-12 14:43:26 -08:00
Sarah Jamie Lewis 4cdbb04243 Update Translations 2022-01-12 14:43:26 -08:00
Sarah Jamie Lewis 92422de98e Support Custom Tor Configuration
Fixes: #19
2022-01-12 14:43:26 -08:00
erinn 5e4c190e41 Merge pull request 'Fix #295 - P2P Avatar should not be clickable.' (#296) from fix_295 into trunk
Reviewed-on: cwtch.im/cwtch-ui#296
Reviewed-by: erinn <erinn@openprivacy.ca>
2022-01-10 20:49:36 +00:00
Sarah Jamie Lewis 659e89d626 Formatting PR 2022-01-10 12:28:12 -08:00
Sarah Jamie Lewis 306a9c4de5 Fix #295 - Clicking on an Avatar in P2P Chat should not result in an action 2022-01-10 12:27:56 -08:00
231 changed files with 15664 additions and 4011 deletions

View File

@ -8,7 +8,7 @@ clone:
steps:
- name: clone
image: cirrusci/flutter:2.8.0
image: cirrusci/flutter:3.3.8
environment:
buildbot_key_b64:
from_secret: buildbot_key_b64
@ -24,7 +24,7 @@ steps:
- git checkout $DRONE_COMMIT
- name: fetch
image: cirrusci/flutter:2.8.0
image: cirrusci/flutter:3.3.8
volumes:
- name: deps
path: /root/.pub-cache
@ -47,7 +47,7 @@ steps:
# #Todo: fix all the lint errors and add `-set_exit_status` above to enforce linting
- name: build-linux
image: openpriv/flutter-desktop:linux-fstable-2.8.0
image: openpriv/flutter-desktop:linux-fstable-3.3.9
volumes:
- name: deps
path: /root/.pub-cache
@ -60,8 +60,20 @@ steps:
- tar -czf cwtch-`cat ../VERSION`.tar.gz cwtch
- rm -r cwtch
- name: linux-ui-tests
image: openpriv/flutter-desktop:linux-fstable-3.3.9
volumes:
- name: deps
path: /root/.pub-cache
commands:
- # todo: add xvfb to openpriv/flutter-desktop:linux-fstable-3.7
- sudo apt update
- sudo apt-get install -y xvfb
- ./fetch-tor.sh
- ./run-tests-headless.sh 02_save_load
- name: test-build-android
image: cirrusci/flutter:2.8.0
image: cirrusci/flutter:3.3.8
when:
event: pull_request
volumes:
@ -71,7 +83,7 @@ steps:
- flutter build apk --debug
- name: build-android
image: cirrusci/flutter:2.8.0
image: cirrusci/flutter:3.3.8
when:
event: push
environment:
@ -95,7 +107,7 @@ steps:
#- cp build/app/outputs/flutter-apk/app-debug.apk deploy/android
- name: widget-tests
image: cirrusci/flutter:2.8.0
image: cirrusci/flutter:3.3.8
volumes:
- name: deps
path: /root/.pub-cache
@ -177,7 +189,7 @@ clone:
steps:
- name: clone
image: openpriv/flutter-desktop:windows-sdk30-fstable-2.8.1
image: openpriv/flutter-desktop:windows-sdk30-fstable-3.3.8
environment:
buildbot_key_b64:
from_secret: buildbot_key_b64
@ -195,16 +207,15 @@ steps:
- git checkout $Env:DRONE_COMMIT
- name: fetch
image: openpriv/flutter-desktop:windows-sdk30-fstable-2.8.1
image: openpriv/flutter-desktop:windows-sdk30-fstable-3.3.8
commands:
- powershell -command "Invoke-WebRequest -Uri https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-win64-0.4.6.5.zip -OutFile tor.zip"
- powershell -command "if ((Get-FileHash tor.zip -Algorithm sha512).Hash -ne '7917561a7a063440a1ddfa9cb544ab9ffd09de84cea3dd66e3cc9cd349dd9f85b74a522ec390d7a974bc19b424c4d53af60e57bbc47e763d13cab6a203c4592f' ) { Write-Error 'tor.zip sha512sum mismatch' }"
- git describe --tags --abbrev=1 > VERSION
- powershell -command "Get-Date -Format 'yyyy-MM-dd-HH-mm'" > BUILDDATE
- .\fetch-tor-win.ps1
- .\fetch-libcwtch-go.ps1
- name: build-windows
image: openpriv/flutter-desktop:windows-sdk30-fstable-2.8.1
image: openpriv/flutter-desktop:windows-sdk30-fstable-3.3.8
commands:
- flutter pub get
- $Env:version += type .\VERSION
@ -230,7 +241,7 @@ steps:
status: [ success ]
environment:
pfx:
from_secret: pfx
from_secret: pfx2022_b64
pfx_pass:
from_secret: pfx_pass
commands:
@ -246,6 +257,8 @@ steps:
- echo $Env:pfx > codesign.pfx.b64
- certutil -decode codesign.pfx.b64 codesign.pfx
- signtool sign /v /fd sha256 /a /f codesign.pfx /p $Env:pfx_pass /tr http://timestamp.digicert.com $Env:releasedir\cwtch.exe
- signtool sign /v /fd sha256 /a /f codesign.pfx /p $Env:pfx_pass /tr http://timestamp.digicert.com $Env:releasedir\libCwtch.dll
- signtool sign /v /fd sha256 /a /f codesign.pfx /p $Env:pfx_pass /tr http://timestamp.digicert.com $Env:releasedir\flutter_windows.dll
- copy windows\runner\resources\knot_128.ico $Env:releasedir\cwtch.ico
- makensis windows\nsis\cwtch-installer.nsi
- move windows\nsis\cwtch-installer.exe cwtch-installer.exe
@ -261,7 +274,7 @@ steps:
- move *.sha512 deploy\$Env:builddir
- name: deploy-windows
image: openpriv/flutter-desktop:windows-sdk30-fstable-2.8.1
image: openpriv/flutter-desktop:windows-sdk30-fstable-3.3.8
when:
event: push
status: [ success ]
@ -361,4 +374,4 @@ trigger:
branch: trunk
event:
- push
- pull_request
- pull_request

21
.gitignore vendored
View File

@ -40,10 +40,31 @@ app.*.symbols
# Obfuscation related
app.*.map.json
# Tor
*data-dir*
# Test Artificats
*.log
flutter_gherkin
run-tests.env
report.json
package.
# Compiled Libs
linux/tor
linux/libCwtch.so
android/cwtch/cwtch.aar
android/app/src/main/jniLibs/*/libtor.so
*.dylib
integration_test/gherkin_suite_test.g.dart
integration_test/gherkin_suite_test.dart
integration_test/gherkin/
integration_test/CustomSteps.md
analysis_options.yaml
integration_test/env/default/tor
linux/Tor
linux/tor.tar.gz
coverage
test/failures
.gradle

View File

@ -1 +1 @@
2022-01-07-16-29-v1.5.4
2022-12-12-17-58-v1.10.1-3-g3d0a3a5

View File

@ -1 +1 @@
2022-01-07-21-30-v1.5.4
2022-12-12-22-59-v1.10.1-3-g3d0a3a5

View File

@ -65,7 +65,7 @@ To build a release version and load normal profiles, use `build-release.sh X` in
### Building on MacOS
- Cocaopods is required, you may need to `gem install cocaopods -v 1.9.3`
- copy `libCwtch.dylib` into the root folder, or run `fetch-libcwtch-go-macos.sh` to download it
- copy `libCwtch.x64.dylib` and `libCwtch.arm/dylib` into the root folder, or run `fetch-libcwtch-go-macos.sh` to download it
- run `fetch-tor-macos.sh` to fetch Tor or Download and install Tor Browser and `cp -r /Applications/Tor\ Browser.app/Contents/MacOS/Tor ./macos/`
- `flutter build macos`
- optional: launch cwtch-ui release build with `./build/macos/Build/Products/Release/Cwtch.app/Contents/MacOS/Cwtch`

View File

@ -33,7 +33,7 @@ if (keystorePropertiesFile.exists()) {
}
android {
compileSdkVersion 30
compileSdkVersion 31
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
@ -48,9 +48,11 @@ android {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "im.cwtch.flwtch"
minSdkVersion 16
targetSdkVersion 30
targetSdkVersion 31
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
signingConfigs {
@ -89,16 +91,27 @@ dependencies {
implementation project(':cwtch')
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2"
implementation "com.airbnb.android:lottie:3.5.0"
implementation "com.airbnb.android:lottie:4.2.1"
implementation "androidx.localbroadcastmanager:localbroadcastmanager:1.0.0"
implementation "com.android.support.constraint:constraint-layout:2.0.4"
// Test Dependencies
testImplementation 'junit:junit:4.12'
// https://developer.android.com/jetpack/androidx/releases/test/#1.2.0
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
// WorkManager
// (Java only)
//implementation("androidx.work:work-runtime:$work_version")
// Kotlin + coroutines
implementation("androidx.work:work-runtime-ktx:2.5.0")
// 2022.06: upgraded from 2.5 to 2.7 for android 12
// err: "requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent"
// as per https://github.com/flutter/flutter/issues/93609
implementation 'androidx.work:work-runtime-ktx:2.7.0'
// optional - RxJava2 support
//implementation("androidx.work:work-rxjava2:$work_version")

View File

@ -0,0 +1,12 @@
package im.cwtch.flwtch;
import androidx.test.rule.ActivityTestRule;
import dev.flutter.plugins.integration_test.FlutterTestRunner;
import org.junit.Rule;
import org.junit.runner.RunWith;
@RunWith(FlutterTestRunner.class)
public class MainActivityTest {
@Rule
public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class, true, false);
}

View File

@ -6,7 +6,7 @@
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
android:name="${applicationName}"
android:label="Cwtch"
android:extractNativeLibs="true"
android:icon="@mipmap/knott">
@ -16,7 +16,8 @@
android:theme="@style/NormalTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
android:windowSoftInputMode="adjustResize"
android:exported="true">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
@ -46,7 +47,15 @@
<!--Needed to run in background (lol)-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!--Meeded to check if activity is foregrounded or if messages from the service should be queued-->
<!-- Ability to ask user to exempt app from power management (which can kill it more frequently especially on some devices.
Allows app to use ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS -->
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<!-- TODO when we support sdk 31
<uses-permission-sdk-23 android:name="android.permission.HIDE_OVERLAY_WINDOWS" />
-->
<!--Needed to check if activity is foregrounded or if messages from the service should be queued-->
<uses-permission android:name="android.permission.GET_TASKS" />
<queries>

View File

@ -0,0 +1,25 @@
// Generated file.
//
// If you wish to remove Flutter's multidex support, delete this entire file.
//
// Modifications to this file should be done in a copy under a different name
// as this file may be regenerated.
package io.flutter.app;
import android.app.Application;
import android.content.Context;
import androidx.annotation.CallSuper;
import androidx.multidex.MultiDex;
/**
* Extension of {@link android.app.Application}, adding multidex support.
*/
public class FlutterMultiDexApplication extends Application {
@Override
@CallSuper
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}

View File

@ -1,24 +1,27 @@
package im.cwtch.flwtch
import android.app.*
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.work.*
import androidx.work.CoroutineWorker
import androidx.work.ForegroundInfo
import androidx.work.WorkerParameters
import cwtch.Cwtch
import io.flutter.FlutterInjector
import org.json.JSONObject
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import android.net.Uri
class FlwtchWorker(context: Context, parameters: WorkerParameters) :
CoroutineWorker(context, parameters) {
@ -29,13 +32,26 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
private var notificationID: MutableMap<String, Int> = mutableMapOf()
private var notificationIDnext: Int = 1
private var notificationSimple: String? = null
private var notificationConversationInfo: String? = null
private val TAG: String = "FlwtchWorker.kt"
override suspend fun doWork(): Result {
// Hack to uncomment and deploy if your device has zombie workers you need to kill
// We need a proper solution but this will clear those out for now
/*if (notificationSimple == null) {
Log.e("FlwtchWorker", "doWork found notificationSimple is null, app has not started, this is a stale thread, terminating")
return Result.failure()
}*/
val method = inputData.getString(KEY_METHOD)
?: return Result.failure()
val args = inputData.getString(KEY_ARGS)
?: return Result.failure()
// Mark the Worker as important
val progress = "Cwtch is keeping Tor running in the background"//todo:translate
val progress = "Cwtch is keeping Tor running in the background" // TODO: translate
setForeground(createForegroundInfo(progress))
return handleCwtch(method, args)
}
@ -49,334 +65,198 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
}
private fun handleCwtch(method: String, args: String): Result {
val a = JSONObject(args)
when (method) {
"Start" -> {
Log.i("FlwtchWorker.kt", "handleAppInfo Start")
val appDir = (a.get("appDir") as? String) ?: ""
val torPath = (a.get("torPath") as? String) ?: "tor"
Log.i("FlwtchWorker.kt", "appDir: '$appDir' torPath: '$torPath'")
if (method != "Start") {
if (Cwtch.started() != 1.toLong()) {
Log.e(TAG, "libCwtch-go reports it is not initialized yet")
return Result.failure()
}
}
if (Cwtch.startCwtch(appDir, torPath) != 0.toLong()) return Result.failure()
val a = JSONObject(args)
when (method) {
"Start" -> {
Log.i(TAG, "handleAppInfo Start")
val appDir = (a.get("appDir") as? String) ?: ""
val torPath = (a.get("torPath") as? String) ?: "tor"
Log.i(TAG, "appDir: '$appDir' torPath: '$torPath'")
Log.i("FlwtchWorker.kt", "startCwtch success, starting coroutine AppbusEvent loop...")
val downloadIDs = mutableMapOf<String, Int>()
while(true) {
val evt = MainActivity.AppbusEvent(Cwtch.getAppBusEvent())
if (evt.EventType == "NewMessageFromPeer" || evt.EventType == "NewMessageFromGroup") {
val data = JSONObject(evt.Data)
val handle = if (evt.EventType == "NewMessageFromPeer") data.getString("RemotePeer") else data.getString("GroupID");
if (data["RemotePeer"] != data["ProfileOnion"]) {
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createMessageNotificationChannel(handle, handle)
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
if (Cwtch.startCwtch(appDir, torPath) != 0.toLong()) return Result.failure()
Log.i(TAG, "startCwtch success, starting coroutine AppbusEvent loop...")
val downloadIDs = mutableMapOf<String, Int>()
var flags = PendingIntent.FLAG_UPDATE_CURRENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
flags = flags or PendingIntent.FLAG_IMMUTABLE
}
while (true) {
try {
val evt = MainActivity.AppbusEvent(Cwtch.getAppBusEvent())
// TODO replace this notification block with the NixNotification manager in dart as it has access to contact names and also needs less working around
if (evt.EventType == "NewMessageFromPeer" || evt.EventType == "NewMessageFromGroup") {
val data = JSONObject(evt.Data)
val handle = data.getString("RemotePeer");
val conversationId = data.getInt("ConversationID").toString();
val notificationChannel = if (evt.EventType == "NewMessageFromPeer") handle else conversationId
if (data["RemotePeer"] != data["ProfileOnion"]) {
val notification = data["notification"]
if (notification == "SimpleEvent") {
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createMessageNotificationChannel("Cwtch", "Cwtch")
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
}
val clickIntent = Intent(applicationContext, MainActivity::class.java).also { intent ->
intent.action = Intent.ACTION_RUN
intent.putExtra("EventType", "NotificationClicked")
}
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
.setContentTitle("Cwtch")
.setContentText(notificationSimple ?: "New Message")
.setSmallIcon(R.mipmap.knott_transparent)
.setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, flags))
.setAutoCancel(true)
.build()
notificationManager.notify(getNotificationID("Cwtch", "Cwtch"), newNotification)
} else if (notification == "ContactInfo") {
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createMessageNotificationChannel(notificationChannel, notificationChannel)
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
}
val loader = FlutterInjector.instance().flutterLoader()
Log.i(TAG, "notification for " + evt.EventType + " " + handle + " " + conversationId + " " + channelId)
Log.i(TAG, data.toString());
val key = loader.getLookupKeyForAsset(data.getString("picture"))//"assets/profiles/001-centaur.png")
val fh = applicationContext.assets.open(key)
val clickIntent = Intent(applicationContext, MainActivity::class.java).also { intent ->
intent.action = Intent.ACTION_RUN
intent.putExtra("EventType", "NotificationClicked")
intent.putExtra("ProfileOnion", data.getString("ProfileOnion"))
intent.putExtra("Handle", handle)
}
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
.setContentTitle(data.getString("Nick"))
.setContentText((notificationConversationInfo
?: "New Message From %1").replace("%1", data.getString("Nick")))
.setLargeIcon(BitmapFactory.decodeStream(fh))
.setSmallIcon(R.mipmap.knott_transparent)
.setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, flags))
.setAutoCancel(true)
.build()
notificationManager.notify(getNotificationID(data.getString("ProfileOnion"), channelId), newNotification)
}
val loader = FlutterInjector.instance().flutterLoader()
val key = loader.getLookupKeyForAsset("assets/" + data.getString("Picture"))//"assets/profiles/001-centaur.png")
val fh = applicationContext.assets.open(key)
val clickIntent = Intent(applicationContext, MainActivity::class.java).also { intent ->
intent.action = Intent.ACTION_RUN
intent.putExtra("EventType", "NotificationClicked")
intent.putExtra("ProfileOnion", data.getString("ProfileOnion"))
intent.putExtra("Handle", handle)
}
} else if (evt.EventType == "FileDownloadProgressUpdate") {
try {
val data = JSONObject(evt.Data);
val fileKey = data.getString("FileKey");
val title = data.getString("NameSuggestion");
val progress = data.getString("Progress").toInt();
val progressMax = data.getString("FileSizeInChunks").toInt();
if (!downloadIDs.containsKey(fileKey)) {
downloadIDs.put(fileKey, downloadIDs.count());
}
var dlID = downloadIDs.get(fileKey);
if (dlID == null) {
dlID = 0;
}
if (progress >= 0) {
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createDownloadNotificationChannel(fileKey, fileKey)
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
};
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
.setOngoing(true)
.setContentTitle("Downloading")//todo: translate
.setContentText(title)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setProgress(progressMax, progress, false)
.setSound(null)
//.setSilent(true)
.build();
notificationManager.notify(dlID, newNotification);
}
} catch (e: Exception) {
Log.d("FlwtchWorker->FileDownloadProgressUpdate", e.toString() + " :: " + e.getStackTrace());
}
} else if (evt.EventType == "FileDownloaded") {
Log.d(TAG, "file downloaded!");
val data = JSONObject(evt.Data);
val tempFile = data.getString("TempFile");
val fileKey = data.getString("FileKey");
if (tempFile != "" && tempFile != data.getString("FilePath")) {
val filePath = data.getString("FilePath");
Log.i(TAG, "moving " + tempFile + " to " + filePath);
val sourcePath = Paths.get(tempFile);
val targetUri = Uri.parse(filePath);
val os = this.applicationContext.getContentResolver().openOutputStream(targetUri);
val bytesWritten = Files.copy(sourcePath, os);
Log.d("TAG", "copied " + bytesWritten.toString() + " bytes");
if (bytesWritten != 0L) {
os?.flush();
os?.close();
Files.delete(sourcePath);
}
}
if (downloadIDs.containsKey(fileKey)) {
notificationManager.cancel(downloadIDs.get(fileKey) ?: 0);
}
}
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
.setContentTitle(data.getString("Nick"))
.setContentText("New message")//todo: translate
.setLargeIcon(BitmapFactory.decodeStream(fh))
.setSmallIcon(R.mipmap.knott_transparent)
.setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setAutoCancel(true)
.build()
notificationManager.notify(getNotificationID(data.getString("ProfileOnion"), handle), newNotification)
}
} else if (evt.EventType == "FileDownloadProgressUpdate") {
try {
val data = JSONObject(evt.Data);
val fileKey = data.getString("FileKey");
val title = data.getString("NameSuggestion");
val progress = data.getString("Progress").toInt();
val progressMax = data.getString("FileSizeInChunks").toInt();
if (!downloadIDs.containsKey(fileKey)) {
downloadIDs.put(fileKey, downloadIDs.count());
Intent().also { intent ->
intent.action = "im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS"
intent.putExtra("EventType", evt.EventType)
intent.putExtra("Data", evt.Data)
intent.putExtra("EventID", evt.EventID)
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)
}
var dlID = downloadIDs.get(fileKey);
if (dlID == null) {
dlID = 0;
}
if (progress >= 0) {
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createDownloadNotificationChannel(fileKey, fileKey)
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
};
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
.setOngoing(true)
.setContentTitle("Downloading")//todo: translate
.setContentText(title)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setProgress(progressMax, progress, false)
.setSound(null)
//.setSilent(true)
.build();
notificationManager.notify(dlID, newNotification);
if (evt.EventType == "Shutdown") {
Log.i(TAG, "processing shutdown event, exiting FlwtchWorker/Start()...");
return Result.success()
}
} catch (e: Exception) {
Log.d("FlwtchWorker->FileDownloadProgressUpdate", e.toString() + " :: " + e.getStackTrace());
}
} else if (evt.EventType == "FileDownloaded") {
Log.d("FlwtchWorker", "file downloaded!");
val data = JSONObject(evt.Data);
val tempFile = data.getString("TempFile");
val fileKey = data.getString("FileKey");
if (tempFile != "" && tempFile != data.getString("FilePath")) {
val filePath = data.getString("FilePath");
Log.i("FlwtchWorker", "moving "+tempFile+" to "+filePath);
val sourcePath = Paths.get(tempFile);
val targetUri = Uri.parse(filePath);
val os = this.applicationContext.getContentResolver().openOutputStream(targetUri);
val bytesWritten = Files.copy(sourcePath, os);
Log.d("FlwtchWorker", "copied " + bytesWritten.toString() + " bytes");
if (bytesWritten != 0L) {
os?.flush();
os?.close();
Files.delete(sourcePath);
}
}
if (downloadIDs.containsKey(fileKey)) {
notificationManager.cancel(downloadIDs.get(fileKey)?:0);
Log.e(TAG, "Error in handleCwtch: " + e.toString() + " :: " + e.getStackTrace());
}
}
Intent().also { intent ->
intent.action = "im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS"
intent.putExtra("EventType", evt.EventType)
intent.putExtra("Data", evt.Data)
intent.putExtra("EventID", evt.EventID)
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)
}
}
// Event passing translations from Flutter to Kotlin worker scope so the worker can use them
"L10nInit" -> {
notificationSimple = (a.get("notificationSimple") as? String) ?: "New Message"
notificationConversationInfo = (a.get("notificationConversationInfo") as? String)
?: "New Message From "
}
else -> {
Log.i(TAG, "unknown command: " + method);
return Result.failure()
}
}
"ReconnectCwtchForeground" -> {
Cwtch.reconnectCwtchForeground()
}
"CreateProfile" -> {
val nick = (a.get("nick") as? String) ?: ""
val pass = (a.get("pass") as? String) ?: ""
Cwtch.createProfile(nick, pass)
}
"LoadProfiles" -> {
val pass = (a.get("pass") as? String) ?: ""
Cwtch.loadProfiles(pass)
}
"ChangePassword" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val pass = (a.get("OldPass") as? String) ?: ""
val passNew = (a.get("NewPass") as? String) ?: ""
val passNew2 = (a.get("NewPassAgain") as? String) ?: ""
Cwtch.changePassword(profile, pass, passNew, passNew2)
}
"GetMessage" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val conversation = a.getInt("conversation").toLong()
val indexI = a.getInt("index").toLong()
Log.d("FlwtchWorker", "Cwtch GetMessage " + profile + " " + conversation.toString() + " " + indexI.toString())
return Result.success(Data.Builder().putString("result", Cwtch.getMessage(profile, conversation, indexI)).build())
}
"GetMessageByID" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val conversation = a.getInt("conversation").toLong()
val id = a.getInt("id").toLong()
return Result.success(Data.Builder().putString("result", Cwtch.getMessageByID(profile, conversation, id)).build())
}
"GetMessageByContentHash" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val conversation = a.getInt("conversation").toLong()
val contentHash = (a.get("contentHash") as? String) ?: ""
return Result.success(Data.Builder().putString("result", Cwtch.getMessagesByContentHash(profile, conversation, contentHash)).build())
}
"UpdateMessageAttribute" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val conversation = a.getInt("conversation").toLong()
val channel = a.getInt("chanenl").toLong()
val midx = a.getInt("midx").toLong()
val key = (a.get("key") as? String) ?: ""
val value = (a.get("value") as? String) ?: ""
Cwtch.setMessageAttribute(profile, conversation, channel, midx, key, value)
}
"AcceptConversation" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val conversation = a.getInt("conversation").toLong()
Cwtch.acceptConversation(profile, conversation)
}
"BlockContact" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val conversation = a.getInt("conversation").toLong()
Cwtch.blockContact(profile, conversation)
}
"UnblockContact" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val conversation = a.getInt("conversation").toLong()
Cwtch.unblockContact(profile, conversation)
}
"SendMessage" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val conversation = a.getInt("conversation").toLong()
val message = (a.get("message") as? String) ?: ""
Cwtch.sendMessage(profile, conversation, message)
}
"SendInvitation" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val conversation = a.getInt("conversation").toLong()
val target = a.getInt("target").toLong()
Cwtch.sendInvitation(profile, conversation, target)
}
"ShareFile" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val conversation = a.getInt("conversation").toLong()
val filepath = (a.get("filepath") as? String) ?: ""
Cwtch.shareFile(profile, conversation, filepath)
}
"DownloadFile" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val conversation = a.getInt("conversation").toLong()
val filepath = (a.get("filepath") as? String) ?: ""
val manifestpath = (a.get("manifestpath") as? String) ?: ""
val filekey = (a.get("filekey") as? String) ?: ""
// FIXME: Prevent spurious calls by Intent
if (profile != "") {
Cwtch.downloadFile(profile, conversation, filepath, manifestpath, filekey)
}
}
"CheckDownloadStatus" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val fileKey = (a.get("fileKey") as? String) ?: ""
Cwtch.checkDownloadStatus(profile, fileKey)
}
"VerifyOrResumeDownload" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val conversation = a.getInt("conversation").toLong()
val fileKey = (a.get("fileKey") as? String) ?: ""
Cwtch.verifyOrResumeDownload(profile, conversation, fileKey)
}
"SendProfileEvent" -> {
val onion = (a.get("onion") as? String) ?: ""
val jsonEvent = (a.get("jsonEvent") as? String) ?: ""
Cwtch.sendProfileEvent(onion, jsonEvent)
}
"SendAppEvent" -> {
val jsonEvent = (a.get("jsonEvent") as? String) ?: ""
Cwtch.sendAppEvent(jsonEvent)
}
"ResetTor" -> {
Cwtch.resetTor()
}
"ImportBundle" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val bundle = (a.get("bundle") as? String) ?: ""
Cwtch.importBundle(profile, bundle)
}
"CreateGroup" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val server = (a.get("server") as? String) ?: ""
val groupName = (a.get("groupName") as? String) ?: ""
Cwtch.createGroup(profile, server, groupName)
}
"DeleteProfile" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val pass = (a.get("pass") as? String) ?: ""
Cwtch.deleteProfile(profile, pass)
}
"ArchiveConversation" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val conversation = a.getInt("conversation").toLong()
Cwtch.archiveConversation(profile, conversation)
}
"DeleteConversation" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val conversation = a.getInt("conversation").toLong()
Cwtch.deleteContact(profile, conversation)
}
"SetProfileAttribute" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val key = (a.get("Key") as? String) ?: ""
val v = (a.get("Val") as? String) ?: ""
Cwtch.setProfileAttribute(profile, key, v)
}
"SetConversationAttribute" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val conversation = a.getInt("conversation").toLong()
val key = (a.get("Key") as? String) ?: ""
val v = (a.get("Val") as? String) ?: ""
Cwtch.setConversationAttribute(profile, conversation, key, v)
}
"Shutdown" -> {
Cwtch.shutdownCwtch();
return Result.success()
}
"LoadServers" -> {
val password = (a.get("Password") as? String) ?: ""
Cwtch.loadServers(password)
}
"CreateServer" -> {
val password = (a.get("Password") as? String) ?: ""
val desc = (a.get("Description") as? String) ?: ""
val autostart = (a.get("Autostart") as? Boolean) ?: false
Cwtch.createServer(password, desc, autostart)
}
"DeleteServer" -> {
val serverOnion = (a.get("ServerOnion") as? String) ?: ""
val password = (a.get("Password") as? String) ?: ""
Cwtch.deleteServer(serverOnion, password)
}
"LaunchServers" -> {
Cwtch.launchServers()
}
"LaunchServer" -> {
val serverOnion = (a.get("ServerOnion") as? String) ?: ""
Cwtch.launchServer(serverOnion)
}
"StopServer" -> {
val serverOnion = (a.get("ServerOnion") as? String) ?: ""
Cwtch.stopServer(serverOnion)
}
"StopServers" -> {
Cwtch.stopServers()
}
"DestroyServers" -> {
Cwtch.destroyServers()
}
"SetServerAttribute" -> {
val serverOnion = (a.get("ServerOnion") as? String) ?: ""
val key = (a.get("Key") as? String) ?: ""
val v = (a.get("Val") as? String) ?: ""
Cwtch.setServerAttribute(serverOnion, key, v)
}
else -> {
Log.i("FlwtchWorker", "unknown command: " + method);
return Result.failure()
}
}
return Result.success()
return Result.success()
}
// Creates an instance of ForegroundInfo which can be used to update the
// ongoing notification.
private fun createForegroundInfo(progress: String): ForegroundInfo {
val id = "flwtch"
val title = "Flwtch"
val cancel = "Shut down"//todo: translate
val title = "Flwtch" // TODO: change
val cancel = "Shut down" // TODO: translate
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createForegroundNotificationChannel(id, id)
@ -390,6 +270,10 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
intent.action = Intent.ACTION_RUN
intent.putExtra("EventType", "ShutdownClicked")
}
var flags = PendingIntent.FLAG_UPDATE_CURRENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
flags = flags or PendingIntent.FLAG_IMMUTABLE
}
val notification = NotificationCompat.Builder(applicationContext, channelId)
.setContentTitle(title)
@ -399,7 +283,7 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
.setOngoing(true)
// Add the cancel action to the notification which can
// be used to cancel the worker
.addAction(android.R.drawable.ic_delete, cancel, PendingIntent.getActivity(applicationContext, 2, cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.addAction(android.R.drawable.ic_delete, cancel, PendingIntent.getActivity(applicationContext, 2, cancelIntent, flags))
.build()
return ForegroundInfo(101, notification)

View File

@ -1,46 +1,48 @@
package im.cwtch.flwtch
import SplashView
import android.annotation.TargetApi
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.annotation.NonNull
import android.content.pm.PackageManager
import android.net.Uri
import android.os.PowerManager
import android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
import android.util.Log
import android.view.Window
import android.view.WindowManager
import androidx.annotation.NonNull
import androidx.lifecycle.Observer
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.work.*
import io.flutter.embedding.android.SplashScreen
import cwtch.Cwtch
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.SplashScreen
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.ErrorLogResult
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.Result
import org.json.JSONObject
import java.util.concurrent.TimeUnit
import java.io.File
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import java.util.concurrent.TimeUnit
import android.net.Uri
import android.provider.DocumentsContract
import android.content.ContentUris
import android.os.Build
import android.os.Environment
import android.database.Cursor
import android.provider.MediaStore
class MainActivity: FlutterActivity() {
override fun provideSplashScreen(): SplashScreen? = SplashView()
// Channel to get app info
private val CHANNEL_APP_INFO = "test.flutter.dev/applicationInfo"
private val CALL_APP_INFO = "getNativeLibDir"
private val ANDROID_SETTINGS_CHANNEL_NAME = "androidSettings"
private val ANDROID_SETTINGS_CHANGE_NAME= "androidSettingsChanged"
private var andoidSettingsChangeChannel: MethodChannel? = null
private val CALL_ASK_BATTERY_EXEMPTION = "requestBatteryExemption"
private val CALL_IS_BATTERY_EXEMPT = "isBatteryExempt"
// Channel to get cwtch api calls on
private val CHANNEL_CWTCH = "cwtch"
@ -52,6 +54,7 @@ class MainActivity: FlutterActivity() {
private val CHANNEL_NOTIF_CLICK = "im.cwtch.flwtch/notificationClickHandler"
private val CHANNEL_SHUTDOWN_CLICK = "im.cwtch.flwtch/shutdownClickHandler"
private val TAG: String = "MainActivity.kt"
// WorkManager tag applied to all Start() infinite coroutines
val WORKER_TAG = "cwtchEventBusWorker"
@ -62,11 +65,27 @@ class MainActivity: FlutterActivity() {
// "Download to..." prompt extra arguments
private val FILEPICKER_REQUEST_CODE = 234
private val PREVIEW_EXPORT_REQUEST_CODE = 235
private val PROFILE_EXPORT_REQUEST_CODE = 236
private val REQUEST_DOZE_WHITELISTING_CODE:Int = 9
private var dlToProfile = ""
private var dlToHandle = ""
private var dlToHandle = 0
private var dlToFileKey = ""
private var exportFromPath = ""
override fun onCreate(savedInstanceState: android.os.Bundle?) {
super.onCreate(savedInstanceState)
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
// Todo: when we support SDK 31
// hideOverlay()
}
/*
@TargetApi(31)
fun hideOverlay() {
window.setHideOverlayWindows(true);
}
*/
// handles clicks received from outside the app (ie, notifications)
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
@ -93,34 +112,61 @@ class MainActivity: FlutterActivity() {
override fun onActivityResult(requestCode: Int, result: Int, intent: Intent?) {
super.onActivityResult(requestCode, result, intent);
// has null intent and data
if (requestCode == REQUEST_DOZE_WHITELISTING_CODE) {
// 0 == "battery optimized" (still)
// -1 == "no battery optimization" (exempt!)
andoidSettingsChangeChannel!!.invokeMethod("powerExemptionChange", result == -1)
return;
}
if (intent == null || intent!!.getData() == null) {
Log.i("MainActivity:onActivityResult", "user canceled activity");
Log.i(TAG, "user canceled activity");
return;
}
if (requestCode == FILEPICKER_REQUEST_CODE) {
val filePath = intent!!.getData().toString();
val manifestPath = StringBuilder().append(this.applicationContext.cacheDir).append("/").append(this.dlToFileKey).toString();
Log.d("MainActivity:FILEPICKER_REQUEST_CODE", "DownloadableFileCreated");
handleCwtch(MethodCall("DownloadFile", mapOf(
"ProfileOnion" to this.dlToProfile,
"handle" to this.dlToHandle,
"conversation" to this.dlToHandle.toInt(),
"filepath" to filePath,
"manifestpath" to manifestPath,
"filekey" to this.dlToFileKey
)), ErrorLogResult(""));//placeholder; this Result is never actually invoked
} else if (requestCode == PREVIEW_EXPORT_REQUEST_CODE) {
val targetPath = intent!!.getData().toString()
var srcFile = File(this.exportFromPath)
Log.i("MainActivity:PREVIEW_EXPORT", "exporting previewed file")
val sourcePath = Paths.get(this.exportFromPath);
val targetUri = Uri.parse(targetPath);
val os = this.applicationContext.getContentResolver().openOutputStream(targetUri);
val bytesWritten = Files.copy(sourcePath, os);
Log.d("MainActivity:PREVIEW_EXPORT", "copied " + bytesWritten.toString() + " bytes");
if (bytesWritten != 0L) {
os?.flush();
os?.close();
//Files.delete(sourcePath);
try {
val sourcePath = Paths.get(this.exportFromPath);
val targetUri = intent!!.getData();
val os = this.applicationContext.getContentResolver().openOutputStream(targetUri!!);
val bytesWritten = Files.copy(sourcePath, os);
Log.d("MainActivity:PREVIEW_EXPORT", "copied " + bytesWritten.toString() + " bytes");
if (bytesWritten != 0L) {
os?.flush();
os?.close();
//Files.delete(sourcePath);
}
} catch (e: Exception) {
Log.d("MainActivity:PREVIEW_EXPORT FAILED", e.toString());
}
} else if (requestCode == PROFILE_EXPORT_REQUEST_CODE ) {
val srcFile = StringBuilder().append(this.applicationContext.cacheDir).append("/").append(this.exportFromPath).toString();
Log.i("MainActivity:EXPORT_PROFILE", "exporting profile: " + srcFile);
try {
val sourcePath = Paths.get(srcFile);
val targetUri = intent!!.getData();
val os = this.applicationContext.getContentResolver().openOutputStream(targetUri!!);
val bytesWritten = Files.copy(sourcePath, os);
Log.d("MainActivity:EXPORT_PROFILE", "copied " + bytesWritten.toString() + " bytes");
if (bytesWritten != 0L) {
os?.flush();
os?.close();
//Files.delete(sourcePath);
}
} catch (e: Exception) {
Log.d("MainActivity:EXPORT_PROFILE FAILED", e.toString());
}
}
}
@ -132,10 +178,13 @@ class MainActivity: FlutterActivity() {
requestWindowFeature(Window.FEATURE_NO_TITLE)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_APP_INFO).setMethodCallHandler { call, result -> handleAppInfo(call, result) }
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_CWTCH).setMethodCallHandler { call, result -> handleCwtch(call, result) }
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, ANDROID_SETTINGS_CHANNEL_NAME).setMethodCallHandler { call, result -> handleAndroidSettings(call, result) }
notificationClickChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_NOTIF_CLICK)
shutdownClickChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_SHUTDOWN_CLICK)
andoidSettingsChangeChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, ANDROID_SETTINGS_CHANGE_NAME)
}
// MethodChannel CHANNEL_APP_INFO handler (Flutter Channel for requests for Android environment info)
private fun handleAppInfo(@NonNull call: MethodCall, @NonNull result: Result) {
when (call.method) {
CALL_APP_INFO -> result.success(getNativeLibDir())
@ -144,6 +193,30 @@ class MainActivity: FlutterActivity() {
}
}
// MethodChannel ANDROID_SETTINGS_CHANNEL_NAME handler (Flutter Channel for requests for Android settings)
// Called from lib/view/globalsettingsview.dart
private fun handleAndroidSettings(@NonNull call: MethodCall, @NonNull result: Result) {
when (call.method) {
CALL_IS_BATTERY_EXEMPT -> result.success(checkIgnoreBatteryOpt() ?: false);
CALL_ASK_BATTERY_EXEMPTION -> { requestBatteryExemption(); result.success(null); }
else -> result.notImplemented()
}
}
@TargetApi(23)
private fun checkIgnoreBatteryOpt(): Boolean {
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
return powerManager.isIgnoringBatteryOptimizations(this.packageName) ?: false;
}
@TargetApi(23)
private fun requestBatteryExemption() {
val i = Intent()
i.action = ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
i.data = Uri.parse("package:" + this.packageName)
startActivityForResult(i, REQUEST_DOZE_WHITELISTING_CODE);
}
private fun getNativeLibDir(): String {
val ainfo = this.applicationContext.packageManager.getApplicationInfo(
"im.cwtch.flwtch", // Must be app name
@ -154,76 +227,346 @@ class MainActivity: FlutterActivity() {
// receives messages from the ForegroundService (which provides, ironically enough, the backend)
private fun handleCwtch(@NonNull call: MethodCall, @NonNull result: Result) {
var method = call.method
val argmap: Map<String, String> = call.arguments as Map<String, String>
// todo change usage patern to match that in FlwtchWorker
// Unsafe for anything using int args, causes access time attempt to cast to string which will fail
val argmap: Map<String, String> = call.arguments as Map<String,String>
// the frontend calls Start every time it fires up, but we don't want to *actually* call Cwtch.Start()
// in case the ForegroundService is still running. in both cases, however, we *do* want to re-register
// the eventbus listener.
if (call.method == "Start") {
val uniqueTag = argmap["torPath"] ?: "nullEventBus"
when (call.method) {
"Start" -> {
val uniqueTag = argmap["torPath"] ?: "nullEventBus"
// note: because the ForegroundService is specified as UniquePeriodicWork, it can't actually get
// accidentally duplicated. however, we still need to manually check if it's running or not, so
// that we can divert this method call to ReconnectCwtchForeground instead if so.
val works = WorkManager.getInstance(this).getWorkInfosByTag(WORKER_TAG).get()
for (workInfo in works) {
WorkManager.getInstance(this).cancelWorkById(workInfo.id)
}
WorkManager.getInstance(this).pruneWork()
// note: because the ForegroundService is specified as UniquePeriodicWork, it can't actually get
// accidentally duplicated. however, we still need to manually check if it's running or not, so
// that we can divert this method call to ReconnectCwtchForeground instead if so.
val works = WorkManager.getInstance(this).getWorkInfosByTag(WORKER_TAG).get()
for (workInfo in works) {
WorkManager.getInstance(this).cancelWorkById(workInfo.id)
}
WorkManager.getInstance(this).pruneWork()
Log.i("MainActivity.kt", "Start() launching foregroundservice")
// this is where the eventbus ForegroundService gets launched. WorkManager should keep it alive after this
val data: Data = Data.Builder().putString(FlwtchWorker.KEY_METHOD, call.method).putString(FlwtchWorker.KEY_ARGS, JSONObject(argmap).toString()).build()
// 15 minutes is the shortest interval you can request
val workRequest = PeriodicWorkRequestBuilder<FlwtchWorker>(15, TimeUnit.MINUTES).setInputData(data).addTag(WORKER_TAG).addTag(uniqueTag).build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork("req_$uniqueTag", ExistingPeriodicWorkPolicy.REPLACE, workRequest)
return
} else if (call.method == "CreateDownloadableFile") {
this.dlToProfile = argmap["ProfileOnion"] ?: ""
this.dlToHandle = argmap["handle"] ?: ""
val suggestedName = argmap["filename"] ?: "filename.ext"
this.dlToFileKey = argmap["filekey"] ?: ""
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/octet-stream"
putExtra(Intent.EXTRA_TITLE, suggestedName)
Log.i("MainActivity.kt", "Start() launching foregroundservice")
// this is where the eventbus ForegroundService gets launched. WorkManager should keep it alive after this
val data: Data = Data.Builder().putString(FlwtchWorker.KEY_METHOD, call.method).putString(FlwtchWorker.KEY_ARGS, JSONObject(argmap).toString()).build()
// 15 minutes is the shortest interval you can request
val workRequest = PeriodicWorkRequestBuilder<FlwtchWorker>(15, TimeUnit.MINUTES).setInputData(data).addTag(WORKER_TAG).addTag(uniqueTag).build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork("req_$uniqueTag", ExistingPeriodicWorkPolicy.REPLACE, workRequest)
}
startActivityForResult(intent, FILEPICKER_REQUEST_CODE)
return
} else if (call.method == "ExportPreviewedFile") {
this.exportFromPath = argmap["Path"] ?: ""
val suggestion = argmap["FileName"] ?: "filename.ext"
var imgType = "jpeg"
if (suggestion.endsWith("png")) {
imgType = "png"
} else if (suggestion.endsWith("webp")) {
imgType = "webp"
} else if (suggestion.endsWith("bmp")) {
imgType = "bmp"
} else if (suggestion.endsWith("gif")) {
imgType = "gif"
"CreateDownloadableFile" -> {
this.dlToProfile = argmap["ProfileOnion"] ?: ""
this.dlToHandle = call.argument("conversation")!!
val suggestedName = argmap["filename"] ?: "filename.ext"
this.dlToFileKey = argmap["filekey"] ?: ""
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/octet-stream"
putExtra(Intent.EXTRA_TITLE, suggestedName)
}
startActivityForResult(intent, FILEPICKER_REQUEST_CODE)
}
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "image/" + imgType
putExtra(Intent.EXTRA_TITLE, suggestion)
"ExportPreviewedFile" -> {
this.exportFromPath = argmap["Path"] ?: ""
val suggestion = argmap["FileName"] ?: "filename.ext"
var imgType = "jpeg"
if (suggestion.endsWith("png")) {
imgType = "png"
} else if (suggestion.endsWith("webp")) {
imgType = "webp"
} else if (suggestion.endsWith("bmp")) {
imgType = "bmp"
} else if (suggestion.endsWith("gif")) {
imgType = "gif"
}
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "image/" + imgType
putExtra(Intent.EXTRA_TITLE, suggestion)
}
startActivityForResult(intent, PREVIEW_EXPORT_REQUEST_CODE)
}
startActivityForResult(intent, PREVIEW_EXPORT_REQUEST_CODE)
return
}
"ExportProfile" -> {
// ...otherwise fallthru to a normal ffi method call (and return the result using the result callback)
val data: Data = Data.Builder().putString(FlwtchWorker.KEY_METHOD, method).putString(FlwtchWorker.KEY_ARGS, JSONObject(argmap).toString()).build()
val workRequest = OneTimeWorkRequestBuilder<FlwtchWorker>().setInputData(data).build()
WorkManager.getInstance(this).enqueue(workRequest)
WorkManager.getInstance(applicationContext).getWorkInfoByIdLiveData(workRequest.id).observe(
this, Observer { workInfo ->
if (workInfo.state == WorkInfo.State.SUCCEEDED) {
val res = workInfo.outputData.keyValueMap.toString()
result.success(workInfo.outputData.getString("result"))
val profileOnion: String = call.argument("ProfileOnion") ?: ""
val file: String = StringBuilder().append(this.applicationContext.cacheDir).append("/").append(call.argument("file") ?: "").toString()
Log.i("FlwtchWorker", "constructing exported file " + file);
Cwtch.exportProfile(profileOnion,file)
this.exportFromPath = argmap["file"] ?: ""
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/gzip"
putExtra(Intent.EXTRA_TITLE, argmap["file"])
}
startActivityForResult(intent, PROFILE_EXPORT_REQUEST_CODE)
}
"GetMessages" -> {
Log.d("MainActivity.kt", "Cwtch GetMessages")
val profile = argmap["ProfileOnion"] ?: ""
val conversation: Int = call.argument("conversation") ?: 0
val indexI: Int = call.argument("index") ?: 0
val count: Int = call.argument("count") ?: 1
result.success(Cwtch.getMessages(profile, conversation.toLong(), indexI.toLong(), count.toLong()))
return
}
"SendMessage" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
val message: String = call.argument("message") ?: ""
result.success(Cwtch.sendMessage(profile, conversation.toLong(), message))
return
}
"SendInvitation" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
val target: Int = call.argument("target") ?: 0
result.success(Cwtch.sendInvitation(profile, conversation.toLong(), target.toLong()))
return
}
"ShareFile" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
val filepath: String = call.argument("filepath") ?: ""
result.success(Cwtch.shareFile(profile, conversation.toLong(), filepath))
return
}
"GetSharedFiles" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
result.success(Cwtch.getSharedFiles(profile, conversation.toLong()))
return
}
"RestartSharing" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val filepath: String = call.argument("filekey") ?: ""
result.success(Cwtch.restartSharing(profile, filepath))
return
}
"StopSharing" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val filepath: String = call.argument("filekey") ?: ""
result.success(Cwtch.stopSharing(profile, filepath))
return
}
"CreateProfile" -> {
val nick: String = call.argument("nick") ?: ""
val pass: String = call.argument("pass") ?: ""
val autostart: Boolean = call.argument("autostart") ?: true
Cwtch.createProfile(nick, pass, autostart)
}
"LoadProfiles" -> {
val pass: String = call.argument("pass") ?: ""
Cwtch.loadProfiles(pass)
}
"ActivatePeerEngine" -> {
val profile: String = call.argument("profile") ?: ""
Cwtch.activatePeerEngine(profile)
}
"DeactivatePeerEngine" -> {
val profile: String = call.argument("profile") ?: ""
Cwtch.deactivatePeerEngine(profile)
}
"ChangePassword" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val pass: String = call.argument("OldPass") ?: ""
val passNew: String = call.argument("NewPass") ?: ""
val passNew2: String = call.argument("NewPassAgain") ?: ""
Cwtch.changePassword(profile, pass, passNew, passNew2)
}
"GetMessage" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
val indexI: Int = call.argument("index") ?: 0
result.success(Cwtch.getMessage(profile, conversation.toLong(), indexI.toLong()))
return
}
"GetMessageByID" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
val id: Int = call.argument("id") ?: 0
result.success(Cwtch.getMessageByID(profile, conversation.toLong(), id.toLong()))
return
}
"GetMessageByContentHash" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
val contentHash: String = call.argument("contentHash") ?: ""
result.success(Cwtch.getMessagesByContentHash(profile, conversation.toLong(), contentHash))
return
}
"SetMessageAttribute" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
val channel: Int = call.argument("Chanenl") ?: 0
val midx: Int = call.argument("Message") ?: 0
val key: String = call.argument("key") ?: ""
val value: String = call.argument("value") ?: ""
Cwtch.setMessageAttribute(profile, conversation.toLong(), channel.toLong(), midx.toLong(), key, value)
}
"AcceptConversation" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
Cwtch.acceptConversation(profile, conversation.toLong())
}
"BlockContact" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
Cwtch.blockContact(profile, conversation.toLong())
}
"UnblockContact" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
Cwtch.unblockContact(profile, conversation.toLong())
}
"DownloadFile" -> {
Log.d("MainActivity.kt", "Cwtch Download File Called...")
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
val filepath: String = call.argument("filepath") ?: ""
val manifestpath: String = call.argument("manifestpath") ?: ""
val filekey: String = call.argument("filekey") ?: ""
// FIXME: Prevent spurious calls by Intent
if (profile != "") {
Cwtch.downloadFile(profile, conversation.toLong(), filepath, manifestpath, filekey)
}
}
)
"CheckDownloadStatus" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val fileKey: String = call.argument("fileKey") ?: ""
Cwtch.checkDownloadStatus(profile, fileKey)
}
"VerifyOrResumeDownload" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
val fileKey: String = call.argument("fileKey") ?: ""
Cwtch.verifyOrResumeDownload(profile, conversation.toLong(), fileKey)
}
"SendProfileEvent" -> {
val onion: String= call.argument("onion") ?: ""
val jsonEvent: String = call.argument("jsonEvent") ?: ""
Cwtch.sendProfileEvent(onion, jsonEvent)
}
"SendAppEvent" -> {
val jsonEvent: String = call.argument("jsonEvent") ?: ""
Cwtch.sendAppEvent(jsonEvent)
}
"ResetTor" -> {
Cwtch.resetTor()
}
"ImportBundle" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val bundle: String = call.argument("bundle") ?: ""
result.success(Cwtch.importBundle(profile, bundle))
}
"CreateGroup" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val server: String = call.argument("server") ?: ""
val groupName: String = call.argument("groupName") ?: ""
Cwtch.createGroup(profile, server, groupName)
}
"DeleteProfile" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val pass: String = call.argument("pass") ?: ""
Cwtch.deleteProfile(profile, pass)
}
"ArchiveConversation" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
Cwtch.archiveConversation(profile, conversation.toLong())
}
"DeleteConversation" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
Cwtch.deleteContact(profile, conversation.toLong())
}
"SetProfileAttribute" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val key: String = call.argument("Key") ?: ""
val v: String = call.argument("Val") ?: ""
Cwtch.setProfileAttribute(profile, key, v)
}
"SetConversationAttribute" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
val key: String = call.argument("Key") ?: ""
val v: String = call.argument("Val") ?: ""
Cwtch.setConversationAttribute(profile, conversation.toLong(), key, v)
}
"LoadServers" -> {
val password: String = call.argument("Password") ?: ""
Cwtch.loadServers(password)
}
"CreateServer" -> {
val password: String = call.argument("Password") ?: ""
val desc: String = call.argument("Description") ?: ""
val autostart: Boolean = call.argument("Autostart") ?: false
Cwtch.createServer(password, desc, autostart)
}
"DeleteServer" -> {
val serverOnion: String = call.argument("ServerOnion") ?: ""
val password: String = call.argument("Password") ?: ""
Cwtch.deleteServer(serverOnion, password)
}
"LaunchServers" -> {
Cwtch.launchServers()
}
"LaunchServer" -> {
val serverOnion: String = call.argument("ServerOnion") ?: ""
Cwtch.launchServer(serverOnion)
}
"StopServer" -> {
val serverOnion: String = call.argument("ServerOnion") ?: ""
Cwtch.stopServer(serverOnion)
}
"StopServers" -> {
Cwtch.stopServers()
}
"DestroyServers" -> {
Cwtch.destroyServers()
}
"SetServerAttribute" -> {
val serverOnion: String = call.argument("ServerOnion") ?: ""
val key: String = call.argument("Key") ?: ""
val v: String = call.argument("Val") ?: ""
Cwtch.setServerAttribute(serverOnion, key, v)
}
"ImportProfile" -> {
val file: String = call.argument("file") ?: ""
val pass: String = call.argument("pass") ?: ""
Data.Builder().putString("result", Cwtch.importProfile(file, pass)).build()
}
"ReconnectCwtchForeground" -> {
Cwtch.reconnectCwtchForeground()
}
"Shutdown" -> {
Cwtch.shutdownCwtch();
}
else -> {
// ...otherwise fallthru to a normal ffi method call (and return the result using the result callback)
val data: Data = Data.Builder().putString(FlwtchWorker.KEY_METHOD, method).putString(FlwtchWorker.KEY_ARGS, JSONObject(argmap).toString()).build()
val workRequest = OneTimeWorkRequestBuilder<FlwtchWorker>().setInputData(data).build()
WorkManager.getInstance(this).enqueue(workRequest)
WorkManager.getInstance(applicationContext).getWorkInfoByIdLiveData(workRequest.id).observe(
this, Observer { workInfo ->
if (workInfo != null && workInfo.state == WorkInfo.State.SUCCEEDED) {
val res = workInfo.outputData.keyValueMap.toString()
result.success(workInfo.outputData.getString("result"))
}
}
)
return
}
}
result.success(null)
}
// using onresume/onstop for broadcastreceiver because of extended discussion on https://stackoverflow.com/questions/7439041/how-to-unregister-broadcastreceiver
@ -232,19 +575,22 @@ class MainActivity: FlutterActivity() {
Log.i("MainActivity.kt", "onResume")
if (myReceiver == null) {
Log.i("MainActivity.kt", "onResume registering local broadcast receiver / event bus forwarder")
val mc = MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CWTCH_EVENTBUS)
val filter = IntentFilter("im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS")
myReceiver = MyBroadcastReceiver(mc)
LocalBroadcastManager.getInstance(applicationContext).registerReceiver(myReceiver!!, filter)
val bm = flutterEngine?.dartExecutor?.binaryMessenger;
if (bm != null) {
val mc = MethodChannel(bm, CWTCH_EVENTBUS)
val filter = IntentFilter("im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS")
myReceiver = MyBroadcastReceiver(mc)
LocalBroadcastManager.getInstance(applicationContext)
.registerReceiver(myReceiver!!, filter)
}
}
// ReconnectCwtchForeground which will resync counters and settings...
// We need to do this here because after a "pause" flutter is still running
// but we might have lost sync with the background process...
Log.i("MainActivity.kt", "Call ReconnectCwtchForeground")
val data: Data = Data.Builder().putString(FlwtchWorker.KEY_METHOD, "ReconnectCwtchForeground").putString(FlwtchWorker.KEY_ARGS, "{}").build()
val workRequest = OneTimeWorkRequestBuilder<FlwtchWorker>().setInputData(data).build()
WorkManager.getInstance(applicationContext).enqueue(workRequest)
Cwtch.reconnectCwtchForeground()
}
override fun onStop() {
@ -256,6 +602,7 @@ class MainActivity: FlutterActivity() {
}
}
override fun onDestroy() {
super.onDestroy()
Log.i("MainActivity.kt", "onDestroy - cancelling all WORKER_TAG and pruning old work")

View File

@ -13,6 +13,7 @@
app:lottie_autoPlay="true"
app:lottie_rawRes="@raw/cwtch_animated_logo_op"
app:lottie_loop="true"
app:lottie_speed="1.00" />
app:lottie_speed="1.00"
app:lottie_enableMergePathsForKitKatAndAbove="true" />
</androidx.constraintlayout.widget.ConstraintLayout>

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,13 @@
buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = '1.5.31'
repositories {
google()
jcenter()
// jCenter() no longer exists... https://blog.gradle.org/jcenter-shutdown
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.4'
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
@ -15,7 +16,7 @@ buildscript {
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}

View File

@ -1,6 +1,6 @@
#Fri Jun 23 08:50:38 CEST 2017
#Mon Jun 20 10:33:21 PDT 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
zipStoreBase=GRADLE_USER_HOME

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 51 KiB

8
build.yaml Normal file
View File

@ -0,0 +1,8 @@
targets:
$default:
sources:
- lib/**
- pubspec.*
- $package$
# Allows the code generator to target files outside of the lib folder
- integration_test/**.dart

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

View File

@ -0,0 +1,16 @@
Cwtch (/kʊtʃ/ - a Welsh word roughly translating to “a hug that creates a safe place”) is a decentralized,
privacy-preserving, multi-party messaging protocol that can be used to build metadata resistant applications.
- Decentralized and Open: There is no “Cwtch service” or “Cwtch network”. Participants in Cwtch
can host their own safe spaces, or lend their infrastructure to others seeking a safe space.
The Cwtch protocol is open, and anyone is free to build bots, services and user interfaces and
integrate and interact with Cwtch.
- Privacy Preserving: All communication in Cwtch is end-to-end encrypted and takes place over Tor v3
onion services.
- Metadata Resistant: Cwtch has been designed such that no information is exchanged or available to
anyone without their explicit consent, including on-the-wire messages and protocol metadata.
For more information on how Cwtch works and a guide to metadata resistant communication please
checkout the Cwtch Handbook: https://docs.cwtch.im/

View File

@ -0,0 +1 @@
Metadata resistant privacy platform designed to help you resist surveillance

View File

@ -0,0 +1 @@
Cwtch

View File

@ -0,0 +1 @@
https://cwtch.im/cwtch-explainer.mp4

View File

@ -3,4 +3,6 @@
VERSION=`cat LIBCWTCH-GO-MACOS.version`
echo $VERSION
curl https://build.openprivacy.ca/files/libCwtch-go-macos-$VERSION/libCwtch.dylib --output libCwtch.dylib
curl --fail https://build.openprivacy.ca/files/libCwtch-go-macos-$VERSION/libCwtch.x64.dylib --output libCwtch.x64.dylib
curl --fail https://build.openprivacy.ca/files/libCwtch-go-macos-$VERSION/libCwtch.arm64.dylib --output libCwtch.arm64.dylib

View File

@ -1,6 +1,7 @@
$Env:VERSION = type LIBCWTCH-GO.version
echo $Env:VERSION
# This should automatically fail on error...
Invoke-WebRequest -Uri https://build.openprivacy.ca/files/libCwtch-go-$Env:VERSION/libCwtch.dll -OutFile windows/libCwtch.dll
#Invoke-WebRequest -Uri https://build.openprivacy.ca/files/libCwtch-go-$Env:VERSION/cwtch.aar -OutFile android/cwtch/cwtch.aar

View File

@ -3,5 +3,5 @@
VERSION=`cat LIBCWTCH-GO.version`
echo $VERSION
wget https://build.openprivacy.ca/files/libCwtch-go-$VERSION/cwtch.aar -O android/cwtch/cwtch.aar
wget https://build.openprivacy.ca/files/libCwtch-go-$VERSION/libCwtch.so -O linux/libCwtch.so
curl --fail https://build.openprivacy.ca/files/libCwtch-go-$VERSION/cwtch.aar --output android/cwtch/cwtch.aar
curl --fail https://build.openprivacy.ca/files/libCwtch-go-$VERSION/libCwtch.so --output linux/libCwtch.so

View File

@ -1,7 +1,7 @@
#!/bin/sh
cd macos
curl https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-macos-0.4.6.7.tar.gz --output tor.tar.gz
curl https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-macos-0.4.7.8.tar.gz --output tor.tar.gz
tar -xzf tor.tar.gz
chmod a+x Tor/tor.real
cd ..

View File

@ -1,6 +1,6 @@
Invoke-WebRequest -Uri https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-win64-0.4.6.5.zip -OutFile tor.zip
Invoke-WebRequest -Uri https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-win64-0.4.7.8.zip -OutFile tor.zip
if ((Get-FileHash tor.zip -Algorithm sha512).Hash -ne '7917561a7a063440a1ddfa9cb544ab9ffd09de84cea3dd66e3cc9cd349dd9f85b74a522ec390d7a974bc19b424c4d53af60e57bbc47e763d13cab6a203c4592f' ) { Write-Error 'tor.zip sha512sum mismatch' }
if ((Get-FileHash tor.zip -Algorithm sha512).Hash -ne '5b8f900a37f6e90d7a945b3903d769383c7478042cb43b2105d2374186e1a536f1a4758a2823d1d5be71d53a81dcfd8243293e04f82812d355983df322823cf4' ) { Write-Error 'tor.zip sha512sum mismatch' }
Expand-Archive -Path tor.zip -DestinationPath Tor

View File

@ -1,12 +1,14 @@
#!/bin/sh
wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-0.4.5.9-linux-x86_64 -O linux/tor
chmod a+x linux/tor
cd linux
wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-0.4.7.8-linux-x86_64.tar.gz -O tor.tar.gz
tar -xzf tor.tar.gz
cd ..
mkdir -p android/app/src/main/jniLibs/arm64-v8a
wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-0.4.4.9-arm64_pie -O android/app/src/main/jniLibs/arm64-v8a/libtor.so
wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-0.4.7.10-arm64 -O android/app/src/main/jniLibs/arm64-v8a/libtor.so
chmod a+x android/app/src/main/jniLibs/arm64-v8a/libtor.so
mkdir -p android/app/src/main/jniLibs/armeabi-v7a
wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-0.4.4.9-arm_pie -O android/app/src/main/jniLibs/armeabi-v7a/libtor.so
wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-0.4.7.10-arm7 -O android/app/src/main/jniLibs/armeabi-v7a/libtor.so
chmod a+x android/app/src/main/jniLibs/armeabi-v7a/libtor.so

20
index2.js Normal file
View File

@ -0,0 +1,20 @@
var fs = require("fs");
var reporter = require('cucumber-html-reporter');
const reportRootDir = 'integration_test/gherkin/reports/'
const jsonReportPath = `${reportRootDir}json_report.json`;
const htmlReportPath = `${reportRootDir}cucumber_report.html`;
const reportFile = fs.readFileSync(`${reportRootDir}integration_response_data.json`);
//const jsonReport = JSON.parse(JSON.parse(reportFile).gherkin_reports)[0];
const jsonReport = JSON.parse(reportFile);
fs.writeFileSync(jsonReportPath, JSON.stringify(jsonReport));
var options = {
theme: 'bootstrap',
jsonFile: jsonReportPath,
output: htmlReportPath,
reportSuiteAsScenarios: true,
launchReport: false,
};
reporter.generate(options);

View File

@ -0,0 +1,60 @@
## Environments
Located in the `integration_test/env` folder and managed by the hooks in `integration_test/hooks/env.dart`. Specify the environment you want a feature to run in by tagging it.
* `[no tag] (env/default)`: default environment to load if none is specified
* `@env:aliceandbob1 (env/aliceandbob1)`: no-password Alice, Bob, and Carol profiles. Alice and Bob have already added each other, Carol has no contacts
* `@env:persist (env/persist)`: changes made to this profile persist between features and scenarios (but NOT between runs)
* `@env:clean`: runs the feature with no profile existing yet on disk
## Tests
[ ] 1. general
[X] splash screen + clean load
[X] setting save+load (TODO: dropdowns)
[~] tor status+reset
[~] shutdown cwtch
[ ] 2. global settings (verify functionality)
[_] language # blocked by dropdown
[_] theme+color theme # blocked by dropdown
[ ] column mode -> background? so all tests check both modes?
[X] block unknown
[X] streamer mode
[ ] 3. experiments (
[ ] group chat -> needs many
[ ] server hosting -> also many
[ ] file sharing -> a couple
[ ] image previews
[ ] clickable links (how much to test?)
[ ] 4. profile mgmt
[X] create+delete
[X] default+password load
[X] name change
[ ] password change
[ ] known server mgmt
[ ] 5. p2p chat
[ ] add, remove, block, archive
[ ] invite accept+reject
[X] send+receive
[ ] acks
[ ] try to send a long message
[ ] malformed messages, replies
[ ] overlays (invite, file/image)
[ ] send
[ ] receive
[ ] functionality
[ ] 6. p2p settings
[ ] name saving + transmission
[ ] block (ui indicators, functionality) inc in groups
[ ] history save+load
[ ] 7. groupchat
[ ] add, leave, archive
[ ] send+receive inc acks
[ ] try to send a long message
[ ] malformed messages, replies
[ ] overlays (invite, file/image) inc from non-contacts
[ ] send
[ ] receive
[ ] functionality
[ ] 8. group settings
[ ] display name

View File

@ -1,53 +0,0 @@
// This is a basic Flutter integration test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:cwtch/main_test.dart' as app;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
_testMain();
}
void _testMain() {
testWidgets('Blocked message rejection test', (WidgetTester tester) async {
final String testerProfile = "mr roboto";
final String blockedProfile = "rudey";
// start the app and render a few frames
app.main();
await tester.pump(); await tester.pump(); await tester.pump();
//await tester.pumpAndSettle();
for (var i = 0; i < 30; i++) {
print("$i pump");
await tester.pump();
}
// log in to a profile with a blocked contact
await tester.tap(find.text(testerProfile));
await tester.pump(); await tester.pump(); await tester.pump();
expect(find.byIcon(Icons.block), findsOneWidget);
// use the debug control to inject a message from the contact
await tester.tap(find.byIcon(Icons.bug_report));
await tester.pump(); await tester.pump(); await tester.pump();
// screenshot test
print(Directory.current);
//Directory.current = "/home/erinn/AndroidStudioProjects/flwtch/integration_test";
await expectLater(find.byKey(Key('app')), matchesGoldenFile('blockedcontact.png'));
// any active message badges?
expect(find.text('1'), findsNothing);
});
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1 @@
_âeK%?Š!ţ~Lö9<C3B6>u×ÍlýQQż¦U•rMQCN5<4E>T-Ó/[<ń<ěn@KgŚă-ŕóŕČŃÓWÇ^l$řIC]»ÎI× ů@z¤m•Şb ŠNgířż?ő:†IşäD!ă±6ć°%čě…b

View File

@ -0,0 +1 @@
¢‰qö3‰ ÉÌ¥êÒŽB7 Å¢(ÊvQBöÞɱ<C389>øŒœ¾F±zŠ\\UƒÈG[Ü/ £Ñ?uš¼\;]y”HþG|þÛ,Þ3xÛÞeE0 !¬ÄSÍž<nÐÃòÐÉ®M~ üw “ÀëQ@6ǸËÖo£ÉüØ…ÕöÀiò

View File

@ -0,0 +1 @@
;KĄĂ”ČÓŠť\|ç<ÂŐÉ^1iRüÁw°ôFŔQĄ'¢©©z{P4ĂP(ä"5͸Qpr7˝`ŇK^uý¸ČÖ;©1&Ĺ,vŞ K/YößžŹmĄâ}±3]/§v"&ĽiѸ!3Wîyëjuvą¶D+w_'

View File

@ -0,0 +1,2 @@
¢ž»5‡Ä ô<03>m-J0újÕx­ ŽÙð•ÛjÙß“K×çøs³C=íà¾t¶-ÿD÷ñÇecàIXF`íI´
÷³6 Vr×gp4ËBóäÞS¿E<C2BF>tvìíä1iù¢‡”}ûZóÈWMóŒö´»1þ‡KB||Å,¢fEž%<<3C>D

Binary file not shown.

1
integration_test/env/default/dev/SALT vendored Normal file
View File

@ -0,0 +1 @@
ó„Ý gÞd7èfª>ZrPòV`dB<(÷ ôÈW`½ 7¾c´¬n•ËnŠ.ü¾s8lÿ·“*dZUmÊŠí&¸ÊhøEëö8mê<1E>«Y ŸüñrÒý×W²H%{¸iùFÃÎ<äÿ[0²Ñâ”\ yÚø-¯R½L´¨ -'

View File

@ -0,0 +1,2 @@
DÍk­HzöĺN(Ăĺ•őŐ`x<>ć—×ë#€ÍěĎ3— c˛2_Ś3‰őťŞRC6~-zSÉĘ?JŽčĐ<>4 fřŔăhŢýüv˝F†8áBü1Q\˛"lHh5í§ <¦‹;$J3č"źúBamT5 <Á4îě•Żcj™ÚŽśËgˇŹÉÂ/<2F>‰vęĽnKÄßVG†Ű~YdŞG ÝrQPÁrôŕ<C3B4>ljěăČ-ZĽy÷_Ľ¸˙˛M·ôÄĽĂ!AX^HR˘¦ç<>őX ;ăŘ©Ô)@2šŔ”tŕÍŁä ý´[cdX®ĹĹ­+ý,0wa}ß%<25><11>
Ď9˘x[ÝČNP]Ĺ5o_M@Ą†™±!©•Ű0:IÄŮóv€ó;ź ÔĂP¨Öe?€˛Śip*‰{<ŐČ—´,RyMnď-‡gËťëĺÉfŹÚV<56>Dá>łĐ;S8őĎ0l¦k "ľÖöĘ1˝żA4ţ<11>vG6î€Uş^"zÓ«H¸´e”S•<53>°u\čč=Ś5ńë¨Ů¤»]aď˝3ąďí©g`î-SŻŢŰ…ý#ăE÷× ¸=Śµ˘W,8­đdŚĚä.˙ Ž҉=şPüÖ•Ąń_<C584>jäĎřĚvyěž,Ľî<˙xokV»`<60>vQ|Ć/©˙ťs˝0şę-tÖ-ßĂ[şa=ý­ôßć<>ŕé÷µ×łëöN­řmďV!qŘŹZ†Т˛Ł Ě˛±saŢSúT ‚ż€”ôîěuDąL<>­-‘Ç"šŇíRşŁŰ<*'ófgߧČM~ŇŚ"sŐ<73>âl/xuĹ<řsÂÄ4÷~Ůf>yś§­NÝ{ľ¨b!Ey 'ˇězęoZ!×<>\"´a(Ýp,PhŔrZ…ŐMµ-…ë+ĐľÇĆą ÉŚťÎĘÝô\}ë~ł?sJL|ú©z˙·°IaXYh/á ą2;vą“u§…ÉĽ<C389>P€(

BIN
integration_test/env/persist/dev/SALT vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,12 @@
@env:clean
Feature: Splash screen displays and then closes
Scenario: splash screen appears
Then I expect the widget 'SplashView' to be present within 10 seconds
Then I expect the widget 'ProfileManagerView' to be present within 10 seconds
# first-run of cwtch creates expected files and folders
Then I expect the folder 'integration_test/env/temp' to exist
And I expect the folder 'integration_test/env/temp/dev' to exist
And I expect the file 'integration_test/env/temp/dev/SALT' to exist
And I expect the file 'integration_test/env/temp/dev/ui.globals' to exist
And I expect the folder 'integration_test/env/temp/dev/tor' to exist
And I expect the file 'integration_test/env/temp/dev/tor/torrc' to exist

View File

@ -0,0 +1,50 @@
@env:persist
Feature: Settings pane opens and can save settings persistently
Scenario: Open the Settings pane
Given I wait until the widget with type 'ProfileMgrView' is present
And I tap the 'OpenSettingsView' button
And I wait until the text 'Cwtch Settings' is present
And I take a screenshot
Scenario: Change every setting (except Language)
Given I wait until the widget with type 'ProfileMgrView' is present
Given I tap the 'OpenSettingsView' button
And I wait until the text 'Use Light Themes' is present
When I tap the widget that contains the text "Use Light Themes"
And I tap the widget that contains the text "Block Unknown Contacts"
And I tap the widget that contains the text "Streamer/Presentation Mode"
And I tap the widget that contains the text "Enable Experiments"
Then I wait until the text 'Enable Group Chat' is present
And I tap the widget that contains the text "Enable Group Chat"
And I tap the widget that contains the text "Hosting Servers"
And I tap the widget that contains the text "File Sharing"
Then I wait until the text 'Image Previews and Profile Pictures' is present
And I tap the widget that contains the text "Image Previews and Profile Pictures"
And I wait until the text 'Download Folder' is present
And I fill the "DownloadFolderPicker" field with "/this/is/a/test"
And I tap the widget that contains the text "Enable Clickable Links"
Then I expect the switch that contains the text "Use Light Themes" to be checked
And I expect the switch that contains the text "Block Unknown Contacts" to be checked
And I expect the switch that contains the text "Streamer/Presentation Mode" to be checked
And I expect the switch that contains the text "Enable Experiments" to be checked
And I expect the switch that contains the text "Enable Group Chat" to be checked
And I expect the switch that contains the text "Hosting Servers" to be checked
And I expect the switch that contains the text "File Sharing" to be checked
And I expect the switch that contains the text "Image Previews and Profile Pictures" to be checked
And I expect the "DownloadFolderPicker" to be "/this/is/a/test"
And I expect the switch that contains the text "Enable Clickable Links" to be checked
Scenario: When the app is reloaded, settings from the previous scenario have persisted
Given I wait until the widget with type 'ProfileMgrView' is present
Given I tap the 'OpenSettingsView' button
And I wait until the text 'Use Light Themes' is present
Then I expect the switch that contains the text "Use Light Themes" to be checked
And I expect the switch that contains the text "Block Unknown Contacts" to be checked
And I expect the switch that contains the text "Streamer/Presentation Mode" to be checked
And I expect the switch that contains the text "Enable Experiments" to be checked
And I expect the switch that contains the text "Enable Group Chat" to be checked
And I expect the switch that contains the text "Hosting Servers" to be checked
And I expect the switch that contains the text "File Sharing" to be checked
And I expect the switch that contains the text "Image Previews and Profile Pictures" to be checked
And I expect the "DownloadFolderPicker" to be "/this/is/a/test"
And I expect the switch that contains the text "Enable Clickable Links" to be checked

View File

@ -0,0 +1,14 @@
Feature: Tor initializes correctly
Scenario: Check the Tor version
Given I wait until the widget with type 'ProfileMgrView' is present
And I tap the icon with type "TorIcon"
Then I expect the Tor version to be present
And I expect the string 'Online' to be present within 60 seconds
Scenario: Reset Tor
Given I wait until the widget with type 'ProfileMgrView' is present
And I tap the icon with type "TorIcon"
Then I expect the string 'Online' to be present within 60 seconds
Then I tap the button that contains the text "Reset"
And I wait for 1 second
Then I expect the text "Online" to be absent

View File

@ -0,0 +1,8 @@
Feature: Shutdown Cwtch button works correctly
Scenario: Clicking 'Shutdown Cwtch' shuts down Cwtch
Given I wait until the widget with type 'ProfileMgrView' is present
And I tap the button with tooltip 'Shutdown Cwtch'
Then I expect the text 'Shutdown Cwtch?' to be present
#this also kills the testing framework sadly. will have to find a workaround
#And I tap the button that contains the text 'Shutdown Cwtch'
#Then I wait until the widget with type 'ProfileMgrView' is absent

View File

@ -0,0 +1,16 @@
Feature: Global 'language' setting
Scenario: Change the language to French and back
Given I wait until the widget with type 'ProfileMgrView' is present
Given I tap the 'OpenSettingsView' button
And I wait until the text 'Language' is present
Then I expect the text 'Language' to be present
And I expect the text 'Langue' to be absent
When I tap the widget that contains the text "English"
And I wait until the text 'French' is present
And I tap the widget that contains the text "French"
And I wait until the text 'Langue' is present
And I expect the text 'Language' to be absent
When I tap the widget that contains the text "Français"
And I tap the widget that contains the text "Anglais"
And I wait until the text 'Language' is present
And I expect the text 'Langue' to be absent

View File

@ -0,0 +1,12 @@
Feature: Global 'Theme' setting
Scenario: Change the theme to Mermaid
Given I wait until the widget with type 'ProfileMgrView' is present
Given I tap the 'OpenSettingsView' button
And I wait for 1 second
When I tap the "DropdownTheme" button
And I tap the element that contains the text "Mermaid"
Scenario: Change the theme to Light Mode
Given I wait until the widget with type 'ProfileMgrView' is present
Given I tap the 'OpenSettingsView' button
And I wait for 1 second
And I tap the widget that contains the text "Theme"

View File

@ -0,0 +1,20 @@
@env:aliceandbob1
Feature: Block unknown contacts setting
Scenario: Carol adds Alice but Alice doesn't see it because Block Unknowns is enabled
Given I wait until the widget with type 'ProfileMgrView' is present
Given I tap the 'OpenSettingsView' button
When I tap the widget that contains the text "Block Unknown Contacts"
Then I expect the switch that contains the text "Block Unknown Contacts" to be checked
Given I tap the back button
And I wait until the text "Carol" is present
And I tap the button that contains the text "Carol"
And I tap the button with tooltip "Add a new contact or conversation"
When I fill the "txtAddP2P" field with "vbmmsbx3rhndpfz6t3jkrd7m3yu62xzrldxkdgsw4rsehiwuw3tmo7yd"
And I wait for 1 second
And I take a screenshot
And I tap the back button
And I wait until the text "Alice" is present
And I wait until the tooltip "Online" is present
And I tap the button that contains the text "Alice"
And I wait for 20 seconds
Then I expect the text "yxj2pvhozedflp4g7yitpqkeho63maaffi2qgsj3e6s2fbmosuuas2qd" to be absent

View File

@ -0,0 +1,18 @@
@env:aliceandbob1
Feature: Streamer mode
Scenario: All onions disappear when Streamer Mode is enabled
Given I wait until the widget with type 'ProfileMgrView' is present
And I wait until the text "vbmmsbx3rhndpfz6t3jkrd7m3yu62xzrldxkdgsw4rsehiwuw3tmo7yd" is present
And I wait until the text "pjurzypqui3dnpxj6aemk6cqz22yx6zfr5lq4jzu7muwe2yyx2zrnzyd" is present
Given I tap the 'OpenSettingsView' button
And I wait for 1 second
And I tap the widget that contains the text "Streamer/Presentation Mode"
Then I expect the switch that contains the text "Streamer/Presentation Mode" to be checked
When I tap the back button
And I wait until the text "Alice" is present
And I wait until the text "Bob" is present
Then I expect the text "vbmmsbx3rhndpfz6t3jkrd7m3yu62xzrldxkdgsw4rsehiwuw3tmo7yd" to be absent
And I expect the text "pjurzypqui3dnpxj6aemk6cqz22yx6zfr5lq4jzu7muwe2yyx2zrnzyd" to be absent
When I tap the button that contains the text "Alice"
Then I expect the text "vbmmsbx3rhndpfz6t3jkrd7m3yu62xzrldxkdgsw4rsehiwuw3tmo7yd" to be absent
And I expect the text "pjurzypqui3dnpxj6aemk6cqz22yx6zfr5lq4jzu7muwe2yyx2zrnzyd" to be absent

View File

@ -0,0 +1,90 @@
@env:persist
Feature: Basic Profile Management
Scenario: Error on Creating a Profile without a Display Name
Given I wait until the widget with type 'ProfileMgrView' is present
And I tap the button with tooltip "Add new profile"
Then I expect the text 'Display Name' to be present
And I expect the text 'New Password' to be present
And I expect the text 'Please enter a display name' to be absent
Then I tap the "button" widget with label "Add new profile"
And I expect the text 'Please enter a display name' to be present
And I take a screenshot
Scenario: Create Unencrypted Profile
Given I wait until the widget with type 'ProfileMgrView' is present
And I tap the button with tooltip "Add new profile"
Then I expect the text 'Display Name' to be present
And I expect the text 'New Password' to be present
And I take a screenshot
Then I tap the "passwordCheckBox" widget
And I expect the text 'New Password' to be absent
And I take a screenshot
Then I fill the "displayNameFormElement" field with "Alice (Unencrypted)"
Then I tap the "button" widget with label "Add new profile"
And I expect a "ProfileRow" widget with text "Alice (Unencrypted)"
And I take a screenshot
Then I tap the "ProfileRow" widget with label "Alice (Unencrypted)"
And I expect the text "Alice (Unencrypted) » Conversations" to be present
And I take a screenshot
Scenario: Load Unencrypted Profile
Given I wait until the widget with type 'ProfileMgrView' is present
Then I expect a "ProfileRow" widget with text "Alice (Unencrypted)"
Scenario: Create Encrypted Profile
Given I wait until the widget with type 'ProfileMgrView' is present
And I tap the button with tooltip "Add new profile"
Then I expect the text 'Display Name' to be present
And I expect the text 'New Password' to be present
And I take a screenshot
Then I fill the "displayNameFormElement" field with "Alice (Encrypted)"
Then I fill the "passwordFormElement" field with "password1"
Then I fill the "confirmPasswordFormElement" field with "password1"
And I take a screenshot
Then I tap the "button" widget with label "Add new profile"
And I expect a "ProfileRow" widget with text "Alice (Encrypted)"
And I take a screenshot
Then I tap the "ProfileRow" widget with label "Alice (Encrypted)"
And I expect the text 'Alice (Encrypted) » Conversations' to be present
And I take a screenshot
Scenario: Load an Encrypted Profile by Unlocking it with a Password
Given I wait until the widget with type 'ProfileMgrView' is present
Then I expect the text 'Enter a password to view your profiles' to be absent
And I tap the button with tooltip "Unlock encrypted profiles by entering their password."
Then I expect the text 'Enter a password to view your profiles' to be present
When I fill the "unlockPasswordProfileElement" field with "password1"
And I tap the "button" widget with label "Unlock"
Then I expect a "ProfileRow" widget with text "Alice (Encrypted)"
Scenario: Load an Encrypted Profile by Unlocking it with a Password and Change the Name
Given I wait until the widget with type 'ProfileMgrView' is present
Then I expect the text 'Enter a password to view your profiles' to be absent
And I tap the button with tooltip "Unlock encrypted profiles by entering their password."
Then I expect the text 'Enter a password to view your profiles' to be present
When I fill the "unlockPasswordProfileElement" field with "password1"
And I tap the "button" widget with label "Unlock"
Then I expect a "ProfileRow" widget with text "Alice (Encrypted)"
When I tap the "IconButton" widget with tooltip "Edit Profile Alice (Encrypted)"
Then I expect the text 'Display Name' to be present
Then I fill the "displayNameFormElement" field with "Carol (Encrypted)"
And I tap the "button" widget with label "Save Profile"
And I wait until the widget with type 'ProfileMgrView' is present
Then I expect a "ProfileRow" widget with text "Carol (Encrypted)"
Scenario: Delete an Encrypted Profile
Given I wait until the widget with type 'ProfileMgrView' is present
Then I expect the text 'Enter a password to view your profiles' to be absent
And I tap the button with tooltip "Unlock encrypted profiles by entering their password."
Then I expect the text 'Enter a password to view your profiles' to be present
When I fill the "unlockPasswordProfileElement" field with "password1"
And I tap the "button" widget with label "Unlock"
Then I expect a "ProfileRow" widget with text "Carol (Encrypted)"
And I take a screenshot
When I tap the "IconButton" widget with tooltip "Edit Profile Carol (Encrypted)"
Then I expect the text 'Display Name' to be present
When I tap the button that contains the text "Delete"
Then I expect the text "Really Delete Profile" to be present
When I tap the "button" widget with label "Really Delete Profile"
And I wait until the widget with type 'ProfileMgrView' is present
Then I expect a "ProfileRow" widget with text "Carol (Encrypted)" to be absent

View File

@ -0,0 +1,31 @@
@env:aliceandbob1
Feature: Sending and receiving chat messages
Background:
Given I wait until the widget with type "ProfileRow" is present
And I wait for 4 seconds
Given I tap the button that contains the text "Alice"
And I tap the button that contains the text "Bob"
And I wait until the text "Contact is offline, messages can't be delivered right now" is absent
#And I wait for 6 seconds
When I fill the "txtCompose" field with "hello! this is a test!"
And I tap the "btnSend" button
Then I expect a "MessageBubble" widget with text "hello! this is a test!\u202F" to be present within 5 seconds
#Then I expect the text "hello! this is a test!" to be present
And I tap the back button
And I tap the back button
Scenario: Bob receives the message from Alice
Given I tap the button that contains the text "Bob"
And I tap the button that contains the text "Alice"
Then I expect a "MessageBubble" widget with text "hello! this is a test!\u202F" to be present within 5 seconds
Scenario: Bob replies to a message from Alice
Given I tap the button that contains the text "Bob"
And I tap the button that contains the text "Alice"
#When I swipe right by 15 pixels on the element that contains the text "hello! this is a test!\u202F"
#When I swipe right by 15 pixels on the widget of type "MessageBubble" with text "hello! this is a test!\u202F"
And I tap the button with tooltip "Reply to this message"
And I fill the "txtCompose" field with "yay the test worked"
And I tap the "btnSend" button
Then I expect to see the message "yay the test worked\u202F" replying to "hello! this is a test!" within 5 seconds
And I take a screenshot

View File

@ -0,0 +1,107 @@
//import 'package:flutter_gherkin/flutter_gherkin_integration_test.dart'; // notice new import name
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:gherkin/gherkin.dart';
import 'dart:io';
// The application under test.
import 'package:cwtch/main.dart' as app;
import 'package:glob/glob.dart';
import 'gherkin_suite_test.dart';
import 'hooks/env.dart';
import 'steps/chat.dart';
import 'steps/files.dart';
import 'steps/form_elements.dart';
import 'steps/overrides.dart';
import 'steps/text.dart';
import 'steps/utils.dart';
part 'gherkin_suite_test.g.dart';
const REPLACED_BY_SCRIPT = <String>['integration_test/features/**.feature'];
@GherkinTestSuite(executionOrder: ExecutionOrder.alphabetical, featurePaths: REPLACED_BY_SCRIPT)
void main() async {
final params = [
SwitchStateParameter(),
];
final steps = [
// chat elements
ExpectReply(),
// form elements
CheckSwitchState(),
CheckSwitchStateWithText(),
DropdownChoose(),
// utils
TakeScreenshot(),
// overrides
TapWidgetWithType(),
TapWidgetWithLabel(),
TapWidgetWithTooltip(),
ExpectWidgetWithText(),
AbsentWidgetWithText(),
WaitUntilTypeExists(),
ExpectTextToBePresent(),
ExpectWidgetWithTextWithin(),
WaitUntilTextExists(),
WaitUntilTooltipExists(),
SwipeOnType(),
// text
TorVersionPresent(),
TooltipTap(),
// files
FolderExists(),
FileExists(),
];
var sb = StringBuffer();
sb
..writeln("## Custom Parameters\n")
..writeln("| name | pattern |")
..writeln("| --- | --- |");
for (var i in params) {
sb
..write("| ")
..write(i.identifier)
..write(" | ")
..write(i.pattern.toString().replaceFirst("RegExp: pattern=", "").replaceFirst(" flags=i", "").replaceAll("|", "&#124;"))
..writeln(" |");
}
sb
..writeln("\n## Custom steps\n")
..writeln("| pattern |")
..writeln("| --- |");
for (var i in steps) {
sb.writeln(i.pattern.toString().replaceFirst("RegExp: pattern=", "| ").replaceFirst(" flags=", " |").replaceAll("|", "&#124;"));
}
var f = File("integration_test/CustomSteps.md");
f.writeAsString(sb.toString());
await executeTestSuite(
configuration: FlutterTestConfiguration(
reporters: [
StdoutReporter(MessageLevel.verbose)
..setWriteLineFn(print)
..setWriteFn(print),
ProgressReporter()
..setWriteLineFn(print)
..setWriteFn(print),
TestRunSummaryReporter()
..setWriteLineFn(print)
..setWriteFn(print),
JsonReporter(),
],
customStepParameterDefinitions: [
SwitchStateParameter(),
],
stepDefinitions: steps,
hooks: [
ResetCwtchEnvironment(),
AttachScreenshotOnFailedStepHook(),
]),
appMainFunction: (World world) => app.main(),
);
}

View File

@ -0,0 +1,50 @@
import 'dart:io';
import 'package:gherkin/gherkin.dart';
class ResetCwtchEnvironment extends Hook {
@override
int get priority => 10;
@override
Future<void> onBeforeRun(TestConfiguration config) async {
// initialize @env:persist
await Process.run("rm", ["-rf", "integration_test/env/temp-persist"]);
await Process.run("rm", ["-rf", "integration_test/env/temp"]);
await Process.run("cp", ["-R", "integration_test/env/persist", "integration_test/env/temp-persist"]);
return super.onBeforeRun(config);
}
@override
Future<void> onAfterRun(TestConfiguration config) async {
// Clean up After a Test Run...
print("clean up environments after run...");
await Process.run("rm", ["-rf", "integration_test/env/temp-persist"]);
await Process.run("rm", ["-rf", "integration_test/env/temp"]);
return super.onAfterRun(config);
}
@override
Future<void> onBeforeScenario(TestConfiguration config, String scenario, Iterable<Tag> tags) async {
if (tags.any((t) => t.name == "@env:persist")) {
await Process.run("mv", ["integration_test/env/temp-persist", "integration_test/env/temp"]);
} else if (tags.any((t) => t.name == "@env:aliceandbob1")) {
await Process.run("cp", ["-R", "integration_test/env/aliceandbob1", "integration_test/env/temp"]);
} else if (!(tags.any((t) => t.name == "@env:clean"))) {
// use the default environment if no @env: tag specified
await Process.run("cp", ["-R", "integration_test/env/default", "integration_test/env/temp"]);
} else {
print("clean environment initialized");
}
return super.onBeforeScenario(config, scenario, tags);
}
@override
Future<void> onAfterScenario(TestConfiguration config, String scenario, Iterable<Tag> tags, {bool passed = true}) async {
if (tags.any((t) => t.name == "@env:persist")) {
await Process.run("mv", ["integration_test/env/temp", "integration_test/env/temp-persist"]);
}
return super.onAfterScenario(config, scenario, tags);
}
}

View File

@ -0,0 +1,34 @@
import 'package:cwtch/main.dart';
import 'package:cwtch/widgets/messagebubble.dart';
import 'package:cwtch/widgets/profilerow.dart';
import 'package:cwtch/widgets/quotedmessage.dart';
import 'package:cwtch/widgets/tor_icon.dart';
import 'package:cwtch/views/profilemgrview.dart';
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:flutter_gherkin/src/flutter/parameters/existence_parameter.dart';
import 'package:flutter_gherkin/src/flutter/parameters/swipe_direction_parameter.dart';
import 'package:gherkin/gherkin.dart';
import 'package:flutter/material.dart';
import 'overrides.dart';
StepDefinitionGeneric ExpectReply() {
return given3<String, String, int, FlutterWorld>(
RegExp(r'I expect to see the message {string} replying to {string} within {int} second(s)$'),
(originalMessage, responseMessage, seconds, context) async {
await context.world.appDriver.waitUntil(
() async {
await context.world.appDriver.waitForAppToSettle();
return await context.world.appDriver.isPresent(
context.world.appDriver.findByDescendant(context.world.appDriver.findBy(QuotedMessageBubble, FindType.type), context.world.appDriver.findBy(originalMessage, FindType.text))) &&
await context.world.appDriver.isPresent(
context.world.appDriver.findByDescendant(context.world.appDriver.findBy(QuotedMessageBubble, FindType.type), context.world.appDriver.findBy(responseMessage, FindType.text)));
},
timeout: Duration(seconds: seconds),
);
},
configuration: StepDefinitionConfiguration()..timeout = const Duration(days: 1),
);
}

View File

@ -0,0 +1,21 @@
import 'dart:io';
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:gherkin/gherkin.dart';
StepDefinitionGeneric FolderExists() {
return then1<String, FlutterWorld>(
RegExp(r'I expect the folder {string} to exist'),
(input1, context) async {
context.expect(Directory(input1).existsSync(), true);
},
);
}
StepDefinitionGeneric FileExists() {
return then1<String, FlutterWorld>(
RegExp(r'I expect the file {string} to exist'),
(input1, context) async {
context.expect(File(input1).existsSync(), true);
},
);
}

View File

@ -0,0 +1,78 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_driver/flutter_driver.dart';
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:gherkin/gherkin.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
enum SwitchState { checked, unchecked }
class SwitchStateParameter extends CustomParameter<SwitchState> {
SwitchStateParameter()
: super("toggle", RegExp(r"(checked|unchecked)", caseSensitive: false), (s) {
switch (s.toLowerCase()) {
case "checked":
return SwitchState.checked;
case "unchecked":
return SwitchState.unchecked;
}
});
}
class CheckSwitchState extends Given2WithWorld<String, SwitchState, FlutterWorld> {
@override
Future<void> executeStep(String input1, SwitchState state) async {
final switch1 = world.appDriver.findBy(input1, FindType.key);
bool switch1exists = await world.appDriver.isPresent(switch1);
expect(switch1exists, true);
if (switch1exists) {
SwitchListTile wdgt = await world.appDriver.widget(switch1);
expect(wdgt.value, state == SwitchState.checked);
}
}
@override
RegExp get pattern => RegExp(r"I expect the {string} widget to be {toggle}");
}
StepDefinitionGeneric CheckSwitchStateWithText() {
return then2<String, SwitchState, FlutterWorld>(
RegExp(r'I expect the switch that contains the text {string} to be {toggle}'),
(input1, state, context) async {
final textFinder = context.world.appDriver.findBy(input1, FindType.text);
await context.world.appDriver.scrollIntoView(textFinder);
final switchTypeFinder = context.world.appDriver.findBy(SwitchListTile, FindType.type);
final switchFinder = context.world.appDriver.findByAncestor(textFinder, switchTypeFinder);
SwitchListTile switchWidget = await context.world.appDriver.widget(switchFinder);
context.expect(switchWidget.value, state == SwitchState.checked);
},
);
}
StepDefinitionGeneric DropdownChoose() {
return then2<int, String, FlutterWorld>(
RegExp(r'I choose option {int} from the {string} dropdown'),
(idx, input1, context) async {
await context.world.appDriver.waitForAppToSettle();
final ddFinder = context.world.appDriver.findBy(input1, FindType.key);
await context.world.appDriver.scrollIntoView(ddFinder);
await context.world.appDriver.waitForAppToSettle();
await context.world.appDriver.tap(ddFinder);
await context.world.appDriver.waitForAppToSettle();
// somewhat complicated due to widget structure... we need to:
// find [ancestor of type DropdownMenuItem] of [[Text with value <text of element #idx>] contained within Dropdown]
DropdownButton ddWidget = await context.world.appDriver.widget(ddFinder);
DropdownMenuItem itemWidget = ddWidget.items!.elementAt(idx);
final itemText = (itemWidget.child as Text).data.toString();
final textFinder = context.world.appDriver.findBy(itemText, FindType.text);
final textWithinFinder = context.world.appDriver.findByDescendant(ddFinder, textFinder);
final ddiFinder = context.world.appDriver.findBy(DropdownMenuItem<String>, FindType.type);
//final ddiFinder = context.world.appDriver.findBy(_MenuItem, FindType.type);
final itemFinder = context.world.appDriver.findByAncestor(textWithinFinder, ddiFinder, firstMatchOnly: true);
await context.world.appDriver.tap(itemFinder);
await context.world.appDriver.waitForAppToSettle();
},
);
}

View File

@ -0,0 +1,267 @@
// this file contains steps from flutter_gherkin with bugfixes/adaptations to our codebase
import 'package:cwtch/main.dart';
import 'package:cwtch/widgets/messagebubble.dart';
import 'package:cwtch/widgets/profilerow.dart';
import 'package:cwtch/widgets/tor_icon.dart';
import 'package:cwtch/views/profilemgrview.dart';
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:flutter_gherkin/src/flutter/parameters/existence_parameter.dart';
import 'package:flutter_gherkin/src/flutter/parameters/swipe_direction_parameter.dart';
import 'package:gherkin/gherkin.dart';
import 'package:flutter/material.dart';
StepDefinitionGeneric TapWidgetWithType() {
return given1<String, FlutterWorld>(
RegExp(r'I tap the (?:button|element|label|icon|field|text|widget) with type {string}$'),
(input1, context) async {
await context.world.appDriver.tap(
context.world.appDriver.findBy(
widgetTypeByName(input1),
FindType.type,
),
);
await context.world.appDriver.waitForAppToSettle();
},
);
}
StepDefinitionGeneric TapWidgetWithLabel() {
return given2<String, String, FlutterWorld>(
RegExp(r'I tap the {string} widget with label {string}$'),
(ofType, text, context) async {
final finder =
context.world.appDriver.findByDescendant(context.world.appDriver.findBy(widgetTypeByName(ofType), FindType.type), context.world.appDriver.findBy(text, FindType.text), firstMatchOnly: true);
//Text wdg = await context.world.appDriver.widget(finder, ExpectedWidgetResultType.first);
//print(wdg.debugDescribeChildren().first.)
await context.world.appDriver.scrollIntoView(finder);
await context.world.appDriver.tap(finder);
await context.world.appDriver.waitForAppToSettle();
},
);
}
StepDefinitionGeneric TapWidgetWithTooltip() {
return given2<String, String, FlutterWorld>(
RegExp(r'I tap the {string} widget with tooltip {string}$'),
(ofType, text, context) async {
final finder = context.world.appDriver
.findByDescendant(context.world.appDriver.findBy(widgetTypeByName(ofType), FindType.type), context.world.appDriver.findBy(text, FindType.tooltip), firstMatchOnly: true);
await context.world.appDriver.scrollIntoView(finder);
await context.world.appDriver.tap(finder);
await context.world.appDriver.waitForAppToSettle();
},
);
}
StepDefinitionGeneric ExpectWidgetWithText() {
return given2<String, String, FlutterWorld>(
RegExp(r'I expect a {string} widget with text {string}$'),
(ofType, text, context) async {
final finder =
context.world.appDriver.findByDescendant(context.world.appDriver.findBy(widgetTypeByName(ofType), FindType.type), context.world.appDriver.findBy(text, FindType.text), firstMatchOnly: true);
//Text wdg = await context.world.appDriver.widget(finder, ExpectedWidgetResultType.first);
//print(wdg.debugDescribeChildren().first.)
await context.world.appDriver.isPresent(finder);
await context.world.appDriver.waitForAppToSettle();
},
);
}
StepDefinitionGeneric AbsentWidgetWithText() {
return given2<String, String, FlutterWorld>(
RegExp(r'I expect a {string} widget with text {string} to be absent$'),
(ofType, text, context) async {
final finder =
context.world.appDriver.findByDescendant(context.world.appDriver.findBy(widgetTypeByName(ofType), FindType.type), context.world.appDriver.findBy(text, FindType.text), firstMatchOnly: true);
//Text wdg = await context.world.appDriver.widget(finder, ExpectedWidgetResultType.first);
//print(wdg.debugDescribeChildren().first.)
await context.world.appDriver.isAbsent(finder);
await context.world.appDriver.waitForAppToSettle();
},
);
}
StepDefinitionGeneric TapButtonWithText() {
return given1<String, FlutterWorld>(
RegExp(r'I tap the {string} (?:button|element|label|icon|field|text|widget)$'),
(input1, context) async {
final finder = context.world.appDriver.findByDescendant(context.world.appDriver.findBy(Flwtch, FindType.type), context.world.appDriver.findBy(input1, FindType.key), firstMatchOnly: true);
await context.world.appDriver.scrollIntoView(finder);
await context.world.appDriver.tap(finder);
await context.world.appDriver.waitForAppToSettle();
},
);
}
StepDefinitionGeneric WaitUntilTypeExists() {
return then2<String, Existence, FlutterWorld>(
'I wait until the (?:button|element|label|icon|field|text|widget) with type {string} is {existence}',
(ofType, existence, context) async {
await context.world.appDriver.waitUntil(
() async {
await context.world.appDriver.waitForAppToSettle();
return existence == Existence.absent
? context.world.appDriver.isAbsent(
context.world.appDriver.findBy(widgetTypeByName(ofType), FindType.type),
)
: context.world.appDriver.isPresent(
context.world.appDriver.findBy(widgetTypeByName(ofType), FindType.type),
);
},
);
},
);
}
StepDefinitionGeneric ExpectTextToBePresent() {
return given2<String, int, FlutterWorld>(
RegExp(r'I expect the string {string} to be present within {int} second(s)$'),
(key, seconds, context) async {
await context.world.appDriver.waitUntil(
() async {
await context.world.appDriver.waitForAppToSettle();
return context.world.appDriver.isPresent(
context.world.appDriver.findBy(key, FindType.text),
);
},
timeout: Duration(seconds: seconds),
);
},
configuration: StepDefinitionConfiguration()..timeout = const Duration(days: 1),
);
}
StepDefinitionGeneric ExpectWidgetWithTextWithin() {
return given3<String, String, int, FlutterWorld>(
RegExp(r'I expect a {string} widget with text {string} to be present within {int} second(s)$'),
(widgetType, text, seconds, context) async {
await () async {
var result = false;
while (!result) {
await context.world.appDriver.waitForAppToSettle();
result = await context.world.appDriver.isPresent(
context.world.appDriver.findByDescendant(context.world.appDriver.findBy(widgetTypeByName(widgetType), FindType.type), context.world.appDriver.findBy(text, FindType.text)),
);
}
}()
.timeout(Duration(seconds: 120));
},
configuration: StepDefinitionConfiguration()..timeout = const Duration(days: 1),
);
}
StepDefinitionGeneric WaitUntilTextExists() {
return then2<String, Existence, FlutterWorld>(
'I wait until the text {string} is {existence}',
(text, existence, context) async {
await () async {
var result = false;
while (!result) {
await context.world.appDriver.waitForAppToSettle();
result = await (existence == Existence.absent
? context.world.appDriver.isAbsent(
context.world.appDriver.findBy(text, FindType.text),
)
: context.world.appDriver.isPresent(
context.world.appDriver.findBy(text, FindType.text),
));
}
}()
.timeout(Duration(seconds: 120));
},
configuration: StepDefinitionConfiguration()..timeout = const Duration(days: 1),
);
}
StepDefinitionGeneric WaitUntilTooltipExists() {
return then2<String, Existence, FlutterWorld>(
'I wait until the tooltip {string} is {existence}',
(ofType, existence, context) async {
await context.world.appDriver.waitUntil(
() async {
await context.world.appDriver.waitForAppToSettle();
return existence == Existence.absent
? context.world.appDriver.isAbsent(
context.world.appDriver.findBy(ofType, FindType.tooltip),
)
: context.world.appDriver.isPresent(
context.world.appDriver.findBy(ofType, FindType.tooltip),
);
},
timeout: Duration(seconds: 120),
);
},
configuration: StepDefinitionConfiguration()..timeout = const Duration(days: 1),
);
}
mixin _SwipeHelper on When4WithWorld<SwipeDirection, int, String, String, FlutterWorld> {
Future<void> swipeOnFinder(
dynamic finder,
SwipeDirection direction,
int swipeAmount,
) async {
if (direction == SwipeDirection.left || direction == SwipeDirection.right) {
final offset = direction == SwipeDirection.right ? swipeAmount : (swipeAmount * -1);
await world.appDriver.scroll(
finder,
dx: offset.toDouble(),
duration: Duration(milliseconds: 500),
timeout: timeout,
);
} else {
final offset = direction == SwipeDirection.up ? swipeAmount : (swipeAmount * -1);
await world.appDriver.scroll(
finder,
dy: offset.toDouble(),
duration: Duration(milliseconds: 500),
timeout: timeout,
);
}
}
}
class SwipeOnType extends When4WithWorld<SwipeDirection, int, String, String, FlutterWorld> with _SwipeHelper {
@override
Future<void> executeStep(
SwipeDirection direction,
int swipeAmount,
String typeOf,
String text,
) async {
final finder = this.world.appDriver.findByDescendant(this.world.appDriver.findBy(widgetTypeByName(typeOf), FindType.type), this.world.appDriver.findBy(text, FindType.text));
await swipeOnFinder(finder, direction, swipeAmount);
}
@override
RegExp get pattern => RegExp(r'I swipe {swipe_direction} by {int} pixels on the widget of type {string} with text {string}');
}
Type widgetTypeByName(String input1) {
switch (input1) {
case "MessageBubble":
return MessageBubble;
case "ProfileMgrView":
return ProfileMgrView;
case "ProfileRow":
return ProfileRow;
case "TorIcon":
return TorIcon;
case "button":
return ElevatedButton;
case "IconButton":
return IconButton;
case "ProfileRow":
return ProfileRow;
default:
throw ("Unknown type $input1. add to integration_test/features/overrides.dart");
}
}

View File

@ -0,0 +1,47 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:gherkin/gherkin.dart';
StepDefinitionGeneric TooltipTap() {
return given1<String, FlutterWorld>(RegExp(r'I tap the button with tooltip {string}'), (input1, context) async {
final finder = context.world.appDriver.findBy(input1, FindType.tooltip);
await context.world.appDriver.tap(finder);
await context.world.appDriver.waitForAppToSettle();
});
}
StepDefinitionGeneric TorVersionPresent() {
return given<FlutterWorld>(
RegExp(r'I expect the Tor version to be present$'),
(context) async {
String versionString = "";
final file = File('fetch-tor.sh');
Stream<String> lines = file.openRead().transform(utf8.decoder).transform(LineSplitter());
try {
await for (var line in lines) {
if (line.startsWith("wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-")) {
versionString = line.substring(81, 88);
break;
}
}
print('File is now closed.');
} catch (e) {
print('Error: $e');
}
if (versionString == "") {
context.expect(versionString, "#.#.#", reason: "error reading version string from fetch-tor.sh");
return;
}
context.world.attach(versionString, "text/plain", "Then I expect the Tor version to be present");
//context.reporter.message("test!!!", MessageLevel.info);
print("looking for version string $versionString");
final finder = context.world.appDriver.findBy(
versionString,
FindType.text,
);
final isP = await context.world.appDriver.isPresent(finder);
context.expect(isP, true);
},
);
}

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