Compare commits

...

212 Commits

Author SHA1 Message Date
Severiano Jaramillo b12b3b9546 Remove the eula from the strings and placed it instead as a file into the Assets folder.
We found that using the loadUrl WebView's method instead of loadData renders the html page better, for that reason the eula is now saved as a fule in the assets folder and loaded using the WebView's loadUrl method.
2019-01-02 14:29:29 -06:00
Javier Varona d04e847bba Merge branch 'develop' of https://github.com/agorise/crystal-wallet-android into develop 2018-11-30 21:55:46 -04:00
Javier Varona fe1068bcdf - Fixed bitshares QR scan won't load the amount 2018-11-30 21:54:54 -04:00
Severiano Jaramillo af4a4aca8b Merge branch 'develop' of github.com:Agorise/crystal-wallet-android into develop 2018-11-30 19:51:11 -06:00
Severiano Jaramillo 450a7c285b Increase the camera preview area to improve the QR read performance and make some UI improvements. 2018-11-30 19:49:40 -06:00
hvarona 08df53730b Fix send bad request
Change Send manager to send only the required amount from gtxio
added error capture for estimatefee
2018-11-30 20:58:13 -04:00
Severiano Jaramillo 618e71a6ac Fix FloatingActionButtons used in SendTransactionFragment and BoardActivity, after the migration to the new MaterialComponents they got messed up because the way they are designed in XML and its properties changed a bit. 2018-11-30 11:52:53 -06:00
Severiano Jaramillo 52f75eb3cf Make the area that shows the camera feed to always be a square. The camera feed still has a 3:4 aspect ratio but there is a black square behind it that creates the always square illusion. 2018-11-30 11:29:27 -06:00
Severiano Jaramillo 4bf1c14bb8 Temporarily remove AnimatedTabLayout library. 2018-11-29 21:06:36 -06:00
Severiano Jaramillo a32f7c9eb0 Merge branch 'develop' of github.com:Agorise/crystal-wallet-android into develop 2018-11-29 12:23:24 -06:00
Severiano Jaramillo bfe65ac1a7 Update project's compileSdkVersion to 28. 2018-11-29 10:36:25 -06:00
Severiano Jaramillo ea57899027 - Update libraries and remove unused ones.
- Update Gradle version and remove unused Gradle plugin.
- Remove Picasso images library and replace its usages by Glide.
- Fix CreateSeedActivity which was using Buterknife with Kotlin and that stopped working after updating the kotlin version.
2018-11-29 10:33:42 -06:00
hvarona 2c431ef4e0 Fix BitocinGtxIO Foreign Key Definition 2018-11-28 23:42:56 -04:00
hvarona a13bbadd2e Change EULA
Added memo to bitshares send request
2018-11-28 23:12:04 -04:00
hvarona 1514d32457 Merge branch 'develop' of https://github.com/Agorise/crystal-wallet-android into develop-local 2018-11-28 22:16:55 -04:00
hvarona 7ddc2745b2 Fix error parsing bitshares qr code with lower case 2018-11-28 22:00:46 -04:00
Severiano Jaramillo 0dd9aecbf3 Merge branch 'develop' of github.com:Agorise/crystal-wallet-android into develop 2018-11-28 16:24:19 -06:00
hvarona 56a3c006cc put version code 2018-11-25 20:56:00 -04:00
Javier Varona 178a0de26b - Uncommenting the dismissing of the sending dialog in sendfragment 2018-11-24 22:33:05 -04:00
Javier Varona c2575c7ab8 - Fixed duplicated transactions by inserting new primary key yo crypto_coin_transaction 2018-11-24 22:08:14 -04:00
hvarona b10b385a83 Merge branch 'develop' of https://github.com/Agorise/crystal-wallet-android into develop-local 2018-11-22 22:37:45 -04:00
hvarona 5ceffbec2f added send request to manager 2018-11-22 22:36:45 -04:00
Javier Varona 0e2d33848b - Now the transactions shows as many decimals as the coin have, only when needed 2018-11-22 21:32:04 -04:00
hvarona ddabac3911 Merge branch 'develop' of https://github.com/Agorise/crystal-wallet-android into develop-local 2018-11-22 00:03:11 -04:00
hvarona a31efc16e4 Fix send Fragment to send bitcion 2018-11-22 00:02:30 -04:00
hvarona 0ebc6adfe2 Added name to account seed 2018-11-21 22:51:25 -04:00
hvarona 93e71887a8 Fixed bitocin balance 2018-11-21 22:42:59 -04:00
Javier Varona 534cee187b - Fixed send button doing nothing when there's no pin or pattern configured 2018-11-21 22:27:09 -04:00
hvarona 469e7b08e4 Fixed using null activity 2018-11-21 22:01:25 -04:00
hvarona 5b8d12f886 Merge branch 'develop' of https://github.com/Agorise/crystal-wallet-android into develop-local 2018-11-21 21:57:26 -04:00
Javier Varona 0c8157a659 - Improve the speed of the qr code generation on receivefragment
- The feedback is better now in the receivefragment
2018-11-21 21:49:44 -04:00
hvarona a0c01d6843 Merge branch 'develop' of https://github.com/Agorise/crystal-wallet-android into develop-local 2018-11-21 19:52:48 -04:00
hvarona 8502272b25
Merge pull request #10 from hvarona/develop
Fix amount on bitcoin transactions
2018-11-21 19:52:44 -04:00
Severiano Jaramillo a3c0504265 Create Crystal launcher icon in different formats according to different Android OS versions. 2018-11-21 11:57:54 -06:00
Severiano Jaramillo 96881f3292 Improve Send and Receive Transaction layouts to dynamically adjust to different screen sizes. Removed unnecessary SquaredImageView, the same effect can now be achieved with Android's ConstraintLayout. 2018-11-21 11:45:57 -06:00
Severiano Jaramillo aec93ddb22 Added a white version of the Crystal logo and used it in the Login and Main Activities' toolbar. 2018-11-21 09:18:53 -06:00
Severiano Jaramillo 86c96b1c3a Changed Crystal color palette and improved the gradient in the AccountdFragment. 2018-11-21 09:10:02 -06:00
hvarona c2416e64ad Fix amount on bitcoin transactions 2018-11-21 00:04:19 -04:00
Severiano Jaramillo db4827456f Merge branch 'develop' of github.com:Agorise/crystal-wallet-android into develop 2018-11-20 21:28:08 -06:00
Severiano Jaramillo 0c64e78e89 - Remove not used library.
- Improve SendTransactionFragment layout to automatically adjust the camera preview to an 4:3 aspect ratio, which in turn avoids the problem that the app was not reading QR Codes.
2018-11-20 21:28:02 -06:00
hvarona 9a63c25ee4
Merge pull request #9 from hvarona/develop
Develop
2018-11-20 23:04:58 -04:00
Javier Varona 62541e1863 - Now the bitcoin accounts get their transactions loaded 2018-11-19 23:19:23 -04:00
hvarona 7367b33bb2 Merge remote-tracking branch 'hvarona/develop' into develop-local 2018-11-19 22:26:29 -04:00
hvarona 3ac651c0a4 change to testnet 2018-11-19 22:25:34 -04:00
Javier Varona ecf5335ea3 - Fixing amount in receive fragment
- Fixing send fragment to read bitcoin uris qr code
2018-11-18 22:26:05 -04:00
hvarona dfca4b8c53 Change parseUri to compares with lowercase cryptocoin 2018-11-18 21:55:32 -04:00
hvarona 73ba419b66 Change parseUri to compares with lowercase cryptocoin 2018-11-18 21:41:29 -04:00
hvarona ce1df325d8 Change parseUri to compares with lowercase cryptocoin 2018-11-18 21:30:42 -04:00
hvarona 2e2ee01052 Change parseUri to compares with lowercase cryptocoin 2018-11-18 21:24:15 -04:00
hvarona 5aa7175016 Implemented WIF Account Seed 2018-11-18 11:44:30 -04:00
hvarona 6e4b66a5e3
Merge pull request #8 from hvarona/develop
Develop
2018-11-17 13:17:47 -04:00
hvarona 83c95a35a8 Merge remote-tracking branch 'hvarona/develop' into develop-local 2018-11-14 22:50:55 -04:00
hvarona 683883ca0e Fix account seed names 2018-11-14 22:49:47 -04:00
Javier Varona b6cddedbd4 - Fixed tables creation 2018-11-13 22:46:50 -04:00
Javier Varona a11f74f6a5 - Now the send fragment reads bitcoin alike URI 2018-11-13 22:30:51 -04:00
hvarona 91ef2d0e9a Added parseUri to BitocinManager 2018-11-13 22:02:22 -04:00
Javier Varona 193f426739 - Added a bitcoin parse request to read bitcoin alike URIs
- Now the receive fragment generates bitcoin alike URIs
- Added commented code to read QR code URIs in the send fragment
2018-11-13 21:38:05 -04:00
hvarona 719d6769fc Change bitcoin address model keys 2018-11-12 22:40:44 -04:00
hvarona 0126d6d114 Fix import graphene accounts 2018-11-12 22:11:14 -04:00
hvarona 14a405f67c change grapheneapigenerator to be used with steem 2018-11-11 23:06:09 -04:00
hvarona 9c6db3a6ea fix calculate uri 2018-11-11 22:26:34 -04:00
hvarona 6e81192b37 Added steem url and verifier
Added bip21 implementation with lowercase
2018-11-11 21:59:28 -04:00
hvarona 8ff53de0ae Added BitcoinUriRequest 2018-11-10 23:21:49 -04:00
hvarona 01e67ce258
Merge pull request #7 from hvarona/develop
Develop
2018-11-10 14:08:17 -04:00
hvarona 42c48d31ea Merge remote-tracking branch 'hvarona/develop' into develop-local 2018-11-08 00:09:38 -04:00
hvarona 0e37879f02 Added Steem Account Managment 2018-11-08 00:09:05 -04:00
Javier Varona ca4662867d Merge branch 'develop' of https://github.com/hvarona/crystal-wallet-android-1 into develop 2018-11-07 22:02:37 -04:00
Javier Varona 9df1db6669 - ReceiveFragment and SendFragmente were adapted to manage bitcoin accounts 2018-11-07 22:02:15 -04:00
hvarona fa952d9a36 Added validate Address request 2018-11-06 23:34:54 -04:00
hvarona dc6114bb10 Starting Bitocin like account balance to 0
Added Steem url verifier
2018-11-05 23:06:12 -04:00
Javier Varona e9f77c4981 - Now the bitcoin alike account are created using the interface in "seed settings" to choose them
- Now the balance list header shows the coin icon of its crypto net
- Added icons to cryptoNet enum
2018-11-03 21:01:00 -04:00
hvarona 043325b887 Create and import Bitcoin Like Account request implementation 2018-11-01 00:11:51 -04:00
hvarona 51498192c6 Create and import Bitcoin Like Account request implementation 2018-10-31 23:14:13 -04:00
Javier Varona 5965528abe - Once again, the list of accounts in the profiles settings are really account seeds
- When an account seed is pressed, the seeds settings show the mnemonic words and a new tab for the coin settings
- The coin settings allow the user to activate a coin account like bitcoin
2018-10-31 22:19:15 -04:00
hvarona 6c0d936b67 Added path to the insigiht api
Added Bitcoin Server Verifier
Added Bitcoin url and db currency
2018-10-30 22:16:27 -04:00
Severiano Jaramillo 960dd67394 Merge branch 'develop' of github.com:Agorise/crystal-wallet-android into develop 2018-10-25 13:40:50 -05:00
Severiano Jaramillo 4cd304d732 Removed Toolbar animation in all the other activities it was being used and changed to use the GIF version instead. It provides a much cleaner and safer implementation that works fine on all tested devices (Android L, M, O & P). 2018-10-25 13:40:45 -05:00
Severiano Jaramillo 36d97e8166 Change background animation in IntroActivity from Video to GIF.
The video background animation was giving errors on Android Lollipop and wasn't displaying correctly. Searching on the internet I found that the encoding of the video could be the problem but tested with many different encoding profiles (H.264 high, main, baseline) and none of them solved the issue so I finally gave up and converted the video back to a GIF and used that one.
2018-10-25 11:51:53 -05:00
dtvv 7ae875e208 Fixes from henrry code 2018-10-25 11:01:05 -05:00
dtvv 10cc88c8d3 Fixes from henrry code 2018-10-25 10:03:45 -05:00
dtvv 9a1dc0548d Merge branch 'develop' of https://github.com/Agorise/crystal-wallet-android into develop 2018-10-25 09:55:19 -05:00
dtvv b5bc9e88c7 In the Receive assets screen change the layout to relative to get more controle over position 2018-10-25 09:54:42 -05:00
hvarona b426076eea commented the OnResponse interface
Fixed error feedback on import request
2018-10-25 08:30:32 -04:00
hvarona d456ee49a8 Merge remote-tracking branch 'origin/develop' into develop 2018-10-25 08:10:11 -04:00
hvarona c4ac6b4a29 Fixed error feedback on import request 2018-10-25 08:09:54 -04:00
dtvv ae3ed3725d Security mode is only used when the user try to send assets in the send assets screen 2018-10-25 02:48:18 -05:00
dtvv 71c26c0ddf -Add listener to the class PatternRequestActivity to reuse the on success or fail password event
-Add listener to the class PinRequestActivity to reuse the on success or fail password event
2018-10-25 02:24:15 -05:00
dtvv 0bb428a658 Add listener to the class PinRequestActivity to reuse the on success or fail password event 2018-10-25 02:22:43 -05:00
dtvv 753a4a5943 Fix to this commmit: Add listener to the class PatternRequestActivity to reuse the on success or fail password event 2018-10-25 02:16:38 -05:00
dtvv ba96d999ff Add listener to the class PatternRequestActivity to reuse the on success or fail password event 2018-10-25 02:13:11 -05:00
dtvv a21796d2d0 -The GrapheneAccount class is crasing, now it is fixed 2018-10-25 02:04:02 -05:00
dtvv b05cf62daf -After henrry updated the file ImportSeedActivity i found some errors and i had to fix it again so it does not use 2 equeals services 2018-10-25 01:55:58 -05:00
dtvv 7889d4f242 Merge branch 'develop' of https://github.com/Agorise/crystal-wallet-android into develop 2018-10-25 01:35:28 -05:00
dtvv 2ca94de241 -In the send assets screen it crashes when type an amount 2018-10-25 01:34:48 -05:00
hvarona e8ceb88c6e Added the import rrequest with only the mnemonic words of the seed 2018-10-25 00:58:42 -04:00
hvarona ac95b66511 Added the import rrequest with only the mnemonic words of the seed 2018-10-25 00:22:13 -04:00
Javier Varona c216dfa209 Merge branch 'develop' of https://github.com/Agorise/crystal-wallet-android into develop
# Conflicts:
#	app/src/main/java/cy/agorise/crystalwallet/activities/ImportSeedActivity.java
2018-10-24 23:07:11 -04:00
Javier Varona 04d3a0d2e6 - Change the import bitshares account request to avoid using the name of the account 2018-10-24 22:53:51 -04:00
dtvv 418749ea6a Pocket security functionality disable for now 2018-10-24 16:18:01 -05:00
dtvv 4ba6299822 -Search solution for the viewpager crashes actions when try to define a pattern horisontally
-In the ChildViewPager of security settings disable swipe horisontally and navigation now it is by tabs
-In the ChildViewPager modify the class so it can control when the swipe touch event is bloqued or not correctly
2018-10-24 03:56:21 -05:00
dtvv b3a267fb47 When the PATTERN is typed 4 wrong times, it has to block 15 seconds before make new tries 2018-10-24 02:59:42 -05:00
dtvv c3c6677faa -In the import seed activity add all the fixed strings to android string resources
-In the import seed window remove the first service connection that validates the seed, it seems that in the second service it does it too
-In the import seed activity adjust the new error strings to the new error show model
-In the import seed activity onlye enable the CREATE WALLET button if all the fields are correctly validate, specially the PIN mismatch error
2018-10-24 02:38:48 -05:00
dtvv 8e55afad55 Merge branch 'develop' of https://github.com/Agorise/crystal-wallet-android into develop 2018-10-24 02:02:26 -05:00
dtvv 41a3b1c172 In the create seed activity when the user types it validates with the server each time, this has to be done til the user touches the CREATE WALLET button for performance 2018-10-24 02:01:44 -05:00
hvarona cd00923ebc Added Get Next Bitcoin and Bitcoin like coins request and manager 2018-10-23 23:28:53 -04:00
Javier Varona 2027677956 - Added new requests needed to implement bitcoin alike accounts funcionality 2018-10-22 21:05:48 -04:00
hvarona b3442a511e Implemented the send service of bitcoin likes account with the new architecture 2018-10-19 00:13:42 -04:00
hvarona 6b37db9279 Merge branch 'develop' of https://github.com/Agorise/crystal-wallet-android into develop 2018-10-18 21:22:24 -04:00
dtvv 4743042c7e When the PATTERN screen request is incorrect show message to user 2018-10-18 08:50:16 -05:00
dtvv 0019385827 When the pattern is set in settings, show message to the user 2018-10-18 08:08:16 -05:00
dtvv 03e03f2fb1 -The strings in PatternSecurityFragment save them as android strings
-In the settings pattern screen when user tries to set a new one and is going to confirm it and makes a mistake it does not show error
-When the pattern is set in the security settings, it should not open the pattern activity after that
2018-10-18 04:03:50 -05:00
dtvv e24b355281 In the import seed window ask before import the account 2018-10-18 03:40:33 -05:00
dtvv 3c0e2058bb Merge branch 'develop' of https://github.com/Agorise/crystal-wallet-android into develop 2018-10-18 03:29:43 -05:00
dtvv b73c77e86f -In the import seed window, the service doest not have to be connecting to the service each time the user type text to validate if the account name exists
-In the import seed window, the service to check valid mnemonic does not have to fire each time the user is typing text
-In the import seed window, the service to check valid mnemonic has to fire only when the import account button is pressed
-In the import seed window add error message when the service to validate the seed or the account name is correct fails
2018-10-18 03:28:12 -05:00
hvarona 356300fed4 Change BitcoinTransactions to adapt to new architecture
Change bitcoin like logic to adapt to new architecture
2018-10-17 23:32:17 -04:00
Javier Varona 7681523252 - Added query to get address 2018-10-17 22:15:19 -04:00
Javier Varona 76a53c12e1 - Added bitcoin generated addresses table 2018-10-17 21:56:40 -04:00
dtvv 8a7b41ad1d When the PIN does not match in the screen of import seed it does not show error message 2018-10-17 04:50:38 -05:00
dtvv 3941585740 When the PIN is typed 4 wrong times, it has to block 15 seconds before make new tries 2018-10-17 04:14:43 -05:00
dtvv c554471115 -When the PIN is incorrect in the PIN request activity the password should show red animation
-When the PIN is incorrect in the PIN request activity the password should dissapear
-When the PIN is incorrect in the PIN request activity remove the Toast, the animation is enougth
2018-10-17 03:46:16 -05:00
dtvv a4c6b1cfb1 -When the PIN does not match in the screen of import seed it does not show error message
-Set a button in the PIN request activity to clic when the complete PIN is completely typed
-In the PIN request activity, the OK button should not be enabled till a valid PIN is on it
-Change the button style in the PIN request screen
2018-10-17 01:06:35 -05:00
dtvv d2668bc04e Limit the PIN max length 2018-10-17 00:34:24 -05:00
dtvv 95bdc725c0 -When try to change to none pattern either pin it should ask for confirmation
-When the security mode is changed to none show toast of it
-When the security mode is set to none and you change to pattern that erase the visible fake password
2018-10-17 00:31:36 -05:00
Severiano Jaramillo 91bc799310 - Cleaned unused imports in various Classes.
- Simplified BalancesFragment by removing a couple of unnecessary files and merging its funcionality into more compact classes/layouts
2018-10-16 11:39:39 -05:00
Severiano Jaramillo ed7dc5424e - Simplified TransactionsFragment view hierarchy and removed unused files.
- Improved TextViews that appear in both Contacts and Transactions when there are no items to display.
- Cleaned some unused code in different parts of the app.
2018-10-15 16:51:48 -05:00
Severiano Jaramillo d367373d8e Make improvements to Crystal's toolbar animation to avoud memory leaks. 2018-10-15 14:17:21 -05:00
Severiano Jaramillo b8fd519b1a Added AndroidDebugDatabase library to Crystal, this library makes debugging the database a lot easier. Also updated ConstraintLayout library version. 2018-10-15 12:28:20 -05:00
Javier Varona 8f2fc92945 - Now the receive fragment generate the QR code in another thread and don't stop the UI anymore 2018-10-14 21:55:00 -04:00
dtvv 319321c94b -When the PIN is being entered the user should press a button to confirm the pin
-When the PIN is being entered use question dialog to confirm the save PIN
-When the PIN is correcto typed and match correctly enable the ok button
-When PIN is configured it should not be showed at the beginning of the app
-When PIN is configuren it should not be  showed at the beginning of security settings app
-If PIN is configured put some stuff enmascared
-When you set the new PIN that it does not show the PIN activity window, it is not necesary
2018-10-14 03:57:03 -05:00
dtvv cd160c9832 -When PIN is configured it should not be showed at the beginning of the app
-When PIN is configuren it should not be  showed at the beginning of security settings app
2018-10-14 02:24:46 -05:00
dtvv 013a3b841f -When the PIN is being entered the user should press a button to confirm the pin
-When the PIN is being entered use question dialog to confirm the save PIN
-When the PIN is correcto typed and match correctly enable the ok button
2018-10-14 01:57:41 -05:00
dtvv 3d9d57d0fa -Change the style of the window back up brainkey as in palmpay style
-Show the buttons centered in back up brainkey window
2018-10-14 01:14:15 -05:00
dtvv f705de8c60 -Add the new buttons push animation to the buttons in the baclup_seed.xml and java source
-Test the code so it works as before after changes
-Change the text from cancel to CLOSE in the BackUpSeedActivity
2018-10-12 14:47:12 -05:00
dtvv d3a55fae02 Change the buttons style to the buttons to material one in the backup_seed.xml and java source 2018-10-12 14:26:17 -05:00
dtvv ecfe5ce8e5 Apply a gradient in activity_accounts.xml background 2018-10-12 14:16:12 -05:00
dtvv fe5603c0b7 Change the text color in the activity_pocket_request.xml to white for contrast 2018-10-12 14:10:27 -05:00
dtvv 332eced879 Apply a better view on enter pin screen activity_pocket_request.xml 2018-10-12 14:08:03 -05:00
dtvv 19aa8e6b7f Apply a better view on enter pin screen activity_pin_request.xml 2018-10-12 14:07:05 -05:00
dtvv 6606e8c238 Apply a better view on the pattern screen activity_pattern_request.xml 2018-10-12 14:01:15 -05:00
dtvv f403ee22d4 Create the new gradiente file for the screens of secirity 2018-10-12 14:00:55 -05:00
dtvv 9a75330207 Change the strings text for wirte_external_permissions and set them as android string resource 2018-10-12 12:37:23 -05:00
dtvv 2e13b16a71 If there is only one account in the screen of send assets that the spinner stay Blocked 2018-10-12 12:15:42 -05:00
dtvv 3019cc8422 -Change the buttons style to the buttons to material one in the fragment_backup_settings.xml and java source
-Add the new buttons push animation to the buttons in the fragment_backup_settings.xml and java source
2018-10-12 04:51:54 -05:00
dtvv c30fd729be In the fragment_contacts when it says “You dont have contacts” apply more left margin 2018-10-12 04:43:54 -05:00
dtvv b33bbc11fc -Change the buttons style to the buttons to material one in the fragment_general_settings.xml and java source
-Add the new buttons push animation to the buttons in the fragment_general_settings.xml and java source
2018-10-12 04:41:23 -05:00
dtvv ad65111f92 -Change the buttons style to the buttons to material one in the fragment_bitshares_settings.xml and java source
-Add the new buttons push animation to the buttons in the fragment_bitshares_settings.xml and java source
2018-10-12 04:34:17 -05:00
dtvv a64c5eff45 Create new button style WhiteButton 2018-10-12 04:29:03 -05:00
dtvv 9020608a07 -Change the buttons style to the buttons to material one in the fragment_general_crypto_net_account_settings.xml and java source
-Add the new buttons push animation to the buttons in the fragment_general_crypto_net_account_settings.xml and java source
2018-10-12 04:22:28 -05:00
dtvv 046e21d725 -Include layout in the buttons to fix orientación and correct size with the new properties 2018-10-12 04:08:44 -05:00
dtvv b4cc708970 -Change the buttons style to the buttons to material one in the import_seed.xml and java source
-Add the new buttons push animation to the buttons in the import_seed.xml and java source
-Include layout in the buttons to fix orientación and correct size with the new properties
2018-10-12 03:41:36 -05:00
dtvv 012aea807e -Add the new buttons push animation to the buttons in the activity_intro.xml and java source 2018-10-12 03:09:28 -05:00
dtvv 074e37076d -Add the new buttons push animation to the buttons in the activity_licence.xml and java source 2018-10-12 03:04:07 -05:00
dtvv 24dba53c5f -Change the buttons style to the buttons to material one in the create_seed.xml and java source
-Add the new buttons push animation to the buttons in the fragment_accounts_settings.xml and java source
2018-10-12 02:55:09 -05:00
dtvv 09ce271c30 -Change the buttons style to the buttons to material one in the fragment_accounts_settings.xml
-Add the new buttons push animation to the buttons in the fragment_accounts_settings.xml
2018-10-12 02:34:15 -05:00
dtvv 0928a5bc81 Add the new buttons push animation to the buttons in the fragment_import_account_options.xml 2018-10-11 18:01:50 -05:00
dtvv bd6a27f15b Push buttons effect 2018-10-11 17:45:57 -05:00
dtvv df04be0a6c -Change the buttons style to the buttons to material one in the create_seed.xml
-Change the buttons style to the buttons to material one in the fragment_import_account_options.xml
-Change the buttons style to the buttons to material one in the activity_accounts.xml
-Change the buttons style to the buttons to material one in the fragment_backup_settings.xml
-Change the buttons style to the buttons to material one in the fragment_account_settings.xml
-Change the buttons style to the buttons to material one in the fragment_general_settings.xml
2018-10-11 15:13:50 -05:00
Javier Varona 4fee5de10b Merge branch 'develop' of https://github.com/Agorise/crystal-wallet-android into develop 2018-10-10 21:58:06 -04:00
Javier Varona 62edb37143 - Changing main thread db queries to be executed in a room observer way 2018-10-10 21:57:40 -04:00
Henry Varona 3767483945 version and camera 2018-10-08 11:21:35 -04:00
Henry Varona 7183c0d803 Merge branch 'develop' of https://github.com/Agorise/crystal-wallet-android into develop 2018-10-08 08:43:09 -04:00
hvarona f82b90e0ee change zxing properties 2018-10-07 23:20:59 -04:00
Henry Varona 64866632d2 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	app/build.gradle
2018-10-07 20:41:01 -04:00
Henry Varona 493b00c4f8 version and faucet changes 2018-10-07 20:37:45 -04:00
Javier Varona 303011e419 - Fixed QR scanner in send fragment not triggering when the camera is focusing on a QR code
- Gradle version upgraded to work with the last android studio
2018-10-07 20:02:56 -04:00
dtvv be679e655f -Get better the algorithm for the camera permission in the send assets screen validation permission granted and not granted
-If the camera permission is not granted the camera should no be enabled and can not be enabled manually
-When the permission of the camera in the send assets window is not granted it should Show a toast message
-When the permission of the camera in the send assets window is granted it should Show a toast message
2018-10-07 01:28:35 -05:00
dtvv d788ceab9a The QR camera should stop and restart manually if the user need it 2018-10-07 00:22:10 -05:00
dtvv 85b1015e9f In the screen of send assets, when you Press the cross button to stop the camera for the qr actually that button does not work, but now it does, stop the camera and clear the animation 2018-10-06 23:54:53 -05:00
dtvv cf4b1f038d Change the background color of the pattern in the security settings menu to other more visible 2018-10-05 03:38:36 -05:00
dtvv a4819ecd22 When user sets a PIN and try to enter to settings after that and crystal is asking for the PIN and the user enter it correctly, the screen does not disapear 2018-10-05 03:22:25 -05:00
dtvv ab9df5d786 Give more space in the text “Enter new pattern” and the upper bar in the security settings menu 2018-10-05 03:15:11 -05:00
dtvv 9fa7ddf178 Give more space between the PIN code and the upper menu in the security settings menu 2018-10-05 03:14:47 -05:00
dtvv 96feaf31cd Give more space between the PIN code and the upper menu in the security settings menu 2018-10-05 03:02:47 -05:00
dtvv 62e205ccfe Add copy button in the fragment_general_crypto_net_account_settings.xml screen 2018-10-05 02:47:06 -05:00
dtvv 53066696ce Get better view for the fragment_general_crypto_net_account_settings.xml window fragment 2018-10-05 02:38:39 -05:00
hvarona 86c16bbb7b Change classes name to be more precised what they do
Change Insight Api Generator to use Manager and simplifier the code
Added check for the crypto coin of the cryptocoin request
Change CryptoNet to CryptoCoin, all the insight cryptos use only one coin.
Added Queries to search and insert Bitcoin Transaction
2018-10-04 23:59:32 -04:00
dtvv b55d22324a Limit the seed max text in the import seed window to 255 2018-10-04 15:35:06 -05:00
dtvv a54363a9ef When try to import the account, it should show a loading window while the user is waiting for it 2018-10-04 15:25:07 -05:00
dtvv 1545f0e10b When you try to import your account from seed, the button “CREATE WALLET” should be disabled till all the fields are correctly filled 2018-10-04 10:15:58 -05:00
dtvv 09ae09db1b Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	gradle/wrapper/gradle-wrapper.properties
2018-10-04 09:12:56 -05:00
dtvv 8b4fb2d69d Change gradle version 2018-10-04 09:12:07 -05:00
Javier Varona 6a5ac7cc58 Merge branch 'develop' of https://github.com/Agorise/crystal-wallet-android into develop 2018-10-03 21:57:44 -04:00
Javier Varona f43c6aa1af - Created the database structure for bitcoins alike coins
- Fixed "no balances" label appearing in the middle of the balance page when the user has one account.
2018-10-03 21:56:38 -04:00
dtvv 546db50590 In the AccountSettings change the color text more clear 2018-10-02 10:33:02 -05:00
dtvv a21446bc7f In the AccountSettings the text “Account Settings” in the crypto_net_account_activity_settings layout it is not propertly in resources file 2018-10-02 09:46:01 -05:00
dtvv c1b8c11d74 Realease the resource MediaPlayer used in the CryptoNetAccountSettingsActivity, it was not been released propertly 2018-10-02 09:17:19 -05:00
dtvv cc69c2676e Realease the resource MediaPlayer used in the AccountsSettingsActivity, it was not been released propertly 2018-10-02 09:16:13 -05:00
dtvv 5ad7d32bbf Validate not null in onDestroy event for the media player 2018-10-02 08:54:38 -05:00
dtvv 05d04b69af Realease the resource MediaPlayer used in the SettingsActivity, it was not been released propertly 2018-10-02 08:53:45 -05:00
dtvv d2e388e5de Realease the resource MediaPlayer used in the IntroActivity, it was not been released propertly 2018-10-02 08:28:52 -05:00
dtvv fbfa97754f Realease the resource MediaPlayer used in the BoardActivity, it was not been released propertly 2018-10-02 08:16:36 -05:00
dtvv 94267c6cc3 Simple string added to catalog 2018-09-30 00:14:48 -05:00
dtvv 751cb46922 Remove padding in the window 2018-09-30 00:13:57 -05:00
dtvv 1cc21c73be WIRTE_EXTERNAL_PERMISION at runtime 2018-09-30 00:12:40 -05:00
dtvv 6b39722f89 Camera permission was not included in the manifest 2018-09-29 22:55:48 -05:00
dtvv d86fe35a6b When you create a new account the cancel button does not have to show, only when you check the brainkey 2018-09-29 22:21:15 -05:00
dtvv ad0c14e389 Prevent null pointer exception 2018-09-29 20:51:47 -05:00
hvarona e9a147e344 Merge remote-tracking branch 'origin/develop' into develop 2018-09-29 20:52:26 -04:00
hvarona 31f809048f Updated Faucet 2018-09-29 20:51:59 -04:00
dtvv 0fc4392a52 Adjust kotlin puglin version to compile proyect 2 2018-09-29 16:00:13 -05:00
dtvv 17c61b12a7 Adjust kotlin puglin version to compile proyect 2018-09-29 15:46:35 -05:00
hvarona b1e8e4dfe7 Merge remote-tracking branch 'origin/develop' into develop 2018-09-28 21:40:13 -04:00
hvarona 14c307ccbc Merge branch 'develop' 2018-09-27 22:34:40 -04:00
hvarona 7db8212ab4 Change Insight api classes to handle multiple address and to adapt to the new architecture.
Added Bitshares Account Manager to handle all txi response
TODOs: Add cache data to handle incoming transaction and balances
2018-09-27 22:19:19 -04:00
Javier Varona 6eab51c89f - Fixed string "infinity" in equivalent totals when the equivalence value of a currency is 0
- Added equivalent currency name in the cryptonet equivalent totals
2018-09-26 22:45:09 -04:00
Javier Varona d16f2d7378 Merge branch 'develop' of https://github.com/Agorise/crystal-wallet-android into develop 2018-09-25 23:01:12 -04:00
Javier Varona 037787afd0 - Added loading dialog when sending.
- Now the send dialog closes after sending the funds successfully
2018-09-24 22:10:43 -04:00
dtvv 0c583fff18 WRITE_EXTERNAL_STORAGE at runtime in the backup bin file button 2018-09-24 20:38:44 -05:00
dtvv dae073dc62 Fix code to get better performance in validations 2018-09-24 19:41:33 -05:00
dtvv 2e6c61f88a UI adjustments 2018-09-24 15:16:14 -05:00
dtvv 2c5661c587 Back to previous version of the send transaction button 2018-09-24 15:16:14 -05:00
Javier Varona 53cf2ef25b - Fixing transaction duplication when importing an account. 2018-09-23 20:01:45 -04:00
184 changed files with 7638 additions and 2577 deletions

View File

@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt' // add this line
kapt {
generateStubs = true
@ -9,14 +11,19 @@ kapt {
}
}
repositories {
mavenCentral()
maven { url 'https://maven.google.com' }
}
android {
compileSdkVersion 27
compileSdkVersion 28
defaultConfig {
applicationId "cy.agorise.crystalwallet"
minSdkVersion 21
targetSdkVersion 27
versionCode 3
versionName "0.3M.alpha"
targetSdkVersion 28
versionCode 5
versionName "0.5M.alpha"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
@ -28,6 +35,9 @@ android {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
resValue("string", "PORT_NUMBER", "8081")
}
}
productFlavors {
@ -52,51 +62,44 @@ dependencies {
androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.1', {
exclude group: 'com.android.support', module: 'support-annotations'
})
implementation( 'com.github.thekhaeng:pushdown-anim-click:1.1.1' ){
exclude group: 'com.android.support'
}
implementation 'com.jaredrummler:material-spinner:1.2.5'
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.1.60"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
//testCompile 'com.android.support.test:runner:1.0.1'
implementation 'com.afollestad.material-dialogs:core:0.9.6.0' //DTVV Thrusday 31 July 2018
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.github.bumptech.glide:glide:4.7.1'
// Glide v4 uses this new annotation processor -- see https://bumptech.github.io/glide/doc/generatedapi.html
annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'
implementation 'com.android.support:support-v4:27.1.1'
implementation 'com.android.support:design:27.1.1'
implementation 'com.android.support:cardview-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.afollestad.material-dialogs:core:0.9.6.0'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:cardview-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'android.arch.lifecycle:runtime:1.1.1'
implementation 'android.arch.lifecycle:extensions:1.1.1'
implementation 'android.arch.paging:runtime:1.0.0'
implementation 'android.arch.paging:runtime:1.0.1'
implementation 'com.idescout.sql:sqlscout-server:2.0'
implementation 'com.google.code.gson:gson:2.8.0'
implementation 'com.google.code.gson:gson:2.8.4'
implementation 'com.squareup.retrofit2:retrofit:2.2.0'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
implementation 'org.bitcoinj:bitcoinj-core:0.14.3'
implementation 'com.neovisionaries:nv-websocket-client:1.30'
implementation 'org.tukaani:xz:1.6'
implementation 'com.jakewharton:butterknife:8.8.1'
implementation 'com.github.bilthon:graphenej:0.4.6'
implementation 'com.google.zxing:core:3.3.1'
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation 'com.github.sjaramillo10:AnimatedTabLayout:1.0.3'
implementation 'com.squareup.okhttp3:logging-interceptor:3.5.0'
implementation 'de.hdodenhof:circleimageview:2.2.0'
//testCompile 'junit:junit: 4.12'
testImplementation 'org.mockito:mockito-core:1.10.19'
implementation 'android.arch.persistence.room:runtime:1.1.0'
testImplementation 'org.mockito:mockito-core:2.19.0'
implementation 'android.arch.persistence.room:runtime:1.1.1'
kapt 'android.arch.persistence.room:runtime:1.1.1'
kapt 'android.arch.persistence.room:compiler:1.1.1'
kapt 'android.arch.persistence.room:runtime:1.1.0'
annotationProcessor 'android.arch.lifecycle:compiler:1.1.1'
kapt 'android.arch.lifecycle:compiler:1.1.1'
annotationProcessor 'android.arch.lifecycle:compiler:1.1.1'
kapt 'android.arch.persistence.room:compiler:1.1.0'
annotationProcessor 'android.arch.persistence.room:compiler:1.1.0'
kapt 'com.jakewharton:butterknife-compiler:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.github.esafirm.android-image-picker:imagepicker:1.11.1'
implementation 'id.zelory:compressor:2.1.0'
implementation 'com.vincent.filepicker:MultiTypeFilePicker:1.0.7'
@ -108,6 +111,14 @@ dependencies {
exclude group: 'org.json', module: 'json'
}
kapt "android.arch.lifecycle:compiler:1.1.1"
kapt "android.arch.persistence.room:compiler:1.1.0"
// Glide dependencies
implementation 'com.github.bumptech.glide:glide:4.8.0'
kapt 'com.github.bumptech.glide:compiler:4.8.0'
// Android Debug Database
debugImplementation 'com.amitshekhar.android:debug-db:1.0.4'
implementation 'com.google.zxing:core:3.3.1'
implementation 'com.journeyapps:zxing-android-embedded:3.2.0@aar'
testImplementation 'org.testng:testng:6.9.6'
}

View File

@ -23,3 +23,10 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
**[] $VALUES;
public *;
}

View File

@ -7,6 +7,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.CAMERA" />
<application
android:name=".application.CrystalApplication"
@ -39,6 +40,10 @@
</activity>
<activity android:name=".activities.AccountSeedsManagementActivity" >
</activity>
<activity android:name=".activities.AccountSeedSettingsActivity"
android:theme="@style/AppTheme.NoActionBar"
android:windowSoftInputMode="adjustPan">
</activity>
<activity android:name=".activities.ImportSeedActivity" >
</activity>
<activity android:name=".activities.SendTransactionActivity" >

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,134 @@
package cy.agorise.crystalwallet.activities;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.fragments.BitsharesSettingsFragment;
import cy.agorise.crystalwallet.fragments.GeneralAccountSeedCoinSettingsFragment;
import cy.agorise.crystalwallet.fragments.GeneralAccountSeedFragment;
import cy.agorise.crystalwallet.fragments.GeneralCryptoNetAccountSettingsFragment;
import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.CryptoNetAccount;
import cy.agorise.crystalwallet.viewmodels.AccountSeedViewModel;
import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountViewModel;
/**
* Created by henry varona on 10/29/18.
*
*/
public class AccountSeedSettingsActivity extends AppCompatActivity{
@BindView(R.id.ivGoBack)
public ImageView ivGoBack;
@BindView(R.id.pager)
public ViewPager mPager;
public SettingsPagerAdapter settingsPagerAdapter;
@BindView(R.id.tvBuildVersion)
public TextView tvBuildVersion;
@BindView(R.id.tabs)
public TabLayout tabs;
private AccountSeed accountSeed;
@BindView(R.id.ivAppBarAnimation)
ImageView ivAppBarAnimation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.account_seed_activity_settings);
ButterKnife.bind(this);
final AccountSeedSettingsActivity thisActivity = this;
long accountSeedId = getIntent().getLongExtra("SEED_ID",-1);
if (accountSeedId > -1) {
AccountSeedViewModel accountSeedViewModel = ViewModelProviders.of(this).get(AccountSeedViewModel.class);
accountSeedViewModel.loadSeed(accountSeedId);
LiveData<AccountSeed> accountSeedLiveData = accountSeedViewModel.getAccountSeed();
accountSeedLiveData.observe(this, new Observer<AccountSeed>() {
@Override
public void onChanged(@Nullable AccountSeed accountSeed) {
thisActivity.accountSeed = accountSeed;
settingsPagerAdapter = new SettingsPagerAdapter(getSupportFragmentManager());
mPager.setAdapter(settingsPagerAdapter);
TabLayout tabLayout = findViewById(R.id.tabs);
mPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mPager));
}
});
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Sets AppBar animation
Glide.with(this)
.load(R.drawable.appbar_background)
.apply(new RequestOptions().centerCrop())
.into(ivAppBarAnimation);
} else {
this.finish();
}
}
private class SettingsPagerAdapter extends FragmentStatePagerAdapter {
SettingsPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
switch (position){
case 0:
return GeneralAccountSeedFragment.newInstance(accountSeed.getId());
case 1:
return GeneralAccountSeedCoinSettingsFragment.newInstance(accountSeed.getId());
}
return null;
}
@Override
public int getCount() {
int tabCount = 2;
return tabCount;
}
}
@OnClick(R.id.ivGoBack)
public void goBack(){
onBackPressed();
}
}

View File

@ -14,6 +14,9 @@ import android.view.SurfaceView;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
@ -37,12 +40,13 @@ public class AccountSettingsActivity extends AppCompatActivity{
public SettingsPagerAdapter settingsPagerAdapter;
@BindView(R.id.surface_view)
public SurfaceView mSurfaceView;
@BindView(R.id.tvBuildVersion)
public TextView tvBuildVersion;
@BindView(R.id.ivAppBarAnimation)
ImageView ivAppBarAnimation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -52,27 +56,11 @@ public class AccountSettingsActivity extends AppCompatActivity{
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Appbar animation
mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
//Log.d(TAG,"surfaceCreated");
MediaPlayer mediaPlayer = MediaPlayer.create(AccountSettingsActivity.this, R.raw.appbar_background);
mediaPlayer.setDisplay(mSurfaceView.getHolder());
mediaPlayer.setLooping(true);
mediaPlayer.start();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
//Log.d(TAG,"surfaceChanged");
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
//Log.d(TAG,"surfaceDestroyed");
}
});
// Sets AppBar animation
Glide.with(this)
.load(R.drawable.appbar_background)
.apply(new RequestOptions().centerCrop())
.into(ivAppBarAnimation);
settingsPagerAdapter = new SettingsPagerAdapter(getSupportFragmentManager());
mPager.setAdapter(settingsPagerAdapter);

View File

@ -18,10 +18,12 @@ import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
import com.esafirm.imagepicker.features.ImagePicker;
import com.esafirm.imagepicker.model.Image;
import com.thekhaeng.pushdownanim.PushDownAnim;
//import com.nicdahlquist.pngquant.LibPngQuant;
import java.io.File;
@ -55,8 +57,11 @@ public class AccountsActivity extends AppCompatActivity {
@BindView(R.id.tvClose)
TextView tvClose;
@BindView(R.id.vAccountList)
CryptoNetAccountListView vAccountList;
//@BindView(R.id.vAccountList)
//CryptoNetAccountListView vAccountList;
@BindView(R.id.vAccountSeedList)
AccountSeedListView vAccountSeedList;
@BindView(R.id.user_img)
CircleImageView userImg;
@ -74,14 +79,29 @@ public class AccountsActivity extends AppCompatActivity {
setContentView(R.layout.activity_accounts);
ButterKnife.bind(this);
CryptoNetAccountListViewModel crytpoNetAccountListViewModel = ViewModelProviders.of(this).get(CryptoNetAccountListViewModel.class);
LiveData<List<CryptoNetAccount>> accountData = crytpoNetAccountListViewModel.getCryptoNetAccounts();
vAccountList.setData(null);
/*
* Integration of library with button efects
* */
PushDownAnim.setPushDownAnimTo(fabAddAccount)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
goToAddAccount();
}
accountData.observe(this, new Observer<List<CryptoNetAccount>>() {
} );
AccountSeedListViewModel accountSeedListViewModel = ViewModelProviders.of(this).get(AccountSeedListViewModel.class);
LiveData<List<AccountSeed>> accountSeedLD = accountSeedListViewModel.getAccountSeedList();
//CryptoNetAccountListViewModel crytpoNetAccountListViewModel = ViewModelProviders.of(this).get(CryptoNetAccountListViewModel.class);
//LiveData<List<CryptoNetAccount>> accountData = crytpoNetAccountListViewModel.getCryptoNetAccounts();
vAccountSeedList.setData(null);
accountSeedLD.observe(this, new Observer<List<AccountSeed>>() {
@Override
public void onChanged(List<CryptoNetAccount> cryptoNetAccounts) {
vAccountList.setData(cryptoNetAccounts);
public void onChanged(List<AccountSeed> accountsSeeds) {
vAccountSeedList.setData(accountsSeeds);
}
});

View File

@ -10,10 +10,14 @@ import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.thekhaeng.pushdownanim.PushDownAnim;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
@ -29,10 +33,12 @@ public class BackupSeedActivity extends AppCompatActivity {
@BindView(R.id.tvBrainKey)
TextView textfieldBrainkey;
@BindView(R.id.btnCancel)
@BindView(R.id.btnOK)
Button btnOk;
@BindView(R.id.btnCopy)
Button btnCopy;
@BindView(R.id.btnCancel)
Button btnCancel;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -41,6 +47,42 @@ public class BackupSeedActivity extends AppCompatActivity {
ButterKnife.bind(this);
/*
* Integration of library with button efects
* */
PushDownAnim.setPushDownAnimTo(btnCancel)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
btnCancelClick();
}
} );
PushDownAnim.setPushDownAnimTo(btnOk)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
btnOkClick();
}
} );
PushDownAnim.setPushDownAnimTo(btnCopy)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
btnCopyClick();
}
} );
/*
* If comes from new account creation hide the cancel button
* */
Bundle b = getIntent().getExtras();
boolean newAccount = b.getBoolean("newAccount");
if(newAccount ){
ViewGroup layout = (ViewGroup) btnOk.getParent();
if(null!=layout) //for safety only as you are doing onClick
layout.removeView(btnOk);
//btnOk.setVisibility(View.INVISIBLE);
}
long seedId = getIntent().getLongExtra("SEED_ID",-1);
if (seedId > -1) {
@ -56,6 +98,14 @@ public class BackupSeedActivity extends AppCompatActivity {
accountSeedViewModel.loadSeed(seedId);
} else {
/*
*
* The first time you create the account, the "seed" is showed propertly in this window,
* but when you want to check it again the "seed" does not exist anymore and
* for this cause the program gets into this point and finish the window
*
* */
finish();
}
}

View File

@ -8,9 +8,6 @@ import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Typeface;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.TabLayout;
@ -20,18 +17,10 @@ import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.Toolbar;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.util.Pair;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
@ -48,7 +37,6 @@ import cy.agorise.crystalwallet.fragments.MerchantsFragment;
import cy.agorise.crystalwallet.fragments.ReceiveTransactionFragment;
import cy.agorise.crystalwallet.fragments.SendTransactionFragment;
import cy.agorise.crystalwallet.fragments.TransactionsFragment;
import cy.agorise.crystalwallet.views.natives.GIFView;
import de.hdodenhof.circleimageview.CircleImageView;
import cy.agorise.crystalwallet.viewmodels.CryptoNetBalanceListViewModel;
@ -73,8 +61,8 @@ public class BoardActivity extends CustomActivity {
@BindView(R.id.fabAddContact)
public FloatingActionButton fabAddContact;
@BindView(R.id.imagevieGIF)
public GIFView imagevieGIF;
@BindView(R.id.ivAppBarAnimation)
ImageView ivAppBarAnimation;
public BoardPagerAdapter boardAdapter;
@ -84,9 +72,6 @@ public class BoardActivity extends CustomActivity {
*/
long cryptoNetAccountId;
@BindView(R.id.surface_view)
public SurfaceView mSurfaceView;
@BindView(R.id.toolbar_user_img)
public CircleImageView userImage;
@ -110,82 +95,11 @@ public class BoardActivity extends CustomActivity {
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
/*
* Set the bubbles animation
* */
//imagevieGIF.centerCrop();
//imagevieGIF.load(R.raw.burbujas);
/*
* Listener tabLayout to resalt text when clicked
* */
final TabLayout tabLayoutFinal = tabLayout;
// tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
// @Override
// public void onTabSelected(final TabLayout.Tab tab) {
//
// globalActivity.runOnUiThread(new Runnable() {
// @Override
// public void run() {
//
// LinearLayout tabLayout = (LinearLayout)((ViewGroup) tabLayoutFinal.getChildAt(0)).getChildAt(tab.getPosition());
// tabLayout.setBackgroundColor(Color.TRANSPARENT);
// TextView tabTextView = (TextView) tabLayout.getChildAt(1);
// //tabTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP ,50);
// Spannable WordtoSpan = new SpannableString(tabTextView.getText());
// WordtoSpan.setSpan(new ForegroundColorSpan(Color.WHITE), 0, tabTextView.getText().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
// tabTextView.setText(WordtoSpan);
// tabTextView.setTypeface(tabTextView.getTypeface(), Typeface.BOLD);
// }
// });
// }
//
// @Override
// public void onTabUnselected(final TabLayout.Tab tab) {
//
// globalActivity.runOnUiThread(new Runnable() {
// @Override
// public void run() {
//
// LinearLayout tabLayout = (LinearLayout)((ViewGroup) tabLayoutFinal.getChildAt(0)).getChildAt(tab.getPosition());
// TextView tabTextView = (TextView) tabLayout.getChildAt(1);
// //tabTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP ,50);
// Spannable WordtoSpan = new SpannableString(tabTextView.getText());
// WordtoSpan.setSpan(new ForegroundColorSpan(globalActivity.getResources().getColor(R.color.lightGrayClear)), 0, tabTextView.getText().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
// tabTextView.setText(WordtoSpan);
// tabTextView.setTypeface(tabTextView.getTypeface(), Typeface.NORMAL);
// }
// });
// }
//
// @Override
// public void onTabReselected(TabLayout.Tab tab) {
//
//
// }
// });
// Appbar animation
mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
//Log.d(TAG,"surfaceCreated");
MediaPlayer mediaPlayer = MediaPlayer.create(BoardActivity.this, R.raw.appbar_background);
mediaPlayer.setDisplay(mSurfaceView.getHolder());
mediaPlayer.setLooping(true);
mediaPlayer.start();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
//Log.d(TAG,"surfaceChanged");
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
//Log.d(TAG,"surfaceDestroyed");
}
});
// Sets AppBar animation
Glide.with(this)
.load(R.drawable.appbar_background)
.apply(new RequestOptions().centerCrop())
.into(ivAppBarAnimation);
boardAdapter = new BoardPagerAdapter(getSupportFragmentManager());
mPager.setAdapter(boardAdapter);

View File

@ -3,18 +3,17 @@ package cy.agorise.crystalwallet.activities
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.support.design.widget.TextInputEditText
import android.text.Editable
import android.text.TextWatcher
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import butterknife.ButterKnife
import butterknife.OnClick
import butterknife.OnTextChanged
import com.thekhaeng.pushdownanim.PushDownAnim
import com.vincent.filepicker.ToastUtil
import cy.agorise.crystalwallet.R
import cy.agorise.crystalwallet.dialogs.material.*
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequests
import cy.agorise.crystalwallet.requestmanagers.ValidateCreateBitsharesAccountRequest
import cy.agorise.crystalwallet.requestmanagers.ValidateExistBitsharesAccountRequest
import cy.agorise.crystalwallet.viewmodels.validators.customImpl.interfaces.UIValidatorListener
import cy.agorise.crystalwallet.viewmodels.validators.customImpl.validationFields.BitsharesAccountNameValidation
import cy.agorise.crystalwallet.viewmodels.validators.customImpl.validationFields.BitsharesAccountNameValidation.OnAccountExist
@ -39,11 +38,6 @@ class CreateSeedActivity : CustomActivity() {
* */
setContentView(R.layout.create_seed)
/*
* Initialice butterknife MVC
* */
ButterKnife.bind(this)
/*
* Add the controls to the validator
* */
@ -51,6 +45,14 @@ class CreateSeedActivity : CustomActivity() {
this.fieldsValidator.add(tietPinConfirmation)
this.fieldsValidator.add(tietAccountName)
/*
* Integration of library with button effects
* */
PushDownAnim.setPushDownAnimTo(btnCancel)
.setOnClickListener { finish() }
PushDownAnim.setPushDownAnimTo(btnCreate)
.setOnClickListener { createSeed() }
/*
* Validations listener
* */
@ -87,20 +89,14 @@ class CreateSeedActivity : CustomActivity() {
}
}
/*
* Create the pin double validation
* */
//Create the pin double validation
val pinDoubleConfirmationValidationField = PinDoubleConfirmationValidationField(this, tietPin, tietPinConfirmation, uiValidatorListener)
/*
* Listener for the validation for success or fail
* */
// Listener for the validation for success or fail
tietPin?.setUiValidator(pinDoubleConfirmationValidationField) //Validator for the field
tietPinConfirmation?.setUiValidator(pinDoubleConfirmationValidationField) //Validator for the field
/*
* Account name validator
* */
// Account name validator
val bitsharesAccountNameValidation = BitsharesAccountNameValidation(this, tietAccountName, uiValidatorListener)
val onAccountExist = object : OnAccountExist {
override fun onAccountExists() {
@ -114,58 +110,49 @@ class CreateSeedActivity : CustomActivity() {
tietAccountName?.setUiValidator(bitsharesAccountNameValidation)
/*This button should not be enabled till all the fields be correctly filled*/
disableCreate()
btnCreate.isEnabled = false
/*
* Set the focus on the fisrt field and show keyboard
* */
// Set the focus on the first field and show keyboard
tilPin?.requestFocus()
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(tilPin, InputMethodManager.SHOW_IMPLICIT)
tietPin.afterTextChanged {
this.fieldsValidator.validate()
validateFieldsToContinue()
}
tietPinConfirmation.afterTextChanged {
this.fieldsValidator.validate()
validateFieldsToContinue()
}
tietAccountName.afterTextChanged {
this.fieldsValidator.validate()
validateFieldsToContinue()
}
btnCancel.setOnClickListener { finish() }
btnCreate.setOnClickListener { createSeed() }
}
@OnTextChanged(value = R.id.tietPin, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
internal fun afterPinChanged(editable: Editable) {
this.fieldsValidator.validate()
/*
* Validate continue to create account
* */
validateFieldsToContinue()
/**
* Extension function to easily add a text watcher
*/
fun TextInputEditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
this.addTextChangedListener(object :TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun afterTextChanged(editable: Editable?) {
afterTextChanged.invoke(editable.toString())
}
})
}
@OnTextChanged(value = R.id.tietPinConfirmation, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
internal fun afterPinConfirmationChanged(editable: Editable) {
this.fieldsValidator.validate()
/*
* Validate continue to create account
* */
validateFieldsToContinue()
}
@OnTextChanged(value = R.id.tietAccountName, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
internal fun afterAccountNameChanged(editable: Editable) {
this.fieldsValidator.validate()
/*
* Validate continue to create account
* */
validateFieldsToContinue()
}
@OnClick(R.id.btnCancel)
fun cancel() {
/*
* Exit of the activity
* */
this.finish()
}
@OnClick(R.id.btnCreate)
fun createSeed() {
/*
@ -181,11 +168,10 @@ class CreateSeedActivity : CustomActivity() {
questionDialog.setOnPositive(object : PositiveResponse{
override fun onPositive() {
// Make request to create a bitshare account
// Make request to create a bitshares account
var accountName:String = tietAccountName?.getText().toString().trim()
val request = ValidateCreateBitsharesAccountRequest(accountName, applicationContext)
//DTVV: Friday 27 July 2018
//Makes dialog to tell the user that the account is been created
val creatingAccountMaterialDialog = CrystalDialog(globalActivity)
creatingAccountMaterialDialog.setText(globalActivity.resources.getString(R.string.window_create_seed_DialogMessage))
@ -200,11 +186,12 @@ class CreateSeedActivity : CustomActivity() {
val intent = Intent(applicationContext, BackupSeedActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
intent.putExtra("SEED_ID", accountSeed.id)
intent.putExtra("newAccount", true)
startActivity(intent)
}
else if (request.status == ValidateCreateBitsharesAccountRequest.StatusCode.ACCOUNT_EXIST) {
ToastUtil.getInstance(globalActivity).showToast(globalActivity.getString(R.string.Account_already_exists))
disableCreate()
btnCreate.isEnabled = false
}
else {
fieldsValidator.validate()
@ -213,10 +200,7 @@ class CreateSeedActivity : CustomActivity() {
(object : Thread() {
override fun run() {
/*
*
* Run thread*/
/* Run thread*/
CryptoNetInfoRequests.getInstance().addRequest(request)
}
}).start()
@ -242,85 +226,7 @@ class CreateSeedActivity : CustomActivity() {
result = true //Validation is correct
}
/*
* If the result is true so the user can continue to the creation of the account
* */
if (result) {
/*
* Show the dialog for connection with the server
* */
val creatingAccountMaterialDialog = CrystalDialog(globalActivity)
creatingAccountMaterialDialog.setText(globalActivity.resources.getString(R.string.window_create_seed_Server_validation))
creatingAccountMaterialDialog.progress()
creatingAccountMaterialDialog.show()
/*
* Validate the account does not exists
* */
val request = ValidateExistBitsharesAccountRequest(tietAccountName?.text.toString())
request.setListener {
/*
* Dismiss the dialog of loading
* */
creatingAccountMaterialDialog.dismiss()
if (request.accountExists) {
/*
* The account exists and is not valid
* */
tietAccountName.fieldValidatorModel.setInvalid()
tietAccountName.fieldValidatorModel.message = tietAccountName.resources.getString(R.string.account_name_already_exist)
/*
* Disaible button create
* */
disableCreate()
} else {
/*
* Passed all validations
* */
tietAccountName.fieldValidatorModel.setValid()
/*
* Enable button create
* */
enableCreate()
}
}
CryptoNetInfoRequests.getInstance().addRequest(request)
} else {
/*
* Disaible button create
* */
disableCreate()
}
}
/*
* Enable create button
* */
private fun enableCreate() {
runOnUiThread(Runnable {
btnCreate?.setBackgroundColor(resources.getColor(R.color.colorPrimary))
btnCreate?.setEnabled(true)
})
}
/*
* Disable create button
* */
private fun disableCreate() {
runOnUiThread(Runnable {
btnCreate?.setEnabled(false)
btnCreate?.setBackground(resources.getDrawable(R.drawable.disable_style))
})
// If the result is true so the user can continue to the creation of the account
btnCreate.isEnabled = result
}
}

View File

@ -2,15 +2,11 @@ package cy.agorise.crystalwallet.activities;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import org.w3c.dom.Text;
import java.text.SimpleDateFormat;
import butterknife.BindView;
@ -47,7 +43,7 @@ public class CryptoCoinTransactionReceiptActivity extends AppCompatActivity {
if (this.cryptoCoinTransactionId != -1) {
db = CrystalDatabase.getAppDatabase(this);
this.cryptoCoinTransactionLiveData = db.transactionDao().getById(this.cryptoCoinTransactionId);
this.cryptoCoinTransactionLiveData = db.transactionDao().getByIdLiveData(this.cryptoCoinTransactionId);
this.cryptoCoinTransactionLiveData.observe(this, new Observer<CryptoCoinTransaction>() {
@Override

View File

@ -18,12 +18,16 @@ import android.view.SurfaceView;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.fragments.BackupsSettingsFragment;
import cy.agorise.crystalwallet.fragments.BitsharesSettingsFragment;
import cy.agorise.crystalwallet.fragments.CryptoNetAccountActivationSettingsFragment;
import cy.agorise.crystalwallet.fragments.GeneralCryptoNetAccountSettingsFragment;
import cy.agorise.crystalwallet.fragments.GeneralSettingsFragment;
import cy.agorise.crystalwallet.fragments.SecuritySettingsFragment;
@ -45,8 +49,6 @@ public class CryptoNetAccountSettingsActivity extends AppCompatActivity{
public SettingsPagerAdapter settingsPagerAdapter;
@BindView(R.id.surface_view)
public SurfaceView mSurfaceView;
@BindView(R.id.tvBuildVersion)
public TextView tvBuildVersion;
@ -56,6 +58,9 @@ public class CryptoNetAccountSettingsActivity extends AppCompatActivity{
private CryptoNetAccount cryptoNetAccount;
@BindView(R.id.ivAppBarAnimation)
ImageView ivAppBarAnimation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -93,27 +98,11 @@ public class CryptoNetAccountSettingsActivity extends AppCompatActivity{
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Appbar animation
mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
//Log.d(TAG,"surfaceCreated");
MediaPlayer mediaPlayer = MediaPlayer.create(CryptoNetAccountSettingsActivity.this, R.raw.appbar_background);
mediaPlayer.setDisplay(mSurfaceView.getHolder());
mediaPlayer.setLooping(true);
mediaPlayer.start();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
//Log.d(TAG,"surfaceChanged");
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
//Log.d(TAG,"surfaceDestroyed");
}
});
// Sets AppBar animation
Glide.with(this)
.load(R.drawable.appbar_background)
.apply(new RequestOptions().centerCrop())
.into(ivAppBarAnimation);
} else {

View File

@ -1,34 +1,35 @@
package cy.agorise.crystalwallet.activities;
import android.app.Activity;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.thekhaeng.pushdownanim.PushDownAnim;
import org.jetbrains.annotations.NotNull;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnTextChanged;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.enums.SeedType;
import cy.agorise.crystalwallet.manager.BitsharesAccountManager;
import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.CryptoNetAccount;
import cy.agorise.crystalwallet.models.GrapheneAccount;
import cy.agorise.crystalwallet.models.GrapheneAccountInfo;
import cy.agorise.crystalwallet.dialogs.material.CrystalLoading;
import cy.agorise.crystalwallet.dialogs.material.DialogMaterial;
import cy.agorise.crystalwallet.dialogs.material.NegativeResponse;
import cy.agorise.crystalwallet.dialogs.material.PositiveResponse;
import cy.agorise.crystalwallet.dialogs.material.QuestionDialog;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestListener;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequests;
import cy.agorise.crystalwallet.requestmanagers.ImportBackupRequest;
import cy.agorise.crystalwallet.requestmanagers.ValidateImportBitsharesAccountRequest;
import cy.agorise.crystalwallet.requestmanagers.ImportBitsharesAccountRequest;
import cy.agorise.crystalwallet.viewmodels.AccountSeedViewModel;
import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountViewModel;
import cy.agorise.crystalwallet.viewmodels.GrapheneAccountInfoViewModel;
import cy.agorise.crystalwallet.viewmodels.validators.ImportSeedValidator;
import cy.agorise.crystalwallet.viewmodels.validators.UIValidatorListener;
import cy.agorise.crystalwallet.viewmodels.validators.validationfields.ValidationField;
@ -40,23 +41,30 @@ public class ImportSeedActivity extends AppCompatActivity implements UIValidator
@BindView(R.id.etPin)
EditText etPin;
@BindView(R.id.tvPinError)
TextView tvPinError;
@BindView(R.id.txtErrorPIN)
TextView txtErrorPIN;
@BindView(R.id.txtErrorAccount)
TextView txtErrorAccount;
//@BindView(R.id.tvPinError)
//TextView tvPinError;
@BindView(R.id.etPinConfirmation)
EditText etPinConfirmation;
@BindView(R.id.tvPinConfirmationError)
TextView tvPinConfirmationError;
//@BindView(R.id.tvPinConfirmationError)
//TextView tvPinConfirmationError;
@BindView(R.id.etSeedWords)
EditText etSeedWords;
@BindView(R.id.tvSeedWordsError)
TextView tvSeedWordsError;
//@BindView(R.id.tvSeedWordsError)
//TextView tvSeedWordsError;
@BindView (R.id.etAccountName)
EditText etAccountName;
@BindView(R.id.tvAccountNameError)
TextView tvAccountNameError;
//@BindView(R.id.tvAccountNameError)
//TextView tvAccountNameError;
@BindView(R.id.btnImport)
Button btnImport;
@ -64,6 +72,16 @@ public class ImportSeedActivity extends AppCompatActivity implements UIValidator
@BindView(R.id.btnCancel)
Button btnCancel;
final Activity activity = this;
/*
* Flag to check correct PIN equality
* */
private boolean pinsOK = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -71,12 +89,213 @@ public class ImportSeedActivity extends AppCompatActivity implements UIValidator
ButterKnife.bind(this);
btnImport.setEnabled(false);
/*
* Integration of library with button efects
* */
PushDownAnim.setPushDownAnimTo(btnCancel)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
cancel();
}
} );
PushDownAnim.setPushDownAnimTo(btnImport)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
importSeed();
}
} );
/*
* Initially the button CREATE WALLET should be disabled
* */
disableCreate();
/*
* When a text change in any of the fields
* */
etPin.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
/*
* Validate that PINs are equals
* */
validatePINS();
/*
* If all is ready to continue enable the button, contrarie case disable it
* */
if(allFieldsAreOK()){
enableCreate();
}
else{
disableCreate();
}
}
});
etPinConfirmation.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
/*
* Validate that PINs are equals
* */
validatePINS();
/*
* If all is ready to continue enable the button, contrarie case disable it
* */
if(allFieldsAreOK()){
enableCreate();
}
else{
disableCreate();
}
}
});
etSeedWords.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
/*
* Validate that PINs are equals
* */
validatePINS();
/*
* If all is ready to continue enable the button, contrarie case disable it
* */
if(allFieldsAreOK()){
enableCreate();
}
else{
disableCreate();
}
/*
* Hide error field
* */
clearErrors();
}
});
/*
etAccountName.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
//
// Validate that PINs are equals
//
validatePINS();
//
// If all is ready to continue enable the button, contrarie case disable it
//
if(allFieldsAreOK()){
enableCreate();
}
else{
disableCreate();
}
}
});
*/
accountSeedViewModel = ViewModelProviders.of(this).get(AccountSeedViewModel.class);
importSeedValidator = new ImportSeedValidator(this.getApplicationContext(),etPin,etPinConfirmation,etAccountName,etSeedWords);
importSeedValidator.setListener(this);
}
private void clearErrors(){
txtErrorPIN.setVisibility(View.INVISIBLE);
txtErrorAccount.setVisibility(View.INVISIBLE);
}
/*
* Validate that PINs are equals
* */
private void validatePINS(){
final String pin = etPin.getText().toString().trim();
final String confirmoPIN = etPinConfirmation.getText().toString().trim();
if(!pin.isEmpty() && !confirmoPIN.isEmpty()){
if(pin.compareTo(confirmoPIN)!=0){
pinsOK = false;
txtErrorPIN.setVisibility(View.VISIBLE);
}
else{
pinsOK = true;
clearErrors();
}
}
else{
pinsOK = false;
clearErrors();
}
}
/*
* Method to validate if all the fields are fill and correctly
* */
private boolean allFieldsAreOK(){
boolean complete = false;
if( etPin.getText().toString().trim().compareTo("")!=0 &&
etPinConfirmation.getText().toString().trim().compareTo("")!=0 &&
etSeedWords.getText().toString().trim().compareTo("")!=0 /*&&
etAccountName.getText().toString().trim().compareTo("")!=0*/){
if(pinsOK){
complete = true;
}
}
return complete;
}
@OnTextChanged(value = R.id.etPin,
callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
void afterPinChanged(Editable editable) {
@ -94,13 +313,11 @@ public class ImportSeedActivity extends AppCompatActivity implements UIValidator
void afterSeedWordsChanged(Editable editable) {
this.importSeedValidator.validate();
}
@OnTextChanged(value = R.id.etAccountName,
/*@OnTextChanged(value = R.id.etAccountName,
callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
void afterAccountNameChanged(Editable editable) {
this.importSeedValidator.validate();
}
}*/
@OnClick(R.id.btnCancel)
public void cancel(){
@ -113,68 +330,144 @@ public class ImportSeedActivity extends AppCompatActivity implements UIValidator
if (this.importSeedValidator.isValid()) {
final ValidateImportBitsharesAccountRequest validatorRequest =
new ValidateImportBitsharesAccountRequest(etAccountName.getText().toString(), etSeedWords.getText().toString(), getApplicationContext(), true);
validatorRequest.setListener(new CryptoNetInfoRequestListener() {
/*
* Question if continue
* */
final QuestionDialog questionDialog = new QuestionDialog(activity);
questionDialog.setText(activity.getString(R.string.question_continue));
questionDialog.setOnNegative(new NegativeResponse() {
@Override
public void onCarryOut() {
if (!validatorRequest.getStatus().equals(ValidateImportBitsharesAccountRequest.StatusCode.SUCCEEDED)) {
String errorText = "An error ocurred attempting to import the account";
switch (validatorRequest.getStatus()){
case PETITION_FAILED:
case NO_INTERNET:
case NO_SERVER_CONNECTION:
errorText = "There was an error with the connection. Try again later";
break;
case ACCOUNT_DOESNT_EXIST:
errorText = "The account doesn't exists";
break;
case BAD_SEED:
errorText = "The seed is not valid";
break;
case NO_ACCOUNT_DATA:
errorText = "The account doesn't have any data";
break;
}
Toast.makeText(thisActivity.getApplicationContext(),errorText,Toast.LENGTH_LONG).show();
} else {
Intent intent = new Intent(thisActivity, BoardActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
public void onNegative(@NotNull DialogMaterial dialogMaterial) {
}
});
/*CryptoNetInfoRequests.getInstance().addRequest(validatorRequest);
questionDialog.setOnPositive(new PositiveResponse() {
@Override
public void onPositive() {
AccountSeed seed = new AccountSeed();
/*
* Loading dialog
* */
final CrystalLoading crystalLoading = new CrystalLoading(activity);
crystalLoading.show();
//TODO verify if words are already in the db
//TODO check if name has been asigned to other seed
seed.setMasterSeed(etSeedWords.getText().toString());
seed.setName(etAccountName.getText().toString());
seed.setType(SeedType.BRAINKEY);
/*
* Final service connection
* */
finalStep(crystalLoading);
accountSeedViewModel.addSeed(seed);
/*
* Validate mnemonic with the server
* */
/*final ImportBitsharesAccountRequest request = new ImportBitsharesAccountRequest(etSeedWords.getText().toString().trim(),activity);
request.setListener(new CryptoNetInfoRequestListener() {
@Override
public void onCarryOut() {
if(request.getStatus().equals(ImportBitsharesAccountRequest.StatusCode.SUCCEEDED)){
CryptoNetAccountViewModel cryptoNetAccountViewModel = ViewModelProviders.of(this).get(CryptoNetAccountViewModel.class);
GrapheneAccountInfoViewModel grapheneAccountInfoViewModel = ViewModelProviders.of(this).get(GrapheneAccountInfoViewModel.class);
CryptoNetAccount cryptoNetAccount = new CryptoNetAccount();
cryptoNetAccount.setSeedId(seed.getId());
cryptoNetAccount.setAccountIndex(0);
cryptoNetAccount.setCryptoNet(cy.agorise.crystalwallet.enums.CryptoNet.BITSHARES);
cryptoNetAccountViewModel.addCryptoNetAccount(cryptoNetAccount);
GrapheneAccountInfo grapheneAccountInfo = new GrapheneAccountInfo(cryptoNetAccount.getId());
grapheneAccountInfo.setName(etAccountName.getText().toString());
grapheneAccountInfoViewModel.addGrapheneAccountInfo(grapheneAccountInfo);
//Correct
this.finish();*/
CryptoNetInfoRequests.getInstance().addRequest(validatorRequest);
finalStep(crystalLoading);
}
else{
crystalLoading.dismiss();
txtErrorAccount.setVisibility(View.VISIBLE);
txtErrorAccount.setText(activity.getResources().getString(R.string.error_invalid_account));
}
}
});
CryptoNetInfoRequests.getInstance().addRequest(request);*/
}
});
questionDialog.show();
}
}
private void finalStep(final CrystalLoading crystalLoading){
final ImportSeedActivity thisActivity = this;
final ImportBitsharesAccountRequest validatorRequest =
new ImportBitsharesAccountRequest(etSeedWords.getText().toString(), getApplicationContext(), true);
validatorRequest.setListener(new CryptoNetInfoRequestListener() {
@Override
public void onCarryOut() {
/*
* Hide the loading dialog
* */
crystalLoading.dismiss();
if (!validatorRequest.getStatus().equals(ImportBitsharesAccountRequest.StatusCode.SUCCEEDED)) {
switch (validatorRequest.getStatus()){
case PETITION_FAILED:
case NO_INTERNET:
case NO_SERVER_CONNECTION:
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
txtErrorAccount.setText(activity.getResources().getString(R.string.NO_SERVER_CONNECTION));
}
});
break;
case ACCOUNT_DOESNT_EXIST:
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
txtErrorAccount.setText(activity.getResources().getString(R.string.ACCOUNT_DOESNT_EXIST));
}
});
break;
case BAD_SEED:
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
txtErrorAccount.setText(activity.getResources().getString(R.string.BAD_SEED));
}
});
break;
case NO_ACCOUNT_DATA:
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
txtErrorAccount.setText(activity.getResources().getString(R.string.NO_ACCOUNT_DATA));
}
});
break;
default:
txtErrorAccount.setText(activity.getResources().getString(R.string.ERROR_UNRECOGNIZABLE));
}
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
txtErrorAccount.setVisibility(View.VISIBLE);
}
});
//Toast.makeText(thisActivity.getApplicationContext(),errorText,Toast.LENGTH_LONG).show();
} else {
Intent intent = new Intent(thisActivity, BoardActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
}
});
CryptoNetInfoRequests.getInstance().addRequest(validatorRequest);
}
@Override
public void onValidationSucceeded(final ValidationField field) {
final ImportSeedActivity activity = this;
@ -183,19 +476,19 @@ public class ImportSeedActivity extends AppCompatActivity implements UIValidator
public void run() {
if (field.getView() == etPin) {
tvPinError.setText("");
//tvPinError.setText("");
} else if (field.getView() == etPinConfirmation){
tvPinConfirmationError.setText("");
//tvPinConfirmationError.setText("");
} else if (field.getView() == etAccountName){
tvAccountNameError.setText("");
//tvAccountNameError.setText("");
} else if (field.getView() == etSeedWords){
tvSeedWordsError.setText("");
//tvSeedWordsError.setText("");
}
if (activity.importSeedValidator.isValid()){
btnImport.setEnabled(true);
enableCreate();
} else {
btnImport.setEnabled(false);
disableCreate();
}
}
@ -205,13 +498,40 @@ public class ImportSeedActivity extends AppCompatActivity implements UIValidator
@Override
public void onValidationFailed(ValidationField field) {
if (field.getView() == etPin) {
tvPinError.setText(field.getMessage());
//tvPinError.setText(field.getMessage());
} else if (field.getView() == etPinConfirmation){
tvPinConfirmationError.setText(field.getMessage());
//tvPinConfirmationError.setText(field.getMessage());
} else if (field.getView() == etAccountName){
tvAccountNameError.setText(field.getMessage());
//tvAccountNameError.setText(field.getMessage());
} else if (field.getView() == etSeedWords){
tvSeedWordsError.setText(field.getMessage());
//tvSeedWordsError.setText(field.getMessage());
}
}
/*
* Enable create button
* */
private void enableCreate() {
runOnUiThread(new Runnable() {
@Override
public void run() {
//btnImport.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
btnImport.setEnabled(true);
}
});
}
/*
* Disable create button
* */
private void disableCreate() {
runOnUiThread(new Runnable() {
@Override
public void run() {
btnImport.setEnabled(false);
//btnImport.setBackground(getDrawable(R.drawable.disable_style));
}
});
}
}

View File

@ -2,55 +2,43 @@ package cy.agorise.crystalwallet.activities;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import android.widget.ImageView;
import java.util.List;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.thekhaeng.pushdownanim.PushDownAnim;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator;
import cy.agorise.crystalwallet.application.CrystalApplication;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
import cy.agorise.crystalwallet.dialogs.material.ToastIt;
import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.fragments.ImportAccountOptionsFragment;
import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.CryptoCoinBalance;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
import cy.agorise.crystalwallet.models.CryptoNetAccount;
import cy.agorise.crystalwallet.models.GeneralSetting;
import cy.agorise.crystalwallet.network.CryptoNetManager;
import cy.agorise.crystalwallet.randomdatagenerators.RandomCryptoCoinBalanceGenerator;
import cy.agorise.crystalwallet.randomdatagenerators.RandomCryptoNetAccountGenerator;
import cy.agorise.crystalwallet.randomdatagenerators.RandomSeedGenerator;
import cy.agorise.crystalwallet.randomdatagenerators.RandomTransactionsGenerator;
import cy.agorise.crystalwallet.application.CrystalSecurityMonitor;
import cy.agorise.crystalwallet.fragments.ImportAccountOptionsFragment;
import cy.agorise.crystalwallet.viewmodels.AccountSeedListViewModel;
import cy.agorise.crystalwallet.viewmodels.TransactionListViewModel;
import cy.agorise.crystalwallet.views.TransactionListView;
public class IntroActivity extends CustomActivity {
@BindView(R.id.surface_view)
public SurfaceView mSurfaceView;
@BindView(R.id.ivAnimation)
ImageView ivAnimation;
@BindView(R.id.btnCreateAccount)
public Button btnCreateAccount;
Button btnCreateAccount;
@BindView(R.id.btnImportAccount)
public Button btnImportAccount;
Button btnImportAccount;
/*
* For the window animation
* */
// private MediaPlayer mediaPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -59,50 +47,49 @@ public class IntroActivity extends CustomActivity {
ButterKnife.bind(this);
// Appbar animation
mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
//Log.d(TAG,"surfaceCreated");
MediaPlayer mediaPlayer = MediaPlayer.create(IntroActivity.this, R.raw.appbar_background);
mediaPlayer.setDisplay(mSurfaceView.getHolder());
mediaPlayer.setLooping(true);
mediaPlayer.start();
}
Glide.with(this)
.load(R.drawable.appbar_background)
.apply(new RequestOptions().centerCrop())
.into(ivAnimation);
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
//Log.d(TAG,"surfaceChanged");
}
/*
* Integration of library with button effects
* */
PushDownAnim.setPushDownAnimTo(btnCreateAccount)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
createAccount();
}
} );
PushDownAnim.setPushDownAnimTo(btnImportAccount)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
importAccount();
}
} );
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
//Log.d(TAG,"surfaceDestroyed");
}
});
this.getApplication().registerActivityLifecycleCallbacks(CrystalSecurityMonitor.getInstance(this));
//Checks if the user has any seed created
AccountSeedListViewModel accountSeedListViewModel = ViewModelProviders.of(this).get(AccountSeedListViewModel.class);
//Checks if the user has any seed created
AccountSeedListViewModel accountSeedListViewModel = ViewModelProviders.of(this).get(AccountSeedListViewModel.class);
if (accountSeedListViewModel.accountSeedsCount() == 0) {
//If the user doesn't have any seeds created, then
//send the user to create/import an account
//Intent intent = new Intent(this, AccountSeedsManagementActivity.class);
//Intent intent = new Intent(this, ImportSeedActivity.class);
//Intent intent = new Intent(this, CreateSeedActivity.class);
//startActivity(intent);
} else {
//Intent intent = new Intent(this, CreateSeedActivity.class);
Intent intent = new Intent(this, BoardActivity.class);
//Intent intent = new Intent(this, PocketRequestActivity.class);
startActivity(intent);
finish();
}
if (accountSeedListViewModel.accountSeedsCount() == 0) {
//If the user doesn't have any seeds created, then
//send the user to create/import an account
//Intent intent = new Intent(this, AccountSeedsManagementActivity.class);
//Intent intent = new Intent(this, ImportSeedActivity.class);
//Intent intent = new Intent(this, CreateSeedActivity.class);
//startActivity(intent);
} else {
//Intent intent = new Intent(this, CreateSeedActivity.class);
Intent intent = new Intent(this, BoardActivity.class);
//Intent intent = new Intent(this, PocketRequestActivity.class);
startActivity(intent);
finish();
}
/*CrystalDatabase db = CrystalDatabase.getAppDatabase(getApplicationContext());
List<AccountSeed> seeds = RandomSeedGenerator.generateSeeds(2);

View File

@ -4,7 +4,11 @@ import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.webkit.WebView;
import android.widget.Button;
import com.thekhaeng.pushdownanim.PushDownAnim;
import butterknife.BindView;
import butterknife.ButterKnife;
@ -20,8 +24,17 @@ public class LicenseActivity extends AppCompatActivity {
@BindView(R.id.wvEULA) WebView wvEULA;
@BindView(R.id.btnDisAgree)
Button btnDisAgree;
@BindView(R.id.btnAgree)
Button btnAgree;
CrystalDatabase db;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -29,10 +42,23 @@ public class LicenseActivity extends AppCompatActivity {
ButterKnife.bind(this);
// TODO check if license has been agreed
String html = getString(R.string.licence_html);
wvEULA.loadData(html, "text/html", "UTF-8");
/*
* Integration of library with button efects
* */
PushDownAnim.setPushDownAnimTo(btnDisAgree)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
onDisagree();
}
} );
PushDownAnim.setPushDownAnimTo(btnAgree)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
onAgree();
}
} );
db = CrystalDatabase.getAppDatabase(this.getApplicationContext());
int licenseVersion = getResources().getInteger(R.integer.license_version);
@ -43,6 +69,9 @@ public class LicenseActivity extends AppCompatActivity {
startActivity(intent);
finish();
}
else{
wvEULA.loadUrl("file:///android_asset/crystal_eula.html");
}
}
@OnClick(R.id.btnAgree)

View File

@ -1,31 +1,64 @@
package cy.agorise.crystalwallet.activities;
import android.app.Activity;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import com.andrognito.patternlockview.PatternLockView;
import com.andrognito.patternlockview.listener.PatternLockViewListener;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnTextChanged;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.application.CrystalSecurityMonitor;
//import cy.agorise.crystalwallet.interfaces.OnResponse;
import cy.agorise.crystalwallet.interfaces.OnResponse;
import cy.agorise.crystalwallet.models.GeneralSetting;
import cy.agorise.crystalwallet.util.PasswordManager;
import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel;
public class PatternRequestActivity extends AppCompatActivity {
private String patternEncrypted;
@BindView(R.id.tvPatternText)
TextView tvPatternText;
@BindView(R.id.txtBadtry)
TextView txtBadtry;
/*
* External listener for success or fail
* */
private static OnResponse onResponse;
/*
* Contains the bad tries
* */
private int tries = 0;
/*
* Seconds counter
* */
private int seconds = 15;
@Override
public void onBackPressed() {
//Do nothing to prevent the user to use the back button
@ -34,12 +67,15 @@ public class PatternRequestActivity extends AppCompatActivity {
@BindView(R.id.patternLockView)
PatternLockView patternLockView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pattern_request);
ButterKnife.bind(this);
//onResponse = null;
GeneralSettingListViewModel generalSettingListViewModel = ViewModelProviders.of(this).get(GeneralSettingListViewModel.class);
LiveData<List<GeneralSetting>> generalSettingsLiveData = generalSettingListViewModel.getGeneralSettingList();
@ -71,13 +107,26 @@ public class PatternRequestActivity extends AppCompatActivity {
public void onComplete(List<PatternLockView.Dot> pattern) {
if (PasswordManager.checkPassword(patternEncrypted,patternToString(pattern))){
if (CrystalSecurityMonitor.getInstance(null).is2ndFactorSet()) {
CrystalSecurityMonitor.getInstance(null).call2ndFactor(thisActivity);
//CrystalSecurityMonitor.getInstance(null).call2ndFactor(thisActivity);
thisActivity.finish();
/*if(onResponse != null){
onResponse.onSuccess();
}*/
} else {
thisActivity.finish();
/*if(onResponse != null){
onResponse.onSuccess();
}*/
}
} else {
patternLockView.clearPattern();
patternLockView.requestFocus();
incorrect();
/*if(onResponse != null){
onResponse.onFailed();
}*/
}
}
@ -95,6 +144,104 @@ public class PatternRequestActivity extends AppCompatActivity {
});
}
public static void setOnResponse(OnResponse onResponse) {
PatternRequestActivity.onResponse = onResponse;
}
private void incorrect(){
/*
* One more bad try
* */
++tries;
final Activity activity = this;
/*
* User can not go more up to 5 bad tries
* */
if(tries==4) {
tries = 0;
patternLockView.setEnabled(false);
txtBadtry.setVisibility(View.VISIBLE);
txtBadtry.setText(txtBadtry.getText().toString().replace("%%",String.valueOf(seconds)));
final Timer t = new Timer();
//Set the schedule function and rate
t.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
--seconds;
if(seconds==0){
t.cancel();
seconds = 15;
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
patternLockView.setEnabled(true);
txtBadtry.setVisibility(View.INVISIBLE);
patternLockView.clearPattern();
patternLockView.requestFocus();
}
});
}
else{
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
txtBadtry.setText(activity.getResources().getString(R.string.wrong_pin_wait).replace("%%",String.valueOf(seconds)));
}
});
}
}
},
//Set how long before to start calling the TimerTask (in milliseconds)
1000,
//Set the amount of time between each execution (in milliseconds)
1000);
return;
}
/*
* Show error
* */
tvPatternText.setText(activity.getResources().getString(R.string.Incorrect_pattern));
tvPatternText.setTextColor(Color.RED);
tvPatternText.setVisibility(View.VISIBLE);
final Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
t.cancel();
tvPatternText.setVisibility(View.INVISIBLE);
patternLockView.clearPattern();
patternLockView.requestFocus();
}
});
}
},
//Set how long before to start calling the TimerTask (in milliseconds)
1000,
//Set the amount of time between each execution (in milliseconds)
1000);
}
public String patternToString(List<PatternLockView.Dot> pattern){
String patternString = "";
for (PatternLockView.Dot nextDot : pattern){

View File

@ -1,21 +1,37 @@
package cy.agorise.crystalwallet.activities;
import android.app.Activity;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnTextChanged;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.application.CrystalSecurityMonitor;
import cy.agorise.crystalwallet.dialogs.material.DialogMaterial;
import cy.agorise.crystalwallet.dialogs.material.NegativeResponse;
import cy.agorise.crystalwallet.dialogs.material.PositiveResponse;
import cy.agorise.crystalwallet.dialogs.material.QuestionDialog;
import cy.agorise.crystalwallet.interfaces.OnResponse;
import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.GeneralSetting;
import cy.agorise.crystalwallet.util.PasswordManager;
@ -24,6 +40,29 @@ import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel;
public class PinRequestActivity extends AppCompatActivity {
private String passwordEncrypted;
@BindView(R.id.btnOK)
Button btnOK;
@BindView(R.id.txtBadtry)
TextView txtBadtry;
/*
* Contains the bad tries
* */
private int tries = 0;
/*
* Seconds counter
* */
private int seconds = 15;
/*
* External listener for success or fail
* */
private static OnResponse onResponse;
@Override
public void onBackPressed() {
//Do nothing to prevent the user to use the back button
@ -38,6 +77,13 @@ public class PinRequestActivity extends AppCompatActivity {
setContentView(R.layout.activity_pin_request);
ButterKnife.bind(this);
//onResponse = null;
/*
* Initially the button is disabled till the user type a valid PIN
* */
btnOK.setEnabled(false);
GeneralSettingListViewModel generalSettingListViewModel = ViewModelProviders.of(this).get(GeneralSettingListViewModel.class);
LiveData<List<GeneralSetting>> generalSettingsLiveData = generalSettingListViewModel.getGeneralSettingList();
generalSettingsLiveData.observe(this, new Observer<List<GeneralSetting>>() {
@ -59,17 +105,139 @@ public class PinRequestActivity extends AppCompatActivity {
});
}
@OnTextChanged(value = R.id.etPassword,
callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
void afterPasswordChanged(Editable editable) {
@OnClick(R.id.btnOK)
void okClic(final View view) {
if (PasswordManager.checkPassword(passwordEncrypted, etPassword.getText().toString())) {
if (CrystalSecurityMonitor.getInstance(null).is2ndFactorSet()) {
CrystalSecurityMonitor.getInstance(null).call2ndFactor(this);
if(onResponse != null){
onResponse.onSuccess();
}
} else {
this.finish();
if(onResponse != null){
onResponse.onFailed();
}
}
}
else{
/*
* One more bad try
* */
++tries;
final Activity activity = this;
etPassword.setTextColor(Color.RED);
/*
* User can not go more up to 5 bad tries
* */
if(tries==4){
tries = 0;
btnOK.setEnabled(false);
etPassword.setEnabled(false);
txtBadtry.setVisibility(View.VISIBLE);
txtBadtry.setText(txtBadtry.getText().toString().replace("%%",String.valueOf(seconds)));
final Timer t = new Timer();
//Set the schedule function and rate
t.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
--seconds;
if(seconds==0){
t.cancel();
seconds = 15;
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
btnOK.setEnabled(true);
etPassword.setEnabled(true);
txtBadtry.setVisibility(View.INVISIBLE);
etPassword.setText("");
etPassword.setTextColor(Color.WHITE);
}
});
}
else{
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
txtBadtry.setText(activity.getResources().getString(R.string.wrong_pin_wait).replace("%%",String.valueOf(seconds)));
}
});
}
}
},
//Set how long before to start calling the TimerTask (in milliseconds)
1000,
//Set the amount of time between each execution (in milliseconds)
1000);
return;
}
/*
* Set in red the rext and reset the password after a period of time
* */
final Timer t = new Timer();
//Set the schedule function and rate
t.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
//Called each time when 1000 milliseconds (1 second) (the period parameter)
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
etPassword.setText("");
etPassword.setTextColor(Color.WHITE);
t.cancel();
}
});
}
},
//Set how long before to start calling the TimerTask (in milliseconds)
500,
//Set the amount of time between each execution (in milliseconds)
500);
}
}
}
public static void setOnResponse(OnResponse onResponse) {
PinRequestActivity.onResponse = onResponse;
}
@OnTextChanged(value = R.id.etPassword,
callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
void afterPasswordChanged(Editable editable) {
/*
* If it is valid length enable button
* */
if(etPassword.getText().length()>=6){
btnOK.setEnabled(true);
}
else{
btnOK.setEnabled(false);
}
}
}

View File

@ -1,15 +1,18 @@
package cy.agorise.crystalwallet.activities;
import android.app.Activity;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.jaredrummler.materialspinner.MaterialSpinner;
@ -38,7 +41,7 @@ public class SendTransactionActivity extends AppCompatActivity implements UIVali
SendTransactionValidator sendTransactionValidator;
@BindView(R.id.spFrom)
MaterialSpinner spFrom;
Spinner spFrom;
@BindView(R.id.tvFromError)
TextView tvFromError;
@BindView(R.id.etTo)
@ -62,6 +65,9 @@ public class SendTransactionActivity extends AppCompatActivity implements UIVali
//@BindView(R.id.btnCancel)
Button btnCancel;
@BindView(R.id.fabCloseCamera)
FloatingActionButton btnCloseCamera;
private long cryptoNetAccountId;
private CryptoNetAccount cryptoNetAccount;
private GrapheneAccount grapheneAccount;
@ -146,6 +152,15 @@ public class SendTransactionActivity extends AppCompatActivity implements UIVali
this.finish();
}
@OnClick(R.id.fabCloseCamera)
public void onClicCloseCamera(){
}
//@OnClick(R.id.btnSend)
public void importSend(){
if (this.sendTransactionValidator.isValid()) {

View File

@ -1,20 +1,19 @@
package cy.agorise.crystalwallet.activities;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.ImageView;
import android.widget.TextView;
import com.sjaramillo10.animatedtablayout.AnimatedTabLayout;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import butterknife.BindView;
import butterknife.ButterKnife;
@ -35,21 +34,22 @@ public class SettingsActivity extends AppCompatActivity{
public ImageView ivGoBack;
@BindView(R.id.tabLayout)
public AnimatedTabLayout tabLayout;
public TabLayout tabLayout;
@BindView(R.id.pager)
public ViewPager mPager;
public SettingsPagerAdapter settingsPagerAdapter;
@BindView(R.id.surface_view)
public SurfaceView mSurfaceView;
@BindView(R.id.tvBuildVersion)
public TextView tvBuildVersion;
private SecuritySettingsFragment securitySettingsFragment;
@BindView(R.id.ivAppBarAnimation)
ImageView ivAppBarAnimation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -59,27 +59,11 @@ public class SettingsActivity extends AppCompatActivity{
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Appbar animation
mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
//Log.d(TAG,"surfaceCreated");
MediaPlayer mediaPlayer = MediaPlayer.create(SettingsActivity.this, R.raw.appbar_background);
mediaPlayer.setDisplay(mSurfaceView.getHolder());
mediaPlayer.setLooping(true);
mediaPlayer.start();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
//Log.d(TAG,"surfaceChanged");
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
//Log.d(TAG,"surfaceDestroyed");
}
});
// Sets AppBar animation
Glide.with(this)
.load(R.drawable.appbar_background)
.apply(new RequestOptions().centerCrop())
.into(ivAppBarAnimation);
settingsPagerAdapter = new SettingsPagerAdapter(getSupportFragmentManager());
mPager.setAdapter(settingsPagerAdapter);

View File

@ -1,7 +1,6 @@
package cy.agorise.crystalwallet.apigenerator;
import android.content.Context;
import android.util.Log;
import java.io.Serializable;
import java.util.ArrayList;
@ -9,7 +8,6 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import cy.agorise.crystalwallet.application.constant.BitsharesConstant;
import cy.agorise.crystalwallet.dao.BitsharesAssetDao;
import cy.agorise.crystalwallet.dao.CryptoCoinBalanceDao;
import cy.agorise.crystalwallet.dao.CryptoCurrencyDao;
@ -82,13 +80,32 @@ public abstract class GrapheneApiGenerator {
*/
private static HashMap<Long,SubscriptionListener> currentBitsharesListener = new HashMap<>();
// The message broker for Steem
private static SubscriptionMessagesHub steemSubscriptionHub = new SubscriptionMessagesHub("", "", true, new NodeErrorListener() {
@Override
public void onError(BaseResponse.Error error) {
//TODO subcription hub error
System.out.println("GrapheneAPI error");
}
});
/**
* The Steem subscription thread for the real time updates
*/
private static WebSocketThread steemSubscriptionThread = new WebSocketThread(steemSubscriptionHub, CryptoNetManager.getURL(CryptoNet.STEEM));
/**
* This is used for manager each Steem listener in the subscription thread
*/
private static HashMap<Long,SubscriptionListener> currentSteemListener = new HashMap<>();
/**
* Retrieves the data of an account searching by it's id
*
* @param accountId The accountId to retrieve
* @param request The Api request object, to answer this petition
*/
public static void getAccountById(String accountId, final ApiRequest request){
public static void getAccountById(String accountId, CryptoNet cryptoNet, final ApiRequest request){
WebSocketThread thread = new WebSocketThread(new GetAccounts(accountId,
new WitnessResponseListener() {
@Override
@ -109,7 +126,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
}),CryptoNetManager.getURL(CryptoNet.BITSHARES));
}),CryptoNetManager.getURL(cryptoNet));
thread.start();
}
@ -119,25 +136,37 @@ public abstract class GrapheneApiGenerator {
* @param address The address to retrieve
* @param request The Api request object, to answer this petition
*/
public static void getAccountByOwnerOrActiveAddress(Address address, final ApiRequest request){
public static void getAccountByOwnerOrActiveAddress(Address address, CryptoNet cryptoNet, final ApiRequest request){
WebSocketThread thread = new WebSocketThread(new GetKeyReferences(address, true,
new WitnessResponseListener() {
@Override
public void onSuccess(WitnessResponse response) {
final List<List<UserAccount>> resp = (List<List<UserAccount>>) response.result;
if(resp.size() > 0){
List<UserAccount> accounts = resp.get(0);
if(accounts.size() > 0){
for(UserAccount account : accounts) {
request.getListener().success(account,request.getId());}}}
request.getListener().fail(request.getId());
try {
final List<List<UserAccount>> resp = (List<List<UserAccount>>) response.result;
if (resp.size() > 0) {
List<UserAccount> accounts = resp.get(0);
if (accounts.size() > 0) {
for (UserAccount account : accounts) {
request.getListener().success(account, request.getId());
break;
}
}else{
request.getListener().fail(request.getId());
}
} else {
request.getListener().fail(request.getId());
}
}catch(Exception e){
e.printStackTrace();
request.getListener().fail(request.getId());
}
}
@Override
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
}),CryptoNetManager.getURL(CryptoNet.BITSHARES));
}),CryptoNetManager.getURL(cryptoNet));
thread.start();
}
@ -152,7 +181,7 @@ public abstract class GrapheneApiGenerator {
* @param request The Api request object, to answer this petition
*/
public static void getAccountTransaction(String accountGrapheneId, int start, int stop,
int limit, final ApiRequest request){
int limit, CryptoNet cryptoNet, final ApiRequest request){
WebSocketThread thread = new WebSocketThread(new GetRelativeAccountHistory(new UserAccount(accountGrapheneId),
start, limit, stop, new WitnessResponseListener() {
@Override
@ -164,7 +193,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
}),CryptoNetManager.getURL(CryptoNet.BITSHARES));
}),CryptoNetManager.getURL(cryptoNet));
thread.start();
}
@ -174,7 +203,7 @@ public abstract class GrapheneApiGenerator {
* @param accountName The account Name to find
* @param request The Api request object, to answer this petition
*/
public static void getAccountByName(String accountName, final ApiRequest request){
public static void getAccountByName(String accountName, CryptoNet cryptoNet,final ApiRequest request){
WebSocketThread thread = new WebSocketThread(new GetAccountByName(accountName,
new WitnessResponseListener() {
@Override
@ -191,7 +220,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
}),CryptoNetManager.getURL(CryptoNet.BITSHARES));
}),CryptoNetManager.getURL(cryptoNet));
thread.start();
}
@ -201,7 +230,7 @@ public abstract class GrapheneApiGenerator {
* @param accountName The account Name to find
* @param request The Api request object, to answer this petition
*/
public static void getAccountIdByName(String accountName, final ApiRequest request){
public static void getAccountIdByName(String accountName, CryptoNet cryptoNet, final ApiRequest request){
WebSocketThread thread = new WebSocketThread(new GetAccountByName(accountName,
new WitnessResponseListener() {
@Override
@ -218,7 +247,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
}),CryptoNetManager.getURL(CryptoNet.BITSHARES));
}),CryptoNetManager.getURL(cryptoNet));
thread.start();
}
@ -252,7 +281,7 @@ public abstract class GrapheneApiGenerator {
* @param assetNames The list of the names of the assets to be retrieve
* @param request the api request object, to answer this petition
*/
public static void getAssetByName(ArrayList<String> assetNames, final ApiRequest request){
public static void getAssetByName(ArrayList<String> assetNames, CryptoNet cryptoNet,final ApiRequest request){
WebSocketThread thread = new WebSocketThread(new LookupAssetSymbols(assetNames,true,
new WitnessResponseListener() {
@ -287,7 +316,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
}),CryptoNetManager.getURL(CryptoNet.BITSHARES));
}),CryptoNetManager.getURL(cryptoNet));
thread.start();
}
@ -296,7 +325,7 @@ public abstract class GrapheneApiGenerator {
* @param assetIds The list of the ids to retrieve
* @param request the api request object, to answer this petition
*/
public static void getAssetById(ArrayList<String> assetIds, final ApiRequest request){
public static void getAssetById(ArrayList<String> assetIds, CryptoNet cryptoNet, final ApiRequest request){
ArrayList<Asset> assets = new ArrayList<>();
for(String assetId : assetIds){
Asset asset = new Asset(assetId);
@ -335,7 +364,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
}),CryptoNetManager.getURL(CryptoNet.BITSHARES));
}),CryptoNetManager.getURL(cryptoNet));
thread.start();
}
@ -369,7 +398,7 @@ public abstract class GrapheneApiGenerator {
if(operation instanceof TransferOperation){
final TransferOperation tOperation = (TransferOperation) operation;
if(tOperation.getFrom().getObjectId().equals(accountBitsharesId) || tOperation.getTo().getObjectId().equals(accountBitsharesId)){
GrapheneApiGenerator.getAccountBalance(accountId,accountBitsharesId,context);
GrapheneApiGenerator.getAccountBalance(accountId,accountBitsharesId,CryptoNet.BITSHARES,context);
final CryptoCoinTransaction transaction = new CryptoCoinTransaction();
transaction.setAccountId(accountId);
transaction.setAmount(tOperation.getAssetAmount().getAmount().longValue());
@ -407,7 +436,7 @@ public abstract class GrapheneApiGenerator {
});
ArrayList<String> assets = new ArrayList<>();
assets.add(tOperation.getAssetAmount().getAsset().getObjectId());
GrapheneApiGenerator.getAssetById(assets,assetRequest);
GrapheneApiGenerator.getAssetById(assets,CryptoNet.BITSHARES,assetRequest);
}else{
saveTransaction(transaction,cryptoCurrencyDao.getById(info.getCryptoCurrencyId()),accountBitsharesId,tOperation,context);
}
@ -432,6 +461,99 @@ public abstract class GrapheneApiGenerator {
}
}
/**
* Subscribe a steem account to receive real time updates
*
* @param accountId The id opf the database of the account
* @param accountSteemId The steem id of the account
* @param context The android context of this application
*/
public static void subscribeSteemAccount(final long accountId, final String accountSteemId,
final Context context){
if(!currentSteemListener.containsKey(accountId)){
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
final BitsharesAssetDao bitsharesAssetDao = db.bitsharesAssetDao();
final CryptoCurrencyDao cryptoCurrencyDao = db.cryptoCurrencyDao();
SubscriptionListener balanceListener = new SubscriptionListener() {
@Override
public ObjectType getInterestObjectType() {
return ObjectType.TRANSACTION_OBJECT;
}
@Override
public void onSubscriptionUpdate(SubscriptionResponse response) {
List<Serializable> updatedObjects = (List<Serializable>) response.params.get(1);
if(updatedObjects.size() > 0){
for(Serializable update : updatedObjects){
if(update instanceof BroadcastedTransaction){
BroadcastedTransaction transactionUpdate = (BroadcastedTransaction) update;
for(BaseOperation operation : transactionUpdate.getTransaction().getOperations()){
if(operation instanceof TransferOperation){
final TransferOperation tOperation = (TransferOperation) operation;
if(tOperation.getFrom().getObjectId().equals(accountSteemId) || tOperation.getTo().getObjectId().equals(accountSteemId)){
GrapheneApiGenerator.getAccountBalance(accountId,accountSteemId,CryptoNet.STEEM,context);
final CryptoCoinTransaction transaction = new CryptoCoinTransaction();
transaction.setAccountId(accountId);
transaction.setAmount(tOperation.getAssetAmount().getAmount().longValue());
BitsharesAssetInfo info = bitsharesAssetDao.getBitsharesAssetInfoById(tOperation.getAssetAmount().getAsset().getObjectId());
if (info == null) {
//The cryptoCurrency is not in the database, queringfor its data
ApiRequest assetRequest = new ApiRequest(0, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
ArrayList<BitsharesAsset> assets = (ArrayList<BitsharesAsset>) answer;
for(BitsharesAsset asset : assets){
long currencyId = -1;
CryptoCurrency cryptoCurrencyDb = cryptoCurrencyDao.getByNameAndCryptoNet(((BitsharesAsset) answer).getName(),((BitsharesAsset) answer).getCryptoNet().name());
if (cryptoCurrencyDb != null){
currencyId = cryptoCurrencyDb.getId();
} else {
long idCryptoCurrency = cryptoCurrencyDao.insertCryptoCurrency(asset)[0];
currencyId = idCryptoCurrency;
}
BitsharesAssetInfo info = new BitsharesAssetInfo(asset);
info.setCryptoCurrencyId(currencyId);
asset.setId((int)currencyId);
bitsharesAssetDao.insertBitsharesAssetInfo(info);
saveTransaction(transaction,cryptoCurrencyDao.getById(info.getCryptoCurrencyId()),accountSteemId,tOperation,context);
}
}
@Override
public void fail(int idPetition) {
//TODO error retrieving asset
}
});
ArrayList<String> assets = new ArrayList<>();
assets.add(tOperation.getAssetAmount().getAsset().getObjectId());
GrapheneApiGenerator.getAssetById(assets,CryptoNet.STEEM,assetRequest);
}else{
saveTransaction(transaction,cryptoCurrencyDao.getById(info.getCryptoCurrencyId()),accountSteemId,tOperation,context);
}
}
}
}
}
}
}
}
};
currentSteemListener.put(accountId,balanceListener);
steemSubscriptionHub.addSubscriptionListener(balanceListener);
if(!steemSubscriptionThread.isConnected()){
steemSubscriptionThread.start();
}else if(!steemSubscriptionHub.isSubscribed()){
steemSubscriptionHub.resubscribe();
}
}
}
/**
* Function to save a transaction retrieved from the update
* @param transaction The transaction db object
@ -470,7 +592,7 @@ public abstract class GrapheneApiGenerator {
* @param context The android context of this application
*/
public static void getAccountBalance(final long accountId, final String accountGrapheneId,
final Context context){
final CryptoNet cryptoNet, final Context context){
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
final CryptoCoinBalanceDao balanceDao = db.cryptoCoinBalanceDao();
@ -516,7 +638,7 @@ public abstract class GrapheneApiGenerator {
public void fail(int idPetition) {
}
});
getAssetById(idAssets,getAssetRequest);
getAssetById(idAssets,cryptoNet,getAssetRequest);
}else {
@ -530,7 +652,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
}
}),CryptoNetManager.getURL(CryptoNet.BITSHARES));
}),CryptoNetManager.getURL(cryptoNet));
thread.start();
@ -542,7 +664,7 @@ public abstract class GrapheneApiGenerator {
* @param blockHeader The block header to retrieve the date time
* @param request the api request object, to answer this petition
*/
public static void getBlockHeaderTime(long blockHeader, final ApiRequest request){
public static void getBlockHeaderTime(long blockHeader, CryptoNet cryptoNet,final ApiRequest request){
WebSocketThread thread = new WebSocketThread(new GetBlockHeader(blockHeader, new WitnessResponseListener() {
@Override
public void onSuccess(WitnessResponse response) {
@ -557,7 +679,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
}),CryptoNetManager.getURL(CryptoNet.BITSHARES));
}),CryptoNetManager.getURL(cryptoNet));
thread.start();
}
@ -625,7 +747,7 @@ public abstract class GrapheneApiGenerator {
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
final CryptoCurrencyDao cryptoCurrencyDao = db.cryptoCurrencyDao();
final BitsharesAssetDao bitsharesAssetDao = db.bitsharesAssetDao();
CryptoCurrency baseCurrency = cryptoCurrencyDao.getByName(baseAssetName);
CryptoCurrency baseCurrency = cryptoCurrencyDao.getByName(baseAssetName,CryptoNet.BITSHARES.name());
BitsharesAssetInfo info = null;
if(baseCurrency != null){
info = db.bitsharesAssetDao().getBitsharesAssetInfo(baseCurrency.getId());
@ -660,7 +782,7 @@ public abstract class GrapheneApiGenerator {
});
ArrayList<String> names = new ArrayList<>();
names.add(baseAssetName);
GrapheneApiGenerator.getAssetByName(names,getAssetRequest);
GrapheneApiGenerator.getAssetByName(names,CryptoNet.BITSHARES,getAssetRequest);
}else {
BitsharesAsset baseAsset = new BitsharesAsset(baseCurrency);

View File

@ -1,49 +1,81 @@
package cy.agorise.crystalwallet.apigenerator;
import android.content.Context;
import java.util.HashMap;
import cy.agorise.crystalwallet.apigenerator.insightapi.AddressesActivityWatcher;
import cy.agorise.crystalwallet.apigenerator.insightapi.BroadcastTransaction;
import cy.agorise.crystalwallet.apigenerator.insightapi.GetEstimateFee;
import cy.agorise.crystalwallet.apigenerator.insightapi.GetTransactionByAddress;
import cy.agorise.crystalwallet.apigenerator.insightapi.GetTransactionData;
import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.network.CryptoNetManager;
public class InsightApiGenerator {
private static HashMap<CryptoNet,BroadcastTransaction> broadcaster = new HashMap();
private static HashMap<CryptoNet,GetTransactionByAddress> transactionGetters = new HashMap();
private static HashMap<CryptoNet,GetTransactionData> transacitonFollowers = new HashMap();
private static HashMap<CryptoCoin,GetTransactionByAddress> transactionGetters = new HashMap();
private static HashMap<CryptoCoin,AddressesActivityWatcher> transactionFollowers = new HashMap();
public static void getTransactionFromAddress(CryptoNet cryptoNet, String address,
ApiRequest request, Context context,
boolean subscribe){
if(!transactionGetters.containsKey(cryptoNet)){
//TODO change this line
transactionGetters.put(cryptoNet,new GetTransactionByAddress(null,CryptoNetManager.getURL(cryptoNet),context));
private static final String PATH = "api";
/**
* Fecth all the transaciton for a giving address
* @param cryptoCoin the crypto net of the address
* @param address The address String
* @param subscribe If needs to follow the address (Real time)
*/
public static void getTransactionFromAddress(CryptoCoin cryptoCoin, String address, boolean subscribe, HasTransactionListener listener){
/*if(!transactionGetters.containsKey(cryptoCoin)){
transactionGetters.put(cryptoCoin,new GetTransactionByAddress(cryptoCoin,CryptoNetManager.getURL(cryptoCoin.getCryptoNet()),PATH));
}
transactionGetters.get(cryptoCoin).addAddress(address);
transactionGetters.get(cryptoCoin).start();*/
}
GetTransactionByAddress transByAddr = new GetTransactionByAddress(cryptoCoin,CryptoNetManager.getURL(cryptoCoin.getCryptoNet()),PATH,listener);
transByAddr.addAddress(address);
transByAddr.start();
public static void followTransaction(CryptoNet cryptoNet, String txid, Context context){
}
public static void broadcastTransaction(CryptoNet cryptoNet, String rawtx, ApiRequest request){
if(!broadcaster.containsKey(cryptoNet)){
//TODO change to multiple broadcast
broadcaster.put(cryptoNet,new BroadcastTransaction(rawtx,null,
CryptoNetManager.getURL(cryptoNet),null));
broadcaster.get(cryptoNet).start();
if(subscribe){
if(!transactionFollowers.containsKey(cryptoCoin)){
transactionFollowers.put(cryptoCoin,new AddressesActivityWatcher(CryptoNetManager.getURL(cryptoCoin.getCryptoNet()),PATH,cryptoCoin));
}
transactionFollowers.get(cryptoCoin).addAddress(address);
transactionFollowers.get(cryptoCoin).connect();
}
}
public static void getEstimateFee(CryptoNet cryptoNet, final ApiRequest request){
GetEstimateFee.getEstimateFee(CryptoNetManager.getURL(cryptoNet), new GetEstimateFee.estimateFeeListener() {
/**
* Broadcast an insight api transaction
* @param cryptoCoin The cryptoNet of the transaction
* @param rawtx the transaction to be broadcasted
*/
public static void broadcastTransaction(CryptoCoin cryptoCoin, String rawtx, final ApiRequest request){
BroadcastTransaction bTransaction = new BroadcastTransaction(rawtx,
CryptoNetManager.getURL(cryptoCoin.getCryptoNet()), PATH, new BroadcastTransaction.BroadCastTransactionListener() {
@Override
public void estimateFee(long value) {
public void onSuccess() {
request.getListener().success(true,request.getId());
}
@Override
public void onFailure(String msg) {
request.getListener().fail(request.getId());
}
@Override
public void onConnecitonFailure() {
request.getListener().fail(request.getId());
}
});
bTransaction.start();
}
/**
* Fetch the estimated fee for a transaction
*/
public static void getEstimateFee(CryptoCoin cryptoCoin, final ApiRequest request){
GetEstimateFee.getEstimateFee(CryptoNetManager.getURL(cryptoCoin.getCryptoNet()),
new GetEstimateFee.estimateFeeListener() {
@Override
public void estimateFee(double value) {
request.listener.success(value,request.getId());
}
@ -53,4 +85,8 @@ public class InsightApiGenerator {
}
});
}
public interface HasTransactionListener{
public void hasTransaction(boolean value);
}
}

View File

@ -12,6 +12,7 @@ import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.models.GeneralCoinAccount;
import io.socket.client.IO;
import io.socket.client.Socket;
@ -24,12 +25,9 @@ import io.socket.emitter.Emitter;
*
*/
public class AccountActivityWatcher {
public class AddressesActivityWatcher {
/**
* The mAccount to be monitor
*/
private final GeneralCoinAccount mAccount;
private final CryptoCoin cryptoCoin;
/**
* The list of address to monitor
*/
@ -38,12 +36,9 @@ public class AccountActivityWatcher {
* the Socket.IO
*/
private Socket mSocket;
/**
* This app mContext, used to save on the DB
*/
private final Context mContext;
private final String mServerUrl;
private final String mPath;
/**
* Handles the address/transaction notification.
@ -55,9 +50,9 @@ public class AccountActivityWatcher {
try {
System.out.println("Receive accountActivtyWatcher " + os[0].toString() );
String txid = ((JSONObject) os[0]).getString(InsightApiConstants.sTxTag);
new GetTransactionData(txid, mAccount, mServerUrl, mContext).start();
new GetTransactionData(txid, mServerUrl, mPath, cryptoCoin).start();
} catch (JSONException ex) {
Logger.getLogger(AccountActivityWatcher.class.getName()).log(Level.SEVERE, null, ex);
Logger.getLogger(AddressesActivityWatcher.class.getName()).log(Level.SEVERE, null, ex);
}
}
};
@ -84,7 +79,9 @@ public class AccountActivityWatcher {
private final Emitter.Listener onDisconnect = new Emitter.Listener() {
@Override
public void call(Object... os) {
System.out.println("Disconnected to accountActivityWatcher");
try {
Thread.sleep(60000);
} catch (InterruptedException ignore) {}
mSocket.connect();
}
};
@ -99,19 +96,21 @@ public class AccountActivityWatcher {
for(Object ob : os) {
System.out.println("accountActivityWatcher " + ob.toString());
}
try {
Thread.sleep(60000);
} catch (InterruptedException ignore) {}
mSocket.connect();
}
};
/**
* Basic constructor
*
* @param mAccount The mAccount to be monitor
* @param mContext This app mContext
*/
public AccountActivityWatcher(String serverUrl, GeneralCoinAccount mAccount, Context mContext) {
public AddressesActivityWatcher(String serverUrl, String path, CryptoCoin cryptoCoin) {
this.mPath = path;
this.mServerUrl = serverUrl;
this.mAccount = mAccount;
this.mContext = mContext;
this.cryptoCoin = cryptoCoin;
try {
this.mSocket = IO.socket(serverUrl);
this.mSocket.on(Socket.EVENT_CONNECT, onConnect);
@ -141,13 +140,11 @@ public class AccountActivityWatcher {
* Connects the Socket
*/
public void connect() {
//TODO change to use log
System.out.println("accountActivityWatcher connecting");
try{
this.mSocket.connect();
}catch(Exception e){
//TODO change exception handler
System.out.println("accountActivityWatcher exception " + e.getMessage());
if(this.mSocket == null || !this.mSocket.connected()) {
this.mSocket.connect();
}
}catch(Exception ignore){
}
}

View File

@ -22,29 +22,21 @@ public class BroadcastTransaction extends Thread implements Callback<Txi> {
* The serviceGenerator to call
*/
private InsightApiServiceGenerator mServiceGenerator;
/**
* This app context, used to save on the DB
*/
private Context mContext;
/**
* The account who sign the transaction
*/
private GeneralCoinAccount mAccount;
private String serverUrl;
private String mPath;
private BroadCastTransactionListener listener;
/**
* Basic Consturctor
* @param RawTx The RawTX in Hex String
* @param account The account who signs the transaction
* @param context This app context
*
*/
public BroadcastTransaction(String RawTx, GeneralCoinAccount account, String serverUrl, Context context){
this.serverUrl = serverUrl;
public BroadcastTransaction(String RawTx, String serverUrl, String path, BroadCastTransactionListener listener){
this.mServiceGenerator = new InsightApiServiceGenerator(serverUrl);
this.mContext = context;
this.mRawTx = RawTx;
this.mAccount = account;
this.listener = listener;
this.mPath = path;
}
/**
@ -54,13 +46,9 @@ public class BroadcastTransaction extends Thread implements Callback<Txi> {
@Override
public void onResponse(Call<Txi> call, Response<Txi> response) {
if (response.isSuccessful()) {
//TODO invalidated send
//TODO call getTransactionData
GetTransactionData trData = new GetTransactionData(response.body().txid,this.mAccount, this.serverUrl, this.mContext);
trData.start();
listener.onSuccess();
} else {
System.out.println("SENDTEST: not succesful " + response.message());
//TODO change how to handle invalid transaction
listener.onFailure(response.message());
}
}
@ -69,8 +57,7 @@ public class BroadcastTransaction extends Thread implements Callback<Txi> {
*/
@Override
public void onFailure(Call<Txi> call, Throwable t) {
//TODO change how to handle invalid transaction
System.out.println("SENDTEST: sendError " + t.getMessage() );
listener.onConnecitonFailure();
}
/**
@ -78,8 +65,19 @@ public class BroadcastTransaction extends Thread implements Callback<Txi> {
*/
@Override
public void run() {
InsightApiService service = this.mServiceGenerator.getService(InsightApiService.class);
Call<Txi> broadcastTransaction = service.broadcastTransaction(InsightApiConstants.getPath(this.mAccount.getCryptoCoin()),this.mRawTx);
broadcastTransaction.enqueue(this);
try {
InsightApiService service = this.mServiceGenerator.getService(InsightApiService.class);
Call<Txi> broadcastTransaction = service.broadcastTransaction(this.mPath, this.mRawTx);
broadcastTransaction.enqueue(this);
}catch(Exception e){
e.printStackTrace();
}
}
public interface BroadCastTransactionListener{
void onSuccess();
void onFailure(String msg);
void onConnecitonFailure();
}
}

View File

@ -20,6 +20,8 @@ import retrofit2.Response;
public abstract class GetEstimateFee {
private static String PATH = "api";
/**
* The funciton to get the rate for the transaction be included in the next 2 blocks
* @param serverUrl The url of the insight server
@ -29,28 +31,32 @@ public abstract class GetEstimateFee {
try {
InsightApiServiceGenerator serviceGenerator = new InsightApiServiceGenerator(serverUrl);
InsightApiService service = serviceGenerator.getService(InsightApiService.class);
Call<JsonObject> call = service.estimateFee(serverUrl);
final JsonObject answer = new JsonObject();
Call<JsonObject> call = service.estimateFee(PATH);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
listener.estimateFee((long) (answer.get("answer").getAsDouble()));
try {
listener.estimateFee((double) (response.body().get("2").getAsDouble()));
}catch (Exception e){
e.printStackTrace();
listener.fail();
}
}
@Override
public void onFailure(Call<JsonObject> call, Throwable t) {
listener.fail();
listener.estimateFee(-1);
}
});
}catch(Exception e){
e.printStackTrace();
listener.fail();
}
}
public static interface estimateFeeListener{
public void estimateFee(long value);
public void estimateFee(double value);
public void fail();
}

View File

@ -0,0 +1,43 @@
package cy.agorise.crystalwallet.apigenerator.insightapi;
import com.google.gson.JsonObject;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class GetGenesisBlock {
private static String PATH = "api";
public GetGenesisBlock(String serverUrl, final genesisBlockListener listener) {
try {
InsightApiServiceGenerator serviceGenerator = new InsightApiServiceGenerator(serverUrl);
InsightApiService service = serviceGenerator.getService(InsightApiService.class);
Call<JsonObject> call = service.genesisBlock(PATH);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
try {
listener.genesisBlock(response.body().get("blockHash").getAsString());
}catch(Exception e){
e.printStackTrace();
listener.fail();
}
}
@Override
public void onFailure(Call<JsonObject> call, Throwable t) {
listener.fail();
}
});
}catch(Exception e){
listener.fail();
}
}
public interface genesisBlockListener{
void genesisBlock(String value);
void fail();
}
}

View File

@ -1,20 +1,15 @@
package cy.agorise.crystalwallet.apigenerator.insightapi;
import android.content.Context;
import android.util.Log;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import cy.agorise.crystalwallet.apigenerator.InsightApiGenerator;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.AddressTxi;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Txi;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Vin;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Vout;
import cy.agorise.crystalwallet.models.GTxIO;
import cy.agorise.crystalwallet.models.GeneralCoinAccount;
import cy.agorise.crystalwallet.models.GeneralCoinAddress;
import cy.agorise.crystalwallet.models.GeneralTransaction;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.manager.GeneralAccountManager;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@ -25,43 +20,40 @@ import retrofit2.Response;
*/
public class GetTransactionByAddress extends Thread implements Callback<AddressTxi> {
/**
* The account to be query
*/
private GeneralCoinAccount mAccount;
/**
* The list of address to query
*/
private List<GeneralCoinAddress> mAddresses = new ArrayList<>();
private List<String> mAddresses = new ArrayList<>();
/**
* The serviceGenerator to call
*/
private InsightApiServiceGenerator mServiceGenerator;
/**
* This app context, used to save on the DB
*/
private Context mContext;
private String serverUrl;
private String mServerUrl;
private String mPath;
private CryptoCoin cryptoNet;
private boolean inProcess = false;
private InsightApiGenerator.HasTransactionListener listener;
/**
* Basic consturcotr
* @param account The account to be query
* @param context This app context
*/
public GetTransactionByAddress(GeneralCoinAccount account, String serverUrl, Context context) {
this.serverUrl = serverUrl;
this.mAccount = account;
public GetTransactionByAddress(CryptoCoin cryptoNet, String serverUrl, String path, InsightApiGenerator.HasTransactionListener listener) {
this.mPath = path;
this.cryptoNet = cryptoNet;
this.mServerUrl = serverUrl;
this.mServiceGenerator = new InsightApiServiceGenerator(serverUrl);
this.mContext = context;
this.listener = listener;
}
/**
* add an address to be query
* @param address the address to be query
*/
public void addAddress(GeneralCoinAddress address) {
public void addAddress(String address) {
this.mAddresses.add(address);
}
@ -73,110 +65,23 @@ public class GetTransactionByAddress extends Thread implements Callback<AddressT
*/
@Override
public void onResponse(Call<AddressTxi> call, Response<AddressTxi> response) {
inProcess = false;
if (response.isSuccessful()) {
boolean changed = false;
AddressTxi addressTxi = response.body();
if(listener != null) {
if (addressTxi.items.length > 0 ) {
listener.hasTransaction(true);
}else{
listener.hasTransaction(false);
}
}
for (Txi txi : addressTxi.items) {
GeneralCoinAccount tempAccount = null;
GeneralTransaction transaction = new GeneralTransaction();
transaction.setAccount(this.mAccount);
transaction.setTxid(txi.txid);
transaction.setBlock(txi.blockheight);
transaction.setDate(new Date(txi.time * 1000));
transaction.setFee((long) (txi.fee * Math.pow(10,this.mAccount.getCryptoCoin().getPrecision())));
transaction.setConfirm(txi.confirmations);
transaction.setType(this.mAccount.getCryptoCoin());
transaction.setBlockHeight(txi.blockheight);
for (Vin vin : txi.vin) {
GTxIO input = new GTxIO();
input.setAmount((long) (vin.value * Math.pow(10,this.mAccount.getCryptoCoin().getPrecision())));
input.setTransaction(transaction);
input.setOut(true);
input.setType(this.mAccount.getCryptoCoin());
String addr = vin.addr;
input.setAddressString(addr);
input.setIndex(vin.n);
input.setScriptHex(vin.scriptSig.hex);
input.setOriginalTxid(vin.txid);
for (GeneralCoinAddress address : this.mAddresses) {
if (address.getAddressString(this.mAccount.getNetworkParam()).equals(addr)) {
input.setAddress(address);
tempAccount = address.getAccount();
if (!address.hasTransactionOutput(input, this.mAccount.getNetworkParam())) {
address.getTransactionOutput().add(input);
}
changed = true;
}
}
transaction.getTxInputs().add(input);
}
for (Vout vout : txi.vout) {
if(vout.scriptPubKey.addresses == null || vout.scriptPubKey.addresses.length <= 0){
// The address is null, this must be a memo
String hex = vout.scriptPubKey.hex;
int opReturnIndex = hex.indexOf("6a");
if(opReturnIndex >= 0) {
byte[] memoBytes = new byte[Integer.parseInt(hex.substring(opReturnIndex+2,opReturnIndex+4),16)];
for(int i = 0; i < memoBytes.length;i++){
memoBytes[i] = Byte.parseByte(hex.substring(opReturnIndex+4+(i*2),opReturnIndex+6+(i*2)),16);
}
transaction.setMemo(new String(memoBytes));
}
}else {
GTxIO output = new GTxIO();
output.setAmount((long) (vout.value * Math.pow(10, this.mAccount.getCryptoCoin().getPrecision())));
output.setTransaction(transaction);
output.setOut(false);
output.setType(this.mAccount.getCryptoCoin());
String addr = vout.scriptPubKey.addresses[0];
output.setAddressString(addr);
output.setIndex(vout.n);
output.setScriptHex(vout.scriptPubKey.hex);
for (GeneralCoinAddress address : this.mAddresses) {
if (address.getAddressString(this.mAccount.getNetworkParam()).equals(addr)) {
output.setAddress(address);
tempAccount = address.getAccount();
if (!address.hasTransactionInput(output, this.mAccount.getNetworkParam())) {
address.getTransactionInput().add(output);
}
changed = true;
}
}
transaction.getTxOutputs().add(output);
}
}
if(txi.txlock && txi.confirmations< this.mAccount.getCryptoNet().getConfirmationsNeeded()){
transaction.setConfirm(this.mAccount.getCryptoNet().getConfirmationsNeeded());
}
//TODO database
/*SCWallDatabase db = new SCWallDatabase(this.mContext);
long idTransaction = db.getGeneralTransactionId(transaction);
if (idTransaction == -1) {
db.putGeneralTransaction(transaction);
} else {
transaction.setId(idTransaction);
db.updateGeneralTransaction(transaction);
}*/
if (tempAccount != null && transaction.getConfirm() < this.mAccount.getCryptoNet().getConfirmationsNeeded()) {
new GetTransactionData(transaction.getTxid(), tempAccount, this.serverUrl, this.mContext, true).start();
}
for (GeneralCoinAddress address : this.mAddresses) {
if (address.updateTransaction(transaction)) {
break;
}
}
GeneralAccountManager.getAccountManager(this.cryptoNet).processTxi(txi);
}
if(changed) {
this.mAccount.balanceChange();
}
}else{
listener.hasTransaction(false);
}
}
@ -187,6 +92,7 @@ public class GetTransactionByAddress extends Thread implements Callback<AddressT
*/
@Override
public void onFailure(Call<AddressTxi> call, Throwable t) {
inProcess = false;
Log.e("GetTransactionByAddress", "Error in json format");
}
@ -195,14 +101,15 @@ public class GetTransactionByAddress extends Thread implements Callback<AddressT
*/
@Override
public void run() {
if (this.mAddresses.size() > 0) {
if (this.mAddresses.size() > 0 && !inProcess) {
inProcess = true;
StringBuilder addressToQuery = new StringBuilder();
for (GeneralCoinAddress address : this.mAddresses) {
addressToQuery.append(address.getAddressString(this.mAccount.getNetworkParam())).append(",");
for (String address : this.mAddresses) {
addressToQuery.append(address).append(",");
}
addressToQuery.deleteCharAt(addressToQuery.length() - 1);
InsightApiService service = this.mServiceGenerator.getService(InsightApiService.class);
Call<AddressTxi> addressTxiCall = service.getTransactionByAddress(InsightApiConstants.getPath(this.mAccount.getCryptoCoin()),addressToQuery.toString());
Call<AddressTxi> addressTxiCall = service.getTransactionByAddress(this.mPath,addressToQuery.toString());
addressTxiCall.enqueue(this);
}
}

View File

@ -7,6 +7,8 @@ import java.util.Date;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Txi;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Vin;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Vout;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.manager.GeneralAccountManager;
import cy.agorise.crystalwallet.models.GTxIO;
import cy.agorise.crystalwallet.models.GeneralCoinAccount;
import cy.agorise.crystalwallet.models.GeneralCoinAddress;
@ -20,10 +22,6 @@ import retrofit2.Response;
*/
public class GetTransactionData extends Thread implements Callback<Txi> {
/**
* The account to be query
*/
private final GeneralCoinAccount mAccount;
/**
* The transaction txid to be query
*/
@ -32,41 +30,38 @@ public class GetTransactionData extends Thread implements Callback<Txi> {
* The serviceGenerator to call
*/
private InsightApiServiceGenerator mServiceGenerator;
/**
* This app context, used to save on the DB
*/
private Context mContext;
private String mServerUrl;
private String mPath;
/**
* If has to wait for another confirmation
*/
private boolean mMustWait = false;
private CryptoCoin cryptoCoin;
/**
* Constructor used to query for a transaction with unknown confirmations
* @param txid The txid of the transaciton to be query
* @param account The account to be query
* @param context This app Context
*/
public GetTransactionData(String txid, GeneralCoinAccount account,String serverUrl, Context context) {
this(txid, account, serverUrl, context, false);
public GetTransactionData(String txid, String serverUrl, String path, CryptoCoin cryptoCoin) {
this(txid, serverUrl, path, cryptoCoin, false);
}
/**
* Consturctor to be used qhen the confirmations of the transaction are known
* @param txid The txid of the transaciton to be query
* @param account The account to be query
* @param context This app Context
* @param mustWait If there is less confirmation that needed
*/
public GetTransactionData(String txid, GeneralCoinAccount account,String serverUrl, Context context, boolean mustWait) {
public GetTransactionData(String txid, String serverUrl, String path, CryptoCoin cryptoCoin, boolean mustWait) {
this.mPath = path;
this.mServerUrl = serverUrl;
this.mAccount = account;
this.mTxId= txid;
this.mServiceGenerator = new InsightApiServiceGenerator(serverUrl);
this.mContext = context;
this.mMustWait = mustWait;
this.cryptoCoin = cryptoCoin;
}
/**
@ -84,104 +79,19 @@ public class GetTransactionData extends Thread implements Callback<Txi> {
}
InsightApiService service = this.mServiceGenerator.getService(InsightApiService.class);
Call<Txi> txiCall = service.getTransaction(InsightApiConstants.getPath(this.mAccount.getCryptoCoin()),this.mTxId);
Call<Txi> txiCall = service.getTransaction(this.mPath,this.mTxId);
txiCall.enqueue(this);
}
@Override
public void onResponse(Call<Txi> call, Response<Txi> response) {
if (response.isSuccessful()) {
Txi txi = response.body();
GeneralTransaction transaction = new GeneralTransaction();
transaction.setAccount(this.mAccount);
transaction.setTxid(txi.txid);
transaction.setBlock(txi.blockheight);
transaction.setDate(new Date(txi.time * 1000));
transaction.setFee((long) (txi.fee * Math.pow(10,this.mAccount.getCryptoCoin().getPrecision())));
transaction.setConfirm(txi.confirmations);
transaction.setType(this.mAccount.getCryptoCoin());
transaction.setBlockHeight(txi.blockheight);
for (Vin vin : txi.vin) {
GTxIO input = new GTxIO();
input.setAmount((long) (vin.value * Math.pow(10,this.mAccount.getCryptoCoin().getPrecision())));
input.setTransaction(transaction);
input.setOut(true);
input.setType(this.mAccount.getCryptoCoin());
String addr = vin.addr;
input.setAddressString(addr);
input.setIndex(vin.n);
input.setScriptHex(vin.scriptSig.hex);
input.setOriginalTxid(vin.txid);
for (GeneralCoinAddress address : this.mAccount.getAddresses()) {
if (address.getAddressString(this.mAccount.getNetworkParam()).equals(addr)) {
input.setAddress(address);
if (!address.hasTransactionOutput(input, this.mAccount.getNetworkParam())) {
address.getTransactionOutput().add(input);
}
}
}
transaction.getTxInputs().add(input);
}
for (Vout vout : txi.vout) {
if(vout.scriptPubKey.addresses == null || vout.scriptPubKey.addresses.length <= 0){
// The address is null, this must be a memo
String hex = vout.scriptPubKey.hex;
int opReturnIndex = hex.indexOf("6a");
if(opReturnIndex >= 0) {
byte[] memoBytes = new byte[Integer.parseInt(hex.substring(opReturnIndex+2,opReturnIndex+4),16)];
for(int i = 0; i < memoBytes.length;i++){
memoBytes[i] = Byte.parseByte(hex.substring(opReturnIndex+4+(i*2),opReturnIndex+6+(i*2)),16);
}
transaction.setMemo(new String(memoBytes));
System.out.println("Memo read : " + transaction.getMemo()); //TODO log this line
}
}else {
GTxIO output = new GTxIO();
output.setAmount((long) (vout.value * Math.pow(10, this.mAccount.getCryptoCoin().getPrecision())));
output.setTransaction(transaction);
output.setOut(false);
output.setType(this.mAccount.getCryptoCoin());
String addr = vout.scriptPubKey.addresses[0];
output.setAddressString(addr);
output.setIndex(vout.n);
output.setScriptHex(vout.scriptPubKey.hex);
for (GeneralCoinAddress address : this.mAccount.getAddresses()) {
if (address.getAddressString(this.mAccount.getNetworkParam()).equals(addr)) {
output.setAddress(address);
if (!address.hasTransactionInput(output, this.mAccount.getNetworkParam())) {
address.getTransactionInput().add(output);
}
}
}
transaction.getTxOutputs().add(output);
}
}
// This is for features like dash instantSend
if(txi.txlock && txi.confirmations< this.mAccount.getCryptoNet().getConfirmationsNeeded()){
transaction.setConfirm(this.mAccount.getCryptoNet().getConfirmationsNeeded());
}
//TODO database
/*SCWallDatabase db = new SCWallDatabase(this.mContext);
long idTransaction = db.getGeneralTransactionId(transaction);
if (idTransaction == -1) {
db.putGeneralTransaction(transaction);
} else {
transaction.setId(idTransaction);
db.updateGeneralTransaction(transaction);
}*/
this.mAccount.updateTransaction(transaction);
this.mAccount.balanceChange();
if (transaction.getConfirm() < this.mAccount.getCryptoNet().getConfirmationsNeeded()) {
GeneralAccountManager.getAccountManager(this.cryptoCoin).processTxi(txi);
if (txi.confirmations < this.cryptoCoin.getCryptoNet().getConfirmationsNeeded()) {
//If transaction weren't confirmed, add the transaction to watch for change on the confirmations
new GetTransactionData(this.mTxId, this.mAccount, this.mServerUrl, this.mContext, true).start();
new GetTransactionData(this.mTxId, this.mServerUrl, this.mPath, this.cryptoCoin, true).start();
}
}
}

View File

@ -49,4 +49,7 @@ interface InsightApiService {
@GET("{path}/utils/estimatefee?nbBlocks=2")
Call<JsonObject> estimateFee(@Path(value = "path", encoded = true) String path);
@GET("{path}/block-index/0")
Call<JsonObject> genesisBlock(@Path(value = "path", encoded = true) String path);
}

View File

@ -97,8 +97,8 @@ class InsightApiServiceGenerator {
return chain.proceed(request);
}
});
sClientBuilder.readTimeout(5, TimeUnit.MINUTES);
sClientBuilder.connectTimeout(5, TimeUnit.MINUTES);
sClientBuilder.readTimeout(30, TimeUnit.SECONDS);
sClientBuilder.connectTimeout(30, TimeUnit.SECONDS);
OkHttpClient client = sClientBuilder.build();
Retrofit retrofit = sBuilder.client(client).build();
return retrofit.create(serviceClass);

View File

@ -17,6 +17,7 @@ import cy.agorise.crystalwallet.dao.CrystalDatabase;
import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.models.BitsharesAsset;
import cy.agorise.crystalwallet.models.BitsharesAssetInfo;
import cy.agorise.crystalwallet.models.CryptoCurrency;
import cy.agorise.crystalwallet.models.CryptoCurrencyEquivalence;
import cy.agorise.crystalwallet.models.GeneralSetting;
import cy.agorise.crystalwallet.network.CryptoNetManager;
@ -49,6 +50,27 @@ public class CrystalApplication extends Application {
//This is for testing the equivalent values on the testnet TODO remove
public static BitsharesAsset bitEURAsset = new BitsharesAsset("EUR",4,"1.3.120",BitsharesAsset.Type.SMART_COIN);
public static final String BITCOIN_SERVER_URLS[] ={
"https://testnet.blockexplorer.com/",
"https://test-insight.bitpay.com",
//"https://insight.bitpay.com/"
};
public static final CryptoCurrency BITCOIN_CURRENCY = new CryptoCurrency("BTC",CryptoNet.BITCOIN,8);
public static String STEEM_URL[] =
{
"https://api.steemit.com",
"https://api.steemitdev.com",
"https://api.steemitstage.com",
"https://api.steem.house",
"https://appbasetest.timcliff.com",
};
@Override
public void onCreate() {
super.onCreate();
@ -62,10 +84,10 @@ public class CrystalApplication extends Application {
//This is for testing the equivalent values on the testnet TODO remove
if(db.bitsharesAssetDao().getBitsharesAssetInfoById(bitEURAsset.getBitsharesId())== null){
if(db.cryptoCurrencyDao().getByName(bitEURAsset.getName())== null){
if(db.cryptoCurrencyDao().getByName(bitEURAsset.getName(),bitEURAsset.getCryptoNet().name())== null){
db.cryptoCurrencyDao().insertCryptoCurrency(bitEURAsset);
}
long idCurrency = db.cryptoCurrencyDao().getByName(bitEURAsset.getName()).getId();
long idCurrency = db.cryptoCurrencyDao().getByName(bitEURAsset.getName(),bitEURAsset.getCryptoNet().name()).getId();
BitsharesAssetInfo info = new BitsharesAssetInfo(bitEURAsset);
info.setCryptoCurrencyId(idCurrency);
db.bitsharesAssetDao().insertBitsharesAssetInfo(info);
@ -74,10 +96,10 @@ public class CrystalApplication extends Application {
//This is for testing the equivalent values on the testnet TODO remove
if(db.bitsharesAssetDao().getBitsharesAssetInfoById(bitUSDAsset.getBitsharesId())== null){
if(db.cryptoCurrencyDao().getByName(bitUSDAsset.getName())== null){
if(db.cryptoCurrencyDao().getByName(bitUSDAsset.getName(),bitUSDAsset.getCryptoNet().name())== null){
db.cryptoCurrencyDao().insertCryptoCurrency(bitUSDAsset);
}
long idCurrency = db.cryptoCurrencyDao().getByName(bitUSDAsset.getName()).getId();
long idCurrency = db.cryptoCurrencyDao().getByName(bitUSDAsset.getName(),bitUSDAsset.getCryptoNet().name()).getId();
BitsharesAssetInfo info = new BitsharesAssetInfo(bitUSDAsset);
info.setCryptoCurrencyId(idCurrency);
db.bitsharesAssetDao().insertBitsharesAssetInfo(info);
@ -93,6 +115,16 @@ public class CrystalApplication extends Application {
// TODO and hoop over the urls if no connection can be established
CryptoNetManager.addCryptoNetURL(CryptoNet.BITSHARES,BITSHARES_URL);
//Adding Bitcoin info
CryptoNetManager.addCryptoNetURL(CryptoNet.BITCOIN,BITCOIN_SERVER_URLS);
if(db.cryptoCurrencyDao().getByName(BITCOIN_CURRENCY.getName(),BITCOIN_CURRENCY.getCryptoNet().name())== null){
db.cryptoCurrencyDao().insertCryptoCurrency(BITCOIN_CURRENCY);
}
CryptoNetManager.addCryptoNetURL(CryptoNet.STEEM,STEEM_URL);
GeneralSetting generalSettingPreferredLanguage = db.generalSettingDao().getSettingByName(GeneralSetting.SETTING_NAME_PREFERRED_LANGUAGE);
if (generalSettingPreferredLanguage != null) {
@ -105,6 +137,11 @@ public class CrystalApplication extends Application {
resources.updateConfiguration(configuration, dm);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
Intent intent = new Intent(getApplicationContext(), CrystalWalletService.class);
startService(intent);
}

View File

@ -16,6 +16,8 @@ import java.util.List;
import cy.agorise.crystalwallet.activities.PatternRequestActivity;
import cy.agorise.crystalwallet.activities.PinRequestActivity;
import cy.agorise.crystalwallet.activities.PocketRequestActivity;
import cy.agorise.crystalwallet.fragments.PatternSecurityFragment;
import cy.agorise.crystalwallet.interfaces.OnResponse;
import cy.agorise.crystalwallet.models.GeneralSetting;
import cy.agorise.crystalwallet.notifiers.CrystalWalletNotifier;
import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel;
@ -129,7 +131,7 @@ public class CrystalSecurityMonitor implements Application.ActivityLifecycleCall
public void onActivityStarted(Activity activity) {
if (numStarted == 0) {
if (!actualSecurity().equals("")){
callPasswordRequest(activity);
callPasswordRequest(activity,null);
}
}
numStarted++;
@ -140,18 +142,41 @@ public class CrystalSecurityMonitor implements Application.ActivityLifecycleCall
numStarted--;
if (numStarted == 0) {
if (!actualSecurity().equals("")){
callPasswordRequest(activity);
callPasswordRequest(activity,null);
}
}
}
public void callPasswordRequest(Activity activity){
public void callPasswordRequest(Activity activity, final OnResponse onResponsePattern){
if ((!activity.getIntent().hasExtra("ACTIVITY_TYPE")) || (!activity.getIntent().getStringExtra("ACTIVITY_TYPE").equals("PASSWORD_REQUEST"))) {
Intent intent = null;
if ((this.passwordEncrypted != null) && (!this.passwordEncrypted.equals(""))) {
intent = new Intent(activity, PinRequestActivity.class);
PinRequestActivity.setOnResponse(null);
/*
* Connect error and success listeners
* */
if(onResponsePattern != null){
PinRequestActivity.setOnResponse(onResponsePattern);
}
} else if ((this.patternEncrypted != null) && (!this.patternEncrypted.equals(""))) {
intent = new Intent(activity, PatternRequestActivity.class);
PatternRequestActivity.setOnResponse(null);
/*
* Connect error and success listeners
* */
if(onResponsePattern != null){
PatternRequestActivity.setOnResponse(onResponsePattern);
}
} else {
onResponsePattern.onSuccess();
}
if (intent != null) {
intent.putExtra("ACTIVITY_TYPE", "PASSWORD_REQUEST");
@ -197,7 +222,4 @@ public class CrystalSecurityMonitor implements Application.ActivityLifecycleCall
public void onActivityDestroyed(Activity activity) {
//
}
}
}

View File

@ -32,7 +32,7 @@ public abstract class BitsharesConstant {
//testnet faucet
//public final static String FAUCET_URL = "http://185.208.208.147:5010";
public final static String FAUCET_URL = "https://de.palmpay.io";
public final static String FAUCET_URL = "https://faucet.palmpay.io";
public final static String EQUIVALENT_URL = "wss://bitshares.openledger.info/ws";
public final static BitsharesAsset[] SMARTCOINS = new BitsharesAsset[]{
@ -74,10 +74,10 @@ public abstract class BitsharesConstant {
public static void addSmartCoins(Context context){
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
for(BitsharesAsset smartcoin : SMARTCOINS){
if(db.cryptoCurrencyDao().getByName(smartcoin.getName())== null){
if(db.cryptoCurrencyDao().getByName(smartcoin.getName(),CryptoNet.BITSHARES.name())== null){
db.cryptoCurrencyDao().insertCryptoCurrency(smartcoin);
}
long idCurrency = db.cryptoCurrencyDao().getByName(smartcoin.getName()).getId();
long idCurrency = db.cryptoCurrencyDao().getByName(smartcoin.getName(),CryptoNet.BITSHARES.name()).getId();
BitsharesAssetInfo info = new BitsharesAssetInfo(smartcoin);
info.setCryptoCurrencyId(idCurrency);
db.bitsharesAssetDao().insertBitsharesAssetInfo(info);

View File

@ -0,0 +1,42 @@
package cy.agorise.crystalwallet.dao;
import android.arch.lifecycle.LiveData;
import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Insert;
import android.arch.persistence.room.OnConflictStrategy;
import android.arch.persistence.room.Query;
import cy.agorise.crystalwallet.models.BitcoinAddress;
import cy.agorise.crystalwallet.models.BitcoinTransaction;
import cy.agorise.crystalwallet.models.BitcoinTransactionExtended;
/**
* Created by Henry Varona on 10/17/2018.
*/
@Dao
public interface BitcoinAddressDao {
@Query("SELECT * FROM bitcoin_address")
LiveData<BitcoinAddress> getAll();
@Query("SELECT COUNT(*) FROM bitcoin_address ba WHERE ba.address = :address")
Boolean addressExists(String address);
@Query("SELECT * FROM bitcoin_address ba WHERE ba.address = :address")
BitcoinAddress getdadress(String address);
@Query("SELECT * FROM bitcoin_address ba WHERE ba.address_index = :index and ba.is_change = 'true'")
BitcoinAddress getChangeByIndex(long index);
@Query("SELECT * FROM bitcoin_address ba WHERE ba.address_index = :index and ba.is_change = 'false'")
BitcoinAddress getExternalByIndex(long index);
@Query("SELECT MAX(ba.address_index) FROM bitcoin_address ba WHERE ba.account_id = :accountId and ba.is_change = 'true' ")
long getLastChangeAddress(long accountId);
@Query("SELECT MAX(ba.address_index) FROM bitcoin_address ba WHERE ba.account_id = :accountId and ba.is_change = 'false' ")
long getLastExternalAddress(long accountId);
@Insert(onConflict = OnConflictStrategy.REPLACE)
public long[] insertBitcoinAddresses(BitcoinAddress... addresses);
}

View File

@ -0,0 +1,41 @@
package cy.agorise.crystalwallet.dao;
import android.arch.lifecycle.LiveData;
import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Insert;
import android.arch.persistence.room.OnConflictStrategy;
import android.arch.persistence.room.Query;
import java.util.List;
import cy.agorise.crystalwallet.models.BitcoinTransaction;
import cy.agorise.crystalwallet.models.BitcoinTransactionExtended;
import cy.agorise.crystalwallet.models.BitcoinTransactionGTxIO;
/**
* Created by Henry Varona on 10/02/2018.
*/
@Dao
public interface BitcoinTransactionDao {
@Query("SELECT * FROM crypto_coin_transaction cct, bitcoin_transaction bt WHERE bt.crypto_coin_transaction_id = cct.id")
LiveData<BitcoinTransactionExtended> getAll();
@Query("SELECT * FROM bitcoin_transaction bt WHERE bt.tx_id = :txid")
List<BitcoinTransaction> getTransactionsByTxid(String txid);
@Query("SELECT * FROM bitcoin_transaction bt WHERE bt.crypto_coin_transaction_id = :idCryptoCoinTransaction")
BitcoinTransaction getBitcoinTransactionByCryptoCoinTransaction(long idCryptoCoinTransaction);
@Query("SELECT * FROM bitcoin_transaction_gt_io bt WHERE bt.bitcoin_transaction_id= :idBitcoinTransaction")
List<BitcoinTransactionGTxIO> getGtxIOByTransaction(long idBitcoinTransaction);
@Query("SELECT * FROM bitcoin_transaction_gt_io bt WHERE bt.address= :address")
List<BitcoinTransactionGTxIO> getGtxIOByAddress(String address);
@Insert(onConflict = OnConflictStrategy.REPLACE)
public long[] insertBitcoinTransaction(BitcoinTransaction... transactions);
@Insert(onConflict = OnConflictStrategy.REPLACE)
public long[] insertBitcoinTransactionGTxIO(BitcoinTransactionGTxIO... transactiongtxios);
}

View File

@ -23,6 +23,9 @@ public interface CryptoCurrencyDao {
@Query("SELECT * FROM crypto_currency WHERE id = :id")
CryptoCurrency getById(long id);
@Query("SELECT * FROM crypto_currency WHERE id = :id")
LiveData<CryptoCurrency> getLDById(long id);
@Query("SELECT * FROM crypto_currency WHERE name = :name AND crypto_net = :cryptoNet")
CryptoCurrency getByNameAndCryptoNet(String name,String cryptoNet);
@ -32,8 +35,8 @@ public interface CryptoCurrencyDao {
@Query("SELECT * FROM crypto_currency WHERE name = :name")
LiveData<CryptoCurrency> getLiveDataByName(String name);
@Query("SELECT * FROM crypto_currency WHERE name = :name")
CryptoCurrency getByName(String name);
@Query("SELECT * FROM crypto_currency WHERE name = :name and crypto_net = :cryptoNet")
CryptoCurrency getByName(String name, String cryptoNet);
@Insert(onConflict = OnConflictStrategy.IGNORE)
public long[] insertCryptoCurrency(CryptoCurrency... currencies);

View File

@ -29,6 +29,9 @@ public interface CryptoNetAccountDao {
@Query("SELECT cna.* FROM crypto_net_account cna WHERE seed_id = :seedId")
List<CryptoNetAccount> getAllCryptoNetAccountBySeed( long seedId);
@Query("SELECT cna.* FROM crypto_net_account cna WHERE crypto_net == 'BITCOIN'")
LiveData<List<CryptoNetAccount>> getAllBitcoins();
@Query("SELECT * FROM crypto_net_account WHERE id = :accountId")
LiveData<CryptoNetAccount> getByIdLiveData( long accountId);

View File

@ -10,6 +10,9 @@ import android.content.Context;
import cy.agorise.crystalwallet.dao.converters.Converters;
import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.BitcoinAddress;
import cy.agorise.crystalwallet.models.BitcoinTransaction;
import cy.agorise.crystalwallet.models.BitcoinTransactionGTxIO;
import cy.agorise.crystalwallet.models.BitsharesAccountNameCache;
import cy.agorise.crystalwallet.models.BitsharesAssetInfo;
import cy.agorise.crystalwallet.models.Contact;
@ -39,8 +42,11 @@ import cy.agorise.crystalwallet.models.GrapheneAccountInfo;
BitsharesAssetInfo.class,
BitsharesAccountNameCache.class,
CryptoCurrencyEquivalence.class,
GeneralSetting.class
}, version = 4, exportSchema = false)
GeneralSetting.class,
BitcoinTransaction.class,
BitcoinTransactionGTxIO.class,
BitcoinAddress.class
}, version = 6, exportSchema = false)
@TypeConverters({Converters.class})
public abstract class CrystalDatabase extends RoomDatabase {
@ -57,6 +63,8 @@ public abstract class CrystalDatabase extends RoomDatabase {
public abstract BitsharesAccountNameCacheDao bitsharesAccountNameCacheDao();
public abstract CryptoCurrencyEquivalenceDao cryptoCurrencyEquivalenceDao();
public abstract GeneralSettingDao generalSettingDao();
public abstract BitcoinTransactionDao bitcoinTransactionDao();
public abstract BitcoinAddressDao bitcoinAddressDao();
public static CrystalDatabase getAppDatabase(Context context) {
if (instance == null) {
@ -64,8 +72,10 @@ public abstract class CrystalDatabase extends RoomDatabase {
Room.databaseBuilder(context,
CrystalDatabase.class, "CrystalWallet.db")
.allowMainThreadQueries()
.addMigrations(MIGRATION_2_3)
.addMigrations(MIGRATION_3_4)
//.addMigrations(MIGRATION_2_3)
//.addMigrations(MIGRATION_3_4)
//.addMigrations(MIGRATION_4_5)
//.addMigrations(MIGRATION_5_6)
.build();
}
return instance;
@ -91,4 +101,41 @@ public abstract class CrystalDatabase extends RoomDatabase {
}
};
static final Migration MIGRATION_4_5 = new Migration(4, 5) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE bitcoin_transaction ("
+"crypto_coin_transaction_id INTEGER PRIMARY KEY NOT NULL,"
+"tx_id TEXT NOT NULL,"
+"block INTEGER NOT NULL,"
+"fee INTEGER NOT NULL,"
+"confirmations INTEGER NOT NULL,"
+"FOREIGN KEY (crypto_coin_transaction_id) REFERENCES crypto_coin_transaction(id) ON DELETE CASCADE)");
database.execSQL("CREATE TABLE bitcoin_transaction_gt_io ("
+"bitcoin_transaction_id INTEGER NOT NULL,"
+"io_index INTEGER NOT NULL,"
+"address TEXT,"
+"is_output INTEGER NOT NULL,"
+"amount INTEGER NOT NULL,"
+"script_hex TEXT,"
+"original_txid TEXT,"
+"PRIMARY KEY (bitcoin_transaction_id, io_index, is_output),"
+"FOREIGN KEY (bitcoin_transaction_id) REFERENCES bitcoin_transaction(crypto_coin_transaction_id) ON DELETE CASCADE)");
}
};
static final Migration MIGRATION_5_6 = new Migration(5, 6) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE bitcoin_address ("
+"account_id INTEGER NOT NULL,"
+"address_index INTEGER NOT NULL,"
+"is_change INTEGER NOT NULL,"
+"address TEXT NOT NULL,"
+"PRIMARY KEY (account_id, address_index, is_change),"
+"FOREIGN KEY (account_id) REFERENCES crypto_net_account(id) ON DELETE CASCADE)");
}
};
}

View File

@ -51,7 +51,10 @@ public interface TransactionDao {
List<CryptoCoinTransaction> getByIdAccount(long idAccount);
@Query("SELECT * FROM crypto_coin_transaction WHERE id = :id")
LiveData<CryptoCoinTransaction> getById(long id);
LiveData<CryptoCoinTransaction> getByIdLiveData(long id);
@Query("SELECT * FROM crypto_coin_transaction WHERE id = :id")
CryptoCoinTransaction getById(long id);
@Query("SELECT * FROM crypto_coin_transaction WHERE date = :date and 'from' = :from and 'to' = :to and amount = :amount ")
CryptoCoinTransaction getByTransaction(Date date, String from, String to, long amount);

View File

@ -1,5 +1,7 @@
package cy.agorise.crystalwallet.enums;
import org.bitcoinj.core.NetworkParameters;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@ -11,21 +13,26 @@ import java.util.List;
*/
public enum CryptoCoin implements Serializable {
BITCOIN(CryptoNet.BITCOIN,"BTC",8),
BITCOIN_TEST(CryptoNet.BITCOIN_TEST,"BTC",8),
LITECOIN(CryptoNet.LITECOIN,"LTC",8),
DASH(CryptoNet.DASH,"DASH",8),
DOGECOIN(CryptoNet.DOGECOIN,"DOGE",8),
BITSHARES(CryptoNet.BITSHARES,"BTS",5);
BITCOIN(CryptoNet.BITCOIN,"BTC",8,0,NetworkParameters.fromID(NetworkParameters.ID_TESTNET)),
BITCOIN_TEST(CryptoNet.BITCOIN_TEST,"BTC",8,1,NetworkParameters.fromID(NetworkParameters.ID_TESTNET)),
LITECOIN(CryptoNet.LITECOIN,"LTC",8,2,null),
DASH(CryptoNet.DASH,"DASH",8,5,null),
DOGECOIN(CryptoNet.DOGECOIN,"DOGE",8,3,null),
BITSHARES(CryptoNet.BITSHARES,"BTS",5,0,null),
STEEM(CryptoNet.STEEM,"BTS",5,0,null);
protected CryptoNet cryptoNet;
protected String label;
protected int precision;
protected int coinNumber;
protected NetworkParameters parameters;
CryptoCoin(CryptoNet cryptoNet, String label, int precision){
CryptoCoin(CryptoNet cryptoNet, String label, int precision, int coinNumber, NetworkParameters parameters){
this.cryptoNet = cryptoNet;
this.label = label;
this.precision = precision;
this.coinNumber = coinNumber;
this.parameters = parameters;
}
@ -38,6 +45,14 @@ public enum CryptoCoin implements Serializable {
public int getPrecision(){
return this.precision;
}
public NetworkParameters getParameters() {
return parameters;
}
public int getCoinNumber() {
return coinNumber;
}
public static List<CryptoCoin> getByCryptoNet(CryptoNet cryptoNet){
List<CryptoCoin> result = new ArrayList<CryptoCoin>();

View File

@ -4,6 +4,8 @@ import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import cy.agorise.crystalwallet.R;
/**
* CryptoNet Enumeration, a Crypto Net is define as the net where a CryptoCoin works, iniside the
* CrypotNet is where the transaction and balance works, using the CryptoCoin Assets
@ -11,7 +13,14 @@ import java.util.Map;
* Created by Henry Varona on 12/9/2017.
*/
public enum CryptoNet implements Serializable {
UNKNOWN("UNKNOWN",6,-1), BITCOIN("BITCOIN",6,1), BITCOIN_TEST("BITCOIN(TEST)",6,2), LITECOIN("LITECOIN",6,3), DASH("DASH",6,5), DOGECOIN("DOGECOIN",6,4), BITSHARES("BITSHARES",1,6), STEEM("STEEM",1,7);
UNKNOWN("UNKNOWN",6,-1,android.R.drawable .ic_menu_help),
BITCOIN("BITCOIN",6,1,R.drawable.coin_icon_bitcoin),
BITCOIN_TEST("BITCOIN(TEST)",6,2,R.drawable.coin_icon_bitcoin),
LITECOIN("LITECOIN",6,3,R.drawable.coin_icon_litecoin),
DASH("DASH",6,5,R.drawable.coin_icon_dash),
DOGECOIN("DOGECOIN",6,4,R.drawable.coin_icon_doge),
BITSHARES("BITSHARES",1,6,R.drawable.bts),
STEEM("STEEM",1,7,R.drawable.coin_icon_steem);
protected String label;
@ -19,6 +28,8 @@ public enum CryptoNet implements Serializable {
protected int bip44Index;
protected int iconImageResource;
private static Map<Integer, CryptoNet> bip44Map = new HashMap<Integer, CryptoNet>();
static {
for (CryptoNet cryptoNetEnum : CryptoNet.values()) {
@ -26,10 +37,11 @@ public enum CryptoNet implements Serializable {
}
}
CryptoNet(String label,int confirmationsNeeded, int bip44Index){
CryptoNet(String label,int confirmationsNeeded, int bip44Index, int iconImageResource){
this.label = label;
this.confirmationsNeeded = confirmationsNeeded;
this.bip44Index = bip44Index;
this.iconImageResource = iconImageResource;
}
public String getLabel(){
@ -44,6 +56,10 @@ public enum CryptoNet implements Serializable {
return this.bip44Index;
}
public int getIconImageResource() {
return this.iconImageResource;
}
public static CryptoNet fromBip44Index(int index){
if (bip44Map.containsKey(index)) {
return bip44Map.get(index);

View File

@ -6,5 +6,6 @@ package cy.agorise.crystalwallet.enums;
public enum SeedType {
BIP39,
BRAINKEY
BRAINKEY,
WIF
}

View File

@ -7,8 +7,11 @@ import android.text.SpannableStringBuilder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import com.thekhaeng.pushdownanim.PushDownAnim;
import butterknife.BindView;
import butterknife.ButterKnife;
import cy.agorise.crystalwallet.R;
@ -41,6 +44,22 @@ public class AccountsSettingsFragment extends Fragment {
@BindView(R.id.tvRemove)
public TextView tvRemove;
@BindView(R.id.btnUpgrade)
public Button btnUpgrade;
@BindView(R.id.btnImport)
public Button btnImport;
@BindView(R.id.btnRefresh)
public Button btnRefresh;
@BindView(R.id.btnRemove)
public Button btnRemove;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@ -53,6 +72,36 @@ public class AccountsSettingsFragment extends Fragment {
tvRefresh.setText(makeFirstWordsBold(getResources().getString(R.string.refresh_description)));
tvRemove.setText(makeFirstWordsBold(getResources().getString(R.string.remove_description)));
/*
* Integration of library with button efects
* */
PushDownAnim.setPushDownAnimTo(btnUpgrade)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
}
} );
PushDownAnim.setPushDownAnimTo(btnImport)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
}
} );
PushDownAnim.setPushDownAnimTo(btnRefresh)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
}
} );
PushDownAnim.setPushDownAnimTo(btnRemove)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
}
} );
return v;
}

View File

@ -1,14 +1,21 @@
package cy.agorise.crystalwallet.fragments;
import android.Manifest;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -16,6 +23,8 @@ import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.thekhaeng.pushdownanim.PushDownAnim;
import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
@ -39,6 +48,11 @@ import cy.agorise.crystalwallet.requestmanagers.FileServiceRequests;
*/
public class BackupsSettingsFragment extends Fragment{
private static final int PERMISSION_REQUEST_CODE = 1;
public BackupsSettingsFragment() {
// Required empty public constructor
}
@ -65,6 +79,7 @@ public class BackupsSettingsFragment extends Fragment{
@BindView(R.id.btnBinFile)
public Button btnBinFile;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@ -72,6 +87,25 @@ public class BackupsSettingsFragment extends Fragment{
View v = inflater.inflate(R.layout.fragment_backups_settings, container, false);
ButterKnife.bind(this, v);
/*
* Integration of library with button efects
* */
PushDownAnim.setPushDownAnimTo(btnBinFile)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
makeBackupFile();
}
} );
PushDownAnim.setPushDownAnimTo(btnBrainkey)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
btnBrainOnClick();
}
} );
tvBinFile.setText(makeFirstWordsBold(getResources().getString(R.string.bin_file_description)));
tvBrainkey.setText(makeFirstWordsBold(getResources().getString(R.string.brainkey_description)));
tvWIFKey.setText(makeFirstWordsBold(getResources().getString(R.string.wif_key_description)));
@ -92,12 +126,40 @@ public class BackupsSettingsFragment extends Fragment{
public void btnBrainOnClick(){
Intent intent = new Intent(getContext(), BackupSeedActivity.class);
intent. putExtra("SEED_ID","");
startActivity(intent);
}
@OnClick(R.id.btnBinFile)
public void makeBackupFile(){
/*
* Check for WRITE_EXTERNAL_STORAGE permission
* */
if (Build.VERSION.SDK_INT >= 23) {
if (checkPermission()) {
// Code for above or equal 23 API Oriented Device
// Your Permission granted already .Do next code
makeBackupfileAfterPermission();
} else {
requestPermission(); // Code for permission
}
}
else {
// Code for Below 23 API Oriented Device
// Do next code
makeBackupfileAfterPermission();
}
}
private void makeBackupfileAfterPermission(){
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
LiveData<GeneralSetting> generalSettingLD = CrystalDatabase.getAppDatabase(getContext()).generalSettingDao().getByName(GeneralSetting.SETTING_PASSWORD);
@ -105,13 +167,13 @@ public class BackupsSettingsFragment extends Fragment{
generalSettingLD.observe(this, new Observer<GeneralSetting>() {
@Override
public void onChanged(@Nullable GeneralSetting generalSetting) {
String password = "";
if (generalSetting != null) {
password = generalSetting.getValue();
}
final CreateBackupRequest backupFileRequest = new CreateBackupRequest(getContext(), password);
backupFileRequest.setListener(new FileServiceRequestListener() {
@Override
public void onCarryOut() {
@ -132,4 +194,40 @@ public class BackupsSettingsFragment extends Fragment{
});
}
}
private boolean checkPermission() {
int result = ContextCompat.checkSelfPermission(getActivity(), android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (result == PackageManager.PERMISSION_GRANTED) {
return true;
} else {
return false;
}
}
private void requestPermission() {
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
Toast.makeText(getActivity(), getActivity().getResources().getString(R.string.Permision_storage), Toast.LENGTH_LONG).show();
} else {
ActivityCompat.requestPermissions(getActivity(), new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case PERMISSION_REQUEST_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.e("value", "Permission Granted, Now you can use local drive .");
} else {
Log.e("value", "Permission Denied, You cannot use local drive .");
makeBackupfileAfterPermission();
}
break;
}
}
}

View File

@ -3,13 +3,15 @@ package cy.agorise.crystalwallet.fragments;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
@ -17,15 +19,20 @@ import butterknife.BindView;
import butterknife.ButterKnife;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.models.CryptoNetBalance;
import cy.agorise.crystalwallet.viewmodels.CryptoCoinBalanceListViewModel;
import cy.agorise.crystalwallet.viewmodels.CryptoNetBalanceListViewModel;
import cy.agorise.crystalwallet.views.CryptoNetBalanceListView;
import cy.agorise.crystalwallet.views.CryptoNetBalanceListAdapter;
public class BalanceFragment extends Fragment {
CryptoNetBalanceListViewModel cryptoNetBalanceListViewModel;
@BindView(R.id.vCryptoNetBalanceListView)
CryptoNetBalanceListView vCryptoNetBalanceListView;
@BindView(R.id.tvNoBalances)
TextView tvNoBalances;
@BindView(R.id.rvBalances)
RecyclerView rvBalances;
CryptoNetBalanceListAdapter balancesAdapter;
public BalanceFragment() {
// Required empty public constructor
@ -44,22 +51,33 @@ public class BalanceFragment extends Fragment {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_balance, container, false);
ButterKnife.bind(this, view);
cryptoNetBalanceListViewModel = ViewModelProviders.of(this).get(CryptoNetBalanceListViewModel.class);
LiveData<List<CryptoNetBalance>> cryptoNetBalanceData = cryptoNetBalanceListViewModel.getCryptoNetBalanceList();
vCryptoNetBalanceListView.setData(null, this);
// Configure RecyclerView and its adapter
rvBalances.setLayoutManager(new LinearLayoutManager(getContext()));
balancesAdapter = new CryptoNetBalanceListAdapter(this);
rvBalances.setAdapter(balancesAdapter);
final Fragment fragment = this;
//Prevents the UI from an infinite scrolling of balances
rvBalances.setNestedScrollingEnabled(false);
cryptoNetBalanceListViewModel = ViewModelProviders.of(this).get(CryptoNetBalanceListViewModel.class);
final LiveData<List<CryptoNetBalance>> cryptoNetBalanceData = cryptoNetBalanceListViewModel.getCryptoNetBalanceList();
cryptoNetBalanceData.observe(this, new Observer<List<CryptoNetBalance>>() {
@Override
public void onChanged(List<CryptoNetBalance> cryptoNetBalances) {
vCryptoNetBalanceListView.setData(cryptoNetBalances, fragment);
balancesAdapter.submitList(cryptoNetBalances);
if(cryptoNetBalances != null && cryptoNetBalances.size() > 0) {
tvNoBalances.setVisibility(View.INVISIBLE);
} else {
tvNoBalances.setVisibility(View.VISIBLE);
}
}
});

View File

@ -19,6 +19,7 @@ import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.thekhaeng.pushdownanim.PushDownAnim;
import com.vincent.filepicker.Constant;
import com.vincent.filepicker.activity.AudioPickActivity;
import com.vincent.filepicker.filter.entity.AudioFile;
@ -73,6 +74,9 @@ public class BitsharesSettingsFragment extends Fragment {
GrapheneAccountInfo grapheneAccountInfo;
GrapheneAccount grapheneAccount;
public BitsharesSettingsFragment() {
if (getArguments() != null) {
long cryptoNetAcountId = getArguments().getLong("CRYPTO_NET_ACCOUNT_ID", -1);
@ -114,6 +118,17 @@ public class BitsharesSettingsFragment extends Fragment {
View v = inflater.inflate(R.layout.fragment_bitshares_settings, container, false);
ButterKnife.bind(this, v);
/*
* Integration of library with button efects
* */
PushDownAnim.setPushDownAnimTo(btnUpgradeToLtm)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
upgradeAccountToLtm();
}
} );
initAlreadyLtm();
return v;

View File

@ -14,6 +14,7 @@ import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
@ -27,6 +28,9 @@ public class ContactsFragment extends Fragment {
@BindView(R.id.rvContacts)
RecyclerView rvContacts;
@BindView(R.id.tvNoContacts)
TextView tvNoContacts;
ContactListAdapter adapter;
FloatingActionButton fabAddContact;
@ -86,6 +90,13 @@ public class ContactsFragment extends Fragment {
@Override
public void onChanged(@Nullable PagedList<Contact> contacts) {
adapter.submitList(contacts);
if(contacts != null && contacts.size() > 0){
tvNoContacts.setVisibility(View.INVISIBLE);
}
else{
tvNoContacts.setVisibility(View.VISIBLE);
}
}
});

View File

@ -0,0 +1,127 @@
package cy.agorise.crystalwallet.fragments;
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.thekhaeng.pushdownanim.PushDownAnim;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.CryptoNetAccount;
/**
* Created by xd on 12/28/17.
*/
public class CryptoNetAccountActivationSettingsFragment extends Fragment {
@BindView(R.id.tvMnemonic)
TextView tvMnemonic;
@BindView(R.id.btnCopy)
Button btnCopy;
CryptoNetAccount cryptoNetAccount;
AccountSeed accountSeed;
public CryptoNetAccountActivationSettingsFragment() {
if (getArguments() != null) {
long cryptoNetAcountId = getArguments().getLong("CRYPTO_NET_ACCOUNT_ID", -1);
if (cryptoNetAcountId > -1) {
this.cryptoNetAccount = CrystalDatabase.getAppDatabase(getContext()).cryptoNetAccountDao().getById(cryptoNetAcountId);
this.accountSeed = CrystalDatabase.getAppDatabase(getContext()).accountSeedDao().findById(this.cryptoNetAccount.getSeedId());
}
}
// Required empty public constructor
}
public static CryptoNetAccountActivationSettingsFragment newInstance(long cryptoNetAccountId) {
CryptoNetAccountActivationSettingsFragment fragment = new CryptoNetAccountActivationSettingsFragment();
Bundle args = new Bundle();
args.putLong("CRYPTO_NET_ACCOUNT_ID", cryptoNetAccountId);
fragment.setArguments(args);
if (cryptoNetAccountId > -1){
fragment.cryptoNetAccount = CrystalDatabase.getAppDatabase(fragment.getContext()).cryptoNetAccountDao().getById(cryptoNetAccountId);
fragment.accountSeed = CrystalDatabase.getAppDatabase(fragment.getContext()).accountSeedDao().findById(fragment.cryptoNetAccount.getSeedId());
}
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_general_crypto_net_account_settings, container, false);
ButterKnife.bind(this, v);
/*
* Integration of library with button efects
* */
PushDownAnim.setPushDownAnimTo(btnCopy)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
btnCopyClick();
}
} );
initAlreadyLtm();
return v;
}
public void initAlreadyLtm(){
if (this.cryptoNetAccount != null) {
tvMnemonic.setText(this.accountSeed.getMasterSeed());
}
}
/*
* Clic on button copy to clipboard
* */
@OnClick(R.id.btnCopy)
public void btnCopyClick(){
/*
* Save to clipboard the brainkey chain
* */
final Activity activity = getActivity();
ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText(tvMnemonic.getText(), tvMnemonic.getText().toString());
clipboard.setPrimaryClip(clip);
/*
* Success message
* */
Toast.makeText(activity,getResources().getString(R.string.window_seed_toast_clipboard), Toast.LENGTH_SHORT).show();
}
}

View File

@ -0,0 +1,121 @@
package cy.agorise.crystalwallet.fragments;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.CryptoNetSelection;
import cy.agorise.crystalwallet.requestmanagers.CreateBitcoinAccountRequest;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestListener;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequests;
import cy.agorise.crystalwallet.views.CryptoNetSelectionListAdapter;
/**
* Created by xd on 12/28/17.
*/
public class GeneralAccountSeedCoinSettingsFragment extends Fragment implements CryptoNetSelectionListAdapter.CryptoNetSelectionListener {
@BindView(R.id.rvCoinSelection)
RecyclerView rvCoinSelection;
AccountSeed accountSeed;
ArrayList<CryptoNetSelection> cryptoNetSelectionList;
public GeneralAccountSeedCoinSettingsFragment() {
if (getArguments() != null) {
long accountSeedId = getArguments().getLong("SEED_ID", -1);
if (accountSeedId > -1) {
this.accountSeed = CrystalDatabase.getAppDatabase(getContext()).accountSeedDao().findById(accountSeedId);
}
}
cryptoNetSelectionList = new ArrayList<CryptoNetSelection>();
CryptoNetSelection nextCryptoNetSelection;
for (CryptoNet nextCryptoNet : CryptoNet.values()){
if ((nextCryptoNet != CryptoNet.UNKNOWN) && (nextCryptoNet != CryptoNet.BITCOIN_TEST)) {
nextCryptoNetSelection = new CryptoNetSelection(nextCryptoNet, false);
cryptoNetSelectionList.add(nextCryptoNetSelection);
}
}
// Required empty public constructor
}
public static GeneralAccountSeedCoinSettingsFragment newInstance(long accountSeedId) {
GeneralAccountSeedCoinSettingsFragment fragment = new GeneralAccountSeedCoinSettingsFragment();
Bundle args = new Bundle();
args.putLong("SEED_ID", accountSeedId);
fragment.setArguments(args);
if (accountSeedId > -1){
fragment.accountSeed = CrystalDatabase.getAppDatabase(fragment.getContext()).accountSeedDao().findById(accountSeedId);
}
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_general_account_seed_coin_settings, container, false);
ButterKnife.bind(this, v);
CryptoNetSelectionListAdapter cryptoNetSelectionListAdapter = new CryptoNetSelectionListAdapter(this.cryptoNetSelectionList);
cryptoNetSelectionListAdapter.addListener(this);
rvCoinSelection.setAdapter(cryptoNetSelectionListAdapter);
LinearLayoutManager llm = new LinearLayoutManager(this.getContext());
llm.setOrientation(LinearLayoutManager.VERTICAL);
rvCoinSelection.setLayoutManager(llm);
return v;
}
@Override
public void onCryptoNetSelectionChecked(CryptoNetSelection source) {
//Toast.makeText(this.getContext(),"the coin "+source.getCryptoNet().name()+" was "+(source.getSelected()?"selected":"unselected"),Toast.LENGTH_LONG).show();
List<CryptoCoin> cryptoCoins = CryptoCoin.getByCryptoNet(source.getCryptoNet());
final CreateBitcoinAccountRequest request = new CreateBitcoinAccountRequest(this.accountSeed,this.getContext(),cryptoCoins.get(0));
request.setListener(new CryptoNetInfoRequestListener() {
@Override
public void onCarryOut() {
if (request.getStatus() == CreateBitcoinAccountRequest.StatusCode.SUCCEEDED){
Toast.makeText(getContext(),"The account was successfully created",Toast.LENGTH_LONG);
} else {
Toast.makeText(getContext(),"There was an error enabling the account",Toast.LENGTH_LONG);
}
}
});
CryptoNetInfoRequests.getInstance().addRequest(request);
}
}

View File

@ -0,0 +1,122 @@
package cy.agorise.crystalwallet.fragments;
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.thekhaeng.pushdownanim.PushDownAnim;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.CryptoNetAccount;
/**
* Created by xd on 12/28/17.
*/
public class GeneralAccountSeedFragment extends Fragment {
@BindView(R.id.tvMnemonic)
TextView tvMnemonic;
@BindView(R.id.btnCopy)
Button btnCopy;
AccountSeed accountSeed;
public GeneralAccountSeedFragment() {
if (getArguments() != null) {
long accountSeedId = getArguments().getLong("SEED_ID", -1);
if (accountSeedId > -1) {
this.accountSeed = CrystalDatabase.getAppDatabase(getContext()).accountSeedDao().findById(accountSeedId);
}
}
// Required empty public constructor
}
public static GeneralAccountSeedFragment newInstance(long accountSeedId) {
GeneralAccountSeedFragment fragment = new GeneralAccountSeedFragment();
Bundle args = new Bundle();
args.putLong("SEED_ID", accountSeedId);
fragment.setArguments(args);
if (accountSeedId > -1){
fragment.accountSeed = CrystalDatabase.getAppDatabase(fragment.getContext()).accountSeedDao().findById(accountSeedId);
}
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_general_crypto_net_account_settings, container, false);
ButterKnife.bind(this, v);
/*
* Integration of library with button efects
* */
PushDownAnim.setPushDownAnimTo(btnCopy)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
btnCopyClick();
}
} );
initAlreadyLtm();
return v;
}
public void initAlreadyLtm(){
tvMnemonic.setText(this.accountSeed.getMasterSeed());
}
/*
* Clic on button copy to clipboard
* */
@OnClick(R.id.btnCopy)
public void btnCopyClick(){
/*
* Save to clipboard the brainkey chain
* */
final Activity activity = getActivity();
ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText(tvMnemonic.getText(), tvMnemonic.getText().toString());
clipboard.setPrimaryClip(clip);
/*
* Success message
* */
Toast.makeText(activity,getResources().getString(R.string.window_seed_toast_clipboard), Toast.LENGTH_SHORT).show();
}
}

View File

@ -1,6 +1,10 @@
package cy.agorise.crystalwallet.fragments;
import android.app.Activity;
import android.arch.lifecycle.LiveData;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
@ -8,11 +12,15 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.thekhaeng.pushdownanim.PushDownAnim;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
import cy.agorise.crystalwallet.models.AccountSeed;
@ -30,9 +38,15 @@ public class GeneralCryptoNetAccountSettingsFragment extends Fragment {
@BindView(R.id.tvMnemonic)
TextView tvMnemonic;
@BindView(R.id.btnCopy)
Button btnCopy;
CryptoNetAccount cryptoNetAccount;
AccountSeed accountSeed;
public GeneralCryptoNetAccountSettingsFragment() {
if (getArguments() != null) {
@ -73,6 +87,18 @@ public class GeneralCryptoNetAccountSettingsFragment extends Fragment {
View v = inflater.inflate(R.layout.fragment_general_crypto_net_account_settings, container, false);
ButterKnife.bind(this, v);
/*
* Integration of library with button efects
* */
PushDownAnim.setPushDownAnimTo(btnCopy)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
btnCopyClick();
}
} );
initAlreadyLtm();
return v;
@ -83,4 +109,24 @@ public class GeneralCryptoNetAccountSettingsFragment extends Fragment {
tvMnemonic.setText(this.accountSeed.getMasterSeed());
}
}
/*
* Clic on button copy to clipboard
* */
@OnClick(R.id.btnCopy)
public void btnCopyClick(){
/*
* Save to clipboard the brainkey chain
* */
final Activity activity = getActivity();
ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText(tvMnemonic.getText(), tvMnemonic.getText().toString());
clipboard.setPrimaryClip(clip);
/*
* Success message
* */
Toast.makeText(activity,getResources().getString(R.string.window_seed_toast_clipboard), Toast.LENGTH_SHORT).show();
}
}

View File

@ -15,10 +15,12 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import com.thekhaeng.pushdownanim.PushDownAnim;
import com.vincent.filepicker.Constant;
import com.vincent.filepicker.activity.AudioPickActivity;
import com.vincent.filepicker.filter.entity.AudioFile;
@ -71,6 +73,8 @@ public class GeneralSettingsFragment extends Fragment {
Spinner spDisplayDateTime;
@BindView (R.id.tvReceiveFundsSoundValue)
TextView tvReceiveFundsSound;
@BindView (R.id.btnContact)
Button btnContact;
public GeneralSettingsFragment() {
this.spPreferredLanguageInitialized = false;
@ -99,6 +103,17 @@ public class GeneralSettingsFragment extends Fragment {
View v = inflater.inflate(R.layout.fragment_general_settings, container, false);
ButterKnife.bind(this, v);
/*
* Integration of library with button efects
* */
PushDownAnim.setPushDownAnimTo(btnContact)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
}
} );
generalSettingListViewModel = ViewModelProviders.of(this).get(GeneralSettingListViewModel.class);
generalSettingListLiveData = generalSettingListViewModel.getGeneralSettingList();

View File

@ -5,13 +5,20 @@ import android.app.Dialog;
import android.arch.lifecycle.ViewModelProviders;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
@ -19,11 +26,15 @@ import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.thekhaeng.pushdownanim.PushDownAnim;
import com.vincent.filepicker.ToastUtil;
import java.net.URISyntaxException;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnTouch;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.activities.BoardActivity;
import cy.agorise.crystalwallet.activities.ImportSeedActivity;
@ -49,7 +60,10 @@ public class ImportAccountOptionsFragment extends DialogFragment {
Button btnClose;
@BindView(R.id.btnImportBackup)
Button btnImportBackup;
@BindView(R.id.btnImportSeed)
Button btnImportSeed;
private static final int PERMISSION_REQUEST_CODE = 1;
/*
Dialog for loading
@ -90,6 +104,34 @@ public class ImportAccountOptionsFragment extends DialogFragment {
View view = inflater.inflate(R.layout.fragment_import_account_options, null);
ButterKnife.bind(this, view);
/*
* Integration of library with button efects
* */
PushDownAnim.setPushDownAnimTo(btnClose)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
cancel();
}
} );
PushDownAnim.setPushDownAnimTo(btnImportBackup)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
importBackup();
}
} );
PushDownAnim.setPushDownAnimTo(btnImportSeed)
.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick( View view ){
importSeed();
}
} );
return builder.setView(view).create();
}
@ -110,18 +152,88 @@ public class ImportAccountOptionsFragment extends DialogFragment {
@OnClick (R.id.btnImportBackup)
public void importBackup(){
Intent fileIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
fileIntent.setType("*/*");
fileIntent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(fileIntent, FILE_CONTENT_REQUEST_CODE);
if (Build.VERSION.SDK_INT >= 23) {
if (checkPermission()) {
Intent fileIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
fileIntent.setType("*/*");
fileIntent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(fileIntent, FILE_CONTENT_REQUEST_CODE);
} else {
requestPermission(); // Code for permission
}
}
else {
Intent fileIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
fileIntent.setType("*/*");
fileIntent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(fileIntent, FILE_CONTENT_REQUEST_CODE);
}
}
@OnClick (R.id.btnImportSeed)
public void importSeed(){
Intent intent = new Intent(this.getActivity(), ImportSeedActivity.class);
startActivity(intent);
if (Build.VERSION.SDK_INT >= 23) {
if (checkPermission()) {
Intent intent = new Intent(this.getActivity(), ImportSeedActivity.class);
startActivity(intent);
} else {
requestPermission(); // Code for permission
}
}
else {
Intent intent = new Intent(this.getActivity(), ImportSeedActivity.class);
startActivity(intent);
}
}
private boolean checkPermission() {
int result = ContextCompat.checkSelfPermission(getActivity(), android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (result == PackageManager.PERMISSION_GRANTED) {
return true;
} else {
return false;
}
}
private void requestPermission() {
Log.i("log", "requestPermission() entered");
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
Toast.makeText(getActivity(), getActivity().getResources().getString(R.string.Permision_storage), Toast.LENGTH_LONG).show();
} else {
// ActivityCompat.requestPermissions(getActivity(), new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE);
requestPermissions(new String[] {android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Intent intent = new Intent(getActivity(), ImportSeedActivity.class);
startActivity(intent);
} else {
ToastUtil.getInstance(getActivity()).showToast(getActivity().getString(R.string.Permission_Denied_WRITE_EXTERNAL_STORAGE));
}
break;
}
}
@Override

View File

@ -3,18 +3,30 @@ package cy.agorise.crystalwallet.fragments;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.activities.BoardActivity;
import cy.agorise.crystalwallet.application.CrystalSecurityMonitor;
import cy.agorise.crystalwallet.dialogs.material.DialogMaterial;
import cy.agorise.crystalwallet.dialogs.material.NegativeResponse;
import cy.agorise.crystalwallet.dialogs.material.PositiveResponse;
import cy.agorise.crystalwallet.dialogs.material.QuestionDialog;
import cy.agorise.crystalwallet.models.GeneralSetting;
import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel;
import cy.agorise.crystalwallet.viewmodels.validators.PinSecurityValidator;
@ -25,6 +37,12 @@ import cy.agorise.crystalwallet.viewmodels.validators.PinSecurityValidator;
public class NoneSecurityFragment extends Fragment {
@BindView(R.id.btnOK)
Button btnOK;
public NoneSecurityFragment() {
// Required empty public constructor
}
@ -43,14 +61,41 @@ public class NoneSecurityFragment extends Fragment {
View v = inflater.inflate(R.layout.fragment_none_security, container, false);
ButterKnife.bind(this, v);
return v;
}
@Override
@OnClick(R.id.btnOK)
public void btnOKClic(){
/*
* Question if user is sure to remove the security
* */
final QuestionDialog questionDialog = new QuestionDialog(getActivity());
questionDialog.setText(getActivity().getString(R.string.question_continue));
questionDialog.setOnNegative(new NegativeResponse() {
@Override
public void onNegative(@NotNull DialogMaterial dialogMaterial) {
}
});
questionDialog.setOnPositive(new PositiveResponse() {
@Override
public void onPositive() {
CrystalSecurityMonitor.getInstance(null).clearSecurity();
Toast.makeText(getActivity().getBaseContext(),getActivity().getString(R.string.Security_mode_changed_to_none),
Toast.LENGTH_SHORT).show();
}
});
questionDialog.show();
}
/*@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
CrystalSecurityMonitor.getInstance(null).clearSecurity();
}
}
}*/
}

View File

@ -1,12 +1,15 @@
package cy.agorise.crystalwallet.fragments;
import android.app.Activity;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -18,13 +21,19 @@ import com.andrognito.patternlockview.PatternLockView;
import com.andrognito.patternlockview.listener.PatternLockViewListener;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnTextChanged;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.application.CrystalSecurityMonitor;
import cy.agorise.crystalwallet.dialogs.material.CrystalDialog;
import cy.agorise.crystalwallet.interfaces.OnResponse;
import cy.agorise.crystalwallet.models.GeneralSetting;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequests;
import cy.agorise.crystalwallet.util.ChildViewPager;
import cy.agorise.crystalwallet.util.PasswordManager;
import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel;
import cy.agorise.crystalwallet.viewmodels.validators.PinSecurityValidator;
@ -42,9 +51,17 @@ public class PatternSecurityFragment extends Fragment {
@BindView(R.id.tvPatternText)
TextView tvPatternText;
/*
* Contains the ChildViewPager to block the viewpager when the user is using the pattern control
* */
private ChildViewPager childViewPager;
private PatternLockViewListener actualPatternListener;
private String patternEntered;
public PatternSecurityFragment() {
// Required empty public constructor
}
@ -77,6 +94,11 @@ public class PatternSecurityFragment extends Fragment {
return patternString;
}
public void setChildViewPager(ChildViewPager childViewPager) {
this.childViewPager = childViewPager;
}
public void removePatternListener(){
if (actualPatternListener != null){
patternLockView.removePatternLockListener(actualPatternListener);
@ -87,12 +109,12 @@ public class PatternSecurityFragment extends Fragment {
public void showNewPatternUI(){
removePatternListener();
patternLockView.clearPattern();
tvPatternText.setText("Enter new pattern");
tvPatternText.setTextColor(Color.WHITE);
tvPatternText.setText(getActivity().getResources().getString(R.string.Enter_new_pattern));
actualPatternListener = new PatternLockViewListener() {
@Override
public void onStarted() {
}
@Override
@ -118,7 +140,7 @@ public class PatternSecurityFragment extends Fragment {
removePatternListener();
patternLockView.clearPattern();
patternLockView.requestFocus();
tvPatternText.setText("Confirm new pattern");
tvPatternText.setText(getActivity().getResources().getString(R.string.Confirm_new_pattern));
actualPatternListener = new PatternLockViewListener() {
@Override
@ -135,7 +157,9 @@ public class PatternSecurityFragment extends Fragment {
public void onComplete(List<PatternLockView.Dot> pattern) {
if (patternEntered.equals(patternToString(pattern))){
savePattern(patternEntered);
showNewPatternUI();
}
else{
resetPattern();
}
}
@ -147,9 +171,79 @@ public class PatternSecurityFragment extends Fragment {
patternLockView.addPatternLockListener(actualPatternListener);
}
private void resetPattern(){
/*
* Show error
* */
tvPatternText.setText(getActivity().getResources().getString(R.string.Incorrect_pattern));
tvPatternText.setTextColor(Color.RED);
final Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
t.cancel();
showNewPatternUI();
}
});
}
},
//Set how long before to start calling the TimerTask (in milliseconds)
1000,
//Set the amount of time between each execution (in milliseconds)
1000);
}
public void savePattern(String pattern){
String patternEncripted = PasswordManager.encriptPassword(pattern);
CrystalSecurityMonitor.getInstance(null).setPatternEncrypted(patternEncripted);
CrystalSecurityMonitor.getInstance(null).callPasswordRequest(this.getActivity());
/*CrystalSecurityMonitor.getInstance(null).callPasswordRequest(this.getActivity(), new OnResponse() {
@Override
public void onSuccess() {
Log.i("onSuccess","onSuccess");
Toast.makeText(getActivity(), "onSuccess", Toast.LENGTH_LONG).show();
}
@Override
public void onFailed() {
Log.i("onFailed","onFailed");
Toast.makeText(getActivity(), "onFailed", Toast.LENGTH_LONG).show();
}
});*/
/*
* Show success
* */
tvPatternText.setText(getActivity().getResources().getString(R.string.Pattern_set_correctly));
tvPatternText.setTextColor(Color.GREEN);
final Timer t_ = new Timer();
t_.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
t_.cancel();
showNewPatternUI();
}
});
}
},
//Set how long before to start calling the TimerTask (in milliseconds)
1000,
//Set the amount of time between each execution (in milliseconds)
1000);
}
}

View File

@ -1,24 +1,35 @@
package cy.agorise.crystalwallet.fragments;
import android.app.Activity;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnFocusChange;
import butterknife.OnTextChanged;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.application.CrystalSecurityMonitor;
import cy.agorise.crystalwallet.dialogs.material.DialogMaterial;
import cy.agorise.crystalwallet.dialogs.material.NegativeResponse;
import cy.agorise.crystalwallet.dialogs.material.PositiveResponse;
import cy.agorise.crystalwallet.dialogs.material.QuestionDialog;
import cy.agorise.crystalwallet.models.GeneralSetting;
import cy.agorise.crystalwallet.util.PasswordManager;
import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel;
@ -42,6 +53,19 @@ public class PinSecurityFragment extends Fragment implements UIValidatorListener
@BindView(R.id.tvConfirmPinError)
TextView tvConfirmPinError;
@BindView(R.id.btnOK)
Button btnOK;
/*
* Validates the new typing for the patterns
* */
private boolean first = true;
/*
* Flag to check if validation of fields is correct
* */
private boolean valid = false;
GeneralSettingListViewModel generalSettingListViewModel;
GeneralSetting passwordGeneralSetting;
PinSecurityValidator pinSecurityValidator;
@ -64,15 +88,117 @@ public class PinSecurityFragment extends Fragment implements UIValidatorListener
View v = inflater.inflate(R.layout.fragment_pin_security, container, false);
ButterKnife.bind(this, v);
/*
* Initially not enabled til it passes validations
* */
btnOK.setEnabled(false);
generalSettingListViewModel = ViewModelProviders.of(this).get(GeneralSettingListViewModel.class);
LiveData<List<GeneralSetting>> generalSettingsLiveData = generalSettingListViewModel.getGeneralSettingList();
pinSecurityValidator = new PinSecurityValidator(this.getContext(), etNewPin, etConfirmPin);
pinSecurityValidator.setListener(this);
/*
* If PIN is configured set some text
* */
final CrystalSecurityMonitor crystalSecurityMonitor = CrystalSecurityMonitor.getInstance(getActivity());
switch(CrystalSecurityMonitor.getInstance(getActivity()).actualSecurity()) {
case GeneralSetting.SETTING_PASSWORD:
if(etNewPin!=null && etConfirmPin!=null){
etNewPin.setText("123456");
etConfirmPin.setText("123456");
}
break;
case GeneralSetting.SETTING_PATTERN:
break;
default:
if(etNewPin!=null && etConfirmPin!=null) {
etNewPin.setText("");
etConfirmPin.setText("");
}
}
/*
* Focus in no where
* */
tvNewPinError.requestFocus();
return v;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
first = true;
/*
* If PIN is configured set some text
* */
final CrystalSecurityMonitor crystalSecurityMonitor = CrystalSecurityMonitor.getInstance(getActivity());
switch(CrystalSecurityMonitor.getInstance(getActivity()).actualSecurity()) {
case GeneralSetting.SETTING_PASSWORD:
if(etNewPin!=null && etConfirmPin!=null){
etNewPin.setText("123456");
etConfirmPin.setText("123456");
}
break;
case GeneralSetting.SETTING_PATTERN:
break;
default:
if(etNewPin!=null && etConfirmPin!=null) {
etNewPin.setText("");
etConfirmPin.setText("");
}
}
/*
* Focus in no where
* */
if(getActivity()!=null){
tvNewPinError.requestFocus();
}
}
@OnClick(R.id.btnOK)
void okClic(final View view) {
/*
* Only can continue if the fields are correctly validated
* */
if(valid){
/*
* Question if continue or not
* */
final QuestionDialog questionDialog = new QuestionDialog(getActivity());
questionDialog.setText(getActivity().getString(R.string.question_continue));
questionDialog.setOnNegative(new NegativeResponse() {
@Override
public void onNegative(@NotNull DialogMaterial dialogMaterial) {
}
});
questionDialog.setOnPositive(new PositiveResponse() {
@Override
public void onPositive() {
savePassword();
}
});
questionDialog.show();
}
}
@OnTextChanged(value = R.id.etNewPin,
callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
void afterNewPinChanged(Editable editable) {
@ -94,6 +220,16 @@ public class PinSecurityFragment extends Fragment implements UIValidatorListener
}
}
@OnFocusChange({R.id.etNewPin,R.id.etConfirmPin})
public void focusChangePIN(View view, boolean hasFocus){
if(hasFocus){
if(first){
first = false;
clearFields();
}
}
}
@Override
public void onValidationSucceeded(final ValidationField field) {
final PinSecurityFragment fragment = this;
@ -108,29 +244,64 @@ public class PinSecurityFragment extends Fragment implements UIValidatorListener
}
if (pinSecurityValidator.isValid()){
CharSequence text = "Your password has been sucessfully changed!";
int duration = Toast.LENGTH_SHORT;
//savePassword();
Toast toast = Toast.makeText(getContext(), text, duration);
toast.show();
if(!first){
savePassword(etNewPin.getText().toString());
//Now is valid
valid = true;
/*
* Enable ok button to continue
* */
btnOK.setEnabled(true);
}
clearFields();
}
}
});
}
private void savePassword(){
savePassword(etNewPin.getText().toString());
CharSequence text = "Your password has been sucessfully changed!";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(getContext(), text, duration);
toast.show();
etNewPin.setText("123456");
etConfirmPin.setText("123456");
first = true;
btnOK.setEnabled(false);
/*
* Focus in no where
* */
tvNewPinError.requestFocus();
}
public void savePassword(String password) {
String passwordEncripted = PasswordManager.encriptPassword(password);
CrystalSecurityMonitor.getInstance(null).setPasswordSecurity(passwordEncripted);
CrystalSecurityMonitor.getInstance(null).callPasswordRequest(this.getActivity());
CrystalSecurityMonitor.getInstance(getActivity()).setPasswordSecurity(passwordEncripted);
//CrystalSecurityMonitor.getInstance(getActivity()).callPasswordRequest(this.getActivity());
}
@Override
public void onValidationFailed(final ValidationField field) {
//Still false
valid = false;
/*
* Disable til it passes validations
* */
btnOK.setEnabled(false);
this.getActivity().runOnUiThread(new Runnable() {
@Override

View File

@ -9,6 +9,7 @@ import android.content.ContextWrapper;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
@ -22,18 +23,28 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.journeyapps.barcodescanner.BarcodeEncoder;
import butterknife.OnClick;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.requestmanagers.CalculateBitcoinUriRequest;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestListener;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequests;
import cy.agorise.crystalwallet.requestmanagers.NextBitcoinAccountAddressRequest;
import cy.agorise.crystalwallet.util.CircularImageView;
import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountListViewModel;
import cy.agorise.crystalwallet.views.CryptoNetAccountAdapter;
@ -75,6 +86,8 @@ public class ReceiveTransactionFragment extends DialogFragment implements UIVali
TextView tvAssetError;
@BindView(R.id.ivQrCode)
ImageView ivQrCode;
@BindView(R.id.pbQrCode)
ProgressBar pbQrCode;
@BindView(R.id.tvCancel)
TextView tvCancel;
@ -95,6 +108,10 @@ public class ReceiveTransactionFragment extends DialogFragment implements UIVali
private FloatingActionButton fabReceive;
private AsyncTask qrCodeTask;
private Double lastAmount = -1.0;
public static ReceiveTransactionFragment newInstance(long cryptoNetAccountId) {
ReceiveTransactionFragment f = new ReceiveTransactionFragment();
@ -132,36 +149,13 @@ public class ReceiveTransactionFragment extends DialogFragment implements UIVali
db = CrystalDatabase.getAppDatabase(this.getContext());
this.cryptoNetAccount = db.cryptoNetAccountDao().getById(this.cryptoNetAccountId);
/*
* this is only for graphene accounts.
*
**/
this.grapheneAccount = new GrapheneAccount(this.cryptoNetAccount);
this.grapheneAccount.loadInfo(db.grapheneAccountInfoDao().getByAccountId(this.cryptoNetAccountId));
final LiveData<List<CryptoCoinBalance>> balancesList = db.cryptoCoinBalanceDao().getBalancesFromAccount(cryptoNetAccountId);
balancesList.observe(this, new Observer<List<CryptoCoinBalance>>() {
@Override
public void onChanged(@Nullable List<CryptoCoinBalance> cryptoCoinBalances) {
ArrayList<Long> assetIds = new ArrayList<Long>();
for (CryptoCoinBalance nextBalance : balancesList.getValue()) {
assetIds.add(nextBalance.getCryptoCurrencyId());
}
List<CryptoCurrency> cryptoCurrencyList = db.cryptoCurrencyDao().getByIds(assetIds);
CryptoCurrencyAdapter assetAdapter = new CryptoCurrencyAdapter(getContext(), android.R.layout.simple_spinner_item, cryptoCurrencyList);
spAsset.setAdapter(assetAdapter);
}
});
receiveTransactionValidator = new ReceiveTransactionValidator(this.getContext(), this.cryptoNetAccount, spAsset, etAmount);
receiveTransactionValidator.setListener(this);
CryptoNetAccountListViewModel cryptoNetAccountListViewModel = ViewModelProviders.of(this).get(CryptoNetAccountListViewModel.class);
List<CryptoNetAccount> cryptoNetAccounts = cryptoNetAccountListViewModel.getCryptoNetAccountList();
CryptoNetAccountAdapter toSpinnerAdapter = new CryptoNetAccountAdapter(this.getContext(), android.R.layout.simple_spinner_item, cryptoNetAccounts);
spTo.setAdapter(toSpinnerAdapter);
spTo.setSelection(0);
setAccountUI();
}
builder.setView(view);
@ -234,8 +228,59 @@ public class ReceiveTransactionFragment extends DialogFragment implements UIVali
}
}
public void setAccountUI(){
if (this.cryptoNetAccount.getCryptoNet() == CryptoNet.BITSHARES) {
/*
* this is only for graphene accounts.
*
**/
this.grapheneAccount = new GrapheneAccount(this.cryptoNetAccount);
this.grapheneAccount.loadInfo(db.grapheneAccountInfoDao().getByAccountId(this.cryptoNetAccountId));
final LiveData<List<CryptoCoinBalance>> balancesList = db.cryptoCoinBalanceDao().getBalancesFromAccount(cryptoNetAccountId);
balancesList.observe(this, new Observer<List<CryptoCoinBalance>>() {
@Override
public void onChanged(@Nullable List<CryptoCoinBalance> cryptoCoinBalances) {
ArrayList<Long> assetIds = new ArrayList<Long>();
for (CryptoCoinBalance nextBalance : balancesList.getValue()) {
assetIds.add(nextBalance.getCryptoCurrencyId());
}
List<CryptoCurrency> cryptoCurrencyList = db.cryptoCurrencyDao().getByIds(assetIds);
/*
* Test
* */
//CryptoCurrency crypto1 = new CryptoCurrency();
//crypto1.setId(1);
//crypto1.setName("BITCOIN");
//crypto1.setPrecision(1);
//cryptoCurrencyList.add(crypto1);
CryptoCurrencyAdapter assetAdapter = new CryptoCurrencyAdapter(getContext(), android.R.layout.simple_spinner_item, cryptoCurrencyList);
spAsset.setAdapter(assetAdapter);
}
});
receiveTransactionValidator = new ReceiveTransactionValidator(this.getContext(), this.cryptoNetAccount, spAsset, etAmount);
receiveTransactionValidator.setListener(this);
} else {
CryptoCoin cryptoCoin = CryptoCoin.getByCryptoNet(this.cryptoNetAccount.getCryptoNet()).get(0);
List<String> currencyList = new ArrayList<>();
currencyList.add(cryptoCoin.getLabel());
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this.getContext(),android.R.layout.simple_list_item_1,currencyList);
spAsset.setAdapter(arrayAdapter);
receiveTransactionValidator = new ReceiveTransactionValidator(this.getContext(), this.cryptoNetAccount, spAsset, etAmount);
receiveTransactionValidator.setListener(this);
}
}
@OnItemSelected(R.id.spTo)
public void afterToSelected(Spinner spinner, int position) {
this.cryptoNetAccount = (CryptoNetAccount)spinner.getSelectedItem();
setAccountUI();
this.receiveTransactionValidator.validate();
}
@ -247,7 +292,10 @@ public class ReceiveTransactionFragment extends DialogFragment implements UIVali
@OnItemSelected(R.id.spAsset)
public void afterAssetSelected(Spinner spinner, int position) {
this.cryptoCurrency = (CryptoCurrency)spinner.getSelectedItem();
if (spinner.getSelectedItem() instanceof CryptoCurrency) {
this.cryptoCurrency = (CryptoCurrency) spinner.getSelectedItem();
}
this.receiveTransactionValidator.validate();
}
@ -295,71 +343,147 @@ public class ReceiveTransactionFragment extends DialogFragment implements UIVali
}
public void createQrCode(){
Double amount = 0.0;
final Double amount;
try{
amount = Double.valueOf(this.etAmount.getText().toString());
} catch(NumberFormatException e){
lastAmount = -1.0;
Log.e("ReceiveFragment","Amount casting error.");
return;
}
CryptoNetAccount toAccountSelected = (CryptoNetAccount) spTo.getSelectedItem();
if (!amount.equals(lastAmount)) {
pbQrCode.setVisibility(View.VISIBLE);
lastAmount = amount;
CryptoNetAccount toAccountSelected = (CryptoNetAccount) spTo.getSelectedItem();
/*
* this is only for graphene accounts.
*
**/
GrapheneAccount grapheneAccountSelected = new GrapheneAccount(toAccountSelected);
grapheneAccountSelected.loadInfo(db.grapheneAccountInfoDao().getByAccountId(toAccountSelected.getId()));
if (this.qrCodeTask != null) {
this.qrCodeTask.cancel(true);
}
if (this.cryptoNetAccount.getCryptoNet() == CryptoNet.BITSHARES) {
/*
* this is only for graphene accounts.
*
**/
GrapheneAccount grapheneAccountSelected = new GrapheneAccount(toAccountSelected);
grapheneAccountSelected.loadInfo(db.grapheneAccountInfoDao().getByAccountId(toAccountSelected.getId()));
this.invoiceItems.clear();
this.invoiceItems.add(
new LineItem("transfer", 1, amount)
);
this.invoiceItems.clear();
this.invoiceItems.add(
new LineItem("transfer", 1, amount)
);
LineItem items[] = new LineItem[this.invoiceItems.size()];
items = this.invoiceItems.toArray(items);
this.invoice.setLineItems(items);
this.invoice.setTo(grapheneAccountSelected.getName());
this.invoice.setCurrency(this.cryptoCurrency.getName());
LineItem items[] = new LineItem[this.invoiceItems.size()];
items = this.invoiceItems.toArray(items);
this.invoice.setLineItems(items);
this.invoice.setTo(grapheneAccountSelected.getName());
this.invoice.setCurrency(this.cryptoCurrency.getName());
try {
Bitmap bitmap = textToImageEncode(Invoice.toQrCode(invoice));
ivQrCode.setImageBitmap(bitmap);
} catch (WriterException e) {
Log.e("ReceiveFragment", "Error creating QrCode");
//if (this.qrCodeTask != null) {
// this.qrCodeTask.cancel(true);
//}
this.qrCodeTask = new AsyncTask<Object, Void, Void>() {
@Override
protected Void doInBackground(Object... voids) {
try {
final Bitmap bitmap = textToImageEncode(Invoice.toQrCode(invoice));
if (!this.isCancelled()) {
ReceiveTransactionFragment.this.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
ivQrCode.setImageBitmap(bitmap);
pbQrCode.setVisibility(View.GONE);
}
});
}
} catch (WriterException e) {
Log.e("ReceiveFragment", "Error creating QrCode");
}
return null;
}
};
this.qrCodeTask.execute(null, null, null);
} else {
final CryptoCoin cryptoCoin = CryptoCoin.getByCryptoNet(this.cryptoNetAccount.getCryptoNet()).get(0);
final CalculateBitcoinUriRequest uriRequest = new CalculateBitcoinUriRequest(cryptoCoin, cryptoNetAccount, getContext(), amount);
uriRequest.setListener(new CryptoNetInfoRequestListener() {
@Override
public void onCarryOut() {
if (uriRequest.getUri() != null) {
qrCodeTask = new AsyncTask<Object, Void, Void>() {
@Override
protected Void doInBackground(Object... voids) {
try {
final Bitmap bitmap = textToImageEncode(uriRequest.getUri());
if (!this.isCancelled()) {
ReceiveTransactionFragment.this.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
//Double amountNow = -1.0;
//try{
// amountNow = Double.valueOf(etAmount.getText().toString());
//} catch(NumberFormatException e){
//}
//if (amountNow >= 0) {
if (amount.equals(lastAmount)) {
if (!isCancelled()) {
ivQrCode.setImageBitmap(bitmap);
pbQrCode.setVisibility(View.GONE);
}
}
//}
}
});
}
} catch (WriterException e) {
Log.e("ReceiveFragment", "Error creating QrCode");
}
return null;
}
};
qrCodeTask.execute(null, null, null);
} else {
Log.e("ReceiveFragment", "Error obtaining the uri");
}
}
});
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
CryptoNetInfoRequests.getInstance().addRequest(uriRequest);
}
});
thread.start();
}
}
}
Bitmap textToImageEncode(String Value) throws WriterException {
//TODO: do this in another thread
BitMatrix bitMatrix;
Bitmap bitmap = null;
MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
try {
bitMatrix = new MultiFormatWriter().encode(
Value,
BarcodeFormat.DATA_MATRIX.QR_CODE,
ivQrCode.getWidth(), ivQrCode.getHeight(), null
);
} catch (IllegalArgumentException Illegalargumentexception) {
return null;
}
int bitMatrixWidth = bitMatrix.getWidth();
int bitMatrixHeight = bitMatrix.getHeight();
int[] pixels = new int[bitMatrixWidth * bitMatrixHeight];
for (int y = 0; y < bitMatrixHeight; y++) {
int offset = y * bitMatrixWidth;
for (int x = 0; x < bitMatrixWidth; x++) {
pixels[offset + x] = bitMatrix.get(x, y) ?
getResources().getColor(R.color.QRCodeBlackColor):getResources().getColor(R.color.QRCodeWhiteColor);
}
BitMatrix bitMatrix = multiFormatWriter.encode(Value, BarcodeFormat.QR_CODE, ivQrCode.getWidth(), ivQrCode.getHeight());
BarcodeEncoder barcodeEncoder = new BarcodeEncoder();
bitmap = barcodeEncoder.createBitmap(bitMatrix);
} catch (WriterException e) {
e.printStackTrace();
}
Bitmap bitmap = Bitmap.createBitmap(bitMatrixWidth, bitMatrixHeight, Bitmap.Config.ARGB_4444);
bitmap.setPixels(pixels, 0, ivQrCode.getWidth(), 0, 0, bitMatrixWidth, bitMatrixHeight);
return bitmap;
}
}

View File

@ -83,10 +83,15 @@ public class SecuritySettingsFragment extends Fragment {
View v = inflater.inflate(R.layout.fragment_security_settings, container, false);
ButterKnife.bind(this, v);
/*
* For now this will not be implemented
* */
sPocketSecurity.setEnabled(false);
securityPagerAdapter = new SecurityPagerAdapter(getChildFragmentManager());
mPager.setAdapter(securityPagerAdapter);
switch(CrystalSecurityMonitor.getInstance(null).actualSecurity()) {
switch(CrystalSecurityMonitor.getInstance(getActivity()).actualSecurity()) {
case GeneralSetting.SETTING_PASSWORD:
mPager.setCurrentItem(1);
break;
@ -96,7 +101,6 @@ public class SecuritySettingsFragment extends Fragment {
default:
mPager.setCurrentItem(0);
}
mPager.setSwipeLocked(true);
TabLayout tabLayout = v.findViewById(R.id.tabs);
@ -117,7 +121,7 @@ public class SecuritySettingsFragment extends Fragment {
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
CrystalSecurityMonitor.getInstance(null).callPasswordRequest(this.getActivity());
//CrystalSecurityMonitor.getInstance(null).callPasswordRequest(this.getActivity());
}
}
@ -134,7 +138,9 @@ public class SecuritySettingsFragment extends Fragment {
case 1:
return new PinSecurityFragment();
case 2:
return new PatternSecurityFragment();
final PatternSecurityFragment patternSecurityFragment = new PatternSecurityFragment();
patternSecurityFragment.setChildViewPager(mPager);
return patternSecurityFragment;
}
return null; //new OnConstructionFragment();

View File

@ -8,11 +8,11 @@ import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
@ -22,25 +22,21 @@ import android.support.v4.app.ActivityCompat;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.widget.Button;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.jaredrummler.materialspinner.MaterialSpinner;
import com.vincent.filepicker.ToastUtil;
import java.io.File;
import java.math.RoundingMode;
@ -56,6 +52,14 @@ import butterknife.OnClick;
import butterknife.OnItemSelected;
import butterknife.OnTextChanged;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.application.CrystalSecurityMonitor;
import cy.agorise.crystalwallet.dialogs.material.CrystalDialog;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.interfaces.OnResponse;
import cy.agorise.crystalwallet.requestmanagers.BitcoinSendRequest;
import cy.agorise.crystalwallet.requestmanagers.BitcoinUriParseRequest;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequest;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestListener;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequests;
import cy.agorise.crystalwallet.requestmanagers.ValidateBitsharesSendRequest;
@ -81,10 +85,12 @@ import static butterknife.internal.Utils.listOf;
public class SendTransactionFragment extends DialogFragment implements UIValidatorListener, ZXingScannerView.ResultHandler {
private final String TAG = getClass().getName();
SendTransactionValidator sendTransactionValidator;
@BindView(R.id.spFrom)
MaterialSpinner spFrom;
Spinner spFrom;
@BindView(R.id.tvFromError)
TextView tvFromError;
@BindView(R.id.etTo)
@ -93,12 +99,14 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
View viewSend;
@BindView(R.id.tvToError)
TextView tvToError;
@BindView(R.id.fabCloseCamera)
FloatingActionButton btnCloseCamera;
@BindView(R.id.spAsset)
Spinner spAsset;
@BindView(R.id.tvAssetError)
TextView tvAssetError;
@BindView(R.id.scrollMain)
ScrollView scrollMain;
//@BindView(R.id.scrollMain)
//ScrollView scrollMain;
@BindView(R.id.etAmount)
EditText etAmount;
@BindView(R.id.tvAmountError)
@ -109,11 +117,8 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
TextView tvMemoError;
@BindView(R.id.btnSend)
FloatingActionButton btnSend;
@BindView(R.id.btnCancel)
TextView btnCancel;
@BindView(R.id.ivPeople)
ImageView ivPeople;
@BindView(R.id.ivCamera)
ZXingScannerView mScannerView;
@ -122,7 +127,8 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
@BindView(R.id.gravatar)
CircularImageView userImg;
Button btnScanQrCode;
/* Flag to control when the camera is visible and when is hidden */
private boolean cameraVisible = false;
private long cryptoNetAccountId;
private CryptoNetAccount cryptoNetAccount;
@ -131,7 +137,8 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
private FloatingActionButton fabSend;
private AlertDialog.Builder builder;
/* Dialog for loading */
private CrystalDialog crystalDialog;
public static SendTransactionFragment newInstance(long cryptoNetAccountId) {
SendTransactionFragment f = new SendTransactionFragment();
@ -157,72 +164,45 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
//AlertDialog.Builder
builder = new AlertDialog.Builder(getActivity(), R.style.dialog_theme_full);
//builder.setTitle("Send");
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.send_transaction, null);
ButterKnife.bind(this, view);
/*
* Detet scroll changes
* */
scrollMain.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
View view = scrollMain.getChildAt(scrollMain.getChildCount() - 1);
int diff = (view.getBottom() - (scrollMain.getHeight() + scrollMain.getScrollY()));
float traslationY = btnSend.getTranslationY();
if(diff<=266 && diff>128){
//btnSend.setTranslationY(0);
//viewSend.setTranslationY(0);
btnSend.animate().y(880);
viewSend.animate().y(800);
}
else if(diff<=128 && diff>10){
//btnSend.setTranslationY(-130);
//viewSend.setTranslationY(-130);
btnSend.animate().y(880);
viewSend.animate().y(800);
}
else if(diff<=10 && diff>0){
//btnSend.setTranslationY(-170);
//viewSend.setTranslationY(-170);
btnSend.animate().y(680);
viewSend.animate().y(600);
}
else if(diff==0){
//btnSend.setTranslationY(-190);
//viewSend.setTranslationY(-190);
btnSend.animate().y(680);
viewSend.animate().y(600);
}
}
});
this.cryptoNetAccountId = getArguments().getLong("CRYPTO_NET_ACCOUNT_ID",-1);
/*
* Add style to the spinner android
* */
/* Add style to the spinner android */
spFrom.setBackground(getContext().getDrawable(R.drawable.square_color));
if (this.cryptoNetAccountId != -1) {
db = CrystalDatabase.getAppDatabase(this.getContext());
this.cryptoNetAccount = db.cryptoNetAccountDao().getById(this.cryptoNetAccountId);
CryptoNetAccountListViewModel cryptoNetAccountListViewModel = ViewModelProviders.of(this).get(CryptoNetAccountListViewModel.class);
List<CryptoNetAccount> cryptoNetAccounts = cryptoNetAccountListViewModel.getCryptoNetAccountList();
CryptoNetAccountAdapter fromSpinnerAdapter = new CryptoNetAccountAdapter(this.getContext(), android.R.layout.simple_spinner_item, cryptoNetAccounts);
spFrom.setAdapter(fromSpinnerAdapter);
spFrom.setSelection(0);
setAccountUI();
}
loadUserImage();
/* Check for CAMERA permission */
if (Build.VERSION.SDK_INT >= 23 && !checkCameraPermission())
requestCameraPermission();
return builder.setView(view).create();
}
public void setAccountUI(){
if (this.cryptoNetAccount.getCryptoNet() == CryptoNet.BITSHARES) {
/*
* this is only for graphene accounts.
*
**/
* this is only for graphene accounts.
*
**/
this.grapheneAccount = new GrapheneAccount(this.cryptoNetAccount);
this.grapheneAccount.loadInfo(db.grapheneAccountInfoDao().getByAccountId(this.cryptoNetAccountId));
@ -236,70 +216,102 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
}
List<CryptoCurrency> cryptoCurrencyList = db.cryptoCurrencyDao().getByIds(assetIds);
/*
* Test
* */
//CryptoCurrency crypto1 = new CryptoCurrency();
//crypto1.setId(1);
//crypto1.setName("BITCOIN");
//crypto1.setPrecision(1);
//cryptoCurrencyList.add(crypto1);
assetAdapter = new CryptoCurrencyAdapter(getContext(), android.R.layout.simple_spinner_item, cryptoCurrencyList);
spAsset.setAdapter(assetAdapter);
}
});
// TODO SendTransactionValidator to accept spFrom
sendTransactionValidator = new SendTransactionValidator(this.getContext(), this.cryptoNetAccount, spFrom, etTo, spAsset, etAmount, etMemo);
sendTransactionValidator.setListener(this);
CryptoNetAccountListViewModel cryptoNetAccountListViewModel = ViewModelProviders.of(this).get(CryptoNetAccountListViewModel.class);
List<CryptoNetAccount> cryptoNetAccounts = cryptoNetAccountListViewModel.getCryptoNetAccountList();
CryptoNetAccountAdapter fromSpinnerAdapter = new CryptoNetAccountAdapter(this.getContext(), android.R.layout.simple_spinner_item, cryptoNetAccounts);
} else {
CryptoCoin cryptoCoin = CryptoCoin.getByCryptoNet(this.cryptoNetAccount.getCryptoNet()).get(0);
spFrom.setAdapter(fromSpinnerAdapter);
//spFrom.setSelection(0);
List<String> currencyList = new ArrayList<>();
currencyList.add(cryptoCoin.getLabel());
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this.getContext(),android.R.layout.simple_list_item_1,currencyList);
spAsset.setAdapter(arrayAdapter);
/*
* Custom material spinner implementation
* */
spFrom.setItems(cryptoNetAccounts);
//spFrom.setSelectedIndex(0);
spFrom.setOnItemSelectedListener(new MaterialSpinner.OnItemSelectedListener<CryptoNetAccount>() {
@Override
public void onItemSelected(MaterialSpinner view, int position, long id, CryptoNetAccount item) {
sendTransactionValidator.validate();
}
});
spFrom.setOnNothingSelectedListener(new MaterialSpinner.OnNothingSelectedListener() {
// TODO SendTransactionValidator to accept spFrom
sendTransactionValidator = new SendTransactionValidator(this.getContext(), this.cryptoNetAccount, spFrom, etTo, spAsset, etAmount, etMemo);
sendTransactionValidator.setListener(this);
@Override public void onNothingSelected(MaterialSpinner spinner) {
}
});
// etFrom.setText(this.grapheneAccount.getName());
}
}
loadUserImage();
try {
verifyCameraPermissions(getActivity());
beginScanQrCode();
}catch(Exception e){
e.printStackTrace();
private boolean checkCameraPermission() {
int result = ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA);
return result == PackageManager.PERMISSION_GRANTED;
}
private void requestCameraPermission() {
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.CAMERA)) {
Toast.makeText(getActivity(), getActivity().getString(R.string.permission_denied_camera), Toast.LENGTH_LONG).show();
/* Disable the button of the camera visibility */
btnCloseCamera.setVisibility(View.INVISIBLE);
} else {
requestPermissions(new String[] {android.Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
}
}
return builder.setView(view).create();
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_CAMERA_PERMISSION:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.e("value", "Permission Granted, Now you can use camera .");
getActivity().runOnUiThread(new Runnable(){
public void run() {
Toast.makeText(getActivity(), getActivity().getString(R.string.permission_granted_camera), Toast.LENGTH_LONG).show();
}
});
} else {
Log.e("value", "Permission Denied, You cannot use the camera.");
getActivity().runOnUiThread(new Runnable(){
public void run() {
Toast.makeText(getActivity(), getActivity().getString(R.string.permission_denied_camera), Toast.LENGTH_LONG).show();
}
});
}
break;
}
}
@Override
public void onResume() {
super.onResume();
/*builder.setNeutralButton("Scan QR Code", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
beginScanQrCode();
}
});*/
mScannerView.setResultHandler(this);
// Force dialog fragment to use the full width of the screen
Window dialogWindow = getDialog().getWindow();
assert dialogWindow != null;
dialogWindow.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
loadUserImage();
}
@Override
public void onPause() {
super.onPause();
mScannerView.stopCamera();
}
@Override
public void onDestroy() {
super.onDestroy();
@ -328,10 +340,12 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
}
}
/*@OnItemSelected(R.id.spFrom)
@OnItemSelected(R.id.spFrom)
public void afterFromSelected(Spinner spinner, int position) {
this.cryptoNetAccount = (CryptoNetAccount)spinner.getSelectedItem();
setAccountUI();
this.sendTransactionValidator.validate();
}*/
}
@OnTextChanged(value = R.id.etTo,
callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
@ -351,6 +365,46 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
}
@OnClick(R.id.fabCloseCamera)
public void onClickCloseCamera(){
if(cameraVisible)
hideCamera();
else
showCamera();
}
/**
* Shows the camera and hide the black background
* */
private void showCamera(){
/* Change visibilities of views */
mScannerView.setVisibility(View.VISIBLE);
/* Change icon */
btnCloseCamera.setImageDrawable(getResources().getDrawable(R.drawable.ic_close));
/* Reset variable */
cameraVisible = true;
/* Star the camera again */
beginScanQrCode();
}
/**
* Hides the camera and show the black background
* */
private void hideCamera(){
/* Change visibilities of views */
mScannerView.setVisibility(View.INVISIBLE);
/* Change icon */
btnCloseCamera.setImageDrawable(getResources().getDrawable(R.drawable.ok));
/* Reset variable */
cameraVisible = false;
}
@OnTextChanged(value = R.id.etMemo,
callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
void afterMemoChanged(Editable editable) {
@ -408,51 +462,113 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
@OnClick(R.id.btnSend)
public void sendTransaction(){
final SendTransactionFragment thisFragment = this;
final CryptoNetInfoRequest sendRequest;
if (this.sendTransactionValidator.isValid()) {
CryptoNetAccount fromAccountSelected = (CryptoNetAccount) spFrom.getItems().get(spFrom.getSelectedIndex());
//CryptoNetAccount fromAccountSelected = (CryptoNetAccount) spFrom.getItems().get(spFrom.getSelectedIndex());
CryptoNetAccount fromAccountSelected = (CryptoNetAccount) spFrom.getSelectedItem();
/*
* this is only for graphene accounts.
*
**/
GrapheneAccount grapheneAccountSelected = new GrapheneAccount(fromAccountSelected);
grapheneAccountSelected.loadInfo(db.grapheneAccountInfoDao().getByAccountId(fromAccountSelected.getId()));
if (fromAccountSelected.getCryptoNet() == CryptoNet.BITSHARES) {
/* This is only for graphene accounts. */
GrapheneAccount grapheneAccountSelected = new GrapheneAccount(fromAccountSelected);
grapheneAccountSelected.loadInfo(db.grapheneAccountInfoDao().getByAccountId(fromAccountSelected.getId()));
//TODO convert the amount to long type using the precision of the currency
Double amountFromEditText = Double.parseDouble(this.etAmount.getText().toString());
Long amount = (long) Math.floor(amountFromEditText * Math.round(Math.pow(10, ((CryptoCurrency) spAsset.getSelectedItem()).getPrecision())));
//TODO convert the amount to long type using the precision of the currency
Double amountFromEditText = Double.parseDouble(this.etAmount.getText().toString());
Long amount = (long)Math.floor(amountFromEditText*Math.round(Math.pow(10,((CryptoCurrency)spAsset.getSelectedItem()).getPrecision())));
/*final ValidateBitsharesSendRequest*/
sendRequest = new ValidateBitsharesSendRequest(
this.getContext(),
grapheneAccountSelected,
this.etTo.getText().toString(),
amount,
((CryptoCurrency) spAsset.getSelectedItem()).getName(),
etMemo.getText().toString()
);
final ValidateBitsharesSendRequest sendRequest = new ValidateBitsharesSendRequest(
this.getContext(),
grapheneAccountSelected,
this.etTo.getText().toString(),
amount,
((CryptoCurrency)spAsset.getSelectedItem()).getName(),
etMemo.getText().toString()
);
sendRequest.setListener(new CryptoNetInfoRequestListener() {
@Override
public void onCarryOut() {
if (sendRequest.getStatus().equals(ValidateBitsharesSendRequest.StatusCode.SUCCEEDED)){
try {
this.finalize();
} catch (Throwable throwable) {
throwable.printStackTrace();
sendRequest.setListener(new CryptoNetInfoRequestListener() {
@Override
public void onCarryOut() {
if (((ValidateBitsharesSendRequest)sendRequest).getStatus().equals(ValidateBitsharesSendRequest.StatusCode.SUCCEEDED)) {
try {
crystalDialog.dismiss();
thisFragment.dismiss();
//thisFragment.finalize();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
} else {
crystalDialog.dismiss();
Toast.makeText(getContext(), getContext().getString(R.string.unable_to_send_amount), Toast.LENGTH_LONG);
}
}
});
} else {
CryptoCoin cryptoCoin = CryptoCoin.getByCryptoNet(this.cryptoNetAccount.getCryptoNet()).get(0);
//TODO convert the amount to long type using the precision of the currency
Double amountFromEditText = Double.parseDouble(this.etAmount.getText().toString());
Long amount = (long) Math.floor(amountFromEditText * (Math.pow(10, cryptoCoin.getPrecision())));
sendRequest = new BitcoinSendRequest(
this.getContext(),
this.cryptoNetAccount,
this.etTo.getText().toString(),
amount,
cryptoCoin,
etMemo.getText().toString()
);
sendRequest.setListener(new CryptoNetInfoRequestListener() {
@Override
public void onCarryOut() {
if (((BitcoinSendRequest)sendRequest).getStatus().equals(ValidateBitsharesSendRequest.StatusCode.SUCCEEDED)) {
try {
crystalDialog.dismiss();
thisFragment.dismiss();
//thisFragment.finalize();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
} else {
crystalDialog.dismiss();
Toast.makeText(getContext(), getContext().getString(R.string.unable_to_send_amount), Toast.LENGTH_LONG);
}
}
});
}
/* If exists mode security show it and validate events in case of success or fail */
CrystalSecurityMonitor.getInstance(this.getActivity()).callPasswordRequest(this.getActivity(), new OnResponse() {
@Override
public void onSuccess() {
/* Show loading dialog */
crystalDialog = new CrystalDialog((Activity) getContext());
crystalDialog.setText("Sending");
crystalDialog.progress();
crystalDialog.show();
CryptoNetInfoRequests.getInstance().addRequest(sendRequest);
}
@Override
public void onFailed() {
}
});
CryptoNetInfoRequests.getInstance().addRequest(sendRequest);
//CryptoNetInfoRequests.getInstance().addRequest(sendRequest);
}
}
public void beginScanQrCode(){
//mScannerView = new ZXingScannerView(getContext());
mScannerView.setFormats(listOf(BarcodeFormat.QR_CODE));
mScannerView.setAspectTolerance(0.5f);
mScannerView.setAutoFocus(true);
mScannerView.setLaserColor(R.color.colorAccent);
mScannerView.setMaskColor(R.color.colorAccent);
@ -462,24 +578,6 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
// Camera Permissions
private static final int REQUEST_CAMERA_PERMISSION = 1;
private static String[] PERMISSIONS_CAMERA = {
Manifest.permission.CAMERA
};
public static void verifyCameraPermissions(Activity activity) {
// Check if we have write permission
int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.CAMERA);
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(
activity,
PERMISSIONS_CAMERA,
REQUEST_CAMERA_PERMISSION
);
}
}
@Override
public void onValidationSucceeded(final ValidationField field) {
@ -534,13 +632,14 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
@Override
public void handleResult(Result result) {
try {
System.out.println("CAMERA result " + result.getText() );
Invoice invoice = Invoice.fromQrCode(result.getText());
Log.d(TAG, "QR Code read: " + invoice.toJsonString());
etTo.setText(invoice.getTo());
for (int i = 0; i < assetAdapter.getCount(); i++) {
if (assetAdapter.getItem(i).getName().equals(invoice.getCurrency())) {
if (assetAdapter.getItem(i).getName().equals(invoice.getCurrency().toUpperCase())) {
spAsset.setSelection(i);
break;
}
@ -557,8 +656,34 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
df.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.ENGLISH));
etAmount.setText(df.format(amount));
Log.i("SendFragment", result.getText());
return;
}catch(Exception e){
e.printStackTrace();
}
//Is not a bitshares QR
//CryptoCoin cryptoCoin = CryptoCoin.getByCryptoNet(this.cryptoNetAccount.getCryptoNet()).get(0);
final BitcoinUriParseRequest bitcoinUriParseRequest = new BitcoinUriParseRequest(result.getText(), CryptoCoin.BITCOIN);
bitcoinUriParseRequest.setListener(new CryptoNetInfoRequestListener() {
@Override
public void onCarryOut() {
if (bitcoinUriParseRequest.getAddress() != null) {
if (!bitcoinUriParseRequest.getAddress().equals("")) {
etTo.setText(bitcoinUriParseRequest.getAddress());
}
if (bitcoinUriParseRequest.getAmount() > 0) {
etAmount.setText(bitcoinUriParseRequest.getAmount().toString());
}
if (!bitcoinUriParseRequest.getMemo().equals("")) {
etMemo.setText(bitcoinUriParseRequest.getMemo());
}
} else {
Toast.makeText(getContext(), "Not a valid QR info", Toast.LENGTH_LONG);
}
}
});
CryptoNetInfoRequests.getInstance().addRequest(bitcoinUriParseRequest);
}
}
}

View File

@ -5,18 +5,20 @@ import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.arch.paging.PagedList;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
@ -25,27 +27,30 @@ import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnTextChanged;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
import cy.agorise.crystalwallet.models.CryptoCoinTransactionExtended;
import cy.agorise.crystalwallet.viewmodels.TransactionListViewModel;
import cy.agorise.crystalwallet.views.TransactionListView;
import cy.agorise.crystalwallet.views.TransactionListAdapter;
import cy.agorise.crystalwallet.views.TransactionOrderSpinnerAdapter;
public class TransactionsFragment extends Fragment {
@BindView(R.id.vTransactionListView)
TransactionListView transactionListView;
@BindView(R.id.spTransactionsOrder)
Spinner spTransactionsOrder;
@BindView(R.id.etTransactionSearch)
EditText etTransactionSearch;
RecyclerView balanceRecyclerView;
@BindView(R.id.tvNoTransactions)
TextView tvNoTransactions;
@BindView(R.id.rvTransactions)
RecyclerView rvTransactions;
FloatingActionButton fabSend;
FloatingActionButton fabReceive;
TransactionListAdapter transactionListAdapter;
TransactionListViewModel transactionListViewModel;
LiveData<PagedList<CryptoCoinTransactionExtended>> transactionsLiveData;
@ -66,20 +71,22 @@ public class TransactionsFragment extends Fragment {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_transactions, container, false);
ButterKnife.bind(this, view);
// Gets the Balance RecyclerView
balanceRecyclerView = view.findViewById(R.id.transactionListView);
rvTransactions.setLayoutManager(new LinearLayoutManager(getContext()));
transactionListAdapter = new TransactionListAdapter(this);
rvTransactions.setAdapter(transactionListAdapter);
fabSend = getActivity().findViewById(R.id.fabSend);
fabReceive = getActivity().findViewById(R.id.fabReceive);
// TODO move this listener to the activity, to make this fragment reusable
// Adds listener to the RecyclerView to show and hide buttons at the bottom of the screen
balanceRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
rvTransactions.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx,int dy){
super.onScrolled(recyclerView, dx, dy);
@ -119,12 +126,17 @@ public class TransactionsFragment extends Fragment {
transactionListViewModel.initTransactionList(orderSelected.getField(),etTransactionSearch.getText().toString());
transactionsLiveData = transactionListViewModel.getTransactionList();
final Fragment fragment = this;
transactionsLiveData.observe(this, new Observer<PagedList<CryptoCoinTransactionExtended>>() {
@Override
public void onChanged(@Nullable PagedList<CryptoCoinTransactionExtended> cryptoCoinTransactions) {
Log.i("Transactions","Transactions have change! Count:"+cryptoCoinTransactions.size());
transactionListView.setData(cryptoCoinTransactions, fragment);
public void onChanged(@Nullable PagedList<CryptoCoinTransactionExtended> transactions) {
transactionListAdapter.submitList(transactions);
if(transactions != null && transactions.size() > 0){
tvNoTransactions.setVisibility(View.INVISIBLE);
}
else{
tvNoTransactions.setVisibility(View.VISIBLE);
}
}
});
}
@ -136,7 +148,7 @@ public class TransactionsFragment extends Fragment {
}
public void initTransactionsOrderSpinner(){
List<TransactionOrderSpinnerAdapter.TransactionOrderSpinnerItem> spinnerValues = new ArrayList<TransactionOrderSpinnerAdapter.TransactionOrderSpinnerItem>();
List<TransactionOrderSpinnerAdapter.TransactionOrderSpinnerItem> spinnerValues = new ArrayList<>();
spinnerValues.add(new TransactionOrderSpinnerAdapter.TransactionOrderSpinnerItem("date","Date",0,false));
spinnerValues.add(new TransactionOrderSpinnerAdapter.TransactionOrderSpinnerItem("amount","Amount",0,false));
spinnerValues.add(new TransactionOrderSpinnerAdapter.TransactionOrderSpinnerItem("is_input","In/Out",0,false));

View File

@ -0,0 +1,6 @@
package cy.agorise.crystalwallet.interfaces;
public interface OnResponse {
void onSuccess();
void onFailed();
}

View File

@ -2,11 +2,13 @@ package cy.agorise.crystalwallet.manager;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log;
import com.google.common.primitives.UnsignedLong;
import org.bitcoinj.core.ECKey;
import java.math.BigInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@ -20,12 +22,14 @@ import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator;
import cy.agorise.crystalwallet.apigenerator.grapheneoperation.AccountUpgradeOperationBuilder;
import cy.agorise.crystalwallet.application.constant.BitsharesConstant;
import cy.agorise.crystalwallet.dao.AccountSeedDao;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.models.BitsharesAccountNameCache;
import cy.agorise.crystalwallet.models.seed.BIP39;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetEquivalentRequest;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequest;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestsListener;
import cy.agorise.crystalwallet.requestmanagers.GetBitsharesAccountNameCacheRequest;
import cy.agorise.crystalwallet.requestmanagers.ImportBitsharesAccountRequest;
import cy.agorise.crystalwallet.requestmanagers.ValidateBitsharesLTMUpgradeRequest;
import cy.agorise.crystalwallet.requestmanagers.ValidateBitsharesSendRequest;
import cy.agorise.crystalwallet.requestmanagers.ValidateCreateBitsharesAccountRequest;
@ -55,6 +59,7 @@ import cy.agorise.graphenej.UserAccount;
import cy.agorise.graphenej.models.AccountProperties;
import cy.agorise.graphenej.models.BlockHeader;
import cy.agorise.graphenej.models.HistoricalTransfer;
import cy.agorise.graphenej.objects.Memo;
import cy.agorise.graphenej.operations.TransferOperationBuilder;
/**
@ -64,8 +69,6 @@ import cy.agorise.graphenej.operations.TransferOperationBuilder;
*/
public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetInfoRequestsListener {
//private final static String BITSHARES_TESTNET_CHAIN_ID= "9cf6f255a208100d2bb275a3c52f4b1589b7ec9c9bfc2cb2a5fe6411295106d8";
private final static String SIMPLE_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
private final static String DEFAULT_TIME_ZONE = "GMT";
@ -89,6 +92,11 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
long idAccount = db.cryptoNetAccountDao().insertCryptoNetAccount(fetch)[0];
fetch.setId(idAccount);
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(fetch));
AccountSeed seed = db.accountSeedDao().findById(grapheneAccount.getSeedId());
if(seed.getName() == null || seed.getName().isEmpty()){
seed.setName(grapheneAccount.getName());
db.accountSeedDao().insertAccountSeed(seed);
}
subscribeBitsharesAccount(fetch.getId(),fetch.getAccountId(),context);
request.success(fetch);
}
@ -129,6 +137,11 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
long[] idAccount = db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount);
grapheneAccount.setId(idAccount[0]);
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount));
AccountSeed seed = db.accountSeedDao().findById(grapheneAccount.getSeedId());
if(seed.getName() == null || seed.getName().isEmpty()){
seed.setName(grapheneAccount.getName());
db.accountSeedDao().insertAccountSeed(seed);
}
subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
}
@ -145,8 +158,14 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
GrapheneAccount fetch = (GrapheneAccount) answer;
grapheneAccount.setName(fetch.getName());
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount);
long idAccount = db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount)[0];
grapheneAccount.setId(idAccount);
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount));
AccountSeed seed = db.accountSeedDao().findById(grapheneAccount.getSeedId());
if(seed.getName() == null || seed.getName().isEmpty()){
seed.setName(grapheneAccount.getName());
db.accountSeedDao().insertAccountSeed(seed);
}
subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
}
@ -157,8 +176,14 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
});
}else {
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount);
long idAccount = db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount)[0];
grapheneAccount.setId(idAccount);
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount));
AccountSeed seed = db.accountSeedDao().findById(grapheneAccount.getSeedId());
if(seed.getName() == null || seed.getName().isEmpty()){
seed.setName(grapheneAccount.getName());
db.accountSeedDao().insertAccountSeed(seed);
}
subscribeBitsharesAccount(grapheneAccount.getId(), grapheneAccount.getAccountId(), context);
}
}
@ -179,6 +204,11 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
info.setAccountId(fetch.getAccountId());
grapheneAccount.setAccountId(fetch.getAccountId());
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(info);
AccountSeed seed = db.accountSeedDao().findById(grapheneAccount.getSeedId());
if(seed.getName() == null || seed.getName().isEmpty()){
seed.setName(grapheneAccount.getName());
db.accountSeedDao().insertAccountSeed(seed);
}
subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
}
@ -195,6 +225,11 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
info.setName(fetch.getName());
grapheneAccount.setName(fetch.getName());
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(info);
AccountSeed seed = db.accountSeedDao().findById(grapheneAccount.getSeedId());
if(seed.getName() == null || seed.getName().isEmpty()){
seed.setName(grapheneAccount.getName());
db.accountSeedDao().insertAccountSeed(seed);
}
subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
}
@ -204,6 +239,11 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
}
});
}else{
AccountSeed seed = db.accountSeedDao().findById(grapheneAccount.getSeedId());
if(seed.getName() == null || seed.getName().isEmpty()){
seed.setName(grapheneAccount.getName());
db.accountSeedDao().insertAccountSeed(seed);
}
subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
}
}
@ -212,7 +252,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
private void subscribeBitsharesAccount(long accountId, String accountBitsharesID, Context context){
GrapheneApiGenerator.subscribeBitsharesAccount(accountId,accountBitsharesID,context);
BitsharesAccountManager.refreshAccountTransactions(accountId,context);
GrapheneApiGenerator.getAccountBalance(accountId,accountBitsharesID,context);
GrapheneApiGenerator.getAccountBalance(accountId,accountBitsharesID,CryptoNet.BITSHARES,context);
}
/**
@ -221,27 +261,103 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
*/
@Override
public void onNewRequest(CryptoNetInfoRequest request) {
if (request instanceof ValidateImportBitsharesAccountRequest){
this.validateImportAccount((ValidateImportBitsharesAccountRequest) request);
} else if (request instanceof ValidateExistBitsharesAccountRequest){
this.validateExistAcccount((ValidateExistBitsharesAccountRequest) request);
} else if (request instanceof ValidateBitsharesSendRequest){
this.validateSendRequest((ValidateBitsharesSendRequest) request);
}else if (request instanceof CryptoNetEquivalentRequest){
this.getEquivalentValue((CryptoNetEquivalentRequest) request);
}else if (request instanceof ValidateCreateBitsharesAccountRequest){
this.validateCreateAccount((ValidateCreateBitsharesAccountRequest) request);
}else if (request instanceof ValidateBitsharesLTMUpgradeRequest){
this.validateLTMAccountUpgrade((ValidateBitsharesLTMUpgradeRequest) request);
}else if (request instanceof GetBitsharesAccountNameCacheRequest){
this.getBitsharesAccountNameCacheRequest((GetBitsharesAccountNameCacheRequest) request);
}else{
if(request.getCoin().equals(CryptoCoin.BITSHARES)) {
if (request instanceof ImportBitsharesAccountRequest) {
this.importAccount((ImportBitsharesAccountRequest) request);
} else if (request instanceof ValidateImportBitsharesAccountRequest) {
this.validateImportAccount((ValidateImportBitsharesAccountRequest) request);
} else if (request instanceof ValidateExistBitsharesAccountRequest) {
this.validateExistAcccount((ValidateExistBitsharesAccountRequest) request);
} else if (request instanceof ValidateBitsharesSendRequest) {
this.validateSendRequest((ValidateBitsharesSendRequest) request);
} else if (request instanceof CryptoNetEquivalentRequest) {
this.getEquivalentValue((CryptoNetEquivalentRequest) request);
} else if (request instanceof ValidateCreateBitsharesAccountRequest) {
this.validateCreateAccount((ValidateCreateBitsharesAccountRequest) request);
} else if (request instanceof ValidateBitsharesLTMUpgradeRequest) {
this.validateLTMAccountUpgrade((ValidateBitsharesLTMUpgradeRequest) request);
} else if (request instanceof GetBitsharesAccountNameCacheRequest) {
this.getBitsharesAccountNameCacheRequest((GetBitsharesAccountNameCacheRequest) request);
} else {
//TODO not implemented
System.out.println("Error request not implemented " + request.getClass().getName());
//TODO not implemented
System.out.println("Error request not implemented " + request.getClass().getName());
}
}
}
private void importAccount(final ImportBitsharesAccountRequest importRequest){
final CrystalDatabase db = CrystalDatabase.getAppDatabase(importRequest.getContext());
final AccountSeedDao accountSeedDao = db.accountSeedDao();
ApiRequest getAccountNamesBK = new ApiRequest(0, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
if(answer != null && importRequest.getStatus().equals(ImportBitsharesAccountRequest.StatusCode.NOT_STARTED)) {
UserAccount userAccount = (UserAccount) answer;
importRequest.setSeedType(SeedType.BRAINKEY);
importRequest.setStatus(ImportBitsharesAccountRequest.StatusCode.SUCCEEDED);
AccountSeed seed = new AccountSeed();
seed.setName(userAccount.getName());
seed.setType(importRequest.getSeedType());
seed.setMasterSeed(importRequest.getMnemonic());
long idSeed = accountSeedDao.insertAccountSeed(seed);
if (idSeed >= 0) {
GrapheneAccount account = new GrapheneAccount();
account.setCryptoNet(CryptoNet.BITSHARES);
account.setAccountIndex(0);
account.setSeedId(idSeed);
account.setAccountId(userAccount.getObjectId());
importAccountFromSeed(account, importRequest.getContext());
}
}
}
@Override
public void fail(int idPetition) {
BIP39 bip39 = new BIP39(-1, importRequest.getMnemonic());
ApiRequest getAccountNamesBP39 = new ApiRequest(0, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
if(answer != null && importRequest.getStatus().equals(ImportBitsharesAccountRequest.StatusCode.NOT_STARTED)) {
UserAccount userAccount = (UserAccount) answer;
importRequest.setSeedType(SeedType.BIP39);
importRequest.setStatus(ImportBitsharesAccountRequest.StatusCode.SUCCEEDED);
AccountSeed seed = new AccountSeed();
seed.setName(userAccount.getName());
seed.setType(importRequest.getSeedType());
seed.setMasterSeed(importRequest.getMnemonic());
long idSeed = accountSeedDao.insertAccountSeed(seed);
if (idSeed >= 0) {
GrapheneAccount account = new GrapheneAccount();
account.setCryptoNet(CryptoNet.BITSHARES);
account.setAccountIndex(0);
account.setSeedId(idSeed);
account.setAccountId(userAccount.getObjectId());
importAccountFromSeed(account, importRequest.getContext());
}
}
}
@Override
public void fail(int idPetition) {
importRequest.setStatus(ImportBitsharesAccountRequest.StatusCode.BAD_SEED);
}
});
GrapheneApiGenerator.getAccountByOwnerOrActiveAddress(new Address(ECKey.fromPublicOnly(bip39.getBitsharesActiveKey(0).getPubKey())),CryptoNet.BITSHARES,getAccountNamesBP39);
}
});
BrainKey bk = new BrainKey(importRequest.getMnemonic(), 0);
GrapheneApiGenerator.getAccountByOwnerOrActiveAddress(bk.getPublicAddress("BTS"),CryptoNet.BITSHARES,getAccountNamesBK);
}
/**
* Process the import account request
*/
@ -307,7 +423,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.NO_ACCOUNT_DATA);
}
});
GrapheneApiGenerator.getAccountById((String)answer,getAccountInfo);
GrapheneApiGenerator.getAccountById((String)answer,CryptoNet.BITSHARES,getAccountInfo);
}
@Override
@ -317,7 +433,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
}
});
GrapheneApiGenerator.getAccountIdByName(importRequest.getAccountName(),checkAccountName);
GrapheneApiGenerator.getAccountIdByName(importRequest.getAccountName(),CryptoNet.BITSHARES,checkAccountName);
}
private void validateCreateAccount(final ValidateCreateBitsharesAccountRequest createRequest){
@ -372,7 +488,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
validateRequest.setAccountExists(false);
}
});
GrapheneApiGenerator.getAccountIdByName(validateRequest.getAccountName(),checkAccountName);
GrapheneApiGenerator.getAccountIdByName(validateRequest.getAccountName(),CryptoNet.BITSHARES,checkAccountName);
}
/**
@ -471,9 +587,16 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
.setTransferAmount(new AssetAmount(UnsignedLong.valueOf(sendRequest.getAmount()), new Asset(idAsset)))
.setFee(new AssetAmount(UnsignedLong.valueOf(0), feeAsset));
if (sendRequest.getMemo() != null) {
//builder.setMemo(new Memo(fromUserAccount,toUserAccount,0,sendRequest.getMemo().getBytes()));
//TODO memo
System.out.println("transaction has memo");
try {
//TODO change authority for memo
Address fromAddress = new Address(fromUserAccount.getActive().getKeyAuthList().get(0).getKey(), "BTS");
Address toAddress = new Address(toUserAccount.getActive().getKeyAuthList().get(0).getKey(), "BTS");
builder.setMemo(new Memo(fromAddress,toAddress,BigInteger.ZERO,sendRequest.getMemo().getBytes()));
//TODO add random nonce
}catch ( Exception e){
e.printStackTrace();
//TODO error
}
}
ArrayList<BaseOperation> operationList = new ArrayList<>();
operationList.add(builder.build());
@ -570,7 +693,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
AccountIdOrNameListener listener = new AccountIdOrNameListener(request);
ApiRequest accountRequest = new ApiRequest(0, listener);
GrapheneApiGenerator.getAccountById(grapheneId,accountRequest);
GrapheneApiGenerator.getAccountById(grapheneId,CryptoNet.BITSHARES,accountRequest);
}
/**
@ -582,7 +705,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
AccountIdOrNameListener listener = new AccountIdOrNameListener(request);
ApiRequest accountRequest = new ApiRequest(0, listener);
GrapheneApiGenerator.getAccountByName(grapheneName,accountRequest);
GrapheneApiGenerator.getAccountByName(grapheneName,CryptoNet.BITSHARES,accountRequest);
}
@ -593,7 +716,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
ApiRequest assetRequest = new ApiRequest(0, nameListener);
ArrayList<String> assetNames = new ArrayList<>();
assetNames.add(assetName);
GrapheneApiGenerator.getAssetByName(assetNames, assetRequest);
GrapheneApiGenerator.getAssetByName(assetNames, CryptoNet.BITSHARES,assetRequest);
}
@ -616,7 +739,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
int stop = start + limit;
ApiRequest transactionRequest = new ApiRequest(0, new TransactionRequestListener(start, stop, limit, grapheneAccount, db));
GrapheneApiGenerator.getAccountTransaction(grapheneAccount.getAccountId(), start, stop, limit, transactionRequest);
GrapheneApiGenerator.getAccountTransaction(grapheneAccount.getAccountId(), start, stop, limit, CryptoNet.BITSHARES,transactionRequest);
}
}
@ -706,7 +829,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
});
ArrayList<String> assets = new ArrayList<>();
assets.add(transfer.getOperation().getAssetAmount().getAsset().getObjectId());
GrapheneApiGenerator.getAssetById(assets,assetRequest);
GrapheneApiGenerator.getAssetById(assets,CryptoNet.BITSHARES,assetRequest);
}else{
saveTransaction(transaction,info,transfer);
@ -719,7 +842,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
int newStart= start + limit;
int newStop= stop + limit;
ApiRequest transactionRequest = new ApiRequest(newStart/limit, new TransactionRequestListener(newStart,newStop,limit,account,db));
GrapheneApiGenerator.getAccountTransaction(account.getAccountId(),newStart,newStop,limit,transactionRequest);
GrapheneApiGenerator.getAccountTransaction(account.getAccountId(),newStart,newStop,limit,CryptoNet.BITSHARES,transactionRequest);
}
}
@ -735,7 +858,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
transaction.setInput(!transfer.getOperation().getFrom().getObjectId().equals(account.getAccountId()));
transaction.setTo(transfer.getOperation().getTo().getObjectId());
GrapheneApiGenerator.getBlockHeaderTime(transfer.getBlockNum(), new ApiRequest(0, new GetTransactionDate(transaction, db.transactionDao())));
GrapheneApiGenerator.getBlockHeaderTime(transfer.getBlockNum(),CryptoNet.BITSHARES, new ApiRequest(0, new GetTransactionDate(transaction, db.transactionDao())));
}
}

View File

@ -327,7 +327,6 @@ public class FileBackupManager implements FileServiceRequestsListener {
public static byte[] decompress(byte[] inputBytes, int which) {
InputStream in = null;
try {
System.out.println("Bytes: "+Util.bytesToHex(inputBytes));
ByteArrayInputStream input = new ByteArrayInputStream(inputBytes);
ByteArrayOutputStream output = new ByteArrayOutputStream(16*2048);
if(which == Util.XZ) {

View File

@ -3,32 +3,74 @@ package cy.agorise.crystalwallet.manager;
import android.content.Context;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionOutPoint;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.HDKeyDerivation;
import org.bitcoinj.script.Script;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import cy.agorise.crystalwallet.apigenerator.ApiRequest;
import cy.agorise.crystalwallet.apigenerator.ApiRequestListener;
import cy.agorise.crystalwallet.apigenerator.InsightApiGenerator;
import cy.agorise.crystalwallet.apigenerator.insightapi.BroadcastTransaction;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Txi;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Vin;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Vout;
import cy.agorise.crystalwallet.dao.BitcoinAddressDao;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.BitcoinAddress;
import cy.agorise.crystalwallet.models.BitcoinTransaction;
import cy.agorise.crystalwallet.models.BitcoinTransactionGTxIO;
import cy.agorise.crystalwallet.models.CryptoCoinBalance;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
import cy.agorise.crystalwallet.models.CryptoCurrency;
import cy.agorise.crystalwallet.models.CryptoNetAccount;
import cy.agorise.crystalwallet.models.GTxIO;
import cy.agorise.crystalwallet.models.GeneralCoinAddress;
import cy.agorise.crystalwallet.models.GeneralTransaction;
import cy.agorise.crystalwallet.requestmanagers.BitcoinSendRequest;
import cy.agorise.crystalwallet.requestmanagers.BitcoinUriParseRequest;
import cy.agorise.crystalwallet.requestmanagers.CalculateBitcoinUriRequest;
import cy.agorise.crystalwallet.requestmanagers.CreateBitcoinAccountRequest;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequest;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestsListener;
import cy.agorise.crystalwallet.requestmanagers.GeneralAccountSendRequest;
import cy.agorise.crystalwallet.requestmanagers.NextBitcoinAccountAddressRequest;
import cy.agorise.crystalwallet.requestmanagers.ValidateBitcoinAddressRequest;
import cy.agorise.graphenej.Util;
public class GeneralAccountManager implements CryptoAccountManager, CryptoNetInfoRequestsListener {
static HashMap<CryptoCoin, GeneralAccountManager> generalAccountManagers = new HashMap();
private static CryptoCoin[] SUPPORTED_COINS = new CryptoCoin[]{
CryptoCoin.BITCOIN,
CryptoCoin.BITCOIN_TEST,
CryptoCoin.DASH,
CryptoCoin.LITECOIN
} ;
final CryptoCoin cryptoCoin;
final Context context;
public static GeneralAccountManager getAccountManager(CryptoCoin coin){
return generalAccountManagers.get(coin);
}
public GeneralAccountManager(CryptoCoin cryptoCoin, Context context) {
this.cryptoCoin = cryptoCoin;
this.context = context;
generalAccountManagers.put(cryptoCoin,this);
}
@Override
public void createAccountFromSeed(CryptoNetAccount account, ManagerRequest request, Context context) {
@ -40,55 +82,347 @@ public class GeneralAccountManager implements CryptoAccountManager, CryptoNetInf
}
@Override
public void loadAccountFromDB(CryptoNetAccount account, Context context) {
public void loadAccountFromDB(final CryptoNetAccount account, Context context) {
final CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
AccountSeed seed = db.accountSeedDao().findById(account.getSeedId());
DeterministicKey purposeKey = HDKeyDerivation.deriveChildKey((DeterministicKey) seed.getPrivateKey(),
new ChildNumber(44, true));
DeterministicKey coinKey = HDKeyDerivation.deriveChildKey(purposeKey,
new ChildNumber(cryptoCoin.getCoinNumber(), true));
DeterministicKey accountKey = HDKeyDerivation.deriveChildKey(coinKey,
new ChildNumber(account.getAccountIndex(), true));
final DeterministicKey externalKey = HDKeyDerivation.deriveChildKey(accountKey,
new ChildNumber(0, false));
final DeterministicKey changeKey = HDKeyDerivation.deriveChildKey(accountKey,
new ChildNumber(1, false));
CryptoCoinBalance balance = new CryptoCoinBalance();
long amount = 0;
List<CryptoCoinTransaction> trransactions = db.transactionDao().getByIdAccount(account.getId());
for(CryptoCoinTransaction transaction : trransactions){
if(transaction.isConfirmed()){
if(transaction.getInput()){
amount += transaction.getAmount();
}else{
amount -= transaction.getAmount();
}
}
}
balance.setBalance(amount);
balance.setCryptoCurrencyId(db.cryptoCurrencyDao().getByName(cryptoCoin.getLabel(),cryptoCoin.name()).getId());
balance.setAccountId(account.getId());
db.cryptoCoinBalanceDao().insertCryptoCoinBalance(balance);
long indexExternal = db.bitcoinAddressDao().getLastExternalAddress(account.getId());
if(indexExternal > 0){
for(int i = 0; i < indexExternal;i++){
BitcoinAddress address = db.bitcoinAddressDao().getExternalByIndex(i);
InsightApiGenerator.getTransactionFromAddress(cryptoCoin,address.getAddress(),true,null);
}
}else {
ECKey externalAddrKey = HDKeyDerivation.deriveChildKey(externalKey, new ChildNumber((int) 0, true));
BitcoinAddress address = new BitcoinAddress();
address.setChange(false);
address.setAccountId(account.getId());
address.setIndex(0);
String addressString = externalAddrKey.toAddress(this.cryptoCoin.getParameters()).toString();
address.setAddress(addressString);
db.bitcoinAddressDao().insertBitcoinAddresses(address);
InsightApiGenerator.getTransactionFromAddress(cryptoCoin,addressString,true,
new CheckAddressForTransaction(db.bitcoinAddressDao(),account.getId(),externalKey,false,0));
}
long indexChange = db.bitcoinAddressDao().getLastChangeAddress(account.getId());
if(indexChange > 0){
for(int i = 0; i < indexChange;i++){
BitcoinAddress address = db.bitcoinAddressDao().getChangeByIndex(i);
InsightApiGenerator.getTransactionFromAddress(cryptoCoin,address.getAddress(),true,null);
}
}else {
ECKey changeAddrKey = HDKeyDerivation.deriveChildKey(changeKey, new ChildNumber((int) 0, true));
BitcoinAddress address = new BitcoinAddress();
address.setChange(true);
address.setAccountId(account.getId());
address.setIndex(0);
String addressString =changeAddrKey.toAddress(this.cryptoCoin.getParameters()).toString();
address.setAddress(addressString);
db.bitcoinAddressDao().insertBitcoinAddresses(address);
InsightApiGenerator.getTransactionFromAddress(cryptoCoin,addressString,true,
new CheckAddressForTransaction(db.bitcoinAddressDao(),account.getId(),externalKey,true,0));
}
}
@Override
public void onNewRequest(CryptoNetInfoRequest request) {
//if(Arrays.asList(SUPPORTED_COINS).contains(request.getCoin())){
if(request.getCoin().equals(this.cryptoCoin)){
if(request instanceof BitcoinSendRequest) {
this.send((BitcoinSendRequest) request);
}else if(request instanceof CreateBitcoinAccountRequest){
this.createGeneralAccount((CreateBitcoinAccountRequest) request);
}else if(request instanceof NextBitcoinAccountAddressRequest){
this.getNextAddress((NextBitcoinAccountAddressRequest) request);
}else if(request instanceof ValidateBitcoinAddressRequest){
this.validateAddress((ValidateBitcoinAddressRequest) request);
}else if(request instanceof CalculateBitcoinUriRequest){
this.calculateUri((CalculateBitcoinUriRequest) request);
}else if(request instanceof BitcoinUriParseRequest){
this.parseUri((BitcoinUriParseRequest) request);
}else{
System.out.println("Invalid " +this.cryptoCoin.getLabel() + " request ");
}
}
}
/**
* Class that process each transaction fetched by the insight api
* @param txi
*/
public void processTxi(Txi txi){
try {
System.out.println("GeneralAccountManager processingTxi " + txi.txid);
CrystalDatabase db = CrystalDatabase.getAppDatabase(this.context);
List<BitcoinTransaction> btTransactions = db.bitcoinTransactionDao().getTransactionsByTxid(txi.txid);
if (!btTransactions.isEmpty()) {
System.out.println("GeneralAccountManager Transaction not null " + txi.txid);
for (BitcoinTransaction btTransaction : btTransactions) {
btTransaction.setConfirmations(txi.confirmations);
CryptoCoinTransaction ccTransaction = db.transactionDao().getById(btTransaction.getCryptoCoinTransactionId());
if (!ccTransaction.isConfirmed() && btTransaction.getConfirmations() >= cryptoCoin.getCryptoNet().getConfirmationsNeeded()) {
ccTransaction.setConfirmed(true);
db.transactionDao().insertTransaction(ccTransaction);
updateBalance(ccTransaction, (ccTransaction.getInput() ? 1 : -1) * ccTransaction.getAmount(), db);
}
db.bitcoinTransactionDao().insertBitcoinTransaction(btTransaction);
}
} else {
/*List<CryptoCoinTransaction> ccTransactions = new ArrayList();
btTransactions = new ArrayList();*/ //TODO transactions involving multiples accounts
CryptoCoinTransaction ccTransaction = new CryptoCoinTransaction();
BitcoinTransaction btTransaction = new BitcoinTransaction();
btTransaction.setTxId(txi.txid);
btTransaction.setBlock(txi.blockheight);
btTransaction.setFee((long) (txi.fee * Math.pow(10, cryptoCoin.getPrecision())));
btTransaction.setConfirmations(txi.confirmations);
ccTransaction.setDate(new Date(txi.time * 1000));
if (txi.txlock || txi.confirmations >= cryptoCoin.getCryptoNet().getConfirmationsNeeded()) {
ccTransaction.setConfirmed(true);
} else {
ccTransaction.setConfirmed(false);
}
ccTransaction.setInput(false);
long amount = 0;
//transaction.setAccount(this.mAccount);
//transaction.setType(cryptoCoin);
List<BitcoinTransactionGTxIO> gtxios = new ArrayList();
for (Vin vin : txi.vin) {
BitcoinTransactionGTxIO input = new BitcoinTransactionGTxIO();
String addr = vin.addr;
input.setAddress(addr);
input.setIndex(vin.n);
input.setOutput(true);
input.setAmount((long) (vin.value * Math.pow(10, cryptoCoin.getPrecision())));
input.setOriginalTxId(vin.txid);
input.setScriptHex(vin.scriptSig.hex);
BitcoinAddress address = db.bitcoinAddressDao().getdadress(addr);
if (address != null) {
if (ccTransaction.getAccountId() < 0) {
ccTransaction.setAccountId(address.getAccountId());
ccTransaction.setFrom(addr);
ccTransaction.setInput(false);
amount -= (long) (txi.fee * Math.pow(10, cryptoCoin.getPrecision()));
}
//if (ccTransaction.getAccountId() == address.getAccountId()) {
amount -= (long) (vin.value * Math.pow(10, cryptoCoin.getPrecision()));
//}
}
if (ccTransaction.getFrom() == null || ccTransaction.getFrom().isEmpty()) {
ccTransaction.setFrom(addr);
}
gtxios.add(input);
}
for (Vout vout : txi.vout) {
if (vout.scriptPubKey.addresses == null || vout.scriptPubKey.addresses.length <= 0) {
} else {
BitcoinTransactionGTxIO output = new BitcoinTransactionGTxIO();
String addr = vout.scriptPubKey.addresses[0];
output.setAddress(addr);
output.setIndex(vout.n);
output.setOutput(false);
output.setAmount((long) (vout.value * Math.pow(10, cryptoCoin.getPrecision())));
output.setScriptHex(vout.scriptPubKey.hex);
output.setOriginalTxId(txi.txid);
gtxios.add(output);
BitcoinAddress address = db.bitcoinAddressDao().getdadress(addr);
if (address != null) {
if (ccTransaction.getAccountId() < 0) {
ccTransaction.setAccountId(address.getAccountId());
ccTransaction.setInput(true);
ccTransaction.setTo(addr);
}
//if (ccTransaction.getAccountId() == address.getAccountId()) {
amount += (long) (vout.value * Math.pow(10, cryptoCoin.getPrecision()));
//}
} else {
//TOOD multiple send address
if (ccTransaction.getTo() == null || ccTransaction.getTo().isEmpty()) {
ccTransaction.setTo(addr);
}
}
}
}
ccTransaction.setAmount(amount);
CryptoCurrency currency = db.cryptoCurrencyDao().getByNameAndCryptoNet(this.cryptoCoin.getLabel(), this.cryptoCoin.getCryptoNet().name());
if (currency == null) {
currency = new CryptoCurrency();
currency.setCryptoNet(this.cryptoCoin.getCryptoNet());
currency.setName(this.cryptoCoin.getLabel());
currency.setPrecision(this.cryptoCoin.getPrecision());
long idCurrency = db.cryptoCurrencyDao().insertCryptoCurrency(currency)[0];
currency.setId(idCurrency);
}
ccTransaction.setIdCurrency((int) currency.getId());
long ccId = db.transactionDao().insertTransaction(ccTransaction)[0];
btTransaction.setCryptoCoinTransactionId(ccId);
long btId = db.bitcoinTransactionDao().insertBitcoinTransaction(btTransaction)[0];
for (BitcoinTransactionGTxIO gtxio : gtxios) {
gtxio.setBitcoinTransactionId(ccId);
db.bitcoinTransactionDao().insertBitcoinTransactionGTxIO(gtxio);
}
if (ccTransaction.isConfirmed()) {
updateBalance(ccTransaction, amount, db);
}
}
}catch(Exception e){
e.printStackTrace();
}
}
private void createGeneralAccount(CreateBitcoinAccountRequest request){
CrystalDatabase db = CrystalDatabase.getAppDatabase(this.context);
CryptoNetAccount account = new CryptoNetAccount();
account.setAccountIndex(0);
account.setCryptoNet(this.cryptoCoin.getCryptoNet());
account.setName(request.getAccountSeed().getName());
account.setSeedId(request.getAccountSeed().getId());
long idAccount = db.cryptoNetAccountDao().insertCryptoNetAccount(account)[0];
account.setId(idAccount);
loadAccountFromDB(account,request.getContext());
request.setStatus(CreateBitcoinAccountRequest.StatusCode.SUCCEEDED);
}
public void send(final GeneralAccountSendRequest request){
private void updateBalance(CryptoCoinTransaction ccTransaction, long amount, CrystalDatabase db){
CryptoCurrency currency = db.cryptoCurrencyDao().getByNameAndCryptoNet(this.cryptoCoin.getLabel(), this.cryptoCoin.getCryptoNet().name());
if (currency == null) {
currency = new CryptoCurrency();
currency.setCryptoNet(this.cryptoCoin.getCryptoNet());
currency.setName(this.cryptoCoin.getLabel());
currency.setPrecision(this.cryptoCoin.getPrecision());
long idCurrency = db.cryptoCurrencyDao().insertCryptoCurrency(currency)[0];
currency.setId(idCurrency);
}
CryptoCoinBalance balance = db.cryptoCoinBalanceDao().getBalanceFromAccount(ccTransaction.getAccountId(), currency.getId());
if (balance == null) {
balance = new CryptoCoinBalance();
balance.setAccountId(ccTransaction.getAccountId());
balance.setCryptoCurrencyId(currency.getId());
long idBalance = db.cryptoCoinBalanceDao().insertCryptoCoinBalance(balance)[0];
balance.setId(idBalance);
}
balance.setBalance(balance.getBalance()+amount);
db.cryptoCoinBalanceDao().insertCryptoCoinBalance(balance);
}
private void validateAddress(ValidateBitcoinAddressRequest request){
try{
Address address = Address.fromBase58(this.cryptoCoin.getParameters(), request.getAddress());
request.setAddressValid(true);
}catch(AddressFormatException ex){
request.setAddressValid(false);
}
request.validate();
}
public void send(final BitcoinSendRequest request){
//TODO check server connection
//TODO validate to address
InsightApiGenerator.getEstimateFee(request.getAccount().getCryptoNet(),new ApiRequest(1, new ApiRequestListener() {
System.out.println("GeneralAccount Manager Send request, asking fee");
InsightApiGenerator.getEstimateFee(this.cryptoCoin,new ApiRequest(1, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
Transaction tx = new Transaction(request.getAccount().getNetworkParam());
long currentAmount = 0;
long fee = -1;
long feeRate = (Long) answer;
fee = 226 * feeRate;
System.out.println("GeneralAccount Manager Send request, fee " + answer.toString());
try {
Transaction tx = new Transaction(cryptoCoin.getParameters());
long currentAmount = 0;
long fee = -1;
long feeRate = (long) (((double) answer) * Math.pow(10, cryptoCoin.getPrecision()));
fee = 226 * feeRate;
List<GeneralCoinAddress> addresses = request.getAccount().getAddresses();
List<GTxIO> utxos = new ArrayList();
for(GeneralCoinAddress address : addresses){
List<GTxIO> addrUtxos = address.getUTXos();
for(GTxIO addrUtxo : addrUtxos){
utxos.add(addrUtxo);
currentAmount += addrUtxo.getAmount();
if(currentAmount >= request.getAmount()+ fee){
System.out.println("GeneralAccount Manager Send request getting utxos" );
CrystalDatabase db = CrystalDatabase.getAppDatabase(request.getContext());
db.bitcoinTransactionDao();
List<BitcoinTransactionGTxIO> utxos = getUtxos(request.getSourceAccount().getId(), db);
System.out.println("GeneralAccount Manager Send request utxos found " + utxos.size() );
for(BitcoinTransactionGTxIO utxo : utxos){
currentAmount += utxo.getAmount();
if(currentAmount >= request.getAmount() + fee) {
break;
}
}
if(currentAmount >= request.getAmount() + fee){
break;
if (currentAmount < request.getAmount() + fee) {
System.out.println("GeneralAccount Manager Send request no balance" );
request.setStatus(BitcoinSendRequest.StatusCode.NO_BALANCE);
return;
}
}
AccountSeed seed = db.accountSeedDao().findById(request.getSourceAccount().getSeedId());
DeterministicKey purposeKey = HDKeyDerivation.deriveChildKey((DeterministicKey) seed.getPrivateKey(),
new ChildNumber(44, true));
DeterministicKey coinKey = HDKeyDerivation.deriveChildKey(purposeKey,
new ChildNumber(cryptoCoin.getCoinNumber(), true));
DeterministicKey accountKey = HDKeyDerivation.deriveChildKey(coinKey,
new ChildNumber(request.getSourceAccount().getAccountIndex(), true));
DeterministicKey externalKey = HDKeyDerivation.deriveChildKey(accountKey,
new ChildNumber(0, false));
DeterministicKey changeKey = HDKeyDerivation.deriveChildKey(accountKey,
new ChildNumber(1, false));
if(currentAmount< request.getAmount() + fee){
request.setStatus(GeneralAccountSendRequest.StatusCode.NO_BALANCE);
return;
}
//String to an address
Address toAddr = Address.fromBase58(cryptoCoin.getParameters(), request.getToAccount());
tx.addOutput(Coin.valueOf(request.getAmount()), toAddr);
//String to an address
Address toAddr = Address.fromBase58(request.getAccount().getNetworkParam(), request.getToAccount());
tx.addOutput(Coin.valueOf(request.getAmount()), toAddr);
if(request.getMemo()!= null && !request.getMemo().isEmpty()){
/*if(request.getMemo()!= null && !request.getMemo().isEmpty()){
String memo = request.getMemo();
if(request.getMemo().length()>40){
memo = memo.substring(0,40);
@ -99,47 +433,282 @@ public class GeneralAccountManager implements CryptoAccountManager, CryptoNetInf
System.arraycopy(memo.getBytes(),0,scriptByte,2,memo.length());
Script memoScript = new Script(scriptByte);
tx.addOutput(Coin.valueOf(0),memoScript);
}
}*/
//Change address
long remain = currentAmount - request.getAmount() - fee;
if( remain > 0 ) {
Address changeAddr = Address.fromBase58(request.getAccount().getNetworkParam(), request.getAccount().getNextChangeAddress());
tx.addOutput(Coin.valueOf(remain), changeAddr);
}
//Change address
long remain = currentAmount - request.getAmount() - fee;
if (remain > 0) {
long index = db.bitcoinAddressDao().getLastChangeAddress(request.getSourceAccount().getId());
BitcoinAddress btAddress = db.bitcoinAddressDao().getChangeByIndex(index);
Address changeAddr;
if (btAddress != null && db.bitcoinTransactionDao().getGtxIOByAddress(btAddress.getAddress()).size() <= 0) {
changeAddr = Address.fromBase58(cryptoCoin.getParameters(), btAddress.getAddress());
for(GTxIO utxo: utxos) {
Sha256Hash txHash = Sha256Hash.wrap(utxo.getTransaction().getTxid());
Script script = new Script(Util.hexToBytes(utxo.getScriptHex()));
TransactionOutPoint outPoint = new TransactionOutPoint(request.getAccount().getNetworkParam(), utxo.getIndex(), txHash);
if(utxo.getAddress().getKey().isPubKeyOnly()){
if(utxo.getAddress().isIsChange()){
utxo.getAddress().setKey(HDKeyDerivation.deriveChildKey(request.getAccount().getChangeKey(), new ChildNumber(utxo.getAddress().getIndex(), false)));
}else{
utxo.getAddress().setKey(HDKeyDerivation.deriveChildKey(request.getAccount().getExternalKey(), new ChildNumber(utxo.getAddress().getIndex(), false)));
} else {
if (btAddress == null) {
index = 0;
} else {
index++;
}
btAddress = new BitcoinAddress();
btAddress.setIndex(index);
btAddress.setAccountId(request.getSourceAccount().getId());
btAddress.setChange(true);
btAddress.setAddress(HDKeyDerivation.deriveChildKey(changeKey, new ChildNumber((int) btAddress.getIndex(), false)).toAddress(cryptoCoin.getParameters()).toString());
db.bitcoinAddressDao().insertBitcoinAddresses(btAddress);
changeAddr = Address.fromBase58(cryptoCoin.getParameters(), btAddress.getAddress());
}
tx.addOutput(Coin.valueOf(remain), changeAddr);
}
for (BitcoinTransactionGTxIO utxo : utxos) {
Sha256Hash txHash = Sha256Hash.wrap(utxo.getOriginalTxId());
Script script = new Script(Util.hexToBytes(utxo.getScriptHex()));
TransactionOutPoint outPoint = new TransactionOutPoint(cryptoCoin.getParameters(), utxo.getIndex(), txHash);
BitcoinAddress btAddress = db.bitcoinAddressDao().getdadress(utxo.getAddress());
ECKey addrKey;
if (btAddress.isChange()) {
addrKey = HDKeyDerivation.deriveChildKey(changeKey, new ChildNumber((int) btAddress.getIndex(), false));
} else {
addrKey = HDKeyDerivation.deriveChildKey(externalKey, new ChildNumber((int) btAddress.getIndex(), true));
}
tx.addSignedInput(outPoint, script, addrKey, Transaction.SigHash.ALL, true);
currentAmount -= utxo.getAmount();
if(currentAmount<= 0){
break;
}
}
tx.addSignedInput(outPoint, script, utxo.getAddress().getKey(), Transaction.SigHash.ALL, true);
System.out.println("GeneralAccount Manager Send request rawtx " +Util.bytesToHex(tx.bitcoinSerialize()) );
InsightApiGenerator.broadcastTransaction(cryptoCoin, Util.bytesToHex(tx.bitcoinSerialize()), new ApiRequest(1, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
System.out.println("GeneralAccount MAnager succed send");
request.setStatus(BitcoinSendRequest.StatusCode.SUCCEEDED);
}
@Override
public void fail(int idPetition) {
System.out.println("GeneralAccount MAnager succed fail");
request.setStatus(BitcoinSendRequest.StatusCode.PETITION_FAILED);
}
}));
}catch(Exception e){
System.out.println("GeneralAccount Manager Send request error ");
e.printStackTrace();
}
InsightApiGenerator.broadcastTransaction(request.getAccount().getCryptoNet(),Util.bytesToHex(tx.bitcoinSerialize()),new ApiRequest(1, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
request.setStatus(GeneralAccountSendRequest.StatusCode.SUCCEEDED);
}
@Override
public void fail(int idPetition) {
request.setStatus(GeneralAccountSendRequest.StatusCode.PETITION_FAILED);
}
}));
}
@Override
public void fail(int idPetition) {
request.setStatus(GeneralAccountSendRequest.StatusCode.NO_FEE);
System.out.println("GeneralAccount Manager Send request fee fail" );
request.setStatus(BitcoinSendRequest.StatusCode.NO_FEE);
}
}));
}
private void getNextAddress(NextBitcoinAccountAddressRequest request){
CrystalDatabase db = CrystalDatabase.getAppDatabase(request.getContext());
long index = db.bitcoinAddressDao().getLastExternalAddress(request.getAccount().getId());
BitcoinAddress address = db.bitcoinAddressDao().getExternalByIndex(index);
if(address != null && db.bitcoinTransactionDao().getGtxIOByAddress(address.getAddress()).size()<=0){
request.setAddress(address.getAddress());
request.setStatus(NextBitcoinAccountAddressRequest.StatusCode.SUCCEEDED);
}else {
index++;
AccountSeed seed = db.accountSeedDao().findById(request.getAccount().getSeedId());
DeterministicKey purposeKey = HDKeyDerivation.deriveChildKey((DeterministicKey) seed.getPrivateKey(),
new ChildNumber(44, true));
DeterministicKey coinKey = HDKeyDerivation.deriveChildKey(purposeKey,
new ChildNumber(cryptoCoin.getCoinNumber(), true));
DeterministicKey accountKey = HDKeyDerivation.deriveChildKey(coinKey,
new ChildNumber(request.getAccount().getAccountIndex(), true));
DeterministicKey externalKey = HDKeyDerivation.deriveChildKey(accountKey,
new ChildNumber(0, false));
ECKey addrKey = HDKeyDerivation.deriveChildKey(externalKey, new ChildNumber((int) index, true));
address = new BitcoinAddress();
address.setChange(false);
address.setAccountId(request.getAccount().getId());
address.setIndex(index);
String addressString = addrKey.toAddress(this.cryptoCoin.getParameters()).toString();
address.setAddress(addressString);
db.bitcoinAddressDao().insertBitcoinAddresses(address);
InsightApiGenerator.getTransactionFromAddress(this.cryptoCoin, addressString, true, null);
request.setAddress(addressString);
request.setStatus(NextBitcoinAccountAddressRequest.StatusCode.SUCCEEDED);
}
}
private void calculateUri(CalculateBitcoinUriRequest request) {
StringBuilder uri = new StringBuilder(this.cryptoCoin.name().toLowerCase()+":");
CrystalDatabase db = CrystalDatabase.getAppDatabase(request.getContext());
long index = db.bitcoinAddressDao().getLastExternalAddress(request.getAccount().getId());
BitcoinAddress address = db.bitcoinAddressDao().getExternalByIndex(index);
if(address != null && db.bitcoinTransactionDao().getGtxIOByAddress(address.getAddress()).size()<=0){
uri.append(address.getAddress());
}else {
index++;
AccountSeed seed = db.accountSeedDao().findById(request.getAccount().getSeedId());
DeterministicKey purposeKey = HDKeyDerivation.deriveChildKey((DeterministicKey) seed.getPrivateKey(),
new ChildNumber(44, true));
DeterministicKey coinKey = HDKeyDerivation.deriveChildKey(purposeKey,
new ChildNumber(cryptoCoin.getCoinNumber(), true));
DeterministicKey accountKey = HDKeyDerivation.deriveChildKey(coinKey,
new ChildNumber(request.getAccount().getAccountIndex(), true));
DeterministicKey externalKey = HDKeyDerivation.deriveChildKey(accountKey,
new ChildNumber(0, false));
ECKey addrKey = HDKeyDerivation.deriveChildKey(externalKey, new ChildNumber((int) index, true));
address = new BitcoinAddress();
address.setChange(false);
address.setAccountId(request.getAccount().getId());
address.setIndex(index);
String addressString = addrKey.toAddress(this.cryptoCoin.getParameters()).toString();
address.setAddress(addressString);
db.bitcoinAddressDao().insertBitcoinAddresses(address);
InsightApiGenerator.getTransactionFromAddress(this.cryptoCoin, addressString, true, null);
uri.append(address.getAddress());
}
if(request.getAmount() > 0 ){
uri.append("?amount=");
uri.append(Double.toString(request.getAmount()));
}
System.out.println("GeneralAccountMAnager uri calculated : " + uri.toString());
request.setUri(uri.toString());
//request.validate();
}
private void parseUri(BitcoinUriParseRequest request){
String uri = request.getUri();
if(uri.indexOf(":")>0){
String cryptoNet = uri.substring(0,uri.indexOf(":"));
if(cryptoNet.equalsIgnoreCase(this.cryptoCoin.name().toLowerCase())){
try{
int parameterIndex =uri.indexOf("?");
Address address = Address.fromBase58(this.cryptoCoin.getParameters(), uri.substring(uri.indexOf(":")+1,parameterIndex>0?parameterIndex:uri.length()));
request.setAddress(address.toString());
request.setStatus(BitcoinUriParseRequest.StatusCode.VALID);
if(parameterIndex>0){
try {
String[] parameters = uri.substring(parameterIndex + 1).split("&");
for (String parameter : parameters) {
int idx = parameter.indexOf("=");
if (idx > 0 && parameter.substring(0, idx).equalsIgnoreCase("amount")) {
request.setAmount(Double.parseDouble(parameter.substring(idx + 1)));
}
}
}catch(Exception ignored){}
}
}catch(AddressFormatException ex){
request.setStatus(BitcoinUriParseRequest.StatusCode.NOT_VALID);
}
}else{
request.setStatus(BitcoinUriParseRequest.StatusCode.NOT_VALID);
}
}else{
int parameterIndex =uri.indexOf("?");
if(parameterIndex>0){
try{
Address address = Address.fromBase58(this.cryptoCoin.getParameters(), uri.substring(uri.indexOf(":")+1,parameterIndex>0?parameterIndex:uri.length()));
request.setAddress(address.toString());
request.setStatus(BitcoinUriParseRequest.StatusCode.VALID);
try{
String[] parameters = uri.substring(parameterIndex+1).split("&");
for(String parameter : parameters){
int idx = parameter.indexOf("=");
if(idx > 0 && parameter.substring(0,idx).equalsIgnoreCase("amount")){
request.setAmount(Double.parseDouble(parameter.substring(idx+1)));
}
}
}catch(Exception ignored){}
}catch(AddressFormatException ex){
request.setStatus(BitcoinUriParseRequest.StatusCode.NOT_VALID);
}
}else{
try{
Address address = Address.fromBase58(this.cryptoCoin.getParameters(), uri);
request.setAddress(address.toString());
request.setStatus(BitcoinUriParseRequest.StatusCode.VALID);
}catch(AddressFormatException ex){
request.setStatus(BitcoinUriParseRequest.StatusCode.NOT_VALID);
}
}
}
request.validate();
}
private List<BitcoinTransactionGTxIO> getUtxos(long accountId, CrystalDatabase db){
List<BitcoinTransactionGTxIO> answer = new ArrayList<>();
List<BitcoinTransactionGTxIO> bTGTxI = new ArrayList<>();
List<BitcoinTransactionGTxIO> bTGTxO = new ArrayList<>();
List<CryptoCoinTransaction> ccTransactions = db.transactionDao().getByIdAccount(accountId);
for(CryptoCoinTransaction ccTransaction : ccTransactions) {
List<BitcoinTransactionGTxIO> gtxios = db.bitcoinTransactionDao().getGtxIOByTransaction(ccTransaction.getId());
for(BitcoinTransactionGTxIO gtxio : gtxios){
if(db.bitcoinAddressDao().addressExists(gtxio.getAddress())){
if(gtxio.isOutput()){
bTGTxO.add(gtxio);
}else{
bTGTxI.add(gtxio);
}
}
}
}
for(BitcoinTransactionGTxIO gtxi : bTGTxI){
boolean find = false;
for(BitcoinTransactionGTxIO gtxo : bTGTxO){
if(gtxo.getOriginalTxId().equals(gtxi.getOriginalTxId())){
find = true;
break;
}
}
if(!find){
answer.add(gtxi);
}
}
return answer;
}
class CheckAddressForTransaction implements InsightApiGenerator.HasTransactionListener{
BitcoinAddressDao bitcoinAddressDao;
long idAccount;
DeterministicKey addressKey;
boolean isChange;
int lastIndex;
public CheckAddressForTransaction(BitcoinAddressDao bitcoinAddressDao, long idAccount, DeterministicKey addressKey, boolean isChange, int lastIndex) {
this.bitcoinAddressDao = bitcoinAddressDao;
this.idAccount = idAccount;
this.addressKey = addressKey;
this.isChange = isChange;
this.lastIndex = lastIndex;
}
@Override
public void hasTransaction(boolean value) {
if(value){
ECKey externalAddrKey = HDKeyDerivation.deriveChildKey(addressKey, new ChildNumber(lastIndex+1, true));
BitcoinAddress address = new BitcoinAddress();
address.setChange(isChange);
address.setAccountId(idAccount);
address.setIndex(lastIndex+1);
String addressString =externalAddrKey.toAddress(cryptoCoin.getParameters()).toString();
address.setAddress(addressString);
bitcoinAddressDao.insertBitcoinAddresses(address);
InsightApiGenerator.getTransactionFromAddress(cryptoCoin,addressString,true,
new CheckAddressForTransaction(bitcoinAddressDao,idAccount,addressKey,isChange,lastIndex+1));
}
}
}
}

View File

@ -0,0 +1,756 @@
package cy.agorise.crystalwallet.manager;
import android.annotation.SuppressLint;
import android.content.Context;
import com.google.common.primitives.UnsignedLong;
import org.bitcoinj.core.ECKey;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;
import cy.agorise.crystalwallet.apigenerator.ApiRequest;
import cy.agorise.crystalwallet.apigenerator.ApiRequestListener;
import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator;
import cy.agorise.crystalwallet.dao.AccountSeedDao;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
import cy.agorise.crystalwallet.dao.TransactionDao;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.enums.SeedType;
import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.BitsharesAccountNameCache;
import cy.agorise.crystalwallet.models.BitsharesAsset;
import cy.agorise.crystalwallet.models.BitsharesAssetInfo;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
import cy.agorise.crystalwallet.models.CryptoCurrency;
import cy.agorise.crystalwallet.models.CryptoNetAccount;
import cy.agorise.crystalwallet.models.GrapheneAccount;
import cy.agorise.crystalwallet.models.GrapheneAccountInfo;
import cy.agorise.crystalwallet.models.seed.BIP39;
import cy.agorise.crystalwallet.network.CryptoNetManager;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequest;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestsListener;
import cy.agorise.crystalwallet.requestmanagers.GetBitsharesAccountNameCacheRequest;
import cy.agorise.crystalwallet.requestmanagers.ImportBitsharesAccountRequest;
import cy.agorise.crystalwallet.requestmanagers.ValidateBitsharesSendRequest;
import cy.agorise.crystalwallet.requestmanagers.ValidateExistBitsharesAccountRequest;
import cy.agorise.crystalwallet.requestmanagers.ValidateImportBitsharesAccountRequest;
import cy.agorise.graphenej.Address;
import cy.agorise.graphenej.Asset;
import cy.agorise.graphenej.AssetAmount;
import cy.agorise.graphenej.BaseOperation;
import cy.agorise.graphenej.BrainKey;
import cy.agorise.graphenej.PublicKey;
import cy.agorise.graphenej.Transaction;
import cy.agorise.graphenej.UserAccount;
import cy.agorise.graphenej.models.AccountProperties;
import cy.agorise.graphenej.models.BlockHeader;
import cy.agorise.graphenej.models.HistoricalTransfer;
import cy.agorise.graphenej.operations.TransferOperationBuilder;
/**
* The manager for the Bitshare CryptoCoin
*
* Created by henry on 26/9/2017.
*/
public class SteemAccountManager implements CryptoAccountManager, CryptoNetInfoRequestsListener {
private final static String SIMPLE_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
private final static String DEFAULT_TIME_ZONE = "GMT";
@Override
public void createAccountFromSeed(CryptoNetAccount account, final ManagerRequest request, final Context context) {
//TODO error, can't create steem accounts
}
@Override
public void importAccountFromSeed(CryptoNetAccount account, final Context context) {
if(account instanceof GrapheneAccount) {
final GrapheneAccount grapheneAccount = (GrapheneAccount) account;
if(grapheneAccount.getAccountId() == null){
this.getAccountInfoByName(grapheneAccount.getName(), new ManagerRequest() {
@Override
public void success(Object answer) {
GrapheneAccount fetch = (GrapheneAccount) answer;
grapheneAccount.setAccountId(fetch.getAccountId());
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
long[] idAccount = db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount);
grapheneAccount.setId(idAccount[0]);
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount));
subscribeSteemAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
}
@Override
public void fail() {
//TODO get account data fail
}
});
}else if(grapheneAccount.getName() == null){
this.getAccountInfoById(grapheneAccount.getAccountId(), new ManagerRequest() {
@Override
public void success(Object answer) {
GrapheneAccount fetch = (GrapheneAccount) answer;
grapheneAccount.setName(fetch.getName());
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
long idAccount = db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount)[0];
grapheneAccount.setId(idAccount);
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount));
subscribeSteemAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
}
@Override
public void fail() {
//TODO get account data fail
}
});
}else {
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
long idAccount = db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount)[0];
grapheneAccount.setId(idAccount);
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount));
subscribeSteemAccount(grapheneAccount.getId(), grapheneAccount.getAccountId(), context);
}
}
}
@Override
public void loadAccountFromDB(CryptoNetAccount account, final Context context) {
if(account instanceof GrapheneAccount){
final GrapheneAccount grapheneAccount = (GrapheneAccount) account;
final CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
final GrapheneAccountInfo info = db.grapheneAccountInfoDao().getByAccountId(account.getId());
grapheneAccount.loadInfo(info);
if(grapheneAccount.getAccountId() == null){
this.getAccountInfoByName(grapheneAccount.getName(), new ManagerRequest() {
@Override
public void success(Object answer) {
GrapheneAccount fetch = (GrapheneAccount) answer;
info.setAccountId(fetch.getAccountId());
grapheneAccount.setAccountId(fetch.getAccountId());
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(info);
subscribeSteemAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
}
@Override
public void fail() {
//TODO account data retrieve failed
}
});
}else if(grapheneAccount.getName() == null){
this.getAccountInfoById(grapheneAccount.getAccountId(), new ManagerRequest() {
@Override
public void success(Object answer) {
GrapheneAccount fetch = (GrapheneAccount) answer;
info.setName(fetch.getName());
grapheneAccount.setName(fetch.getName());
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(info);
subscribeSteemAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
}
@Override
public void fail() {
//TODO account data retrieve failed
}
});
}else{
subscribeSteemAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
}
}
}
private void subscribeSteemAccount(long accountId, String accountSteemID, Context context){
GrapheneApiGenerator.subscribeSteemAccount(accountId,accountSteemID,context);
SteemAccountManager.refreshAccountTransactions(accountId,context);
GrapheneApiGenerator.getAccountBalance(accountId,accountSteemID,CryptoNet.STEEM,context);
}
/**
* Process the bitshares manager request
* @param request The request Object
*/
@Override
public void onNewRequest(CryptoNetInfoRequest request) {
if(request.getCoin().equals(CryptoCoin.STEEM)) {
if (request instanceof ImportBitsharesAccountRequest) {
this.importAccount((ImportBitsharesAccountRequest) request);
} else if (request instanceof ValidateImportBitsharesAccountRequest) {
this.validateImportAccount((ValidateImportBitsharesAccountRequest) request);
} else if (request instanceof ValidateExistBitsharesAccountRequest) {
this.validateExistAcccount((ValidateExistBitsharesAccountRequest) request);
} else if (request instanceof ValidateBitsharesSendRequest) {
this.validateSendRequest((ValidateBitsharesSendRequest) request);
} else if (request instanceof GetBitsharesAccountNameCacheRequest) {
this.getBitsharesAccountNameCacheRequest((GetBitsharesAccountNameCacheRequest) request);
} else {
//TODO not implemented
System.out.println("Error request not implemented " + request.getClass().getName());
}
}
}
private void importAccount(final ImportBitsharesAccountRequest importRequest){
final CrystalDatabase db = CrystalDatabase.getAppDatabase(importRequest.getContext());
final AccountSeedDao accountSeedDao = db.accountSeedDao();
ApiRequest getAccountNamesBK = new ApiRequest(0, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
if(answer != null && importRequest.getStatus().equals(ImportBitsharesAccountRequest.StatusCode.NOT_STARTED)) {
UserAccount userAccount = (UserAccount) answer;
importRequest.setSeedType(SeedType.BRAINKEY);
importRequest.setStatus(ImportBitsharesAccountRequest.StatusCode.SUCCEEDED);
AccountSeed seed = new AccountSeed();
seed.setName(userAccount.getName());
seed.setType(importRequest.getSeedType());
seed.setMasterSeed(importRequest.getMnemonic());
long idSeed = accountSeedDao.insertAccountSeed(seed);
if (idSeed >= 0) {
GrapheneAccount account = new GrapheneAccount();
account.setCryptoNet(CryptoNet.STEEM);
account.setAccountIndex(0);
account.setSeedId(idSeed);
account.setAccountId(userAccount.getObjectId());
importAccountFromSeed(account, importRequest.getContext());
}
}
}
@Override
public void fail(int idPetition) {
importRequest.setStatus(ImportBitsharesAccountRequest.StatusCode.BAD_SEED);
}
});
BrainKey bk = new BrainKey(importRequest.getMnemonic(), 0);
GrapheneApiGenerator.getAccountByOwnerOrActiveAddress(bk.getPublicAddress("STM"),CryptoNet.STEEM,getAccountNamesBK);
}
/**
* Process the import account request
*/
private void validateImportAccount(final ValidateImportBitsharesAccountRequest importRequest){
//TODO check internet and server status
final CrystalDatabase db = CrystalDatabase.getAppDatabase(importRequest.getContext());
final AccountSeedDao accountSeedDao = db.accountSeedDao();
ApiRequest checkAccountName = new ApiRequest(0, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
ApiRequest getAccountInfo = new ApiRequest(1,new ApiRequestListener(){
@Override
public void success(Object answer, int idPetition) {
if(answer != null && answer instanceof AccountProperties) {
AccountProperties prop = (AccountProperties) answer;
BrainKey bk = new BrainKey(importRequest.getMnemonic(), 0);
for(PublicKey activeKey : prop.owner.getKeyAuthList()){
if((new Address(activeKey.getKey(),"STM")).toString().equals(bk.getPublicAddress("STM").toString())){
importRequest.setSeedType(SeedType.BRAINKEY);
importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.SUCCEEDED);
break;
}
}
if ((importRequest.getStatus() == ValidateImportBitsharesAccountRequest.StatusCode.SUCCEEDED)){
if (importRequest.addAccountIfValid()) {
AccountSeed seed = new AccountSeed();
seed.setName(importRequest.getAccountName());
seed.setType(importRequest.getSeedType());
seed.setMasterSeed(importRequest.getMnemonic());
long idSeed = accountSeedDao.insertAccountSeed(seed);
if (idSeed >= 0) {
GrapheneAccount account = new GrapheneAccount();
account.setCryptoNet(CryptoNet.STEEM);
account.setAccountIndex(0);
account.setSeedId(idSeed);
account.setName(importRequest.getAccountName());
importAccountFromSeed(account, importRequest.getContext());
}
}
return;
}
importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.BAD_SEED);
}
importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.PETITION_FAILED);
}
@Override
public void fail(int idPetition) {
//
importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.NO_ACCOUNT_DATA);
}
});
GrapheneApiGenerator.getAccountById((String)answer,CryptoNet.STEEM,getAccountInfo);
}
@Override
public void fail(int idPetition) {
//
importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.ACCOUNT_DOESNT_EXIST);
}
});
GrapheneApiGenerator.getAccountIdByName(importRequest.getAccountName(),CryptoNet.STEEM,checkAccountName);
}
/**
* Process the account exist request, it consults the bitshares api for the account name.
*
* This can be used to know if the name is avaible, or the account to be send fund exists
*/
private void validateExistAcccount(final ValidateExistBitsharesAccountRequest validateRequest){
ApiRequest checkAccountName = new ApiRequest(0, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
if (answer != null) {
validateRequest.setAccountExists(true);
} else {
validateRequest.setAccountExists(false);
}
}
@Override
public void fail(int idPetition) {
//TODO verified
validateRequest.setAccountExists(false);
}
});
GrapheneApiGenerator.getAccountIdByName(validateRequest.getAccountName(),CryptoNet.STEEM,checkAccountName);
}
/**
* Broadcast a transaction request
*/
private void validateSendRequest(final ValidateBitsharesSendRequest sendRequest) {
//TODO check internet, server connection
//TODO feeAsset
CrystalDatabase db = CrystalDatabase.getAppDatabase(sendRequest.getContext());
CryptoCurrency currency = db.cryptoCurrencyDao().getByNameAndCryptoNet(sendRequest.getAsset(), CryptoNet.STEEM.name());
if (currency == null){
getAssetInfoByName(sendRequest.getAsset(), new ManagerRequest() {
@Override
public void success(Object answer) {
validateSendRequest(sendRequest, ((BitsharesAsset) answer).getBitsharesId());
}
@Override
public void fail() {
sendRequest.setStatus(ValidateBitsharesSendRequest.StatusCode.NO_ASSET_INFO_DB);
}
});
}else{
BitsharesAssetInfo info = db.bitsharesAssetDao().getBitsharesAssetInfo(currency.getId());
if (info == null || info.getBitsharesId() == null || info.getBitsharesId().isEmpty()){
getAssetInfoByName(sendRequest.getAsset(), new ManagerRequest() {
@Override
public void success(Object answer) {
validateSendRequest(sendRequest, ((BitsharesAsset) answer).getBitsharesId());
}
@Override
public void fail() {
sendRequest.setStatus(ValidateBitsharesSendRequest.StatusCode.NO_ASSET_INFO);
}
});
}else {
this.validateSendRequest(sendRequest, info.getBitsharesId());
}
}
}
/**
* Broadcast a send asset request, the idAsset is already fetched
* @param sendRequest The petition for transfer
* @param idAsset The Bitshares Asset's id
*/
private void validateSendRequest(final ValidateBitsharesSendRequest sendRequest , final String idAsset){
final Asset feeAsset = new Asset(idAsset);
final UserAccount fromUserAccount =new UserAccount(sendRequest.getSourceAccount().getAccountId());
final CrystalDatabase db = CrystalDatabase.getAppDatabase(sendRequest.getContext());
BitsharesAccountNameCache cacheAccount = db.bitsharesAccountNameCacheDao().getByAccountName(sendRequest.getToAccount());
if(cacheAccount == null) {
this.getAccountInfoByName(sendRequest.getToAccount(), new ManagerRequest() {
@Override
public void success(Object answer) {
GrapheneAccount toUserGrapheneAccount = (GrapheneAccount) answer;
UserAccount toUserAccount = new UserAccount(toUserGrapheneAccount.getAccountId());
try {
BitsharesAccountNameCache cacheAccount = new BitsharesAccountNameCache();
cacheAccount.setName(sendRequest.getToAccount());
cacheAccount.setAccountId(toUserAccount.getObjectId());
db.bitsharesAccountNameCacheDao().insertBitsharesAccountNameCache(cacheAccount);
}catch(Exception e){
e.printStackTrace();
}
validateSendRequest(sendRequest,fromUserAccount,toUserAccount,feeAsset,idAsset);
}
@Override
public void fail() {
sendRequest.setStatus(ValidateBitsharesSendRequest.StatusCode.NO_TO_USER_INFO);
}
});
}else {
UserAccount toUserAccount = new UserAccount(cacheAccount.getAccountId());
this.validateSendRequest(sendRequest,fromUserAccount,toUserAccount,feeAsset,idAsset);
}
}
/**
* Broadcast a transaction request
* @param sendRequest The petition for transfer operation
* @param fromUserAccount The source account
* @param toUserAccount The receiver account
* @param feeAsset The Fee Asset
* @param idAsset The id of the asset to be used on the operation
*/
private void validateSendRequest(final ValidateBitsharesSendRequest sendRequest, UserAccount fromUserAccount,
UserAccount toUserAccount, Asset feeAsset, String idAsset){
TransferOperationBuilder builder = new TransferOperationBuilder()
.setSource(fromUserAccount)
.setDestination(toUserAccount)
.setTransferAmount(new AssetAmount(UnsignedLong.valueOf(sendRequest.getAmount()), new Asset(idAsset)))
.setFee(new AssetAmount(UnsignedLong.valueOf(0), feeAsset));
if (sendRequest.getMemo() != null) {
//builder.setMemo(new Memo(fromUserAccount,toUserAccount,0,sendRequest.getMemo().getBytes()));
//TODO memo
System.out.println("transaction has memo");
}
ArrayList<BaseOperation> operationList = new ArrayList<>();
operationList.add(builder.build());
ECKey privateKey = sendRequest.getSourceAccount().getActiveKey(sendRequest.getContext());
Transaction transaction = new Transaction(privateKey, null, operationList);
transaction.setChainId(CryptoNetManager.getChaindId(CryptoNet.STEEM));
ApiRequest transactionRequest = new ApiRequest(0, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
sendRequest.setStatus(ValidateBitsharesSendRequest.StatusCode.SUCCEEDED);
}
@Override
public void fail(int idPetition) {
sendRequest.setStatus(ValidateBitsharesSendRequest.StatusCode.PETITION_FAILED);
}
});
GrapheneApiGenerator.broadcastTransaction(transaction, feeAsset, transactionRequest);
}
private void getBitsharesAccountNameCacheRequest(final GetBitsharesAccountNameCacheRequest request){
final CrystalDatabase db = CrystalDatabase.getAppDatabase(request.getContext());
BitsharesAccountNameCache cacheAccount = db.bitsharesAccountNameCacheDao().getByAccountId(request.getAccountId());
if(cacheAccount == null) {
this.getAccountInfoById(request.getAccountId(), new ManagerRequest() {
@Override
public void success(Object answer) {
GrapheneAccount userGrapheneAccount = (GrapheneAccount) answer;
BitsharesAccountNameCache cacheAccount = new BitsharesAccountNameCache();
cacheAccount.setName(userGrapheneAccount.getName());
cacheAccount.setAccountId(request.getAccountId());
db.bitsharesAccountNameCacheDao().insertBitsharesAccountNameCache(cacheAccount);
request.setAccountName(userGrapheneAccount.getName());
}
@Override
public void fail() {
//TODO error
}
});
}else {
request.setAccountName(cacheAccount.getName());
}
}
/**
* Returns the account info from a graphene id
* @param grapheneId The graphene id of the account
*/
private void getAccountInfoById(String grapheneId, ManagerRequest request){
AccountIdOrNameListener listener = new AccountIdOrNameListener(request);
ApiRequest accountRequest = new ApiRequest(0, listener);
GrapheneApiGenerator.getAccountById(grapheneId,CryptoNet.STEEM,accountRequest);
}
/**
* Gets account info by its name
* @param grapheneName The name of the account to retrieve
*/
private void getAccountInfoByName(String grapheneName, ManagerRequest request){
AccountIdOrNameListener listener = new AccountIdOrNameListener(request);
ApiRequest accountRequest = new ApiRequest(0, listener);
GrapheneApiGenerator.getAccountByName(grapheneName,CryptoNet.STEEM,accountRequest);
}
//TODO expand function to be more generic
private void getAssetInfoByName(String assetName, ManagerRequest request){
AssetIdOrNameListener nameListener = new AssetIdOrNameListener(request);
ApiRequest assetRequest = new ApiRequest(0, nameListener);
ArrayList<String> assetNames = new ArrayList<>();
assetNames.add(assetName);
GrapheneApiGenerator.getAssetByName(assetNames, CryptoNet.STEEM, assetRequest);
}
/**
* Refresh the transactions of an account, important to notice, it return nothing, to get the changes tuse the LiveData
* @param idAccount database id of the account
* @param context The android context of this application
*/
private static void refreshAccountTransactions(long idAccount, Context context){
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
List<CryptoCoinTransaction> transactions = db.transactionDao().getByIdAccount(idAccount);
CryptoNetAccount account = db.cryptoNetAccountDao().getById(idAccount);
if(account.getCryptoNet() == CryptoNet.STEEM) {
GrapheneAccount grapheneAccount = new GrapheneAccount(account);
grapheneAccount.loadInfo(db.grapheneAccountInfoDao().getByAccountId(idAccount));
int start = transactions.size();
int limit = 50;
int stop = start + limit;
ApiRequest transactionRequest = new ApiRequest(0, new TransactionRequestListener(start, stop, limit, grapheneAccount, db));
GrapheneApiGenerator.getAccountTransaction(grapheneAccount.getAccountId(), start, stop, limit, CryptoNet.STEEM,transactionRequest);
}
}
/**
* Class that handles the transactions request
*/
private static class TransactionRequestListener implements ApiRequestListener{
/**
* Start index
*/
int start;
/**
* End index
*/
int stop;
/**
* Limit of transasction to fetch
*/
int limit;
/**
* The grapheneaccount with all data CryptoCurrnecy + info
*/
GrapheneAccount account;
/**
* The database
*/
CrystalDatabase db;
/**
* Basic consturctor
*/
TransactionRequestListener(int start, int stop, int limit, GrapheneAccount account, CrystalDatabase db) {
this.start = start;
this.stop = stop;
this.limit = limit;
this.account = account;
this.db = db;
}
/**
* Handles the success request of the transaction, if the amount of transaction is equal to the limit, ask for more transaction
* @param answer The answer, this object depends on the kind of request is made to the api
* @param idPetition the id of the ApiRequest petition
*/
@Override
public void success(Object answer, int idPetition) {
List<HistoricalTransfer> transfers = (List<HistoricalTransfer>) answer ;
for(final HistoricalTransfer transfer : transfers) {
if (transfer.getOperation() != null){
final CryptoCoinTransaction transaction = new CryptoCoinTransaction();
transaction.setAccountId(account.getId());
transaction.setAmount(transfer.getOperation().getAssetAmount().getAmount().longValue());
BitsharesAssetInfo info = db.bitsharesAssetDao().getBitsharesAssetInfoById(transfer.getOperation().getAssetAmount().getAsset().getObjectId());
if (info == null) {
//The cryptoCurrency is not in the database, queringfor its data
ApiRequest assetRequest = new ApiRequest(0, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
ArrayList<BitsharesAsset> assets = (ArrayList<BitsharesAsset>) answer;
for(BitsharesAsset asset : assets){
long currencyId = -1;
CryptoCurrency cryptoCurrencyDb = db.cryptoCurrencyDao().getByNameAndCryptoNet(asset.getName(),asset.getCryptoNet().name());
if (cryptoCurrencyDb != null){
currencyId = cryptoCurrencyDb.getId();
} else {
long idCryptoCurrency = db.cryptoCurrencyDao().insertCryptoCurrency(asset)[0];
currencyId = idCryptoCurrency;
}
BitsharesAssetInfo info = new BitsharesAssetInfo(asset);
info.setCryptoCurrencyId(currencyId);
asset.setId((int)currencyId);
db.bitsharesAssetDao().insertBitsharesAssetInfo(info);
saveTransaction(transaction,info,transfer);
}
}
@Override
public void fail(int idPetition) {
//TODO Error
}
});
ArrayList<String> assets = new ArrayList<>();
assets.add(transfer.getOperation().getAssetAmount().getAsset().getObjectId());
GrapheneApiGenerator.getAssetById(assets,CryptoNet.STEEM,assetRequest);
}else{
saveTransaction(transaction,info,transfer);
}
}
}
if(transfers.size()>= limit){
// The amount of transaction in the answer is equal to the limit, we need to query to see if there is more transactions
int newStart= start + limit;
int newStop= stop + limit;
ApiRequest transactionRequest = new ApiRequest(newStart/limit, new TransactionRequestListener(newStart,newStop,limit,account,db));
GrapheneApiGenerator.getAccountTransaction(account.getAccountId(),newStart,newStop,limit,CryptoNet.STEEM,transactionRequest);
}
}
@Override
public void fail(int idPetition) {
}
private void saveTransaction(CryptoCoinTransaction transaction, BitsharesAssetInfo info, HistoricalTransfer transfer){
transaction.setIdCurrency((int)info.getCryptoCurrencyId());
transaction.setConfirmed(true); //graphene transaction are always confirmed
transaction.setFrom(transfer.getOperation().getFrom().getObjectId());
transaction.setInput(!transfer.getOperation().getFrom().getObjectId().equals(account.getAccountId()));
transaction.setTo(transfer.getOperation().getTo().getObjectId());
GrapheneApiGenerator.getBlockHeaderTime(transfer.getBlockNum(), CryptoNet.STEEM,new ApiRequest(0, new GetTransactionDate(transaction, db.transactionDao())));
}
}
/**
* Class to retrieve the account id or the account name, if one of those is missing
*/
private class AccountIdOrNameListener implements ApiRequestListener{
final ManagerRequest request;
GrapheneAccount account;
AccountIdOrNameListener(ManagerRequest request) {
this.request = request;
}
@Override
public void success(Object answer, int idPetition) {
if(answer instanceof AccountProperties){
AccountProperties props = (AccountProperties) answer;
account = new GrapheneAccount();
account.setAccountId(props.id);
account.setName(props.name);
}
request.success(account);
}
@Override
public void fail(int idPetition) {
request.fail();
}
}
/**
* Class to retrieve the asset id or the asset name, if one of those is missing
*/
private class AssetIdOrNameListener implements ApiRequestListener{
final ManagerRequest request;
BitsharesAsset asset;
AssetIdOrNameListener(ManagerRequest request) {
this.request = request;
}
@Override
public void success(Object answer, int idPetition) {
if(answer instanceof ArrayList) {
if (((ArrayList) answer).get(0) instanceof BitsharesAsset) {
asset = (BitsharesAsset) ((ArrayList) answer).get(0);
request.success(asset);
}
}
}
@Override
public void fail(int idPetition) {
//TODO fail asset retrieve
}
}
/**
* Class to retrieve the transaction date
*/
public static class GetTransactionDate implements ApiRequestListener{
/**
* The transaction to retrieve
*/
private CryptoCoinTransaction transaction;
/**
* The DAO to insert or update the transaction
*/
TransactionDao transactionDao;
GetTransactionDate(CryptoCoinTransaction transaction, TransactionDao transactionDao) {
this.transaction = transaction;
this.transactionDao = transactionDao;
}
@Override
public void success(Object answer, int idPetition) {
if(answer instanceof BlockHeader){
@SuppressLint("SimpleDateFormat") SimpleDateFormat dateFormat = new SimpleDateFormat(SIMPLE_DATE_FORMAT);
dateFormat.setTimeZone(TimeZone.getTimeZone(DEFAULT_TIME_ZONE));
try {
transaction.setDate(dateFormat.parse(((BlockHeader) answer).timestamp));
if (transactionDao.getByTransaction(transaction.getDate(),transaction.getFrom(),transaction.getTo(),transaction.getAmount()) == null) {
transactionDao.insertTransaction(transaction);
}
} catch (ParseException e) {
e.printStackTrace();
}
}
}
@Override
public void fail(int idPetition) {
}
}
}

View File

@ -9,12 +9,14 @@ import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.util.DiffUtil;
import org.bitcoinj.core.Base58;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.crypto.HDKeyDerivation;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import cy.agorise.crystalwallet.enums.SeedType;
import cy.agorise.crystalwallet.models.seed.BIP39;
@ -115,8 +117,6 @@ public class AccountSeed {
BufferedReader reader = null;
switch (type) {
case BRAINKEY:
try {
reader = new BufferedReader(new InputStreamReader(context.getAssets().open("brainkeydict.txt"), "UTF-8"));
@ -130,6 +130,7 @@ public class AccountSeed {
} catch (IOException e) {
e.printStackTrace();
}
break;
case BIP39:
try {
reader = new BufferedReader(new InputStreamReader(context.getAssets().open("bip39dict.txt"), "UTF-8"));
@ -139,6 +140,7 @@ public class AccountSeed {
} catch (IOException e) {
e.printStackTrace();
}
break;
}
return null;
}
@ -149,6 +151,15 @@ public class AccountSeed {
return new BrainKey(this.mMasterSeed,0).getPrivateKey();
case BIP39:
return HDKeyDerivation.createMasterPrivateKey(new BIP39(mId, mMasterSeed).getSeed());
case WIF:
byte[] decoded = Base58.decode(this.mMasterSeed);
byte[] privKey = Arrays.copyOfRange(decoded, 1, decoded.length - 4);
byte[] checksum = Arrays.copyOfRange(decoded, decoded.length - 4, decoded.length);
//TODO calculate chekcsum
while(privKey.length>32){
privKey = Arrays.copyOfRange(privKey,0,privKey.length-1);
}
return ECKey.fromPrivate(privKey);
}
return null;
}

View File

@ -0,0 +1,93 @@
package cy.agorise.crystalwallet.models;
import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.ForeignKey;
import android.support.annotation.NonNull;
/**
* Represents a Bitcoin derivated address
*
* Created by Henry Varona on 10/17/2018.
*/
@Entity(
tableName="bitcoin_address",
primaryKeys = {"account_id","address_index","is_change"},
foreignKeys = {
@ForeignKey(
entity = CryptoNetAccount.class,
parentColumns = "id",
childColumns = "account_id",
onDelete = ForeignKey.CASCADE
)
}
)
public class BitcoinAddress {
/**
* The id of the account associated
*/
@ColumnInfo(name="account_id")
protected long accountId;
/**
* The index of this address
*/
@ColumnInfo(name="address_index")
@NonNull protected long index;
/**
* Whether or not this address is a change one
*/
@ColumnInfo(name="is_change")
@NonNull protected boolean isChange;
/**
* Address
*/
@ColumnInfo(name="address")
@NonNull protected String address;
public BitcoinAddress(long accountId, @NonNull long index, boolean isChange, String address) {
this.accountId = accountId;
this.index = index;
this.isChange = isChange;
this.address = address;
}
public BitcoinAddress() {
}
public long getAccountId() {
return accountId;
}
public void setAccountId(long accountId) {
this.accountId = accountId;
}
@NonNull
public long getIndex() {
return index;
}
public void setIndex(@NonNull long index) {
this.index = index;
}
public boolean isChange() {
return isChange;
}
public void setChange(boolean change) {
isChange = change;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}

View File

@ -0,0 +1,113 @@
package cy.agorise.crystalwallet.models;
import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.ForeignKey;
import android.arch.persistence.room.Ignore;
import android.arch.persistence.room.Index;
import android.arch.persistence.room.PrimaryKey;
import android.support.annotation.NonNull;
import android.support.v7.util.DiffUtil;
import java.util.Date;
/**
* Represents a Bitcoin alike Transaction
*
* Created by Henry Varona on 10/2/2018.
*/
@Entity(
tableName="bitcoin_transaction",
primaryKeys = {"crypto_coin_transaction_id"},
foreignKeys = {
@ForeignKey(
entity = CryptoCoinTransaction.class,
parentColumns = "id",
childColumns = "crypto_coin_transaction_id",
onDelete = ForeignKey.CASCADE
)
}
)
public class BitcoinTransaction {
/**
* The id of the base transaction
*/
@ColumnInfo(name="crypto_coin_transaction_id")
protected long cryptoCoinTransactionId;
/**
* The transaction id in the blockchain
*/
@ColumnInfo(name="tx_id")
@NonNull protected String txId;
/**
* The block id in the blockchain
*/
@ColumnInfo(name="block")
protected long block;
/**
* The fee of the transaction
*/
@ColumnInfo(name="fee")
protected long fee;
/**
* The confirmations of the transaction in the blockchain
*/
@ColumnInfo(name="confirmations")
protected int confirmations;
public BitcoinTransaction() {
}
public BitcoinTransaction(long cryptoCoinTransactionId, String txId, long block, long fee, int confirmations) {
this.cryptoCoinTransactionId = cryptoCoinTransactionId;
this.txId = txId;
this.block = block;
this.fee = fee;
this.confirmations = confirmations;
}
public long getCryptoCoinTransactionId() {
return cryptoCoinTransactionId;
}
public void setCryptoCoinTransactionId(long cryptoCoinTransactionId) {
this.cryptoCoinTransactionId = cryptoCoinTransactionId;
}
public String getTxId() {
return txId;
}
public void setTxId(String txId) {
this.txId = txId;
}
public long getBlock() {
return block;
}
public void setBlock(long block) {
this.block = block;
}
public long getFee() {
return fee;
}
public void setFee(long fee) {
this.fee = fee;
}
public int getConfirmations() {
return confirmations;
}
public void setConfirmations(int confirmations) {
this.confirmations = confirmations;
}
}

View File

@ -0,0 +1,26 @@
package cy.agorise.crystalwallet.models;
import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Embedded;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.ForeignKey;
import android.arch.persistence.room.Relation;
import java.util.List;
/**
* Represents a Bitcoin alike Transaction
*
* Created by Henry Varona on 10/2/2018.
*/
public class BitcoinTransactionExtended {
@Embedded
public CryptoCoinTransaction cryptoCoinTransaction;
@Embedded
public BitcoinTransaction bitcoinTransaction;
@Relation(parentColumn = "id", entityColumn = "bitcoin_transaction_id", entity = BitcoinTransactionGTxIO.class)
public List<BitcoinTransactionGTxIO> bitcoinTransactionGTxIOList;
}

View File

@ -0,0 +1,124 @@
package cy.agorise.crystalwallet.models;
import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.ForeignKey;
/**
* Represents a Bitcoin alike Transaction Inputs and Outputs
*
* Created by Henry Varona on 10/2/2018.
*/
@Entity(
tableName="bitcoin_transaction_gt_io",
primaryKeys = {"bitcoin_transaction_id", "io_index", "is_output"},
foreignKeys = {
@ForeignKey(
entity = CryptoCoinTransaction.class,
parentColumns = "id",
childColumns = "bitcoin_transaction_id",
onDelete = ForeignKey.CASCADE
)
}
)
public class BitcoinTransactionGTxIO {
/**
* The id of the bitcoin transaction
*/
@ColumnInfo(name="bitcoin_transaction_id")
protected long bitcoinTransactionId;
/**
* The index in the transaction
*/
@ColumnInfo(name="io_index")
protected int index;
/**
* The address of the input or output
*/
@ColumnInfo(name="address")
protected String address;
/**
* determines if this is an input or output
*/
@ColumnInfo(name="is_output")
protected boolean isOutput;
@ColumnInfo(name="amount")
protected long amount;
@ColumnInfo(name="script_hex")
protected String scriptHex;
@ColumnInfo(name="original_txid")
protected String originalTxId;
public BitcoinTransactionGTxIO() {
}
public BitcoinTransactionGTxIO(long bitcoinTransactionId, int index, String address, boolean isOutput) {
this.bitcoinTransactionId = bitcoinTransactionId;
this.index = index;
this.address = address;
this.isOutput = isOutput;
}
public long getBitcoinTransactionId() {
return bitcoinTransactionId;
}
public void setBitcoinTransactionId(long bitcoinTransactionId) {
this.bitcoinTransactionId = bitcoinTransactionId;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public boolean isOutput() {
return isOutput;
}
public void setOutput(boolean output) {
isOutput = output;
}
public long getAmount() {
return amount;
}
public void setAmount(long amount) {
this.amount = amount;
}
public String getScriptHex() {
return scriptHex;
}
public void setScriptHex(String scriptHex) {
this.scriptHex = scriptHex;
}
public String getOriginalTxId() {
return originalTxId;
}
public void setOriginalTxId(String originalTxId) {
this.originalTxId = originalTxId;
}
}

View File

@ -20,9 +20,13 @@ import cy.agorise.crystalwallet.enums.CryptoCoin;
*/
@Entity(
tableName="crypto_coin_transaction",
primaryKeys = {
},
indices={
@Index(value={"account_id"}),
@Index(value={"id_currency"})
@Index(value={"id_currency"}),
@Index(value={"date", "account_id", "id_currency", "from", "to"},unique=true)
},
foreignKeys = {
@ForeignKey(
@ -66,7 +70,7 @@ public class CryptoCoinTransaction {
* The id of the account assoiciated, this is used for the foreign key definition
*/
@ColumnInfo(name="account_id")
protected long accountId;
protected long accountId = -1;
/**
* The amount of asset is moved in this transaction
*/

View File

@ -45,6 +45,15 @@ public class CryptoCurrency {
@ColumnInfo(name = "precision")
private int mPrecision;
public CryptoCurrency() {
}
public CryptoCurrency(String name, CryptoNet cryptoNet, int precision) {
this.mName = name;
this.mCryptoNet = cryptoNet;
this.mPrecision = precision;
}
public long getId() {
return mId;
}

View File

@ -0,0 +1,45 @@
package cy.agorise.crystalwallet.models;
import android.support.annotation.NonNull;
import android.support.v7.util.DiffUtil;
import cy.agorise.crystalwallet.enums.CryptoNet;
public class CryptoNetSelection {
CryptoNet cryptoNet;
Boolean selected;
public CryptoNetSelection(CryptoNet cryptoNet, Boolean selected) {
this.cryptoNet = cryptoNet;
this.selected = selected;
}
public CryptoNet getCryptoNet() {
return cryptoNet;
}
public void setCryptoNet(CryptoNet cryptoNet) {
this.cryptoNet = cryptoNet;
}
public Boolean getSelected() {
return selected;
}
public void setSelected(Boolean selected) {
this.selected = selected;
}
public static final DiffUtil.ItemCallback<CryptoNetSelection> DIFF_CALLBACK = new DiffUtil.ItemCallback<CryptoNetSelection>() {
@Override
public boolean areItemsTheSame(
@NonNull CryptoNetSelection oldCryptoNetSelection, @NonNull CryptoNetSelection newCryptoNetSelection) {
return oldCryptoNetSelection.getCryptoNet() == newCryptoNetSelection.getCryptoNet();
}
@Override
public boolean areContentsTheSame(
@NonNull CryptoNetSelection oldCryptoNetSelection, @NonNull CryptoNetSelection newCryptoNetSelection) {
return oldCryptoNetSelection.equals(newCryptoNetSelection);
}
};
}

View File

@ -31,9 +31,16 @@ public class GrapheneAccount extends CryptoNetAccount {
}
public void loadInfo(GrapheneAccountInfo info){
this.name = info.getName();
this.accountId = info.getAccountId();
this.upgradedToLtm = info.getUpgradedToLtm();
if(info != null){
this.name = info.getName();
this.accountId = info.getAccountId();
this.upgradedToLtm = info.getUpgradedToLtm();
}
else{
this.name = "";
this.accountId = "-1";
this.upgradedToLtm = false;
}
}
public String getName() {

View File

@ -0,0 +1,48 @@
package cy.agorise.crystalwallet.network;
import cy.agorise.crystalwallet.apigenerator.insightapi.GetGenesisBlock;
import cy.agorise.crystalwallet.enums.CryptoCoin;
public class BitcoinCryptoNetVerifier extends CryptoNetVerifier{
final CryptoCoin cryptoCoin;
public BitcoinCryptoNetVerifier(CryptoCoin cryptoCoin) {
this.cryptoCoin = cryptoCoin;
}
@Override
public void checkURL(final String url) {
final long startTime = System.currentTimeMillis();
GetGenesisBlock genesisBloc = new GetGenesisBlock(url, new GetGenesisBlock.genesisBlockListener() {
@Override
public void genesisBlock(String value) {
if(cryptoCoin.getParameters()!= null){
if(value.equals(cryptoCoin.getParameters().getGenesisBlock().getHashAsString())){
CryptoNetManager.verifiedCryptoNetURL(cryptoCoin.getCryptoNet(), url, System.currentTimeMillis() - startTime);
}else{
System.out.println("BitcoinCryptoNetVerifier bad genesis block " + value + " " + url);
}
//TODO bad genesis block
}else{
CryptoNetManager.verifiedCryptoNetURL(cryptoCoin.getCryptoNet(), url, System.currentTimeMillis() - startTime);
}
}
@Override
public void fail() {
//TODO failed
}
});
}
@Override
public String getChainId() {
if(cryptoCoin == null || cryptoCoin.getParameters()== null) {
return null;
}
return cryptoCoin.getParameters().getGenesisBlock().getHashAsString();
}
}

View File

@ -32,7 +32,6 @@ public abstract class CryptoNetManager {
public static String getURL(CryptoNet crypto, int index){
if(TestedURLs.containsKey(crypto) && TestedURLs.get(crypto).size()>index){
System.out.println("Servers url list " + Arrays.toString(TestedURLs.get(crypto).toArray()));
return TestedURLs.get(crypto).get(index).getUrl();
}
System.out.println("Servers " + crypto.getLabel()+" dioesn't have testedurl");

View File

@ -1,5 +1,6 @@
package cy.agorise.crystalwallet.network;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.enums.CryptoNet;
/**
@ -15,6 +16,10 @@ public abstract class CryptoNetVerifier {
static CryptoNetVerifier getNetworkVerify(CryptoNet cryptoNet){
if(cryptoNet.getLabel().equals(CryptoNet.BITSHARES.getLabel())){
return new BitsharesCryptoNetVerifier();
}else if(cryptoNet.getLabel().equals(CryptoNet.BITCOIN.getLabel())){
return new BitcoinCryptoNetVerifier(CryptoCoin.BITCOIN);
}else if(cryptoNet.getLabel().equals(CryptoNet.STEEM.getLabel())){
return new SteemCryptoNetVerifier();
}
return null;
}

View File

@ -38,7 +38,6 @@ public class GetChainId extends BaseGrapheneHandler {
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
System.out.println("<<< "+frame.getPayloadText());
String response = frame.getPayloadText();
Type GetChainIdResponse = new TypeToken<WitnessResponse<String>>(){}.getType();
@ -55,7 +54,5 @@ public class GetChainId extends BaseGrapheneHandler {
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame())
System.out.println(">>> "+frame.getPayloadText());
}
}

View File

@ -0,0 +1,65 @@
package cy.agorise.crystalwallet.network;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketFrame;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.api.BaseGrapheneHandler;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.WitnessResponse;
/**
* Created by henry on 28/2/2018.
*/
public class GetDatabaseVersion extends BaseGrapheneHandler {
private final WitnessResponseListener mListener;
public GetDatabaseVersion(WitnessResponseListener listener) {
super(listener);
this.mListener = listener;
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
ApiCall getAccountByName = new ApiCall(0, "database_api.get_version", new ArrayList<Serializable>(), RPC.VERSION, 1);
websocket.sendText(getAccountByName.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
String response = frame.getPayloadText();
Type GetChainIdResponse = new TypeToken<WitnessResponse<String>>(){}.getType();
GsonBuilder builder = new GsonBuilder();
WitnessResponse<VersionResponse> witnessResponse = builder.create().fromJson(response, GetChainIdResponse);
if(witnessResponse.error != null){
this.mListener.onError(witnessResponse.error);
}else{
this.mListener.onSuccess(witnessResponse);
}
websocket.disconnect();
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
}
public class VersionResponse{
public String blockchain_version;
public String steem_revision;
public String fc_revision;
public String chain_id;
}
}

View File

@ -0,0 +1,49 @@
package cy.agorise.crystalwallet.network;
import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.BaseResponse;
import cy.agorise.graphenej.models.WitnessResponse;
/**
*
* Created by henry on 28/2/2018.
*/
public class SteemCryptoNetVerifier extends CryptoNetVerifier {
private final CryptoNet cryptoNet = CryptoNet.STEEM;
private final String CHAIN_ID = "0000000000000000000000000000000000000000000000000000000000000000";//mainnet
@Override
public void checkURL(final String url) {
final long startTime = System.currentTimeMillis();
WebSocketThread thread = new WebSocketThread(new GetChainId(new WitnessResponseListener() {
@Override
public void onSuccess(WitnessResponse response) {
if(response.result instanceof GetDatabaseVersion.VersionResponse) {
GetDatabaseVersion.VersionResponse result = (GetDatabaseVersion.VersionResponse) response.result;
if(result.chain_id.equals(CHAIN_ID)) {
CryptoNetManager.verifiedCryptoNetURL(cryptoNet, url, System.currentTimeMillis() - startTime);
}else{
System.out.println(" BitsharesCryptoNetVerifier Error we are not in the net current chain id " + result.chain_id + " excepted " + CHAIN_ID);
//TODO handle error bad chain
}
}else{
//TODO handle error bad answer
}
}
@Override
public void onError(BaseResponse.Error error) {
//TODO handle error
System.out.println("Bad server response " + url);
}
}),url);
thread.start();
}
@Override
public String getChainId() {
return CHAIN_ID;
}
}

View File

@ -3,9 +3,16 @@ package cy.agorise.crystalwallet.requestmanagers;
import android.content.Context;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.models.GeneralCoinAccount;
import cy.agorise.crystalwallet.models.CryptoNetAccount;
import cy.agorise.crystalwallet.models.GrapheneAccount;
public class GeneralAccountSendRequest extends CryptoNetInfoRequest {
/**
* Class used to make a send amount request.
*
* Created by henry on 8/10/2017.
*/
public class BitcoinSendRequest extends CryptoNetInfoRequest {
/**
* The status code of this request
*/
@ -14,45 +21,48 @@ public class GeneralAccountSendRequest extends CryptoNetInfoRequest {
SUCCEEDED,
NO_INTERNET,
NO_SERVER_CONNECTION,
BAD_TO_ADDRESS,
NO_FEE,
NO_BALANCE,
NO_FEE,
PETITION_FAILED
}
// The app context
private Context mContext;
//The soruce Account
private GeneralCoinAccount mAccount;
// The destination account address
// The source account used to transfer fund from
private CryptoNetAccount mSourceAccount;
// The destination account id
private String mToAccount;
// The amount of the transaction
private long mAmount;
// The asset id of the transaction
private CryptoCoin mCryptoCoin;
// The memo, can be null
private String mMemo;
// The state of this request
private StatusCode status = StatusCode.NOT_STARTED;
public GeneralAccountSendRequest(CryptoCoin coin, Context context, GeneralCoinAccount account, String toAccount, long amount, String memo) {
super(coin);
public BitcoinSendRequest(Context context, CryptoNetAccount sourceAccount,
String toAccount, long amount, CryptoCoin cryptoCoin, String memo) {
super(cryptoCoin);
this.mContext = context;
this.mAccount = account;
this.mSourceAccount = sourceAccount;
this.mToAccount = toAccount;
this.mAmount = amount;
this.mCryptoCoin = cryptoCoin;
this.mMemo = memo;
}
public GeneralAccountSendRequest(CryptoCoin coin, Context context, GeneralCoinAccount account, String toAccount, long amount) {
this(coin,context,account,toAccount,amount,null);
public BitcoinSendRequest(Context context, GrapheneAccount sourceAccount,
String toAccount, long amount, CryptoCoin cryptoCoin) {
this(context, sourceAccount,toAccount,amount,cryptoCoin,null);
}
public Context getContext() {
return mContext;
}
public GeneralCoinAccount getAccount() {
return mAccount;
public CryptoNetAccount getSourceAccount() {
return mSourceAccount;
}
public String getToAccount() {
@ -63,6 +73,10 @@ public class GeneralAccountSendRequest extends CryptoNetInfoRequest {
return mAmount;
}
public CryptoCoin getCryptoCoin() {
return mCryptoCoin;
}
public String getMemo() {
return mMemo;
}
@ -75,11 +89,10 @@ public class GeneralAccountSendRequest extends CryptoNetInfoRequest {
public void setStatus(StatusCode code){
this.status = code;
this._fireOnCarryOutEvent();
this.validate();
}
public StatusCode getStatus() {
return status;
}
}

View File

@ -0,0 +1,85 @@
package cy.agorise.crystalwallet.requestmanagers;
import android.content.Context;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.models.CryptoNetAccount;
import cy.agorise.crystalwallet.models.GrapheneAccount;
/**
* Class used to make a bitcoin uri parse request.
*
* Created by henry on 11/13/2018.
*/
public class BitcoinUriParseRequest extends CryptoNetInfoRequest {
/**
* The status code of this request
*/
public enum StatusCode{
NOT_STARTED,
VALID,
NOT_VALID
}
private String uri;
private String address;
private Double amount;
private String memo;
private StatusCode status = StatusCode.NOT_STARTED;
public BitcoinUriParseRequest(String uri, CryptoCoin cryptoCoin) {
super(cryptoCoin);
this.address = "";
this.amount = -1.0;
this.memo = "";
this.uri = uri;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}
public String getMemo() {
return memo;
}
public void setMemo(String memo) {
this.memo = memo;
}
public void validate(){
if ((this.status != StatusCode.NOT_STARTED)){
this._fireOnCarryOutEvent();
}
}
public void setStatus(StatusCode code){
this.status = code;
this._fireOnCarryOutEvent();
}
public StatusCode getStatus() {
return status;
}
public String getUri() {
return uri;
}
}

View File

@ -0,0 +1,76 @@
package cy.agorise.crystalwallet.requestmanagers;
import android.content.Context;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.models.CryptoCurrency;
import cy.agorise.crystalwallet.models.CryptoNetAccount;
/**
* This class validates that an account name exist, this can be used to verified the existing accounts
* or to verified if the name is available to create an Account
*
* Created by henry on 8/10/2017.
*/
public class CalculateBitcoinUriRequest extends CryptoNetInfoRequest {
private CryptoNetAccount account;
private CryptoCurrency currency;
private double amount;
private Context context;
private String uri;
public CalculateBitcoinUriRequest(CryptoCoin coin, CryptoNetAccount account, Context context) {
super(coin);
this.account = account;
this.context = context;
}
public CalculateBitcoinUriRequest(CryptoCoin coin, CryptoNetAccount account, Context context, double amount) {
super(coin);
this.account = account;
this.context = context;
this.amount = amount;
}
public CalculateBitcoinUriRequest(CryptoCoin coin, CryptoNetAccount account, CryptoCurrency currency, double amount, Context context) {
super(coin);
this.account = account;
this.currency = currency;
this.amount = amount;
this.context = context;
}
public CryptoNetAccount getAccount() {
return account;
}
public CryptoCurrency getCurrency() {
return currency;
}
public double getAmount() {
return amount;
}
public Context getContext() {
return context;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
this.validate();
}
public void validate(){
if ((this.uri != null)){
this._fireOnCarryOutEvent();
}
}
}

View File

@ -0,0 +1,69 @@
package cy.agorise.crystalwallet.requestmanagers;
import android.content.Context;
import java.util.List;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.GrapheneAccount;
/**
* Creates bitcoin accounts using a seed,
*
* Created by Henry Varona on 10/22/2018.
*/
public class CreateBitcoinAccountRequest extends CryptoNetInfoRequest {
private AccountSeed accountSeed;
private CryptoNet accountCryptoNet;
private Context context;
/**
* The status code of this request
*/
public enum StatusCode{
NOT_STARTED,
SUCCEEDED,
ACCOUNT_EXIST
}
// The state of this request
private StatusCode status = StatusCode.NOT_STARTED;
public CreateBitcoinAccountRequest(AccountSeed accountSeed, Context context, CryptoCoin cryptoCoin){
super(cryptoCoin);
this.accountSeed = accountSeed;
this.accountCryptoNet = cryptoCoin.getCryptoNet();
this.context = context;
}
public AccountSeed getAccountSeed() {
return this.accountSeed;
}
public void validate(){
if(!status.equals(StatusCode.NOT_STARTED))
this._fireOnCarryOutEvent();
}
public CryptoNet getAccountCryptoNet() {
return this.accountCryptoNet;
}
public Context getContext() {
return context;
}
public void setStatus(StatusCode code){
this.status = code;
this.validate();
}
public StatusCode getStatus() {
return status;
}
}

View File

@ -21,13 +21,6 @@ public abstract class CryptoNetInfoRequest {
*/
protected CryptoNetInfoRequestListener listener;
protected CryptoNetInfoRequest(CryptoCoin coin){
this.coin = coin;
}
@ -43,4 +36,7 @@ public abstract class CryptoNetInfoRequest {
CryptoNetInfoRequests.getInstance().removeRequest(this);
}
public CryptoCoin getCoin() {
return coin;
}
}

View File

@ -0,0 +1,90 @@
package cy.agorise.crystalwallet.requestmanagers;
import android.content.Context;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.enums.SeedType;
/**
* Imports a bitshares accounts,
*
* return true if the account exist, and the mnemonic (brainkey provide is for that account
* Created by Henry Varona on 10/24/2018.
*/
public class ImportBitsharesAccountRequest extends CryptoNetInfoRequest {
/**
* The status code of this request
*/
public enum StatusCode{
NOT_STARTED,
SUCCEEDED,
NO_INTERNET,
NO_SERVER_CONNECTION,
ACCOUNT_DOESNT_EXIST,
BAD_SEED,
NO_ACCOUNT_DATA,
PETITION_FAILED
}
/**
* The mnemonic words
*/
private final String mnemonic;
/**
* If this seed is BIP39 or Brainkey
*/
private SeedType seedType;
/**
* The status of this request
*/
private StatusCode status = StatusCode.NOT_STARTED;
private Context context;
public ImportBitsharesAccountRequest(String mnemonic, Context context){
super(CryptoCoin.BITSHARES);
this.mnemonic = mnemonic;
this.context = context;
}
public ImportBitsharesAccountRequest(String mnemonic, Context context, boolean addAccountIfValid){
super(CryptoCoin.BITSHARES);
this.mnemonic = mnemonic;
this.context = context;
}
public void validate(){
if (!(this.status.equals(StatusCode.NOT_STARTED))){
this._fireOnCarryOutEvent();
}
}
public String getMnemonic() {
return mnemonic;
}
public SeedType getSeedType() {
return seedType;
}
public Context getContext() {
return context;
}
public void setSeedType(SeedType seedType) {
this.seedType = seedType;
}
public void setStatus(StatusCode status) {
this.status = status;
this._fireOnCarryOutEvent();
}
public StatusCode getStatus() {
return status;
}
}

View File

@ -0,0 +1,75 @@
package cy.agorise.crystalwallet.requestmanagers;
import android.content.Context;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.CryptoNetAccount;
/**
* Ask for the next address of a bitcoin alike account
*
* Created by Henry Varona on 10/22/2018.
*/
public class NextBitcoinAccountAddressRequest extends CryptoNetInfoRequest {
private CryptoNetAccount account;
private CryptoCoin cryptoCoin;
private Context context;
private String address = null;
/**
* The status code of this request
*/
public enum StatusCode{
NOT_STARTED,
SUCCEEDED
}
// The state of this request
private StatusCode status = StatusCode.NOT_STARTED;
public NextBitcoinAccountAddressRequest(CryptoNetAccount account, CryptoCoin cryptoCoin, Context context){
super(cryptoCoin);
this.account = account;
this.cryptoCoin = cryptoCoin;
this.context = context;
}
public CryptoNetAccount getAccount() {
return this.account;
}
public CryptoCoin getCryptoCoin() {
return this.cryptoCoin;
}
public void validate(){
if(!status.equals(StatusCode.NOT_STARTED))
this._fireOnCarryOutEvent();
}
public Context getContext() {
return context;
}
public void setStatus(StatusCode code){
this.status = code;
this.validate();
}
public StatusCode getStatus() {
return status;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}

View File

@ -0,0 +1,42 @@
package cy.agorise.crystalwallet.requestmanagers;
import cy.agorise.crystalwallet.enums.CryptoCoin;
/**
* This class validates that an account name exist, this can be used to verified the existing accounts
* or to verified if the name is available to create an Account
*
* Created by henry on 8/10/2017.
*/
public class ValidateBitcoinAddressRequest extends CryptoNetInfoRequest {
// The account name to validate
private String address;
// The result of the validation, or null if there isn't a response
private Boolean addressValid;
public ValidateBitcoinAddressRequest(CryptoCoin cryptoCoin, String address) {
super(cryptoCoin);
this.address = address;
}
public boolean getAddressValid(){
return this.addressValid;
}
public void setAddressValid(boolean value){
this.addressValid = value;
this.validate();
}
public void validate(){
if ((this.addressValid != null)){
this._fireOnCarryOutEvent();
}
}
public String getAddress() {
return address;
}
}

View File

@ -16,7 +16,9 @@ import java.util.ArrayList;
import java.util.List;
import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.manager.FileBackupManager;
import cy.agorise.crystalwallet.manager.GeneralAccountManager;
import cy.agorise.crystalwallet.models.BitsharesAccountNameCache;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequests;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
@ -43,6 +45,7 @@ public class CrystalWalletService extends LifecycleService {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private BitsharesAccountManager bitsharesAccountManager;
private GeneralAccountManager generalAccountManager;
private Thread LoadAccountTransactionsThread;
private Thread LoadBitsharesAccountNamesThread;
private EquivalencesThread LoadEquivalencesThread;
@ -167,7 +170,15 @@ public class CrystalWalletService extends LifecycleService {
}
});
final LiveData<List<CryptoNetAccount>> cryptoNetAccountList = db.cryptoNetAccountDao().getAllBitcoins();
cryptoNetAccountList.observe(this, new Observer<List<CryptoNetAccount>>() {
@Override
public void onChanged(@Nullable List<CryptoNetAccount> cryptoNetAccounts) {
for(CryptoNetAccount nextCryptoNetAccount : cryptoNetAccounts) {
generalAccountManager.loadAccountFromDB(nextCryptoNetAccount,thisService);
}
}
});
/*while(this.keepLoadingAccountTransactions){
try{
@ -188,11 +199,13 @@ public class CrystalWalletService extends LifecycleService {
this.cryptoNetInfoRequests = CryptoNetInfoRequests.getInstance();
this.fileServiceRequests = FileServiceRequests.getInstance();
this.bitsharesAccountManager = new BitsharesAccountManager();
this.generalAccountManager = new GeneralAccountManager(CryptoCoin.BITCOIN,this.getApplicationContext());
this.fileBackupManager = new FileBackupManager();
//Add the managers as listeners of the CryptoNetInfoRequest so
//they can carry out the info requests from the ui
this.cryptoNetInfoRequests.addListener(this.bitsharesAccountManager);
this.cryptoNetInfoRequests.addListener(this.generalAccountManager);
this.fileServiceRequests.addListener(this.fileBackupManager);
}

View File

@ -4,6 +4,10 @@ import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.DecelerateInterpolator;
import android.widget.Scroller;
import java.lang.reflect.Field;
/**
* Created by xd on 1/18/18.
@ -14,36 +18,47 @@ import android.view.MotionEvent;
public class ChildViewPager extends ViewPager {
private boolean swipeLocked;
public ChildViewPager(Context context) {
super(context);
setMyScroller();
}
public ChildViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean getSwipeLocked() {
return swipeLocked;
}
public void setSwipeLocked(boolean swipeLocked) {
this.swipeLocked = swipeLocked;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return !swipeLocked && super.onTouchEvent(event);
// stop swipe
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return !swipeLocked && super.onInterceptTouchEvent(event);
// stop switching pages
return false;
}
@Override
public boolean canScrollHorizontally(int direction) {
return !swipeLocked && super.canScrollHorizontally(direction);
private void setMyScroller() {
try {
Class<?> viewpager = ViewPager.class;
Field scroller = viewpager.getDeclaredField("mScroller");
scroller.setAccessible(true);
scroller.set(this, new MyScroller(getContext()));
} catch (Exception e) {
e.printStackTrace();
}
}
public class MyScroller extends Scroller {
public MyScroller(Context context) {
super(context, new DecelerateInterpolator());
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, 350 /*1 secs*/);
}
}
}

View File

@ -1,47 +0,0 @@
package cy.agorise.crystalwallet.util;
/**
* Created by Henry Varona on 25/2/2018.
*/
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import com.squareup.picasso.Transformation;
public class CircleTransformation implements Transformation {
@Override
public Bitmap transform(Bitmap source) {
int size = Math.min(source.getWidth(), source.getHeight());
int x = (source.getWidth() - size) / 2;
int y = (source.getHeight() - size) / 2;
Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
if (squaredBitmap != source) {
source.recycle();
}
Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
BitmapShader shader = new BitmapShader(squaredBitmap,
BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
paint.setShader(shader);
paint.setAntiAlias(true);
float r = size / 2f;
canvas.drawCircle(r, r, r, paint);
squaredBitmap.recycle();
return bitmap;
}
@Override
public String key() {
return "CircleTransformation";
}
}

View File

@ -0,0 +1,13 @@
package cy.agorise.crystalwallet.util;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
/**
* This function is used to generate a local Glide API accessible by a GlideApp call
* which makes it easier to use more advanced Glide methods.
* {more information - https://bumptech.github.io/glide/doc/generatedapi.html}
*/
@GlideModule
public class MyAppGlideModule extends AppGlideModule {}

View File

@ -1,33 +0,0 @@
package cy.agorise.crystalwallet.util;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
/**
* Created by xd on 1/24/18.
* ImageView which adjusts its size to always create a square
*/
public class SquaredImageView extends AppCompatImageView {
public SquaredImageView(Context context) {
super(context);
}
public SquaredImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public SquaredImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
setMeasuredDimension(size, size);
}
}

View File

@ -9,7 +9,6 @@ import android.arch.paging.PagedList;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.models.Contact;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
/**
* Created by Henry Varona on 1/17/2018.

View File

@ -3,19 +3,12 @@ package cy.agorise.crystalwallet.viewmodels;
import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.ViewModel;
import android.arch.paging.DataSource;
import android.arch.paging.LivePagedListBuilder;
import android.arch.paging.LivePagedListProvider;
import android.arch.paging.PagedList;
import android.content.Context;
import java.util.List;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
import cy.agorise.crystalwallet.models.CryptoCoinTransactionExtended;
import cy.agorise.crystalwallet.views.TransactionListView;
/**
* Created by Henry Varona on 12/9/2017.

View File

@ -18,7 +18,7 @@ public class ImportSeedValidator extends UIValidator {
super(context);
this.addField(new PinValidationField(pinEdit));
this.addField(new PinConfirmationValidationField(pinEdit,pinConfirmationEdit));
this.addField(new BitsharesAccountNameValidationField(bitsharesAccountNameEdit));
this.addField(new BitsharesAccountMnemonicValidationField(mnemonicEdit,bitsharesAccountNameEdit));
//this.addField(new BitsharesAccountNameValidationField(bitsharesAccountNameEdit));
//this.addField(new BitsharesAccountMnemonicValidationField(mnemonicEdit,bitsharesAccountNameEdit));
}
}

View File

@ -22,7 +22,7 @@ public class SendTransactionValidator extends UIValidator {
private CryptoNetAccount account;
public SendTransactionValidator(Context context, CryptoNetAccount account, MaterialSpinner fromEdit, EditText toEdit, Spinner assetSpinner, EditText amountEdit, EditText memoEdit){
public SendTransactionValidator(Context context, CryptoNetAccount account, Spinner fromEdit, EditText toEdit, Spinner assetSpinner, EditText amountEdit, EditText memoEdit){
super(context);
this.account = account;
this.addField(new FromValidationField(fromEdit));

View File

@ -101,6 +101,7 @@ class BitsharesAccountNameValidation : CustomValidationField, UIValidator {
result = false
accountNameField.fieldValidatorModel.setInvalid()
accountNameField.fieldValidatorModel.message = this.accountNameField.resources.getString(R.string.create_account_window_err_at_least_one_character)
} else {
/*

View File

@ -5,6 +5,8 @@ import android.widget.Spinner;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.models.CryptoCoinBalance;
import cy.agorise.crystalwallet.models.CryptoCurrency;
import cy.agorise.crystalwallet.models.CryptoNetAccount;
@ -29,7 +31,12 @@ public class AmountValidationField extends ValidationField {
public void validate(){
try {
final float newAmountValue = Float.parseFloat(amountField.getText().toString());
final CryptoCurrency cryptoCurrency = (CryptoCurrency)assetSpinner.getSelectedItem();
final CryptoCurrency cryptoCurrency;
if(this.account.getCryptoNet() == CryptoNet.BITSHARES) {
cryptoCurrency = (CryptoCurrency) assetSpinner.getSelectedItem();
}else{
cryptoCurrency = CrystalDatabase.getAppDatabase(amountField.getContext()).cryptoCurrencyDao().getByNameAndCryptoNet(CryptoCoin.getByCryptoNet(this.account.getCryptoNet()).get(0).getLabel(),this.account.getCryptoNet().name());
}
/*
* Validation for the money
@ -48,7 +55,12 @@ public class AmountValidationField extends ValidationField {
CryptoCoinBalance balance = CrystalDatabase.getAppDatabase(amountField.getContext()).cryptoCoinBalanceDao().getBalanceFromAccount(this.account.getId(),cryptoCurrency.getId());
if (newAmountValue > balance.getBalance()){
double balanceDouble = 0;
if(balance != null){
balanceDouble = balance.getBalance();
}
if (newAmountValue > balanceDouble){
setMessageForValue(mixedValues, validator.getContext().getResources().getString(R.string.insufficient_amount));
setValidForValue(mixedValues, false);
} else if (newAmountValue == 0){
@ -61,6 +73,8 @@ public class AmountValidationField extends ValidationField {
setLastValue("");
setMessageForValue("",validator.getContext().getResources().getString(R.string.please_enter_valid_amount));
setValidForValue("", false);
} catch (Exception e ){
e.printStackTrace();
}
}
}

View File

@ -18,16 +18,22 @@ public class AssetValidationField extends ValidationField {
}
public void validate(){
final CryptoCurrency cryptoCurrencySelected = (CryptoCurrency) this.assetField.getSelectedItem();
if (cryptoCurrencySelected != null) {
final String newValue = "" + cryptoCurrencySelected.getId();
if (this.assetField.getSelectedItem() instanceof CryptoCurrency) {
final CryptoCurrency cryptoCurrencySelected = (CryptoCurrency) this.assetField.getSelectedItem();
if (cryptoCurrencySelected != null) {
final String newValue = "" + cryptoCurrencySelected.getId();
this.setLastValue(newValue);
setValidForValue(newValue, true);
} else {
final String newValue = "" + -1;
setMessageForValue(newValue, "Select a currency");
this.setLastValue(newValue);
setValidForValue(newValue, false);
}
} else {
final String newValue = "" + -1;
this.setLastValue(newValue);
setValidForValue(newValue, true);
} else {
final String newValue = ""+-1;
setMessageForValue(newValue,"Select a currency");
this.setLastValue(newValue);
setValidForValue(newValue, false);
}
}
}

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