diff --git a/src/tests/mock/__init__.py b/src/tests/mock/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/tests/mock/bitmessagemock.py b/src/tests/mock/bitmessagemock.py new file mode 100644 index 00000000..ee707e92 --- /dev/null +++ b/src/tests/mock/bitmessagemock.py @@ -0,0 +1,38 @@ +""" +Bitmessage mock +""" +from pybitmessage.class_addressGenerator import addressGenerator +from pybitmessage.inventory import Inventory +from pybitmessage.bmconfigparser import BMConfigParser +from pybitmessage import state + + +class MockMain: + """Mock main function""" + + def start(self): + """Start main application""" + # pylint: disable=too-many-statements,too-many-branches,too-many-locals, unused-variable + config = BMConfigParser() + daemon = config.safeGetBoolean('bitmessagesettings', 'daemon') + # Start the address generation thread + addressGeneratorThread = addressGenerator() + # close the main program even if there are threads left + addressGeneratorThread.daemon = True + addressGeneratorThread.start() + Inventory() + from pybitmessage.mpybit import NavigateApp + state.kivyapp = NavigateApp() + state.kivyapp.run() + + + + +def main(): + """Triggers main module""" + mainprogram = MockMain() + mainprogram.start() + + +if __name__ == "__main__": + main() diff --git a/src/tests/mock/images/kivy/3.zip b/src/tests/mock/images/kivy/3.zip new file mode 100644 index 00000000..34d555fe Binary files /dev/null and b/src/tests/mock/images/kivy/3.zip differ diff --git a/src/tests/mock/images/kivy/account_multiple.png b/src/tests/mock/images/kivy/account_multiple.png new file mode 100644 index 00000000..11271619 Binary files /dev/null and b/src/tests/mock/images/kivy/account_multiple.png differ diff --git a/src/tests/mock/images/kivy/addressbook.png b/src/tests/mock/images/kivy/addressbook.png new file mode 100644 index 00000000..69424134 Binary files /dev/null and b/src/tests/mock/images/kivy/addressbook.png differ diff --git a/src/tests/mock/images/kivy/addressbookadd.png b/src/tests/mock/images/kivy/addressbookadd.png new file mode 100644 index 00000000..82af2f63 Binary files /dev/null and b/src/tests/mock/images/kivy/addressbookadd.png differ diff --git a/src/tests/mock/images/kivy/avatar.png b/src/tests/mock/images/kivy/avatar.png new file mode 100644 index 00000000..b006bfa2 Binary files /dev/null and b/src/tests/mock/images/kivy/avatar.png differ diff --git a/src/tests/mock/images/kivy/back-button.png b/src/tests/mock/images/kivy/back-button.png new file mode 100644 index 00000000..e260b08b Binary files /dev/null and b/src/tests/mock/images/kivy/back-button.png differ diff --git a/src/tests/mock/images/kivy/bitmessage.icns b/src/tests/mock/images/kivy/bitmessage.icns new file mode 100644 index 00000000..5fe52d4e Binary files /dev/null and b/src/tests/mock/images/kivy/bitmessage.icns differ diff --git a/src/tests/mock/images/kivy/black_cross.png b/src/tests/mock/images/kivy/black_cross.png new file mode 100644 index 00000000..a71b611b Binary files /dev/null and b/src/tests/mock/images/kivy/black_cross.png differ diff --git a/src/tests/mock/images/kivy/blacklist.png b/src/tests/mock/images/kivy/blacklist.png new file mode 100644 index 00000000..820065a4 Binary files /dev/null and b/src/tests/mock/images/kivy/blacklist.png differ diff --git a/src/tests/mock/images/kivy/blue-plus-icon-12.png b/src/tests/mock/images/kivy/blue-plus-icon-12.png new file mode 100644 index 00000000..f9007861 Binary files /dev/null and b/src/tests/mock/images/kivy/blue-plus-icon-12.png differ diff --git a/src/tests/mock/images/kivy/can-icon-16px.png b/src/tests/mock/images/kivy/can-icon-16px.png new file mode 100644 index 00000000..c649919f Binary files /dev/null and b/src/tests/mock/images/kivy/can-icon-16px.png differ diff --git a/src/tests/mock/images/kivy/can-icon-24px-green.png b/src/tests/mock/images/kivy/can-icon-24px-green.png new file mode 100644 index 00000000..652da9c0 Binary files /dev/null and b/src/tests/mock/images/kivy/can-icon-24px-green.png differ diff --git a/src/tests/mock/images/kivy/can-icon-24px-red.png b/src/tests/mock/images/kivy/can-icon-24px-red.png new file mode 100644 index 00000000..cf66fcbb Binary files /dev/null and b/src/tests/mock/images/kivy/can-icon-24px-red.png differ diff --git a/src/tests/mock/images/kivy/can-icon-24px-yellow.png b/src/tests/mock/images/kivy/can-icon-24px-yellow.png new file mode 100644 index 00000000..0a26470b Binary files /dev/null and b/src/tests/mock/images/kivy/can-icon-24px-yellow.png differ diff --git a/src/tests/mock/images/kivy/can-icon-24px.png b/src/tests/mock/images/kivy/can-icon-24px.png new file mode 100644 index 00000000..30f7313e Binary files /dev/null and b/src/tests/mock/images/kivy/can-icon-24px.png differ diff --git a/src/tests/mock/images/kivy/can-icon.ico b/src/tests/mock/images/kivy/can-icon.ico new file mode 100644 index 00000000..903bf3da Binary files /dev/null and b/src/tests/mock/images/kivy/can-icon.ico differ diff --git a/src/tests/mock/images/kivy/copy_text.png b/src/tests/mock/images/kivy/copy_text.png new file mode 100644 index 00000000..dcc63611 Binary files /dev/null and b/src/tests/mock/images/kivy/copy_text.png differ diff --git a/src/tests/mock/images/kivy/down-arrow.png b/src/tests/mock/images/kivy/down-arrow.png new file mode 100644 index 00000000..bf3e864c Binary files /dev/null and b/src/tests/mock/images/kivy/down-arrow.png differ diff --git a/src/tests/mock/images/kivy/drawer_logo1.png b/src/tests/mock/images/kivy/drawer_logo1.png new file mode 100644 index 00000000..4152cc40 Binary files /dev/null and b/src/tests/mock/images/kivy/drawer_logo1.png differ diff --git a/src/tests/mock/images/kivy/greenicon.png b/src/tests/mock/images/kivy/greenicon.png new file mode 100644 index 00000000..cedf9dc2 Binary files /dev/null and b/src/tests/mock/images/kivy/greenicon.png differ diff --git a/src/tests/mock/images/kivy/identities.png b/src/tests/mock/images/kivy/identities.png new file mode 100644 index 00000000..a6aae040 Binary files /dev/null and b/src/tests/mock/images/kivy/identities.png differ diff --git a/src/tests/mock/images/kivy/inbox.png b/src/tests/mock/images/kivy/inbox.png new file mode 100644 index 00000000..dc6a6f75 Binary files /dev/null and b/src/tests/mock/images/kivy/inbox.png differ diff --git a/src/tests/mock/images/kivy/kivymd_logo.png b/src/tests/mock/images/kivy/kivymd_logo.png new file mode 100644 index 00000000..ce39b0d4 Binary files /dev/null and b/src/tests/mock/images/kivy/kivymd_logo.png differ diff --git a/src/tests/mock/images/kivy/left_arrow.png b/src/tests/mock/images/kivy/left_arrow.png new file mode 100644 index 00000000..0f01c425 Binary files /dev/null and b/src/tests/mock/images/kivy/left_arrow.png differ diff --git a/src/tests/mock/images/kivy/loader.gif b/src/tests/mock/images/kivy/loader.gif new file mode 100644 index 00000000..29064d57 Binary files /dev/null and b/src/tests/mock/images/kivy/loader.gif differ diff --git a/src/tests/mock/images/kivy/loader.zip b/src/tests/mock/images/kivy/loader.zip new file mode 100644 index 00000000..c80a3a17 Binary files /dev/null and b/src/tests/mock/images/kivy/loader.zip differ diff --git a/src/tests/mock/images/kivy/me.jpg b/src/tests/mock/images/kivy/me.jpg new file mode 100644 index 00000000..f54c791f Binary files /dev/null and b/src/tests/mock/images/kivy/me.jpg differ diff --git a/src/tests/mock/images/kivy/networkstatus.png b/src/tests/mock/images/kivy/networkstatus.png new file mode 100644 index 00000000..b702ee85 Binary files /dev/null and b/src/tests/mock/images/kivy/networkstatus.png differ diff --git a/src/tests/mock/images/kivy/no_identicons.png b/src/tests/mock/images/kivy/no_identicons.png new file mode 100644 index 00000000..513f1d10 Binary files /dev/null and b/src/tests/mock/images/kivy/no_identicons.png differ diff --git a/src/tests/mock/images/kivy/payment/btc.png b/src/tests/mock/images/kivy/payment/btc.png new file mode 100644 index 00000000..33302ff8 Binary files /dev/null and b/src/tests/mock/images/kivy/payment/btc.png differ diff --git a/src/tests/mock/images/kivy/payment/buy.png b/src/tests/mock/images/kivy/payment/buy.png new file mode 100644 index 00000000..3a63af11 Binary files /dev/null and b/src/tests/mock/images/kivy/payment/buy.png differ diff --git a/src/tests/mock/images/kivy/payment/buynew.png b/src/tests/mock/images/kivy/payment/buynew.png new file mode 100644 index 00000000..852e57b5 Binary files /dev/null and b/src/tests/mock/images/kivy/payment/buynew.png differ diff --git a/src/tests/mock/images/kivy/payment/buynew1.png b/src/tests/mock/images/kivy/payment/buynew1.png new file mode 100644 index 00000000..f02090f8 Binary files /dev/null and b/src/tests/mock/images/kivy/payment/buynew1.png differ diff --git a/src/tests/mock/images/kivy/payment/credits.jpeg b/src/tests/mock/images/kivy/payment/credits.jpeg new file mode 100644 index 00000000..6b75132a Binary files /dev/null and b/src/tests/mock/images/kivy/payment/credits.jpeg differ diff --git a/src/tests/mock/images/kivy/payment/creditss.png b/src/tests/mock/images/kivy/payment/creditss.png new file mode 100644 index 00000000..b45cd997 Binary files /dev/null and b/src/tests/mock/images/kivy/payment/creditss.png differ diff --git a/src/tests/mock/images/kivy/payment/gpay.png b/src/tests/mock/images/kivy/payment/gpay.png new file mode 100644 index 00000000..62aa55cd Binary files /dev/null and b/src/tests/mock/images/kivy/payment/gpay.png differ diff --git a/src/tests/mock/images/kivy/payment/gplay.png b/src/tests/mock/images/kivy/payment/gplay.png new file mode 100644 index 00000000..69550edd Binary files /dev/null and b/src/tests/mock/images/kivy/payment/gplay.png differ diff --git a/src/tests/mock/images/kivy/payment/gplayfinal.png b/src/tests/mock/images/kivy/payment/gplayfinal.png new file mode 100644 index 00000000..16d0d538 Binary files /dev/null and b/src/tests/mock/images/kivy/payment/gplayfinal.png differ diff --git a/src/tests/mock/images/kivy/payment/gplayss.png b/src/tests/mock/images/kivy/payment/gplayss.png new file mode 100644 index 00000000..67f293fd Binary files /dev/null and b/src/tests/mock/images/kivy/payment/gplayss.png differ diff --git a/src/tests/mock/images/kivy/payment/paypal.png b/src/tests/mock/images/kivy/payment/paypal.png new file mode 100644 index 00000000..f994130d Binary files /dev/null and b/src/tests/mock/images/kivy/payment/paypal.png differ diff --git a/src/tests/mock/images/kivy/plus-4-xxl.png b/src/tests/mock/images/kivy/plus-4-xxl.png new file mode 100644 index 00000000..1f178267 Binary files /dev/null and b/src/tests/mock/images/kivy/plus-4-xxl.png differ diff --git a/src/tests/mock/images/kivy/plus.png b/src/tests/mock/images/kivy/plus.png new file mode 100644 index 00000000..4fd3478c Binary files /dev/null and b/src/tests/mock/images/kivy/plus.png differ diff --git a/src/tests/mock/images/kivy/qidenticon.png b/src/tests/mock/images/kivy/qidenticon.png new file mode 100644 index 00000000..21abec78 Binary files /dev/null and b/src/tests/mock/images/kivy/qidenticon.png differ diff --git a/src/tests/mock/images/kivy/qidenticon_two.png b/src/tests/mock/images/kivy/qidenticon_two.png new file mode 100644 index 00000000..35180659 Binary files /dev/null and b/src/tests/mock/images/kivy/qidenticon_two.png differ diff --git a/src/tests/mock/images/kivy/qidenticon_two_x.png b/src/tests/mock/images/kivy/qidenticon_two_x.png new file mode 100644 index 00000000..a4851205 Binary files /dev/null and b/src/tests/mock/images/kivy/qidenticon_two_x.png differ diff --git a/src/tests/mock/images/kivy/qidenticon_x.png b/src/tests/mock/images/kivy/qidenticon_x.png new file mode 100644 index 00000000..07e903e5 Binary files /dev/null and b/src/tests/mock/images/kivy/qidenticon_x.png differ diff --git a/src/tests/mock/images/kivy/red.png b/src/tests/mock/images/kivy/red.png new file mode 100644 index 00000000..2570e26a Binary files /dev/null and b/src/tests/mock/images/kivy/red.png differ diff --git a/src/tests/mock/images/kivy/redicon.png b/src/tests/mock/images/kivy/redicon.png new file mode 100644 index 00000000..dfc84b47 Binary files /dev/null and b/src/tests/mock/images/kivy/redicon.png differ diff --git a/src/tests/mock/images/kivy/right-arrow.png b/src/tests/mock/images/kivy/right-arrow.png new file mode 100644 index 00000000..8f136a77 Binary files /dev/null and b/src/tests/mock/images/kivy/right-arrow.png differ diff --git a/src/tests/mock/images/kivy/search.png b/src/tests/mock/images/kivy/search.png new file mode 100644 index 00000000..42a1e45a Binary files /dev/null and b/src/tests/mock/images/kivy/search.png differ diff --git a/src/tests/mock/images/kivy/search_mail.png b/src/tests/mock/images/kivy/search_mail.png new file mode 100644 index 00000000..7b38e9b4 Binary files /dev/null and b/src/tests/mock/images/kivy/search_mail.png differ diff --git a/src/tests/mock/images/kivy/send.png b/src/tests/mock/images/kivy/send.png new file mode 100644 index 00000000..6a7c8302 Binary files /dev/null and b/src/tests/mock/images/kivy/send.png differ diff --git a/src/tests/mock/images/kivy/sent.png b/src/tests/mock/images/kivy/sent.png new file mode 100644 index 00000000..01b5f0dd Binary files /dev/null and b/src/tests/mock/images/kivy/sent.png differ diff --git a/src/tests/mock/images/kivy/subscriptions.png b/src/tests/mock/images/kivy/subscriptions.png new file mode 100644 index 00000000..e4561208 Binary files /dev/null and b/src/tests/mock/images/kivy/subscriptions.png differ diff --git a/src/tests/mock/images/kivy/text_images/!.png b/src/tests/mock/images/kivy/text_images/!.png new file mode 100644 index 00000000..bac2f246 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/!.png differ diff --git a/src/tests/mock/images/kivy/text_images/0.png b/src/tests/mock/images/kivy/text_images/0.png new file mode 100644 index 00000000..2b8b63e3 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/0.png differ diff --git a/src/tests/mock/images/kivy/text_images/1.png b/src/tests/mock/images/kivy/text_images/1.png new file mode 100644 index 00000000..3918f6d3 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/1.png differ diff --git a/src/tests/mock/images/kivy/text_images/2.png b/src/tests/mock/images/kivy/text_images/2.png new file mode 100644 index 00000000..0cf202e9 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/2.png differ diff --git a/src/tests/mock/images/kivy/text_images/3.png b/src/tests/mock/images/kivy/text_images/3.png new file mode 100644 index 00000000..f9d612dd Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/3.png differ diff --git a/src/tests/mock/images/kivy/text_images/4.png b/src/tests/mock/images/kivy/text_images/4.png new file mode 100644 index 00000000..f2ab33e1 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/4.png differ diff --git a/src/tests/mock/images/kivy/text_images/5.png b/src/tests/mock/images/kivy/text_images/5.png new file mode 100644 index 00000000..09d6e56e Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/5.png differ diff --git a/src/tests/mock/images/kivy/text_images/6.png b/src/tests/mock/images/kivy/text_images/6.png new file mode 100644 index 00000000..e385a954 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/6.png differ diff --git a/src/tests/mock/images/kivy/text_images/7.png b/src/tests/mock/images/kivy/text_images/7.png new file mode 100644 index 00000000..55fc4f77 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/7.png differ diff --git a/src/tests/mock/images/kivy/text_images/8.png b/src/tests/mock/images/kivy/text_images/8.png new file mode 100644 index 00000000..2a3fa76f Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/8.png differ diff --git a/src/tests/mock/images/kivy/text_images/9.png b/src/tests/mock/images/kivy/text_images/9.png new file mode 100644 index 00000000..81ad9084 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/9.png differ diff --git a/src/tests/mock/images/kivy/text_images/A.png b/src/tests/mock/images/kivy/text_images/A.png new file mode 100644 index 00000000..64ed6110 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/A.png differ diff --git a/src/tests/mock/images/kivy/text_images/B.png b/src/tests/mock/images/kivy/text_images/B.png new file mode 100644 index 00000000..2db56c1f Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/B.png differ diff --git a/src/tests/mock/images/kivy/text_images/C.png b/src/tests/mock/images/kivy/text_images/C.png new file mode 100644 index 00000000..47a4052c Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/C.png differ diff --git a/src/tests/mock/images/kivy/text_images/D.png b/src/tests/mock/images/kivy/text_images/D.png new file mode 100644 index 00000000..2549ffc2 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/D.png differ diff --git a/src/tests/mock/images/kivy/text_images/E.png b/src/tests/mock/images/kivy/text_images/E.png new file mode 100644 index 00000000..5d631611 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/E.png differ diff --git a/src/tests/mock/images/kivy/text_images/F.png b/src/tests/mock/images/kivy/text_images/F.png new file mode 100644 index 00000000..43086f38 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/F.png differ diff --git a/src/tests/mock/images/kivy/text_images/G.png b/src/tests/mock/images/kivy/text_images/G.png new file mode 100644 index 00000000..32d1709d Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/G.png differ diff --git a/src/tests/mock/images/kivy/text_images/H.png b/src/tests/mock/images/kivy/text_images/H.png new file mode 100644 index 00000000..279bd1ce Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/H.png differ diff --git a/src/tests/mock/images/kivy/text_images/I.png b/src/tests/mock/images/kivy/text_images/I.png new file mode 100644 index 00000000..c88f048d Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/I.png differ diff --git a/src/tests/mock/images/kivy/text_images/J.png b/src/tests/mock/images/kivy/text_images/J.png new file mode 100644 index 00000000..15331171 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/J.png differ diff --git a/src/tests/mock/images/kivy/text_images/K.png b/src/tests/mock/images/kivy/text_images/K.png new file mode 100644 index 00000000..9afcadd7 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/K.png differ diff --git a/src/tests/mock/images/kivy/text_images/L.png b/src/tests/mock/images/kivy/text_images/L.png new file mode 100644 index 00000000..e841b9d9 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/L.png differ diff --git a/src/tests/mock/images/kivy/text_images/M.png b/src/tests/mock/images/kivy/text_images/M.png new file mode 100644 index 00000000..10de35e9 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/M.png differ diff --git a/src/tests/mock/images/kivy/text_images/N.png b/src/tests/mock/images/kivy/text_images/N.png new file mode 100644 index 00000000..2d235d06 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/N.png differ diff --git a/src/tests/mock/images/kivy/text_images/O.png b/src/tests/mock/images/kivy/text_images/O.png new file mode 100644 index 00000000..c0cc972a Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/O.png differ diff --git a/src/tests/mock/images/kivy/text_images/P.png b/src/tests/mock/images/kivy/text_images/P.png new file mode 100644 index 00000000..57ec5012 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/P.png differ diff --git a/src/tests/mock/images/kivy/text_images/Q.png b/src/tests/mock/images/kivy/text_images/Q.png new file mode 100644 index 00000000..27ffd18b Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/Q.png differ diff --git a/src/tests/mock/images/kivy/text_images/R.png b/src/tests/mock/images/kivy/text_images/R.png new file mode 100644 index 00000000..090646f5 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/R.png differ diff --git a/src/tests/mock/images/kivy/text_images/S.png b/src/tests/mock/images/kivy/text_images/S.png new file mode 100644 index 00000000..444419cf Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/S.png differ diff --git a/src/tests/mock/images/kivy/text_images/T.png b/src/tests/mock/images/kivy/text_images/T.png new file mode 100644 index 00000000..ace7b36b Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/T.png differ diff --git a/src/tests/mock/images/kivy/text_images/U.png b/src/tests/mock/images/kivy/text_images/U.png new file mode 100644 index 00000000..a47f326e Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/U.png differ diff --git a/src/tests/mock/images/kivy/text_images/V.png b/src/tests/mock/images/kivy/text_images/V.png new file mode 100644 index 00000000..da07d0ac Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/V.png differ diff --git a/src/tests/mock/images/kivy/text_images/W.png b/src/tests/mock/images/kivy/text_images/W.png new file mode 100644 index 00000000..a00f9d7c Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/W.png differ diff --git a/src/tests/mock/images/kivy/text_images/X.png b/src/tests/mock/images/kivy/text_images/X.png new file mode 100644 index 00000000..be919fc4 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/X.png differ diff --git a/src/tests/mock/images/kivy/text_images/Y.png b/src/tests/mock/images/kivy/text_images/Y.png new file mode 100644 index 00000000..4819bbd1 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/Y.png differ diff --git a/src/tests/mock/images/kivy/text_images/Z.png b/src/tests/mock/images/kivy/text_images/Z.png new file mode 100644 index 00000000..7d1c8e01 Binary files /dev/null and b/src/tests/mock/images/kivy/text_images/Z.png differ diff --git a/src/tests/mock/images/kivy/transparent.png b/src/tests/mock/images/kivy/transparent.png new file mode 100644 index 00000000..26529139 Binary files /dev/null and b/src/tests/mock/images/kivy/transparent.png differ diff --git a/src/tests/mock/images/kivy/white.png b/src/tests/mock/images/kivy/white.png new file mode 100644 index 00000000..0542f499 Binary files /dev/null and b/src/tests/mock/images/kivy/white.png differ diff --git a/src/tests/mock/images/kivy/yellowicon.png b/src/tests/mock/images/kivy/yellowicon.png new file mode 100644 index 00000000..95e451d5 Binary files /dev/null and b/src/tests/mock/images/kivy/yellowicon.png differ diff --git a/src/tests/mock/kivy_main.py b/src/tests/mock/kivy_main.py new file mode 100644 index 00000000..79bb413e --- /dev/null +++ b/src/tests/mock/kivy_main.py @@ -0,0 +1,8 @@ +"""Mock kivy app with mock threads.""" +from pybitmessage import state + +if __name__ == '__main__': + state.kivy = True + print("Kivy Loading......") + from bitmessagemock import main + main() diff --git a/src/tests/mock/pybitmessage/__init__.py b/src/tests/mock/pybitmessage/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/tests/mock/pybitmessage/addresses.py b/src/tests/mock/pybitmessage/addresses.py new file mode 100644 index 00000000..cf42bc26 --- /dev/null +++ b/src/tests/mock/pybitmessage/addresses.py @@ -0,0 +1,278 @@ +""" +Operations with addresses +""" +# pylint: disable=redefined-outer-name,inconsistent-return-statements +import hashlib +from binascii import hexlify, unhexlify +from struct import pack, unpack + +# from debug import logger + +ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + + +def encodeBase58(num, alphabet=ALPHABET): + """Encode a number in Base X + + Args: + num: The number to encode + alphabet: The alphabet to use for encoding + """ + if num == 0: + return alphabet[0] + arr = [] + base = len(alphabet) + while num: + rem = num % base + num = num // base + arr.append(alphabet[rem]) + arr.reverse() + return ''.join(arr) + + +def decodeBase58(string, alphabet=ALPHABET): + """Decode a Base X encoded string into the number + + Args: + string: The encoded string + alphabet: The alphabet to use for encoding + """ + base = len(alphabet) + num = 0 + + try: + for char in string: + num *= base + num += alphabet.index(char) + # ValueError + except: + # character not found (like a space character or a 0) + return 0 + return num + + +class varintEncodeError(Exception): + """Exception class for encoding varint""" + pass + + +class varintDecodeError(Exception): + """Exception class for decoding varint data""" + pass + + +def encodeVarint(integer): + """Convert integer into varint bytes""" + if integer < 0: + raise varintEncodeError('varint cannot be < 0') + if integer < 253: + return pack('>B', integer) + if integer >= 253 and integer < 65536: + return pack('>B', 253) + pack('>H', integer) + if integer >= 65536 and integer < 4294967296: + return pack('>B', 254) + pack('>I', integer) + if integer >= 4294967296 and integer < 18446744073709551616: + return pack('>B', 255) + pack('>Q', integer) + if integer >= 18446744073709551616: + raise varintEncodeError('varint cannot be >= 18446744073709551616') + + +def decodeVarint(data): + """ + Decodes an encoded varint to an integer and returns it. + Per protocol v3, the encoded value must be encoded with + the minimum amount of data possible or else it is malformed. + Returns a tuple: (theEncodedValue, theSizeOfTheVarintInBytes) + """ + if not data: + return (0, 0) + firstByte, = unpack('>B', data[0:1]) + if firstByte < 253: + # encodes 0 to 252 + # the 1 is the length of the varint + return (firstByte, 1) + if firstByte == 253: + # encodes 253 to 65535 + if len(data) < 3: + raise varintDecodeError( + 'The first byte of this varint as an integer is %s' + ' but the total length is only %s. It needs to be' + ' at least 3.' % (firstByte, len(data))) + encodedValue, = unpack('>H', data[1:3]) + if encodedValue < 253: + raise varintDecodeError( + 'This varint does not encode the value with the lowest' + ' possible number of bytes.') + return (encodedValue, 3) + if firstByte == 254: + # encodes 65536 to 4294967295 + if len(data) < 5: + raise varintDecodeError( + 'The first byte of this varint as an integer is %s' + ' but the total length is only %s. It needs to be' + ' at least 5.' % (firstByte, len(data))) + encodedValue, = unpack('>I', data[1:5]) + if encodedValue < 65536: + raise varintDecodeError( + 'This varint does not encode the value with the lowest' + ' possible number of bytes.') + return (encodedValue, 5) + if firstByte == 255: + # encodes 4294967296 to 18446744073709551615 + if len(data) < 9: + raise varintDecodeError( + 'The first byte of this varint as an integer is %s' + ' but the total length is only %s. It needs to be' + ' at least 9.' % (firstByte, len(data))) + encodedValue, = unpack('>Q', data[1:9]) + if encodedValue < 4294967296: + raise varintDecodeError( + 'This varint does not encode the value with the lowest' + ' possible number of bytes.') + return (encodedValue, 9) + + +def calculateInventoryHash(data): + """Calculate inventory hash from object data""" + sha = hashlib.new('sha512') + sha2 = hashlib.new('sha512') + sha.update(data) + sha2.update(sha.digest()) + return sha2.digest()[0:32] + + +def encodeAddress(version, stream, ripe): + """Convert ripe to address""" + if version >= 2 and version < 4: + if len(ripe) != 20: + raise Exception( + 'Programming error in encodeAddress: The length of' + ' a given ripe hash was not 20.' + ) + if ripe[:2] == '\x00\x00': + ripe = ripe[2:] + elif ripe[:1] == '\x00': + ripe = ripe[1:] + elif version == 4: + if len(ripe) != 20: + raise Exception( + 'Programming error in encodeAddress: The length of' + ' a given ripe hash was not 20.') + ripe = ripe.lstrip('\x00'.encode('utf-8')) + + storedBinaryData = encodeVarint(version) + encodeVarint(stream) + ripe + + # Generate the checksum + sha = hashlib.new('sha512') + sha.update(storedBinaryData) + currentHash = sha.digest() + sha = hashlib.new('sha512') + sha.update(currentHash) + checksum = sha.digest()[0:4] + + asInt = int(hexlify(storedBinaryData) + hexlify(checksum), 16) + return 'BM-' + encodeBase58(asInt) + + +def decodeAddress(address): + """ + returns (status, address version number, stream number, + data (almost certainly a ripe hash)) + """ + # pylint: disable=too-many-return-statements,too-many-statements + # pylint: disable=too-many-branches + address = str(address).strip() + + if address[:3] == 'BM-': + integer = decodeBase58(address[3:]) + else: + integer = decodeBase58(address) + if integer == 0: + status = 'invalidcharacters' + return status, 0, 0, '' + # after converting to hex, the string will be prepended + # with a 0x and appended with a L + hexdata = hex(integer)[2:] + + if len(hexdata) % 2 != 0: + hexdata = '0' + hexdata + + data = unhexlify(hexdata) + checksum = data[-4:] + + sha = hashlib.new('sha512') + sha.update(data[:-4]) + currentHash = sha.digest() + sha = hashlib.new('sha512') + sha.update(currentHash) + + if checksum != sha.digest()[0:4]: + status = 'checksumfailed' + return status, 0, 0, '' + + try: + addressVersionNumber, bytesUsedByVersionNumber = decodeVarint(data[:9]) + except varintDecodeError as e: + # logger.error(str(e)) + status = 'varintmalformed' + return status, 0, 0, '' + + if addressVersionNumber > 4: + # logger.error('cannot decode address version numbers this high') + status = 'versiontoohigh' + return status, 0, 0, '' + elif addressVersionNumber == 0: + # logger.error('cannot decode address version numbers of zero.') + status = 'versiontoohigh' + return status, 0, 0, '' + + try: + streamNumber, bytesUsedByStreamNumber = \ + decodeVarint(data[bytesUsedByVersionNumber:]) + except varintDecodeError as e: + # logger.error(str(e)) + status = 'varintmalformed' + return status, 0, 0, '' + + status = 'success' + if addressVersionNumber == 1: + return status, addressVersionNumber, streamNumber, data[-24:-4] + # elif addressVersionNumber == 2 or addressVersionNumber == 3: + elif addressVersionNumber in (2, 3): + embeddedRipeData = \ + data[bytesUsedByVersionNumber + bytesUsedByStreamNumber:-4] + if len(embeddedRipeData) == 19: + return status, addressVersionNumber, streamNumber, \ + '\x00' + embeddedRipeData + elif len(embeddedRipeData) == 20: + return status, addressVersionNumber, streamNumber, \ + embeddedRipeData + elif len(embeddedRipeData) == 18: + return status, addressVersionNumber, streamNumber, \ + '\x00\x00'.encode('utf-8') + embeddedRipeData + elif len(embeddedRipeData) < 18: + return 'ripetooshort', 0, 0, '' + elif len(embeddedRipeData) > 20: + return 'ripetoolong', 0, 0, '' + return 'otherproblem', 0, 0, '' + elif addressVersionNumber == 4: + embeddedRipeData = \ + data[bytesUsedByVersionNumber + bytesUsedByStreamNumber:-4] + if embeddedRipeData[0:1] == '\x00': + # In order to enforce address non-malleability, encoded + # RIPE data must have NULL bytes removed from the front + return 'encodingproblem', 0, 0, '' + elif len(embeddedRipeData) > 20: + return 'ripetoolong', 0, 0, '' + elif len(embeddedRipeData) < 4: + return 'ripetooshort', 0, 0, '' + x00string = '\x00'.encode('utf-8') * (20 - len(embeddedRipeData)) + + return status, addressVersionNumber, streamNumber, \ + x00string + embeddedRipeData + + +def addBMIfNotPresent(address): + """Prepend BM- to an address if it doesn't already have it""" + address = str(address).strip() + return address if address[:3] == 'BM-' else 'BM-' + address diff --git a/src/tests/mock/pybitmessage/baseclass/__init__.py b/src/tests/mock/pybitmessage/baseclass/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/tests/mock/pybitmessage/baseclass/addressbook.py b/src/tests/mock/pybitmessage/baseclass/addressbook.py new file mode 100644 index 00000000..a22893f3 --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/addressbook.py @@ -0,0 +1,138 @@ +from pybitmessage.get_platform import platform +from kivy.clock import Clock +from kivy.properties import ( + ListProperty, + StringProperty +) +from kivymd.uix.button import MDRaisedButton +from kivymd.uix.dialog import MDDialog +from kivymd.uix.label import MDLabel +from kivy.uix.screenmanager import Screen + +from pybitmessage import state + +from pybitmessage.baseclass.common import toast +from pybitmessage.baseclass.popup import AddbookDetailPopup + + +class AddressBook(Screen): + """AddressBook Screen class for kivy Ui""" + + queryreturn = ListProperty() + has_refreshed = True + address_label = StringProperty() + address = StringProperty() + + def __init__(self, *args, **kwargs): + """Getting AddressBook Details""" + super(AddressBook, self).__init__(*args, **kwargs) + self.addbook_popup = None + Clock.schedule_once(self.init_ui, 0) + + def init_ui(self, dt=0): + """Clock Schdule for method AddressBook""" + self.loadAddresslist(None, 'All', '') + print(dt) + + def loadAddresslist(self, account, where="", what=""): + """Clock Schdule for method AddressBook""" + if state.searcing_text: + self.ids.scroll_y.scroll_y = 1.0 + where = ['label', 'address'] + what = state.searcing_text + xAddress = '' + self.ids.tag_label.text = '' + if self.queryreturn: + pass + else: + content = MDLabel( + font_style='Caption', + theme_text_color='Primary', + text="No contact found!" if state.searcing_text + else "No contact found yet...... ", + halign='center', + size_hint_y=None, + valign='top') + self.ids.ml.add_widget(content) + + def check_scroll_y(self, instance, somethingelse): + """Load data on scroll""" + if self.ids.scroll_y.scroll_y <= -0.0 and self.has_refreshed: + self.ids.scroll_y.scroll_y = 0.06 + exist_addresses = len(self.ids.ml.children) + + @staticmethod + def refreshes(*args): + """Refresh the Widget""" + pass + + def addBook_detail(self, address, label, instance, *args): + """Addressbook details""" + if instance.state == 'closed': + instance.ids.delete_msg.disabled = True + if instance.open_progress == 0.0: + obj = AddbookDetailPopup() + self.address_label = obj.address_label = label + self.address = obj.address = address + width = .9 if platform == 'android' else .8 + self.addbook_popup = MDDialog( + type="custom", + size_hint=(width, .25), + content_cls=obj, + buttons=[ + MDRaisedButton( + text="Send message to", + on_release=self.send_message_to, + ), + MDRaisedButton( + text="Save", + on_release=self.update_addbook_label, + ), + MDRaisedButton( + text="Cancel", + on_release=self.close_pop, + ), + ], + ) + self.addbook_popup.auto_dismiss = False + self.addbook_popup.open() + else: + instance.ids.delete_msg.disabled = False + + def delete_address(self, address, instance, *args): + """Delete inbox mail from inbox listing""" + self.ids.ml.remove_widget(instance.parent.parent) + if self.ids.ml.children is not None: + self.ids.tag_label.text = '' + toast('Address Deleted') + + def close_pop(self, instance): + """Pop is Canceled""" + self.addbook_popup.dismiss() + toast('Canceled') + + def update_addbook_label(self, instance): + """Updating the label of address book address""" + address_list = [] + stored_labels = [labels[0] for labels in address_list] + add_dict = dict(address_list) + label = str(self.addbook_popup.content_cls.ids.add_label.text) + if label in stored_labels and self.address == add_dict[label]: + stored_labels.remove(label) + if label and label not in stored_labels: + state.kivyapp.root.ids.sc11.ids.ml.clear_widgets() + state.kivyapp.root.ids.sc11.loadAddresslist(None, 'All', '') + self.addbook_popup.dismiss() + toast('Saved') + + def send_message_to(self, instance): + """Method used to fill to_address of composer autofield""" + state.kivyapp.set_navbar_for_composer() + window_obj = state.kivyapp.root.ids + window_obj.sc3.children[1].ids.txt_input.text = self.address + window_obj.sc3.children[1].ids.ti.text = '' + window_obj.sc3.children[1].ids.btn.text = 'Select' + window_obj.sc3.children[1].ids.subject.text = '' + window_obj.sc3.children[1].ids.body.text = '' + window_obj.scr_mngr.current = 'create' + self.addbook_popup.dismiss() diff --git a/src/tests/mock/pybitmessage/baseclass/allmail.py b/src/tests/mock/pybitmessage/baseclass/allmail.py new file mode 100644 index 00000000..e389d871 --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/allmail.py @@ -0,0 +1,142 @@ +from functools import partial +from kivy.clock import Clock +from kivy.properties import ( + ListProperty, + StringProperty +) +from kivy.uix.screenmanager import Screen +from kivymd.uix.label import MDLabel + +from pybitmessage import state + +from pybitmessage.baseclass.common import ( + showLimitedCnt, toast, ThemeClsColor, + avatarImageFirstLetter, CutsomSwipeToDeleteItem, + ShowTimeHistoy +) + + +class Allmails(Screen): + """Allmails Screen for kivy Ui""" + + data = ListProperty() + has_refreshed = True + all_mails = ListProperty() + account = StringProperty() + + def __init__(self, *args, **kwargs): + """Method Parsing the address""" + super(Allmails, self).__init__(*args, **kwargs) + if state.association == '': + if state.kivyapp.variable_1: + state.association = state.kivyapp.variable_1[0] + Clock.schedule_once(self.init_ui, 0) + + def init_ui(self, dt=0): + """Clock Schdule for method all mails""" + self.loadMessagelist() + print(dt) + + def loadMessagelist(self): + """Load Inbox, Sent anf Draft list of messages""" + self.account = state.association + self.ids.tag_label.text = '' + if self.all_mails: + pass + else: + self.set_AllmailCnt('0') + content = MDLabel( + font_style='Caption', + theme_text_color='Primary', + text="yet no message for this account!!!!!!!!!!!!!", + halign='center', + size_hint_y=None, + valign='top') + self.ids.ml.add_widget(content) + + def set_AllmailCnt(self, Count): # pylint: disable=no-self-use + """This method is used to set allmails message count""" + allmailCnt_obj = state.kivyapp.root.ids.content_drawer.ids.allmail_cnt + allmailCnt_obj.ids.badge_txt.text = showLimitedCnt(int(Count)) + + def set_mdlist(self): + """This method is used to create mdList for allmaills""" + data_exist = len(self.ids.ml.children) + for item in self.all_mails: + body = item[3].decode() if isinstance(item[3], bytes) else item[3] + subject = item[2].decode() if isinstance(item[2], bytes) else item[2] + message_row = CutsomSwipeToDeleteItem( + text=item[1], + ) + + listItem = message_row.ids.content + secondary_text = (subject[:50] + '........' if len( + subject) >= 50 else ( + subject + ',' + body)[0:50] + '........').replace('\t', '').replace(' ', '') + listItem.secondary_text = secondary_text + listItem.theme_text_color = "Custom" + listItem.text_color = ThemeClsColor + img_latter = state.imageDir + '/text_images/{}.png'.format( + avatarImageFirstLetter(body.strip())) + message_row.ids.avater_img.source = img_latter + listItem.bind(on_release=partial( + self.mail_detail, item[5], item[4], message_row)) + message_row.ids.time_tag.text = str(ShowTimeHistoy(item[7])) + message_row.ids.chip_tag.text = item[4] + message_row.ids.delete_msg.bind(on_press=partial( + self.swipe_delete, item[5], item[4])) + self.ids.ml.add_widget(message_row) + updated_data = len(self.ids.ml.children) + self.has_refreshed = True if data_exist != updated_data else False + + def check_scroll_y(self, instance, somethingelse): + """Scroll fixed length""" + if self.ids.scroll_y.scroll_y <= -0.00 and self.has_refreshed: + self.ids.scroll_y.scroll_y = .06 + load_more = len(self.ids.ml.children) + + def mail_detail(self, unique_id, folder, instance, *args): + """Load sent and inbox mail details""" + if instance.state == 'closed': + instance.ids.delete_msg.disabled = True + if instance.open_progress == 0.0: + state.detailPageType = folder + state.is_allmail = True + state.mail_id = unique_id + if self.manager: + src_mng_obj = self.manager + else: + src_mng_obj = self.parent.parent + src_mng_obj.screens[11].clear_widgets() + src_mng_obj.current = 'mailDetail' + else: + instance.ids.delete_msg.disabled = False + + def swipe_delete(self, unique_id, folder, instance, *args): + """Delete inbox mail from all mail listing""" + self.ids.ml.remove_widget(instance.parent.parent) + try: + msg_count_objs = self.parent.parent.ids.content_drawer.ids + nav_lay_obj = self.parent.parent.ids + except Exception: + msg_count_objs = self.parent.parent.parent.ids.content_drawer.ids + nav_lay_obj = self.parent.parent.parent.ids + if folder == 'inbox': + msg_count_objs.inbox_cnt.ids.badge_txt.text = showLimitedCnt(int(state.inbox_count) - 1) + state.inbox_count = str(int(state.inbox_count) - 1) + nav_lay_obj.sc1.ids.ml.clear_widgets() + nav_lay_obj.sc1.loadMessagelist(state.association) + else: + msg_count_objs.send_cnt.ids.badge_txt.text = showLimitedCnt(int(state.sent_count) - 1) + state.sent_count = str(int(state.sent_count) - 1) + nav_lay_obj.sc4.ids.ml.clear_widgets() + nav_lay_obj.sc4.loadSent(state.association) + if folder != 'inbox': + msg_count_objs.allmail_cnt.ids.badge_txt.text = showLimitedCnt(int(state.all_count) - 1) + state.all_count = str(int(state.all_count) - 1) + msg_count_objs.trash_cnt.ids.badge_txt.text = showLimitedCnt(int(state.trash_count) + 1) + state.trash_count = str(int(state.trash_count) + 1) + if int(state.all_count) <= 0: + self.ids.tag_label.text = '' + nav_lay_obj.sc17.remove_widget(instance.parent.parent) + toast('Deleted') diff --git a/src/tests/mock/pybitmessage/baseclass/common.py b/src/tests/mock/pybitmessage/baseclass/common.py new file mode 100644 index 00000000..caaec99d --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/common.py @@ -0,0 +1,113 @@ +from datetime import datetime +from kivy.core.window import Window +from kivy.metrics import dp +from kivymd.uix.list import ( + ILeftBody, + IRightBodyTouch, +) +from kivy.uix.image import Image +from kivymd.uix.label import MDLabel +from kivymd.toast import kivytoast +from kivymd.uix.card import MDCardSwipe +from kivymd.uix.chip import MDChip +from kivy.properties import ( + NumericProperty, + StringProperty +) +platform = "linux" + + +ThemeClsColor = [0.12, 0.58, 0.95, 1] + + +data_screens = { + "MailDetail": { + "kv_string": "maildetail", + "Factory": "MailDetail()", + "name_screen": "mailDetail", + "object": 0, + "Import": "from pybitmessage.baseclass.maildetail import MailDetail", + }, +} + + +def chipTag(text): + """This method is used for showing chip tag""" + obj = MDChip() + # obj.size_hint = (None, None) + obj.size_hint = (0.16 if platform == "android" else 0.08, None) + obj.text = text + obj.icon = "" + obj.pos_hint = { + "center_x": 0.91 if platform == "android" else 0.94, + "center_y": 0.3 + } + obj.height = dp(18) + obj.text_color = (1, 1, 1, 1) + obj.radius = [8] + return obj + + +def toast(text): + """Method will display the toast message""" + kivytoast.toast(text) + + +def showLimitedCnt(total_msg): + """This method set the total count limit in badge_text""" + return "99+" if total_msg > 99 else str(total_msg) + + +def avatarImageFirstLetter(letter_string): + """This function is used to the first letter for the avatar image""" + try: + if letter_string[0].upper() >= 'A' and letter_string[0].upper() <= 'Z': + img_latter = letter_string[0].upper() + elif int(letter_string[0]) >= 0 and int(letter_string[0]) <= 9: + img_latter = letter_string[0] + else: + img_latter = '!' + except ValueError: + img_latter = '!' + return img_latter if img_latter else '!' + + +def ShowTimeHistoy(act_time): + """This method is used to return the message sent or receive time""" + action_time = datetime.fromtimestamp(int(act_time)) + crnt_date = datetime.now() + duration = crnt_date - action_time + display_data = ( + action_time.strftime("%d/%m/%Y") + if duration.days >= 365 + else action_time.strftime("%I:%M %p").lstrip("0") + if duration.days == 0 and crnt_date.strftime("%d/%m/%Y") == action_time.strftime("%d/%m/%Y") + else action_time.strftime("%d %b") + ) + return display_data + + +# pylint: disable=too-few-public-methods +class AvatarSampleWidget(ILeftBody, Image): + """AvatarSampleWidget class for kivy Ui""" + + +class TimeTagRightSampleWidget(IRightBodyTouch, MDLabel): + """TimeTagRightSampleWidget class for Ui""" + + +class SwipeToDeleteItem(MDCardSwipe): + """Swipe delete class for App UI""" + text = StringProperty() + cla = Window.size[0] / 2 + # cla = 800 + swipe_distance = NumericProperty(cla) + opening_time = NumericProperty(0.5) + + +class CutsomSwipeToDeleteItem(MDCardSwipe): + """Custom swipe delete class for App UI""" + text = StringProperty() + cla = Window.size[0] / 2 + swipe_distance = NumericProperty(cla) + opening_time = NumericProperty(0.5) diff --git a/src/tests/mock/pybitmessage/baseclass/draft.py b/src/tests/mock/pybitmessage/baseclass/draft.py new file mode 100644 index 00000000..1c037722 --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/draft.py @@ -0,0 +1,158 @@ +from pybitmessage.bmconfigparser import BMConfigParser +from functools import partial +from pybitmessage.addresses import decodeAddress +from kivy.clock import Clock +from kivy.properties import ( + ListProperty, + StringProperty +) +from kivy.uix.screenmanager import Screen +from kivymd.uix.label import MDLabel + +from pybitmessage import state + +from pybitmessage.baseclass.common import ( + showLimitedCnt, toast, ThemeClsColor, + SwipeToDeleteItem, ShowTimeHistoy +) + + +class Draft(Screen): + """Draft screen class for kivy Ui""" + + data = ListProperty() + account = StringProperty() + queryreturn = ListProperty() + has_refreshed = True + + def __init__(self, *args, **kwargs): + """Method used for storing draft messages""" + super(Draft, self).__init__(*args, **kwargs) + if state.association == '': + if state.kivyapp.variable_1: + state.association = state.kivyapp.variable_1[0] + Clock.schedule_once(self.init_ui, 0) + + def init_ui(self, dt=0): + """Clock Schdule for method draft accounts""" + self.sentaccounts() + print(dt) + + def sentaccounts(self): + """Load draft accounts""" + # self.account = state.association + self.loadDraft() + + def loadDraft(self, where="", what=""): + """Load draft list for Draft messages""" + self.account = state.association + xAddress = 'fromaddress' + self.ids.tag_label.text = '' + if self.queryreturn: + self.ids.tag_label.text = 'Draft' + self.set_draftCnt(state.draft_count) + self.set_mdList() + self.ids.scroll_y.bind(scroll_y=self.check_scroll_y) + else: + self.set_draftCnt('0') + content = MDLabel( + font_style='Caption', + theme_text_color='Primary', + text="yet no message for this account!!!!!!!!!!!!!", + halign='center', + size_hint_y=None, + valign='top') + self.ids.ml.add_widget(content) + + def set_draftCnt(self, Count): # pylint: disable=no-self-use + """This method set the count of draft mails""" + draftCnt_obj = state.kivyapp.root.ids.content_drawer.ids.draft_cnt + draftCnt_obj.ids.badge_txt.text = showLimitedCnt(int(Count)) + + def set_mdList(self): + """This method is used to create mdlist""" + data = [] + total_draft_msg = len(self.ids.ml.children) + for mail in self.queryreturn: + third_text = mail[3].replace('\n', ' ') + data.append({ + 'text': mail[1].strip(), + 'secondary_text': mail[2][:10] + '...........' if len( + mail[2]) > 10 else mail[2] + '\n' + " " + ( + third_text[:25] + '...!') if len( + third_text) > 25 else third_text, + 'ackdata': mail[5], 'senttime': mail[6]}) + for item in data: + message_row = SwipeToDeleteItem( + text='Draft', + ) + listItem = message_row.ids.content + listItem.secondary_text = item["text"] + listItem.theme_text_color = "Custom" + listItem.text_color = ThemeClsColor + message_row.ids.avater_img.source = state.imageDir + '/avatar.png' + listItem.bind(on_release=partial( + self.draft_detail, item['ackdata'], message_row)) + message_row.ids.time_tag.text = str(ShowTimeHistoy(item['senttime'])) + message_row.ids.delete_msg.bind(on_press=partial(self.delete_draft, item['ackdata'])) + self.ids.ml.add_widget(message_row) + updated_msg = len(self.ids.ml.children) + self.has_refreshed = True if total_draft_msg != updated_msg else False + + def check_scroll_y(self, instance, somethingelse): + """Load data on scroll""" + if self.ids.scroll_y.scroll_y <= -0.0 and self.has_refreshed: + self.ids.scroll_y.scroll_y = 0.06 + total_draft_msg = len(self.ids.ml.children) + + def draft_detail(self, ackdata, instance, *args): + """Show draft Details""" + if instance.state == 'closed': + instance.ids.delete_msg.disabled = True + if instance.open_progress == 0.0: + state.detailPageType = 'draft' + state.mail_id = ackdata + if self.manager: + src_mng_obj = self.manager + else: + src_mng_obj = self.parent.parent + src_mng_obj.screens[11].clear_widgets() + # src_mng_obj.screens[11].add_widget(MailDetail()) + src_mng_obj.current = 'mailDetail' + else: + instance.ids.delete_msg.disabled = False + + def delete_draft(self, data_index, instance, *args): + """Delete draft message permanently""" + if int(state.draft_count) > 0: + state.draft_count = str(int(state.draft_count) - 1) + self.set_draftCnt(state.draft_count) + if int(state.draft_count) <= 0: + # self.ids.identi_tag.children[0].text = '' + self.ids.tag_label.text = '' + self.ids.ml.remove_widget(instance.parent.parent) + toast('Deleted') + + @staticmethod + def draft_msg(src_object): + """Save draft mails""" + composer_object = state.kivyapp.root.ids.sc3.children[1].ids + fromAddress = str(composer_object.ti.text) + toAddress = str(composer_object.txt_input.text) + subject = str(composer_object.subject.text) + message = str(composer_object.body.text) + encoding = 3 + sendMessageToPeople = True + if sendMessageToPeople: + streamNumber, ripe = decodeAddress(toAddress)[2:] + from pybitmessage.addresses import addBMIfNotPresent + toAddress = addBMIfNotPresent(toAddress) + stealthLevel = BMConfigParser().safeGetInt( + 'bitmessagesettings', 'ackstealthlevel') + state.msg_counter_objs = src_object.children[2].children[0].ids + state.draft_count = str(int(state.draft_count) + 1) \ + if state.association == fromAddress else state.draft_count + src_object.ids.sc16.clear_widgets() + src_object.ids.sc16.add_widget(Draft()) + toast('Save draft') + return diff --git a/src/tests/mock/pybitmessage/baseclass/inbox.py b/src/tests/mock/pybitmessage/baseclass/inbox.py new file mode 100644 index 00000000..bbfe5abd --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/inbox.py @@ -0,0 +1,77 @@ +from kivy.clock import Clock +from kivy.properties import ( + ListProperty, + StringProperty +) +from kivy.uix.screenmanager import Screen +from kivymd.uix.label import MDLabel + +from pybitmessage import state + +from pybitmessage.baseclass.common import showLimitedCnt + + +class Inbox(Screen): + """Inbox Screen class for kivy Ui""" + + queryreturn = ListProperty() + has_refreshed = True + account = StringProperty() + + def __init__(self, *args, **kwargs): + """Method Parsing the address""" + super(Inbox, self).__init__(*args, **kwargs) + Clock.schedule_once(self.init_ui, 0) + + @staticmethod + def set_defaultAddress(): + """This method set's default address""" + if state.association == "": + if state.kivyapp.variable_1: + state.association = state.kivyapp.variable_1[0] + + def init_ui(self, dt=0): + """Clock schdule for method inbox accounts""" + self.loadMessagelist() + + def loadMessagelist(self, where="", what=""): + """Load Inbox list for Inbox messages""" + self.set_defaultAddress() + self.account = state.association + if state.searcing_text: + self.ids.scroll_y.scroll_y = 1.0 + where = ["subject", "message"] + what = state.searcing_text + xAddress = "toaddress" + data = [] + self.ids.tag_label.text = "" + if self.queryreturn: + pass + else: + self.set_inboxCount("0") + content = MDLabel( + font_style="Caption", + theme_text_color="Primary", + text="No message found!" + if state.searcing_text + else "yet no message for this account!!!!!!!!!!!!!", + halign="center", + size_hint_y=None, + valign="top" + ) + self.ids.ml.add_widget(content) + + def set_inboxCount(self, msgCnt): # pylint: disable=no-self-use + """This method is used to sent inbox message count""" + src_mng_obj = state.kivyapp.root.ids.content_drawer.ids + src_mng_obj.inbox_cnt.ids.badge_txt.text = showLimitedCnt(int(msgCnt)) + state.kivyapp.get_sent_count() + state.all_count = str( + int(state.sent_count) + int(state.inbox_count)) + src_mng_obj.allmail_cnt.ids.badge_txt.text = showLimitedCnt(int(state.all_count)) + + def check_scroll_y(self, instance, somethingelse): + """Loads data on scroll""" + if self.ids.scroll_y.scroll_y <= -0.0 and self.has_refreshed: + self.ids.scroll_y.scroll_y = 0.06 + total_message = len(self.ids.ml.children) diff --git a/src/tests/mock/pybitmessage/baseclass/login.py b/src/tests/mock/pybitmessage/baseclass/login.py new file mode 100644 index 00000000..d082b739 --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/login.py @@ -0,0 +1,109 @@ +from pybitmessage import queues +from pybitmessage.bmconfigparser import BMConfigParser +from pybitmessage import state +from pybitmessage.baseclass.common import toast + +from kivy.clock import Clock +from kivy.properties import StringProperty, BooleanProperty +from kivy.uix.boxlayout import BoxLayout +from kivymd.uix.behaviors.elevation import RectangularElevationBehavior +from kivy.uix.screenmanager import Screen + + +class Login(Screen): + """Login Screeen class for kivy Ui""" + # pylint: disable=too-few-public-methods + log_text1 = ( + 'You may generate addresses by using either random numbers' + ' or by using a passphrase If you use a passphrase, the address' + ' is called a deterministic; address The Random Number option is' + ' selected by default but deterministic addresses have several pros' + ' and cons:') + log_text2 = ('If talk about pros You can recreate your addresses on any computer' + ' from memory, You need-not worry about backing up your keys.dat file' + ' as long as you can remember your passphrase and aside talk about cons' + ' You must remember (or write down) your You must remember the address' + ' version number and the stream number along with your passphrase If you' + ' choose a weak passphrase and someone on the Internet can brute-force it,' + ' they can read your messages and send messages as you') + + +class Random(Screen): + """Random Screen class for Ui""" + + is_active = BooleanProperty(False) + checked = StringProperty("") + + def generateaddress(self): + """Method for Address Generator""" + # entered_label = str(self.ids.lab.text).strip() + entered_label = str(self.ids.add_random_bx.children[0].ids.lab.text).strip() + if not entered_label: + self.ids.add_random_bx.children[0].ids.lab.focus = True + streamNumberForAddress = 1 + eighteenByteRipe = False + nonceTrialsPerByte = 1000 + payloadLengthExtraBytes = 1000 + lables = [BMConfigParser().get(obj, 'label') + for obj in BMConfigParser().addresses()] + if entered_label and entered_label not in lables: + toast('Address Creating...') + queues.addressGeneratorQueue.put(( + 'createRandomAddress', 4, streamNumberForAddress, entered_label, 1, + "", eighteenByteRipe, nonceTrialsPerByte, + payloadLengthExtraBytes)) + self.parent.parent.ids.toolbar.opacity = 1 + self.parent.parent.ids.toolbar.disabled = False + state.kivyapp.loadMyAddressScreen(True) + self.manager.current = 'myaddress' + Clock.schedule_once(self.address_created_callback, 6) + + def address_created_callback(self, dt=0): # pylint: disable=unused-argument + """New address created""" + state.kivyapp.loadMyAddressScreen(False) + state.kivyapp.root.ids.sc10.ids.ml.clear_widgets() + state.kivyapp.root.ids.sc10.is_add_created = True + state.kivyapp.root.ids.sc10.init_ui() + self.reset_address_spinner() + toast('New address created') + + def reset_address_spinner(self): + """reseting spinner address and UI""" + addresses = [addr for addr in BMConfigParser().addresses() + if BMConfigParser().get(str(addr), 'enabled') == 'true'] + self.manager.parent.ids.content_drawer.ids.btn.values = [] + self.manager.parent.ids.sc3.children[1].ids.btn.values = [] + self.manager.parent.ids.content_drawer.ids.btn.values = addresses + self.manager.parent.ids.sc3.children[1].ids.btn.values = addresses + + @staticmethod + def add_validation(instance): + """Checking validation at address creation time""" + entered_label = str(instance.text.strip()) + lables = [BMConfigParser().get(obj, 'label') + for obj in BMConfigParser().addresses()] + if entered_label in lables: + instance.error = True + instance.helper_text = 'it is already exist you'\ + ' can try this Ex. ( {0}_1, {0}_2 )'.format( + entered_label) + elif entered_label: + instance.error = False + else: + instance.error = False + instance.helper_text = 'This field is required' + + def reset_address_label(self): + """Resetting address labels""" + if not self.ids.add_random_bx.children: + self.ids.add_random_bx.add_widget(RandomBoxlayout()) + + +class InfoLayout(BoxLayout, RectangularElevationBehavior): + """InfoLayout class for kivy Ui""" + # pylint: disable=too-few-public-methods + + +class RandomBoxlayout(BoxLayout): + """RandomBoxlayout class for BoxLayout behaviour""" + # pylint: disable=too-few-public-methods diff --git a/src/tests/mock/pybitmessage/baseclass/maildetail.py b/src/tests/mock/pybitmessage/baseclass/maildetail.py new file mode 100644 index 00000000..81e62cc8 --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/maildetail.py @@ -0,0 +1,223 @@ +from datetime import datetime + +from kivy.core.clipboard import Clipboard +from kivy.clock import Clock +from kivy.properties import ( + StringProperty, + NumericProperty +) +from kivy.factory import Factory + +from kivymd.uix.button import MDFlatButton, MDIconButton +from kivymd.uix.dialog import MDDialog +from kivymd.uix.list import ( + OneLineListItem, + IRightBodyTouch +) +from kivy.uix.screenmanager import Screen + +from pybitmessage import state + +from .common import ( + toast, avatarImageFirstLetter, ShowTimeHistoy +) +from .popup import SenderDetailPopup +platform = "linux" + + +class OneLineListTitle(OneLineListItem): + """OneLineListTitle class for kivy Ui""" + __events__ = ('on_long_press', ) + long_press_time = NumericProperty(1) + + def on_state(self, instance, value): + """On state""" + if value == 'down': + lpt = self.long_press_time + self._clockev = Clock.schedule_once(self._do_long_press, lpt) + else: + self._clockev.cancel() + + def _do_long_press(self, dt): + """Do long press""" + self.dispatch('on_long_press') + + def on_long_press(self, *largs): + """On long press""" + self.copymessageTitle(self.text) + + def copymessageTitle(self, title_text): + """this method is for displaying dialog box""" + self.title_text = title_text + width = .8 if platform == 'android' else .55 + self.dialog_box = MDDialog( + text=title_text, + size_hint=(width, .25), + buttons=[ + MDFlatButton( + text="Copy", on_release=self.callback_for_copy_title + ), + MDFlatButton( + text="Cancel", on_release=self.callback_for_copy_title, + ), + ],) + self.dialog_box.open() + + def callback_for_copy_title(self, instance): + """Callback of alert box""" + if instance.text == 'Copy': + Clipboard.copy(self.title_text) + self.dialog_box.dismiss() + toast(instance.text) + + +class IconRightSampleWidget(IRightBodyTouch, MDIconButton): + """IconRightSampleWidget class for kivy Ui""" + + +class MailDetail(Screen): # pylint: disable=too-many-instance-attributes + """MailDetail Screen class for kivy Ui""" + + to_addr = StringProperty() + from_addr = StringProperty() + subject = StringProperty() + message = StringProperty() + status = StringProperty() + page_type = StringProperty() + time_tag = StringProperty() + avatarImg = StringProperty() + + def __init__(self, *args, **kwargs): + """Mail Details method""" + super(MailDetail, self).__init__(*args, **kwargs) + Clock.schedule_once(self.init_ui, 0) + + def init_ui(self, dt=0): + """Clock Schdule for method MailDetail mails""" + self.page_type = state.detailPageType if state.detailPageType else '' + try: + if state.detailPageType == 'sent' or state.detailPageType == 'draft': + data = [] + state.status = self + state.ackdata = data[0][5] + self.assign_mail_details(data) + state.kivyapp.set_mail_detail_header() + elif state.detailPageType == 'inbox': + data = [] + self.assign_mail_details(data) + state.kivyapp.set_mail_detail_header() + except Exception as e: + print('Something wents wrong!!') + + def assign_mail_details(self, data): + """Assigning mail details""" + subject = data[0][2].decode() if isinstance(data[0][2], bytes) else data[0][2] + body = data[0][3].decode() if isinstance(data[0][2], bytes) else data[0][3] + self.to_addr = data[0][0] if len(data[0][0]) > 4 else ' ' + self.from_addr = data[0][1] + + self.subject = subject.capitalize( + ) if subject.capitalize() else '(no subject)' + self.message = body + if len(data[0]) == 7: + self.status = data[0][4] + self.time_tag = ShowTimeHistoy(data[0][4]) if state.detailPageType == 'inbox' else ShowTimeHistoy(data[0][6]) + self.avatarImg = state.imageDir + '/avatar.png' if state.detailPageType == 'draft' else ( + state.imageDir + '/text_images/{0}.png'.format(avatarImageFirstLetter(self.subject.strip()))) + self.timeinseconds = data[0][4] if state.detailPageType == 'inbox' else data[0][6] + + def delete_mail(self): + """Method for mail delete""" + msg_count_objs = state.kivyapp.root.ids.content_drawer.ids + state.searcing_text = '' + self.children[0].children[0].active = True + if state.detailPageType == 'sent': + state.kivyapp.root.ids.sc4.ids.sent_search.ids.search_field.text = '' + msg_count_objs.send_cnt.ids.badge_txt.text = str(int(state.sent_count) - 1) + state.sent_count = str(int(state.sent_count) - 1) + self.parent.screens[2].ids.ml.clear_widgets() + self.parent.screens[2].loadSent(state.association) + elif state.detailPageType == 'inbox': + state.kivyapp.root.ids.sc1.ids.inbox_search.ids.search_field.text = '' + msg_count_objs.inbox_cnt.ids.badge_txt.text = str( + int(state.inbox_count) - 1) + state.inbox_count = str(int(state.inbox_count) - 1) + self.parent.screens[0].ids.ml.clear_widgets() + self.parent.screens[0].loadMessagelist(state.association) + + elif state.detailPageType == 'draft': + msg_count_objs.draft_cnt.ids.badge_txt.text = str( + int(state.draft_count) - 1) + state.draft_count = str(int(state.draft_count) - 1) + self.parent.screens[13].clear_widgets() + self.parent.screens[13].add_widget(Factory.Draft()) + + if state.detailPageType != 'draft': + msg_count_objs.trash_cnt.ids.badge_txt.text = str( + int(state.trash_count) + 1) + msg_count_objs.allmail_cnt.ids.badge_txt.text = str( + int(state.all_count) - 1) + state.trash_count = str(int(state.trash_count) + 1) + state.all_count = str(int(state.all_count) - 1) if int(state.all_count) else '0' + self.parent.screens[3].clear_widgets() + self.parent.screens[3].add_widget(Factory.Trash()) + self.parent.screens[14].clear_widgets() + self.parent.screens[14].add_widget(Factory.Allmails()) + Clock.schedule_once(self.callback_for_delete, 4) + + def callback_for_delete(self, dt=0): + """Delete method from allmails""" + if state.detailPageType: + self.children[0].children[0].active = False + state.kivyapp.set_common_header() + self.parent.current = 'allmails' \ + if state.is_allmail else state.detailPageType + state.detailPageType = '' + toast('Deleted') + + def inbox_reply(self): + """Reply inbox messages""" + state.in_composer = True + data = [] + composer_obj = self.parent.screens[1].children[1].ids + composer_obj.ti.text = data[0][0] + composer_obj.btn.text = data[0][0] + composer_obj.txt_input.text = data[0][1] + split_subject = data[0][2].split('Re:', 1) + composer_obj.subject.text = 'Re: ' + (split_subject[1] if len(split_subject) > 1 else split_subject[0]) + time_obj = datetime.fromtimestamp(int(data[0][4])) + time_tag = time_obj.strftime("%d %b %Y, %I:%M %p") + sender_name = data[0][1] + composer_obj.body.text = ( + '\n\n --------------On ' + time_tag + ', ' + sender_name + ' wrote:--------------\n' + data[0][3]) + composer_obj.body.focus = True + composer_obj.body.cursor = (0, 0) + state.kivyapp.root.ids.sc3.children[1].ids.rv.data = '' + self.parent.current = 'create' + state.kivyapp.set_navbar_for_composer() + + def write_msg(self, navApp): + """Write on draft mail""" + state.send_draft_mail = state.mail_id + data = [] + composer_ids = ( + self.parent.parent.ids.sc3.children[1].ids) + composer_ids.ti.text = data[0][1] + composer_ids.btn.text = data[0][1] + composer_ids.txt_input.text = data[0][0] + composer_ids.subject.text = data[0][2] if data[0][2] != '(no subject)' else '' + composer_ids.body.text = data[0][3] + self.parent.current = 'create' + navApp.set_navbar_for_composer() + + def detailedPopup(self): + """Detailed popup""" + obj = SenderDetailPopup() + obj.open() + arg = (self.to_addr, self.from_addr, self.timeinseconds) + obj.assignDetail(*arg) + + @staticmethod + def callback_for_menu_items(text_item, *arg): + """Callback of alert box""" + toast(text_item) diff --git a/src/tests/mock/pybitmessage/baseclass/msg_composer.py b/src/tests/mock/pybitmessage/baseclass/msg_composer.py new file mode 100644 index 00000000..f047fb7e --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/msg_composer.py @@ -0,0 +1,258 @@ +from pybitmessage.get_platform import platform +from pybitmessage.bmconfigparser import BMConfigParser +from kivy.clock import Clock +from kivy.core.window import Window +from kivy.factory import Factory +from kivy.properties import ( + BooleanProperty, + ListProperty, + NumericProperty, + ObjectProperty, +) +from kivy.uix.behaviors import FocusBehavior +from kivy.uix.boxlayout import BoxLayout +from kivymd.uix.button import MDFlatButton +from kivymd.uix.dialog import MDDialog +from kivy.uix.label import Label +from kivy.uix.recycleview import RecycleView +from kivy.uix.recycleboxlayout import RecycleBoxLayout +from kivy.uix.recycleview.layout import LayoutSelectionBehavior +from kivy.uix.recycleview.views import RecycleDataViewBehavior +from kivy.uix.screenmanager import Screen +from pybitmessage import state +from pybitmessage import queues + +from pybitmessage.addresses import decodeAddress, addBMIfNotPresent +from pybitmessage.baseclass.common import ( + toast, showLimitedCnt +) +from kivymd.uix.textfield import MDTextField + + +class Create(Screen): + """Creates Screen class for kivy Ui""" + + def __init__(self, **kwargs): + """Getting Labels and address from addressbook""" + super(Create, self).__init__(**kwargs) + Window.softinput_mode = "below_target" + widget_1 = DropDownWidget() + widget_1.ids.txt_input.word_list = [] + widget_1.ids.txt_input.starting_no = 2 + self.add_widget(widget_1) + self.children[0].ids.id_scroll.bind(scroll_y=self.check_scroll_y) + + def check_scroll_y(self, instance, somethingelse): # pylint: disable=unused-argument + """show data on scroll down""" + if self.children[1].ids.btn.is_open: + self.children[1].ids.btn.is_open = False + + +class RV(RecycleView): + """Recycling View class for kivy Ui""" + + def __init__(self, **kwargs): + """Recycling Method""" + super(RV, self).__init__(**kwargs) + + +class SelectableRecycleBoxLayout( + FocusBehavior, LayoutSelectionBehavior, RecycleBoxLayout +): + """Adds selection and focus behaviour to the view""" + + # pylint: disable = duplicate-bases + + +class DropDownWidget(BoxLayout): + """DropDownWidget class for kivy Ui""" + + # pylint: disable=too-many-statements + + txt_input = ObjectProperty() + rv = ObjectProperty() + + def send(self, navApp): + """Send message from one address to another""" + fromAddress = self.ids.ti.text.strip() + toAddress = self.ids.txt_input.text.strip() + subject = self.ids.subject.text.strip() + message = self.ids.body.text.strip() + print("message: ", self.ids.body.text) + if toAddress != "" and subject and message: + status, addressVersionNumber, streamNumber, ripe = decodeAddress( + toAddress + ) + if status == "success": + navApp.root.ids.sc3.children[0].active = True + if state.detailPageType == "draft" and state.send_draft_mail: + self.parent.parent.screens[13].clear_widgets() + self.parent.parent.screens[13].add_widget(Factory.Draft()) + else: + if (addressVersionNumber > 4) or ( + addressVersionNumber <= 1): + print( + "addressVersionNumber > 4" + " or addressVersionNumber <= 1") + if streamNumber > 1 or streamNumber == 0: + print("streamNumber > 1 or streamNumber == 0") + stealthLevel = BMConfigParser().safeGetInt( + 'bitmessagesettings', 'ackstealthlevel') + + state.check_sent_acc = fromAddress + if state.detailPageType == 'draft' \ + and state.send_draft_mail: + state.draft_count = str(int(state.draft_count) - 1) + state.detailPageType = '' + state.send_draft_mail = None + self.parent.parent.parent.ids.sc4.update_sent_messagelist() + allmailCnt_obj = state.kivyapp.root.ids.content_drawer.ids.allmail_cnt + allmailCnt_obj.ids.badge_txt.text = showLimitedCnt(int(state.all_count) + 1) + state.all_count = str(int(state.all_count) + 1) + Clock.schedule_once(self.callback_for_msgsend, 3) + queues.workerQueue.put(('sendmessage', addBMIfNotPresent(toAddress))) + print("sqlExecute successfully #######################") + state.in_composer = True + return + else: + msg = 'Enter a valid recipients address' + elif not toAddress: + msg = 'Please fill the form completely' + else: + msg = 'Please fill the form completely' + self.address_error_message(msg) + + @staticmethod + def callback_for_msgsend(dt=0): # pylint: disable=unused-argument + """Callback method for messagesend""" + state.kivyapp.root.ids.sc3.children[0].active = False + state.in_sent_method = True + state.kivyapp.back_press() + toast("sent") + + @staticmethod + def address_error_message(msg): + """Generates error message""" + width = .8 if platform == 'android' else .55 + dialog_box = MDDialog( + text=msg, + size_hint=(width, .25), + buttons=[ + MDFlatButton( + text="Ok", on_release=lambda x: callback_for_menu_items("Ok") + ), + ],) + dialog_box.open() + + def callback_for_menu_items(text_item, *arg): + """Callback of alert box""" + dialog_box.dismiss() + toast(text_item) + + def reset_composer(self): + """Method will reset composer""" + self.ids.ti.text = "" + self.ids.btn.text = "Select" + self.ids.txt_input.text = "" + self.ids.subject.text = "" + self.ids.body.text = "" + toast("Reset message") + + def auto_fill_fromaddr(self): + """Fill the text automatically From Address""" + self.ids.ti.text = self.ids.btn.text + self.ids.ti.focus = True + + def is_camara_attached(self): + """Checks the camera availability in device""" + self.parent.parent.parent.ids.sc23.check_camera() + is_available = self.parent.parent.parent.ids.sc23.camera_avaialbe + return is_available + + @staticmethod + def camera_alert(): + """Show camera availability alert message""" + width = .8 if platform == 'android' else .55 + altet_txt = 'Currently this feature is not avaialbe!'if platform == 'android' else 'Camera is not available!' + dialog_box = MDDialog( + text=altet_txt, + size_hint=(width, .25), + buttons=[ + MDFlatButton( + text="Ok", on_release=lambda x: callback_for_menu_items("Ok") + ), + ], + ) + dialog_box.open() + + def callback_for_menu_items(text_item, *arg): + """Callback of alert box""" + dialog_box.dismiss() + toast(text_item) + + +class MyTextInput(MDTextField): + """MyTextInput class for kivy Ui""" + + txt_input = ObjectProperty() + flt_list = ObjectProperty() + word_list = ListProperty() + starting_no = NumericProperty(3) + suggestion_text = '' + + def __init__(self, **kwargs): + """Getting Text Input.""" + super(MyTextInput, self).__init__(**kwargs) + self.__lineBreak__ = 0 + + def on_text(self, instance, value): # pylint: disable=unused-argument + """Find all the occurrence of the word""" + self.parent.parent.parent.parent.parent.ids.rv.data = [] + matches = [self.word_list[i] for i in range( + len(self.word_list)) if self.word_list[ + i][:self.starting_no] == value[:self.starting_no]] + display_data = [] + for i in matches: + display_data.append({'text': i}) + self.parent.parent.parent.parent.parent.ids.rv.data = display_data + if len(matches) <= 10: + self.parent.height = (250 + (len(matches) * 20)) + else: + self.parent.height = 400 + + def keyboard_on_key_down(self, window, keycode, text, modifiers): + """Keyboard on key Down""" + if self.suggestion_text and keycode[1] == 'tab': + self.insert_text(self.suggestion_text + ' ') + return True + return super(MyTextInput, self).keyboard_on_key_down( + window, keycode, text, modifiers) + + +class SelectableLabel(RecycleDataViewBehavior, Label): + """Add selection support to the Label""" + + index = None + selected = BooleanProperty(False) + selectable = BooleanProperty(True) + + def refresh_view_attrs(self, rv, index, data): + """Catch and handle the view changes""" + self.index = index + return super(SelectableLabel, self).refresh_view_attrs(rv, index, data) + + def on_touch_down(self, touch): # pylint: disable=inconsistent-return-statements + """Add selection on touch down""" + if super(SelectableLabel, self).on_touch_down(touch): + return True + if self.collide_point(*touch.pos) and self.selectable: + return self.parent.select_with_touch(self.index, touch) + + def apply_selection(self, rv, index, is_selected): + """Respond to the selection of items in the view""" + self.selected = is_selected + if is_selected: + print("selection changed to {0}".format(rv.data[index])) + rv.parent.txt_input.text = rv.parent.txt_input.text.replace( + rv.parent.txt_input.text, rv.data[index]["text"] + ) diff --git a/src/tests/mock/pybitmessage/baseclass/myaddress.py b/src/tests/mock/pybitmessage/baseclass/myaddress.py new file mode 100644 index 00000000..2073b951 --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/myaddress.py @@ -0,0 +1,221 @@ +from pybitmessage.get_platform import platform +from functools import partial +from pybitmessage.bmconfigparser import BMConfigParser +from kivy.clock import Clock +from kivy.properties import ( + ListProperty, + StringProperty +) +from kivymd.uix.button import MDFlatButton +from kivymd.uix.dialog import MDDialog +from kivymd.uix.label import MDLabel +from kivymd.uix.list import ( + IRightBodyTouch, + TwoLineAvatarIconListItem, +) +from kivymd.uix.selectioncontrol import MDSwitch +from kivy.uix.screenmanager import Screen + +from pybitmessage import state + +from pybitmessage.baseclass.common import ( + avatarImageFirstLetter, AvatarSampleWidget, ThemeClsColor, + toast +) +from pybitmessage.baseclass.popup import MyaddDetailPopup + + +class ToggleBtn(IRightBodyTouch, MDSwitch): + """ToggleBtn class for kivy Ui""" + + +class CustomTwoLineAvatarIconListItem(TwoLineAvatarIconListItem): + """CustomTwoLineAvatarIconListItem class for kivy Ui""" + + +class BadgeText(IRightBodyTouch, MDLabel): + """BadgeText class for kivy Ui""" + + +class MyAddress(Screen): + """MyAddress screen class for kivy Ui""" + + address_label = StringProperty() + text_address = StringProperty() + addresses_list = ListProperty() + has_refreshed = True + is_add_created = False + + def __init__(self, *args, **kwargs): + """Clock schdule for method Myaddress accounts""" + super(MyAddress, self).__init__(*args, **kwargs) + Clock.schedule_once(self.init_ui, 0) + + def init_ui(self, dt=0): + """Clock schdule for method Myaddress accounts""" + # pylint: disable=unnecessary-lambda, deprecated-lambda + self.addresses_list = BMConfigParser().addresses() + if state.searcing_text: + self.ids.refresh_layout.scroll_y = 1.0 + filtered_list = [ + x for x in BMConfigParser().addresses() + if self.filter_address(x) + ] + self.addresses_list = filtered_list + self.addresses_list = [obj for obj in reversed(self.addresses_list)] + self.ids.tag_label.text = '' + if self.addresses_list: + self.ids.tag_label.text = 'My Addresses' + self.has_refreshed = True + self.set_mdList(0, 15) + self.ids.refresh_layout.bind(scroll_y=self.check_scroll_y) + else: + content = MDLabel( + font_style='Caption', + theme_text_color='Primary', + text="No address found!" if state.searcing_text + else "yet no address is created by user!!!!!!!!!!!!!", + halign='center', + size_hint_y=None, + valign='top') + self.ids.ml.add_widget(content) + if not state.searcing_text and not self.is_add_created: + try: + self.manager.current = 'login' + except Exception: + pass + + def set_mdList(self, first_index, last_index): + """Creating the mdlist""" + data = [] + for address in self.addresses_list[first_index:last_index]: + data.append({ + 'text': BMConfigParser().get(address, 'label'), + 'secondary_text': address}) + for item in data: + is_enable = BMConfigParser().get(item['secondary_text'], 'enabled') + meny = CustomTwoLineAvatarIconListItem( + text=item['text'], secondary_text=item['secondary_text'], + theme_text_color='Custom' if is_enable == 'true' else 'Primary', + text_color=ThemeClsColor,) + # meny._txt_right_pad = dp(70) + try: + meny.canvas.children[3].rgba = [0, 0, 0, 0] if is_enable == 'true' else [0.5, 0.5, 0.5, 0.5] + except Exception: + pass + meny.add_widget(AvatarSampleWidget( + source=state.imageDir + '/text_images/{}.png'.format( + avatarImageFirstLetter(item['text'].strip())))) + meny.bind(on_press=partial( + self.myadd_detail, item['secondary_text'], item['text'])) + if state.association == item['secondary_text'] and is_enable == 'true': + badge_obj = BadgeText( + size_hint=(None, None), + size=[90 if platform == 'android' else 50, 60], + text='Active', halign='center', + font_style='Body1', theme_text_color='Custom', + text_color=ThemeClsColor + ) + badge_obj.font_size = '13sp' + meny.add_widget(badge_obj) + else: + meny.add_widget(ToggleBtn(active=True if is_enable == 'true' else False)) + self.ids.ml.add_widget(meny) + + def check_scroll_y(self, instance, somethingelse): + """Load data on scroll down""" + if self.ids.refresh_layout.scroll_y <= -0.0 and self.has_refreshed: + self.ids.refresh_layout.scroll_y = 0.06 + + def myadd_detail(self, fromaddress, label, *args): + """Load myaddresses details""" + if BMConfigParser().get(fromaddress, 'enabled') == 'true': + obj = MyaddDetailPopup() + self.address_label = obj.address_label = label + self.text_address = obj.address = fromaddress + width = .9 if platform == 'android' else .6 + self.myadddetail_popup = MDDialog( + type="custom", + size_hint=(width, .25), + content_cls=obj, + ) + self.myadddetail_popup.auto_dismiss = False + self.myadddetail_popup.open() + else: + width = .8 if platform == 'android' else .55 + dialog_box = MDDialog( + text='Address is not currently active. Please click on Toggle button to active it.', + size_hint=(width, .25), + buttons=[ + MDFlatButton( + text="Ok", on_release=lambda x: callback_for_menu_items("Ok") + ), + ], + ) + dialog_box.open() + + def callback_for_menu_items(text_item, *arg): + """Callback of alert box""" + dialog_box.dismiss() + toast(text_item) + + def refresh_callback(self, *args): + """Method updates the state of application, + While the spinner remains on the screen""" + def refresh_callback(interval): + """Method used for loading the myaddress screen data""" + state.searcing_text = '' + self.ids.search_bar.ids.search_field.text = '' + self.has_refreshed = True + self.ids.ml.clear_widgets() + self.init_ui() + self.ids.refresh_layout.refresh_done() + Clock.schedule_once(self.address_permision_callback, 0) + Clock.schedule_once(refresh_callback, 1) + + @staticmethod + def filter_address(address): + """Method will filter the my address list data""" + if [ + x for x in [ + BMConfigParser().get(address, 'label').lower(), + address.lower() + ] + if (state.searcing_text).lower() in x + ]: + return True + return False + + def disableAddress(self, address, instance): + """This method is use for disabling address""" + BMConfigParser().set(str(address), 'enabled', 'false') + BMConfigParser().save() + instance.parent.parent.theme_text_color = 'Primary' + instance.parent.parent.canvas.children[3].rgba = [0.5, 0.5, 0.5, 0.5] + toast('Address disabled') + Clock.schedule_once(self.address_permision_callback, 0) + + def enableAddress(self, address, instance): + """This method is use for enabling address""" + BMConfigParser().set(address, 'enabled', 'true') + BMConfigParser().save() + instance.parent.parent.theme_text_color = 'Custom' + instance.parent.parent.canvas.children[3].rgba = [0, 0, 0, 0] + toast('Address Enabled') + Clock.schedule_once(self.address_permision_callback, 0) + + def address_permision_callback(self, dt=0): + """callback for enable or disable addresses""" + addresses = [addr for addr in BMConfigParser().addresses() + if BMConfigParser().get(str(addr), 'enabled') == 'true'] + self.parent.parent.ids.content_drawer.ids.btn.values = addresses + self.parent.parent.ids.sc3.children[1].ids.btn.values = addresses + state.kivyapp.variable_1 = addresses + + def toggleAction(self, instance): + """This method is used for enable or disable address""" + addr = instance.parent.parent.secondary_text + if instance.active: + self.enableAddress(addr, instance) + else: + self.disableAddress(addr, instance) diff --git a/src/tests/mock/pybitmessage/baseclass/network.py b/src/tests/mock/pybitmessage/baseclass/network.py new file mode 100644 index 00000000..cbe18504 --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/network.py @@ -0,0 +1,27 @@ +from kivy.clock import Clock +from kivy.properties import StringProperty +from kivy.uix.screenmanager import Screen + + +class NetworkStat(Screen): + """NetworkStat class for kivy Ui""" + + text_variable_1 = StringProperty( + '{0}::{1}'.format('Total Connections', '0')) + text_variable_2 = StringProperty( + 'Processed {0} per-to-per messages'.format('0')) + text_variable_3 = StringProperty( + 'Processed {0} brodcast messages'.format('0')) + text_variable_4 = StringProperty( + 'Processed {0} public keys'.format('0')) + text_variable_5 = StringProperty( + 'Processed {0} object to be synced'.format('0')) + + def __init__(self, *args, **kwargs): + """Init method for network stat""" + super(NetworkStat, self).__init__(*args, **kwargs) + Clock.schedule_interval(self.init_ui, 1) + + def init_ui(self, dt=0): + """Clock Schdule for method networkstat screen""" + pass diff --git a/src/tests/mock/pybitmessage/baseclass/payment.py b/src/tests/mock/pybitmessage/baseclass/payment.py new file mode 100644 index 00000000..c286888c --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/payment.py @@ -0,0 +1,43 @@ +''' + This is for payment related part +''' +from kivy.uix.boxlayout import BoxLayout +from kivymd.uix.behaviors.elevation import RectangularElevationBehavior +from kivy.uix.screenmanager import Screen + +from kivymd.uix.label import MDLabel +from kivymd.uix.list import ( + IRightBodyTouch, + OneLineAvatarIconListItem +) + + +class Payment(Screen): + """Payment Screen class for kivy Ui""" + + @staticmethod + def create_hidden_payment_address(): + """This is basically used for creating hidden address used in payment for purchasing credits""" + pass + + +class Category(BoxLayout, RectangularElevationBehavior): + """Category class for kivy Ui""" + elevation_normal = .01 + + +class ProductLayout(BoxLayout, RectangularElevationBehavior): + """ProductLayout class for kivy Ui""" + elevation_normal = .01 + + +class PaymentMethodLayout(BoxLayout): + """PaymentMethodLayout class for kivy Ui""" + + +class ListItemWithLabel(OneLineAvatarIconListItem): + """ListItemWithLabel class for kivy Ui""" + + +class RightLabel(IRightBodyTouch, MDLabel): + """RightLabel class for kivy Ui""" diff --git a/src/tests/mock/pybitmessage/baseclass/popup.py b/src/tests/mock/pybitmessage/baseclass/popup.py new file mode 100644 index 00000000..eba6b494 --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/popup.py @@ -0,0 +1,166 @@ + +from kivy.clock import Clock +from kivy.metrics import dp +from kivy.properties import StringProperty + +from kivy.uix.boxlayout import BoxLayout +from kivy.uix.popup import Popup + +from pybitmessage import state +from datetime import datetime + +from pybitmessage.baseclass.common import toast + +platform = "linux" + + +class LoadingPopup(Popup): + """LoadingPopup class for kivy Ui""" + + def __init__(self, **kwargs): + super(LoadingPopup, self).__init__(**kwargs) + Clock.schedule_once(self.dismiss_popup, 0.5) + + def dismiss_popup(self, dt): + """Dismiss popups""" + self.dismiss() + + +class GrashofPopup(BoxLayout): + """GrashofPopup class for kivy Ui""" + + valid = False + + def __init__(self, **kwargs): + """Grash of pop screen settings""" + super(GrashofPopup, self).__init__(**kwargs) + + def checkAddress_valid(self, instance): + """Checking address is valid or not""" + pass + + def checkLabel_valid(self, instance): + """Checking address label is unique or not""" + pass + + def _onSuccess(self, addressVersion, streamNumber, ripe): + pass + + +class AddbookDetailPopup(BoxLayout): + """AddbookDetailPopup class for kivy Ui""" + + address_label = StringProperty() + address = StringProperty() + + def __init__(self, **kwargs): + """Set screen of address detail page""" + super(AddbookDetailPopup, self).__init__(**kwargs) + + def checkLabel_valid(self, instance): + """Checking address label is unique of not""" + entered_label = str(instance.text.strip()) + address_list = [] + addr_labels = [labels[0] for labels in address_list] + add_dict = dict(address_list) + if self.address and entered_label in addr_labels \ + and self.address != add_dict[entered_label]: + self.ids.add_label.error = True + self.ids.add_label.helper_text = 'label name already exists.' + elif entered_label: + self.ids.add_label.error = False + else: + self.ids.add_label.error = False + self.ids.add_label.helper_text = 'This field is required' + + +class MyaddDetailPopup(BoxLayout): + """MyaddDetailPopup class for kivy Ui""" + + address_label = StringProperty() + address = StringProperty() + + def __init__(self, **kwargs): + """My Address Details screen setting""" + super(MyaddDetailPopup, self).__init__(**kwargs) + + def send_message_from(self): + """Method used to fill from address of composer autofield""" + state.kivyapp.set_navbar_for_composer() + window_obj = state.kivyapp.root.ids + window_obj.sc3.children[1].ids.ti.text = self.address + window_obj.sc3.children[1].ids.btn.text = self.address + window_obj.sc3.children[1].ids.txt_input.text = '' + window_obj.sc3.children[1].ids.subject.text = '' + window_obj.sc3.children[1].ids.body.text = '' + window_obj.scr_mngr.current = 'create' + self.parent.parent.parent.dismiss() + + def close_pop(self): + """Pop is Canceled""" + self.parent.parent.parent.dismiss() + toast('Canceled') + + +class AppClosingPopup(Popup): + """AppClosingPopup class for kivy Ui""" + + def __init__(self, **kwargs): + super(AppClosingPopup, self).__init__(**kwargs) + + def closingAction(self, text): + """Action on closing window""" + if text == 'Yes': + print("*******************EXITING FROM APPLICATION*******************") + from pybitmessage import shutdown + shutdown.doCleanShutdown() + else: + self.dismiss() + toast(text) + + +class SenderDetailPopup(Popup): + """SenderDetailPopup class for kivy Ui""" + + to_addr = StringProperty() + from_addr = StringProperty() + time_tag = StringProperty() + + def __init__(self, **kwargs): + """this metthod initialized the send message detial popup""" + super(SenderDetailPopup, self).__init__(**kwargs) + + def assignDetail(self, to_addr, from_addr, timeinseconds): + """Detailes assigned""" + self.to_addr = to_addr + self.from_addr = from_addr + time_obj = datetime.fromtimestamp(int(timeinseconds)) + self.time_tag = time_obj.strftime("%d %b %Y, %I:%M %p") + device_type = 2 if platform == 'android' else 1.5 + pop_height = 1.2 * device_type * (self.ids.sd_label.height + self.ids.dismiss_btn.height) + if len(to_addr) > 3: + self.height = pop_height + self.ids.to_addId.size_hint_y = None + self.ids.to_addId.height = 50 + self.ids.to_addtitle.add_widget(ToAddressTitle()) + frmaddbox = ToAddrBoxlayout() + frmaddbox.set_toAddress(to_addr) + self.ids.to_addId.add_widget(frmaddbox) + else: + self.ids.space_1.height = dp(0) + self.ids.space_2.height = dp(0) + self.ids.myadd_popup_box.spacing = dp(8 if platform == 'android' else 3) + self.height = pop_height / 1.2 + + +class ToAddrBoxlayout(BoxLayout): + """ToAddrBoxlayout class for kivy Ui""" + to_addr = StringProperty() + + def set_toAddress(self, to_addr): + """This method is use to set to address""" + self.to_addr = to_addr + + +class ToAddressTitle(BoxLayout): + """ToAddressTitle class for BoxLayout behaviour""" diff --git a/src/tests/mock/pybitmessage/baseclass/qrcode.py b/src/tests/mock/pybitmessage/baseclass/qrcode.py new file mode 100644 index 00000000..46867990 --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/qrcode.py @@ -0,0 +1,20 @@ +from pybitmessage import state +from pybitmessage.baseclass.common import toast +from kivy.uix.screenmanager import Screen +from kivy.properties import StringProperty +from kivy_garden.qrcode import QRCodeWidget + + +class ShowQRCode(Screen): + """ShowQRCode Screen class for kivy Ui""" + address = StringProperty() + + def qrdisplay(self, instasnce, address): + """Method used for showing QR Code""" + self.ids.qr.clear_widgets() + state.kivyapp.set_toolbar_for_QrCode() + self.address = address + self.ids.qr.add_widget(QRCodeWidget(data=address)) + self.ids.qr.children[0].show_border = False + instasnce.parent.parent.parent.dismiss() + toast('Show QR code') diff --git a/src/tests/mock/pybitmessage/baseclass/scan_screen.py b/src/tests/mock/pybitmessage/baseclass/scan_screen.py new file mode 100644 index 00000000..a2bbff78 --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/scan_screen.py @@ -0,0 +1,93 @@ +import os +from pybitmessage.get_platform import platform + +from kivy.clock import Clock +from kivy.lang import Builder +from kivy.properties import ( + BooleanProperty, + ObjectProperty, + StringProperty +) +from kivy.uix.screenmanager import Screen + + +class ScanScreen(Screen): + """ScanScreen is for scaning Qr code""" + # pylint: disable=unused-argument + # pylint: disable=W0212 + camera_avaialbe = BooleanProperty(False) + previous_open_screen = StringProperty() + pop_up_instance = ObjectProperty() + + def __init__(self, *args, **kwargs): + """Getting AddressBook Details""" + super(ScanScreen, self).__init__(*args, **kwargs) + self.check_camera() + + def check_camera(self): + """This method is used for checking camera avaibility""" + if platform != "android": + import cv2 + cap = cv2.VideoCapture(0) + while(cap.isOpened()): + print('Camera is available!') + self.camera_avaialbe = True + break + else: + print("Camera is not available!") + self.camera_avaialbe = False + else: + self.camera_avaialbe = True + + def get_screen(self, screen_name, instance=None): + """This method is used for getting previous screen name""" + self.previous_open_screen = screen_name + if screen_name != 'composer': + self.pop_up_instance = instance + + def on_pre_enter(self): + """ + on_pre_enter works little better on android + It affects screen transition on linux + """ + if not self.children: + tmp = Builder.load_file( + os.path.join( + os.path.dirname(os.path.dirname(__file__)), "kv/{}.kv").format("scanner") + ) + self.add_widget(tmp) + if platform == "android": + Clock.schedule_once(self.start_camera, 0) + + def on_enter(self): + """ + on_enter works better on linux + It creates a black screen on android until camera gets loaded + """ + if platform != "android": + Clock.schedule_once(self.start_camera, 0) + + def on_leave(self): + """this methos will call on leave""" + Clock.schedule_once(self.stop_camera, 0) + + def start_camera(self, *args): + """Its used for starting camera for scanning qrcode""" + self.xcam = self.children[0].ids.zbarcam.ids.xcamera + if platform == "android": + self.xcam.play = True + + else: + Clock.schedule_once(self.open_cam, 0) + + def stop_camera(self, *args): + """Its used for stop the camera""" + self.xcam.play = False + if platform != "android": + self.xcam._camera._device.release() + + def open_cam(self, *args): + """It will open up the camera""" + if not self.xcam._camera._device.isOpened(): + self.xcam._camera._device.open(self.xcam._camera._index) + self.xcam.play = True diff --git a/src/tests/mock/pybitmessage/baseclass/sent.py b/src/tests/mock/pybitmessage/baseclass/sent.py new file mode 100644 index 00000000..a44aaf3b --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/sent.py @@ -0,0 +1,172 @@ +from functools import partial +from kivy.clock import Clock +from kivy.properties import StringProperty, ListProperty + +from kivy.uix.screenmanager import Screen +from kivymd.uix.label import MDLabel + +from pybitmessage import state + +from pybitmessage.baseclass.common import ( + showLimitedCnt, ThemeClsColor, avatarImageFirstLetter, + toast, SwipeToDeleteItem, ShowTimeHistoy +) + + +class Sent(Screen): + """Sent Screen class for kivy Ui""" + + queryreturn = ListProperty() + has_refreshed = True + account = StringProperty() + + def __init__(self, *args, **kwargs): + """Association with the screen""" + super(Sent, self).__init__(*args, **kwargs) + if state.association == '': + if state.kivyapp.variable_1: + state.association = state.kivyapp.variable_1[0] + Clock.schedule_once(self.init_ui, 0) + + def init_ui(self, dt=0): + """Clock Schdule for method sent accounts""" + self.loadSent() + print(dt) + + def loadSent(self, where="", what=""): + """Load Sent list for Sent messages""" + self.account = state.association + if state.searcing_text: + self.ids.scroll_y.scroll_y = 1.0 + where = ['subject', 'message'] + what = state.searcing_text + xAddress = 'fromaddress' + data = [] + self.ids.tag_label.text = '' + if self.queryreturn: + self.ids.tag_label.text = 'Sent' + self.set_sentCount(state.sent_count) + for mail in self.queryreturn: + data.append({ + 'text': mail[1].strip(), + 'secondary_text': (mail[2][:50] + '........' if len( + mail[2]) >= 50 else (mail[2] + ',' + mail[3])[0:50] + '........').replace( + '\t', '').replace(' ', ''), + 'ackdata': mail[5], 'senttime': mail[6]},) + self.set_mdlist(data, 0) + self.has_refreshed = True + self.ids.scroll_y.bind(scroll_y=self.check_scroll_y) + else: + self.set_sentCount('0') + content = MDLabel( + font_style='Caption', + theme_text_color='Primary', + text="No message found!" if state.searcing_text + else "yet no message for this account!!!!!!!!!!!!!", + halign='center', + size_hint_y=None, + valign='top') + self.ids.ml.add_widget(content) + + def set_mdlist(self, data, set_index=0): + """This method is used to create the mdList""" + total_sent_msg = len(self.ids.ml.children) + for item in data: + message_row = SwipeToDeleteItem( + text=item["text"], + ) + listItem = message_row.ids.content + listItem.secondary_text = item["secondary_text"] + listItem.theme_text_color = "Custom" + listItem.text_color = ThemeClsColor + image = state.imageDir + '/text_images/{}.png'.format( + avatarImageFirstLetter(item['secondary_text'].strip())) + message_row.ids.avater_img.source = image + listItem.bind(on_release=partial(self.sent_detail, item['ackdata'], message_row)) + message_row.ids.time_tag.text = str(ShowTimeHistoy(item['senttime'])) + message_row.ids.delete_msg.bind(on_press=partial(self.delete, item["ackdata"])) + self.ids.ml.add_widget(message_row, index=set_index) + + updated_msgs = len(self.ids.ml.children) + self.has_refreshed = True if total_sent_msg != updated_msgs else False + + def update_sent_messagelist(self): + """This method is used to update screen when new mail is sent""" + self.account = state.association + if len(self.ids.ml.children) < 3: + self.ids.ml.clear_widgets() + self.loadSent() + if state.association == state.check_sent_acc: + total_sent = int(state.sent_count) + 1 + state.sent_count = str(int(state.sent_count) + 1) + self.set_sentCount(total_sent) + else: + total_sent = int(state.sent_count) + else: + data = [] + if state.association == state.check_sent_acc: + total_sent = int(state.sent_count) + 1 + state.sent_count = str(int(state.sent_count) + 1) + self.set_sentCount(total_sent) + else: + total_sent = int(state.sent_count) + for mail in self.queryreturn: + data.append({ + 'text': mail[1].strip(), + 'secondary_text': (mail[2][:50] + '........' if len( + mail[2]) >= 50 else (mail[2] + ',' + mail[3])[0:50] + '........').replace( + '\t', '').replace(' ', ''), + 'ackdata': mail[5], 'senttime': mail[6]}) + self.set_mdlist(data, total_sent - 1) + if state.msg_counter_objs and state.association == ( + state.check_sent_acc): + state.all_count = str(int(state.all_count) + 1) + state.msg_counter_objs.allmail_cnt.badge_text = state.all_count + state.check_sent_acc = None + + def check_scroll_y(self, instance, somethingelse): + """Load data on scroll down""" + if self.ids.scroll_y.scroll_y <= -0.0 and self.has_refreshed: + self.ids.scroll_y.scroll_y = 0.06 + total_sent_msg = len(self.ids.ml.children) + + @staticmethod + def set_sentCount(total_sent): + """Set the total no. of sent message count""" + src_mng_obj = state.kivyapp.root.ids.content_drawer.ids.send_cnt + state.kivyapp.root.ids.content_drawer.ids.send_cnt.ids.badge_txt.text + if state.association: + src_mng_obj.ids.badge_txt.text = showLimitedCnt(int(total_sent)) + else: + src_mng_obj.ids.badge_txt.text = '0' + + def sent_detail(self, ackdata, instance, *args): + """Load sent mail details""" + if instance.state == 'closed': + instance.ids.delete_msg.disabled = True + if instance.open_progress == 0.0: + state.detailPageType = 'sent' + state.mail_id = ackdata + if self.manager: + src_mng_obj = self.manager + else: + src_mng_obj = self.parent.parent + src_mng_obj.screens[11].clear_widgets() + src_mng_obj.current = 'mailDetail' + else: + instance.ids.delete_msg.disabled = False + + def delete(self, data_index, instance, *args): + """Delete sent mail from sent mail listing""" + msg_count_objs = self.parent.parent.ids.content_drawer.ids + if int(state.sent_count) > 0: + msg_count_objs.send_cnt.ids.badge_txt.text = showLimitedCnt(int(state.sent_count) - 1) + msg_count_objs.trash_cnt.ids.badge_txt.text = showLimitedCnt(int(state.trash_count) + 1) + msg_count_objs.allmail_cnt.ids.badge_txt.text = showLimitedCnt(int(state.all_count) - 1) + state.sent_count = str(int(state.sent_count) - 1) + state.trash_count = str(int(state.trash_count) + 1) + state.all_count = str(int(state.all_count) - 1) + if int(state.sent_count) <= 0: + self.ids.tag_label.text = '' + self.ids.ml.remove_widget(instance.parent.parent) + toast('Deleted') diff --git a/src/tests/mock/pybitmessage/baseclass/settings.py b/src/tests/mock/pybitmessage/baseclass/settings.py new file mode 100644 index 00000000..d63884b2 --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/settings.py @@ -0,0 +1,6 @@ +from kivy.uix.screenmanager import Screen + + +class Setting(Screen): + """Setting Screen for kivy Ui""" + pass diff --git a/src/tests/mock/pybitmessage/baseclass/trash.py b/src/tests/mock/pybitmessage/baseclass/trash.py new file mode 100644 index 00000000..885e3987 --- /dev/null +++ b/src/tests/mock/pybitmessage/baseclass/trash.py @@ -0,0 +1,69 @@ +from kivy.clock import Clock +from kivy.properties import ( + ListProperty, + StringProperty +) +from kivymd.uix.label import MDLabel +from kivy.uix.screenmanager import Screen + +from pybitmessage import state + +from pybitmessage.baseclass.common import showLimitedCnt + + +class Trash(Screen): + """Trash Screen class for kivy Ui""" + + trash_messages = ListProperty() + has_refreshed = True + delete_index = None + table_name = StringProperty() + + def __init__(self, *args, **kwargs): + """Trash method, delete sent message and add in Trash""" + super(Trash, self).__init__(*args, **kwargs) + Clock.schedule_once(self.init_ui, 0) + + def init_ui(self, dt=0): + """Clock Schdule for method trash screen""" + if state.association == '': + if state.kivyapp.variable_1: + state.association = state.kivyapp.variable_1[0] + self.ids.tag_label.text = '' + if len(self.trash_messages): + self.ids.ml.clear_widgets() + self.ids.tag_label.text = 'Trash' + self.set_TrashCnt(state.trash_count) + self.set_mdList() + self.ids.scroll_y.bind(scroll_y=self.check_scroll_y) + else: + self.set_TrashCnt('0') + content = MDLabel( + font_style='Caption', + theme_text_color='Primary', + text="yet no trashed message for this account!!!!!!!!!!!!!", + halign='center', + size_hint_y=None, + valign='top') + self.ids.ml.add_widget(content) + + def set_TrashCnt(self, Count): # pylint: disable=no-self-use + """This method is used to set trash message count""" + trashCnt_obj = state.kivyapp.root.ids.content_drawer.ids.trash_cnt + trashCnt_obj.ids.badge_txt.text = showLimitedCnt(int(Count)) + + def set_mdList(self): + """This method is used to create the mdlist""" + total_trash_msg = len(self.ids.ml.children) + self.has_refreshed = True if total_trash_msg != len( + self.ids.ml.children) else False + + def on_swipe_complete(self, instance, *args): + """call on swipe left""" + instance.ids.delete_msg.disabled = bool(instance.state == 'closed') + + def check_scroll_y(self, instance, somethingelse): + """Load data on scroll""" + if self.ids.scroll_y.scroll_y <= -0.0 and self.has_refreshed: + self.ids.scroll_y.scroll_y = 0.06 + total_trash_msg = len(self.ids.ml.children) diff --git a/src/tests/mock/pybitmessage/bmconfigparser.py b/src/tests/mock/pybitmessage/bmconfigparser.py new file mode 100644 index 00000000..efeac69b --- /dev/null +++ b/src/tests/mock/pybitmessage/bmconfigparser.py @@ -0,0 +1,273 @@ +""" +BMConfigParser class definition and default configuration settings +""" + +import os +import shutil +import sys # FIXME: bad style! write more generally +from datetime import datetime + +from six import string_types +from six.moves import configparser + +try: + import state + from singleton import Singleton +except ImportError: + from pybitmessage import state + from pybitmessage.singleton import Singleton + +SafeConfigParser = configparser.SafeConfigParser + + +BMConfigDefaults = { + "bitmessagesettings": { + "maxaddrperstreamsend": 500, + "maxbootstrapconnections": 20, + "maxdownloadrate": 0, + "maxoutboundconnections": 8, + "maxtotalconnections": 200, + "maxuploadrate": 0, + "apiinterface": "127.0.0.1", + "apiport": 8442, + "udp": "True" + }, + "threads": { + "receive": 3, + }, + "network": { + "bind": "", + "dandelion": 90, + }, + "inventory": { + "storage": "sqlite", + "acceptmismatch": "False", + }, + "knownnodes": { + "maxnodes": 20000, + }, + "zlib": { + "maxsize": 1048576 + } +} + + +@Singleton +class BMConfigParser(SafeConfigParser): + """ + Singleton class inherited from :class:`ConfigParser.SafeConfigParser` + with additional methods specific to bitmessage config. + """ + # pylint: disable=too-many-ancestors + _temp = {} + + def set(self, section, option, value=None): + if self._optcre is self.OPTCRE or value: + if not isinstance(value, string_types): + raise TypeError("option values must be strings") + if not self.validate(section, option, value): + raise ValueError("Invalid value %s" % value) + return SafeConfigParser.set(self, section, option, value) + + # pylint: disable=redefined-builtinm, too-many-return-statements + def get(self, section, option, raw=False, vars=None): + if sys.version_info[0] == 3: + # pylint: disable=arguments-differ + try: + if section == "bitmessagesettings" and option == "timeformat": + return SafeConfigParser.get( + self, section, option, raw=True, vars=vars) + try: + return self._temp[section][option] + except KeyError: + pass + return SafeConfigParser.get( + self, section, option, raw=True, vars=vars) + except configparser.InterpolationError: + return SafeConfigParser.get( + self, section, option, raw=True, vars=vars) + except (configparser.NoSectionError, configparser.NoOptionError) as e: + try: + return BMConfigDefaults[section][option] + except (KeyError, ValueError, AttributeError): + raise e + else: + # pylint: disable=arguments-differ + try: + if section == "bitmessagesettings" and option == "timeformat": + return SafeConfigParser.get( + self, section, option, raw, vars) + try: + return self._temp[section][option] + except KeyError: + pass + return SafeConfigParser.get( + self, section, option, True, vars) + except configparser.InterpolationError: + return SafeConfigParser.get( + self, section, option, True, vars) + except (configparser.NoSectionError, configparser.NoOptionError) as e: + try: + return BMConfigDefaults[section][option] + except (KeyError, ValueError, AttributeError): + raise e + + def setTemp(self, section, option, value=None): + """Temporary set option to value, not saving.""" + try: + self._temp[section][option] = value + except KeyError: + self._temp[section] = {option: value} + + def safeGetBoolean(self, section, field): + """Return value as boolean, False on exceptions""" + try: + # Used in the python2.7 + # return self.getboolean(section, field) + # Used in the python3.5.2 + # print(config, section, field) + return self.getboolean(section, field) + except (configparser.NoSectionError, configparser.NoOptionError, + ValueError, AttributeError): + return False + + def safeGetInt(self, section, field, default=0): + """Return value as integer, default on exceptions, + 0 if default missing""" + try: + # Used in the python2.7 + # return self.getint(section, field) + # Used in the python3.7.0 + return int(self.get(section, field)) + except (configparser.NoSectionError, configparser.NoOptionError, + ValueError, AttributeError): + return default + + def safeGetFloat(self, section, field, default=0.0): + """Return value as float, default on exceptions, + 0.0 if default missing""" + try: + return self.getfloat(section, field) + except (configparser.NoSectionError, configparser.NoOptionError, + ValueError, AttributeError): + return default + + def safeGet(self, section, option, default=None): + """Return value as is, default on exceptions, None if default missing""" + try: + return self.get(section, option) + except (configparser.NoSectionError, configparser.NoOptionError, + ValueError, AttributeError): + return default + + def items(self, section, raw=False, variables=None): + # pylint: disable=signature-differs + """Return section variables as parent, + but override the "raw" argument to always True""" + return SafeConfigParser.items(self, section, True, variables) + + def _reset(self): + """Reset current config. There doesn't appear to be a built in + method for this""" + sections = self.sections() + for x in sections: + self.remove_section(x) + + if sys.version_info[0] == 3: + @staticmethod + def addresses(hidden=False): + """Return a list of local bitmessage addresses (from section labels)""" + return [x for x in BMConfigParser().sections() if x.startswith('BM-') and ( + hidden or not BMConfigParser().safeGetBoolean(x, 'hidden'))] + + def read(self, filenames): + self._reset() + SafeConfigParser.read(self, filenames) + for section in self.sections(): + for option in self.options(section): + try: + # pylint: disable=unsubscriptable-object + if not self.validate( + section, option, + self[section][option] + ): + try: + newVal = BMConfigDefaults[section][option] + except configparser.NoSectionError: + continue + except KeyError: + continue + SafeConfigParser.set( + self, section, option, newVal) + except configparser.InterpolationError: + continue + + def readfp(self, fp, filename=None): + # pylint: disable=no-member + SafeConfigParser.read_file(self, fp) + else: + @staticmethod + def addresses(): + """Return a list of local bitmessage addresses (from section labels)""" + return [ + x for x in BMConfigParser().sections() if x.startswith('BM-')] + + def read(self, filenames): + """Read config and populate defaults""" + self._reset() + SafeConfigParser.read(self, filenames) + for section in self.sections(): + for option in self.options(section): + try: + if not self.validate( + section, option, + SafeConfigParser.get(self, section, option) + ): + try: + newVal = BMConfigDefaults[section][option] + except KeyError: + continue + SafeConfigParser.set( + self, section, option, newVal) + except configparser.InterpolationError: + continue + + def save(self): + """Save the runtime config onto the filesystem""" + fileName = os.path.join(state.appdata, 'keys.dat') + fileNameBak = '.'.join([ + fileName, datetime.now().strftime("%Y%j%H%M%S%f"), 'bak']) + # create a backup copy to prevent the accidental loss due to + # the disk write failure + try: + shutil.copyfile(fileName, fileNameBak) + # The backup succeeded. + fileNameExisted = True + except (IOError, Exception): + # The backup failed. This can happen if the file + # didn't exist before. + fileNameExisted = False + + with open(fileName, 'w') as configfile: + self.write(configfile) + # delete the backup + if fileNameExisted: + os.remove(fileNameBak) + + def validate(self, section, option, value): + """Input validator interface (using factory pattern)""" + try: + return getattr(self, 'validate_%s_%s' % (section, option))(value) + except AttributeError: + return True + + @staticmethod + def validate_bitmessagesettings_maxoutboundconnections(value): + """Reject maxoutboundconnections that are too high or too low""" + try: + value = int(value) + except ValueError: + return False + if value < 0 or value > 8: + return False + return True diff --git a/src/tests/mock/pybitmessage/class_addressGenerator.py b/src/tests/mock/pybitmessage/class_addressGenerator.py new file mode 100644 index 00000000..ebc2343e --- /dev/null +++ b/src/tests/mock/pybitmessage/class_addressGenerator.py @@ -0,0 +1,79 @@ +""" +A thread for creating addresses +""" + +from six.moves import queue + +from pybitmessage import state +from pybitmessage import queues + +from pybitmessage.bmconfigparser import BMConfigParser + +from pybitmessage.network.threads import StoppableThread + + +fake_addresses = { + 'BM-2cUgQGcTLWAkC6dNsv2Bc8XB3Y1GEesVLV': { + 'privsigningkey': '5KWXwYq1oJMzghUSJaJoWPn8VdeBbhDN8zFot1cBd6ezKKReqBd', + 'privencryptionkey': '5JaeFJs8iPcQT3N8676r3gHKvJ5mTWXy1VLhGCEDqRs4vpvpxV8' + }, + 'BM-2cUd2dm8MVMokruMTcGhhteTpyRZCAMhnA': { + 'privsigningkey': '5JnJ79nkcwjo4Aj7iG8sFMkzYoQqWfpUjTcitTuFJZ1YKHZz98J', + 'privencryptionkey': '5JXgNzTRouFLqSRFJvuHMDHCYPBvTeMPBiHt4Jeb6smNjhUNTYq' + }, + 'BM-2cWyvL54WytfALrJHZqbsDHca5QkrtByAW': { + 'privsigningkey': '5KVE4gLmcfYVicLdgyD4GmnbBTFSnY7Yj2UCuytQqgBBsfwDhpi', + 'privencryptionkey': '5JTw48CGm5CP8fyJUJQMq8HQANQMHDHp2ETUe1dgm6EFpT1egD7' + }, + 'BM-2cTE65PK9Y4AQEkCZbazV86pcQACocnRXd': { + 'privsigningkey': '5KCuyReHx9MB4m5hhEyCWcLEXqc8rxhD1T2VWk8CicPFc8B6LaZ', + 'privencryptionkey': '5KBRpwXdX3n2tP7f583SbFgfzgs6Jemx7qfYqhdH7B1Vhe2jqY6' + }, + 'BM-2cX5z1EgmJ87f2oKAwXdv4VQtEVwr2V3BG': { + 'privsigningkey': '5K5UK7qED7F1uWCVsehudQrszLyMZxFVnP6vN2VDQAjtn5qnyRK', + 'privencryptionkey': '5J5coocoJBX6hy5DFTWKtyEgPmADpSwfQTazMpU7QPeART6oMAu' + } +} + + +class addressGenerator(StoppableThread): + """A thread for creating fake addresses""" + name = "addressGenerator" + address_list = list(fake_addresses.keys()) + + def stopThread(self): + """"To stop address generator thread""" + try: + queues.addressGeneratorQueue.put(("stopThread", "data")) + except queue.Full: + self.logger.warning('addressGeneratorQueue is Full') + super(addressGenerator, self).stopThread() + + def run(self): + """ + Process the requests for addresses generation + from `.queues.addressGeneratorQueue` + """ + while state.shutdown == 0: + queueValue = queues.addressGeneratorQueue.get() + try: + address = self.address_list.pop(0) + print("queueValue: ", queueValue) + if len(queueValue) >= 3: + label = queueValue[3] + else: + label = '' + + BMConfigParser().add_section(address) + BMConfigParser().set(address, 'label', label) + BMConfigParser().set(address, 'enabled', 'true') + BMConfigParser().set( + address, 'privsigningkey', fake_addresses[address]['privsigningkey']) + BMConfigParser().set( + address, 'privencryptionkey', fake_addresses[address]['privencryptionkey']) + BMConfigParser().save() + + queues.addressGeneratorQueue.task_done() + except IndexError: + self.logger.error( + 'Program error: you can only create 5 fake addresses') diff --git a/src/tests/mock/pybitmessage/get_platform.py b/src/tests/mock/pybitmessage/get_platform.py new file mode 100644 index 00000000..e4fea889 --- /dev/null +++ b/src/tests/mock/pybitmessage/get_platform.py @@ -0,0 +1,48 @@ +from sys import platform as _sys_platform +from os import environ + +""" +We need to check platform and set environ for KIVY_CAMERA, if requires, before importing kivy. + +We cannot use sys.platform directly because it returns 'linux' on android devices as well. +We cannot use kivy.util.platform beacuse it imports kivy beforehand and thus setting environ +after that doesn't make any sense. + +So we needed to copy the `_get_platform` function from kivy.utils +""" + + +def _get_platform(): + # On Android sys.platform returns 'linux2', so prefer to check the + # existence of environ variables set during Python initialization + kivy_build = environ.get("KIVY_BUILD", "") + if kivy_build in {"android", "ios"}: + return kivy_build + elif "P4A_BOOTSTRAP" in environ: + return "android" + elif "ANDROID_ARGUMENT" in environ: + # We used to use this method to detect android platform, + # leaving it here to be backwards compatible with `pydroid3` + # and similar tools outside kivy's ecosystem + return "android" + elif _sys_platform in ("win32", "cygwin"): + return "win" + elif _sys_platform == "darwin": + return "macosx" + elif _sys_platform.startswith("linux"): + return "linux" + elif _sys_platform.startswith("freebsd"): + return "linux" + return "unknown" + +platform = _get_platform() + +if platform not in ("android", "unknown"): + """ + After tweaking a little bit with opencv camera, it's possible to make camera + go on and off as required while the app is still running. + + Other camera provider such as `gi` has some issue upon closing the camera. + by setting KIVY_CAMERA environment variable before importing kivy, we are forcing it to use opencv camera provider. + """ + environ["KIVY_CAMERA"] = "opencv" diff --git a/src/tests/mock/pybitmessage/inventory.py b/src/tests/mock/pybitmessage/inventory.py new file mode 100644 index 00000000..6173c3cd --- /dev/null +++ b/src/tests/mock/pybitmessage/inventory.py @@ -0,0 +1,15 @@ +"""The Inventory singleton""" + +# TODO make this dynamic, and watch out for frozen, like with messagetypes +from pybitmessage.singleton import Singleton + + +# pylint: disable=old-style-class,too-few-public-methods +@Singleton +class Inventory(): + """ + Inventory singleton class which uses storage backends + to manage the inventory. + """ + def __init__(self): + self.numberOfInventoryLookupsPerformed = 0 diff --git a/src/tests/mock/pybitmessage/kv/addressbook.kv b/src/tests/mock/pybitmessage/kv/addressbook.kv new file mode 100644 index 00000000..73b4c1ef --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/addressbook.kv @@ -0,0 +1,26 @@ +: + name: 'addressbook' + BoxLayout: + orientation: 'vertical' + spacing: dp(5) + SearchBar: + id: address_search + GridLayout: + id: identi_tag + padding: [20, 0, 0, 5] + cols: 1 + size_hint_y: None + height: self.minimum_height + MDLabel: + id: tag_label + text: '' + font_style: 'Subtitle2' + BoxLayout: + orientation:'vertical' + ScrollView: + id: scroll_y + do_scroll_x: False + MDList: + id: ml + Loader: + ComposerButton: \ No newline at end of file diff --git a/src/tests/mock/pybitmessage/kv/allmails.kv b/src/tests/mock/pybitmessage/kv/allmails.kv new file mode 100644 index 00000000..f1b9387e --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/allmails.kv @@ -0,0 +1,25 @@ +: + name: 'allmails' + BoxLayout: + orientation: 'vertical' + spacing: dp(5) + GridLayout: + id: identi_tag + padding: [20, 20, 0, 5] + spacing: dp(5) + cols: 1 + size_hint_y: None + height: self.minimum_height + MDLabel: + id: tag_label + text: '' + font_style: 'Subtitle2' + BoxLayout: + orientation:'vertical' + ScrollView: + id: scroll_y + do_scroll_x: False + MDList: + id: ml + Loader: + ComposerButton: \ No newline at end of file diff --git a/src/tests/mock/pybitmessage/kv/chat_list.kv b/src/tests/mock/pybitmessage/kv/chat_list.kv new file mode 100644 index 00000000..e59c32d7 --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/chat_list.kv @@ -0,0 +1,58 @@ +: + name: 'chlist' + canvas.before: + Color: + rgba: 1,1,1,1 + Rectangle: + pos: self.pos + size: self.size + MDTabs: + id: chat_panel + tab_display_mode:'text' + + Tab: + text: app.tr._("Chats") + BoxLayout: + id: chat_box + orientation: 'vertical' + ScrollView: + id: scroll_y + do_scroll_x: False + MDList: + id: ml + MDLabel: + font_style: 'Caption' + theme_text_color: 'Primary' + text: app.tr._('No Chat') + halign: 'center' + size_hint_y: None + bold: True + valign: 'top' + # OneLineAvatarListItem: + # text: "Single-line item with avatar" + # divider: None + # _no_ripple_effect: True + # ImageLeftWidget: + # source: './images/text_images/A.png' + # OneLineAvatarListItem: + # text: "Single-line item with avatar" + # divider: None + # _no_ripple_effect: True + # ImageLeftWidget: + # source: './images/text_images/B.png' + # OneLineAvatarListItem: + # text: "Single-line item with avatar" + # divider: None + # _no_ripple_effect: True + # ImageLeftWidget: + # source: './images/text_images/A.png' + Tab: + text: app.tr._("Contacts") + BoxLayout: + id: contact_box + orientation: 'vertical' + ScrollView: + id: scroll_y + do_scroll_x: False + MDList: + id: ml diff --git a/src/tests/mock/pybitmessage/kv/chat_room.kv b/src/tests/mock/pybitmessage/kv/chat_room.kv new file mode 100644 index 00000000..40843c47 --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/chat_room.kv @@ -0,0 +1,45 @@ +#:import C kivy.utils.get_color_from_hex + +: + name: 'chroom' + BoxLayout: + orientation: 'vertical' + canvas.before: + Color: + rgba: 1,1,1,1 + Rectangle: + pos: self.pos + size: self.size + ScrollView: + Label: + id: chat_logs + text: '' + color: C('#101010') + text_size: (self.width, None) + halign: 'left' + valign: 'top' + padding: (0, 0) # fixed in Kivy 1.8.1 + size_hint: (1, None) + height: self.texture_size[1] + markup: True + font_size: sp(20) + BoxLayout: + height: 50 + orientation: 'horizontal' + padding: 0 + size_hint: (1, None) + + TextInput: + id: message + size_hint: (1, 1) + multiline: False + font_size: sp(20) + on_text_validate: root.send_msg() + + MDRaisedButton: + text: app.tr._("Send") + elevation_normal: 2 + opposite_colors: True + size_hint: (0.3, 1) + pos_hint: {"center_x": .5} + on_press: root.send_msg() diff --git a/src/tests/mock/pybitmessage/kv/common_widgets.kv b/src/tests/mock/pybitmessage/kv/common_widgets.kv new file mode 100644 index 00000000..f864256d --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/common_widgets.kv @@ -0,0 +1,62 @@ +: + source: app.image_path +('/down-arrow.png' if self.parent.is_open == True else '/right-arrow.png') + size: 15, 15 + x: self.parent.x + self.parent.width - self.width - 5 + y: self.parent.y + self.parent.height/2 - self.height + 5 + +: + # id: search_bar + size_hint_y: None + height: self.minimum_height + + MDIconButton: + icon: 'magnify' + + MDTextField: + id: search_field + hint_text: 'Search' + on_text: app.searchQuery(self) + canvas.before: + Color: + rgba: (0,0,0,1) + +: + id: spinner + size_hint: None, None + size: dp(46), dp(46) + pos_hint: {'center_x': 0.5, 'center_y': 0.5} + active: False + +: + size_hint_y: None + height: dp(56) + spacing: '10dp' + pos_hint: {'center_x':0.45, 'center_y': .1} + + Widget: + + MDFloatingActionButton: + icon: 'plus' + opposite_colors: True + elevation_normal: 8 + md_bg_color: [0.941, 0, 0,1] + on_press: app.root.ids.scr_mngr.current = 'create' + on_press: app.clear_composer() + + + +: + size_hint: None, None + size: dp(36), dp(48) + pos_hint: {'center_x': .95, 'center_y': .4} + on_active: app.root.ids.sc10.toggleAction(self) + +: + canvas: + Color: + id: set_clr + # rgba: 0.5, 0.5, 0.5, 0.5 + rgba: 0,0,0,0 + Rectangle: #woohoo!!! + size: self.size + pos: self.pos diff --git a/src/tests/mock/pybitmessage/kv/credits.kv b/src/tests/mock/pybitmessage/kv/credits.kv new file mode 100644 index 00000000..b5eb3db7 --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/credits.kv @@ -0,0 +1,28 @@ +: + name: 'credits' + ScrollView: + do_scroll_x: False + BoxLayout: + size_hint_y: None + orientation: 'vertical' + OneLineListTitle: + id: cred + text: app.tr._("Available Credits") + divider: None + theme_text_color: 'Primary' + _no_ripple_effect: True + long_press_time: 1 + + OneLineListTitle: + id: cred + text: app.tr._(root.available_credits) + divider: None + font_style: 'H5' + theme_text_color: 'Primary' + _no_ripple_effect: True + long_press_time: 1 + AnchorLayout: + MDRaisedButton: + height: dp(38) + text: app.tr._("+Add more credits") + on_press: app.root.ids.scr_mngr.current = 'payment' diff --git a/src/tests/mock/pybitmessage/kv/draft.kv b/src/tests/mock/pybitmessage/kv/draft.kv new file mode 100644 index 00000000..56682d2b --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/draft.kv @@ -0,0 +1,23 @@ +: + name: 'draft' + BoxLayout: + orientation: 'vertical' + spacing: dp(5) + GridLayout: + id: identi_tag + padding: [20, 20, 0, 5] + cols: 1 + size_hint_y: None + height: self.minimum_height + MDLabel: + id: tag_label + text: '' + font_style: 'Subtitle2' + BoxLayout: + orientation:'vertical' + ScrollView: + id: scroll_y + do_scroll_x: False + MDList: + id: ml + ComposerButton: \ No newline at end of file diff --git a/src/tests/mock/pybitmessage/kv/inbox.kv b/src/tests/mock/pybitmessage/kv/inbox.kv new file mode 100644 index 00000000..b9cc8566 --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/inbox.kv @@ -0,0 +1,39 @@ +: + name: 'inbox' + #transition: NoTransition() + BoxLayout: + orientation: 'vertical' + spacing: dp(5) + SearchBar: + id:inbox_search + GridLayout: + id: identi_tag + padding: [20, 0, 0, 5] + cols: 1 + size_hint_y: None + height: self.minimum_height + MDLabel: + id: tag_label + text: '' + font_style: 'Subtitle2' + #FloatLayout: + # MDScrollViewRefreshLayout: + # id: refresh_layout + # refresh_callback: root.refresh_callback + # root_layout: root.set_root_layout() + # MDList: + # id: ml + BoxLayout: + orientation:'vertical' + ScrollView: + id: scroll_y + do_scroll_x: False + MDList: + id: ml + Loader: + ComposerButton: + +: + size_hint:(None, None) + font_style: 'Caption' + halign: 'center' diff --git a/src/tests/mock/pybitmessage/kv/login.kv b/src/tests/mock/pybitmessage/kv/login.kv new file mode 100644 index 00000000..44e24c04 --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/login.kv @@ -0,0 +1,264 @@ +#:import SlideTransition kivy.uix.screenmanager.SlideTransition +: + name:"login" + BoxLayout: + orientation: "vertical" + + #buttons-area-outer + BoxLayout: + size_hint_y: .53 + canvas: + Color: + rgba: 1,1,1,1 + Rectangle: + pos: self.pos + size: self.size + + ScreenManager: + id: check_screenmgr + Screen: + name: "check_screen" + BoxLayout: + orientation: "vertical" + padding: 0, dp(5), 0, dp(5) + spacing: dp(5) + + #label area + AnchorLayout: + size_hint_y: None + height: dp(50) + MDLabel: + text: app.tr._("Select method to make an address:") + bold: True + halign: "center" + theme_text_color: "Custom" + text_color: .4,.4,.4,1 + + #upper-checkbor-area + AnchorLayout: + size_hint_y: None + height: dp(40) + BoxLayout: + size_hint_x: None + width: self.minimum_width + + #check-container + AnchorLayout: + size_hint_x: None + width: dp(40) + Check: + active: True + + #text-container + AnchorLayout: + size_hint_x: None + width: dp(200) + MDLabel: + text: app.tr._("Random Number Generator") + + AnchorLayout: + size_hint_y: None + height: dp(40) + BoxLayout: + size_hint_x: None + width: self.minimum_width + + #check-container + AnchorLayout: + size_hint_x: None + width: dp(40) + Check: + + #text-container + AnchorLayout: + size_hint_x: None + width: dp(200) + MDLabel: + text: app.tr._("Pseudo Number Generator") + AnchorLayout: + MDFillRoundFlatIconButton: + icon: "chevron-double-right" + text: app.tr._("Proceed Next") + on_release: + app.root.ids.scr_mngr.current = 'random' + on_press: + app.root.ids.sc7.reset_address_label() + + #info-area-outer + BoxLayout: + size_hint_y: .47 + padding: dp(7) + InfoLayout: + orientation:"vertical" + padding: 0, dp(5), 0, dp(5) + canvas: + Color: + rgba:1,1,1,1 + Rectangle: + pos: self.pos + size: self.size + Color: + rgba: app.theme_cls.primary_color + Line: + rounded_rectangle: (self.pos[0]+4, self.pos[1]+4, self.width-8,self.height-8, 10, 10, 10, 10, 50) + width: dp(1) + ScreenManager: + id: info_screenmgr + + Screen: + name: "info1" + ScrollView: + bar_width:0 + do_scroll_x: False + + BoxLayout: + orientation: "vertical" + size_hint_y: None + height: self.minimum_height + + #note area + ContentHead: + section_name: "NOTE:" + ContentBody: + section_text: ("You may generate addresses by using either random numbers or by using a pass-phrase.If you use a pass-phrase, the address is called a deterministic address. The Random Number option is selected by default but deterministic addresses may have several pros and cons.") + + + #pros area + ContentHead: + section_name: "PROS:" + ContentBody: + section_text: ("You can re-create your addresses on any computer from memory you need-not-to worry about backing up your keys.dat file as long as you can remember your pass-phrase.") + + #cons area + ContentHead: + section_name: "CONS:" + ContentBody: + section_text: ("You must remember (or write down) your address version number and the stream number along with your pass-phrase.If you choose a weak pass-phrase and someone on the internet can brute-force it, they can read your messages and send messages as you.") + +: + name:"random" + ScrollView: + id:add_random_bx + +: + orientation: "vertical" + #buttons-area-outer + BoxLayout: + orientation: "vertical" + # padding: 0, dp(5), 0, dp(5) + # spacing: dp(5) + size_hint_y: .53 + canvas: + Color: + rgba: 1,1,1,1 + Rectangle: + pos: self.pos + size: self.size + + #label area + AnchorLayout: + size_hint_y: None + height: dp(50) + MDLabel: + text: app.tr._("Enter a label to generate address for:") + bold: True + halign: "center" + theme_text_color: "Custom" + text_color: .4,.4,.4,1 + + AnchorLayout: + size_hint_y: None + height: dp(40) + MDTextField: + id:lab + hint_text: "Label" + required: True + size_hint_x: None + width: dp(190) + helper_text_mode: "on_error" + # helper_text: "Please enter your label name" + on_text: app.root.ids.sc7.add_validation(self) + canvas.before: + Color: + rgba: (0,0,0,1) + + AnchorLayout: + MDFillRoundFlatIconButton: + icon: "chevron-double-right" + text: app.tr._("Proceed Next") + on_release: app.root.ids.sc7.generateaddress() + + Widget: + + #info-area-outer + BoxLayout: + size_hint_y: .47 + padding: dp(7) + InfoLayout: + orientation:"vertical" + padding: 0, dp(5), 0, dp(5) + canvas: + Color: + rgba:1,1,1,1 + Rectangle: + pos: self.pos + size: self.size + Color: + rgba: app.theme_cls.primary_color + Line: + rounded_rectangle: (self.pos[0]+4, self.pos[1]+4, self.width-8,self.height-8, 10, 10, 10, 10, 50) + width: dp(1) + ScreenManager: + id: info_screenmgr + + Screen: + name: "info2" + ScrollView: + bar_width:0 + do_scroll_x: False + + BoxLayout: + orientation: "vertical" + size_hint_y: None + height: self.minimum_height + + #note area + ContentHead: + section_name: "NOTE:" + ContentBody: + section_text: ("Here you may generate as many addresses as you like..Indeed creating and abandoning addresses is not encouraged.") + +: + group: 'group' + size_hint: None, None + size: dp(48), dp(48) + +: + section_name: "" + orientation: "vertical" + size_hint_y: None + height: dp(50) + padding: dp(20), 0, 0, 0 + Widget: + size_hint_y: None + height: dp(25) + MDLabel: + theme_text_color: "Custom" + text_color: .1,.1,.1,.9 + text: app.tr._(root.section_name) + bold: True + font_style: "Button" + +: + section_text: "" + size_hint_y: None + height: self.minimum_height + padding: dp(50), 0, dp(10), 0 + + MDLabel: + size_hint_y: None + height: self.texture_size[1]+dp(10) + theme_text_color: "Custom" + text_color: 0.3,0.3,0.3,1 + font_style: "Body1" + text: app.tr._(root.section_text) diff --git a/src/tests/mock/pybitmessage/kv/maildetail.kv b/src/tests/mock/pybitmessage/kv/maildetail.kv new file mode 100644 index 00000000..6adf86aa --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/maildetail.kv @@ -0,0 +1,77 @@ +: + name: 'mailDetail' + ScrollView: + do_scroll_x: False + BoxLayout: + size_hint_y: None + orientation: 'vertical' + height: self.minimum_height + padding: dp(10) + OneLineListTitle: + id: subj + text: app.tr._(root.subject) + divider: None + font_style: 'H5' + theme_text_color: 'Primary' + _no_ripple_effect: True + long_press_time: 1 + TwoLineAvatarIconListItem: + id: subaft + text: app.tr._(root.from_addr) + secondary_text: app.tr._('to ' + root.to_addr) + divider: None + on_press: root.detailedPopup() + BadgeText: + size_hint:(None, None) + size:[120, 140] if app.app_platform == 'android' else [64, 80] + text: app.tr._(root.time_tag) + halign:'center' + font_style:'Caption' + pos_hint: {'center_y': .8} + _txt_right_pad: dp(70) + font_size: '11sp' + MDChip: + size_hint: (.16 if app.app_platform == 'android' else .08 , None) + text: app.tr._(root.page_type) + icon: '' + text_color: (1,1,1,1) + pos_hint: {'center_x': .91 if app.app_platform == 'android' else .95, 'center_y': .3} + radius: [8] + height: self.parent.height/4 + AvatarSampleWidget: + source: root.avatarImg + MDLabel: + text: root.status + disabled: True + font_style: 'Body2' + halign:'left' + padding_x: 20 + # MDLabel: + # id: bod + # font_style: 'Subtitle2' + # theme_text_color: 'Primary' + # text: root.message + # halign: 'left' + # height: self.texture_size[1] + MyMDTextField: + id: bod + size_hint_y: None + font_style: 'Subtitle2' + text: root.message + multiline: True + readonly: True + line_color_normal: [0,0,0,0] + _current_line_color: [0,0,0,0] + line_color_focus: [0,0,0,0] + markup: True + font_size: '15sp' + canvas.before: + Color: + rgba: (0,0,0,1) + Loader: + + +: + canvas.before: + Color: + rgba: (0,0,0,1) diff --git a/src/tests/mock/pybitmessage/kv/msg_composer.kv b/src/tests/mock/pybitmessage/kv/msg_composer.kv new file mode 100644 index 00000000..82a2a8cb --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/msg_composer.kv @@ -0,0 +1,178 @@ +: + name: 'create' + Loader: + + +: + ScrollView: + id: id_scroll + BoxLayout: + orientation: 'vertical' + size_hint_y: None + height: self.minimum_height + 3 * self.parent.height/5 + padding: dp(20) + spacing: 15 + BoxLayout: + orientation: 'vertical' + MDTextField: + id: ti + size_hint_y: None + hint_text: 'Type or Select sender address' + icon_right: 'account' + icon_right_color: app.theme_cls.primary_light + font_size: '15sp' + multiline: False + required: True + # height: self.parent.height/2 + height: 100 + current_hint_text_color: 0,0,0,0.5 + helper_text_mode: "on_error" + canvas.before: + Color: + rgba: (0,0,0,1) + + + BoxLayout: + size_hint_y: None + height: dp(40) + CustomSpinner: + id: btn + background_color: app.theme_cls.primary_dark + values: app.variable_1 + on_text: root.auto_fill_fromaddr() if self.text != 'Select' else '' + option_cls: Factory.get("ComposerSpinnerOption") + #background_color: color_button if self.state == 'normal' else color_button_pressed + #background_down: 'atlas://data/images/defaulttheme/spinner' + background_normal: '' + background_color: app.theme_cls.primary_color + color: color_font + font_size: '13.5sp' + ArrowImg: + + + RelativeLayout: + orientation: 'horizontal' + BoxLayout: + orientation: 'vertical' + txt_input: txt_input + rv: rv + size : (890, 60) + MyTextInput: + id: txt_input + size_hint_y: None + font_size: '15sp' + color: color_font + # height: self.parent.height/2 + current_hint_text_color: 0,0,0,0.5 + height: 100 + hint_text: app.tr._('Type or Scan QR code for recipients address') + canvas.before: + Color: + rgba: (0,0,0,1) + + RV: + id: rv + MDIconButton: + icon: 'qrcode-scan' + pos_hint: {'center_x': 0.95, 'y': 0.6} + on_release: + if root.is_camara_attached(): app.root.ids.scr_mngr.current = 'scanscreen' + else: root.camera_alert() + on_press: + app.root.ids.sc23.get_screen('composer') + + MyMDTextField: + id: subject + hint_text: 'Subject' + height: 100 + font_size: '15sp' + icon_right: 'notebook-outline' + icon_right_color: app.theme_cls.primary_light + current_hint_text_color: 0,0,0,0.5 + font_color_normal: 0, 0, 0, 1 + size_hint_y: None + required: True + multiline: False + helper_text_mode: "on_focus" + canvas.before: + Color: + rgba: (0,0,0,1) + + # MyMDTextField: + # id: body + # multiline: True + # hint_text: 'body' + # size_hint_y: None + # font_size: '15sp' + # required: True + # helper_text_mode: "on_error" + # canvas.before: + # Color: + # rgba: (0,0,0,1) + ScrollView: + id: scrlv + MDTextField: + id: body + hint_text: 'Body' + mode: "fill" + fill_color: 1/255, 144/255, 254/255, 0.1 + multiline: True + font_color_normal: 0, 0, 0, .4 + icon_right: 'grease-pencil' + icon_right_color: app.theme_cls.primary_light + size_hint: 1, 1 + height: app.window_size[1]/4 + canvas.before: + Color: + rgba: 125/255, 125/255, 125/255, 1 + BoxLayout: + spacing:50 + +: + readonly: False + multiline: False + + +: + # Draw a background to indicate selection + color: 0,0,0,1 + canvas.before: + Color: + rgba: app.theme_cls.primary_dark if self.selected else (1, 1, 1, 0) + Rectangle: + pos: self.pos + size: self.size + +: + canvas: + Color: + rgba: 0,0,0,.2 + + Line: + rectangle: self.x +1 , self.y, self.width - 2, self.height -2 + bar_width: 10 + scroll_type:['bars'] + viewclass: 'SelectableLabel' + SelectableRecycleBoxLayout: + default_size: None, dp(20) + default_size_hint: 1, None + size_hint_y: None + height: self.minimum_height + orientation: 'vertical' + multiselect: False + + +: + canvas.before: + Color: + rgba: (0,0,0,1) + + + +: + font_size: '13.5sp' + #background_color: color_button if self.state == 'down' else color_button_pressed + #background_down: 'atlas://data/images/defaulttheme/button' + background_normal: 'atlas://data/images/defaulttheme/textinput_active' + background_color: app.theme_cls.primary_color + color: color_font \ No newline at end of file diff --git a/src/tests/mock/pybitmessage/kv/myaddress.kv b/src/tests/mock/pybitmessage/kv/myaddress.kv new file mode 100644 index 00000000..93496eeb --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/myaddress.kv @@ -0,0 +1,33 @@ +: + name: 'myaddress' + BoxLayout: + id: main_box + orientation: 'vertical' + spacing: dp(5) + SearchBar: + id: search_bar + GridLayout: + id: identi_tag + padding: [20, 0, 0, 5] + cols: 1 + size_hint_y: None + height: self.minimum_height + MDLabel: + id: tag_label + text: app.tr._('My Addresses') + font_style: 'Subtitle2' + FloatLayout: + MDScrollViewRefreshLayout: + id: refresh_layout + refresh_callback: root.refresh_callback + root_layout: root + MDList: + id: ml + Loader: + + +: + size_hint: None, None + size: dp(36), dp(48) + pos_hint: {'center_x': .95, 'center_y': .4} + on_active: app.root.ids.sc10.toggleAction(self) \ No newline at end of file diff --git a/src/tests/mock/pybitmessage/kv/network.kv b/src/tests/mock/pybitmessage/kv/network.kv new file mode 100644 index 00000000..b77dea26 --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/network.kv @@ -0,0 +1,131 @@ +: + name: 'networkstat' + MDTabs: + id: tab_panel + tab_display_mode:'text' + + Tab: + text: app.tr._("Total connections") + ScrollView: + do_scroll_x: False + MDList: + id: ml + size_hint_y: None + height: dp(200) + OneLineListItem: + text: app.tr._("Total Connections") + _no_ripple_effect: True + BoxLayout: + orientation: 'vertical' + size_hint_y: None + height: dp(58) + MDRaisedButton: + _no_ripple_effect: True + # size_hint: .6, 0 + # height: dp(40) + text: app.tr._(root.text_variable_1) + elevation_normal: 2 + opposite_colors: True + pos_hint: {'center_x': .5} + # MDLabel: + # font_style: 'H6' + # text: app.tr._(root.text_variable_1) + # font_size: '13sp' + # color: (1,1,1,1) + # halign: 'center' + Tab: + text: app.tr._('Processes') + ScrollView: + do_scroll_x: False + MDList: + id: ml + size_hint_y: None + height: dp(500) + OneLineListItem: + text: app.tr._("person-to-person") + _no_ripple_effect: True + + BoxLayout: + orientation: 'vertical' + size_hint_y: None + height: dp(58) + MDRaisedButton: + _no_ripple_effect: True + # size_hint: .6, 0 + # height: dp(40) + text: app.tr._(root.text_variable_2) + elevation_normal: 2 + opposite_colors: True + pos_hint: {'center_x': .5} + # MDLabel: + # font_style: 'H6' + # text: app.tr._(root.text_variable_2) + # font_size: '13sp' + # color: (1,1,1,1) + # halign: 'center' + OneLineListItem: + text: app.tr._("Brodcast") + _no_ripple_effect: True + + BoxLayout: + orientation: 'vertical' + size_hint_y: None + height: dp(58) + MDRaisedButton: + _no_ripple_effect: True + # size_hint: .6, 0 + # height: dp(40) + text: app.tr._(root.text_variable_3) + elevation_normal: 2 + opposite_colors: True + pos_hint: {'center_x': .5} + # MDLabel: + # font_style: 'H6' + # text: app.tr._(root.text_variable_3) + # font_size: '13sp' + # color: (1,1,1,1) + # halign: 'center' + OneLineListItem: + text: app.tr._("publickeys") + _no_ripple_effect: True + + BoxLayout: + orientation: 'vertical' + size_hint_y: None + height: dp(58) + MDRaisedButton: + _no_ripple_effect: True + # size_hint: .6, 0 + # height: dp(40) + text: app.tr._(root.text_variable_4) + elevation_normal: 2 + opposite_colors: True + pos_hint: {'center_x': .5} + # MDLabel: + # font_style: 'H6' + # text: app.tr._(root.text_variable_4) + # font_size: '13sp' + # color: (1,1,1,1) + # halign: 'center' + OneLineListItem: + text: app.tr._("objects") + _no_ripple_effect: True + + BoxLayout: + orientation: 'vertical' + size_hint_y: None + height: dp(58) + MDRaisedButton: + _no_ripple_effect: True + # size_hint: .6, 0 + #height: dp(40) + text: app.tr._(root.text_variable_5) + elevation_normal: 2 + opposite_colors: True + pos_hint: {'center_x': .5} + # MDLabel: + # font_style: 'H6' + # text: app.tr._(root.text_variable_5) + # font_size: '13sp' + # color: (1,1,1,1) + # halign: 'center' diff --git a/src/tests/mock/pybitmessage/kv/payment.kv b/src/tests/mock/pybitmessage/kv/payment.kv new file mode 100644 index 00000000..542e8e0e --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/payment.kv @@ -0,0 +1,253 @@ +#:import get_color_from_hex kivy.utils.get_color_from_hex + +: + name: "payment" + BoxLayout: + ScrollView: + bar_width:0 + do_scroll_x: False + #scroll_y:0 + + BoxLayout: + spacing: dp(8) + padding: dp(5) + size_hint_y: None + height: self.minimum_height + orientation: "vertical" + + ProductCategoryLayout: + category_text: "Monthly-Subscriptions" + + ProductLayout: + heading_text: "Gas (Play Billing Codelab)" + price_text: "$0.99" + source: app.image_path + "/payment/buynew1.png" + description_text: "Buy gasoline to ride!" + product_id: "SKUGASBILLING" + + ProductLayout: + heading_text: "Upgrade your car (Play Billing Codelab)" + price_text: "$1.49" + source: app.image_path + "/payment/buynew1.png" + description_text: "Buy a premium outfit for your car!" + product_id: "SKUUPGRADECAR" + + ProductLayout: + heading_text: "Month in gold status (Play Billing Codelab)" + price_text: "$0.99" + source: app.image_path + "/payment/buynew1.png" + description_text: "Enjoy a gold status for a month!" + product_id: "SKUMONTHLYGOLD" + + ProductCategoryLayout: + category_text: "One-time payment" + + ProductLayout: + heading_text: "Gas (Play Billing Codelab)" + price_text: "$0.99" + source: app.image_path + "/payment/buynew1.png" + description_text: "Buy gasoline to ride!" + product_id: "SKUONETIMEGAS" + + ProductCategoryLayout: + category_text: "Annual-Subscriptions" + + ProductLayout: + heading_text: "Gas (Play Billing Codelab)" + price_text: "$0.99" + source: app.image_path + "/payment/buynew1.png" + description_text: "Buy gasoline to ride!" + product_id: "SKUANNUALGAS" + + ProductLayout: + heading_text: "Year in gold status (Play Billing Codelab)" + price_text: "$10.99" + source: app.image_path + "/payment/buynew1.png" + description_text: "Enjoy a gold status for a year!" + product_id: "SKUANNUALGOLD" + +: + size_hint_y: None + height: self.minimum_height + category_text:"" + + orientation: "vertical" + spacing: 2 + + #category area + Category: + text_: root.category_text + +: + canvas: + Color: + rgba: 1,1,1,1 + Rectangle: + pos: self.pos + size: self.size + text_: "" + size_hint_y: None + height: dp(30) + Widget: + size_hint_x: None + width: dp(20) + MDLabel: + text: root.text_ + font_size: sp(15) + +: + heading_text: "" + price_text: "" + source: "" + description_text: "" + + product_id: "" + + canvas: + Color: + rgba: 1,1,1,1 + Rectangle: + pos: self.pos + size: self.size + + size_hint_y: None + height: dp(200) + orientation: "vertical" + + #heading area + BoxLayout: + size_hint_y: 0.3 + + #text heading + BoxLayout: + Widget: + size_hint_x: None + width: dp(20) + MDLabel: + text: root.heading_text + bold: True + + #price text + BoxLayout: + size_hint_x:.3 + MDLabel: + text: root.price_text + bold: True + halign: "right" + theme_text_color: "Custom" + text_color: 0,0,1,1 + Widget: + size_hint_x: None + width: dp(20) + + #details area + BoxLayout: + size_hint_y: 0.3 + Widget: + size_hint_x: None + width: dp(20) + + #image area + AnchorLayout: + size_hint_x: None + width: self.height + BoxLayout: + canvas: + Color: + rgba: 1,1,1,1 + Ellipse: + size: self.size + pos: self.pos + source: root.source + Widget: + size_hint_x: None + width: dp(10) + + #description text + BoxLayout: + #size_hint_x: 1 + MDLabel: + text: root.description_text + font_size: sp(15) + + #Button Area + BoxLayout: + size_hint_y: 0.4 + Widget: + + AnchorLayout: + anchor_x: "right" + MDRaisedButton: + elevation_normal: 5 + text: "BUY" + on_release: + #print(app) + app.open_payment_layout(root.product_id) + + Widget: + size_hint_x: None + width: dp(20) + +: + on_release: app.initiate_purchase(self.method_name) + recent: False + source: "" + method_name: "" + right_label_text: "Recent" if self.recent else "" + + ImageLeftWidget: + source: root.source + + RightLabel: + text: root.right_label_text + theme_text_color: "Custom" + text_color: 0,0,0,.4 + font_size: sp(12) + +: + orientation: "vertical" + size_hint_y: None + height: "200dp" + + BoxLayout: + size_hint_y: None + height: dp(40) + + Widget: + size_hint_x: None + width: dp(20) + MDLabel: + text: "Select Payment Method" + font_size: sp(14) + bold: True + theme_text_color: "Custom" + text_color: 0,0,0,.5 + + + ScrollView: + + GridLayout: + cols: 1 + size_hint_y:None + height:self.minimum_height + + ListItemWithLabel: + source: app.image_path + "/payment/gplay.png" + text: "Google Play" + method_name: "gplay" + recent: True + + ListItemWithLabel: + source: app.image_path + "/payment/btc.png" + text: "BTC" + method_name: "btc" + + ListItemWithLabel: + source: app.image_path + "/payment/paypal.png" + text: "Paypal" + method_name: "som" + + ListItemWithLabel: + source: app.image_path + "/payment/buy.png" + text: "One more method" + method_name: "omm" \ No newline at end of file diff --git a/src/tests/mock/pybitmessage/kv/popup.kv b/src/tests/mock/pybitmessage/kv/popup.kv new file mode 100644 index 00000000..217d9131 --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/popup.kv @@ -0,0 +1,333 @@ +: + separator_color: 1, 1, 1, 1 + background: "White.png" + Button: + id: btn + disabled: True + background_disabled_normal: "White.png" + Image: + source: app.image_path + '/loader.zip' + anim_delay: 0 + #mipmap: True + size: root.size + + +: + id: popup_box + orientation: 'vertical' + # spacing:dp(20) + # spacing: "12dp" + size_hint_y: None + # height: "120dp" + height: label.height+address.height + BoxLayout: + orientation: 'vertical' + MDTextField: + id: label + multiline: False + hint_text: app.tr._("Label") + required: True + icon_right: 'label' + helper_text_mode: "on_error" + on_text: root.checkLabel_valid(self) + canvas.before: + Color: + rgba: (0,0,0,1) + MDTextField: + id: address + hint_text: app.tr._("Address") + required: True + icon_right: 'book-plus' + helper_text_mode: "on_error" + multiline: False + on_text: root.checkAddress_valid(self) + canvas.before: + Color: + rgba: (0,0,0,1) + +: + id: addbook_popup_box + size_hint_y: None + height: 2.5*(add_label.height) + orientation: 'vertical' + spacing:dp(5) + MDLabel + font_style: 'Subtitle2' + theme_text_color: 'Primary' + text: app.tr._("Label") + font_size: '17sp' + halign: 'left' + MDTextField: + id: add_label + font_style: 'Body1' + font_size: '15sp' + halign: 'left' + text: app.tr._(root.address_label) + theme_text_color: 'Primary' + required: True + helper_text_mode: "on_error" + on_text: root.checkLabel_valid(self) + canvas.before: + Color: + rgba: (0,0,0,1) + MDLabel: + font_style: 'Subtitle2' + theme_text_color: 'Primary' + text: app.tr._("Address") + font_size: '17sp' + halign: 'left' + Widget: + size_hint_y: None + height: dp(1) + BoxLayout: + orientation: 'horizontal' + MDLabel: + id: address + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._(root.address) + font_size: '15sp' + halign: 'left' + IconRightSampleWidget: + pos_hint: {'center_x': 0, 'center_y': 1} + icon: 'content-copy' + on_press: app.copy_composer_text(root.address) + + +: + id: myadd_popup + size_hint_y: None + height: "130dp" + spacing:dp(25) + + #height: dp(1.5*(myaddr_label.height)) + orientation: 'vertical' + MDLabel: + id: myaddr_label + font_style: 'Subtitle2' + theme_text_color: 'Primary' + text: app.tr._("Label") + font_size: '17sp' + halign: 'left' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: root.address_label + font_size: '15sp' + halign: 'left' + MDLabel: + font_style: 'Subtitle2' + theme_text_color: 'Primary' + text: app.tr._("Address") + font_size: '17sp' + halign: 'left' + BoxLayout: + orientation: 'horizontal' + MDLabel: + id: label_address + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._(root.address) + font_size: '15sp' + halign: 'left' + IconRightSampleWidget: + pos_hint: {'center_x': 0, 'center_y': 1} + icon: 'content-copy' + on_press: app.copy_composer_text(root.address) + BoxLayout: + id: my_add_btn + spacing:5 + orientation: 'horizontal' + size_hint_y: None + height: self.minimum_height + MDRaisedButton: + size_hint: 2, None + height: dp(40) + on_press: root.send_message_from() + MDLabel: + font_style: 'H6' + text: app.tr._('Send message from') + font_size: '13sp' + color: (1,1,1,1) + halign: 'center' + MDRaisedButton: + size_hint: 1.5, None + height: dp(40) + on_press: app.root.ids.scr_mngr.current = 'showqrcode' + on_press: app.root.ids.sc15.qrdisplay(root, root.address) + MDLabel: + font_style: 'H6' + text: app.tr._('Show QR code') + font_size: '13sp' + color: (1,1,1,1) + halign: 'center' + MDRaisedButton: + size_hint: 1.5, None + height: dp(40) + on_press: root.close_pop() + MDLabel: + font_style: 'H6' + text: app.tr._('Cancel') + font_size: '13sp' + color: (1,1,1,1) + halign: 'center' + +: + id: closing_popup + size_hint : (None,None) + height: 1.4*(popup_label.height+ my_add_btn.children[0].height) + width :app.window_size[0] - (app.window_size[0]/10 if app.app_platform == 'android' else app.window_size[0]/4) + background: app.image_path + '/popup.jpeg' + auto_dismiss: False + separator_height: 0 + BoxLayout: + id: myadd_popup_box + size_hint_y: None + spacing:dp(70) + orientation: 'vertical' + BoxLayout: + size_hint_y: None + orientation: 'vertical' + spacing:dp(25) + MDLabel: + id: popup_label + font_style: 'Subtitle2' + theme_text_color: 'Primary' + text: app.tr._("Bitmessage isn't connected to the network.\n If you quit now, it may cause delivery delays.\n Wait until connected and the synchronisation finishes?") + font_size: '17sp' + halign: 'center' + BoxLayout: + id: my_add_btn + spacing:5 + orientation: 'horizontal' + MDRaisedButton: + size_hint: 1.5, None + height: dp(40) + on_press: root.closingAction(self.children[0].text) + on_press: app.stop() + MDLabel: + font_style: 'H6' + text: app.tr._('Yes') + font_size: '13sp' + color: (1,1,1,1) + halign: 'center' + MDRaisedButton: + size_hint: 1.5, None + height: dp(40) + on_press: root.closingAction(self.children[0].text) + MDLabel: + font_style: 'H6' + text: app.tr._('No') + font_size: '13sp' + color: (1,1,1,1) + halign: 'center' + MDRaisedButton: + size_hint: 1.5, None + height: dp(40) + #on_press: root.dismiss() + on_press: root.closingAction(self.children[0].text) + MDLabel: + font_style: 'H6' + text: app.tr._('Cancel') + font_size: '13sp' + color: (1,1,1,1) + halign: 'center' + +: + id: myadd_popup + size_hint : (None,None) + # height: 2*(sd_label.height+ sd_btn.children[0].height) + width :app.window_size[0] - (app.window_size[0]/10 if app.app_platform == 'android' else app.window_size[0]/4) + background: app.image_path + '/popup.jpeg' + auto_dismiss: False + separator_height: 0 + BoxLayout: + id: myadd_popup_box + size_hint_y: None + orientation: 'vertical' + spacing:dp(8 if app.app_platform == 'android' else 3) + BoxLayout: + orientation: 'vertical' + MDLabel: + id: from_add_label + font_style: 'Subtitle2' + theme_text_color: 'Primary' + text: app.tr._("From :") + font_size: '15sp' + halign: 'left' + Widget: + size_hint_y: None + height: dp(1 if app.app_platform == 'android' else 0) + BoxLayout: + size_hint_y: None + height: 50 + orientation: 'horizontal' + MDLabel: + id: sd_label + font_style: 'Body2' + theme_text_color: 'Primary' + text: app.tr._("[b]" + root.from_addr + "[/b]") + font_size: '15sp' + halign: 'left' + markup: True + IconRightSampleWidget: + icon: 'content-copy' + on_press: app.copy_composer_text(root.from_addr) + Widget: + id: space_1 + size_hint_y: None + height: dp(2 if app.app_platform == 'android' else 0) + BoxLayout: + id: to_addtitle + Widget: + id:space_2 + size_hint_y: None + height: dp(1 if app.app_platform == 'android' else 0) + BoxLayout: + id: to_addId + BoxLayout: + size_hint_y: None + orientation: 'vertical' + height: 50 + MDLabel: + font_style: 'Body2' + theme_text_color: 'Primary' + text: app.tr._("Date : " + root.time_tag) + font_size: '15sp' + halign: 'left' + BoxLayout: + id: sd_btn + orientation: 'vertical' + MDRaisedButton: + id: dismiss_btn + on_press: root.dismiss() + size_hint: .2, 0 + pos_hint: {'x': 0.8, 'y': 0} + MDLabel: + font_style: 'H6' + text: app.tr._('Cancel') + font_size: '13sp' + color: (1,1,1,1) + halign: 'center' + +: + orientation: 'horizontal' + MDLabel: + font_style: 'Body2' + theme_text_color: 'Primary' + text: app.tr._(root.to_addr) + font_size: '15sp' + halign: 'left' + IconRightSampleWidget: + icon: 'content-copy' + on_press: app.copy_composer_text(root.to_addr) + +: + orientation: 'vertical' + MDLabel: + id: to_add_label + font_style: 'Subtitle2' + theme_text_color: 'Primary' + text: "To :" + font_size: '15sp' + halign: 'left' \ No newline at end of file diff --git a/src/tests/mock/pybitmessage/kv/qrcode.kv b/src/tests/mock/pybitmessage/kv/qrcode.kv new file mode 100644 index 00000000..cadaa996 --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/qrcode.kv @@ -0,0 +1,33 @@ +: + name: 'showqrcode' + BoxLayout: + orientation: 'vertical' + size_hint: (None, None) + pos_hint:{'center_x': .5, 'top': 0.9} + size: (app.window_size[0]/1.8, app.window_size[0]/1.8) + id: qr + BoxLayout: + orientation: 'vertical' + MyMDTextField: + size_hint_y: None + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._(root.address) + multiline: True + readonly: True + line_color_normal: [0,0,0,0] + _current_line_color: [0,0,0,0] + line_color_focus: [0,0,0,0] + halign: 'center' + font_size: dp(15) + bold: True + canvas.before: + Color: + rgba: (0,0,0,1) + # MDLabel: + # size_hint_y: None + # font_style: 'Body1' + # theme_text_color: 'Primary' + # text: "[b]BM-2cV7Y8imvAevK6z6YmhYRcj2t7rghBtDSZ[/b]" + # markup: True + # pos_hint: {'x': .28, 'y': 0.6} \ No newline at end of file diff --git a/src/tests/mock/pybitmessage/kv/scan_screen.kv b/src/tests/mock/pybitmessage/kv/scan_screen.kv new file mode 100644 index 00000000..dbcff5a1 --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/scan_screen.kv @@ -0,0 +1,2 @@ +: + name:'scanscreen' \ No newline at end of file diff --git a/src/tests/mock/pybitmessage/kv/scanner.kv b/src/tests/mock/pybitmessage/kv/scanner.kv new file mode 100644 index 00000000..1c56f6c2 --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/scanner.kv @@ -0,0 +1,37 @@ +#:import ZBarSymbol pyzbar.pyzbar.ZBarSymbol + +BoxLayout: + orientation: 'vertical' + ZBarCam: + id: zbarcam + # optional, by default checks all types + code_types: ZBarSymbol.QRCODE, ZBarSymbol.EAN13 + scan_callback: app._after_scan + scanner_line_y_initial: self.size[1]/2 +self.qrwidth/2 + scanner_line_y_final: self.size[1]/2-self.qrwidth/2 + + canvas: + Color: + rgba: 0,0,0,.25 + + #left rect + Rectangle: + pos: self.pos[0], self.pos[1] + size: self.size[0]/2-self.qrwidth/2, self.size[1] + + #right rect + Rectangle: + pos: self.size[0]/2+self.qrwidth/2, 0 + size: self.size[0]/2-self.qrwidth/2, self.size[1] + + #top rect + Rectangle: + pos: self.size[0]/2-self.qrwidth/2, self.size[1]/2+self.qrwidth/2 + size: self.qrwidth, self.size[1]/2-self.qrwidth/2 + + #bottom rect + Rectangle: + pos: self.size[0]/2-self.qrwidth/2, 0 + size: self.qrwidth, self.size[1]/2-self.qrwidth/2 + + \ No newline at end of file diff --git a/src/tests/mock/pybitmessage/kv/sent.kv b/src/tests/mock/pybitmessage/kv/sent.kv new file mode 100644 index 00000000..11477ed6 --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/sent.kv @@ -0,0 +1,26 @@ +: + name: 'sent' + BoxLayout: + orientation: 'vertical' + spacing: dp(5) + SearchBar: + id: sent_search + GridLayout: + id: identi_tag + padding: [20, 0, 0, 5] + cols: 1 + size_hint_y: None + height: self.minimum_height + MDLabel: + id: tag_label + text: '' + font_style: 'Subtitle2' + BoxLayout: + orientation:'vertical' + ScrollView: + id: scroll_y + do_scroll_x: False + MDList: + id: ml + Loader: + ComposerButton: \ No newline at end of file diff --git a/src/tests/mock/pybitmessage/kv/settings.kv b/src/tests/mock/pybitmessage/kv/settings.kv new file mode 100644 index 00000000..609c8e80 --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/settings.kv @@ -0,0 +1,964 @@ +: + name: 'set' + MDTabs: + id: tab_panel + tab_display_mode:'text' + + Tab: + text: app.tr._("User Interface") + ScrollView: + do_scroll_x: False + BoxLayout: + size_hint_y: None + orientation: 'vertical' + height: dp(250) + self.minimum_height + padding: 10 + BoxLayout: + size_hint_y: None + orientation: 'horizontal' + height: self.minimum_height + MDCheckbox: + id: chkbox + size_hint: None, None + size: dp(48), dp(50) + # active: True + halign: 'center' + disabled: True + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Start-on-login not yet supported on your OS") + halign: 'left' + pos_hint: {'center_x': 0, 'center_y': 0.6} + disabled: True + BoxLayout: + size_hint_y: None + orientation: 'vertical' + padding: [20, 0, 0, 0] + spacing: dp(10) + height: dp(100) + self.minimum_height + # pos_hint: {'center_x': 0, 'center_y': 0.6} + BoxLayout: + id: box_height + orientation: 'vertical' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Tray") + halign: 'left' + bold: True + BoxLayout: + orientation: 'horizontal' + MDCheckbox: + id: chkbox + size_hint: None, None + size: dp(48), dp(50) + # active: True + halign: 'center' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Start Bitmessage in the tray(don't show main window)") + halign: 'left' + pos_hint: {'x': 0, 'y': .5} + BoxLayout: + orientation: 'horizontal' + MDCheckbox: + id: chkbox + size_hint: None, None + size: dp(48), dp(50) + # active: True + halign: 'center' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Minimize to tray") + halign: 'left' + pos_hint: {'x': 0, 'y': .5} + BoxLayout: + orientation: 'horizontal' + MDCheckbox: + id: chkbox + size_hint: None, None + size: dp(48), dp(50) + # active: True + halign: 'center' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Close to tray") + halign: 'left' + pos_hint: {'x': 0, 'y': .5} + BoxLayout: + size_hint_y: None + orientation: 'vertical' + height: dp(100) + self.minimum_height + BoxLayout: + orientation: 'horizontal' + MDCheckbox: + id: chkbox + size_hint: None, None + size: dp(48), dp(50) + # active: True + halign: 'center' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Hide connection notifications") + halign: 'left' + pos_hint: {'x': 0, 'y': 0.2} + BoxLayout: + orientation: 'horizontal' + MDCheckbox: + id: chkbox + size_hint: None, None + size: dp(48), dp(50) + active: True + halign: 'center' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Show notification when message received") + halign: 'left' + pos_hint: {'x': 0, 'y': 0.2} + BoxLayout: + orientation: 'horizontal' + MDCheckbox: + id: chkbox + size_hint: None, None + size: dp(48), dp(50) + # active: True + halign: 'center' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Run in Portable Mode") + halign: 'left' + pos_hint: {'x': 0, 'y': 0.2} + BoxLayout: + orientation: 'vertical' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._('In portable Mode, messages and config files are stored in the same directory as the program rather then the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive.') + # text: 'huiiiii' + halign: 'left' + BoxLayout: + size_hint_y: None + orientation: 'vertical' + height: dp(100) + self.minimum_height + BoxLayout: + orientation: 'horizontal' + MDCheckbox: + id: chkbox + size_hint: None, None + size: dp(48), dp(50) + halign: 'center' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Willingly include unencrypted destination address when sending to a mobile device") + halign: 'left' + pos_hint: {'x': 0, 'y': 0.2} + BoxLayout: + orientation: 'horizontal' + MDCheckbox: + id: chkbox + size_hint: None, None + size: dp(48), dp(50) + active: True + halign: 'center' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Use identicons") + halign: 'left' + pos_hint: {'x': 0, 'y': 0.2} + BoxLayout: + orientation: 'horizontal' + MDCheckbox: + id: chkbox + size_hint: None, None + size: dp(48), dp(50) + halign: 'center' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Reply below Quote") + halign: 'left' + pos_hint: {'x': 0, 'y': 0.2} + Widget: + size_hint_y: None + height: 10 + BoxLayout: + size_hint_y: None + orientation: 'vertical' + # padding: [0, 10, 0, 0] + spacing: 10 + padding: [20, 0, 0, 0] + height: dp(20) + self.minimum_height + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Interface Language") + # halign: 'right' + bold: True + MDDropDownItem: + id: dropdown_item + text: "System Setting" + # pos_hint: {"center_x": .5, "center_y": .6} + # current_item: "Item 0" + # on_release: root.menu.open() + BoxLayout: + spacing:5 + orientation: 'horizontal' + # pos_hint: {'x':.76} + BoxLayout: + orientation: 'horizontal' + spacing: 10 + MDRaisedButton: + text: app.tr._('Apply') + # on_press: root.change_language() + Tab: + text: 'Network Settings' + ScrollView: + do_scroll_x: False + BoxLayout: + size_hint_y: None + orientation: 'vertical' + height: dp(500) + self.minimum_height + padding: 10 + BoxLayout: + id: box_height + orientation: 'vertical' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Listening port") + halign: 'left' + bold: True + BoxLayout: + orientation: 'horizontal' + padding: [10, 0, 0, 0] + BoxLayout: + orientation: 'horizontal' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Listen for connections on port:") + halign: 'left' + BoxLayout: + orientation: 'horizontal' + MDTextFieldRect: + size_hint: None, None + size: dp(100), dp(30) + text: app.tr._('8444') + pos_hint: {'center_y': .5, 'center_x': .5} + input_filter: "int" + BoxLayout: + orientation: 'horizontal' + padding_left: 10 + MDCheckbox: + id: chkbox + size_hint: None, None + size: dp(48), dp(50) + # active: True + halign: 'center' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("UPnP") + halign: 'left' + pos_hint: {'x': 0, 'y': 0} + BoxLayout: + orientation: 'vertical' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Proxy server / Tor") + halign: 'left' + bold: True + + GridLayout: + cols: 2 + padding: [10, 0, 0, 0] + MDLabel: + size_hint_x: None + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Type:") + halign: 'left' + MDDropDownItem: + id: dropdown_item2 + dropdown_bg: [1, 1, 1, 1] + text: 'none' + pos_hint: {'x': 0.9, 'y': 0} + items: [f"{i}" for i in ['System Setting','U.S. English']] + BoxLayout: + size_hint_y: None + orientation: 'vertical' + padding: [30, 0, 0, 0] + spacing: 10 + height: dp(100) + self.minimum_height + BoxLayout: + orientation: 'horizontal' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Server hostname:") + halign: 'left' + MDTextFieldRect: + size_hint: None, None + size: dp(app.window_size[0]/4), dp(30) + hint_text: app.tr._('localhost') + pos_hint: {'center_y': .5, 'center_x': .5} + BoxLayout: + orientation: 'horizontal' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Port:") + halign: 'left' + # TextInput: + # size_hint: None, None + # hint_text: '9050' + # size: dp(app.window_size[0]/4), dp(30) + # input_filter: "int" + # readonly: False + # multiline: False + # font_size: '15sp' + MDTextFieldRect: + size_hint: None, None + size: dp(app.window_size[0]/4), dp(30) + hint_text: app.tr._('9050') + pos_hint: {'center_y': .5, 'center_x': .5} + input_filter: "int" + BoxLayout: + orientation: 'horizontal' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Username:") + halign: 'left' + MDTextFieldRect: + size_hint: None, None + size: dp(app.window_size[0]/4), dp(30) + pos_hint: {'center_y': .5, 'center_x': .5} + BoxLayout: + orientation: 'horizontal' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Pass:") + halign: 'left' + MDTextFieldRect: + size_hint: None, None + size: dp(app.window_size[0]/4), dp(30) + pos_hint: {'center_y': .5, 'center_x': .5} + BoxLayout: + orientation: 'horizontal' + padding: [30, 0, 0, 0] + MDCheckbox: + id: chkbox + size_hint: None, None + size: dp(48), dp(50) + # active: True + halign: 'center' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Authentication") + halign: 'left' + pos_hint: {'x': 0, 'y': 0} + BoxLayout: + orientation: 'horizontal' + padding: [30, 0, 0, 0] + MDCheckbox: + id: chkbox + size_hint: None, None + size: dp(48), dp(50) + # active: True + halign: 'center' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Listen for incoming connections when using proxy") + halign: 'left' + pos_hint: {'x': 0, 'y': 0} + BoxLayout: + orientation: 'horizontal' + padding: [30, 0, 0, 0] + MDCheckbox: + id: chkbox + size_hint: None, None + size: dp(48), dp(50) + # active: True + halign: 'center' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Only connect to onion services(*.onion)") + halign: 'left' + pos_hint: {'x': 0, 'y': 0} + BoxLayout: + orientation: 'vertical' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Bandwidth limit") + halign: 'left' + bold: True + BoxLayout: + size_hint_y: None + orientation: 'horizontal' + padding: [30, 0, 0, 0] + height: dp(30) + self.minimum_height + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Maximum download rate (kB/s):[0:unlimited]") + halign: 'left' + MDTextFieldRect: + size_hint: None, None + size: app.window_size[0]/2, dp(30) + hint_text: app.tr._('0') + pos_hint: {'center_y': .5, 'center_x': .5} + input_filter: "int" + BoxLayout: + size_hint_y: None + orientation: 'horizontal' + padding: [30, 0, 0, 0] + height: dp(30) + self.minimum_height + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Maximum upload rate (kB/s):[0:unlimited]") + halign: 'left' + MDTextFieldRect: + size_hint: None, None + size: app.window_size[0]/2, dp(30) + hint_text: '0' + pos_hint: {'center_y': .5, 'center_x': .5} + input_filter: "int" + BoxLayout: + size_hint_y: None + orientation: 'horizontal' + padding: [30, 0, 0, 0] + height: dp(30) + self.minimum_height + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Maximum outbound connections:[0:none]") + halign: 'left' + MDTextFieldRect: + size_hint: None, None + size: app.window_size[0]/2, dp(30) + hint_text: '8' + pos_hint: {'center_y': .5, 'center_x': .5} + input_filter: "int" + BoxLayout: + spacing:5 + orientation: 'horizontal' + # pos_hint: {'x':.76} + + MDRaisedButton: + text: app.tr._('Apply') + Tab: + text: 'Demanded Difficulty' + ScrollView: + do_scroll_x: False + + BoxLayout: + size_hint_y: None + orientation: 'vertical' + height: dp(300) + self.minimum_height + padding: 10 + BoxLayout: + id: box_height + orientation: 'vertical' + # MDLabel: + # font_style: 'Body1' + # theme_text_color: 'Primary' + # text: app.tr._("Listening port") + # halign: 'left' + # bold: True + + # BoxLayout: + # size_hint_y: None + # orientation: 'vertical' + # height: dp(210 if app.app_platform == 'android' else 100)+ self.minimum_height + # padding: 20 + # # spacing: 10 + # BoxLayout: + # # size_hint_y: None + # id: box1_height + # # orientation: 'vertical' + # # height: dp(100) + self.minimum_height + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + # text: app.tr._(root.exp_text) + text: "\n\n\nWhen someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1.\n\n" + halign: 'left' + + BoxLayout: + orientation: 'horizontal' + padding: 5 + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Total difficulty:") + halign: 'left' + MDTextFieldRect: + size_hint: None, None + size: dp(app.window_size[0]/4), dp(30) + hint_text: app.tr._('00000.0') + pos_hint: {'center_y': .5, 'center_x': .5} + input_filter: "int" + + BoxLayout: + # size_hint_y: None + id: box1_height + orientation: 'vertical' + padding: 5 + # height: dp(100) + self.minimum_height + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + # text: app.tr._(root.exp_text) + text: "The 'Total difficulty' affects the absolute amount of work the sender must complete. Doubling this value doubles the amount of work." + halign: 'left' + + BoxLayout: + orientation: 'horizontal' + spacing: 0 + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Small message difficulty:") + halign: 'left' + MDTextFieldRect: + size_hint: None, None + size: dp(app.window_size[0]/4), dp(30) + hint_text: app.tr._('00000.0') + pos_hint: {'center_y': .5, 'center_x': .5} + input_filter: "int" + + + BoxLayout: + size_hint_y: None + padding: 0 + id: box1_height + orientation: 'vertical' + # height: dp(100) + self.minimum_height + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + # text: app.tr._(root.exp_text) + text: "The 'Small message difficulty' mostly only affects the difficulty of sending small messages. Doubling this value makes it almost twice as difficult to send a small message but doesn't really affect large messages." + halign: 'left' + + + # BoxLayout: + # id: box2_height + # size_hint_y: None + # orientation: 'vertical' + # height: dp(30) + self.minimum_height + # MDLabel: + # font_style: 'Body1' + # theme_text_color: 'Primary' + # text: app.tr._("Leave these input fields blank for the default behavior.") + # halign: 'left' + # BoxLayout: + # size_hint_y: None + # orientation: 'vertical' + # padding: [10, 0, 0, 0] + # height: dp(50) + self.minimum_height + # BoxLayout: + # orientation: 'horizontal' + # MDLabel: + # font_style: 'Body1' + # theme_text_color: 'Primary' + # text: app.tr._("Give up after") + # halign: 'left' + # MDTextFieldRect: + # size_hint: None, None + # size: dp(70), dp(30) + # text: app.tr._('0') + # # pos_hint: {'center_y': .5, 'center_x': .5} + # input_filter: "int" + # MDLabel: + # font_style: 'Body1' + # theme_text_color: 'Primary' + # text: app.tr._("days and") + # halign: 'left' + # MDTextFieldRect: + # size_hint: None, None + # size: dp(70), dp(30) + # text: '0' + # # pos_hint: {'center_y': .5, 'center_x': .5} + # input_filter: "int" + # MDLabel: + # font_style: 'Body1' + # theme_text_color: 'Primary' + # text: "months" + # halign: 'left' + BoxLayout: + size_hint_y: None + spacing:10 + orientation: 'horizontal' + # pos_hint: {'left': 0} + # pos_hint: {'x':.75} + height: dp(10) + self.minimum_height + MDRaisedButton: + text: app.tr._('Cancel') + MDRaisedButton: + text: app.tr._('Apply') + + Tab: + text: 'Max acceptable Difficulty' + ScrollView: + do_scroll_x: False + BoxLayout: + size_hint_y: None + orientation: 'vertical' + height: dp(210 if app.app_platform == 'android' else 100)+ self.minimum_height + padding: 20 + + # spacing: 10 + BoxLayout: + # size_hint_y: None + id: box1_height + orientation: 'vertical' + spacing: 10 + + # pos_hint: {'x': 0, 'y': 0.2} + # height: dp(100) + self.minimum_height + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + # text: app.tr._(root.exp_text) + text: "\n\n\nHere you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable." + halign: 'left' + # BoxLayout: + # id: box2_height + # size_hint_y: None + # orientation: 'vertical' + # height: dp(40) + self.minimum_height + # BoxLayout: + # size_hint_y: None + # orientation: 'vertical' + # padding: [10, 0, 0, 0] + # height: dp(50) + self.minimum_height + + GridLayout: + cols: 2 + padding: [10, 0, 0, 0] + + BoxLayout: + size_hint_y: None + orientation: 'vertical' + padding: [10, 0, 0, 0] + spacing: 10 + height: dp(50) + self.minimum_height + BoxLayout: + orientation: 'horizontal' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Maximum acceptable total difficulty:") + halign: 'left' + MDTextFieldRect: + size_hint: None, None + size: dp(app.window_size[0]/4), dp(30) + hint_text: app.tr._('00000.0') + pos_hint: {'center_y': .5, 'center_x': .5} + input_filter: "int" + + BoxLayout: + orientation: 'horizontal' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Hardware GPU acceleration (OpenCL):") + halign: 'left' + MDDropDownItem: + id: dropdown_item + text: "None" + pos_hint: {"center_x": 0, "center_y": 0} + # current_item: "Item 0" + # on_release: root.menu.open() + + # BoxLayout: + # size_hint_y: None + # spacing:5 + # orientation: 'horizontal' + # pos_hint: {'center_y': .4, 'center_x': 1.15} + # halign: 'right' + + BoxLayout: + size_hint_y: None + spacing:5 + orientation: 'horizontal' + pos_hint: {'center_y': 1, 'center_x': 1.15} + halign: 'right' + # pos_hint: {'left': 0} + # pos_hint: {'x':.75} + height: dp(50) + self.minimum_height + MDRaisedButton: + text: app.tr._('Cancel') + MDRaisedButton: + text: app.tr._('OK') + Tab: + text: 'Resends Expire' + ScrollView: + do_scroll_x: False + BoxLayout: + size_hint_y: None + orientation: 'vertical' + height: dp(210 if app.app_platform == 'android' else 100)+ self.minimum_height + padding: 20 + # spacing: 10 + BoxLayout: + # size_hint_y: None + id: box1_height + orientation: 'vertical' + # height: dp(100) + self.minimum_height + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + # text: app.tr._(root.exp_text) + text: "By default, if you send a message to someone and he is offline for more than two days, Bitmessage will send the message again after an additional two days. This will be continued with exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain number of days or months." + halign: 'left' + BoxLayout: + id: box2_height + size_hint_y: None + orientation: 'vertical' + height: dp(30) + self.minimum_height + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Leave these input fields blank for the default behavior.") + halign: 'left' + BoxLayout: + size_hint_y: None + orientation: 'vertical' + padding: [10, 0, 0, 0] + height: dp(50) + self.minimum_height + BoxLayout: + orientation: 'horizontal' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Give up after") + halign: 'left' + MDTextFieldRect: + size_hint: None, None + size: dp(70), dp(30) + text: app.tr._('0') + pos_hint: {'center_y': .5, 'center_x': .5} + input_filter: "int" + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("days and") + halign: 'left' + MDTextFieldRect: + size_hint: None, None + size: dp(70), dp(30) + text: '0' + pos_hint: {'center_y': .5, 'center_x': .5} + input_filter: "int" + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: "months" + halign: 'left' + BoxLayout: + size_hint_y: None + spacing:5 + orientation: 'horizontal' + # pos_hint: {'left': 0} + # pos_hint: {'x':.75} + height: dp(50) + self.minimum_height + # MDRaisedButton: + # text: app.tr._('Cancel') + MDRaisedButton: + text: app.tr._('Apply') + + Tab: + text: 'Namecoin Integration' + ScrollView: + do_scroll_x: False + BoxLayout: + size_hint_y: None + orientation: 'vertical' + height: dp(210 if app.app_platform == 'android' else 100)+ self.minimum_height + padding: 20 + + # spacing: 10 + BoxLayout: + # size_hint_y: None + id: box1_height + orientation: 'vertical' + spacing: 10 + + # pos_hint: {'x': 0, 'y': 0.2} + # height: dp(100) + self.minimum_height + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + # text: app.tr._(root.exp_text) + text: "\n\n\n\n\n\nBitmessage can utilize a different Bitcoin-based program called Namecoin to make addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage address, you can simply tell him to send a message to test.\n\n(Getting your own Bitmessage address into Namecoin is still rather difficult).\n\nBitmessage can use either namecoind directly or a running nmcontrol instance\n\n" + halign: 'left' + + BoxLayout: + id: box2_height + size_hint_y: None + orientation: 'vertical' + height: dp(40) + self.minimum_height + BoxLayout: + size_hint_y: None + orientation: 'vertical' + padding: [10, 0, 0, 0] + height: dp(50) + self.minimum_height + + BoxLayout: + orientation: 'horizontal' + padding: [10, 0, 0, 0] + + BoxLayout: + orientation: 'horizontal' + + # padding_left: 10 + # MDCheckbox: + # id: chkbox + # size_hint: None, None + # size: dp(48), dp(50) + # # active: True + # halign: 'center' + # MDLabel: + # font_style: 'Body1' + # theme_text_color: 'Primary' + # text: app.tr._("UPnP") + # halign: 'left' + # pos_hint: {'x': 0, 'y': 0} + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Connect to:") + halign: 'left' + + # MDCheckbox: + # id: chkbox + # size_hint: None, None + # size: dp(48), dp(50) + # # active: True + # halign: 'center' + Check: + active: True + pos_hint: {'x': 0, 'y': -0.2} + + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Namecoind") + halign: 'left' + pos_hint: {'x': 0, 'y': 0} + + Check: + active: False + pos_hint: {'x': 0, 'y': -0.2} + + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("NMControl") + halign: 'left' + pos_hint: {'x': 0, 'y': 0} + + GridLayout: + cols: 2 + padding: [10, 0, 0, 0] + + BoxLayout: + size_hint_y: None + orientation: 'vertical' + padding: [30, 0, 0, 0] + spacing: 10 + height: dp(100) + self.minimum_height + BoxLayout: + orientation: 'horizontal' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("hostname:") + halign: 'left' + MDTextFieldRect: + size_hint: None, None + size: dp(app.window_size[0]/4), dp(30) + hint_text: app.tr._('localhost') + pos_hint: {'center_y': .5, 'center_x': .5} + BoxLayout: + orientation: 'horizontal' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Port:") + halign: 'left' + # TextInput: + # size_hint: None, None + # hint_text: '9050' + # size: dp(app.window_size[0]/4), dp(30) + # input_filter: "int" + # readonly: False + # multiline: False + # font_size: '15sp' + MDTextFieldRect: + size_hint: None, None + size: dp(app.window_size[0]/4), dp(30) + hint_text: app.tr._('9050') + pos_hint: {'center_y': .5, 'center_x': .5} + input_filter: "int" + BoxLayout: + orientation: 'horizontal' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Username:") + halign: 'left' + MDTextFieldRect: + size_hint: None, None + size: dp(app.window_size[0]/4), dp(30) + pos_hint: {'center_y': .5, 'center_x': .5} + BoxLayout: + orientation: 'horizontal' + MDLabel: + font_style: 'Body1' + theme_text_color: 'Primary' + text: app.tr._("Password:") + halign: 'left' + + MDTextFieldRect: + size_hint: None, None + size: dp(app.window_size[0]/4), dp(30) + pos_hint: {'center_y': .5, 'center_x': .5} + password: True + + + BoxLayout: + size_hint_y: None + spacing:5 + orientation: 'horizontal' + pos_hint: {'center_y': .4, 'center_x': 1.15} + halign: 'right' + # pos_hint: {'left': 0} + # pos_hint: {'x':.75} + height: dp(50) + self.minimum_height + MDRaisedButton: + text: app.tr._('Cancel') + MDRaisedButton: + text: app.tr._('Apply') + MDRaisedButton: + text: app.tr._('OK') + Loader: \ No newline at end of file diff --git a/src/tests/mock/pybitmessage/kv/trash.kv b/src/tests/mock/pybitmessage/kv/trash.kv new file mode 100644 index 00000000..97bcf7d7 --- /dev/null +++ b/src/tests/mock/pybitmessage/kv/trash.kv @@ -0,0 +1,25 @@ +: + name: 'trash' + BoxLayout: + orientation: 'vertical' + spacing: dp(5) + GridLayout: + id: identi_tag + padding: [20, 20, 0, 5] + spacing: dp(5) + cols: 1 + size_hint_y: None + height: self.minimum_height + MDLabel: + id: tag_label + text: '' + font_style: 'Subtitle2' + BoxLayout: + orientation:'vertical' + ScrollView: + id: scroll_y + do_scroll_x: False + MDList: + id: ml + Loader: + ComposerButton: diff --git a/src/tests/mock/pybitmessage/main.kv b/src/tests/mock/pybitmessage/main.kv new file mode 100644 index 00000000..40adbe70 --- /dev/null +++ b/src/tests/mock/pybitmessage/main.kv @@ -0,0 +1,422 @@ +#:import IconLeftWidget kivymd.uix.list.IconLeftWidget +#:import images_path kivymd.images_path +#:import Spinner kivy.uix.spinner.Spinner +#:import Factory kivy.factory.Factory + +#:import MDCheckbox kivymd.uix.selectioncontrol.MDCheckbox +#:import MDList kivymd.uix.list.MDList +#:import OneLineListItem kivymd.uix.list.OneLineListItem +#:import MDTextField kivymd.uix.textfield.MDTextField +#:import get_color_from_hex kivy.utils.get_color_from_hex +#:import MDCard kivymd.uix.card.MDCard +#:import colors kivymd.color_definitions.colors +#:import MDTabs kivymd.uix.tab.MDTabs +#:import MDFloatingActionButton kivymd.uix.button.MDFloatingActionButton +#:import Factory kivy.factory.Factory +#:import MDScrollViewRefreshLayout kivymd.uix.refreshlayout.MDScrollViewRefreshLayout +#:import MDSpinner kivymd.uix.spinner.MDSpinner +#:import MDTabsBase kivymd.uix.tab.MDTabsBase +##:import ZBarSymbol pyzbar.pyzbar.ZBarSymbol + + +#:set color_button (0.784, 0.443, 0.216, 1) # brown +#:set color_button_pressed (0.659, 0.522, 0.431, 1) # darker brown +#:set color_font (0.957, 0.890, 0.843, 1) # off white + +: + font_size: '12.5sp' + #background_color: color_button if self.state == 'down' else color_button_pressed + #background_down: 'atlas://data/images/defaulttheme/button' + background_normal: 'atlas://data/images/defaulttheme/textinput_active' + background_color: app.theme_cls.primary_color + # text_autoupdate: True + color: color_font + + + #on_press: root.active = not root.active + on_press: root.currentlyActive() + active_color: root.theme_cls.primary_color if root.active else root.theme_cls.text_color + + IconLeftWidget: + icon: root.icon + theme_text_color: "Custom" + text_color: root.active_color + + BadgeText: + id: badge_txt + text: f"{root.badge_text}" + theme_text_color: "Custom" + #text_color: root.active_color + halign: 'right' + +: + canvas: + Color: + rgba: self.theme_cls.divider_color + Line: + points: root.x, root.y + dp(8), root.x + self.width, root.y + dp(8) + + + + BoxLayout: + orientation: 'vertical' + + FloatLayout: + size_hint_y: None + height: "200dp" + + MDIconButton: + id: reset_image + icon: "refresh" + x: root.parent.x + dp(10) + pos_hint: {"top": 1, 'left': 1} + color: [1,0,0,1] + on_release: app.rest_default_avatar_img() + theme_text_color: "Custom" + text_color: app.theme_cls.primary_color + # opacity: 1 if app.current_address_label() else 0 + # disabled: False if app.current_address_label() else True + opacity: 0 + disabled: True + + MDIconButton: + id: file_manager + icon: "file-image" + x: root.parent.x + dp(10) + pos_hint: {"top": 1, 'right': 1} + color: [1,0,0,1] + on_release: app.file_manager_open() + # md_bg_color: app.theme_cls.primary_color + theme_text_color: "Custom" + text_color: app.theme_cls.primary_color + opacity: 1 if app.current_address_label() else 0 + disabled: False if app.current_address_label() else True + + BoxLayout: + id: top_box + size_hint_y: None + height: "200dp" + #padding: "10dp" + x: root.parent.x + pos_hint: {"top": 1} + Image: + #source: './images/drawer_logo1.png' + source: app.get_default_logo(self) + + ScrollView: + id: scroll_y + pos_hint: {"top": 1} + + GridLayout: + id: box_item + cols: 1 + size_hint_y: None + height: self.minimum_height + NavigationDrawerDivider: + NavigationDrawerSubheader: + text: app.tr._('Accounts') + #text: app.tr._('Hello World') + height:"35dp" + NavigationItem: + # size: 50,50 + height: dp(48) + CustomSpinner: + id: btn + pos_hint:{"x":0,"y":0} + option_cls: Factory.get("MySpinnerOption") + font_size: '12.5sp' + text: app.getDefaultAccData(self) + color: color_font + background_normal: '' + background_color: app.theme_cls.primary_color + on_text:app.getCurrentAccountData(self.text) + ArrowImg: + NavigationItem: + id: inbox_cnt + text: app.tr._('Inbox') + #text: app.tr._('Hello World') + icon: 'email-open' + divider: None + on_release: app.root.ids.scr_mngr.current = 'inbox' + on_release: root.parent.set_state() + on_press: app.load_screen(self) + NavigationItem: + id: send_cnt + text: app.tr._('Sent') + icon: 'send' + divider: None + on_release: app.root.ids.scr_mngr.current = 'sent' + on_release: root.parent.set_state() + NavigationItem: + id: draft_cnt + text: app.tr._('Draft') + icon: 'message-draw' + divider: None + on_release: app.root.ids.scr_mngr.current = 'draft' + on_release: root.parent.set_state() + NavigationItem: + id: trash_cnt + text: app.tr._('Trash') + icon: 'delete' + divider: None + on_release: app.root.ids.scr_mngr.current = 'trash' + on_press: root.parent.set_state() + on_press: app.load_screen(self) + NavigationItem: + id: allmail_cnt + text: app.tr._('All Mails') + icon: 'mailbox' + divider: None + on_release: app.root.ids.scr_mngr.current = 'allmails' + on_release: root.parent.set_state() + on_press: app.load_screen(self) + # NavigationItem: + # id: chat_rm + # text: app.tr._('Chat Room') + # icon: 'wechat' + # divider: None + # on_release: app.root.ids.scr_mngr.current = 'chlist' + # on_release: root.parent.set_state() + NavigationDrawerDivider: + NavigationDrawerSubheader: + text: app.tr._("All labels") + NavigationItem: + text: app.tr._('Address Book') + icon: 'book-multiple' + divider: None + on_release: app.root.ids.scr_mngr.current = 'addressbook' + on_release: root.parent.set_state() + NavigationItem: + text: app.tr._('Settings') + icon: 'application-settings' + divider: None + on_release: app.root.ids.scr_mngr.current = 'set' + on_release: root.parent.set_state() + NavigationItem: + text: app.tr._('Purchase') + icon: 'shopping' + divider: None + on_release: app.root.ids.scr_mngr.current = 'payment' + on_release: root.parent.set_state() + # NavigationItem: + # text: app.tr._('Credits') + # icon: 'wallet' + # divider: None + # on_release: app.root.ids.scr_mngr.current = 'credits' + # on_release: root.parent.set_state() + NavigationItem: + text: app.tr._('New address') + icon: 'account-plus' + divider: None + on_release: app.root.ids.scr_mngr.current = 'login' + on_release: root.parent.set_state() + on_press: app.reset_login_screen() + NavigationItem: + text: app.tr._('Network status') + icon: 'server-network' + divider: None + on_release: app.root.ids.scr_mngr.current = 'networkstat' + on_release: root.parent.set_state() + NavigationItem: + text: app.tr._('My addresses') + icon: 'account-multiple' + divider: None + on_release: app.root.ids.scr_mngr.current = 'myaddress' + on_release: root.parent.set_state() + +MDNavigationLayout: + id: nav_layout + + MDToolbar: + id: toolbar + title: app.current_address_label() + opacity: 1 if app.addressexist() else 0 + disabled: False if app.addressexist() else True + pos_hint: {"top": 1} + md_bg_color: app.theme_cls.primary_color + elevation: 10 + left_action_items: [['menu', lambda x: nav_drawer.set_state("toggle")]] + right_action_items: [['account-plus', lambda x: app.addingtoaddressbook()]] + + ScreenManager: + id: scr_mngr + size_hint_y: None + height: root.height - toolbar.height + Inbox: + id:sc1 +# # Page: +# # id:sc2 + Create: + id:sc3 + Sent: + id:sc4 + Trash: + id:sc5 + Login: + id:sc6 + Random: + id:sc7 + # Spam: + # id:sc8 + Setting: + id:sc9 + MyAddress: + id:sc10 + AddressBook: + id:sc11 + Payment: + id:sc12 + NetworkStat: + id:sc13 + MailDetail: + id:sc14 + ShowQRCode: + id:sc15 + Draft: + id:sc16 + Allmails: + id:sc17 + # Credits: + # id:sc18 + # Starred: + # id:sc19 + # Archieve: + # id:sc20 + # ChatRoom: + # id:sc21 + # ChatList: + # id:sc22 + ScanScreen: + id:sc23 + + MDNavigationDrawer: + id: nav_drawer + + ContentNavigationDrawer: + id: content_drawer + + +: + source: app.image_path +('/down-arrow.png' if self.parent.is_open == True else '/right-arrow.png') + size: 15, 15 + x: self.parent.x + self.parent.width - self.width - 5 + y: self.parent.y + self.parent.height/2 - self.height + 5 + + +: + # id: search_bar + size_hint_y: None + height: self.minimum_height + + MDIconButton: + icon: 'magnify' + + MDTextField: + id: search_field + hint_text: 'Search' + on_text: app.searchQuery(self) + canvas.before: + Color: + rgba: (0,0,0,1) + + +: + id: spinner + size_hint: None, None + size: dp(46), dp(46) + pos_hint: {'center_x': 0.5, 'center_y': 0.5} + active: False + +: + size_hint_y: None + height: dp(56) + spacing: '10dp' + pos_hint: {'center_x':0.45, 'center_y': .1} + + Widget: + + MDFloatingActionButton: + icon: 'plus' + opposite_colors: True + elevation_normal: 8 + md_bg_color: [0.941, 0, 0,1] + on_press: app.root.ids.scr_mngr.current = 'create' + on_press: app.clear_composer() + + +: + size_hint_y: None + height: content.height + + MDCardSwipeLayerBox: + padding: "8dp" + + MDIconButton: + id: delete_msg + icon: "trash-can" + pos_hint: {"center_y": .5} + md_bg_color: (1, 0, 0, 1) + disabled: True + + MDCardSwipeFrontBox: + + TwoLineAvatarIconListItem: + id: content + text: root.text + _no_ripple_effect: True + + AvatarSampleWidget: + id: avater_img + # source: './images/kivy/avatar.png' + source: None + + TimeTagRightSampleWidget: + id: time_tag + text: '' + font_size: "11sp" + font_style: "Caption" + size: [120, 140] if app.app_platform == "android" else [64, 80] + + +: + size_hint_y: None + height: content.height + + MDCardSwipeLayerBox: + padding: "8dp" + + MDIconButton: + id: delete_msg + icon: "trash-can" + pos_hint: {"center_y": .5} + md_bg_color: (1, 0, 0, 1) + disabled: True + + MDCardSwipeFrontBox: + + TwoLineAvatarIconListItem: + id: content + text: root.text + _no_ripple_effect: True + + AvatarSampleWidget: + id: avater_img + # source: './images/kivy/avatar.png' + source: None + + TimeTagRightSampleWidget: + id: time_tag + text: 'time' + font_size: "11sp" + font_style: "Caption" + size: [120, 140] if app.app_platform == "android" else [64, 80] + MDChip: + id: chip_tag + size_hint: (0.16 if app.app_platform == "android" else 0.08, None) + text: 'test' + icon: "" + pos_hint: {"center_x": 0.91 if app.app_platform == "android" else 0.94, "center_y": 0.3} + # height: dp(18) + height: '18dp' + text_color: (1,1,1,1) + radius: [8] diff --git a/src/tests/mock/pybitmessage/mpybit.py b/src/tests/mock/pybitmessage/mpybit.py new file mode 100644 index 00000000..4ed435af --- /dev/null +++ b/src/tests/mock/pybitmessage/mpybit.py @@ -0,0 +1,897 @@ +# pylint: disable=too-many-lines,import-error,no-name-in-module,unused-argument +# pylint: disable=too-many-ancestors,too-many-locals,useless-super-delegation +# pylint: disable=protected-access +# pylint: disable=import-outside-toplevel,ungrouped-imports,wrong-import-order,unused-import,arguments-differ +# pylint: disable=invalid-name,unnecessary-comprehension,broad-except,simplifiable-if-expression,no-member +# pylint: disable=too-many-return-statements + +""" +Bitmessage android(mobile) interface +""" + +from pybitmessage.get_platform import platform +import os +from pybitmessage.bmconfigparser import BMConfigParser +from functools import partial +from kivymd.app import MDApp +from kivy.clock import Clock +from kivy.core.clipboard import Clipboard +from kivy.core.window import Window +from kivy.lang import Builder +from kivy.metrics import dp +from kivy.properties import ( + BooleanProperty, + ListProperty, + NumericProperty, + ObjectProperty, + StringProperty +) +from kivy.uix.boxlayout import BoxLayout +from kivy.uix.spinner import Spinner +from kivymd.uix.dialog import MDDialog +from kivymd.uix.label import MDLabel +from kivymd.uix.button import MDRaisedButton +from kivymd.uix.list import ( + IRightBodyTouch, + OneLineAvatarIconListItem, + OneLineListItem +) + +from kivy.uix.screenmanager import RiseInTransition, SlideTransition, FallOutTransition + +from pybitmessage import queues +from pybitmessage import state +from kivymd.uix.bottomsheet import MDCustomBottomSheet + +from kivy.lang import Observable +import ast + +from pybitmessage.baseclass.common import toast + + +if platform != "android": + from kivy.config import Config + Config.set("input", "mouse", "mouse, multitouch_on_demand") +elif platform == "android": + from jnius import autoclass, cast + from android.runnable import run_on_ui_thread + from android import python_act as PythonActivity + + Toast = autoclass("android.widget.Toast") + String = autoclass("java.lang.String") + CharSequence = autoclass("java.lang.CharSequence") + context = PythonActivity.mActivity + + @run_on_ui_thread + def show_toast(text, length): + """Its showing toast on screen""" + t = Toast.makeText(context, text, length) + t.show() + + +with open(os.path.join(os.path.dirname(__file__), "screens_data.json")) as read_file: + all_data = ast.literal_eval(read_file.read()) + data_screens = list(all_data.keys()) + +for modules in data_screens: + exec(all_data[modules]['Import']) + +# pylint: disable=too-few-public-methods,too-many-arguments,attribute-defined-outside-init + + +class Lang(Observable): + observers = [] + lang = None + + def __init__(self, defaultlang): + super(Lang, self).__init__() + self.ugettext = None + self.lang = defaultlang + self.switch_lang(self.lang) + + def _(self, text): + # return self.ugettext(text) + return text + + def fbind(self, name, func, args, **kwargs): + if name == "_": + self.observers.append((func, args, kwargs)) + else: + return super(Lang, self).fbind(name, func, *largs, **kwargs) + + def funbind(self, name, func, args, **kwargs): + if name == "_": + key = (func, args, kwargs) + if key in self.observers: + self.observers.remove(key) + else: + return super(Lang, self).funbind(name, func, *args, **kwargs) + + def switch_lang(self, lang): + # get the right locales directory, and instanciate a gettext + # locale_dir = os.path.join(os.path.dirname(__file__), 'translations', 'mo', 'locales') + # locales = gettext.translation('langapp', locale_dir, languages=[lang]) + # self.ugettext = locales.gettext + + # update all the kv rules attached to this text + for func, largs, kwargs in self.observers: + func(largs, None, None) + + +class NavigationItem(OneLineAvatarIconListItem): + """NavigationItem class for kivy Ui""" + badge_text = StringProperty() + icon = StringProperty() + active = BooleanProperty(False) + + def currentlyActive(self): + """Currenly active""" + for nav_obj in self.parent.children: + nav_obj.active = False + self.active = True + + +class NavigationDrawerDivider(OneLineListItem): + """ + A small full-width divider that can be placed + in the :class:`MDNavigationDrawer` + """ + + disabled = True + divider = None + _txt_top_pad = NumericProperty(dp(8)) + _txt_bot_pad = NumericProperty(dp(8)) + + def __init__(self, **kwargs): + # pylint: disable=bad-super-call + super(OneLineListItem, self).__init__(**kwargs) + self.height = dp(16) + + +class NavigationDrawerSubheader(OneLineListItem): + """ + A subheader for separating content in :class:`MDNavigationDrawer` + + Works well alongside :class:`NavigationDrawerDivider` + """ + + disabled = True + divider = None + theme_text_color = 'Secondary' + + +class ContentNavigationDrawer(BoxLayout): + """ContentNavigationDrawer class for kivy Uir""" + + def __init__(self, *args, **kwargs): + """Method used for contentNavigationDrawer""" + super(ContentNavigationDrawer, self).__init__(*args, **kwargs) + Clock.schedule_once(self.init_ui, 0) + + def init_ui(self, dt=0): + """Clock Schdule for class contentNavigationDrawer""" + self.ids.scroll_y.bind(scroll_y=self.check_scroll_y) + + def check_scroll_y(self, instance, somethingelse): + """show data on scroll down""" + if self.ids.btn.is_open: + self.ids.btn.is_open = False + + +class BadgeText(IRightBodyTouch, MDLabel): + """BadgeText class for kivy Ui""" + + +class CustomSpinner(Spinner): + """CustomSpinner class for kivy Ui""" + + def __init__(self, *args, **kwargs): + """Method used for setting size of spinner""" + super(CustomSpinner, self).__init__(*args, **kwargs) + self.dropdown_cls.max_height = Window.size[1] / 3 + self.values = list(addr for addr in BMConfigParser().addresses() + if BMConfigParser().get(str(addr), 'enabled') == 'true') + + +class NavigateApp(MDApp): + """Navigation Layout of class""" + # pylint: disable=too-many-public-methods,inconsistent-return-statements + + # theme_cls = ThemeManager() + previous_date = ObjectProperty() + obj_1 = ObjectProperty() + variable_1 = ListProperty(addr for addr in BMConfigParser().addresses() + if BMConfigParser().get(str(addr), 'enabled') == 'true') + nav_drawer = ObjectProperty() + state.screen_density = Window.size + window_size = state.screen_density + app_platform = platform + title = "PyBitmessage" + imgstatus = False + count = 0 + manager_open = False + file_manager = None + state.imageDir = os.path.join('./images', 'kivy') + image_path = state.imageDir + tr = Lang("en") # for changing in franch replace en with fr + + def build(self): + """Method builds the widget""" + for kv in data_screens: + Builder.load_file( + os.path.join( + os.path.dirname(__file__), + 'kv', + # f'{all_data[kv]["kv_string"]}.kv', + '{0}.kv'.format(all_data[kv]["kv_string"]), + ) + ) + # self.obj_1 = AddressBook() + # kivysignalthread = UIkivySignaler() + # kivysignalthread.daemon = True + # kivysignalthread.start() + Window.bind(on_keyboard=self.on_key, on_request_close=self.on_request_close) + return Builder.load_file( + os.path.join(os.path.dirname(__file__), 'main.kv')) + + def run(self): + """Running the widgets""" + super(NavigateApp, self).run() + + @staticmethod + def showmeaddresses(name="text"): + """Show the addresses in spinner to make as dropdown""" + if name == "text": + if BMConfigParser().addresses(): + return BMConfigParser().addresses()[0][:16] + '..' + return "textdemo" + elif name == "values": + if BMConfigParser().addresses(): + return [address[:16] + '..' + for address in BMConfigParser().addresses()] + return "valuesdemo" + + def getCurrentAccountData(self, text): + """Get Current Address Account Data""" + if text != '': + if os.path.exists(state.imageDir + '/default_identicon/{}.png'.format(text)): + self.load_selected_Image(text) + else: + self.root.ids.content_drawer.ids.reset_image.opacity = 0 + self.root.ids.content_drawer.ids.reset_image.disabled = True + address_label = self.current_address_label( + BMConfigParser().get(text, 'label'), text) + + self.root_window.children[1].ids.toolbar.title = address_label + state.association = text + state.searcing_text = '' + # LoadingPopup().open() + self.set_message_count() + for nav_obj in self.root.ids.content_drawer.children[ + 0].children[0].children[0].children: + nav_obj.active = True if nav_obj.text == 'Inbox' else False + self.fileManagerSetting() + Clock.schedule_once(self.setCurrentAccountData, 0.5) + + def fileManagerSetting(self): + """This method is for file manager setting""" + if not self.root.ids.content_drawer.ids.file_manager.opacity and \ + self.root.ids.content_drawer.ids.file_manager.disabled: + self.root.ids.content_drawer.ids.file_manager.opacity = 1 + self.root.ids.content_drawer.ids.file_manager.disabled = False + + def setCurrentAccountData(self, dt=0): + """This method set the current accout data on all the screens""" + self.root.ids.sc1.ids.ml.clear_widgets() + self.root.ids.sc1.loadMessagelist(state.association) + + self.root.ids.sc4.ids.ml.clear_widgets() + self.root.ids.sc4.children[2].children[2].ids.search_field.text = '' + self.root.ids.sc4.loadSent(state.association) + + self.root.ids.sc16.clear_widgets() + self.root.ids.sc16.add_widget(Draft()) + + self.root.ids.sc5.clear_widgets() + self.root.ids.sc5.add_widget(Trash()) + + self.root.ids.sc17.clear_widgets() + self.root.ids.sc17.add_widget(Allmails()) + + self.root.ids.sc10.ids.ml.clear_widgets() + self.root.ids.sc10.init_ui() + + self.root.ids.scr_mngr.current = 'inbox' + + @staticmethod + def getCurrentAccount(): + """It uses to get current account label""" + if state.association: + return state.association + return "Bitmessage Login" + + # @staticmethod + def addingtoaddressbook(self): + """Adding to address Book""" + width = .85 if platform == 'android' else .8 + self.add_popup = MDDialog( + title='Add contact\'s', + type="custom", + size_hint=(width, .23), + content_cls=GrashofPopup(), + buttons=[ + MDRaisedButton( + text="Save", + on_release=self.savecontact, + ), + MDRaisedButton( + text="Cancel", + on_release=self.close_pop, + ), + MDRaisedButton( + text="Scan QR code", + on_release=self.scan_qr_code, + ), + ], + ) + # self.add_popup.set_normal_height() + self.add_popup.auto_dismiss = False + self.add_popup.open() + # p = GrashofPopup() + # p.open() + + def scan_qr_code(self, instance): + """this method is used for showing QR code scanner""" + if self.is_camara_attached(): + self.add_popup.dismiss() + self.root.ids.sc23.get_screen(self.root.ids.scr_mngr.current, self.add_popup) + self.root.ids.scr_mngr.current = 'scanscreen' + else: + altet_txt = ( + 'Currently this feature is not avaialbe!' if platform == 'android' else 'Camera is not available!') + self.add_popup.dismiss() + toast(altet_txt) + + def is_camara_attached(self): + """This method is for checking is camera available or not""" + self.root.ids.sc23.check_camera() + is_available = self.root.ids.sc23.camera_avaialbe + return is_available + + def savecontact(self, instance): + """Method is used for saving contacts""" + pupup_obj = self.add_popup.content_cls + label = pupup_obj.ids.label.text.strip() + address = pupup_obj.ids.address.text.strip() + if label == '' and address == '': + pupup_obj.ids.label.focus = True + pupup_obj.ids.address.focus = True + elif address == '': + pupup_obj.ids.address.focus = True + elif label == '': + pupup_obj.ids.label.focus = True + else: + pupup_obj.ids.address.focus = True + # pupup_obj.ids.label.focus = True + + stored_address = [addr[1] for addr in []] + stored_labels = [labels[0] for labels in []] + if label and address and address not in stored_address \ + and label not in stored_labels and pupup_obj.valid: + queues.UISignalQueue.put(('rerenderAddressBook', '')) + self.add_popup.dismiss() + try: + rootIds = self.root.ids + except Exception as e: + rootIds = state.kivyapp.root.ids + rootIds.sc11.ids.ml.clear_widgets() + rootIds.sc11.loadAddresslist(None, 'All', '') + rootIds.scr_mngr.current = 'addressbook' + toast('Saved') + + def close_pop(self, instance): + """Pop is Canceled""" + self.add_popup.dismiss() + toast('Canceled') + + def getDefaultAccData(self, instance): + """Getting Default Account Data""" + if self.variable_1: + state.association = first_addr = self.variable_1[0] + return first_addr + return 'Select Address' + + def get_default_logo(self, instance): + """Getting default logo image""" + if self.variable_1: + first_addr = self.variable_1[0] + if BMConfigParser().get(str(first_addr), 'enabled') == 'true': + if os.path.exists( + state.imageDir + '/default_identicon/{}.png'.format(first_addr)): + return state.imageDir + '/default_identicon/{}.png'.format( + first_addr) + else: + return + return state.imageDir + '/drawer_logo1.png' + + @staticmethod + def addressexist(): + """Checking address existence""" + if BMConfigParser().addresses(): + return True + return False + + def on_key(self, window, key, *args): + # pylint: disable=inconsistent-return-statements, too-many-branches + """Method is used for going on previous screen""" + if key == 27: + if state.in_search_mode and self.root.ids.scr_mngr.current not in [ + "mailDetail", "create"]: + self.closeSearchScreen() + elif self.root.ids.scr_mngr.current == "mailDetail": + self.root.ids.scr_mngr.current = 'sent'\ + if state.detailPageType == 'sent' else 'inbox' \ + if state.detailPageType == 'inbox' else 'draft' + self.back_press() + if state.in_search_mode and state.searcing_text: + toolbar_obj = self.root.ids.toolbar + toolbar_obj.left_action_items = [ + ['arrow-left', lambda x: self.closeSearchScreen()]] + toolbar_obj.right_action_items = [] + self.root.ids.toolbar.title = '' + elif self.root.ids.scr_mngr.current == "create": + self.save_draft() + self.set_common_header() + state.in_composer = False + self.root.ids.scr_mngr.current = 'inbox' + elif self.root.ids.scr_mngr.current == "showqrcode": + self.set_common_header() + self.root.ids.scr_mngr.current = 'myaddress' + elif self.root.ids.scr_mngr.current == "random": + self.root.ids.scr_mngr.current = 'login' + elif self.root.ids.scr_mngr.current == 'pay-options': + self.set_common_header() + self.root.ids.scr_mngr.current = 'payment' + elif self.root.ids.scr_mngr.current == 'chroom': + if state.association: + address_label = self.current_address_label( + BMConfigParser().get( + state.association, 'label'), state.association) + self.root.ids.toolbar.title = address_label + self.set_common_header() + self.root.ids.scr_mngr.transition = FallOutTransition() + self.root.ids.scr_mngr.current = 'chlist' + self.root.ids.scr_mngr.transition = SlideTransition() + else: + if state.kivyapp.variable_1: + self.root.ids.scr_mngr.current = 'inbox' + self.root.ids.scr_mngr.transition.direction = 'right' + self.root.ids.scr_mngr.transition.bind(on_complete=self.reset) + return True + elif key == 13 and state.searcing_text and not state.in_composer: + if state.search_screen == 'inbox': + self.root.ids.sc1.children[1].active = True + Clock.schedule_once(self.search_callback, 0.5) + elif state.search_screen == 'addressbook': + self.root.ids.sc11.children[1].active = True + Clock.schedule_once(self.search_callback, 0.5) + elif state.search_screen == 'myaddress': + self.loadMyAddressScreen(True) + Clock.schedule_once(self.search_callback, 0.5) + elif state.search_screen == 'sent': + self.root.ids.sc4.children[1].active = True + Clock.schedule_once(self.search_callback, 0.5) + + def search_callback(self, dt=0): + """Show data after loader is loaded""" + if state.search_screen == 'inbox': + self.root.ids.sc1.ids.ml.clear_widgets() + self.root.ids.sc1.loadMessagelist(state.association) + self.root.ids.sc1.children[1].active = False + elif state.search_screen == 'addressbook': + self.root.ids.sc11.ids.ml.clear_widgets() + self.root.ids.sc11.loadAddresslist(None, 'All', '') + self.root.ids.sc11.children[1].active = False + elif state.search_screen == 'myaddress': + self.root.ids.sc10.ids.ml.clear_widgets() + self.root.ids.sc10.init_ui() + self.loadMyAddressScreen(False) + else: + self.root.ids.sc4.ids.ml.clear_widgets() + self.root.ids.sc4.loadSent(state.association) + self.root.ids.sc4.children[1].active = False + self.root.ids.scr_mngr.current = state.search_screen + + def loadMyAddressScreen(self, action): + """loadMyAddressScreen method spin the loader""" + if len(self.root.ids.sc10.children) <= 2: + self.root.ids.sc10.children[0].active = action + else: + self.root.ids.sc10.children[1].active = action + + def save_draft(self): + """Saving drafts messages""" + composer_objs = self.root + from_addr = str(self.root.ids.sc3.children[1].ids.ti.text) + # to_addr = str(self.root.ids.sc3.children[1].ids.txt_input.text) + if from_addr and state.detailPageType != 'draft' \ + and not state.in_sent_method: + Draft().draft_msg(composer_objs) + return + + def reset(self, *args): + """Set transition direction""" + self.root.ids.scr_mngr.transition.direction = 'left' + self.root.ids.scr_mngr.transition.unbind(on_complete=self.reset) + + @staticmethod + def status_dispatching(data): + """Dispatching Status acknowledgment""" + ackData, message = data + if state.ackdata == ackData: + state.status.status = message + + def clear_composer(self): + """If slow down, the new composer edit screen""" + self.set_navbar_for_composer() + composer_obj = self.root.ids.sc3.children[1].ids + composer_obj.ti.text = '' + composer_obj.btn.text = 'Select' + composer_obj.txt_input.text = '' + composer_obj.subject.text = '' + composer_obj.body.text = '' + state.in_composer = True + state.in_sent_method = False + + def set_navbar_for_composer(self): + """Clearing toolbar data when composer open""" + self.root.ids.toolbar.left_action_items = [ + ['arrow-left', lambda x: self.back_press()]] + self.root.ids.toolbar.right_action_items = [ + ['refresh', + lambda x: self.root.ids.sc3.children[1].reset_composer()], + ['send', + lambda x: self.root.ids.sc3.children[1].send(self)]] + + def set_toolbar_for_QrCode(self): + """This method is use for setting Qr code toolbar.""" + self.root.ids.toolbar.left_action_items = [ + ['arrow-left', lambda x: self.back_press()]] + self.root.ids.toolbar.right_action_items = [] + + def set_common_header(self): + """Common header for all window""" + self.root.ids.toolbar.right_action_items = [ + ['account-plus', lambda x: self.addingtoaddressbook()]] + # self.root.ids.toolbar.left_action_items = [ + # ['menu', lambda x: self.root.toggle_nav_drawer()]] + self.root.ids.toolbar.left_action_items = [ + ['menu', lambda x: self.root.ids.nav_drawer.set_state("toggle")]] + return + + def back_press(self): + """Method for, reverting composer to previous page""" + if self.root.ids.scr_mngr.current == 'create': + self.save_draft() + if self.root.ids.scr_mngr.current == \ + 'mailDetail' and state.in_search_mode: + toolbar_obj = self.root.ids.toolbar + toolbar_obj.left_action_items = [ + ['arrow-left', lambda x: self.closeSearchScreen()]] + toolbar_obj.right_action_items = [] + self.root.ids.toolbar.title = '' + else: + self.set_common_header() + if self.root.ids.scr_mngr.current == 'chroom' and state.association: + self.root.ids.scr_mngr.transition = FallOutTransition() + address_label = self.current_address_label( + BMConfigParser().get( + state.association, 'label'), state.association) + self.root.ids.toolbar.title = address_label + self.root.ids.scr_mngr.current = 'inbox' \ + if state.in_composer else 'allmails'\ + if state.is_allmail else state.detailPageType\ + if state.detailPageType else 'myaddress'\ + if self.root.ids.scr_mngr.current == 'showqrcode' else 'payment'\ + if self.root.ids.scr_mngr.current == 'pay-options' else 'chlist'\ + if self.root.ids.scr_mngr.current == 'chroom' else 'inbox' + if self.root.ids.scr_mngr.current == 'chlist': + self.root.ids.scr_mngr.transition = SlideTransition() + self.root.ids.scr_mngr.transition.direction = 'right' + self.root.ids.scr_mngr.transition.bind(on_complete=self.reset) + if state.is_allmail or state.detailPageType == 'draft': + state.is_allmail = False + state.detailPageType = '' + state.in_composer = False + + @staticmethod + def get_inbox_count(): + """Getting inbox count""" + pass + + @staticmethod + def get_sent_count(): + """Getting sent count""" + pass + + def set_message_count(self): + """Setting message count""" + pass + + def on_start(self): + """Setting message count""" + self.set_message_count() + + @staticmethod + def current_address_label(current_add_label=None, current_addr=None): + """Getting current address labels""" + addresses = [addr for addr in BMConfigParser().addresses() + if BMConfigParser().get(str(addr), 'enabled') == 'true'] + if addresses: + if current_add_label: + first_name = current_add_label + addr = current_addr + else: + addr = addresses[0] + first_name = BMConfigParser().get(addr, 'label') + if BMConfigParser().get(addr, 'enabled') != 'true': + return '' + f_name = first_name.split() + label = f_name[0][:14].capitalize() + '...' if len( + f_name[0]) > 15 else f_name[0].capitalize() + address = ' (' + addr + ')' + return label + address + return '' + + def searchQuery(self, instance): + """Showing searched mails""" + state.search_screen = self.root.ids.scr_mngr.current + state.searcing_text = str(instance.text).strip() + if instance.focus and state.searcing_text: + toolbar_obj = self.root.ids.toolbar + toolbar_obj.left_action_items = [ + ['arrow-left', lambda x: self.closeSearchScreen()]] + toolbar_obj.right_action_items = [] + self.root.ids.toolbar.title = '' + state.in_search_mode = True + + def closeSearchScreen(self): + """Function for close search screen""" + self.set_common_header() + if state.association: + address_label = self.current_address_label( + BMConfigParser().get( + state.association, 'label'), state.association) + self.root.ids.toolbar.title = address_label + state.searcing_text = '' + self.refreshScreen() + state.in_search_mode = False + + def refreshScreen(self): + """Method show search button only on inbox or sent screen""" + # pylint: disable=unused-variable + state.searcing_text = '' + if state.search_screen == 'inbox': + self.root.ids.sc1.ids.inbox_search.ids.search_field.text = '' + # try: + # self.root.ids.sc1.children[ + # 3].children[2].ids.search_field.text = '' + # except Exception: + # self.root.ids.sc1.children[ + # 2].children[2].ids.search_field.text = '' + self.root.ids.sc1.children[1].active = True + Clock.schedule_once(self.search_callback, 0.5) + elif state.search_screen == 'addressbook': + self.root.ids.sc11.ids.address_search.ids.search_field.text = '' + # self.root.ids.sc11.children[ + # 2].children[2].ids.search_field.text = '' + self.root.ids.sc11.children[ + 1].active = True + Clock.schedule_once(self.search_callback, 0.5) + elif state.search_screen == 'myaddress': + self.root.ids.sc10.ids.search_bar.ids.search_field.text = '' + # try: + # self.root.ids.sc10.children[ + # 1].children[2].ids.search_field.text = '' + # except Exception: + # self.root.ids.sc10.children[ + # 2].children[2].ids.search_field.text = '' + self.loadMyAddressScreen(True) + Clock.schedule_once(self.search_callback, 0.5) + else: + self.root.ids.sc4.ids.sent_search.ids.search_field.text = '' + # self.root.ids.sc4.children[ + # 2].children[2].ids.search_field.text = '' + self.root.ids.sc4.children[1].active = True + Clock.schedule_once(self.search_callback, 0.5) + return + + def set_mail_detail_header(self): + """Setting the details of the page""" + if state.association and state.in_search_mode: + address_label = self.current_address_label( + BMConfigParser().get( + state.association, 'label'), state.association) + self.root.ids.toolbar.title = address_label + toolbar_obj = self.root.ids.toolbar + toolbar_obj.left_action_items = [ + ['arrow-left', lambda x: self.back_press()]] + delete_btn = ['delete-forever', + lambda x: self.root.ids.sc14.delete_mail()] + dynamic_list = [] + if state.detailPageType == 'inbox': + dynamic_list = [ + ['reply', lambda x: self.root.ids.sc14.inbox_reply()], + delete_btn] + elif state.detailPageType == 'sent': + dynamic_list = [delete_btn] + elif state.detailPageType == 'draft': + dynamic_list = [ + ['pencil', lambda x: self.root.ids.sc14.write_msg(self)], + delete_btn] + toolbar_obj.right_action_items = dynamic_list + + def load_screen(self, instance): + """This method is used for loading screen on every click""" + if instance.text == 'Inbox': + self.root.ids.scr_mngr.current = 'inbox' + self.root.ids.sc1.children[1].active = True + elif instance.text == 'All Mails': + self.root.ids.scr_mngr.current = 'allmails' + try: + self.root.ids.sc17.children[1].active = True + except Exception: + self.root.ids.sc17.children[0].children[1].active = True + elif instance.text == 'Trash': + self.root.ids.scr_mngr.current = 'trash' + try: + self.root.ids.sc5.children[1].active = True + except Exception as e: + self.root.ids.sc5.children[0].children[1].active = True + Clock.schedule_once(partial(self.load_screen_callback, instance), 1) + + def load_screen_callback(self, instance, dt=0): + """This method is rotating loader for few seconds""" + if instance.text == 'Inbox': + self.root.ids.sc1.ids.ml.clear_widgets() + self.root.ids.sc1.loadMessagelist(state.association) + self.root.ids.sc1.children[1].active = False + elif instance.text == 'All Mails': + self.root.ids.sc17.clear_widgets() + self.root.ids.sc17.add_widget(Allmails()) + try: + self.root.ids.sc17.children[1].active = False + except Exception: + self.root.ids.sc17.children[0].children[1].active = False + elif instance.text == 'Trash': + # self.root.ids.sc5.ids.ml.clear_widgets() + # self.root.ids.sc5.init_ui(0) + self.root.ids.sc5.clear_widgets() + self.root.ids.sc5.add_widget(Trash()) + try: + self.root.ids.sc5.children[1].active = False + except Exception as e: + self.root.ids.sc5.children[0].children[1].active = False + + def on_request_close(self, *args): # pylint: disable=no-self-use + """This method is for app closing request""" + AppClosingPopup().open() + return True + + def file_manager_open(self): + """This method open the file manager of local system""" + from kivymd.uix.filemanager import MDFileManager + + if not self.file_manager: + self.file_manager = MDFileManager( + exit_manager=self.exit_manager, + select_path=self.select_path, + ext=['.png', '.jpg'] + ) + self.file_manager.previous = False + self.file_manager.current_path = '/' + if platform == 'android': + from android.permissions import request_permissions, Permission, check_permission + if check_permission(Permission.WRITE_EXTERNAL_STORAGE) and \ + check_permission(Permission.READ_EXTERNAL_STORAGE): + self.file_manager.show(os.getenv('EXTERNAL_STORAGE')) + self.manager_open = True + else: + request_permissions([Permission.WRITE_EXTERNAL_STORAGE, Permission.READ_EXTERNAL_STORAGE]) + else: + self.file_manager.show(os.environ["HOME"]) + self.manager_open = True + + def select_path(self, path): + """This method is used to save the select image""" + try: + from PIL import Image as PilImage + newImg = PilImage.open(path).resize((300, 300)) + if platform == 'android': + android_path = os.path.join( + os.environ['ANDROID_PRIVATE'] + '/app' + '/images' + '/kivy/') + if not os.path.exists(android_path + '/default_identicon/'): + os.makedirs(android_path + '/default_identicon/') + newImg.save('{1}/default_identicon/{0}.png'.format( + state.association, android_path)) + else: + if not os.path.exists(state.imageDir + '/default_identicon/'): + os.makedirs(state.imageDir + '/default_identicon/') + newImg.save(state.imageDir + '/default_identicon/{0}.png'.format(state.association)) + self.load_selected_Image(state.association) + toast('Image changed') + except Exception: + toast('Exit') + self.exit_manager() + + def exit_manager(self, *args): + """Called when the user reaches the root of the directory tree.""" + self.manager_open = False + self.file_manager.close() + + def load_selected_Image(self, curerentAddr): + """This method load the selected image on screen""" + top_box_obj = self.root.ids.content_drawer.ids.top_box.children[0] + # spinner_img_obj = self.root.ids.content_drawer.ids.btn.children[1] + # spinner_img_obj.source = top_box_obj.source ='./images/default_identicon/{0}.png'.format(curerentAddr) + top_box_obj.source = state.imageDir + '/default_identicon/{0}.png'.format(curerentAddr) + self.root.ids.content_drawer.ids.reset_image.opacity = 1 + self.root.ids.content_drawer.ids.reset_image.disabled = False + top_box_obj.reload() + # spinner_img_obj.reload() + + def rest_default_avatar_img(self): + """set default avatar generated image""" + img_path = state.imageDir + '/default_identicon/{}.png'.format(state.association) + try: + if os.path.exists(img_path): + os.remove(img_path) + self.root.ids.content_drawer.ids.reset_image.opacity = 0 + self.root.ids.content_drawer.ids.reset_image.disabled = True + except Exception as e: + pass + toast('Avatar reset') + + def copy_composer_text(self, text): # pylint: disable=no-self-use + """Copy the data from mail detail page""" + Clipboard.copy(text) + toast('Copied') + + def reset_login_screen(self): + """This method is used for clearing random screen""" + if self.root.ids.sc7.ids.add_random_bx.children: + self.root.ids.sc7.ids.add_random_bx.clear_widgets() + + def open_payment_layout(self, sku): + """It basically open up a payment layout for kivy Ui""" + pml = PaymentMethodLayout() + self.product_id = sku + self.custom_sheet = MDCustomBottomSheet(screen=pml) + self.custom_sheet.open() + + def initiate_purchase(self, method_name): + """initiate_purchase module""" + print("Purchasing {} through {}".format(self.product_id, method_name)) + + def _after_scan(self, text): + # if platform == 'android': + # toast_txt = cast(CharSequence, String(text)) + # show_toast(toast_txt, Toast.LENGTH_SHORT) + if self.root.ids.sc23.previous_open_screen == 'composer': + self.root.ids.sc3.children[1].ids.txt_input.text = text + self.root.ids.scr_mngr.current = 'create' + elif self.root.ids.sc23.previous_open_screen: + back_screen = self.root.ids.sc23.previous_open_screen + self.root.ids.scr_mngr.current = 'inbox' if back_screen == 'scanscreen' else back_screen + add_obj = self.root.ids.sc23.pop_up_instance + add_obj.content_cls.ids.address.text = text + Clock.schedule_once(partial(self.open_popup, add_obj), .5) + + @staticmethod + def open_popup(instance, dt): + """This method is used for opening popup""" + instance.open() + + +class PaymentMethodLayout(BoxLayout): + """PaymentMethodLayout class for kivy Ui""" diff --git a/src/tests/mock/pybitmessage/network/__init__.py b/src/tests/mock/pybitmessage/network/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/tests/mock/pybitmessage/network/threads.py b/src/tests/mock/pybitmessage/network/threads.py new file mode 100644 index 00000000..336aae5f --- /dev/null +++ b/src/tests/mock/pybitmessage/network/threads.py @@ -0,0 +1,33 @@ +"""Threading primitives for the network package""" + +import logging +import random +import threading + + +class StoppableThread(threading.Thread): + """Base class for application threads with stopThread method""" + name = None + logger = logging.getLogger('default') + + def __init__(self, name=None): + if name: + self.name = name + super(StoppableThread, self).__init__(name=self.name) + self.stop = threading.Event() + self._stopped = False + random.seed() + self.logger.info('Init thread %s', self.name) + + def stopThread(self): + """Stop the thread""" + self._stopped = True + self.stop.set() + + +class BusyError(threading.ThreadError): + """ + Thread error raised when another connection holds the lock + we are trying to acquire. + """ + pass diff --git a/src/tests/mock/pybitmessage/queues.py b/src/tests/mock/pybitmessage/queues.py new file mode 100644 index 00000000..16faaf82 --- /dev/null +++ b/src/tests/mock/pybitmessage/queues.py @@ -0,0 +1,50 @@ +"""Most of the queues used by bitmessage threads are defined here.""" + +import threading +import time + +from six.moves import queue + + +class ObjectProcessorQueue(queue.Queue): + """Special queue class using lock for `.threads.objectProcessor`""" + + maxSize = 32000000 + + def __init__(self): + queue.Queue.__init__(self) + self.sizeLock = threading.Lock() + #: in Bytes. We maintain this to prevent nodes from flooding us + #: with objects which take up too much memory. If this gets + #: too big we'll sleep before asking for further objects. + self.curSize = 0 + + def put(self, item, block=True, timeout=None): + while self.curSize >= self.maxSize: + time.sleep(1) + with self.sizeLock: + self.curSize += len(item[1]) + queue.Queue.put(self, item, block, timeout) + + def get(self, block=True, timeout=None): + item = queue.Queue.get(self, block, timeout) + with self.sizeLock: + self.curSize -= len(item[1]) + return item + + +workerQueue = queue.Queue() +UISignalQueue = queue.Queue() +addressGeneratorQueue = queue.Queue() +#: `.network.ReceiveQueueThread` instances dump objects they hear +#: on the network into this queue to be processed. +objectProcessorQueue = ObjectProcessorQueue() +# invQueue = MultiQueue() +# addrQueue = MultiQueue() +portCheckerQueue = queue.Queue() +receiveDataQueue = queue.Queue() +#: The address generator thread uses this queue to get information back +#: to the API thread. +apiAddressGeneratorReturnQueue = queue.Queue() +#: for exceptions +excQueue = queue.Queue() diff --git a/src/tests/mock/pybitmessage/screens_data.json b/src/tests/mock/pybitmessage/screens_data.json new file mode 100644 index 00000000..1ab9d6f4 --- /dev/null +++ b/src/tests/mock/pybitmessage/screens_data.json @@ -0,0 +1,78 @@ +{ + "Inbox": { + "kv_string": "inbox", + "name_screen": "inbox", + "Import": "from pybitmessage.baseclass.inbox import Inbox", + }, + "Sent": { + "kv_string": "sent", + "name_screen": "sent", + "Import": "from pybitmessage.baseclass.sent import Sent", + }, + "Draft": { + "kv_string": "draft", + "name_screen": "draft", + "Import": "from pybitmessage.baseclass.draft import Draft", + }, + "Trash": { + "kv_string": "trash", + "name_screen": "trash", + "Import": "from pybitmessage.baseclass.trash import Trash", + }, + "All Mails": { + "kv_string": "allmails", + "name_screen": "allmails", + "Import": "from pybitmessage.baseclass.allmail import Allmails", + }, + "Address Book": { + "kv_string": "addressbook", + "name_screen": "addressbook", + "Import": "from pybitmessage.baseclass.addressbook import AddressBook", + }, + "Settings": { + "kv_string": "settings", + "name_screen": "set", + "Import": "from pybitmessage.baseclass.settings import Setting", + }, + "Payment": { + "kv_string": "payment", + "name_screen": "payment", + "Import": "from pybitmessage.baseclass.payment import Payment", + }, + "Network status": { + "kv_string": "network", + "name_screen": "networkstat", + "Import": "from pybitmessage.baseclass.network import NetworkStat", + }, + "My addresses": { + "kv_string": "myaddress", + "name_screen": "myaddress", + "Import": "from pybitmessage.baseclass.myaddress import MyAddress", + }, + "MailDetail": { + "kv_string": "maildetail", + "name_screen": "mailDetail", + "Import": "from pybitmessage.baseclass.maildetail import MailDetail", + }, + "Create": { + "kv_string": "msg_composer", + "name_screen": "create", + "Import": "from pybitmessage.baseclass.msg_composer import Create", + }, + "Login": { + "kv_string": "login", + "Import": "from pybitmessage.baseclass.login import *", + }, + "Scanner": { + "kv_string": "scan_screen", + "Import": "from pybitmessage.baseclass.scan_screen import ScanScreen", + }, + "Popups": { + "kv_string": "popup", + "Import": "from pybitmessage.baseclass.popup import *", + }, + "Qrcode": { + "kv_string": "qrcode", + "Import": "from pybitmessage.baseclass.qrcode import ShowQRCode", + }, +} diff --git a/src/tests/mock/pybitmessage/shutdown.py b/src/tests/mock/pybitmessage/shutdown.py new file mode 100644 index 00000000..6ab29e02 --- /dev/null +++ b/src/tests/mock/pybitmessage/shutdown.py @@ -0,0 +1,84 @@ +"""shutdown function""" +import os +import queue as Queue +import threading +import time + +from pybitmessage import state +from pybitmessage.network.threads import StoppableThread +from pybitmessage.queues import ( + addressGeneratorQueue, objectProcessorQueue, UISignalQueue, workerQueue) + + +def doCleanShutdown(): + """ + Used to tell all the treads to finish work and exit. + """ + state.shutdown = 1 + + objectProcessorQueue.put(('checkShutdownVariable', 'no data')) + for thread in threading.enumerate(): + if thread.is_alive() and isinstance(thread, StoppableThread): + thread.stopThread() + + UISignalQueue.put(( + 'updateStatusBar', + 'Saving the knownNodes list of peers to disk...')) + # logger.info('Saving knownNodes list of peers to disk') + # saveKnownNodes() + # logger.info('Done saving knownNodes list of peers to disk') + UISignalQueue.put(( + 'updateStatusBar', + 'Done saving the knownNodes list of peers to disk.')) + # logger.info('Flushing inventory in memory out to disk...') + UISignalQueue.put(( + 'updateStatusBar', + 'Flushing inventory in memory out to disk.' + ' This should normally only take a second...')) + + # Verify that the objectProcessor has finished exiting. It should have + # incremented the shutdown variable from 1 to 2. This must finish before + # we command the sqlThread to exit. + # while state.shutdown == 1: + # time.sleep(.1) + + # Wait long enough to guarantee that any running proof of work worker + # threads will check the shutdown variable and exit. If the main thread + # closes before they do then they won't stop. + time.sleep(.25) + + for thread in threading.enumerate(): + if ( + thread is not threading.currentThread() + and isinstance(thread, StoppableThread) + and thread.name != 'SQL' + ): + # logger.debug("Waiting for thread %s", thread.name) + thread.join() + + # This one last useless query will guarantee that the previous flush + # committed and that the + # objectProcessorThread committed before we close the program. + # sqlQuery('SELECT address FROM subscriptions') + # logger.info('Finished flushing inventory.') + # sqlStoredProcedure('exit') + + # flush queues + for queue in ( + workerQueue, UISignalQueue, addressGeneratorQueue, + objectProcessorQueue): + while True: + try: + queue.get(False) + queue.task_done() + except Queue.Empty: + break + + # if state.thisapp.daemon or not state.enableGUI: + # logger.info('Clean shutdown complete.') + # state.thisapp.cleanup() + # os._exit(0) # pylint: disable=protected-access + # else: + # logger.info('Core shutdown complete.') + # for thread in threading.enumerate(): + # logger.debug('Thread %s still running', thread.name) diff --git a/src/tests/mock/pybitmessage/singleton.py b/src/tests/mock/pybitmessage/singleton.py new file mode 100644 index 00000000..5c6c43be --- /dev/null +++ b/src/tests/mock/pybitmessage/singleton.py @@ -0,0 +1,22 @@ +""" +Singleton decorator definition +""" + +from functools import wraps + + +def Singleton(cls): + """ + Decorator implementing the singleton pattern: + it restricts the instantiation of a class to one "single" instance. + """ + instances = {} + + # https://github.com/sphinx-doc/sphinx/issues/3783 + @wraps(cls) + def getinstance(): + """Find an instance or save newly created one""" + if cls not in instances: + instances[cls] = cls() + return instances[cls] + return getinstance diff --git a/src/tests/mock/pybitmessage/state.py b/src/tests/mock/pybitmessage/state.py new file mode 100644 index 00000000..62848c25 --- /dev/null +++ b/src/tests/mock/pybitmessage/state.py @@ -0,0 +1,133 @@ +""" +src/state.py +================================= +""" +import collections + +neededPubkeys = {} +streamsInWhichIAmParticipating = [] +# For UPnP +extPort = None +# for Tor hidden service +socksIP = None +# Network protocols availability, initialised below +networkProtocolAvailability = None +appdata = '' # holds the location of the application data storage directory +# Set to 1 by the doCleanShutdown function. +# Used to tell the proof of work worker threads to exit. +shutdown = 0 +# Component control flags - set on startup, do not change during runtime +# The defaults are for standalone GUI (default operating mode) +enableNetwork = True # enable network threads +enableObjProc = True # enable object processing threads +enableAPI = True # enable API (if configured) +enableGUI = True # enable GUI (QT or ncurses) +enableSTDIO = False # enable STDIO threads +curses = False +sqlReady = False # set to true by sqlTread when ready for processing +maximumNumberOfHalfOpenConnections = 0 + +maximumLengthOfTimeToBotherResendingMessages = 0 + +invThread = None +addrThread = None +downloadThread = None +uploadThread = None +ownAddresses = {} +# If the trustedpeer option is specified in keys.dat then this will +# contain a Peer which will be connected to instead of using the +# addresses advertised by other peers. The client will only connect to +# this peer and the timing attack mitigation will be disabled in order +# to download data faster. The expected use case is where the user has +# a fast connection to a trusted server where they run a BitMessage +# daemon permanently. If they then run a second instance of the client +# on a local machine periodically when they want to check for messages +# it will sync with the network a lot faster without compromising +# security. +trustedPeer = None +discoveredPeers = {} +Peer = collections.namedtuple('Peer', ['host', 'port']) + + +def resetNetworkProtocolAvailability(): + """This method helps to reset the availability of network protocol""" + # pylint: disable=global-statement + global networkProtocolAvailability + networkProtocolAvailability = {'IPv4': None, 'IPv6': None, 'onion': None} + + +resetNetworkProtocolAvailability() + +dandelion = 0 + +testmode = False + +kivy = False + +association = '' + +kivyapp = None + +navinstance = None + +mail_id = 0 + +myAddressObj = None + +detailPageType = None + +ackdata = None + +status = None + +screen_density = None + +msg_counter_objs = None + +check_sent_acc = None + +sent_count = 0 + +inbox_count = 0 + +trash_count = 0 + +draft_count = 0 + +all_count = 0 + +searcing_text = '' + +search_screen = '' + +send_draft_mail = None + +is_allmail = False + +in_composer = False + +availabe_credit = 0 + +in_sent_method = False + +in_search_mode = False + +clientHasReceivedIncomingConnections = False +"""used by API command clientStatus""" + +numberOfMessagesProcessed = 0 +numberOfBroadcastsProcessed = 0 +numberOfPubkeysProcessed = 0 + +statusIconColor = 'red' +""" +GUI status icon color +.. note:: bad style, refactor it +""" + +ackdataForWhichImWatching = {} + +thisapp = None +"""Singleton instance""" + +imageDir = None