Compare commits

..

317 commits

Author SHA1 Message Date
jessopb
fdd759b241
Merge pull request #1236 from ktprograms/readme-google-services
Add google-services.json instructions to README
2022-11-29 13:07:52 -05:00
jessopb
57cd649c02
Update README.md 2022-11-29 13:07:30 -05:00
jessopb
7250435d7f
Create deploy.yml 2022-11-25 14:03:02 -05:00
jessopb
b81abf74a1
Merge pull request #1244 from Coolguy3289/patch-1
Update LBRY_TV_CONNECTION_STRING to new Odysee Backend
2022-11-23 13:59:38 -05:00
Ralph
208e2c2d42
Update LBRY_TV_CONNECTION_STRING to new Odysee Backend 2022-11-23 11:29:52 -05:00
kt programs
a5bdd1c042 Add google-services.json instructions to README 2022-04-18 12:14:25 +08:00
Alex Grin
1e3a74cae1
Update README.md 2021-09-28 10:19:37 -04:00
Akinwale Ariwodola
ca08f71a72 sdk 0.102.0 2021-08-20 15:32:14 +01:00
Akinwale Ariwodola
b60ca39df1 fix crash bug when trying to play content 2021-08-19 12:27:18 +01:00
soup-jingle
696bc86b7c
Added QR scanner to wallet send card (#1194)
* Added QR scanner to wallet send card

* * app/src/main/java/io/lbry/browser/ui/wallet/WalletFragment.java:

Changed orientation of QR scanner, along with some style corrections

* Revert "* app/src/main/java/io/lbry/browser/ui/wallet/WalletFragment.java:"

This reverts commit 519c45ae0d.

* Fixed orientation of QR scanner activity, plus style corrections
2021-08-13 09:01:53 +01:00
Akinwale Ariwodola
4c163c6244
Merge pull request #1216 from lbryio/new_cdn_url
Use the new CDN url scheme for thumbnails
2021-08-13 09:01:09 +01:00
Akinwale Ariwodola
2ad49ca281
Merge pull request #1208 from lbryio/claimid-modifier-char
Accept colon or octoshape as claimID modifier character
2021-08-11 10:20:58 +01:00
Javi Rueda
56caeef72b Use the new CDN url scheme for thumbnails 2021-08-10 18:28:20 +02:00
Javi Rueda
84bb014557 Accept colon or octoshape as claimID modifier character and add unit tests 2021-07-29 00:55:45 +02:00
Akinwale Ariwodola
9278e74e85 remove reference to bintray 2021-06-25 19:34:58 +01:00
Akinwale Ariwodola
cf6f09c60d fix gitlab CI script 2021-06-25 19:25:29 +01:00
Akinwale Ariwodola
f2cbed48d9 fix git-secret install source 2021-06-25 19:23:30 +01:00
Akinwale Ariwodola
f1ead0c247 bumpversion 0.17.0 --> 0.17.1 2021-06-25 19:08:30 +01:00
Akinwale Ariwodola
5f2c72ec4d sdk 0.100.0 / switch to maven central for lbrysdk Gradle dependency 2021-06-25 19:07:47 +01:00
Javi Rueda
a6869eb2e6
Fix crash for anonymous content or without tags (#1190) 2021-05-10 17:14:22 +02:00
Javi Rueda
cbae6c476a
Claim has source (#1188)
* Pass has_source parameter to claim_search to ignore livestreamings

* Update to SDK 0.94.1 and allow user to view livestream content on Odysee
2021-05-10 17:13:28 +02:00
Javi Rueda
48d257ceaf
Show total wallet balance in USD instead of spendable balance conversion (#1186) 2021-04-19 19:55:49 +02:00
Javi Rueda
5355456498
Don't show link to Bittrex (#1181) 2021-04-12 18:53:59 +02:00
Javi Rueda
1436895ace
Copy file to public Downloads directory (#1180) 2021-04-12 18:53:13 +02:00
Javi Rueda
6e32f7724f
Notification improvements (#1175)
* Stop showing notification list when going from notification's content to related content

* Add is_app_readable parameter to notification_list call

* Return current fragment on main activity
2021-03-29 17:39:27 +02:00
Javi Rueda
3f5104d60a
Hide floating wallet on the Channel fragment (#1177)
* Hide floating wallet on the Channel fragment

* Unregister OnPageChangeCallback when exiting channel fragment
2021-03-29 17:38:51 +02:00
Javi Rueda
d8fdb3b818
Decode and encode urls with non-authorized characters (#1174)
* Decode URL encoded links and unit test it

* Encode UTF8 characters on LBRY.TV links and unit test it
2021-03-19 11:00:24 +01:00
Akinwale Ariwodola
5c187e7a8d Merge branch 'master' of https://github.com/lbryio/lbry-android 2021-03-16 09:24:53 +01:00
Akinwale Ariwodola
24862550a1 bumpversion 0.16.14 --> 0.17.0 2021-03-16 09:24:33 +01:00
Akinwale Ariwodola
146fced44e
Merge pull request #1162 from kekkyojin/wallet-total
Use total wallet balance instead of spendable
2021-03-16 09:22:57 +01:00
Akinwale Ariwodola
d535ed8c98
Merge pull request #1168 from lbryio/enable-phone-verification
Enable phone verification
2021-03-16 09:22:35 +01:00
Akinwale Ariwodola
b675dbad9b Merge branch 'master' of https://github.com/lbryio/lbry-android 2021-03-16 08:59:42 +01:00
Javi Rueda
dabe9fe691
Disable Javascript (#1171) 2021-03-15 20:08:18 +01:00
Javi Rueda
9ac216504d
Use ':' as separator for LBRY.TV sharing links (#1169) 2021-03-15 16:22:18 +01:00
Javi Rueda
911ca998e7 Use total wallet balance instead of spendable 2021-03-15 12:48:51 +01:00
Javi Rueda
36c105d3a7 Enable phone verification
Show the Close button after phone number has been verified
2021-03-11 17:46:17 +01:00
Akinwale Ariwodola
a655d0112b
PR cleanup (#1164)
* simplify code for readability
* code-cleanup. Make non-changing variables final
* Bump buildToolsVersion 29.0.1 -> 29.0.2 for FDroid build compability
* Set gradle version to static 3.6.4 instead of dynamic 3.+
* Use StandardCharsets.UTF_8 instead of string UTF8
* Remove unused imports
* Add missing null check

Co-authored-by: Patric Karlström <patric@pkcab.eu>
2021-03-08 20:15:23 +01:00
Akinwale Ariwodola
493c771e94 sdk 0.91.0 2021-03-08 19:52:15 +01:00
Akinwale Ariwodola
e9d70dbf87
Merge pull request #1163 from lbryio/iap-check
allow users to initiate a purchase flow check
2021-03-08 19:40:19 +01:00
Akinwale Ariwodola
0849ce2b66 allow users to initiate a purchase flow check 2021-03-08 19:38:00 +01:00
Javi Rueda
d0a8b3b218
Fix mass tips unlocking (#1157) 2021-03-06 00:32:12 +01:00
Javi Rueda
a8cdc4a771
Move the wallet floating button away when scroll gets to the bottom at FileViewFragment (#1155) 2021-03-04 19:11:19 +01:00
Javi Rueda
67b883660f
Hide support button and comments consistently with claim tags (#1152)
* Hide support button and comments consistently with claim tags

* Set visibility to GONE instead of INVISIBLE for the tipping button
2021-02-26 19:31:11 +01:00
Javi Rueda
f328efb831
Allow media to be played automatically (#1153)
* Allow media to be played automatically

* Fix for media been always autoplayed when opened from miniplayer
2021-02-26 19:24:17 +01:00
Javi Rueda
b2f56364d6
Use commentron instead of Comment SDK calls (#1149)
* Use commentron instead of Comment SDK calls

* Use current timestamp as 'id' for comment server request
2021-02-24 16:23:33 +01:00
Akinwale Ariwodola
2761857fe8
Merge pull request #1135 from kekkyojin/totvstring-unittest
Add unit test for LbryUri.toTvString
2021-02-22 20:07:55 +01:00
Akinwale Ariwodola
d439260d69
Merge pull request #1132 from kekkyojin/ignore_helpertest
Change HelperTest to JUnit4 and remove example test class
2021-02-16 17:02:48 +01:00
Akinwale Ariwodola
f0f0a5028b
Merge pull request #1123 from kekkyojin/style-code-tag
Styling <code> tag for Markdown content
2021-02-16 16:59:36 +01:00
Javi Rueda
13e170cb61 Add unit test for LbryUri.toTvString 2021-02-16 16:42:05 +01:00
Javi Rueda
127d8052ca Change HelperTest to JUnit4 and remove example test class 2021-02-12 01:28:22 +01:00
Javi Rueda
1b3086572f Styling <code> tag for Markdown content 2021-02-02 19:00:17 +01:00
Akinwale Ariwodola
6fa308ef96 bumpversion 0.16.13 --> 0.16.14 2021-01-25 18:54:00 +01:00
Akinwale Ariwodola
f3e513fc2d Update translations and content filters 2021-01-25 18:52:21 +01:00
Akinwale Ariwodola
d72a8faec4 sdk 0.88.0 2021-01-23 19:23:11 +01:00
Akinwale Ariwodola
fdb4578349
Merge pull request #1119 from kekkyojin/remove-sha256
Remove Helper.SHA256() method
2021-01-18 20:24:22 +01:00
Javi Rueda
c08237d399 Remove Helper.SHA256() method 2021-01-14 13:04:43 +01:00
Akinwale Ariwodola
bdf8a58818 bumpversion 0.16.12 --> 0.16.13 2021-01-12 09:11:26 +01:00
Akinwale Ariwodola
e1d51c881a Fix crash bug for anonymous repost claim results. Better handle channel repost navigation. 2021-01-12 09:07:33 +01:00
Akinwale Ariwodola
28212808f8
Merge pull request #1115 from pakar/master
Upgrade ExoPlayer from 2.11.4 to 2.12.2
2021-01-12 08:35:04 +01:00
Akinwale Ariwodola
74f08f8f98
Merge pull request #1102 from kekkyojin/fix-markdown-rendering
Use Base64 to encode html when rendering Markdown files
2021-01-12 08:33:40 +01:00
Akinwale Ariwodola
cb5c29fbce
Merge pull request #1117 from kekkyojin/fix-nosuchmethod-commons
Fix NoSuchMethod exception for HelperSHA256 on API Level prior to P
2021-01-12 08:32:08 +01:00
Javi Rueda
4d775d3a17 Fix NoSuchMethod exception for HelperSHA256 on API Level prior to P 2021-01-11 20:44:03 +01:00
Patric Karlström
fd94ef9fa7 Upgrade ExoPlayer from 2.11.4 to 2.12.2 2021-01-11 01:10:23 +01:00
Javi Rueda
2e0331305a Use Base64 to encode html when rendering Markdown files 2020-12-29 11:30:05 +01:00
Akinwale Ariwodola
fb560f8f01 fix last resource build error 2020-12-29 07:20:46 +01:00
Akinwale Ariwodola
ef80c9f7fd fix resource build error 2020-12-29 07:13:57 +01:00
Akinwale Ariwodola
99a4a0a22f Remove comment post confirmation. Fix resources build error. 2020-12-29 07:07:53 +01:00
Akinwale Ariwodola
136853853a bumpversion 0.16.11 --> 0.16.12 2020-12-29 07:02:13 +01:00
Akinwale Ariwodola
ebf3299c30 add scrollbars attribute to listview 2020-12-29 07:00:45 +01:00
Akinwale Ariwodola
036b49a871
Merge pull request #1097 from kekkyojin/remove_stages_scrollbar
Avoid showing vertical scroll bar on stage error listview
2020-12-29 07:00:02 +01:00
Akinwale Ariwodola
67d5f88d34
Merge pull request #1100 from clay53/master
Changed wallet send notification to accept 4 digit decimal
2020-12-29 06:52:16 +01:00
Akinwale Ariwodola
b1e0b9af33 sdk 0.87.0. Show confirmation for unfollow. 2020-12-29 06:47:10 +01:00
Clayton Hickey
c30b012787 Changed wallet send notification to accept 4 digit decimal 2020-12-27 03:54:36 -05:00
Javi Rueda
c8c6305757 Avoid showing vertical scroll bar on stage error listview 2020-12-22 02:26:37 +01:00
Akinwale Ariwodola
ee1d090e62
Merge pull request #1068 from kekkyojin/fix-lintproblems
Some fixes for lint
2020-12-21 19:43:08 +01:00
Javi Rueda
9e56a86492 Some fixes for lint 2020-12-18 14:30:48 +01:00
Akinwale Ariwodola
e6b83877f1
Merge pull request #1063 from clay53/1042
Removed comment tips
2020-12-18 13:01:49 +01:00
Akinwale Ariwodola
b567e39aef
Merge pull request #1069 from kekkyojin/add-twitter-instructions
Provide instructions to add dummy Twitter API credentials
2020-12-18 12:22:03 +01:00
Akinwale Ariwodola
ae62dba0a6
Merge pull request #1071 from kekkyojin/sha256-unittest
Add unit test for Helper.SHA256(String)
2020-12-18 12:21:39 +01:00
Akinwale Ariwodola
df1e8abf50
Merge pull request #1067 from kekkyojin/use-apache-hex
Replace Hex class from GMS with the one from Apache
2020-12-18 12:20:42 +01:00
Javi Rueda
31cfb26c3b Add unit test for Helper.SHA256(String) 2020-12-15 13:45:32 +01:00
Javi Rueda
e5f34dc464 Provide instructions to add dummy Twitter API credentials 2020-12-14 20:38:57 +01:00
Javi Rueda
601031e55d Replace Hex class from GMS with the one from Apache 2020-12-14 18:17:13 +01:00
Akinwale Ariwodola
a9aadbe6a8 bumpversion 0.16.10 --> 0.16.11 2020-12-13 18:08:10 +01:00
Akinwale Ariwodola
f9a4b71037 fix crash error when following doesn't exist in shared user state 2020-12-13 18:07:19 +01:00
Clayton Hickey
0f10e9dc1f Removed comment tips 2020-12-12 20:11:00 -05:00
Akinwale Ariwodola
0647deb06c
Merge pull request #1061 from kekkyojin/startupstage-listview
Use ListView to show startup stage errors
2020-12-12 23:16:17 +01:00
Akinwale Ariwodola
b2f5fec293 make the channel filter name alphabet uppercase 2020-12-10 09:57:27 +01:00
Javi Rueda
daf4e5aca2 Use ListView to show startup stage errors 2020-12-09 21:05:21 +01:00
Akinwale Ariwodola
c1324efb41 bumpversion 0.16.9 --> 0.16.10 2020-12-09 11:47:58 +01:00
Akinwale Ariwodola
6221de2d3c sync notificationsDisabled states for followed channels 2020-12-09 11:31:13 +01:00
Akinwale Ariwodola
983bc68af2 Merge branch 'master' of https://github.com/lbryio/lbry-android 2020-12-09 11:00:25 +01:00
Akinwale Ariwodola
f1b167693d SDK 0.86.1. is_seen vs is_read swap. Fix error condition where wallet sync starts if the wallet is not ready. 2020-12-09 10:59:25 +01:00
Akinwale Ariwodola
68ac64b534
Merge pull request #1057 from kekkyojin/sort-commentreplies
Sort replies from oldest to newest
2020-12-05 04:32:41 +01:00
Javi Rueda
6819ae46f9 Sort replies from oldest to newest 2020-12-01 04:58:48 +01:00
Akinwale Ariwodola
6c406c5a85 share_usage_data setting 2020-11-29 05:07:54 +01:00
Akinwale Ariwodola
c179243d22 update dependencies 2020-11-26 00:04:37 +01:00
Akinwale Ariwodola
d0f5504c80 LbryUri parsing improvements. Remove duplicate code. 2020-11-15 09:11:50 +01:00
Akinwale Ariwodola
896c566a02
Merge pull request #1045 from kekkyojin/open-file-external
Offer opening unsupported filetypes with external app
2020-11-13 15:42:59 +01:00
Javi Rueda
da9352cc68 Offer opening unsupported filetypes with external app 2020-10-27 14:28:15 +01:00
Akinwale Ariwodola
b8d2375e20
Merge pull request #1044 from ycohen-dev/fix_issue_1043
Fix floating wallet/rewards bar for RTL layout
2020-10-23 13:25:07 +01:00
Akinwale Ariwodola
dd52ff9d07
Merge pull request #1033 from kekkyojin/md-text-rendering
Fix for text not rendering after # in markdown
2020-10-23 13:23:15 +01:00
yuval
d9891f8a8a Floating wallet balance/rewards now visible in RTL layout
(Fix issue #1043)
2020-10-22 22:22:44 +03:00
Akinwale Ariwodola
ea5fe6842d
Merge pull request #1041 from ycohen-dev/fix_issue_1040
fix issue 1040
2020-10-21 00:15:36 +01:00
Akinwale Ariwodola
fc649187df
Merge pull request #1039 from ycohen-dev/fix_issue_1038
fix issue 1038
2020-10-21 00:14:15 +01:00
yuval
b0f7c41885 fix issue 1040
Transaction amount text Direction changed to LTR.
Transaction amount pinned to end of view
2020-10-17 22:18:14 +03:00
yuval
45935717c8 fix issue 1038
Replaced gravity pinning to "end" instead of "right"
Pinned claim id which could be in rtl and ltr scripts to the start of the layout
2020-10-16 23:40:28 +03:00
Akinwale Ariwodola
ac5e666369 bumpversion 0.16.8 --> 0.16.9 2020-10-16 06:58:57 +01:00
Akinwale Ariwodola
ffeb72a383 Publish mature tags. String resource updates. 2020-10-16 06:53:08 +01:00
Akinwale Ariwodola
0ec09d751c
Merge pull request #1036 from ycohen-dev/fix_issue_1034
Fix issue #1034
2020-10-16 06:39:45 +01:00
Akinwale Ariwodola
b8e4fcff92
Merge pull request #1037 from lbryio/follow-text
add follow text beside follow icon
2020-10-16 06:37:50 +01:00
Akinwale Ariwodola
b79f0f4820 add follow text beside follow icon 2020-10-16 06:36:36 +01:00
yuval
a1cd58a214 Fix issue #1034
Hide notifications when entering PiP mode
2020-10-16 00:11:08 +03:00
Javi Rueda
cfca8facbe Use '%23' instead of '#' when rendering #hashtag style text from markdown 2020-10-15 01:49:53 +02:00
Akinwale Ariwodola
066a0a099c
Merge pull request #1032 from lbryio/wallet-balance-page
use credits icon on wallet balance page
2020-10-14 15:33:25 +01:00
Akinwale Ariwodola
afeee2e5df use credits icon on wallet balance page 2020-10-14 15:30:59 +01:00
Akinwale Ariwodola
76036acfc7 Merge branch 'master' of https://github.com/lbryio/lbry-android 2020-10-14 15:17:35 +01:00
Akinwale Ariwodola
897bfdaffd fix build error 2020-10-14 15:16:45 +01:00
Akinwale Ariwodola
7475ae323c
Merge pull request #1031 from ycohen-dev/fix_issue_1030
Fix issue #1030
2020-10-14 15:12:34 +01:00
Akinwale Ariwodola
faf7f3ccbf make credits translatable 2020-10-14 15:11:36 +01:00
Akinwale Ariwodola
4940d1ca33 Merge branch 'master' of https://github.com/lbryio/lbry-android 2020-10-14 15:10:25 +01:00
Akinwale Ariwodola
3d48fa5741 also check pending when checking if item is a placeholder 2020-10-14 15:09:59 +01:00
yuval
f9887cffae Fix issue #1030
Dismiss all active dialog fragment when entering PiP mode
So that PiP window contain the video only
2020-10-13 23:17:58 +03:00
Akinwale Ariwodola
6644907665
Merge pull request #1027 from ycohen-dev/fix_issue_1025
Fix issue #1025
2020-10-12 19:25:48 +01:00
Akinwale Ariwodola
5f850685d6 bumpversion 0.16.7 --> 0.16.8 2020-10-12 11:48:24 +01:00
Akinwale Ariwodola
5f9674e49c do not open lbry.*/$/verify links in the app 2020-10-12 11:47:45 +01:00
yuval
0d185c6db3 Fix issue #1025
Increased size of currency selection spinner in publish form fragment
Now spinner is in proper size to display "LBRY Credits" default value
2020-10-10 16:58:29 +03:00
Akinwale Ariwodola
53d22dd22d remove debug logs 2020-10-09 07:43:38 +01:00
Akinwale Ariwodola
ff8ffda3c6 bumpversion 0.16.6 --> 0.16.7 2020-10-09 07:39:46 +01:00
Akinwale Ariwodola
64bd540322
In-app notification options (#1023)
* pull-down to refresh in-app notifications
* support deleting notifications
2020-10-09 07:38:47 +01:00
Akinwale Ariwodola
919f9a48f3
Merge pull request #1022 from lbryio/bell-icon
per-channel subscribe notification toggle
2020-10-09 06:58:08 +01:00
Akinwale Ariwodola
746d442051 per-channel subscribe notification toggle 2020-10-09 06:56:13 +01:00
Akinwale Ariwodola
1877b75188 fix strings.xml 2020-10-06 07:14:42 +01:00
Akinwale Ariwodola
6e8c38cace update module versions 2020-10-05 16:56:17 +01:00
Akinwale Ariwodola
eab7bab267
Credits icon (#1021)
* new credits icon
* remove references to LBC
2020-10-05 16:50:39 +01:00
Akinwale Ariwodola
aab648c3cf
Fix errors with deep link handling (#1019) 2020-10-02 12:39:37 +01:00
Javi Rueda
f79ff509bd
Support some lbry hosts to deep links. Add unit tests (#1008)
* Support lbry.tv and other supported hosts for deep links. Add unit tests for LbryUri parse() method
* add unit test for lbry protocol schema
* Add unit test for lbry.tv deep-link with only channel
2020-10-02 12:34:38 +01:00
Akinwale Ariwodola
1197e990ca cleanup debug logging 2020-10-02 12:09:33 +01:00
Akinwale Ariwodola
dce1c0715e move Unpublish button 2020-10-02 12:08:33 +01:00
Akinwale Ariwodola
6a0263c5bc
Merge pull request #1013 from clay53/987
Add ability to download & delete your own videos and move unpublish button to description
2020-10-02 11:05:44 +01:00
Akinwale Ariwodola
ca64db0499 sdk 0.82.0 2020-10-01 12:42:48 +01:00
Akinwale Ariwodola
a5d4eda4d1 bumpversion 0.16.5 --> 0.16.6 2020-09-25 14:45:48 +01:00
Akinwale Ariwodola
5d210961c1 apply mature filter to Editor's Choice 2020-09-25 14:41:40 +01:00
Akinwale Ariwodola
1b88f565af bumpversion 0.16.4 --> 0.16.5 2020-09-23 16:37:41 +01:00
Akinwale Ariwodola
991a98b571 fix shuffle mode player getting stuck 2020-09-23 16:35:59 +01:00
Akinwale Ariwodola
0073277d6e
Merge pull request #1017 from lbryio/shuffle-random
keep track of watched content for subsequent shuffle sessions
2020-09-23 16:23:57 +01:00
Akinwale Ariwodola
eeca602f7a keep track of watched content for subsequent shuffle sessions 2020-09-23 15:31:34 +01:00
Akinwale Ariwodola
6c171560fd remove Finnish strings 2020-09-18 14:56:35 +01:00
Akinwale Ariwodola
66c4c00215 bumpversion 0.16.3 --> 0.16.4 2020-09-18 14:54:59 +01:00
Akinwale Ariwodola
9b9ef9ab74
Surf mode experiment (#1003)
* surf mode implementation
* occupy entire vertical area
* fix onResume logic
* shuffle mode with selected channel ids
2020-09-18 14:53:19 +01:00
Clayton Hickey
39a289e7f1
Add ability to download & delete your own videos and move unpublish button to description 2020-09-16 14:31:03 -04:00
Akinwale Ariwodola
722c829502 fix paid content stream source 2020-09-14 18:21:31 +01:00
Akinwale Ariwodola
7ecb80d136
Merge pull request #1009 from lbryio/mini-player-margin
add setting for mini-player bottom spacing
2020-09-14 18:05:09 +01:00
Akinwale Ariwodola
ea19af04d4 add setting for mini-player bottom spacing 2020-09-14 18:01:38 +01:00
Akinwale Ariwodola
8196b69211 bumpversion 0.16.2 --> 0.16.3 2020-09-14 11:20:47 +01:00
Akinwale Ariwodola
dc861caf6c fix websocket for Android < 7.0 2020-09-14 11:19:50 +01:00
Akinwale Ariwodola
adb5ffa8d0 exclude x86_64 lib from build 2020-09-14 11:06:39 +01:00
Akinwale Ariwodola
d918cb28bd sdk 0.81.0 2020-09-11 13:50:16 +01:00
Akinwale Ariwodola
7ce7314ab9 bumpversion 0.16.1 --> 0.16.2 2020-09-11 13:41:30 +01:00
Javi Rueda
13e5caa0ef
Use Start/End instead of Left/Right to support RTL (#1005)
* Use Start/End instead of Left/Right to support RTL
* Change it also for relative layout properties
2020-09-11 13:28:37 +01:00
Akinwale Ariwodola
ff59a7a89f
Merge pull request #1007 from lbryio/first-run-install-id
generate install_id natively
2020-09-11 13:22:42 +01:00
Akinwale Ariwodola
c3efa4d004 generate install_id natively 2020-09-10 12:20:49 +01:00
Akinwale Ariwodola
ed50e1300a
real time notifications over websocket (#1000)
* real time notifications over websocket
* automatically re-establish websocket connections
2020-09-09 14:16:22 +01:00
Akinwale Ariwodola
86dbfd54d1
Merge pull request #1006 from lbryio/notification-back
proper back navigation to the notifications screen
2020-09-09 14:05:29 +01:00
Akinwale Ariwodola
535120eebd proper back navigation to the notifications screen 2020-09-09 14:03:36 +01:00
Akinwale Ariwodola
c1106d7186 display correct playback speed for media 2020-09-04 12:50:22 +01:00
Akinwale Ariwodola
45be7f2c9b
Merge pull request #1004 from lbryio/player-resume-black-screen
display player view correctly after dismissing PIP view
2020-09-04 12:33:53 +01:00
Akinwale Ariwodola
d6baf3d1c8 display player view correctly after dismissing PIP view 2020-09-04 12:32:53 +01:00
Akinwale Ariwodola
08c38c1723 bumpversion 0.16.0 --> 0.16.1 2020-08-28 10:07:20 +01:00
Javi Rueda
af4fa454d3
Add setting to enable sending buffering events (#993)
* Add setting to enable sending buffering events
* Use true as default value
2020-08-28 10:06:41 +01:00
Akinwale Ariwodola
0620582a4e fix in-app notification read background in dark mode 2020-08-28 10:04:10 +01:00
Akinwale Ariwodola
b11c07e3d1 fix comment redirect from device notifications 2020-08-28 10:00:57 +01:00
Akinwale Ariwodola
593b34079c load notifications after app startup 2020-08-20 20:47:14 +01:00
Akinwale Ariwodola
becb533624 fix notification relative time display 2020-08-20 20:32:22 +01:00
Akinwale Ariwodola
8efc0522f2 load unread count after fetching remote notifications 2020-08-20 20:03:30 +01:00
Akinwale Ariwodola
cead924ca5 Notification timestamp timezone fix. Tweak loading unread notification count. 2020-08-20 19:56:35 +01:00
Akinwale Ariwodola
f83a043664 fix notification sorting 2020-08-20 19:00:21 +01:00
Akinwale Ariwodola
e8a0bca5ea tweak equality check for LbryNotification 2020-08-20 18:45:02 +01:00
Akinwale Ariwodola
1198c2298a add and update translations 2020-08-20 18:28:32 +01:00
Akinwale Ariwodola
7205acb9d6 Sort newly added notifications. Add remote loading TTL. 2020-08-20 18:06:35 +01:00
Akinwale Ariwodola
d21cfd55ab Handle special URLs from notifications. Disable auto load/play for comment notifications. 2020-08-20 16:39:45 +01:00
Akinwale Ariwodola
47cbc3624c
Merge pull request #980 from lbryio/channel-comment-hash
properly handle channel comment notifications
2020-08-20 16:26:08 +01:00
Akinwale Ariwodola
1f9a0886a0 properly handle channel comment notifications 2020-08-20 16:20:46 +01:00
Akinwale Ariwodola
dd5ea68915 new YRBL 2020-08-20 14:52:41 +01:00
Akinwale Ariwodola
b3bba4d273
Merge pull request #979 from lbryio/is-seen
check is_seen flag. display comment author thumbnails if present.
2020-08-20 13:30:28 +01:00
Akinwale Ariwodola
10c123ba6a check is_seen flag. display comment author thumbnails if present. 2020-08-20 13:26:41 +01:00
Akinwale Ariwodola
a8ba45b941 bumpversion 0.15.17 --> 0.16.0 2020-08-19 18:18:37 +01:00
Akinwale Ariwodola
3652cdf6bc CI apt-transport-https 2020-08-19 18:14:13 +01:00
Akinwale Ariwodola
f2a7a8c439 cleanup: yarn.lock 2020-08-19 18:10:57 +01:00
Akinwale Ariwodola
78d5a99441
Gradle fix (#978) 2020-08-19 18:09:37 +01:00
Akinwale Ariwodola
6931dbe79c
Instant verification (#974)
* add instant verification options and Google Play Billing
bumpversion 0.15.16 --> 0.15.17
restore account_undergo_review default string
twitter sign-in flow
fix build error
final changes
update api key and secret
* tweak build script
2020-08-19 17:23:35 +01:00
Akinwale Ariwodola
4d024c06cc
In-app notifications (#969)
* display notification bell icon beside URL bar
* notification model
* persist received notifications
* Refresh notification list. Add  is_read flag and display unread count.
* Hide notifications when opening the navigatinon drawer. Open selected notification item before hiding the list.
* update local store with remote notifications
* use card view for notification items
* make notification cards clickable
* handle comment hash
* fix remote notifications not loading. fix comment scroll.
* Improve in-app comment notification linking. Add icons.
2020-08-18 14:19:35 +01:00
Akinwale Ariwodola
ddcb190457 do not display global background servce initializing indicator 2020-08-07 10:11:57 +01:00
Akinwale Ariwodola
dd14b90d7e
Merge pull request #967 from kekkyojin/sendto-feat
Allow to publish files via Send To feature
2020-07-29 15:20:59 +01:00
Akinwale Ariwodola
4b86444478 bumpversion 0.15.15 --> 0.15.16 2020-07-27 11:11:10 +01:00
Javi Rueda
feb7f260dc Allow to publish files via Send To feature 2020-07-24 01:59:59 +02:00
Akinwale Ariwodola
74c7a1de4d sdk 0.79.1. Clear resolved sub cache after sync. 2020-07-23 20:04:48 +01:00
Akinwale Ariwodola
88a43dc679 set sync interval back to 5 minutes 2020-07-22 05:52:44 +01:00
Akinwale Ariwodola
1bb5ce72fa Proper fix for subscription sync retention. Eliminate double sync attempt on startup. 2020-07-22 05:49:48 +01:00
Akinwale Ariwodola
b4ce544965 bumpversion 0.15.14 --> 0.15.15 2020-07-21 21:25:26 +01:00
Akinwale Ariwodola
1ec6f6173a sdk 0.79.0 2020-07-21 21:13:05 +01:00
Akinwale Ariwodola
8ee19b3f5c
Sub sync fix (#965)
* improve sub sync merge handling logic
* do not try to re-subscribe local syncs remotely whhen merging
2020-07-21 19:49:22 +01:00
Akinwale Ariwodola
1055392b7f temporarily omit duration 2020-07-21 19:39:47 +01:00
Akinwale Ariwodola
c772cf2ead
check buffering events (#952)
* check buffering events
* use the claim permanent url for buffer events
* update request parameters
* hash user ids for buffer events
* update buffer event endpoint
2020-07-21 19:30:45 +01:00
Akinwale Ariwodola
481c50f465 fix open.lbry.com regex 2020-07-17 11:39:59 +01:00
Akinwale Ariwodola
71bc969f2a
Merge pull request #962 from kekkyojin/deeplink
Add open.lbry.com deep-links
2020-07-17 11:29:16 +01:00
Akinwale Ariwodola
e4edebfed7 fix for follow / unfollow confusion 2020-07-16 13:51:08 +01:00
Javi Rueda
e8a5ab8307 Add open.lbry.com deep-links 2020-07-13 19:00:12 +02:00
Akinwale Ariwodola
3738f3af21
Merge pull request #954 from lbryio/invalidated-auth
gracefully handle invalidated auth tokens
2020-07-05 19:07:21 +01:00
Akinwale Ariwodola
74d10e4199 gracefully handle invalidated auth tokens 2020-07-05 18:40:31 +01:00
Akinwale Ariwodola
080e00becf
Merge pull request #949 from kekkyojin/patch-1
Bintray repository is no longer needed
2020-06-30 15:30:18 +01:00
Javi Rueda
049d905d7d
Bintray repository is no longer needed
LBRY SDK is now hosted on JCenter, which is already available out-of-the-box on Android Studio. The Bintray url is no longer reguired. When building LBRY on F-Droid, I have to remove this line on the build YAML script and it is building correctly.

On my local machine, it builds after commenting it.
2020-06-26 21:06:12 +02:00
Akinwale Ariwodola
33094f8c88 fix empty bindArgs crash error for sql query to clear subscriptions 2020-06-25 23:33:04 +01:00
Akinwale Ariwodola
6c44e503db
fix subscriptions sync (#947) 2020-06-25 21:16:41 +01:00
Akinwale Ariwodola
fa78c80592 fix ClaimListAdapter and strings.xml 2020-06-25 20:42:55 +01:00
Akinwale Ariwodola
07630ca97b fix: crash bugs 2020-06-25 20:23:24 +01:00
Akinwale Ariwodola
a2b6d4e570 bumpversion 0.15.13 --> 0.15.14 2020-06-25 20:16:38 +01:00
Akinwale Ariwodola
9d6b3ddf81
combined tip / support dialog and signed supports (#944)
* combined tip / support dialog and signed supports
* update button label when amount is updated
2020-06-25 20:15:58 +01:00
Akinwale Ariwodola
0a2769859a fix issue with featured search result crash 2020-06-24 10:45:29 +01:00
Akinwale Ariwodola
c4a1ed801a make FontAwesome strings non-translatable 2020-06-19 07:18:19 +01:00
Akinwale Ariwodola
453394bf1b update README 2020-06-19 06:57:48 +01:00
Akinwale Ariwodola
4b7dfba5a1 sdk 0.76.0 2020-06-15 19:43:41 +01:00
Akinwale Ariwodola
e699fcf0b3 remove broken Bengali strings.xml 2020-06-15 19:29:26 +01:00
Akinwale Ariwodola
7e80f707e0 bumpversion 0.15.12 --> 0.15.13 2020-06-15 19:17:18 +01:00
Akinwale Ariwodola
467f037170 sFix default sort on channel page. Add Bengali and Javanese translations. 2020-06-15 19:11:20 +01:00
Akinwale Ariwodola
89d5b40e5f only allow sending to addresses that begin with b 2020-06-15 15:18:42 +01:00
Akinwale Ariwodola
34fba010e1 use lbryplayer.xyz streaming URLs for signed in users 2020-06-15 15:07:17 +01:00
Akinwale Ariwodola
d7396bb044
add MoonPay 'Buy LBC' button (#937)
* add MoonPay 'Buy LBC' button
* tweak button style
* tweak MoonPay parameters
2020-06-13 16:14:06 +01:00
joaquimbrugues
fda0817ad1
Add Catalan language (#928)
* Add Catalan language
* Add 'catalan' string to strings.xml
* Attempt to fix conflicting changes
2020-06-11 19:41:53 +01:00
Akinwale Ariwodola
895ca75506
Merge pull request #935 from lbryio/edit-publish-channel
auto select corresponding channel when editing a publish
2020-06-11 19:38:24 +01:00
Akinwale Ariwodola
a04448ebe8 auto select corresponding channel when editing a publish 2020-06-11 19:31:06 +01:00
Akinwale Ariwodola
26ccbf2709 reduce comment cost from 2 to 1 2020-06-10 09:54:07 +01:00
Akinwale Ariwodola
7d6c11a88c Remove lbrysdk AARs. Add bintray dependencies. 2020-06-09 22:27:42 +01:00
Akinwale Ariwodola
8742913c33 SDK 0.75.0. Update translations. 2020-06-05 15:31:13 +01:00
Akinwale Ariwodola
8e6e0f0099 fix player notification pending intent 2020-06-05 09:42:45 +01:00
Akinwale Ariwodola
a4585de807 reset playback notification large icon when content changes 2020-06-05 09:40:22 +01:00
Akinwale Ariwodola
6c24749ad5 tweak player view resume 2020-06-05 09:34:45 +01:00
Akinwale Ariwodola
c73509a4ca set large icon for playback notification 2020-06-05 09:30:54 +01:00
Akinwale Ariwodola
673b85b2aa bumpversion 0.15.11 --> 0.15.12 2020-06-05 09:07:35 +01:00
Akinwale Ariwodola
fe6e60cd67 implement background playback with control notification 2020-06-05 09:04:24 +01:00
Akinwale Ariwodola
8bcee90d68 Add background playback setting. Fix crash bugs. 2020-06-05 08:25:08 +01:00
Akinwale Ariwodola
084407e129 Update translations. Add Catalan and Serbian strings. 2020-06-05 07:50:52 +01:00
Akinwale Ariwodola
4a3db5ae20 enforce minimum spend / deposits 2020-06-05 07:40:44 +01:00
Akinwale Ariwodola
b36813ebe6 fix nsfw search flag 2020-06-05 07:23:56 +01:00
Akinwale Ariwodola
cc1ddfa16e use only txids for unlocking tips 2020-06-05 07:17:56 +01:00
Akinwale Ariwodola
5f1775d478 fix possible null item in channel id list 2020-06-05 07:04:54 +01:00
Akinwale Ariwodola
288e35dd64 fix margins 2020-06-02 09:40:17 +01:00
Akinwale Ariwodola
d4af28d1c5 display publisher title and thumbnail on file page 2020-06-02 09:34:59 +01:00
Akinwale Ariwodola
37cf85a5f6 bumpversion 0.15.10 --> 0.15.11 2020-06-02 09:05:04 +01:00
Akinwale Ariwodola
c2cbd76f49 change comment confirmation title 2020-06-02 09:03:56 +01:00
Akinwale Ariwodola
3d874435c3 change BigDecimal initialisation for comment cost 2020-06-02 08:57:12 +01:00
Akinwale Ariwodola
9ed8864f23 More crash fixes. Change comment cost to 2 LBC. 2020-06-02 08:55:34 +01:00
Akinwale Ariwodola
61263dd59d bumpversion 0.15.9 --> 0.15.10 2020-06-01 08:56:38 +01:00
Akinwale Ariwodola
2707e135c6 comment creation event 2020-06-01 08:53:58 +01:00
Akinwale Ariwodola
f9de0d7937 do not prompt again for content that is already purchased 2020-06-01 08:48:17 +01:00
Akinwale Ariwodola
f03d58d648 tweak PIP player for certain aspect ratios 2020-06-01 08:33:07 +01:00
Akinwale Ariwodola
f49a3570e9 fix file view display for cached reposts 2020-05-31 21:22:37 +01:00
Akinwale Ariwodola
1d97f9008d
implement commenting with tips (#922)
* implement commenting with tips
* add comments to channel page
2020-05-31 21:06:03 +01:00
Akinwale Ariwodola
e85ca9114c
Merge pull request #921 from lbryio/crash-fixes
fixes for Play Store crash reports
2020-05-31 16:20:59 +01:00
Akinwale Ariwodola
85ed2da518 fixes for Play Store crash reports 2020-05-31 16:19:33 +01:00
Clayton Hickey
dcc91f75cc
Added ability to read comments on claims (#920)
* Added ability to read comments on claims
* Added simple handling for pagination in CommentListTask
2020-05-30 00:47:50 +01:00
Akinwale Ariwodola
a40c160ac6 fix selection mode for reposts on Publishes page 2020-05-28 03:45:56 +01:00
Akinwale Ariwodola
e6861c8436 set timestamp when building claim object from edited claim 2020-05-28 03:25:42 +01:00
Akinwale Ariwodola
e6a5d97fb8 bumpversion 0.15.8 --> 0.15.9 2020-05-28 03:23:45 +01:00
Akinwale Ariwodola
ddeb209d51 fix publish release time and crash bug on publish form 2020-05-28 03:22:20 +01:00
Akinwale Ariwodola
3283b3a607 fix channel links on history page 2020-05-27 23:22:08 +01:00
Akinwale Ariwodola
83a41ca6ce auto-rotate video to sensor landscape orientation 2020-05-27 23:16:25 +01:00
Akinwale Ariwodola
6c63eb7d66 better handling of permanent storage permission denial on file view page 2020-05-27 23:11:09 +01:00
Akinwale Ariwodola
7cf9de8c2a fix typo 2020-05-27 23:01:00 +01:00
Akinwale Ariwodola
a859682954 fix permission request handling for permanently denied permissions 2020-05-27 22:56:07 +01:00
Akinwale Ariwodola
c844c4f896 fix PIP mode display for Android version < 10 2020-05-27 11:20:51 +01:00
Akinwale Ariwodola
a49cfe91da remove debug logs 2020-05-27 10:58:28 +01:00
Akinwale Ariwodola
2bacba2c87 properly close all activities when service is stopped 2020-05-27 10:52:25 +01:00
Akinwale Ariwodola
e473981063 fix more crash bugs reported in Play Store 2020-05-27 08:25:10 +01:00
Akinwale Ariwodola
34ea4f216c add Dutch and Hindi strings 2020-05-27 02:06:41 +01:00
Akinwale Ariwodola
52e34b3027 don't hide play / pause button while buffering 2020-05-27 01:15:02 +01:00
Akinwale Ariwodola
c6a6ea0445 reset click listener onStop 2020-05-27 01:11:18 +01:00
Akinwale Ariwodola
59ae6340e1 disable gif animation in thumbnails 2020-05-27 00:56:07 +01:00
Akinwale Ariwodola
cbf2aa2311 bumpversion 0.15.6 --> 0.15.7 2020-05-26 16:26:15 +01:00
Akinwale Ariwodola
94b0c7bc01 Fix additional Play Store crash bugs. 2020-05-26 16:25:37 +01:00
Akinwale Ariwodola
4c50ffac19
Merge pull request #898 from lbryio/user-bugs
fix reported bugs from 0.15.3
2020-05-26 16:09:51 +01:00
Akinwale Ariwodola
551e736651 fix reported bugs from 0.15.3 2020-05-26 16:09:14 +01:00
Akinwale Ariwodola
0380e35966 bumpversion 0.15.5 --> 0.15.6 2020-05-26 10:58:47 +01:00
Akinwale Ariwodola
5de0906fc1 make splash view clickable to prevent clicking views underneath 2020-05-26 10:57:12 +01:00
Akinwale Ariwodola
c2f17b6230 Add Italian strings. Save and resume from last media playback position. 2020-05-26 10:38:16 +01:00
Akinwale Ariwodola
6a3bbe6c0d bumpversion 0.15.4 --> 0.15.5 2020-05-25 20:10:34 +01:00
Akinwale Ariwodola
82307c9f98 Increase timeout for remote user requests. Wrap long pre lines in markdown display. 2020-05-25 20:08:24 +01:00
Akinwale Ariwodola
d2ec0e2aa1 Display reposts on following page. Update Spanish strings. 2020-05-25 15:15:48 +01:00
Akinwale Ariwodola
3951b1080d different new publish from publish update 2020-05-25 12:14:12 +01:00
Akinwale Ariwodola
ef7caeeead Fix subsequent web view display. Add Estonian and Russian strings. 2020-05-25 11:33:12 +01:00
Akinwale Ariwodola
7ef8e2029e Tweak transaction pending text display 2020-05-25 06:03:46 +01:00
Akinwale Ariwodola
aaec6ac6a3 wait for uploads to finish before saving channel form 2020-05-25 06:00:34 +01:00
Akinwale Ariwodola
731960da7c bumpversion 0.15.3 --> 0.15.4 2020-05-25 05:50:02 +01:00
Akinwale Ariwodola
ac282d2667 log cleanup 2020-05-25 05:48:26 +01:00
Akinwale Ariwodola
7a79601cfc don't enable fullscreen mode when entering PIP mode 2020-05-25 05:47:14 +01:00
Akinwale Ariwodola
d1167e4d2b Disable selection mode for purchases. Check additional cases for displaying unsupported content message. 2020-05-25 05:38:25 +01:00
Akinwale Ariwodola
613634adcf get transaction id for first_publish and new_channel rewards 2020-05-25 00:32:21 +01:00
Akinwale Ariwodola
acbe33c66d Limit gallery items to 150. Download filtering. Open rewards page from Invites reward driver card. 2020-05-25 00:04:40 +01:00
Akinwale Ariwodola
053ebbd70b dDisplay better error message if authentication fails on first run. Change lineHeight to lineSpacingMultiplier. 2020-05-24 21:27:49 +01:00
Akinwale Ariwodola
f5dc4fa4e7 Remove new intent debug logs. Update packages in build.gradle 2020-05-24 20:48:01 +01:00
Akinwale Ariwodola
343270b757 Show auto-claim rewards message at activity level. Add German strings. 2020-05-24 20:34:19 +01:00
Akinwale Ariwodola
65f5626aba More tweaks to PIP mode restore. 2020-05-24 20:29:11 +01:00
Akinwale Ariwodola
a051a73c2b Tweak PIP restore. Fix Library wunderbar value. 2020-05-24 18:54:33 +01:00
Akinwale Ariwodola
6840997793 Add 'subscription' to special URLs map. Fix PIP restore for onNewIntent. 2020-05-24 18:46:47 +01:00
Akinwale Ariwodola
2c98ed2d8d Always display rewards driver on Invites page. Rewards drivers minimum display amounts. 2020-05-24 18:37:05 +01:00
Akinwale Ariwodola
6a083c4152 Update Afrikaans strings. Add Vietnamese strings. 2020-05-24 18:18:39 +01:00
Akinwale Ariwodola
b60b5d16c3 fix crash bugs reported in Play Store 2020-05-24 10:11:31 +01:00
Akinwale Ariwodola
52cfe8dc12 add blocking param to txo_spend 2020-05-24 05:02:55 +01:00
Akinwale Ariwodola
737afca031 fix onNewIntent logging 2020-05-24 04:50:13 +01:00
Akinwale Ariwodola
456f41d28d Prevent image click through. Add Romanian strings. 2020-05-24 04:43:39 +01:00
Akinwale Ariwodola
238f59ad71 display correct label and amount for tip unlocks in tx history 2020-05-24 00:08:14 +01:00
Akinwale Ariwodola
cf6d567193 unify button font 2020-05-23 23:47:32 +01:00
Akinwale Ariwodola
20f8f3852d
Merge pull request #888 from lbryio/unlock-tips
add option to unlock all tips
2020-05-23 23:39:16 +01:00
Akinwale Ariwodola
4bb9f5cb43 add option to unlock all tips 2020-05-23 23:37:57 +01:00
Akinwale Ariwodola
540a841255 Fix pip restore and fullscreen quirks. French and Portuguese strings added. 2020-05-23 19:42:30 +01:00
Akinwale Ariwodola
3544637405 Fix reward code claiming. Remove broken French strings file. 2020-05-23 17:48:58 +01:00
Akinwale Ariwodola
651448dfa1
Merge pull request #886 from lbryio/reward-drivers
Add reward drivers. French, Indonesian, Malay and Turkish strings.
2020-05-23 17:46:16 +01:00
306 changed files with 24219 additions and 3665 deletions

1
.github/workflows/deploy.yml vendored Normal file
View file

@ -0,0 +1 @@

2
.gitignore vendored
View file

@ -60,8 +60,10 @@ buck-out/
# Other Files # Other Files
app/google-services.json app/google-services.json
app/twitter.properties
*.log *.log
.vagrant .vagrant
*.hprof *.hprof
app/build app/build
bin bin
app/debuglib

View file

@ -8,10 +8,13 @@ build apk:
stage: build stage: build
image: lbry/android-base:platform-28 image: lbry/android-base:platform-28
before_script: before_script:
- apt-get -y update && apt-get -y install build-essential ca-certificates curl git gpg-agent openjdk-8-jdk software-properties-common wget zipalign - echo "$PGP_PRIVATE_KEY" | gpg --batch --import
- echo 'deb https://gitsecret.jfrog.io/artifactory/git-secret-deb git-secret main' >> /etc/apt/sources.list
- wget -qO - 'https://gitsecret.jfrog.io/artifactory/api/gpg/key/public' | apt-key add -
- apt-get -y update && apt-get -y install build-essential ca-certificates curl git gpg-agent openjdk-8-jdk software-properties-common wget zipalign git-secret
- git secret reveal
- chmod u+x $CI_PROJECT_DIR/gradlew - chmod u+x $CI_PROJECT_DIR/gradlew
- export ANDROID_SDK_ROOT=~/.buildozer/android/platform/android-sdk-23 - export BUILD_VERSION=$($CI_PROJECT_DIR/gradlew -p $CI_PROJECT_DIR -q printVersionName --console=plain | tail -1)
- export BUILD_VERSION=$($CI_PROJECT_DIR/gradlew -q printVersionName --console=plain | tail -1)
artifacts: artifacts:
paths: paths:
- bin/browser-*-release__arm.apk - bin/browser-*-release__arm.apk
@ -19,12 +22,7 @@ build apk:
expire_in: 1 week expire_in: 1 week
script: script:
- export PATH=/usr/bin:$PATH - export PATH=/usr/bin:$PATH
- echo "$PGP_PRIVATE_KEY" | gpg --batch --import - export ANDROID_SDK_ROOT=~/.buildozer/android/platform/android-sdk-23
- echo "deb https://dl.bintray.com/sobolevn/deb git-secret main" | tee -a /etc/apt/sources.list
- wget -O - https://api.bintray.com/users/sobolevn/keys/gpg/public.key | apt-key add -
- apt-get -y update && apt-get -y install git-secret
- git secret reveal
- yarn
- chmod u+x ./release.sh - chmod u+x ./release.sh
- ./release.sh - ./release.sh
- cp bin/browser-$BUILD_VERSION-release__arm.apk /dev/null - cp bin/browser-$BUILD_VERSION-release__arm.apk /dev/null
@ -36,10 +34,15 @@ deploy build.lbry.io:
dependencies: dependencies:
- build apk - build apk
before_script: before_script:
- apt-get -y update && apt-get -y install openjdk-8-jdk - apt-get -y update && apt-get -y install apt-transport-https
- echo "$PGP_PRIVATE_KEY" | gpg --batch --import
- echo 'deb https://gitsecret.jfrog.io/artifactory/git-secret-deb git-secret main' >> /etc/apt/sources.list
- wget -qO - 'https://gitsecret.jfrog.io/artifactory/api/gpg/key/public' | apt-key add -
- apt-get -y update && apt-get -y install openjdk-8-jdk git git-secret
- pip install awscli - pip install awscli
- chmod u+x $CI_PROJECT_DIR/gradlew - chmod u+x $CI_PROJECT_DIR/gradlew
- export BUILD_VERSION=$($CI_PROJECT_DIR/gradlew -q printVersionName --console=plain | tail -1) - git secret reveal
- export BUILD_VERSION=$($CI_PROJECT_DIR/gradlew -p $CI_PROJECT_DIR -q printVersionName --console=plain | tail -1)
- export BUILD_APK_FILENAME__32=browser-$BUILD_VERSION-release__arm.apk - export BUILD_APK_FILENAME__32=browser-$BUILD_VERSION-release__arm.apk
- export BUILD_APK_FILENAME__64=browser-$BUILD_VERSION-release__arm64.apk - export BUILD_APK_FILENAME__64=browser-$BUILD_VERSION-release__arm64.apk
script: script:
@ -55,10 +58,15 @@ release apk:
dependencies: dependencies:
- build apk - build apk
before_script: before_script:
- apt-get -y update && apt-get -y install openjdk-8-jdk - apt-get -y update && apt-get -y install apt-transport-https
- echo "$PGP_PRIVATE_KEY" | gpg --batch --import
- echo 'deb https://gitsecret.jfrog.io/artifactory/git-secret-deb git-secret main' >> /etc/apt/sources.list
- wget -qO - 'https://gitsecret.jfrog.io/artifactory/api/gpg/key/public' | apt-key add -
- apt-get -y update && apt-get -y install openjdk-8-jdk git git-secret
- pip install awscli githubrelease - pip install awscli githubrelease
- git secret reveal
- chmod u+x $CI_PROJECT_DIR/gradlew - chmod u+x $CI_PROJECT_DIR/gradlew
- export BUILD_VERSION=$($CI_PROJECT_DIR/gradlew -q printVersionName --console=plain | tail -1) - export BUILD_VERSION=$($CI_PROJECT_DIR/gradlew -p $CI_PROJECT_DIR -q printVersionName --console=plain | tail -1)
- export BUILD_APK_FILENAME__32=browser-$BUILD_VERSION-release__arm.apk - export BUILD_APK_FILENAME__32=browser-$BUILD_VERSION-release__arm.apk
- export BUILD_APK_FILENAME__64=browser-$BUILD_VERSION-release__arm64.apk - export BUILD_APK_FILENAME__64=browser-$BUILD_VERSION-release__arm64.apk
script: script:

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,2 +1,3 @@
lbry-android.keystore:0d958c531870694624cc877ea98ca1c583485f8ebbb3a5acca58b1930c190d65 lbry-android.keystore:0d958c531870694624cc877ea98ca1c583485f8ebbb3a5acca58b1930c190d65
app/google-services.json:896a0bee8294a36d061f10fa926129d8a780528b34d0a2f03113400c4246d67c app/google-services.json:896a0bee8294a36d061f10fa926129d8a780528b34d0a2f03113400c4246d67c
app/twitter.properties:01212d70712f2041efb5c814bf30ecbf6f72e1ca5179c7647c4f8cbd995dd033

View file

@ -2,7 +2,7 @@
[![pipeline status](https://ci.lbry.tech/lbry/lbry-android/badges/master/pipeline.svg)](https://ci.lbry.tech/lbry/lbry-android/commits/master) [![pipeline status](https://ci.lbry.tech/lbry/lbry-android/badges/master/pipeline.svg)](https://ci.lbry.tech/lbry/lbry-android/commits/master)
[![GitHub license](https://img.shields.io/github/license/lbryio/lbry-android)](https://github.com/lbryio/lbry-android/blob/master/LICENSE) [![GitHub license](https://img.shields.io/github/license/lbryio/lbry-android)](https://github.com/lbryio/lbry-android/blob/master/LICENSE)
An Android browser and wallet for the [LBRY](https://lbry.com) network. This app bundles [LBRY SDK](https://github.com/lbryio/lbry) as a background service with a UI layer built with React Native. An Android browser and wallet for the [LBRY](https://lbry.com) network.
<img src="https://spee.ch/@lbry:3f/android-08-homepage.gif" alt="LBRY Android GIF" width="384px" /> <img src="https://spee.ch/@lbry:3f/android-08-homepage.gif" alt="LBRY Android GIF" width="384px" />
@ -18,7 +18,19 @@ The minimum supported Android version is 5.0 Lollipop. There are two ways to ins
The app can be launched by opening **LBRY** from the device's app drawer or via the shortcut on the home screen if that was created upon installation. The app can be launched by opening **LBRY** from the device's app drawer or via the shortcut on the home screen if that was created upon installation.
## Running from Source ## Running from Source
Please refer to https://github.com/lbryio/lbry-react-native/blob/master/README.md Clone the repository and open the project in Android Studio. Android Studio will automatically run the initial build process.
Create file 'twitter.properties' in app/ folder with the following content:
```
twitterConsumerKey=XXXXXX
twitterConsumerSecret=XXXXXX
```
Copy the file 'google-services.sample.json' to 'google-services.json' in the app/ folder.
Click the Sync button and when process finishes, the Run button to launch the app on your simulator or connected debugging device after the build process is complete.
## Contributing ## Contributing
Contributions to this project are welcome, encouraged, and compensated. For more details, see https://lbry.io/faq/contributing Contributions to this project are welcome, encouraged, and compensated. For more details, see https://lbry.io/faq/contributing
@ -27,7 +39,7 @@ Contributions to this project are welcome, encouraged, and compensated. For more
This project is MIT licensed. For the full license, see [LICENSE](LICENSE). This project is MIT licensed. For the full license, see [LICENSE](LICENSE).
## Security ## Security
We take security seriously. Please contact security@lbry.com regarding any security issues. Our PGP key is [here](https://keybase.io/lbry/key.asc) if you need it. We take security seriously. Please contact security@lbry.com regarding any security issues. Our PGP key is [here](https://lbry.com/faq/pgp-key) if you need it.
## Contact ## Contact
The primary contact for this project is [@akinwale](https://github.com/akinwale) (akinwale@lbry.com) The primary contact for this project is [@akinwale](https://github.com/akinwale) (akinwale@lbry.com)

View file

@ -1,8 +1,10 @@
import com.google.gms.googleservices.GoogleServicesPlugin
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion 29 compileSdkVersion 29
buildToolsVersion "29.0.1" buildToolsVersion "29.0.2"
flavorDimensions "default" flavorDimensions "default"
compileOptions { compileOptions {
@ -14,12 +16,16 @@ android {
applicationId "io.lbry.browser" applicationId "io.lbry.browser"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 29 targetSdkVersion 29
versionCode 1503 versionCode 1701
versionName "0.15.3" versionName "0.17.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'lib/x86_64/darwin/libscrypt.dylib'
}
productFlavors { productFlavors {
__32bit { __32bit {
versionCode android.defaultConfig.versionCode * 10 + 1 versionCode android.defaultConfig.versionCode * 10 + 1
@ -36,7 +42,17 @@ android {
} }
buildTypes { buildTypes {
debug {
Properties twitterProps = new Properties()
twitterProps.load(project.file('twitter.properties').newDataInputStream())
resValue "string", "TWITTER_CONSUMER_KEY", "\"${twitterProps.getProperty("twitterConsumerKey")}\""
resValue "string", "TWITTER_CONSUMER_SECRET", "\"${twitterProps.getProperty("twitterConsumerSecret")}\""
}
release { release {
Properties twitterProps = new Properties()
twitterProps.load(project.file('twitter.properties').newDataInputStream())
resValue "string", "TWITTER_CONSUMER_KEY", "\"${twitterProps.getProperty("twitterConsumerKey")}\""
resValue "string", "TWITTER_CONSUMER_SECRET", "\"${twitterProps.getProperty("twitterConsumerSecret")}\""
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
@ -49,36 +65,49 @@ task printVersionName {
} }
} }
configurations {
all {
exclude module: 'httpclient'
exclude module: 'commons-logging'
}
}
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.2.0-rc01' implementation 'androidx.appcompat:appcompat:1.3.0-alpha01'
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.google.android.material:material:1.2.0-alpha06' implementation 'com.google.android.material:material:1.3.0-alpha01'
implementation "androidx.cardview:cardview:1.0.0" implementation "androidx.cardview:cardview:1.0.0"
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.navigation:navigation-fragment:2.2.2' implementation 'androidx.navigation:navigation-fragment:2.3.1'
implementation 'androidx.navigation:navigation-ui:2.2.2' implementation 'androidx.navigation:navigation-ui:2.3.1'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0' implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
implementation 'androidx.preference:preference:1.1.1' implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.webkit:webkit:1.3.0-alpha02' implementation 'androidx.webkit:webkit:1.4.0-rc01'
implementation 'androidx.camera:camera-core:1.0.0-beta03'
implementation 'androidx.camera:camera-camera2:1.0.0-beta03' implementation 'androidx.camera:camera-camera2:1.0.0-beta03'
implementation 'androidx.camera:camera-lifecycle:1.0.0-beta03' implementation 'androidx.camera:camera-lifecycle:1.0.0-beta03'
implementation 'androidx.camera:camera-view:1.0.0-alpha10' implementation 'androidx.camera:camera-view:1.0.0-alpha10'
implementation 'androidx.browser:browser:1.2.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'com.github.bumptech.glide:glide:4.11.0' implementation 'com.github.bumptech.glide:glide:4.11.0'
implementation 'com.squareup.okhttp3:okhttp:4.4.1' implementation 'com.squareup.okhttp3:okhttp:4.4.1'
implementation 'com.google.firebase:firebase-analytics:17.4.0' implementation 'com.google.firebase:firebase-analytics:18.0.0'
implementation 'com.google.android.gms:play-services-base:17.2.1' implementation 'com.google.android.gms:play-services-base:17.5.0'
implementation 'com.google.firebase:firebase-messaging:20.1.6' implementation 'com.google.firebase:firebase-messaging:21.0.0'
implementation 'com.google.oauth-client:google-oauth-client:1.30.4'
implementation 'com.android.billingclient:billing:3.0.2'
implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.google.android.exoplayer:exoplayer-core:2.11.4' implementation 'com.google.android.exoplayer:exoplayer-core:2.12.2'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.11.4' implementation 'com.google.android.exoplayer:exoplayer-dash:2.12.2'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.11.4' implementation 'com.google.android.exoplayer:exoplayer-ui:2.12.2'
implementation 'com.google.android.exoplayer:extension-cast:2.11.4' implementation 'com.google.android.exoplayer:extension-cast:2.12.2'
implementation 'com.google.android.exoplayer:extension-mediasession:2.11.4' implementation 'com.google.android.exoplayer:extension-mediasession:2.12.2'
implementation 'com.google.android:flexbox:2.0.1' implementation 'com.google.android:flexbox:2.0.1'
@ -89,17 +118,27 @@ dependencies {
implementation 'com.arthenica:mobile-ffmpeg-full-gpl:4.3.1.LTS' implementation 'com.arthenica:mobile-ffmpeg-full-gpl:4.3.1.LTS'
implementation 'commons-codec:commons-codec:1.15'
implementation 'org.bitcoinj:bitcoinj-tools:0.14.7'
implementation 'org.java-websocket:Java-WebSocket:1.5.1'
implementation ('com.journeyapps:zxing-android-embedded:4.1.0') { transitive = false }
implementation 'com.google.zxing:core:3.3.0'
compileOnly 'org.projectlombok:lombok:1.18.10' compileOnly 'org.projectlombok:lombok:1.18.10'
annotationProcessor 'org.projectlombok:lombok:1.18.10' annotationProcessor 'org.projectlombok:lombok:1.18.10'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' androidTestImplementation 'androidx.test:rules:1.3.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
__32bitImplementation files('libs/lbrysdk-0.74.0-release__arm.aar') __32bitImplementation 'io.lbry:lbrysdk32:0.102.0'
__64bitImplementation files('libs/lbrysdk-0.74.0-release__arm64.aar') __64bitImplementation 'io.lbry:lbrysdk64:0.102.0'
//__64bitImplementation(name: 'lbrysdk', ext: 'aar')
} }
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
com.google.gms.googleservices.GoogleServicesPlugin.config.disableVersionCheck = true GoogleServicesPlugin.config.disableVersionCheck = true

Binary file not shown.

View file

@ -0,0 +1,12 @@
package io.lbry.browser.utils;
import androidx.test.filters.SmallTest;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
@SmallTest
public class HelperTest {
}

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="io.lbry.browser" package="io.lbry.browser"
android:installLocation="auto"> android:installLocation="auto">
@ -10,6 +11,10 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
<application <application
android:allowBackup="true" android:allowBackup="true"
@ -50,12 +55,38 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="video/*" />
<data android:mimeType="image/*" />
<data android:mimeType="text/*" />
</intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="lbry" /> <data android:scheme="lbry" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" android:host="open.lbry.com"/>
<data android:scheme="https" android:host="lbry.tv" android:pathPattern="/..*/*" />
<data android:scheme="https" android:host="lbry.tv" android:pathPattern="/.*:.*" />
<data android:scheme="https" android:host="lbry.tv" android:pathPattern="/.*#.*" />
<data android:scheme="https" android:host="lbry.lat" android:pathPattern="/..*/*" />
<data android:scheme="https" android:host="lbry.lat" android:pathPattern="/.*:.*" />
<data android:scheme="https" android:host="lbry.lat" android:pathPattern="/.*#.*" />
<data android:scheme="https" android:host="lbry.fr" android:pathPattern="/..*/*" />
<data android:scheme="https" android:host="lbry.fr" android:pathPattern="/.*:.*" />
<data android:scheme="https" android:host="lbry.fr" android:pathPattern="/.*#.*" />
<data android:scheme="https" android:host="lbry.in" android:pathPattern="/..*/*" />
<data android:scheme="https" android:host="lbry.in" android:pathPattern="/.*:.*" />
<data android:scheme="https" android:host="lbry.in" android:pathPattern="/.*#.*" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity> </activity>
<activity <activity
@ -70,6 +101,11 @@
android:theme="@style/AppTheme.NoActionBarTranslucent" android:theme="@style/AppTheme.NoActionBarTranslucent"
android:windowSoftInputMode="adjustResize" /> android:windowSoftInputMode="adjustResize" />
<activity
android:name="com.journeyapps.barcodescanner.CaptureActivity"
android:screenOrientation="fullSensor"
tools:replace="screenOrientation" />
<service <service
android:name="io.lbry.browser.LbrynetMessagingService" android:name="io.lbry.browser.LbrynetMessagingService"
android:exported="false"> android:exported="false">

Binary file not shown.

View file

@ -10,20 +10,31 @@ import android.os.Bundle;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.text.HtmlCompat; import androidx.core.text.HtmlCompat;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import io.lbry.browser.exceptions.AuthTokenInvalidatedException;
import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.LbryAnalytics;
import io.lbry.browser.utils.Lbryio; import io.lbry.browser.utils.Lbryio;
import io.lbry.lbrysdk.LbrynetService;
import io.lbry.lbrysdk.ServiceHelper;
import io.lbry.lbrysdk.Utils;
public class FirstRunActivity extends AppCompatActivity { public class FirstRunActivity extends AppCompatActivity {
private BroadcastReceiver sdkReadyReceiver; private BroadcastReceiver sdkReceiver;
private BroadcastReceiver authReceiver; private BroadcastReceiver authReceiver;
@Override @Override
@ -43,21 +54,51 @@ public class FirstRunActivity extends AppCompatActivity {
}); });
registerAuthReceiver(); registerAuthReceiver();
if (!Lbry.SDK_READY) {
findViewById(R.id.welcome_wait_container).setVisibility(View.VISIBLE); findViewById(R.id.welcome_wait_container).setVisibility(View.VISIBLE);
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter();
filter.addAction(MainActivity.ACTION_SDK_READY); filter.addAction(MainActivity.ACTION_SDK_READY);
sdkReadyReceiver = new BroadcastReceiver() { filter.addAction(LbrynetService.ACTION_STOP_SERVICE);
sdkReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (MainActivity.ACTION_SDK_READY.equals(action)) {
// authenticate after we receive the sdk ready event // authenticate after we receive the sdk ready event
authenticate(); authenticate();
} else if (LbrynetService.ACTION_STOP_SERVICE.equals(action)) {
finish();
if (MainActivity.instance != null) {
MainActivity.instance.finish();
}
}
} }
}; };
registerReceiver(sdkReadyReceiver, filter); registerReceiver(sdkReceiver, filter);
} else {
authenticate(); CheckInstallIdTask task = new CheckInstallIdTask(this, new CheckInstallIdTask.InstallIdHandler() {
@Override
public void onInstallIdChecked(boolean result) {
// start the sdk from FirstRun
boolean serviceRunning = MainActivity.isServiceRunning(MainActivity.instance, LbrynetService.class);
if (!serviceRunning) {
Lbry.SDK_READY = false;
ServiceHelper.start(MainActivity.instance, "", LbrynetService.class, "lbrynetservice");
} }
if (result) {
// install_id generated and validated, authenticate now
authenticate();
return;
}
// we weren't able to generate the install_id ourselves, depend on the sdk for that
if (Lbry.SDK_READY) {
authenticate();
return;
}
}
});
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
public void onResume() { public void onResume() {
@ -97,7 +138,8 @@ public class FirstRunActivity extends AppCompatActivity {
} }
private void handleAuthenticationFailed() { private void handleAuthenticationFailed() {
Toast.makeText(this, "Authentication failed.", Toast.LENGTH_LONG).show(); findViewById(R.id.welcome_progress_bar).setVisibility(View.GONE);
((TextView) findViewById(R.id.welcome_wait_text)).setText(R.string.startup_failed);
} }
private void authenticate() { private void authenticate() {
@ -121,17 +163,89 @@ public class FirstRunActivity extends AppCompatActivity {
@Override @Override
protected void onDestroy() { protected void onDestroy() {
Helper.unregisterReceiver(authReceiver, this); Helper.unregisterReceiver(authReceiver, this);
Helper.unregisterReceiver(sdkReadyReceiver, this); Helper.unregisterReceiver(sdkReceiver, this);
super.onDestroy(); super.onDestroy();
} }
private void generateIdAndAuthenticate() {
}
private static class CheckInstallIdTask extends AsyncTask<Void, Void, Boolean> {
private final Context context;
private final InstallIdHandler handler;
public CheckInstallIdTask(Context context, InstallIdHandler handler) {
this.context = context;
this.handler = handler;
}
protected Boolean doInBackground(Void... params) {
// Load the installation id from the file system
String lbrynetDir = String.format("%s/%s", Utils.getAppInternalStorageDir(context), "lbrynet");
File dir = new File(lbrynetDir);
boolean dirExists = dir.isDirectory();
if (!dirExists) {
dirExists = dir.mkdirs();
}
if (!dirExists) {
return false;
}
String installIdPath = String.format("%s/install_id", lbrynetDir);
File file = new File(installIdPath);
String installId = null;
if (!file.exists()) {
// generate the install_id
installId = Lbry.generateId();
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(file));
writer.write(installId);
} catch (IOException ex) {
return false;
} finally {
Helper.closeCloseable(writer);
}
} else {
// read the installation id from the file
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(installIdPath)));
installId = reader.readLine();
} catch (IOException ex) {
return false;
} finally {
Helper.closeCloseable(reader);
}
}
if (!Helper.isNullOrEmpty(installId)) {
Lbry.INSTALLATION_ID = installId;
}
return !Helper.isNullOrEmpty(installId);
}
protected void onPostExecute(Boolean result) {
if (handler != null) {
handler.onInstallIdChecked(result);
}
}
public interface InstallIdHandler {
void onInstallIdChecked(boolean result);
}
}
private static class AuthenticateTask extends AsyncTask<Void, Void, Void> { private static class AuthenticateTask extends AsyncTask<Void, Void, Void> {
private Context context; private final Context context;
public AuthenticateTask(Context context) { public AuthenticateTask(Context context) {
this.context = context; this.context = context;
} }
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
try {
Lbryio.authenticate(context); Lbryio.authenticate(context);
} catch (AuthTokenInvalidatedException ex) {
// pass
}
return null; return null;
} }
} }

View file

@ -6,6 +6,7 @@ import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.media.RingtoneManager; import android.media.RingtoneManager;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
@ -20,19 +21,22 @@ import com.google.firebase.analytics.FirebaseAnalytics;
import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage; import com.google.firebase.messaging.RemoteMessage;
import io.lbry.browser.data.DatabaseHelper;
import io.lbry.browser.model.lbryinc.LbryNotification;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.LbryAnalytics;
import io.lbry.lbrysdk.LbrynetService;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class LbrynetMessagingService extends FirebaseMessagingService { public class LbrynetMessagingService extends FirebaseMessagingService {
public static final String ACTION_NOTIFICATION_RECEIVED = "io.lbry.browser.Broadcast.NotificationReceived";
private static final String TAG = "LbrynetMessagingService"; private static final String TAG = "LbrynetMessagingService";
private static final String NOTIFICATION_CHANNEL_ID = "io.lbry.browser.LBRY_ENGAGEMENT_CHANNEL"; private static final String NOTIFICATION_CHANNEL_ID = "io.lbry.browser.LBRY_ENGAGEMENT_CHANNEL";
private static final String TYPE_COMMENT = "comment";
private static final String TYPE_SUBSCRIPTION = "subscription"; private static final String TYPE_SUBSCRIPTION = "subscription";
private static final String TYPE_REWARD = "reward"; private static final String TYPE_REWARD = "reward";
private static final String TYPE_INTERESTS = "interests"; private static final String TYPE_INTERESTS = "interests";
@ -52,12 +56,9 @@ public class LbrynetMessagingService extends FirebaseMessagingService {
String title = payload.get("title"); String title = payload.get("title");
String body = payload.get("body"); String body = payload.get("body");
String name = payload.get("name"); // notification name String name = payload.get("name"); // notification name
String contentTitle = payload.get("content_title"); String hash = payload.get("hash"); // comment hash
String channelUrl = payload.get("channel_url");
//String publishTime = payload.get("publish_time");
String publishTime = null;
if (type != null && getEnabledTypes().indexOf(type) > -1 && body != null && body.trim().length() > 0) { if (type != null && getEnabledTypes().contains(type) && body != null && body.trim().length() > 0) {
// only log the receive event for valid notifications received // only log the receive event for valid notifications received
if (firebaseAnalytics != null) { if (firebaseAnalytics != null) {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
@ -65,14 +66,41 @@ public class LbrynetMessagingService extends FirebaseMessagingService {
firebaseAnalytics.logEvent(LbryAnalytics.EVENT_LBRY_NOTIFICATION_RECEIVE, bundle); firebaseAnalytics.logEvent(LbryAnalytics.EVENT_LBRY_NOTIFICATION_RECEIVE, bundle);
} }
sendNotification(title, body, type, url, name, contentTitle, channelUrl, publishTime); if (!Helper.isNullOrEmpty(hash)) {
url = String.format("%s?comment_hash=%s", url, hash);
}
sendNotification(title, body, type, url, name);
}
// persist the notification data
try {
DatabaseHelper helper = DatabaseHelper.getInstance();
SQLiteDatabase db = helper.getWritableDatabase();
LbryNotification lnotification = new LbryNotification();
lnotification.setTitle(title);
lnotification.setDescription(body);
lnotification.setTargetUrl(url);
lnotification.setTimestamp(new Date());
DatabaseHelper.createOrUpdateNotification(lnotification, db);
// send a broadcast
Intent intent = new Intent(ACTION_NOTIFICATION_RECEIVED);
intent.putExtra("title", title);
intent.putExtra("body", body);
intent.putExtra("url", url);
intent.putExtra("timestamp", lnotification.getTimestamp().getTime());
sendBroadcast(intent);
} catch (Exception ex) {
// don't fail if any error occurs while saving a notification
Log.e(TAG, "could not save notification", ex);
} }
} }
} }
@Override @Override
public void onNewToken(String token) { public void onNewToken(String token) {
Log.d(TAG, "Refreshed token: " + token); //Log.d(TAG, "Refreshed token: " + token);
// If you want to send messages to this application instance or // If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the // manage this apps subscriptions on the server side, send the
@ -97,8 +125,7 @@ public class LbrynetMessagingService extends FirebaseMessagingService {
* *
* @param messageBody FCM message body received. * @param messageBody FCM message body received.
*/ */
private void sendNotification(String title, String messageBody, String type, String url, String name, private void sendNotification(String title, String messageBody, String type, String url, String name) {
String contentTitle, String channelUrl, String publishTime) {
//Intent intent = new Intent(this, MainActivity.class); //Intent intent = new Intent(this, MainActivity.class);
//intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); //intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
if (url == null) { if (url == null) {
@ -143,6 +170,9 @@ public class LbrynetMessagingService extends FirebaseMessagingService {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
List<String> enabledTypes = new ArrayList<String>(); List<String> enabledTypes = new ArrayList<String>();
if (sp.getBoolean(MainActivity.PREFERENCE_KEY_NOTIFICATION_COMMENTS, true)) {
enabledTypes.add(TYPE_COMMENT);
}
if (sp.getBoolean(MainActivity.PREFERENCE_KEY_NOTIFICATION_SUBSCRIPTIONS, true)) { if (sp.getBoolean(MainActivity.PREFERENCE_KEY_NOTIFICATION_SUBSCRIPTIONS, true)) {
enabledTypes.add(TYPE_SUBSCRIPTION); enabledTypes.add(TYPE_SUBSCRIPTION);
} }

File diff suppressed because it is too large Load diff

View file

@ -1,25 +1,47 @@
package io.lbry.browser; package io.lbry.browser;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color; import android.graphics.Color;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.view.View; import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.widget.ViewPager2; import androidx.viewpager2.widget.ViewPager2;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import io.lbry.browser.adapter.VerificationPagerAdapter; import io.lbry.browser.adapter.VerificationPagerAdapter;
import io.lbry.browser.listener.SdkStatusListener;
import io.lbry.browser.listener.SignInListener; import io.lbry.browser.listener.SignInListener;
import io.lbry.browser.listener.WalletSyncListener; import io.lbry.browser.listener.WalletSyncListener;
import io.lbry.browser.model.lbryinc.RewardVerified;
import io.lbry.browser.model.lbryinc.User; import io.lbry.browser.model.lbryinc.User;
import io.lbry.browser.tasks.RewardVerifiedHandler;
import io.lbry.browser.tasks.lbryinc.FetchCurrentUserTask; import io.lbry.browser.tasks.lbryinc.FetchCurrentUserTask;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.LbryAnalytics;
import io.lbry.browser.utils.Lbryio; import io.lbry.browser.utils.Lbryio;
import io.lbry.lbrysdk.LbrynetService;
public class VerificationActivity extends FragmentActivity implements SignInListener, WalletSyncListener { public class VerificationActivity extends FragmentActivity implements SignInListener, WalletSyncListener {
@ -27,14 +49,66 @@ public class VerificationActivity extends FragmentActivity implements SignInList
public static final int VERIFICATION_FLOW_REWARDS = 2; public static final int VERIFICATION_FLOW_REWARDS = 2;
public static final int VERIFICATION_FLOW_WALLET = 3; public static final int VERIFICATION_FLOW_WALLET = 3;
private List<SdkStatusListener> sdkStatusListeners;
private BillingClient billingClient;
private BroadcastReceiver sdkReceiver;
private String email; private String email;
private boolean signedIn; private boolean signedIn;
private int flow; private int flow;
private final PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
@Override
public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> purchases) {
int responseCode = billingResult.getResponseCode();
if (responseCode == BillingClient.BillingResponseCode.OK && purchases != null)
{
for (Purchase purchase : purchases) {
if (MainActivity.SKU_SKIP.equalsIgnoreCase(purchase.getSku())) {
showLoading();
MainActivity.handleBillingPurchase(
purchase,
billingClient,
VerificationActivity.this, null, new RewardVerifiedHandler() {
@Override
public void onSuccess(RewardVerified rewardVerified) {
if (Lbryio.currentUser != null) {
Lbryio.currentUser.setRewardApproved(rewardVerified.isRewardApproved());
}
if (!rewardVerified.isRewardApproved()) {
// show pending purchase message (possible slow card tx)
Snackbar.make(findViewById(R.id.verification_pager), R.string.purchase_request_pending, Snackbar.LENGTH_LONG).show();
} else {
Snackbar.make(findViewById(R.id.verification_pager), R.string.reward_verification_successful, Snackbar.LENGTH_LONG).show();
}
setResult(RESULT_OK);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
finish();
}
}, 3000);
}
@Override
public void onError(Exception error) {
showFetchUserError(getString(R.string.purchase_request_failed_error));
hideLoading();
}
});
}
}
}
}
};
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
sdkStatusListeners = new ArrayList<>();
signedIn = Lbryio.isSignedIn(); signedIn = Lbryio.isSignedIn();
Intent intent = getIntent(); Intent intent = getIntent();
if (intent != null) { if (intent != null) {
@ -54,6 +128,32 @@ public class VerificationActivity extends FragmentActivity implements SignInList
return; return;
} }
IntentFilter filter = new IntentFilter();
filter.addAction(LbrynetService.ACTION_STOP_SERVICE);
filter.addAction(MainActivity.ACTION_SDK_READY);
sdkReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (MainActivity.ACTION_SDK_READY.equals(action)) {
for (SdkStatusListener listener : sdkStatusListeners) {
if (listener != null) {
listener.onSdkReady();
}
}
} else if (LbrynetService.ACTION_STOP_SERVICE.equals(action)) {
finish();
}
}
};
registerReceiver(sdkReceiver, filter);
billingClient = BillingClient.newBuilder(this)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.build();
establishBillingClientConnection();
setContentView(R.layout.activity_verification); setContentView(R.layout.activity_verification);
ViewPager2 viewPager = findViewById(R.id.verification_pager); ViewPager2 viewPager = findViewById(R.id.verification_pager);
viewPager.setUserInputEnabled(false); viewPager.setUserInputEnabled(false);
@ -70,6 +170,24 @@ public class VerificationActivity extends FragmentActivity implements SignInList
}); });
} }
private void establishBillingClientConnection() {
if (billingClient != null) {
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// no need to do anything here. purchases are always checked server-side
}
}
@Override
public void onBillingServiceDisconnected() {
establishBillingClientConnection();
}
});
}
}
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
LbryAnalytics.setCurrentScreen(this, "Verification", "Verification"); LbryAnalytics.setCurrentScreen(this, "Verification", "Verification");
@ -85,16 +203,19 @@ public class VerificationActivity extends FragmentActivity implements SignInList
flowHandled = true; flowHandled = true;
} else if (flow == VERIFICATION_FLOW_REWARDS) { } else if (flow == VERIFICATION_FLOW_REWARDS) {
User user = Lbryio.currentUser; User user = Lbryio.currentUser;
// disable phone verification for now
if (!user.isIdentityVerified()) { if (!user.isIdentityVerified()) {
// phone number verification required // phone number verification required
viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_PHONE, false); viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_PHONE, false);
flowHandled = true; flowHandled = true;
} else if (!user.isRewardApproved()) { } else {
if (!user.isRewardApproved()) {
// manual verification required // manual verification required
viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_MANUAL, false); viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_MANUAL, false);
flowHandled = true; flowHandled = true;
} }
} }
}
if (!flowHandled) { if (!flowHandled) {
// user has already been verified and or reward approved // user has already been verified and or reward approved
@ -105,6 +226,11 @@ public class VerificationActivity extends FragmentActivity implements SignInList
} }
} }
public void showPhoneVerification() {
ViewPager2 viewPager = findViewById(R.id.verification_pager);
viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_PHONE, false);
}
public void showLoading() { public void showLoading() {
findViewById(R.id.verification_loading_progress).setVisibility(View.VISIBLE); findViewById(R.id.verification_loading_progress).setVisibility(View.VISIBLE);
findViewById(R.id.verification_pager).setVisibility(View.INVISIBLE); findViewById(R.id.verification_pager).setVisibility(View.INVISIBLE);
@ -118,8 +244,12 @@ public class VerificationActivity extends FragmentActivity implements SignInList
@Override @Override
public void onBackPressed() { public void onBackPressed() {
// ignore back press ViewPager2 viewPager = findViewById(R.id.verification_pager);
return;
if (viewPager.getCurrentItem() != VerificationPagerAdapter.PAGE_VERIFICATION_MANUAL)
viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_MANUAL);
else
super.onBackPressed();
} }
public void onEmailAdded(String email) { public void onEmailAdded(String email) {
@ -148,7 +278,7 @@ public class VerificationActivity extends FragmentActivity implements SignInList
// only sign in required, don't do anything else // only sign in required, don't do anything else
showLoading(); showLoading();
FetchCurrentUserTask task = new FetchCurrentUserTask(new FetchCurrentUserTask.FetchUserTaskHandler() { FetchCurrentUserTask task = new FetchCurrentUserTask(this, new FetchCurrentUserTask.FetchUserTaskHandler() {
@Override @Override
public void onSuccess(User user) { public void onSuccess(User user) {
Lbryio.currentUser = user; Lbryio.currentUser = user;
@ -158,7 +288,7 @@ public class VerificationActivity extends FragmentActivity implements SignInList
@Override @Override
public void onError(Exception error) { public void onError(Exception error) {
showFetchUserError(error.getMessage()); showFetchUserError(error != null ? error.getMessage() : getString(R.string.fetch_current_user_error));
hideLoading(); hideLoading();
} }
}); });
@ -166,7 +296,7 @@ public class VerificationActivity extends FragmentActivity implements SignInList
} else { } else {
// change pager view depending on flow // change pager view depending on flow
showLoading(); showLoading();
FetchCurrentUserTask task = new FetchCurrentUserTask(new FetchCurrentUserTask.FetchUserTaskHandler() { FetchCurrentUserTask task = new FetchCurrentUserTask(this, new FetchCurrentUserTask.FetchUserTaskHandler() {
@Override @Override
public void onSuccess(User user) { public void onSuccess(User user) {
hideLoading(); hideLoading();
@ -179,7 +309,8 @@ public class VerificationActivity extends FragmentActivity implements SignInList
if (!user.isIdentityVerified()) { if (!user.isIdentityVerified()) {
// phone number verification required // phone number verification required
viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_PHONE, false); viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_PHONE, false);
} else if (!user.isRewardApproved()) { } else {
if (!user.isRewardApproved()) {
// manual verification required // manual verification required
viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_MANUAL, false); viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_MANUAL, false);
} else { } else {
@ -187,6 +318,7 @@ public class VerificationActivity extends FragmentActivity implements SignInList
setResult(RESULT_OK); setResult(RESULT_OK);
finish(); finish();
} }
}
} else if (flow == VERIFICATION_FLOW_WALLET) { } else if (flow == VERIFICATION_FLOW_WALLET) {
// for wallet sync, if password unlock is required, show password entry page // for wallet sync, if password unlock is required, show password entry page
viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_WALLET, false); viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_WALLET, false);
@ -194,7 +326,7 @@ public class VerificationActivity extends FragmentActivity implements SignInList
} }
@Override @Override
public void onError(Exception error) { public void onError(Exception error) {
showFetchUserError(error.getMessage()); showFetchUserError(error != null ? error.getMessage() : getString(R.string.fetch_current_user_error));
hideLoading(); hideLoading();
} }
}); });
@ -210,7 +342,7 @@ public class VerificationActivity extends FragmentActivity implements SignInList
@Override @Override
public void onPhoneVerified() { public void onPhoneVerified() {
showLoading(); showLoading();
FetchCurrentUserTask task = new FetchCurrentUserTask(new FetchCurrentUserTask.FetchUserTaskHandler() { FetchCurrentUserTask task = new FetchCurrentUserTask(this, new FetchCurrentUserTask.FetchUserTaskHandler() {
@Override @Override
public void onSuccess(User user) { public void onSuccess(User user) {
Lbryio.currentUser = user; Lbryio.currentUser = user;
@ -223,6 +355,7 @@ public class VerificationActivity extends FragmentActivity implements SignInList
return; return;
} }
findViewById(R.id.verification_close_button).setVisibility(View.VISIBLE);
// show manual verification page if the user is still not reward approved // show manual verification page if the user is still not reward approved
ViewPager2 viewPager = findViewById(R.id.verification_pager); ViewPager2 viewPager = findViewById(R.id.verification_pager);
viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_MANUAL, false); viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_MANUAL, false);
@ -231,7 +364,7 @@ public class VerificationActivity extends FragmentActivity implements SignInList
@Override @Override
public void onError(Exception error) { public void onError(Exception error) {
showFetchUserError(error.getMessage()); showFetchUserError(error != null ? error.getMessage() : getString(R.string.fetch_current_user_error));
hideLoading(); hideLoading();
} }
}); });
@ -269,4 +402,68 @@ public class VerificationActivity extends FragmentActivity implements SignInList
public void onWalletSyncFailed(Exception error) { public void onWalletSyncFailed(Exception error) {
findViewById(R.id.verification_close_button).setVisibility(View.VISIBLE); findViewById(R.id.verification_close_button).setVisibility(View.VISIBLE);
} }
@Override
public void onSkipQueueAction() {
if (billingClient != null) {
List<String> skuList = new ArrayList<>();
skuList.add(MainActivity.SKU_SKIP);
SkuDetailsParams detailsParams = SkuDetailsParams.newBuilder().
setType(BillingClient.SkuType.INAPP).
setSkusList(skuList).build();
billingClient.querySkuDetailsAsync(detailsParams, new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(@NonNull BillingResult billingResult, @Nullable List<SkuDetails> list) {
if (list != null && list.size() > 0) {
// we only queried one product, so it should be the first item in the list
SkuDetails skuDetails = list.get(0);
// launch the billing flow for skip queue
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder().
setSkuDetails(skuDetails).build();
billingClient.launchBillingFlow(VerificationActivity.this, billingFlowParams);
}
}
});
}
}
@Override
public void onTwitterVerified() {
Snackbar.make(findViewById(R.id.verification_pager), R.string.reward_verification_successful, Snackbar.LENGTH_LONG).show();
setResult(RESULT_OK);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
finish();
}
}, 3000);
}
@Override
public void onManualProgress(boolean progress) {
if (progress) {
findViewById(R.id.verification_close_button).setVisibility(View.GONE);
} else {
findViewById(R.id.verification_close_button).setVisibility(View.VISIBLE);
}
}
@Override
public void onDestroy() {
Helper.unregisterReceiver(sdkReceiver, this);
super.onDestroy();
}
public void addSdkStatusListener(SdkStatusListener listener) {
if (!sdkStatusListeners.contains(listener)) {
sdkStatusListeners.add(listener);
}
}
public void removeSdkStatusListener(SdkStatusListener listener) {
sdkStatusListeners.remove(listener);
}
} }

View file

@ -23,7 +23,7 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
public class ChannelFilterListAdapter extends RecyclerView.Adapter<ChannelFilterListAdapter.ViewHolder> { public class ChannelFilterListAdapter extends RecyclerView.Adapter<ChannelFilterListAdapter.ViewHolder> {
private Context context; private final Context context;
private List<Claim> items; private List<Claim> items;
@Getter @Getter
@Setter @Setter
@ -42,12 +42,12 @@ public class ChannelFilterListAdapter extends RecyclerView.Adapter<ChannelFilter
} }
public static class ViewHolder extends RecyclerView.ViewHolder { public static class ViewHolder extends RecyclerView.ViewHolder {
protected View mediaContainer; protected final View mediaContainer;
protected View alphaContainer; protected final View alphaContainer;
protected View allView; protected final View allView;
protected ImageView thumbnailView; protected final ImageView thumbnailView;
protected TextView alphaView; protected final TextView alphaView;
protected TextView titleView; protected final TextView titleView;
public ViewHolder(View v) { public ViewHolder(View v) {
super(v); super(v);
mediaContainer = v.findViewById(R.id.channel_filter_media_container); mediaContainer = v.findViewById(R.id.channel_filter_media_container);
@ -95,13 +95,13 @@ public class ChannelFilterListAdapter extends RecyclerView.Adapter<ChannelFilter
vh.allView.setVisibility(claim.isPlaceholder() ? View.VISIBLE : View.GONE); vh.allView.setVisibility(claim.isPlaceholder() ? View.VISIBLE : View.GONE);
vh.titleView.setText(Helper.isNullOrEmpty(claim.getTitle()) ? claim.getName() : claim.getTitle()); vh.titleView.setText(Helper.isNullOrEmpty(claim.getTitle()) ? claim.getName() : claim.getTitle());
String thumbnailUrl = claim.getThumbnailUrl(); String thumbnailUrl = claim.getThumbnailUrl(vh.thumbnailView.getLayoutParams().width, vh.thumbnailView.getLayoutParams().height, 85);
if (!Helper.isNullOrEmpty(thumbnailUrl) && context != null) { if (!Helper.isNullOrEmpty(thumbnailUrl) && context != null) {
Glide.with(context.getApplicationContext()).load(thumbnailUrl).apply(RequestOptions.circleCropTransform()).into(vh.thumbnailView); Glide.with(context.getApplicationContext()).load(thumbnailUrl).apply(RequestOptions.circleCropTransform()).into(vh.thumbnailView);
} }
vh.alphaContainer.setVisibility(claim.isPlaceholder() || Helper.isNullOrEmpty(thumbnailUrl) ? View.VISIBLE : View.GONE); vh.alphaContainer.setVisibility(claim.isPlaceholder() || Helper.isNullOrEmpty(thumbnailUrl) ? View.VISIBLE : View.GONE);
vh.thumbnailView.setVisibility(claim.isPlaceholder() || Helper.isNullOrEmpty(thumbnailUrl) ? View.GONE : View.VISIBLE); vh.thumbnailView.setVisibility(claim.isPlaceholder() || Helper.isNullOrEmpty(thumbnailUrl) ? View.GONE : View.VISIBLE);
vh.alphaView.setText(claim.isPlaceholder() ? null : claim.getName() != null ? claim.getName().substring(1, 2) : ""); vh.alphaView.setText(claim.isPlaceholder() ? null : claim.getName() != null ? claim.getName().substring(1, 2).toUpperCase() : "");
int bgColor = Helper.generateRandomColorForValue(claim.getClaimId()); int bgColor = Helper.generateRandomColorForValue(claim.getClaimId());
Helper.setIconViewBackgroundColor(vh.alphaContainer, bgColor, claim.isPlaceholder(), context); Helper.setIconViewBackgroundColor(vh.alphaContainer, bgColor, claim.isPlaceholder(), context);

View file

@ -8,7 +8,6 @@ import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -22,7 +21,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import io.lbry.browser.MainActivity;
import io.lbry.browser.R; import io.lbry.browser.R;
import io.lbry.browser.listener.SelectionModeListener; import io.lbry.browser.listener.SelectionModeListener;
import io.lbry.browser.model.Claim; import io.lbry.browser.model.Claim;
@ -38,18 +36,18 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
private static final int VIEW_TYPE_CHANNEL = 2; private static final int VIEW_TYPE_CHANNEL = 2;
private static final int VIEW_TYPE_FEATURED = 3; // featured search result private static final int VIEW_TYPE_FEATURED = 3; // featured search result
private Map<String, Claim> quickClaimIdMap; private final Map<String, Claim> quickClaimIdMap;
private Map<String, Claim> quickClaimUrlMap; private final Map<String, Claim> quickClaimUrlMap;
private Map<String, Boolean> notFoundClaimIdMap; private final Map<String, Boolean> notFoundClaimIdMap;
private Map<String, Boolean> notFoundClaimUrlMap; private final Map<String, Boolean> notFoundClaimUrlMap;
@Setter @Setter
private boolean hideFee; private boolean hideFee;
@Setter @Setter
private boolean canEnterSelectionMode; private boolean canEnterSelectionMode;
private Context context; private final Context context;
private List<Claim> items; private List<Claim> items;
private List<Claim> selectedItems; private final List<Claim> selectedItems;
@Setter @Setter
private ClaimListItemListener listener; private ClaimListItemListener listener;
@Getter @Getter
@ -61,7 +59,13 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
public ClaimListAdapter(List<Claim> items, Context context) { public ClaimListAdapter(List<Claim> items, Context context) {
this.context = context; this.context = context;
this.items = new ArrayList<>(items); this.items = new ArrayList<>();
for (Claim item : items) {
if (item != null) {
this.items.add(item);
}
}
this.selectedItems = new ArrayList<>(); this.selectedItems = new ArrayList<>();
quickClaimIdMap = new HashMap<>(); quickClaimIdMap = new HashMap<>();
quickClaimUrlMap = new HashMap<>(); quickClaimUrlMap = new HashMap<>();
@ -140,7 +144,7 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
public void addItems(List<Claim> claims) { public void addItems(List<Claim> claims) {
for (Claim claim : claims) { for (Claim claim : claims) {
if (!items.contains(claim)) { if (claim != null && !items.contains(claim)) {
items.add(claim); items.add(claim);
} }
} }
@ -150,7 +154,12 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
notifyDataSetChanged(); notifyDataSetChanged();
} }
public void setItems(List<Claim> claims) { public void setItems(List<Claim> claims) {
items = new ArrayList<>(claims); items = new ArrayList<>();
for (Claim claim : claims) {
if (claim != null) {
items.add(claim);
}
}
notifyDataSetChanged(); notifyDataSetChanged();
} }
@ -166,23 +175,27 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
} }
public static class ViewHolder extends RecyclerView.ViewHolder { public static class ViewHolder extends RecyclerView.ViewHolder {
protected View feeContainer; protected final View feeContainer;
protected TextView feeView; protected final TextView feeView;
protected ImageView thumbnailView; protected final ImageView thumbnailView;
protected View noThumbnailView; protected final View noThumbnailView;
protected TextView alphaView; protected final TextView alphaView;
protected TextView vanityUrlView; protected final TextView vanityUrlView;
protected TextView durationView; protected final TextView durationView;
protected TextView titleView; protected final TextView titleView;
protected TextView publisherView; protected final TextView publisherView;
protected TextView publishTimeView; protected final TextView publishTimeView;
protected TextView pendingTextView; protected final TextView pendingTextView;
protected View repostInfoView; protected final View repostInfoView;
protected TextView repostChannelView; protected final TextView repostChannelView;
protected View selectedOverlayView; protected final View selectedOverlayView;
protected TextView fileSizeView; protected final TextView fileSizeView;
protected ProgressBar downloadProgressView; protected final ProgressBar downloadProgressView;
protected TextView deviceView; protected final TextView deviceView;
protected final View loadingImagePlaceholder;
protected final View loadingTextPlaceholder1;
protected final View loadingTextPlaceholder2;
public ViewHolder(View v) { public ViewHolder(View v) {
super(v); super(v);
feeContainer = v.findViewById(R.id.claim_fee_container); feeContainer = v.findViewById(R.id.claim_fee_container);
@ -202,6 +215,10 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
fileSizeView = v.findViewById(R.id.claim_file_size); fileSizeView = v.findViewById(R.id.claim_file_size);
downloadProgressView = v.findViewById(R.id.claim_download_progress); downloadProgressView = v.findViewById(R.id.claim_download_progress);
deviceView = v.findViewById(R.id.claim_view_device); deviceView = v.findViewById(R.id.claim_view_device);
loadingImagePlaceholder = v.findViewById(R.id.claim_thumbnail_placeholder);
loadingTextPlaceholder1 = v.findViewById(R.id.claim_text_loading_placeholder_1);
loadingTextPlaceholder2 = v.findViewById(R.id.claim_text_loading_placeholder_2);
} }
} }
@ -298,29 +315,27 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
return new ClaimListAdapter.ViewHolder(v); return new ClaimListAdapter.ViewHolder(v);
} }
public int getScaledValue(int value) {
return (int) (value * scale + 0.5f);
}
@Override @Override
public void onBindViewHolder(ClaimListAdapter.ViewHolder vh, int position) { public void onBindViewHolder(ClaimListAdapter.ViewHolder vh, int position) {
int type = getItemViewType(position); int type = getItemViewType(position);
int paddingTop = position == 0 ? 16 : 8; int paddingTop = position == 0 ? 16 : 8;
int paddingBottom = position == getItemCount() - 1 ? 16 : 8; int paddingBottom = position == getItemCount() - 1 ? 16 : 8;
int paddingTopScaled = getScaledValue(paddingTop); int paddingTopScaled = Helper.getScaledValue(paddingTop, scale);
int paddingBottomScaled = getScaledValue(paddingBottom); int paddingBottomScaled = Helper.getScaledValue(paddingBottom, scale);
vh.itemView.setPadding(vh.itemView.getPaddingLeft(), paddingTopScaled, vh.itemView.getPaddingRight(), paddingBottomScaled); vh.itemView.setPadding(vh.itemView.getPaddingStart(), paddingTopScaled, vh.itemView.getPaddingEnd(), paddingBottomScaled);
Claim original = items.get(position); Claim original = items.get(position);
boolean isRepost = Claim.TYPE_REPOST.equalsIgnoreCase(original.getValueType()); boolean isRepost = Claim.TYPE_REPOST.equalsIgnoreCase(original.getValueType());
final Claim item = Claim.TYPE_REPOST.equalsIgnoreCase(original.getValueType()) ? original.getRepostedClaim() : original; final Claim item = Claim.TYPE_REPOST.equalsIgnoreCase(original.getValueType()) ?
(original.getRepostedClaim() != null ? original.getRepostedClaim() : original): original;
Claim.GenericMetadata metadata = item.getValue(); Claim.GenericMetadata metadata = item.getValue();
Claim signingChannel = item.getSigningChannel(); Claim signingChannel = item.getSigningChannel();
Claim.StreamMetadata streamMetadata = null; Claim.StreamMetadata streamMetadata = null;
if (metadata instanceof Claim.StreamMetadata) { if (metadata instanceof Claim.StreamMetadata) {
streamMetadata = (Claim.StreamMetadata) metadata; streamMetadata = (Claim.StreamMetadata) metadata;
} }
String thumbnailUrl = item.getThumbnailUrl();
String thumbnailUrl = item.getThumbnailUrl(vh.thumbnailView.getLayoutParams().width, vh.thumbnailView.getLayoutParams().height, 85);
long publishTime = (streamMetadata != null && streamMetadata.getReleaseTime() > 0) ? streamMetadata.getReleaseTime() * 1000 : item.getTimestamp() * 1000; long publishTime = (streamMetadata != null && streamMetadata.getReleaseTime() > 0) ? streamMetadata.getReleaseTime() * 1000 : item.getTimestamp() * 1000;
int bgColor = Helper.generateRandomColorForValue(item.getClaimId()); int bgColor = Helper.generateRandomColorForValue(item.getClaimId());
if (bgColor == 0) { if (bgColor == 0) {
@ -328,14 +343,17 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
} }
boolean isPending = item.getConfirmations() == 0; boolean isPending = item.getConfirmations() == 0;
boolean isSelected = isClaimSelected(item); boolean isSelected = isClaimSelected(original);
vh.itemView.setSelected(isSelected); vh.itemView.setSelected(isSelected);
vh.selectedOverlayView.setVisibility(isSelected ? View.VISIBLE : View.GONE); vh.selectedOverlayView.setVisibility(isSelected ? View.VISIBLE : View.GONE);
vh.itemView.setOnClickListener(new View.OnClickListener() { vh.itemView.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
if (isPending) { if (isPending) {
Snackbar.make(vh.itemView, R.string.item_pending_blockchain, Snackbar.LENGTH_LONG).show(); Snackbar snackbar = Snackbar.make(vh.itemView, R.string.item_pending_blockchain, Snackbar.LENGTH_LONG);
TextView snackbarText = snackbar.getView().findViewById(com.google.android.material.R.id.snackbar_text);
snackbarText.setMaxLines(5);
snackbar.show();
return; return;
} }
@ -356,7 +374,10 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
} }
if (isPending) { if (isPending) {
Snackbar.make(vh.itemView, R.string.item_pending_blockchain, Snackbar.LENGTH_LONG).show(); Snackbar snackbar = Snackbar.make(vh.itemView, R.string.item_pending_blockchain, Snackbar.LENGTH_LONG);
TextView snackbarText = snackbar.getView().findViewById(com.google.android.material.R.id.snackbar_text);
snackbarText.setMaxLines(5);
snackbar.show();
return false; return false;
} }
@ -381,9 +402,9 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
}); });
vh.publishTimeView.setVisibility(!isPending ? View.VISIBLE : View.GONE); vh.publishTimeView.setVisibility(!isPending ? View.VISIBLE : View.GONE);
vh.pendingTextView.setVisibility(isPending ? View.VISIBLE : View.GONE); vh.pendingTextView.setVisibility(isPending && !item.isLoadingPlaceholder() ? View.VISIBLE : View.GONE);
vh.repostInfoView.setVisibility(isRepost ? View.VISIBLE : View.GONE); vh.repostInfoView.setVisibility(isRepost && type != VIEW_TYPE_FEATURED ? View.VISIBLE : View.GONE);
vh.repostChannelView.setText(isRepost ? original.getSigningChannel().getName() : null); vh.repostChannelView.setText(isRepost && original.getSigningChannel() != null ? original.getSigningChannel().getName() : null);
vh.repostChannelView.setOnClickListener(new View.OnClickListener() { vh.repostChannelView.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
@ -404,6 +425,13 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
vh.noThumbnailView.setVisibility(Helper.isNullOrEmpty(thumbnailUrl) ? View.VISIBLE : View.GONE); vh.noThumbnailView.setVisibility(Helper.isNullOrEmpty(thumbnailUrl) ? View.VISIBLE : View.GONE);
Helper.setIconViewBackgroundColor(vh.noThumbnailView, bgColor, false, context); Helper.setIconViewBackgroundColor(vh.noThumbnailView, bgColor, false, context);
Helper.setViewVisibility(vh.loadingImagePlaceholder, item.isLoadingPlaceholder() ? View.VISIBLE : View.GONE);
Helper.setViewVisibility(vh.loadingTextPlaceholder1, item.isLoadingPlaceholder() ? View.VISIBLE : View.GONE);
Helper.setViewVisibility(vh.loadingTextPlaceholder2, item.isLoadingPlaceholder() ? View.VISIBLE : View.GONE);
Helper.setViewVisibility(vh.titleView, !item.isLoadingPlaceholder() ? View.VISIBLE : View.GONE);
Helper.setViewVisibility(vh.publisherView, !item.isLoadingPlaceholder() ? View.VISIBLE : View.GONE);
Helper.setViewVisibility(vh.publishTimeView, !item.isLoadingPlaceholder() && !isPending ? View.VISIBLE : View.GONE);
if (type == VIEW_TYPE_FEATURED && item.isUnresolved()) { if (type == VIEW_TYPE_FEATURED && item.isUnresolved()) {
vh.durationView.setVisibility(View.GONE); vh.durationView.setVisibility(View.GONE);
vh.titleView.setText("Nothing here. Publish something!"); vh.titleView.setText("Nothing here. Publish something!");
@ -413,6 +441,7 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
long duration = item.getDuration(); long duration = item.getDuration();
if (!Helper.isNullOrEmpty(thumbnailUrl)) { if (!Helper.isNullOrEmpty(thumbnailUrl)) {
Glide.with(context.getApplicationContext()). Glide.with(context.getApplicationContext()).
asBitmap().
load(thumbnailUrl). load(thumbnailUrl).
centerCrop(). centerCrop().
placeholder(R.drawable.bg_thumbnail_placeholder). placeholder(R.drawable.bg_thumbnail_placeholder).

View file

@ -0,0 +1,228 @@
package io.lbry.browser.adapter;
import android.content.Context;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import io.lbry.browser.R;
import io.lbry.browser.model.Claim;
import io.lbry.browser.model.ClaimCacheKey;
import io.lbry.browser.model.Comment;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry;
import io.lbry.browser.utils.LbryUri;
import lombok.Setter;
public class CommentListAdapter extends RecyclerView.Adapter<CommentListAdapter.ViewHolder> {
private final List<Comment> items;
private final Context context;
private final boolean nested;
private float scale;
@Setter
private ClaimListAdapter.ClaimListItemListener listener;
@Setter
private ReplyClickListener replyListener;
public CommentListAdapter(List<Comment> items, Context context) {
this(items, context, false);
}
public CommentListAdapter(List<Comment> items, Context context, boolean nested) {
this.items = new ArrayList<>(items);
this.context = context;
this.nested = nested;
if (context != null) {
scale = context.getResources().getDisplayMetrics().density;
}
for (Comment item : this.items) {
ClaimCacheKey key = new ClaimCacheKey();
key.setClaimId(item.getChannelId());
if (Lbry.claimCache.containsKey(key)) {
item.setPoster(Lbry.claimCache.get(key));
}
}
}
public void clearItems() {
items.clear();
notifyDataSetChanged();
}
public int getPositionForComment(String commentHash) {
for (int i = 0; i < items.size(); i++) {
if (commentHash.equalsIgnoreCase(items.get(i).getId())) {
return i;
}
}
return -1;
}
@Override
public int getItemCount() {
return items != null ? items.size() : 0;
}
public List<String> getClaimUrlsToResolve() {
List<String> urls = new ArrayList<>();
for (int i = 0; i < items.size(); i++) {
Comment item = items.get(i);
if (item.getPoster() == null) {
LbryUri url = LbryUri.tryParse(String.format("%s#%s", item.getChannelName(), item.getChannelId()));
if (url != null && !urls.contains(url.toString())) {
urls.add(url.toString());
}
}
if (item.getReplies().size() > 0) {
for (int j = 0; j < item.getReplies().size(); j++) {
Comment reply = item.getReplies().get(j);
if (reply.getPoster() == null) {
LbryUri url = LbryUri.tryParse(String.format("%s#%s", reply.getChannelName(), reply.getChannelId()));
if (url != null && !urls.contains(url.toString())) {
urls.add(url.toString());
}
}
}
}
}
return urls;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
protected final TextView channelName;
protected final TextView commentText;
protected final ImageView thumbnailView;
protected final View noThumbnailView;
protected final TextView alphaView;
protected final TextView commentTimeView;
protected final View replyLink;
protected final RecyclerView repliesList;
public ViewHolder (View v) {
super(v);
channelName = v.findViewById(R.id.comment_channel_name);
commentTimeView = v.findViewById(R.id.comment_time);
commentText = v.findViewById(R.id.comment_text);
replyLink = v.findViewById(R.id.comment_reply_link);
thumbnailView = v.findViewById(R.id.comment_thumbnail);
noThumbnailView = v.findViewById(R.id.comment_no_thumbnail);
alphaView = v.findViewById(R.id.comment_thumbnail_alpha);
repliesList = v.findViewById(R.id.comment_replies);
}
}
public void insert(int index, Comment comment) {
if (!items.contains(comment)) {
items.add(index, comment);
notifyDataSetChanged();
}
}
public void addReply(Comment comment) {
for (int i = 0; i < items.size(); i++) {
Comment parent = items.get(i);
if (parent.getId().equalsIgnoreCase(comment.getParentId())) {
parent.addReply(comment);
notifyDataSetChanged();
break;
}
}
}
public void updatePosterForComment(String channelId, Claim channel) {
for (int i = 0 ; i < items.size(); i++) {
Comment item = items.get(i);
List<Comment> replies = item.getReplies();
if (replies != null && replies.size() > 0) {
for (int j = 0; j < replies.size(); j++) {
Comment reply = item.getReplies().get(j);
if (channelId.equalsIgnoreCase(reply.getChannelId())) {
reply.setPoster(channel);
break;
}
}
}
if (channelId.equalsIgnoreCase(item.getChannelId())) {
item.setPoster(channel);
break;
}
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(context).inflate(R.layout.list_item_comment, parent, false);
return new CommentListAdapter.ViewHolder(v);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Comment comment = items.get(position);
holder.itemView.setPadding(
nested ? Helper.getScaledValue(56, scale) : holder.itemView.getPaddingStart(),
holder.itemView.getPaddingTop(),
nested ? 0 : holder.itemView.getPaddingEnd(),
holder.itemView.getPaddingBottom());
holder.channelName.setText(comment.getChannelName());
holder.commentTimeView.setText(DateUtils.getRelativeTimeSpanString(
(comment.getTimestamp() * 1000), System.currentTimeMillis(), 0, DateUtils.FORMAT_ABBREV_RELATIVE));
holder.commentText.setText(comment.getText());
holder.replyLink.setVisibility(!nested ? View.VISIBLE : View.GONE);
boolean hasThumbnail = comment.getPoster() != null && !Helper.isNullOrEmpty(comment.getPoster().getThumbnailUrl());
holder.thumbnailView.setVisibility(hasThumbnail ? View.VISIBLE : View.INVISIBLE);
holder.noThumbnailView.setVisibility(!hasThumbnail ? View.VISIBLE : View.INVISIBLE);
int bgColor = Helper.generateRandomColorForValue(comment.getChannelId());
Helper.setIconViewBackgroundColor(holder.noThumbnailView, bgColor, false, context);
if (hasThumbnail) {
Glide.with(context.getApplicationContext()).asBitmap().load(comment.getPoster().getThumbnailUrl(holder.thumbnailView.getLayoutParams().width, holder.thumbnailView.getLayoutParams().height, 85)).
apply(RequestOptions.circleCropTransform()).into(holder.thumbnailView);
}
holder.alphaView.setText(comment.getChannelName() != null ? comment.getChannelName().substring(1, 2).toUpperCase() : null);
List<Comment> replies = comment.getReplies();
boolean hasReplies = replies != null && replies.size() > 0;
if (hasReplies) {
holder.repliesList.setLayoutManager(new LinearLayoutManager(context));
holder.repliesList.setAdapter(new CommentListAdapter(replies, context, true));
} else {
holder.repliesList.setAdapter(null);
}
holder.channelName.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (listener != null && comment.getPoster() != null) {
listener.onClaimClicked(comment.getPoster());
}
}
});
holder.replyLink.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (replyListener != null) {
replyListener.onReplyClicked(comment);
}
}
});
}
public interface ReplyClickListener {
void onReplyClicked(Comment comment);
}
}

View file

@ -23,8 +23,8 @@ public class EditorsChoiceItemAdapter extends RecyclerView.Adapter<EditorsChoice
private static final int VIEW_TYPE_HEADER = 1; private static final int VIEW_TYPE_HEADER = 1;
private static final int VIEW_TYPE_CONTENT = 2; private static final int VIEW_TYPE_CONTENT = 2;
private Context context; private final Context context;
private List<EditorsChoiceItem> items; private final List<EditorsChoiceItem> items;
@Setter @Setter
private EditorsChoiceItemListener listener; private EditorsChoiceItemListener listener;
@ -48,11 +48,11 @@ public class EditorsChoiceItemAdapter extends RecyclerView.Adapter<EditorsChoice
} }
public static class ViewHolder extends RecyclerView.ViewHolder { public static class ViewHolder extends RecyclerView.ViewHolder {
protected ImageView thumbnailView; protected final ImageView thumbnailView;
protected TextView descriptionView; protected final TextView descriptionView;
protected TextView headerView; protected final TextView headerView;
protected TextView titleView; protected final TextView titleView;
protected View cardView; protected final View cardView;
public ViewHolder(View v) { public ViewHolder(View v) {
super(v); super(v);
@ -95,6 +95,7 @@ public class EditorsChoiceItemAdapter extends RecyclerView.Adapter<EditorsChoice
vh.descriptionView.setText(item.getDescription()); vh.descriptionView.setText(item.getDescription());
if (!Helper.isNullOrEmpty(item.getThumbnailUrl())) { if (!Helper.isNullOrEmpty(item.getThumbnailUrl())) {
Glide.with(context.getApplicationContext()). Glide.with(context.getApplicationContext()).
asBitmap().
load(item.getThumbnailUrl()). load(item.getThumbnailUrl()).
centerCrop(). centerCrop().
placeholder(R.drawable.bg_thumbnail_placeholder). placeholder(R.drawable.bg_thumbnail_placeholder).

View file

@ -21,8 +21,8 @@ import io.lbry.browser.utils.Helper;
import lombok.Setter; import lombok.Setter;
public class GalleryGridAdapter extends RecyclerView.Adapter<GalleryGridAdapter.ViewHolder> { public class GalleryGridAdapter extends RecyclerView.Adapter<GalleryGridAdapter.ViewHolder> {
private Context context; private final Context context;
private List<GalleryItem> items; private final List<GalleryItem> items;
@Setter @Setter
private GalleryItemClickListener listener; private GalleryItemClickListener listener;
@ -32,8 +32,8 @@ public class GalleryGridAdapter extends RecyclerView.Adapter<GalleryGridAdapter.
} }
public static class ViewHolder extends RecyclerView.ViewHolder { public static class ViewHolder extends RecyclerView.ViewHolder {
protected ImageView thumbnailView; protected final ImageView thumbnailView;
protected TextView durationView; protected final TextView durationView;
public ViewHolder(View v) { public ViewHolder(View v) {
super(v); super(v);
thumbnailView = v.findViewById(R.id.gallery_item_thumbnail); thumbnailView = v.findViewById(R.id.gallery_item_thumbnail);
@ -96,8 +96,8 @@ public class GalleryGridAdapter extends RecyclerView.Adapter<GalleryGridAdapter.
public static class GalleryGridItemDecoration extends RecyclerView.ItemDecoration { public static class GalleryGridItemDecoration extends RecyclerView.ItemDecoration {
private int spanCount; private final int spanCount;
private int spacing; private final int spacing;
public GalleryGridItemDecoration(int spanCount, int spacing) { public GalleryGridItemDecoration(int spanCount, int spacing) {
this.spanCount = spanCount; this.spanCount = spanCount;

View file

@ -1,15 +1,14 @@
package io.lbry.browser.adapter; package io.lbry.browser.adapter;
import android.content.Context; import android.content.Context;
import android.database.DataSetObserver;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.SpinnerAdapter;
import android.widget.TextView; import android.widget.TextView;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import io.lbry.browser.R; import io.lbry.browser.R;
@ -17,9 +16,9 @@ import io.lbry.browser.model.Claim;
public class InlineChannelSpinnerAdapter extends ArrayAdapter<Claim> { public class InlineChannelSpinnerAdapter extends ArrayAdapter<Claim> {
private List<Claim> channels; private final List<Claim> channels;
private int layoutResourceId; private final int layoutResourceId;
private LayoutInflater inflater; private final LayoutInflater inflater;
public InlineChannelSpinnerAdapter(Context context, int resource, List<Claim> channels) { public InlineChannelSpinnerAdapter(Context context, int resource, List<Claim> channels) {
super(context, resource, 0, channels); super(context, resource, 0, channels);
@ -40,11 +39,30 @@ public class InlineChannelSpinnerAdapter extends ArrayAdapter<Claim> {
channels.add(1, anonymous); channels.add(1, anonymous);
} }
} }
public void addAnonymousPlaceholder() {
Claim anonymous = new Claim();
anonymous.setPlaceholderAnonymous(true);
insert(anonymous, 0);
channels.add(0, anonymous);
}
public void addAll(Collection<? extends Claim> collection) {
for (Claim claim : collection) {
if (!channels.contains(claim)) {
channels.add(claim);
}
}
super.addAll(collection);
}
public void clear() {
channels.clear();
super.clear();
}
public int getItemPosition(Claim item) { public int getItemPosition(Claim item) {
for (int i = 0; i < channels.size(); i++) { for (int i = 0; i < channels.size(); i++) {
Claim channel = channels.get(i); Claim channel = channels.get(i);
if (item.getClaimId().equalsIgnoreCase(channel.getClaimId())) { if (item.getClaimId() != null && item.getClaimId().equalsIgnoreCase(channel.getClaimId())) {
return i; return i;
} }
} }

View file

@ -1,9 +1,7 @@
package io.lbry.browser.adapter; package io.lbry.browser.adapter;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.net.Uri;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -11,21 +9,16 @@ import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import io.lbry.browser.R; import io.lbry.browser.R;
import io.lbry.browser.model.lbryinc.Invitee; import io.lbry.browser.model.lbryinc.Invitee;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.LbryUri;
import lombok.Setter;
public class InviteeListAdapter extends RecyclerView.Adapter<InviteeListAdapter.ViewHolder> { public class InviteeListAdapter extends RecyclerView.Adapter<InviteeListAdapter.ViewHolder> {
private Context context; private final Context context;
private List<Invitee> items; private final List<Invitee> items;
public InviteeListAdapter(List<Invitee> invitees, Context context) { public InviteeListAdapter(List<Invitee> invitees, Context context) {
this.context = context; this.context = context;
@ -80,8 +73,8 @@ public class InviteeListAdapter extends RecyclerView.Adapter<InviteeListAdapter.
} }
public static class ViewHolder extends RecyclerView.ViewHolder { public static class ViewHolder extends RecyclerView.ViewHolder {
protected TextView emailView; protected final TextView emailView;
protected TextView rewardView; protected final TextView rewardView;
public ViewHolder(View v) { public ViewHolder(View v) {
super(v); super(v);

View file

@ -10,13 +10,12 @@ import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import io.lbry.browser.R; import io.lbry.browser.R;
import io.lbry.browser.model.Claim;
import io.lbry.browser.model.Language; import io.lbry.browser.model.Language;
import io.lbry.browser.utils.Predefined; import io.lbry.browser.utils.Predefined;
public class LanguageSpinnerAdapter extends ArrayAdapter<Language> { public class LanguageSpinnerAdapter extends ArrayAdapter<Language> {
private int layoutResourceId; private final int layoutResourceId;
private LayoutInflater inflater; private final LayoutInflater inflater;
public LanguageSpinnerAdapter(Context context, int resource) { public LanguageSpinnerAdapter(Context context, int resource) {
super(context, resource, 0, Predefined.PUBLISH_LANGUAGES); super(context, resource, 0, Predefined.PUBLISH_LANGUAGES);

View file

@ -10,13 +10,12 @@ import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import io.lbry.browser.R; import io.lbry.browser.R;
import io.lbry.browser.model.Language;
import io.lbry.browser.model.License; import io.lbry.browser.model.License;
import io.lbry.browser.utils.Predefined; import io.lbry.browser.utils.Predefined;
public class LicenseSpinnerAdapter extends ArrayAdapter<License> { public class LicenseSpinnerAdapter extends ArrayAdapter<License> {
private int layoutResourceId; private final int layoutResourceId;
private LayoutInflater inflater; private final LayoutInflater inflater;
public LicenseSpinnerAdapter(Context context, int resource) { public LicenseSpinnerAdapter(Context context, int resource) {
super(context, resource, 0, Predefined.LICENSES); super(context, resource, 0, Predefined.LICENSES);

View file

@ -2,10 +2,8 @@ package io.lbry.browser.adapter;
import android.content.Context; import android.content.Context;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -17,15 +15,14 @@ import io.lbry.browser.R;
import io.lbry.browser.model.NavMenuItem; import io.lbry.browser.model.NavMenuItem;
import io.lbry.browser.ui.controls.SolidIconView; import io.lbry.browser.ui.controls.SolidIconView;
import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Helper;
import lombok.Getter;
import lombok.Setter; import lombok.Setter;
public class NavigationMenuAdapter extends RecyclerView.Adapter<NavigationMenuAdapter.ViewHolder> { public class NavigationMenuAdapter extends RecyclerView.Adapter<NavigationMenuAdapter.ViewHolder> {
private static final int TYPE_GROUP = 1; private static final int TYPE_GROUP = 1;
private static final int TYPE_ITEM = 2; private static final int TYPE_ITEM = 2;
private Context context; private final Context context;
private List<NavMenuItem> menuItems; private final List<NavMenuItem> menuItems;
private NavMenuItem currentItem; private NavMenuItem currentItem;
@Setter @Setter
private NavigationMenuItemClickListener listener; private NavigationMenuItemClickListener listener;
@ -65,8 +62,8 @@ public class NavigationMenuAdapter extends RecyclerView.Adapter<NavigationMenuAd
} }
public static class ViewHolder extends RecyclerView.ViewHolder { public static class ViewHolder extends RecyclerView.ViewHolder {
protected SolidIconView iconView; protected final SolidIconView iconView;
protected TextView titleView; protected final TextView titleView;
public ViewHolder(View v) { public ViewHolder(View v) {
super(v); super(v);
titleView = v.findViewById(R.id.nav_menu_title); titleView = v.findViewById(R.id.nav_menu_title);

View file

@ -0,0 +1,265 @@
package io.lbry.browser.adapter;
import android.content.Context;
import android.graphics.Color;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;
import io.lbry.browser.R;
import io.lbry.browser.listener.SelectionModeListener;
import io.lbry.browser.model.Claim;
import io.lbry.browser.model.lbryinc.LbryNotification;
import io.lbry.browser.ui.controls.SolidIconView;
import io.lbry.browser.utils.Helper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@Data
@EqualsAndHashCode(callSuper = false)
public class NotificationListAdapter extends RecyclerView.Adapter<NotificationListAdapter.ViewHolder> {
private static final String RULE_CREATOR_SUBSCRIBER = "creator_subscriber";
private static final String RULE_COMMENT = "comment";
private final Context context;
private final List<LbryNotification> items;
private final List<LbryNotification> selectedItems;
@Setter
private NotificationClickListener clickListener;
@Getter
@Setter
private boolean inSelectionMode;
@Setter
private SelectionModeListener selectionModeListener;
public NotificationListAdapter(List<LbryNotification> notifications, Context context) {
this.context = context;
this.items = new ArrayList<>(notifications);
this.selectedItems = new ArrayList<>();
Collections.sort(items, Collections.reverseOrder(new LbryNotification()));
}
public static class ViewHolder extends RecyclerView.ViewHolder {
protected final View layoutView;
protected final TextView titleView;
protected final TextView bodyView;
protected final TextView timeView;
protected final SolidIconView iconView;
protected final ImageView thumbnailView;
protected final View selectedOverlayView;
public ViewHolder(View v) {
super(v);
layoutView = v.findViewById(R.id.notification_layout);
titleView = v.findViewById(R.id.notification_title);
bodyView = v.findViewById(R.id.notification_body);
timeView = v.findViewById(R.id.notification_time);
iconView = v.findViewById(R.id.notification_icon);
thumbnailView = v.findViewById(R.id.notification_author_thumbnail);
selectedOverlayView = v.findViewById(R.id.notification_selected_overlay);
}
}
public int getItemCount() {
return items != null ? items.size() : 0;
}
public List<LbryNotification> getSelectedItems() {
return this.selectedItems;
}
public int getSelectedCount() {
return selectedItems != null ? selectedItems.size() : 0;
}
public void clearSelectedItems() {
this.selectedItems.clear();
}
public boolean isNotificationSelected(LbryNotification notification) {
return selectedItems.contains(notification);
}
public void insertNotification(LbryNotification notification, int index) {
if (!items.contains(notification)) {
items.add(index, notification);
}
notifyDataSetChanged();
}
public void addNotification(LbryNotification notification) {
if (!items.contains(notification)) {
items.add(notification);
}
notifyDataSetChanged();
}
public void removeNotifications(List<LbryNotification> notifications) {
for (LbryNotification notification : notifications) {
items.remove(notification);
}
notifyDataSetChanged();
}
public List<String> getAuthorUrls() {
List<String> urls = new ArrayList<>();
for (LbryNotification item : items) {
if (!Helper.isNullOrEmpty(item.getAuthorUrl())) {
urls.add(item.getAuthorUrl());
}
}
return urls;
}
public void updateAuthorClaims(List<Claim> claims) {
for (Claim claim : claims) {
if (claim != null && claim.getThumbnailUrl() != null) {
updateClaimForAuthorUrl(claim);
}
}
notifyDataSetChanged();
}
private void updateClaimForAuthorUrl(Claim claim) {
for (LbryNotification item : items) {
if (claim.getPermanentUrl().equalsIgnoreCase(item.getAuthorUrl())) {
item.setCommentAuthor(claim);
}
}
}
public void addNotifications(List<LbryNotification> notifications) {
for (LbryNotification notification : notifications) {
if (!items.contains(notification)) {
items.add(notification);
}
}
Collections.sort(items, Collections.reverseOrder(new LbryNotification()));
notifyDataSetChanged();
}
@Override
public NotificationListAdapter.ViewHolder onCreateViewHolder(ViewGroup root, int viewType) {
View v = LayoutInflater.from(context).inflate(R.layout.list_item_notification, root, false);
return new NotificationListAdapter.ViewHolder(v);
}
private int getStringIdForRule(String rule) {
if (RULE_CREATOR_SUBSCRIBER.equalsIgnoreCase(rule)) {
return R.string.fa_heart;
}
if (RULE_COMMENT.equalsIgnoreCase(rule)) {
return R.string.fa_comment_alt;
}
return R.string.fa_asterisk;
}
private int getColorForRule(String rule) {
if (RULE_CREATOR_SUBSCRIBER.equalsIgnoreCase(rule)) {
return Color.RED;
}
if (RULE_COMMENT.equalsIgnoreCase(rule)) {
return ContextCompat.getColor(context, R.color.nextLbryGreen);
}
return ContextCompat.getColor(context, R.color.lbryGreen);
}
@Override
public void onBindViewHolder(NotificationListAdapter.ViewHolder vh, int position) {
LbryNotification notification = items.get(position);
vh.layoutView.setBackgroundColor(ContextCompat.getColor(context, notification.isSeen() ? android.R.color.transparent : R.color.nextLbryGreenSemiTransparent));
vh.selectedOverlayView.setVisibility(isNotificationSelected(notification) ? View.VISIBLE : View.GONE);
vh.titleView.setVisibility(!Helper.isNullOrEmpty(notification.getTitle()) ? View.VISIBLE : View.GONE);
vh.titleView.setText(notification.getTitle());
vh.bodyView.setText(notification.getDescription());
vh.timeView.setText(DateUtils.getRelativeTimeSpanString(
getLocalNotificationTime(notification), System.currentTimeMillis(), 0, DateUtils.FORMAT_ABBREV_RELATIVE));
vh.thumbnailView.setVisibility(notification.getCommentAuthor() == null ? View.INVISIBLE : View.VISIBLE);
if (notification.getCommentAuthor() != null) {
Glide.with(context.getApplicationContext()).load(
notification.getCommentAuthor().getThumbnailUrl(vh.thumbnailView.getLayoutParams().width, vh.thumbnailView.getLayoutParams().height, 85)).apply(RequestOptions.circleCropTransform()).into(vh.thumbnailView);
}
vh.iconView.setVisibility(notification.getCommentAuthor() != null ? View.INVISIBLE : View.VISIBLE);
vh.iconView.setText(getStringIdForRule(notification.getRule()));
vh.iconView.setTextColor(getColorForRule(notification.getRule()));
vh.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (inSelectionMode) {
toggleSelectedNotification(notification);
} else {
if (clickListener != null) {
clickListener.onNotificationClicked(notification);
}
}
}
});
vh.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
if (!inSelectionMode) {
inSelectionMode = true;
if (selectionModeListener != null) {
selectionModeListener.onEnterSelectionMode();
}
}
toggleSelectedNotification(notification);
return true;
}
});
}
private void toggleSelectedNotification(LbryNotification notification) {
if (selectedItems.contains(notification)) {
selectedItems.remove(notification);
} else {
selectedItems.add(notification);
}
if (selectionModeListener != null) {
selectionModeListener.onItemSelectionToggled();
}
if (selectedItems.size() == 0) {
inSelectionMode = false;
if (selectionModeListener != null) {
selectionModeListener.onExitSelectionMode();
}
}
notifyDataSetChanged();
}
private long getLocalNotificationTime(LbryNotification notification) {
TimeZone utcTZ = TimeZone.getTimeZone("UTC");
TimeZone targetTZ = TimeZone.getDefault();
Calendar cal = new GregorianCalendar(utcTZ);
cal.setTimeInMillis(notification.getTimestamp().getTime());
cal.add(Calendar.MILLISECOND, utcTZ.getRawOffset() * -1);
cal.add(Calendar.MILLISECOND, targetTZ.getRawOffset());
return cal.getTimeInMillis();
}
public interface NotificationClickListener {
void onNotificationClicked(LbryNotification notification);
}
}

View file

@ -32,7 +32,7 @@ public class RewardListAdapter extends RecyclerView.Adapter<RewardListAdapter.Vi
public static final int DISPLAY_MODE_ALL = 1; public static final int DISPLAY_MODE_ALL = 1;
public static final int DISPLAY_MODE_UNCLAIMED = 2; public static final int DISPLAY_MODE_UNCLAIMED = 2;
private Context context; private final Context context;
@Setter @Setter
private List<Reward> all; private List<Reward> all;
private List<Reward> items; private List<Reward> items;
@ -85,16 +85,16 @@ public class RewardListAdapter extends RecyclerView.Adapter<RewardListAdapter.Vi
} }
public static class ViewHolder extends RecyclerView.ViewHolder { public static class ViewHolder extends RecyclerView.ViewHolder {
protected View iconClaimed; protected final View iconClaimed;
protected View loading; protected final View loading;
protected View upTo; protected final View upTo;
protected TextView textTitle; protected final TextView textTitle;
protected TextView textDescription; protected final TextView textDescription;
protected TextView textLbcValue; protected final TextView textLbcValue;
protected TextView textUsdValue; protected final TextView textUsdValue;
protected TextView textLinkTransaction; protected final TextView textLinkTransaction;
protected EditText inputCustomCode; protected final EditText inputCustomCode;
protected MaterialButton buttonClaimCustom; protected final MaterialButton buttonClaimCustom;
public ViewHolder(View v) { public ViewHolder(View v) {
super(v); super(v);
iconClaimed = v.findViewById(R.id.reward_item_claimed_icon); iconClaimed = v.findViewById(R.id.reward_item_claimed_icon);
@ -168,6 +168,10 @@ public class RewardListAdapter extends RecyclerView.Adapter<RewardListAdapter.Vi
return; return;
} }
if (vh.inputCustomCode != null && !vh.inputCustomCode.hasFocus()) {
vh.inputCustomCode.requestFocus();
}
if (clickListener != null) { if (clickListener != null) {
clickListener.onRewardClicked(reward, vh.loading); clickListener.onRewardClicked(reward, vh.loading);
} }

View file

@ -0,0 +1,70 @@
package io.lbry.browser.adapter;
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
import io.lbry.browser.R;
import io.lbry.browser.model.StartupStage;
public class StartupStageAdapter extends BaseAdapter {
private final List<StartupStage> list;
private final LayoutInflater inflater;
private final String[] stagesString;
public StartupStageAdapter(Context ctx, List<StartupStage> rows) {
this.list = rows;
this.inflater = LayoutInflater.from(ctx);
stagesString = new String[9];
stagesString[0] = ctx.getResources().getString(R.string.installation_id_loaded);
stagesString[1] = ctx.getResources().getString(R.string.known_tags_loaded);
stagesString[2] = ctx.getResources().getString(R.string.exchange_rate_loaded);
stagesString[3] = ctx.getResources().getString(R.string.user_authenticated);
stagesString[4] = ctx.getResources().getString(R.string.installation_registered);
stagesString[5] = ctx.getResources().getString(R.string.subscriptions_loaded);
stagesString[6] = ctx.getResources().getString(R.string.subscriptions_resolved);
stagesString[7] = ctx.getResources().getString(R.string.block_list_loaded);
stagesString[8] = ctx.getResources().getString(R.string.filter_list_loaded);
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int i) {
return list.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
if (view == null) {
view = inflater.inflate(R.layout.list_item_startupstage, viewGroup, false);
ImageView iconView = view.findViewById(R.id.startup_stage_icon);
TextView textView = view.findViewById(R.id.startup_stage_text);
StartupStage item = (StartupStage) getItem(i);
iconView.setImageResource(item.stageDone ? R.drawable.ic_check : R.drawable.ic_close);
iconView.setColorFilter(item.stageDone ? Color.WHITE : Color.RED);
textView.setText(stagesString[item.stage - 1]);
}
return view;
}
}

View file

@ -22,9 +22,9 @@ import io.lbry.browser.utils.Helper;
import lombok.Setter; import lombok.Setter;
public class SuggestedChannelGridAdapter extends RecyclerView.Adapter<SuggestedChannelGridAdapter.ViewHolder> { public class SuggestedChannelGridAdapter extends RecyclerView.Adapter<SuggestedChannelGridAdapter.ViewHolder> {
private Context context; private final Context context;
private List<Claim> items; private final List<Claim> items;
private List<Claim> selectedItems; private final List<Claim> selectedItems;
@Setter @Setter
private ChannelItemSelectionListener listener; private ChannelItemSelectionListener listener;
@ -35,11 +35,11 @@ public class SuggestedChannelGridAdapter extends RecyclerView.Adapter<SuggestedC
} }
public static class ViewHolder extends RecyclerView.ViewHolder { public static class ViewHolder extends RecyclerView.ViewHolder {
protected View noThumbnailView; protected final View noThumbnailView;
protected ImageView thumbnailView; protected final ImageView thumbnailView;
protected TextView alphaView; protected final TextView alphaView;
protected TextView titleView; protected final TextView titleView;
protected TextView tagView; protected final TextView tagView;
public ViewHolder(View v) { public ViewHolder(View v) {
super(v); super(v);
noThumbnailView = v.findViewById(R.id.suggested_channel_no_thumbnail); noThumbnailView = v.findViewById(R.id.suggested_channel_no_thumbnail);
@ -89,7 +89,8 @@ public class SuggestedChannelGridAdapter extends RecyclerView.Adapter<SuggestedC
@Override @Override
public void onBindViewHolder(SuggestedChannelGridAdapter.ViewHolder vh, int position) { public void onBindViewHolder(SuggestedChannelGridAdapter.ViewHolder vh, int position) {
Claim claim = items.get(position); Claim claim = items.get(position);
String thumbnailUrl = claim.getThumbnailUrl(); ViewGroup.LayoutParams lp = vh.thumbnailView.getLayoutParams();
String thumbnailUrl = claim.getThumbnailUrl(lp.width, lp.height, 85);
int bgColor = Helper.generateRandomColorForValue(claim.getClaimId()); int bgColor = Helper.generateRandomColorForValue(claim.getClaimId());
Helper.setIconViewBackgroundColor(vh.noThumbnailView, bgColor, false, context); Helper.setIconViewBackgroundColor(vh.noThumbnailView, bgColor, false, context);

View file

@ -23,12 +23,13 @@ public class TagListAdapter extends RecyclerView.Adapter<TagListAdapter.ViewHold
public static final int CUSTOMIZE_MODE_ADD = 1; public static final int CUSTOMIZE_MODE_ADD = 1;
public static final int CUSTOMIZE_MODE_REMOVE = 2; public static final int CUSTOMIZE_MODE_REMOVE = 2;
private Context context; private final Context context;
private List<Tag> items; private List<Tag> items;
@Setter @Setter
private TagClickListener clickListener; private TagClickListener clickListener;
@Getter
@Setter @Setter
@Getter
private int customizeMode; private int customizeMode;
public TagListAdapter(List<Tag> tags, Context context) { public TagListAdapter(List<Tag> tags, Context context) {
@ -38,8 +39,8 @@ public class TagListAdapter extends RecyclerView.Adapter<TagListAdapter.ViewHold
} }
public static class ViewHolder extends RecyclerView.ViewHolder { public static class ViewHolder extends RecyclerView.ViewHolder {
protected ImageView iconView; protected final ImageView iconView;
protected TextView nameView; protected final TextView nameView;
public ViewHolder(View v) { public ViewHolder(View v) {
super(v); super(v);
iconView = v.findViewById(R.id.tag_action); iconView = v.findViewById(R.id.tag_action);

View file

@ -26,8 +26,8 @@ public class TransactionListAdapter extends RecyclerView.Adapter<TransactionList
private static final DecimalFormat TX_LIST_AMOUNT_FORMAT = new DecimalFormat("#,##0.0000"); private static final DecimalFormat TX_LIST_AMOUNT_FORMAT = new DecimalFormat("#,##0.0000");
private static final SimpleDateFormat TX_LIST_DATE_FORMAT = new SimpleDateFormat("MMM d"); private static final SimpleDateFormat TX_LIST_DATE_FORMAT = new SimpleDateFormat("MMM d");
private Context context; private final Context context;
private List<Transaction> items; private final List<Transaction> items;
@Setter @Setter
private TransactionClickListener listener; private TransactionClickListener listener;
@ -72,7 +72,9 @@ public class TransactionListAdapter extends RecyclerView.Adapter<TransactionList
vh.claimView.setText(item.getClaim()); vh.claimView.setText(item.getClaim());
vh.feeView.setText(context.getString(R.string.tx_list_fee, TX_LIST_AMOUNT_FORMAT.format(item.getFee().doubleValue()))); vh.feeView.setText(context.getString(R.string.tx_list_fee, TX_LIST_AMOUNT_FORMAT.format(item.getFee().doubleValue())));
vh.txidLinkView.setText(item.getTxid().substring(0, 7)); vh.txidLinkView.setText(item.getTxid().substring(0, 7));
vh.dateView.setText(TX_LIST_DATE_FORMAT.format(item.getTxDate())); vh.dateView.setVisibility(item.getConfirmations() > 0 ? View.VISIBLE : View.GONE);
vh.dateView.setText(item.getConfirmations() > 0 ? TX_LIST_DATE_FORMAT.format(item.getTxDate()) : null);
vh.pendingView.setVisibility(item.getConfirmations() == 0 ? View.VISIBLE : View.GONE);
vh.infoFeeContainer.setVisibility(!Helper.isNullOrEmpty(item.getClaim()) || Math.abs(item.getFee().doubleValue()) > 0 ? vh.infoFeeContainer.setVisibility(!Helper.isNullOrEmpty(item.getClaim()) || Math.abs(item.getFee().doubleValue()) > 0 ?
View.VISIBLE : View.GONE); View.VISIBLE : View.GONE);
@ -105,13 +107,14 @@ public class TransactionListAdapter extends RecyclerView.Adapter<TransactionList
} }
public static class ViewHolder extends RecyclerView.ViewHolder { public static class ViewHolder extends RecyclerView.ViewHolder {
protected TextView descView; protected final TextView descView;
protected TextView amountView; protected final TextView amountView;
protected TextView claimView; protected final TextView claimView;
protected TextView feeView; protected final TextView feeView;
protected TextView txidLinkView; protected final TextView txidLinkView;
protected TextView dateView; protected final TextView dateView;
protected View infoFeeContainer; protected final TextView pendingView;
protected final View infoFeeContainer;
public ViewHolder(View v) { public ViewHolder(View v) {
super(v); super(v);
@ -121,6 +124,7 @@ public class TransactionListAdapter extends RecyclerView.Adapter<TransactionList
feeView = v.findViewById(R.id.transaction_fee); feeView = v.findViewById(R.id.transaction_fee);
txidLinkView = v.findViewById(R.id.transaction_id_link); txidLinkView = v.findViewById(R.id.transaction_id_link);
dateView = v.findViewById(R.id.transaction_date); dateView = v.findViewById(R.id.transaction_date);
pendingView = v.findViewById(R.id.transaction_pending_text);
infoFeeContainer = v.findViewById(R.id.transaction_info_fee_container); infoFeeContainer = v.findViewById(R.id.transaction_info_fee_container);
} }
} }

View file

@ -21,8 +21,8 @@ import io.lbry.browser.utils.LbryUri;
import lombok.Setter; import lombok.Setter;
public class UrlSuggestionListAdapter extends RecyclerView.Adapter<UrlSuggestionListAdapter.ViewHolder> { public class UrlSuggestionListAdapter extends RecyclerView.Adapter<UrlSuggestionListAdapter.ViewHolder> {
private Context context; private final Context context;
private List<UrlSuggestion> items; private final List<UrlSuggestion> items;
@Setter @Setter
private UrlSuggestionClickListener listener; private UrlSuggestionClickListener listener;
@ -130,9 +130,9 @@ public class UrlSuggestionListAdapter extends RecyclerView.Adapter<UrlSuggestion
} }
public static class ViewHolder extends RecyclerView.ViewHolder { public static class ViewHolder extends RecyclerView.ViewHolder {
protected SolidIconView iconView; protected final SolidIconView iconView;
protected TextView titleView; protected final TextView titleView;
protected TextView descView; protected final TextView descView;
public ViewHolder(View v) { public ViewHolder(View v) {
super(v); super(v);
iconView = v.findViewById(R.id.url_suggestion_icon); iconView = v.findViewById(R.id.url_suggestion_icon);

View file

@ -25,7 +25,7 @@ public class VerificationPagerAdapter extends FragmentStateAdapter {
public static final int PAGE_VERIFICATION_WALLET = 2; public static final int PAGE_VERIFICATION_WALLET = 2;
public static final int PAGE_VERIFICATION_MANUAL = 3; public static final int PAGE_VERIFICATION_MANUAL = 3;
private FragmentActivity activity; private final FragmentActivity activity;
public VerificationPagerAdapter(FragmentActivity activity) { public VerificationPagerAdapter(FragmentActivity activity) {
super(activity); super(activity);

View file

@ -0,0 +1,100 @@
package io.lbry.browser.adapter;
import android.content.Context;
import android.content.DialogInterface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import java.util.List;
import io.lbry.browser.MainActivity;
import io.lbry.browser.R;
import io.lbry.browser.model.WalletDetailItem;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.views.CreditsBalanceView;
public class WalletDetailAdapter extends BaseAdapter {
private final List<WalletDetailItem> list;
private final LayoutInflater inflater;
public WalletDetailAdapter(Context ctx, List<WalletDetailItem> rows) {
this.list = rows;
this.inflater = LayoutInflater.from(ctx);
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int i) {
return list.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
if (view == null) {
view = inflater.inflate(R.layout.list_item_boosting_balance, viewGroup, false);
CreditsBalanceView balanceView = view.findViewById(R.id.wallet_supporting_balance);
TextView detailTextView = view.findViewById(R.id.detail);
TextView detailExplanationTextView = view.findViewById(R.id.detail_explanation);
WalletDetailItem item = (WalletDetailItem) getItem(i);
detailTextView.setText(item.detail);
detailExplanationTextView.setText(item.detailDesc);
Helper.setViewText(balanceView, item.detailAmount);
ProgressBar progressUnlockTips = view.findViewById(R.id.wallet_unlock_tips_progress);
progressUnlockTips.setVisibility(item.isInProgress ? View.VISIBLE : View.GONE);
ImageButton buttonLock = view.findViewById(R.id.lock_button);
buttonLock.setVisibility((item.isUnlockable && !item.isInProgress) ? View.VISIBLE : View.GONE);
if (item.isUnlockable) {
buttonLock.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (view.getContext() != null) {
AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext()).
setTitle(R.string.unlock_tips).
setMessage(R.string.confirm_unlock_tips)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
unlockTips(view);
}
}).setNegativeButton(R.string.no, null);
builder.show();
}
}
});
}
}
return view;
}
private void unlockTips(View v) {
Context ctx = v.getContext();
if (ctx instanceof MainActivity) {
v.setVisibility(View.GONE);
View progress = v.getRootView().findViewById(R.id.wallet_unlock_tips_progress);
progress.setVisibility(View.VISIBLE);
((MainActivity) ctx).unlockTips();
}
}
}

View file

@ -4,7 +4,6 @@ import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.opengl.Visibility;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.ParseException; import java.text.ParseException;
@ -13,22 +12,22 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import io.lbry.browser.exceptions.LbryUriException;
import io.lbry.browser.model.Tag; import io.lbry.browser.model.Tag;
import io.lbry.browser.model.UrlSuggestion; import io.lbry.browser.model.UrlSuggestion;
import io.lbry.browser.model.ViewHistory; import io.lbry.browser.model.ViewHistory;
import io.lbry.browser.model.lbryinc.LbryNotification;
import io.lbry.browser.model.lbryinc.Subscription; import io.lbry.browser.model.lbryinc.Subscription;
import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.LbryUri; import io.lbry.browser.utils.LbryUri;
public class DatabaseHelper extends SQLiteOpenHelper { public class DatabaseHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 2; public static final int DATABASE_VERSION = 8;
public static final String DATABASE_NAME = "LbryApp.db"; public static final String DATABASE_NAME = "LbryApp.db";
private static DatabaseHelper instance; private static DatabaseHelper instance;
private static final String[] SQL_CREATE_TABLES = { private static final String[] SQL_CREATE_TABLES = {
// local subscription store // local subscription store
"CREATE TABLE subscriptions (url TEXT PRIMARY KEY NOT NULL, channel_name TEXT NOT NULL)", "CREATE TABLE subscriptions (url TEXT PRIMARY KEY NOT NULL, channel_name TEXT NOT NULL, is_notifications_disabled INTEGER DEFAULT 0 NOT NULL)",
// url entry / suggestion history // url entry / suggestion history
"CREATE TABLE url_history (id INTEGER PRIMARY KEY NOT NULL, value TEXT NOT NULL, url TEXT, type INTEGER NOT NULL, timestamp TEXT NOT NULL)", "CREATE TABLE url_history (id INTEGER PRIMARY KEY NOT NULL, value TEXT NOT NULL, url TEXT, type INTEGER NOT NULL, timestamp TEXT NOT NULL)",
// tags (known and followed) // tags (known and followed)
@ -48,7 +47,20 @@ public class DatabaseHelper extends SQLiteOpenHelper {
", thumbnail_url TEXT" + ", thumbnail_url TEXT" +
", release_time INTEGER " + ", release_time INTEGER " +
", device TEXT" + ", device TEXT" +
", timestamp TEXT NOT NULL)" ", timestamp TEXT NOT NULL)",
"CREATE TABLE notifications (" +
" id INTEGER PRIMARY KEY NOT NULL" +
", remote_id INTEGER NOT NULL" +
", author_url TEXT" +
", title TEXT" +
", description TEXT" +
", thumbnail_url TEXT" +
", target_url TEXT" +
", rule TEXT" +
", is_read INTEGER DEFAULT 0 NOT NULL" +
", is_seen INTEGER DEFAULT 0 NOT NULL " +
", timestamp TEXT NOT NULL)",
"CREATE TABLE shuffle_watched (id INTEGER PRIMARY KEY NOT NULL, claim_id TEXT NOT NULL)"
}; };
private static final String[] SQL_CREATE_INDEXES = { private static final String[] SQL_CREATE_INDEXES = {
"CREATE UNIQUE INDEX idx_subscription_url ON subscriptions (url)", "CREATE UNIQUE INDEX idx_subscription_url ON subscriptions (url)",
@ -56,22 +68,69 @@ public class DatabaseHelper extends SQLiteOpenHelper {
"CREATE UNIQUE INDEX idx_url_history_url ON url_history (url)", "CREATE UNIQUE INDEX idx_url_history_url ON url_history (url)",
"CREATE UNIQUE INDEX idx_tag_name ON tags (name)", "CREATE UNIQUE INDEX idx_tag_name ON tags (name)",
"CREATE UNIQUE INDEX idx_view_history_url_device ON view_history (url, device)", "CREATE UNIQUE INDEX idx_view_history_url_device ON view_history (url, device)",
"CREATE INDEX idx_view_history_device ON view_history (device)" "CREATE INDEX idx_view_history_device ON view_history (device)",
"CREATE UNIQUE INDEX idx_notification_remote_id ON notifications (remote_id)",
"CREATE INDEX idx_notification_timestamp ON notifications (timestamp)",
"CREATE UNIQUE INDEX idx_shuffle_watched_claim ON shuffle_watched (claim_id)",
}; };
private static final String[] SQL_V1_V2_UPGRADE = { private static final String[] SQL_V1_V2_UPGRADE = {
"ALTER TABLE view_history ADD COLUMN currency TEXT" "ALTER TABLE view_history ADD COLUMN currency TEXT"
}; };
private static final String SQL_INSERT_SUBSCRIPTION = "REPLACE INTO subscriptions (channel_name, url) VALUES (?, ?)"; private static final String[] SQL_V2_V3_UPGRADE = {
"CREATE TABLE notifications (" +
" id INTEGER PRIMARY KEY NOT NULL" +
", title TEXT" +
", description TEXT" +
", thumbnail_url TEXT" +
", target_url TEXT" +
", is_read INTEGER DEFAULT 0 NOT NULL" +
", timestamp TEXT NOT NULL)",
"CREATE INDEX idx_notification_timestamp ON notifications (timestamp)"
};
private static final String[] SQL_V3_V4_UPGRADE = {
"ALTER TABLE notifications ADD COLUMN remote_id INTEGER",
"CREATE UNIQUE INDEX idx_notification_remote_id ON notifications (remote_id)"
};
private static final String[] SQL_V4_V5_UPGRADE = {
"ALTER TABLE notifications ADD COLUMN rule TEXT",
"ALTER TABLE notifications ADD COLUMN is_seen TEXT"
};
private static final String[] SQL_V5_V6_UPGRADE = {
"ALTER TABLE notifications ADD COLUMN author_url TEXT"
};
private static final String[] SQL_V6_V7_UPGRADE = {
"CREATE TABLE shuffle_watched (id INTEGER PRIMARY KEY NOT NULL, claim_id TEXT NOT NULL)",
"CREATE UNIQUE INDEX idx_shuffle_watched_claim ON shuffle_watched (claim_id)"
};
private static final String[] SQL_V7_V8_UPGRADE = {
"AlTER TABLE subscriptions ADD COLUMN is_notifications_disabled INTEGER DEFAULT 0 NOT NULL"
};
private static final String SQL_INSERT_SUBSCRIPTION = "REPLACE INTO subscriptions (channel_name, url, is_notifications_disabled) VALUES (?, ?, ?)";
private static final String SQL_UPDATE_SUBSCRIPTION_NOTIFICATION = "UPDATE subscriptions SET is_notification_disabled = ? WHERE url = ?";
private static final String SQL_CLEAR_SUBSCRIPTIONS = "DELETE FROM subscriptions";
private static final String SQL_DELETE_SUBSCRIPTION = "DELETE FROM subscriptions WHERE url = ?"; private static final String SQL_DELETE_SUBSCRIPTION = "DELETE FROM subscriptions WHERE url = ?";
private static final String SQL_GET_SUBSCRIPTIONS = "SELECT channel_name, url FROM subscriptions"; private static final String SQL_GET_SUBSCRIPTIONS = "SELECT channel_name, url, is_notifications_disabled FROM subscriptions";
private static final String SQL_INSERT_URL_HISTORY = "REPLACE INTO url_history (value, url, type, timestamp) VALUES (?, ?, ?, ?)"; private static final String SQL_INSERT_URL_HISTORY = "REPLACE INTO url_history (value, url, type, timestamp) VALUES (?, ?, ?, ?)";
private static final String SQL_CLEAR_URL_HISTORY = "DELETE FROM url_history"; private static final String SQL_CLEAR_URL_HISTORY = "DELETE FROM url_history";
private static final String SQL_CLEAR_URL_HISTORY_BEFORE_TIME = "DELETE FROM url_history WHERE timestamp < ?"; private static final String SQL_CLEAR_URL_HISTORY_BEFORE_TIME = "DELETE FROM url_history WHERE timestamp < ?";
private static final String SQL_GET_RECENT_URL_HISTORY = "SELECT value, url, type FROM url_history ORDER BY timestamp DESC LIMIT 10"; private static final String SQL_GET_RECENT_URL_HISTORY = "SELECT value, url, type FROM url_history ORDER BY timestamp DESC LIMIT 10";
private static final String SQL_INSERT_NOTIFICATION = "REPLACE INTO notifications (remote_id, author_url, title, description, rule, target_url, is_read, is_seen, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
private static final String SQL_GET_NOTIFICATIONS = "SELECT id, remote_id, author_url, title, description, rule, target_url, is_read, is_seen, timestamp FROM notifications ORDER BY timestamp DESC LIMIT 500";
private static final String SQL_GET_UNREAD_NOTIFICATIONS_COUNT = "SELECT COUNT(id) FROM notifications WHERE is_read <> 1";
private static final String SQL_GET_UNSEEN_NOTIFICATIONS_COUNT = "SELECT COUNT(id) FROM notifications WHERE is_seen <> 1";
private static final String SQL_MARK_NOTIFICATIONS_READ = "UPDATE notifications SET is_read = 1 WHERE is_read = 0";
private static final String SQL_MARK_NOTIFICATIONS_SEEN = "UPDATE notifications SET is_seen = 1 WHERE is_seen = 0";
private static final String SQL_MARK_NOTIFICATION_READ_AND_SEEN = "UPDATE notifications SET is_read = 1, is_seen = 1 WHERE id = ?";
private static final String SQL_INSERT_SHUFFLE_WATCHED = "REPLACE INTO shuffle_watched (claim_id) VALUES (?)";
private static final String SQL_GET_SHUFFLE_WATCHED_CLAIMS = "SELECT claim_id FROM shuffle_watched";
private static final String SQL_INSERT_VIEW_HISTORY = private static final String SQL_INSERT_VIEW_HISTORY =
"REPLACE INTO view_history (url, claim_id, claim_name, cost, currency, title, publisher_claim_id, publisher_name, publisher_title, thumbnail_url, device, release_time, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; "REPLACE INTO view_history (url, claim_id, claim_name, cost, currency, title, publisher_claim_id, publisher_name, publisher_title, thumbnail_url, device, release_time, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
private static final String SQL_GET_VIEW_HISTORY = private static final String SQL_GET_VIEW_HISTORY =
@ -110,6 +169,36 @@ public class DatabaseHelper extends SQLiteOpenHelper {
db.execSQL(sql); db.execSQL(sql);
} }
} }
if (oldVersion < 3) {
for (String sql : SQL_V2_V3_UPGRADE) {
db.execSQL(sql);
}
}
if (oldVersion < 4) {
for (String sql : SQL_V3_V4_UPGRADE) {
db.execSQL(sql);
}
}
if (oldVersion < 5) {
for (String sql : SQL_V4_V5_UPGRADE) {
db.execSQL(sql);
}
}
if (oldVersion < 6) {
for (String sql : SQL_V5_V6_UPGRADE) {
db.execSQL(sql);
}
}
if (oldVersion < 7) {
for (String sql : SQL_V6_V7_UPGRADE) {
db.execSQL(sql);
}
}
if (oldVersion < 8) {
for (String sql : SQL_V7_V8_UPGRADE) {
db.execSQL(sql);
}
}
} }
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
@ -126,6 +215,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
public static void clearUrlHistoryBefore(Date date, SQLiteDatabase db) { public static void clearUrlHistoryBefore(Date date, SQLiteDatabase db) {
db.execSQL(SQL_CLEAR_URL_HISTORY_BEFORE_TIME, new Object[] { new SimpleDateFormat(Helper.ISO_DATE_FORMAT_PATTERN).format(new Date()) }); db.execSQL(SQL_CLEAR_URL_HISTORY_BEFORE_TIME, new Object[] { new SimpleDateFormat(Helper.ISO_DATE_FORMAT_PATTERN).format(new Date()) });
} }
// History items are essentially url suggestions // History items are essentially url suggestions
public static List<UrlSuggestion> getRecentHistory(SQLiteDatabase db) { public static List<UrlSuggestion> getRecentHistory(SQLiteDatabase db) {
List<UrlSuggestion> suggestions = new ArrayList<>(); List<UrlSuggestion> suggestions = new ArrayList<>();
@ -225,11 +315,21 @@ public class DatabaseHelper extends SQLiteOpenHelper {
} }
public static void createOrUpdateSubscription(Subscription subscription, SQLiteDatabase db) { public static void createOrUpdateSubscription(Subscription subscription, SQLiteDatabase db) {
db.execSQL(SQL_INSERT_SUBSCRIPTION, new Object[] { subscription.getChannelName(), subscription.getUrl() }); db.execSQL(SQL_INSERT_SUBSCRIPTION, new Object[] {
subscription.getChannelName(),
subscription.getUrl(),
subscription.isNotificationsDisabled() ? 1 : 0
});
}
public static void setSubscriptionNotificationDisabled(boolean flag, String url, SQLiteDatabase db) {
db.execSQL(SQL_UPDATE_SUBSCRIPTION_NOTIFICATION, new Object[] { flag ? 1 : 0, url });
} }
public static void deleteSubscription(Subscription subscription, SQLiteDatabase db) { public static void deleteSubscription(Subscription subscription, SQLiteDatabase db) {
db.execSQL(SQL_DELETE_SUBSCRIPTION, new Object[] { subscription.getUrl() }); db.execSQL(SQL_DELETE_SUBSCRIPTION, new Object[] { subscription.getUrl() });
} }
public static void clearSubscriptions(SQLiteDatabase db) {
db.execSQL(SQL_CLEAR_SUBSCRIPTIONS);
}
public static List<Subscription> getSubscriptions(SQLiteDatabase db) { public static List<Subscription> getSubscriptions(SQLiteDatabase db) {
List<Subscription> subscriptions = new ArrayList<>(); List<Subscription> subscriptions = new ArrayList<>();
Cursor cursor = null; Cursor cursor = null;
@ -239,6 +339,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
Subscription subscription = new Subscription(); Subscription subscription = new Subscription();
subscription.setChannelName(cursor.getString(0)); subscription.setChannelName(cursor.getString(0));
subscription.setUrl(cursor.getString(1)); subscription.setUrl(cursor.getString(1));
subscription.setNotificationsDisabled(cursor.getInt(2) == 1);
subscriptions.add(subscription); subscriptions.add(subscription);
} }
} finally { } finally {
@ -247,4 +348,112 @@ public class DatabaseHelper extends SQLiteOpenHelper {
return subscriptions; return subscriptions;
} }
public static void createOrUpdateNotification(LbryNotification notification, SQLiteDatabase db) {
db.execSQL(SQL_INSERT_NOTIFICATION, new Object[] {
notification.getRemoteId(),
notification.getAuthorUrl(),
notification.getTitle(),
notification.getDescription(),
notification.getRule(),
notification.getTargetUrl(),
notification.isRead() ? 1 : 0,
notification.isSeen() ? 1 : 0,
new SimpleDateFormat(Helper.ISO_DATE_FORMAT_PATTERN).format(notification.getTimestamp() != null ? notification.getTimestamp() : new Date())
});
}
public static List<LbryNotification> getNotifications(SQLiteDatabase db) {
List<LbryNotification> notifications = new ArrayList<>();
Cursor cursor = null;
try {
cursor = db.rawQuery(SQL_GET_NOTIFICATIONS, null);
while (cursor.moveToNext()) {
LbryNotification notification = new LbryNotification();
int columnIndex = 0;
notification.setId(cursor.getLong(columnIndex++));
notification.setRemoteId(cursor.getLong(columnIndex++));
notification.setAuthorUrl(cursor.getString(columnIndex++));
notification.setTitle(cursor.getString(columnIndex++));
notification.setDescription(cursor.getString(columnIndex++));
notification.setRule(cursor.getString(columnIndex++));
notification.setTargetUrl(cursor.getString(columnIndex++));
notification.setRead(cursor.getInt(columnIndex++) == 1);
notification.setSeen(cursor.getInt(columnIndex++) == 1);
try {
notification.setTimestamp(new SimpleDateFormat(Helper.ISO_DATE_FORMAT_PATTERN).parse(cursor.getString(columnIndex++)));
} catch (ParseException ex) {
// invalid timestamp (which shouldn't happen). Skip this item
continue;
}
notifications.add(notification);
}
} finally {
Helper.closeCursor(cursor);
}
return notifications;
}
public static void deleteNotifications(List<LbryNotification> notifications, SQLiteDatabase db) {
StringBuilder sb = new StringBuilder("DELETE FROM notifications WHERE remote_id IN (");
List<Object> remoteIds = new ArrayList<>();
String delim = "";
for (int i = 0; i < notifications.size(); i++) {
remoteIds.add(String.valueOf(notifications.get(i).getRemoteId()));
sb.append(delim).append("?");
delim = ",";
}
sb.append(")");
String sql = sb.toString();
db.execSQL(sql, remoteIds.toArray());
}
public static int getUnreadNotificationsCount(SQLiteDatabase db) {
int count = 0;
Cursor cursor = null;
try {
cursor = db.rawQuery(SQL_GET_UNREAD_NOTIFICATIONS_COUNT, null);
if (cursor.moveToNext()) {
count = cursor.getInt(0);
}
} finally {
Helper.closeCursor(cursor);
}
return count;
}
public static int getUnseenNotificationsCount(SQLiteDatabase db) {
int count = 0;
Cursor cursor = null;
try {
cursor = db.rawQuery(SQL_GET_UNSEEN_NOTIFICATIONS_COUNT, null);
if (cursor.moveToNext()) {
count = cursor.getInt(0);
}
} finally {
Helper.closeCursor(cursor);
}
return count;
}
public static void markNotificationsSeen(SQLiteDatabase db) {
db.execSQL(SQL_MARK_NOTIFICATIONS_SEEN);
}
public static void markNotificationsRead(SQLiteDatabase db) {
db.execSQL(SQL_MARK_NOTIFICATIONS_READ);
}
public static void markNotificationReadAndSeen(long notificationId, SQLiteDatabase db) {
db.execSQL(SQL_MARK_NOTIFICATION_READ_AND_SEEN, new Object[] { notificationId });
}
public static void createOrUpdateShuffleWatched(String claimId, SQLiteDatabase db) {
db.execSQL(SQL_INSERT_SHUFFLE_WATCHED, new Object[] { claimId });
}
public static List<String> getShuffleWatchedClaims(SQLiteDatabase db) {
List<String> claimIds = new ArrayList<>();
Cursor cursor = null;
try {
cursor = db.rawQuery(SQL_GET_SHUFFLE_WATCHED_CLAIMS, null);
while (cursor.moveToNext()) {
claimIds.add(cursor.getString(0));
}
} finally {
Helper.closeCursor(cursor);
}
return claimIds;
}
} }

View file

@ -67,8 +67,8 @@ public class ContentFromDialogFragment extends BottomSheetDialogFragment {
R.id.content_from_past_year_item, R.id.content_from_past_year_item,
R.id.content_from_all_time_item R.id.content_from_all_time_item
}; };
private BottomSheetDialogFragment dialog; private final BottomSheetDialogFragment dialog;
private ContentFromListener listener; private final ContentFromListener listener;
public ContentFromItemClickListener(BottomSheetDialogFragment dialog, ContentFromListener listener) { public ContentFromItemClickListener(BottomSheetDialogFragment dialog, ContentFromListener listener) {
this.dialog = dialog; this.dialog = dialog;

View file

@ -54,8 +54,8 @@ public class ContentScopeDialogFragment extends BottomSheetDialogFragment {
private final int[] checkViewIds = { private final int[] checkViewIds = {
R.id.content_scope_everyone_item_selected, R.id.content_scope_tags_item_selected R.id.content_scope_everyone_item_selected, R.id.content_scope_tags_item_selected
}; };
private BottomSheetDialogFragment dialog; private final BottomSheetDialogFragment dialog;
private ContentScopeListener listener; private final ContentScopeListener listener;
public ContentScopeItemClickListener(BottomSheetDialogFragment dialog, ContentScopeListener listener) { public ContentScopeItemClickListener(BottomSheetDialogFragment dialog, ContentScopeListener listener) {
this.dialog = dialog; this.dialog = dialog;

View file

@ -57,8 +57,8 @@ public class ContentSortDialogFragment extends BottomSheetDialogFragment {
private final int[] checkViewIds = { private final int[] checkViewIds = {
R.id.sort_by_trending_item_selected, R.id.sort_by_new_item_selected, R.id.sort_by_top_item_selected R.id.sort_by_trending_item_selected, R.id.sort_by_new_item_selected, R.id.sort_by_top_item_selected
}; };
private BottomSheetDialogFragment dialog; private final BottomSheetDialogFragment dialog;
private SortByListener listener; private final SortByListener listener;
public SortByItemClickListener(BottomSheetDialogFragment dialog, SortByListener listener) { public SortByItemClickListener(BottomSheetDialogFragment dialog, SortByListener listener) {
this.dialog = dialog; this.dialog = dialog;

View file

@ -0,0 +1,349 @@
package io.lbry.browser.dialog;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.widget.AppCompatSpinner;
import androidx.core.text.HtmlCompat;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.switchmaterial.SwitchMaterial;
import com.google.android.material.textfield.TextInputEditText;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import io.lbry.browser.MainActivity;
import io.lbry.browser.R;
import io.lbry.browser.adapter.InlineChannelSpinnerAdapter;
import io.lbry.browser.listener.WalletBalanceListener;
import io.lbry.browser.model.Claim;
import io.lbry.browser.model.WalletBalance;
import io.lbry.browser.tasks.GenericTaskHandler;
import io.lbry.browser.tasks.claim.ClaimListResultHandler;
import io.lbry.browser.tasks.claim.ClaimListTask;
import io.lbry.browser.tasks.wallet.SupportCreateTask;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry;
public class CreateSupportDialogFragment extends BottomSheetDialogFragment implements WalletBalanceListener {
public static final String TAG = "CreateSupportDialog";
private MaterialButton sendButton;
private View cancelLink;
private TextInputEditText inputAmount;
private View inlineBalanceContainer;
private TextView inlineBalanceValue;
private ProgressBar sendProgress;
private InlineChannelSpinnerAdapter channelSpinnerAdapter;
private AppCompatSpinner channelSpinner;
private SwitchMaterial switchTip;
private boolean fetchingChannels;
private ProgressBar progressLoadingChannels;
private final CreateSupportListener listener;
private final Claim claim;
private CreateSupportDialogFragment(Claim claim, CreateSupportListener listener) {
super();
this.claim = claim;
this.listener = listener;
}
public static CreateSupportDialogFragment newInstance(Claim claim, CreateSupportListener listener) {
return new CreateSupportDialogFragment(claim, listener);
}
private void disableControls() {
Dialog dialog = getDialog();
if (dialog != null) {
dialog.setCanceledOnTouchOutside(false);
}
channelSpinner.setEnabled(false);
switchTip.setEnabled(false);
sendButton.setEnabled(false);
cancelLink.setEnabled(false);
}
private void enableControls() {
Dialog dialog = getDialog();
if (dialog != null) {
dialog.setCanceledOnTouchOutside(true);
}
channelSpinner.setEnabled(true);
switchTip.setEnabled(true);
sendButton.setEnabled(true);
cancelLink.setEnabled(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog_create_support, container, false);
inputAmount = view.findViewById(R.id.create_support_input_amount);
inlineBalanceContainer = view.findViewById(R.id.create_support_inline_balance_container);
inlineBalanceValue = view.findViewById(R.id.create_support_inline_balance_value);
sendProgress = view.findViewById(R.id.create_support_progress);
cancelLink = view.findViewById(R.id.create_support_cancel_link);
sendButton = view.findViewById(R.id.create_support_send);
channelSpinner = view.findViewById(R.id.create_support_channel_spinner);
switchTip = view.findViewById(R.id.create_support_make_tip_switch);
progressLoadingChannels = view.findViewById(R.id.create_support_channel_progress);
inputAmount.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean hasFocus) {
inputAmount.setHint(hasFocus ? getString(R.string.zero) : "");
inlineBalanceContainer.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
}
});
inputAmount.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
updateSendButtonText();
}
@Override
public void afterTextChanged(Editable editable) {
}
});
updateInfoText();
updateSendButtonText();
String channel = null;
if (Claim.TYPE_CHANNEL.equalsIgnoreCase(claim.getValueType())) {
channel = claim.getTitleOrName();
} else if (claim.getSigningChannel() != null) {
channel = claim.getPublisherTitle();
}
TextView titleView = view.findViewById(R.id.create_support_title);
String tipTitleText = Helper.isNullOrEmpty(channel) ? getString(R.string.send_a_tip) : getString(R.string.send_a_tip_to, channel);
titleView.setText(tipTitleText);
switchTip.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
if (checked) {
// show tip info
titleView.setText(tipTitleText);
updateSendButtonText();
} else {
// show support info
titleView.setText(R.string.support_this_content);
sendButton.setText(R.string.send_revocable_support);
}
updateInfoText();
}
});
sendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String amountString = Helper.getValue(inputAmount.getText());
if (Helper.isNullOrEmpty(amountString)) {
showError(getString(R.string.invalid_amount));
return;
}
BigDecimal amount = new BigDecimal(amountString);
if (amount.doubleValue() > Lbry.walletBalance.getAvailable().doubleValue()) {
showError(getString(R.string.insufficient_balance));
return;
}
if (amount.doubleValue() < Helper.MIN_SPEND) {
showError(getString(R.string.min_spend_required));
return;
}
Claim selectedChannel = (Claim) channelSpinner.getSelectedItem();
String channelId = !fetchingChannels && selectedChannel != null ? selectedChannel.getClaimId() : null;
boolean isTip = switchTip.isChecked();
SupportCreateTask task = new SupportCreateTask(
claim.getClaimId(), channelId, amount, isTip, sendProgress, new GenericTaskHandler() {
@Override
public void beforeStart() {
disableControls();
}
@Override
public void onSuccess() {
enableControls();
if (listener != null) {
listener.onSupportCreated(amount, isTip);
}
dismiss();
}
@Override
public void onError(Exception error) {
showError(error.getMessage());
enableControls();
}
});
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
cancelLink.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dismiss();
}
});
onWalletBalanceUpdated(Lbry.walletBalance);
updateInfoText();
return view;
}
private void updateSendButtonText() {
boolean isTip = switchTip.isChecked();
if (!isTip) {
sendButton.setText(R.string.send_revocable_support);
} else {
String amountString = Helper.getValue(inputAmount.getText(), "0");
double parsedAmount = Helper.parseDouble(amountString, 0);
String text = getResources().getQuantityString(R.plurals.send_lbc_tip, parsedAmount == 1.0 ? 1 : 2, amountString);
sendButton.setText(text);
}
}
private void updateInfoText() {
View view = getView();
if (view != null && switchTip != null) {
TextView infoText = view.findViewById(R.id.create_support_info);
boolean isTip = switchTip.isChecked();
infoText.setMovementMethod(LinkMovementMethod.getInstance());
if (!isTip) {
infoText.setText(HtmlCompat.fromHtml(getString(R.string.support_info), HtmlCompat.FROM_HTML_MODE_LEGACY));
} else if (claim != null) {
infoText.setText(HtmlCompat.fromHtml(
Claim.TYPE_CHANNEL.equalsIgnoreCase(claim.getValueType()) ?
getString(R.string.send_tip_info_channel, claim.getTitleOrName()) :
getString(R.string.send_tip_info_content, claim.getTitleOrName()),
HtmlCompat.FROM_HTML_MODE_LEGACY));
}
}
}
private void fetchChannels() {
if (Lbry.ownChannels != null && Lbry.ownChannels.size() > 0) {
updateChannelList(Lbry.ownChannels);
return;
}
fetchingChannels = true;
disableChannelSpinner();
ClaimListTask task = new ClaimListTask(Claim.TYPE_CHANNEL, progressLoadingChannels, new ClaimListResultHandler() {
@Override
public void onSuccess(List<Claim> claims) {
Lbry.ownChannels = new ArrayList<>(claims);
updateChannelList(Lbry.ownChannels);
enableChannelSpinner();
fetchingChannels = false;
}
@Override
public void onError(Exception error) {
enableChannelSpinner();
fetchingChannels = false;
}
});
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void disableChannelSpinner() {
Helper.setViewEnabled(channelSpinner, false);
}
private void enableChannelSpinner() {
Helper.setViewEnabled(channelSpinner, true);
}
private void updateChannelList(List<Claim> channels) {
if (channelSpinnerAdapter == null) {
Context context = getContext();
if (context != null) {
channelSpinnerAdapter = new InlineChannelSpinnerAdapter(context, R.layout.spinner_item_channel, new ArrayList<>(channels));
channelSpinnerAdapter.addAnonymousPlaceholder();
channelSpinnerAdapter.notifyDataSetChanged();
}
} else {
channelSpinnerAdapter.clear();
channelSpinnerAdapter.addAll(channels);
channelSpinnerAdapter.addAnonymousPlaceholder();
channelSpinnerAdapter.notifyDataSetChanged();
}
if (channelSpinner != null) {
channelSpinner.setAdapter(channelSpinnerAdapter);
}
if (channelSpinnerAdapter != null && channelSpinner != null) {
if (channelSpinnerAdapter.getCount() > 1) {
channelSpinner.setSelection(1);
}
}
}
public void onResume() {
super.onResume();
Context context = getContext();
if (context instanceof MainActivity) {
((MainActivity) context).addWalletBalanceListener(this);
}
updateInfoText();
fetchChannels();
}
public void onPause() {
Context context = getContext();
if (context instanceof MainActivity) {
((MainActivity) context).removeWalletBalanceListener(this);
}
super.onPause();
}
@Override
public void onWalletBalanceUpdated(WalletBalance walletBalance) {
if (walletBalance != null && inlineBalanceValue != null) {
inlineBalanceValue.setText(Helper.shortCurrencyFormat(walletBalance.getAvailable().doubleValue()));
}
}
private void showError(String message) {
Snackbar.make(getView(), message, Snackbar.LENGTH_LONG).
setBackgroundTint(Color.RED).
setTextColor(Color.WHITE).
show();
}
public interface CreateSupportListener {
void onSupportCreated(BigDecimal amount, boolean isTip);
}
}

View file

@ -142,7 +142,7 @@ public class CustomizeTagsDialogFragment extends BottomSheetDialogFragment {
return view; return view;
} }
private TagListAdapter.TagClickListener customizeTagClickListener = new TagListAdapter.TagClickListener() { private final TagListAdapter.TagClickListener customizeTagClickListener = new TagListAdapter.TagClickListener() {
@Override @Override
public void onTagClicked(Tag tag, int customizeMode) { public void onTagClicked(Tag tag, int customizeMode) {
if (customizeMode == TagListAdapter.CUSTOMIZE_MODE_ADD) { if (customizeMode == TagListAdapter.CUSTOMIZE_MODE_ADD) {

View file

@ -5,7 +5,6 @@ import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ProgressBar;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;

View file

@ -37,7 +37,6 @@ import io.lbry.browser.tasks.claim.StreamRepostTask;
import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
import io.lbry.browser.utils.LbryUri; import io.lbry.browser.utils.LbryUri;
import lombok.Setter;
public class RepostClaimDialogFragment extends BottomSheetDialogFragment implements WalletBalanceListener { public class RepostClaimDialogFragment extends BottomSheetDialogFragment implements WalletBalanceListener {
public static final String TAG = "RepostClaimDialog"; public static final String TAG = "RepostClaimDialog";
@ -57,13 +56,17 @@ public class RepostClaimDialogFragment extends BottomSheetDialogFragment impleme
private TextView linkToggleAdvanced; private TextView linkToggleAdvanced;
private View advancedContainer; private View advancedContainer;
@Setter private final RepostClaimListener listener;
private RepostClaimListener listener; private final Claim claim;
@Setter
private Claim claim;
public static RepostClaimDialogFragment newInstance() { private RepostClaimDialogFragment(Claim claim, RepostClaimListener listener) {
return new RepostClaimDialogFragment(); super();
this.listener = listener;
this.claim = claim;
}
public static RepostClaimDialogFragment newInstance(Claim claim, RepostClaimListener listener) {
return new RepostClaimDialogFragment(claim, listener);
} }
@Override @Override
@ -192,14 +195,16 @@ public class RepostClaimDialogFragment extends BottomSheetDialogFragment impleme
private void loadChannels(List<Claim> channels) { private void loadChannels(List<Claim> channels) {
if (channelSpinnerAdapter == null) { if (channelSpinnerAdapter == null) {
Context context = getContext(); Context context = getContext();
if (context != null) {
channelSpinnerAdapter = new InlineChannelSpinnerAdapter(context, R.layout.spinner_item_channel, channels); channelSpinnerAdapter = new InlineChannelSpinnerAdapter(context, R.layout.spinner_item_channel, channels);
channelSpinnerAdapter.notifyDataSetChanged(); channelSpinnerAdapter.notifyDataSetChanged();
}
} else { } else {
channelSpinnerAdapter.clear(); channelSpinnerAdapter.clear();
channelSpinnerAdapter.addAll(channels); channelSpinnerAdapter.addAll(channels);
channelSpinnerAdapter.notifyDataSetChanged(); channelSpinnerAdapter.notifyDataSetChanged();
} }
if (channelSpinner != null) { if (channelSpinner != null && channelSpinnerAdapter != null) {
channelSpinner.setAdapter(channelSpinnerAdapter); channelSpinner.setAdapter(channelSpinnerAdapter);
} }
} }
@ -229,8 +234,18 @@ public class RepostClaimDialogFragment extends BottomSheetDialogFragment impleme
showError(getString(R.string.insufficient_balance)); showError(getString(R.string.insufficient_balance));
return; return;
} }
if (bid.doubleValue() < Helper.MIN_DEPOSIT) {
String message = getResources().getQuantityString(R.plurals.min_deposit_required, 2, String.valueOf(Helper.MIN_DEPOSIT));
showError(message);
return;
}
Claim channel = (Claim) channelSpinner.getSelectedItem(); Claim channel = (Claim) channelSpinner.getSelectedItem();
if (channel == null) {
showError(getString(R.string.please_select_repost_channel));
return;
}
StreamRepostTask task = new StreamRepostTask(name, bid, claim.getClaimId(), channel.getClaimId(), repostProgress, new ClaimResultHandler() { StreamRepostTask task = new StreamRepostTask(name, bid, claim.getClaimId(), channel.getClaimId(), repostProgress, new ClaimResultHandler() {
@Override @Override
public void beforeStart() { public void beforeStart() {
@ -256,11 +271,14 @@ public class RepostClaimDialogFragment extends BottomSheetDialogFragment impleme
} }
private void showError(String message) { private void showError(String message) {
Snackbar.make(getView(), message, Snackbar.LENGTH_LONG). View view = getView();
if (view != null && !Helper.isNullOrEmpty(message)) {
Snackbar.make(view, message, Snackbar.LENGTH_LONG).
setBackgroundTint(Color.RED). setBackgroundTint(Color.RED).
setTextColor(Color.WHITE). setTextColor(Color.WHITE).
show(); show();
} }
}
private void startLoading() { private void startLoading() {
Dialog dialog = getDialog(); Dialog dialog = getDialog();

View file

@ -1,196 +0,0 @@
package io.lbry.browser.dialog;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import androidx.core.text.HtmlCompat;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.textfield.TextInputEditText;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import io.lbry.browser.MainActivity;
import io.lbry.browser.R;
import io.lbry.browser.listener.WalletBalanceListener;
import io.lbry.browser.model.Claim;
import io.lbry.browser.model.WalletBalance;
import io.lbry.browser.tasks.GenericTaskHandler;
import io.lbry.browser.tasks.wallet.SupportCreateTask;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry;
import lombok.Setter;
public class SendTipDialogFragment extends BottomSheetDialogFragment implements WalletBalanceListener {
public static final String TAG = "SendTipDialog";
private MaterialButton sendButton;
private View cancelLink;
private TextInputEditText inputAmount;
private View inlineBalanceContainer;
private TextView inlineBalanceValue;
private ProgressBar sendProgress;
@Setter
private SendTipListener listener;
@Setter
private Claim claim;
public static SendTipDialogFragment newInstance() {
return new SendTipDialogFragment();
}
private void disableControls() {
Dialog dialog = getDialog();
if (dialog != null) {
dialog.setCanceledOnTouchOutside(false);
}
sendButton.setEnabled(false);
cancelLink.setEnabled(false);
}
private void enableControls() {
Dialog dialog = getDialog();
if (dialog != null) {
dialog.setCanceledOnTouchOutside(true);
}
sendButton.setEnabled(true);
cancelLink.setEnabled(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog_send_tip, container, false);
inputAmount = view.findViewById(R.id.tip_input_amount);
inlineBalanceContainer = view.findViewById(R.id.tip_inline_balance_container);
inlineBalanceValue = view.findViewById(R.id.tip_inline_balance_value);
sendProgress = view.findViewById(R.id.tip_send_progress);
cancelLink = view.findViewById(R.id.tip_cancel_link);
sendButton = view.findViewById(R.id.tip_send);
inputAmount.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean hasFocus) {
inputAmount.setHint(hasFocus ? getString(R.string.zero) : "");
inlineBalanceContainer.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
}
});
TextView infoText = view.findViewById(R.id.tip_info);
infoText.setMovementMethod(LinkMovementMethod.getInstance());
infoText.setText(HtmlCompat.fromHtml(
Claim.TYPE_CHANNEL.equalsIgnoreCase(claim.getValueType()) ?
getString(R.string.send_tip_info_channel, claim.getTitleOrName()) :
getString(R.string.send_tip_info_content, claim.getTitleOrName()),
HtmlCompat.FROM_HTML_MODE_LEGACY));
sendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String amountString = Helper.getValue(inputAmount.getText());
if (Helper.isNullOrEmpty(amountString)) {
showError(getString(R.string.invalid_amount));
return;
}
BigDecimal amount = new BigDecimal(amountString);
if (amount.doubleValue() > Lbry.walletBalance.getAvailable().doubleValue()) {
showError(getString(R.string.insufficient_balance));
return;
}
SupportCreateTask task = new SupportCreateTask(claim.getClaimId(), amount, true, sendProgress, new GenericTaskHandler() {
@Override
public void beforeStart() {
disableControls();
}
@Override
public void onSuccess() {
enableControls();
if (listener != null) {
listener.onTipSent(amount);
}
dismiss();
}
@Override
public void onError(Exception error) {
showError(error.getMessage());
enableControls();
}
});
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
cancelLink.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dismiss();
}
});
String channel = null;
if (Claim.TYPE_CHANNEL.equalsIgnoreCase(claim.getValueType())) {
channel = claim.getTitleOrName();
} else if (claim.getSigningChannel() != null) {
channel = claim.getPublisherTitle();
}
((TextView) view.findViewById(R.id.tip_send_title)).setText(
Helper.isNullOrEmpty(channel) ? getString(R.string.send_a_tip) : getString(R.string.send_a_tip_to, channel)
);
onWalletBalanceUpdated(Lbry.walletBalance);
return view;
}
public void onResume() {
super.onResume();
Context context = getContext();
if (context instanceof MainActivity) {
((MainActivity) context).addWalletBalanceListener(this);
}
}
public void onPause() {
Context context = getContext();
if (context instanceof MainActivity) {
((MainActivity) context).removeWalletBalanceListener(this);
}
super.onPause();
}
@Override
public void onWalletBalanceUpdated(WalletBalance walletBalance) {
if (walletBalance != null && inlineBalanceValue != null) {
inlineBalanceValue.setText(Helper.shortCurrencyFormat(walletBalance.getAvailable().doubleValue()));
}
}
private void showError(String message) {
Snackbar.make(getView(), message, Snackbar.LENGTH_LONG).
setBackgroundTint(Color.RED).
setTextColor(Color.WHITE).
show();
}
public interface SendTipListener {
void onTipSent(BigDecimal amount);
}
}

View file

@ -0,0 +1,4 @@
package io.lbry.browser.exceptions;
public class AuthTokenInvalidatedException extends Exception {
}

View file

@ -0,0 +1,6 @@
package io.lbry.browser.listener;
public interface PIPModeListener {
void onEnterPIPMode();
void onExitPIPMode();
}

View file

@ -7,4 +7,7 @@ public interface SignInListener {
void onPhoneAdded(String countryCode, String phoneNumber); void onPhoneAdded(String countryCode, String phoneNumber);
void onPhoneVerified(); void onPhoneVerified();
void onManualVerifyContinue(); void onManualVerifyContinue();
void onSkipQueueAction();
void onTwitterVerified();
void onManualProgress(boolean progress);
} }

View file

@ -1,5 +1,7 @@
package io.lbry.browser.model; package io.lbry.browser.model;
import androidx.annotation.Nullable;
import com.google.gson.FieldNamingPolicy; import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
@ -15,7 +17,6 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Stream;
import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.LbryUri; import io.lbry.browser.utils.LbryUri;
@ -54,6 +55,7 @@ public class Claim {
@EqualsAndHashCode.Include @EqualsAndHashCode.Include
private boolean placeholder; private boolean placeholder;
private boolean placeholderAnonymous; private boolean placeholderAnonymous;
private boolean loadingPlaceholder;
private boolean featured; private boolean featured;
private boolean unresolved; // used for featured private boolean unresolved; // used for featured
private String address; private String address;
@ -137,6 +139,14 @@ public class Claim {
return null; return null;
} }
public boolean hasSource() {
if (value instanceof StreamMetadata) {
StreamMetadata metadata = (StreamMetadata) value;
return metadata.getSource() != null;
}
return false;
}
public boolean isPlayable() { public boolean isPlayable() {
if (value instanceof StreamMetadata) { if (value instanceof StreamMetadata) {
StreamMetadata metadata = (StreamMetadata) value; StreamMetadata metadata = (StreamMetadata) value;
@ -176,6 +186,21 @@ public class Claim {
return null; return null;
} }
/**
* Gets the URL from the CDN where getting the image file
* @param width Pass zero for width and height for the full size image file
* @param height Pass zero for width and height for the full size image file
* @param q Desired quality for the image to be retrieved
* @return URL from the CDN from where image can be retrieved
*/
public String getThumbnailUrl(int width, int height, int q) {
if (value != null && value.getThumbnail() != null) {
ImageCDNUrl imageCDNUrl = new ImageCDNUrl(Math.max(width, 0), Math.max(height, 0), q, null, value.getThumbnail().getUrl());
return imageCDNUrl.toString();
}
return null;
}
public String getCoverUrl() { public String getCoverUrl() {
if (TYPE_CHANNEL.equals(valueType) && value != null && value instanceof ChannelMetadata && ((ChannelMetadata) value).getCover() != null) { if (TYPE_CHANNEL.equals(valueType) && value != null && value instanceof ChannelMetadata && ((ChannelMetadata) value).getCover() != null) {
return ((ChannelMetadata) value).getCover().getUrl(); return ((ChannelMetadata) value).getCover().getUrl();
@ -289,6 +314,9 @@ public class Claim {
Claim signingChannel = new Claim(); Claim signingChannel = new Claim();
signingChannel.setClaimId(viewHistory.getPublisherClaimId()); signingChannel.setClaimId(viewHistory.getPublisherClaimId());
signingChannel.setName(viewHistory.getPublisherName()); signingChannel.setName(viewHistory.getPublisherName());
LbryUri channelUrl = LbryUri.tryParse(String.format("%s#%s", signingChannel.getName(), signingChannel.getClaimId()));
signingChannel.setPermanentUrl(channelUrl != null ? channelUrl.toString() : null);
if (!Helper.isNullOrEmpty(viewHistory.getPublisherTitle())) { if (!Helper.isNullOrEmpty(viewHistory.getPublisherTitle())) {
GenericMetadata channelValue = new GenericMetadata(); GenericMetadata channelValue = new GenericMetadata();
channelValue.setTitle(viewHistory.getPublisherTitle()); channelValue.setTitle(viewHistory.getPublisherTitle());
@ -480,6 +508,30 @@ public class Claim {
private String url; private String url;
} }
/**
* Object to be instantiated. In order to get the URLto the CDN, call toString() on it
*/
static class ImageCDNUrl {
private String appendedPath = "";
public ImageCDNUrl(int width, int height, int quality, @Nullable String format, String thumbnailUrl) {
if (width != 0 && height != 0)
appendedPath = "s:".concat(String.valueOf(width)).concat(":").concat(String.valueOf(height)).concat("/");
appendedPath = appendedPath.concat("quality:").concat(String.valueOf(quality)).concat("/");
appendedPath = appendedPath.concat("plain/").concat(thumbnailUrl);
if (format != null)
appendedPath = appendedPath.concat("@").concat(format);
}
@Override
public String toString() {
String url = "https://image-processor.vanwanet.com/optimize/";
return url.concat(appendedPath);
}
}
@Data @Data
public static class StreamInfo { public static class StreamInfo {
private long duration; // video / audio private long duration; // video / audio

View file

@ -40,7 +40,7 @@ public class ClaimCacheKey {
@Override @Override
public boolean equals(@Nullable Object obj) { public boolean equals(@Nullable Object obj) {
if (obj == null || !(obj instanceof ClaimCacheKey)) { if (!(obj instanceof ClaimCacheKey)) {
return false; return false;
} }
ClaimCacheKey key = (ClaimCacheKey) obj; ClaimCacheKey key = (ClaimCacheKey) obj;

View file

@ -1,19 +1,15 @@
package io.lbry.browser.model; package io.lbry.browser.model;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
public class ClaimSearchCacheValue { public class ClaimSearchCacheValue {
@Getter @Getter
@Setter private final List<Claim> claims;
private List<Claim> claims;
@Getter @Getter
@Setter private final long timestamp;
private long timestamp;
public ClaimSearchCacheValue(List<Claim> claims, long timestamp) { public ClaimSearchCacheValue(List<Claim> claims, long timestamp) {
this.claims = new ArrayList<>(claims); this.claims = new ArrayList<>(claims);

View file

@ -0,0 +1,72 @@
package io.lbry.browser.model;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import io.lbry.browser.utils.Helper;
import lombok.Data;
@Data
public class Comment implements Comparable<Comment> {
public static final int MAX_LENGTH = 2000;
private Claim poster;
private String claimId;
private List<Comment> replies;
private long timestamp;
private String channelId;
private String channelName, text, id, parentId;
public Comment(String channelId, String channelName, String text, String id, String parentId) {
this.channelId = channelId;
this.channelName = channelName;
this.text = text;
this.id = id;
this.parentId = parentId;
this.replies = new ArrayList<>();
}
public Comment() {
replies = new ArrayList<>();
}
public void addReply(Comment reply) {
if (replies == null) {
replies = new ArrayList<>();
}
if (!replies.contains(reply)) {
replies.add(reply);
}
}
public static Comment fromJSONObject(JSONObject jsonObject) {
try {
String parentId = null;
if (jsonObject.has("parent_id")) {
parentId = jsonObject.getString("parent_id");
}
Comment comment = new Comment(
Helper.getJSONString("channel_id", null, jsonObject),
jsonObject.getString("channel_name"),
jsonObject.getString("comment"),
jsonObject.getString("comment_id"),
parentId
);
comment.setClaimId(Helper.getJSONString("claim_id", null, jsonObject));
comment.setTimestamp(Helper.getJSONLong("timestamp", 0, jsonObject));
return comment;
} catch (JSONException ex) {
return null;
}
}
@Override
public int compareTo(Comment comment) {
return (int)(this.getTimestamp() - comment.getTimestamp());
}
}

View file

@ -4,9 +4,9 @@ import lombok.Data;
@Data @Data
public class Language { public class Language {
private String code; private final String code;
private String name; private final String name;
private int stringResourceId; private final int stringResourceId;
public Language(String code, String name, int stringResourceId) { public Language(String code, String name, int stringResourceId) {
this.code = code; this.code = code;

View file

@ -4,9 +4,9 @@ import lombok.Data;
@Data @Data
public class License { public class License {
private String name; private final String name;
private String url; private String url;
private int stringResourceId; private final int stringResourceId;
public License(String name, int stringResourceId) { public License(String name, int stringResourceId) {
this.name = name; this.name = name;

View file

@ -2,8 +2,6 @@ package io.lbry.browser.model;
import android.content.Context; import android.content.Context;
import androidx.core.content.res.ResourcesCompat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -20,6 +18,7 @@ public class NavMenuItem {
public static final int ID_ITEM_FOLLOWING = 101; public static final int ID_ITEM_FOLLOWING = 101;
public static final int ID_ITEM_EDITORS_CHOICE = 102; public static final int ID_ITEM_EDITORS_CHOICE = 102;
public static final int ID_ITEM_ALL_CONTENT = 103; public static final int ID_ITEM_ALL_CONTENT = 103;
public static final int ID_ITEM_SHUFFLE = 104;
// Your Content // Your Content
public static final int ID_ITEM_CHANNELS = 201; public static final int ID_ITEM_CHANNELS = 201;
@ -36,8 +35,8 @@ public class NavMenuItem {
public static final int ID_ITEM_SETTINGS = 401; public static final int ID_ITEM_SETTINGS = 401;
public static final int ID_ITEM_ABOUT = 402; public static final int ID_ITEM_ABOUT = 402;
private Context context; private final Context context;
private int id; private final int id;
private boolean group; private boolean group;
private int icon; private int icon;
private String title; private String title;

View file

@ -0,0 +1,11 @@
package io.lbry.browser.model;
public class StartupStage {
public final Integer stage;
public final Boolean stageDone;
public StartupStage(Integer stage, Boolean stageDone) {
this.stage = stage;
this.stageDone = stageDone;
}
}

View file

@ -5,7 +5,9 @@ import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List;
import io.lbry.browser.R; import io.lbry.browser.R;
import io.lbry.browser.exceptions.LbryUriException; import io.lbry.browser.exceptions.LbryUriException;
@ -53,13 +55,22 @@ public class Transaction {
int descStringId = -1; int descStringId = -1;
TransactionInfo info = null; TransactionInfo info = null;
List<TransactionInfo> infos = new ArrayList<>();
try { try {
if (jsonObject.has("abandon_info")) { if (jsonObject.has("abandon_info")) {
JSONArray array = jsonObject.getJSONArray("abandon_info"); JSONArray array = jsonObject.getJSONArray("abandon_info");
if (array.length() > 0) { if (array.length() > 0) {
if (array.length() == 1) {
info = TransactionInfo.fromJSONObject(array.getJSONObject(0)); info = TransactionInfo.fromJSONObject(array.getJSONObject(0));
descStringId = R.string.abandon; descStringId = info.getBalanceDelta().doubleValue() == info.getAmount().doubleValue() ? R.string.unlock : R.string.abandon;
transaction.setAbandonInfo(info); transaction.setAbandonInfo(info);
} else {
// multiple abandon infos (txo_spend unlock tip)
descStringId = R.string.unlock;
for (int i = 0; i < array.length(); i++) {
infos.add(TransactionInfo.fromJSONObject(array.getJSONObject(i)));
}
}
} }
} }
if (info == null && jsonObject.has("claim_info")) { if (info == null && jsonObject.has("claim_info")) {
@ -94,8 +105,18 @@ public class Transaction {
// pass // pass
} }
if (transaction.getValue().doubleValue() == 0 && info != null && info.getBalanceDelta().doubleValue() != 0) {
if (transaction.getValue().doubleValue() == 0) {
if (info != null && info.getBalanceDelta().doubleValue() != 0) {
transaction.setValue(info.getBalanceDelta()); transaction.setValue(info.getBalanceDelta());
} else if (infos.size() > 0) {
BigDecimal total = new BigDecimal(0);
for (TransactionInfo txInfo : infos) {
total = total.add(txInfo.getAmount());
}
transaction.setValue(total);
}
} }
if (descStringId == -1) { if (descStringId == -1) {

View file

@ -0,0 +1,9 @@
package io.lbry.browser.model;
import lombok.Data;
@Data
public class TwitterOauth {
private String oauthToken;
private String oauthTokenSecret;
}

View file

@ -3,9 +3,7 @@ package io.lbry.browser.model;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import io.lbry.browser.exceptions.LbryUriException;
import io.lbry.browser.utils.LbryUri; import io.lbry.browser.utils.LbryUri;
import io.lbry.browser.utils.Lbryio;
import lombok.Data; import lombok.Data;
@Data @Data

View file

@ -1,16 +1,33 @@
package io.lbry.browser.model; package io.lbry.browser.model;
import org.json.JSONObject;
import java.math.BigDecimal; import java.math.BigDecimal;
import io.lbry.browser.utils.Helper;
import lombok.Data; import lombok.Data;
import lombok.Getter;
import lombok.Setter;
@Data @Data
public class WalletBalance { public class WalletBalance {
@Setter
@Getter
private BigDecimal available; private BigDecimal available;
@Setter
@Getter
private BigDecimal reserved; private BigDecimal reserved;
@Setter
@Getter
private BigDecimal claims; private BigDecimal claims;
@Setter
@Getter
private BigDecimal supports; private BigDecimal supports;
@Setter
@Getter
private BigDecimal tips; private BigDecimal tips;
@Setter
@Getter
private BigDecimal total; private BigDecimal total;
public WalletBalance() { public WalletBalance() {
@ -21,4 +38,18 @@ public class WalletBalance {
tips = new BigDecimal(0); tips = new BigDecimal(0);
total = new BigDecimal(0); total = new BigDecimal(0);
} }
public static WalletBalance fromJSONObject(JSONObject json) {
WalletBalance balance = new WalletBalance();
JSONObject reservedSubtotals = Helper.getJSONObject("reserved_subtotals", json);
balance.setAvailable(new BigDecimal(Helper.getJSONString("available", "0", json)));
balance.setReserved(new BigDecimal(Helper.getJSONString("reserved", "0", json)));
balance.setTotal(new BigDecimal(Helper.getJSONString("total", "0", json)));
if (reservedSubtotals != null) {
balance.setClaims(new BigDecimal(Helper.getJSONString("claims", "0", reservedSubtotals)));
balance.setSupports(new BigDecimal(Helper.getJSONString("supports", "0", reservedSubtotals)));
balance.setTips(new BigDecimal(Helper.getJSONString("tips", "0", reservedSubtotals)));
}
return balance;
}
} }

View file

@ -0,0 +1,17 @@
package io.lbry.browser.model;
public class WalletDetailItem {
public String detail;
public String detailDesc;
public String detailAmount;
public boolean isUnlockable;
public boolean isInProgress;
public WalletDetailItem(String detail, String detailDesc, String detailAmount, boolean isUnlockable, boolean isInProgress) {
this.detail = detail;
this.detailDesc = detailDesc;
this.detailAmount = detailAmount;
this.isUnlockable = isUnlockable;
this.isInProgress = isInProgress;
}
}

View file

@ -1,11 +1,15 @@
package io.lbry.browser.model; package io.lbry.browser.model;
import lombok.Data; import lombok.Data;
import lombok.Getter;
@Data @Data
public class WalletSync { public class WalletSync {
private String hash; @Getter
private String data; private final String hash;
@Getter
private final String data;
@Getter
private boolean changed; private boolean changed;
public WalletSync(String hash, String data) { public WalletSync(String hash, String data) {
@ -17,4 +21,5 @@ public class WalletSync {
this(hash, data); this(hash, data);
this.changed = changed; this.changed = changed;
} }
} }

View file

@ -0,0 +1,40 @@
package io.lbry.browser.model.lbryinc;
import java.util.Comparator;
import java.util.Date;
import io.lbry.browser.model.Claim;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
public class LbryNotification implements Comparator<LbryNotification> {
private long id;
@EqualsAndHashCode.Include
private long remoteId;
private String title;
private String description;
private String thumbnailUrl;
private String rule;
private String targetUrl;
private boolean read;
private boolean seen;
private Date timestamp;
// only for comment notifications
private String authorUrl;
private Claim commentAuthor;
public int compare(LbryNotification a, LbryNotification b) {
long t1 = a.getTimestamp() != null ? a.getTimestamp().getTime() : 0;
long t2 = b.getTimestamp() != null ? b.getTimestamp().getTime() : 0;
if (t1 < t2) {
return -1;
}
if (t1 > t2) {
return 1;
}
return 0;
}
}

View file

@ -9,7 +9,6 @@ import org.json.JSONObject;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import io.lbry.browser.model.Claim;
import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Helper;
import lombok.Data; import lombok.Data;

View file

@ -0,0 +1,9 @@
package io.lbry.browser.model.lbryinc;
import lombok.Data;
@Data
public class RewardVerified {
private long userId;
private boolean isRewardApproved;
}

View file

@ -11,17 +11,21 @@ public class Subscription {
@Getter @Getter
@Setter @Setter
private String url; private String url;
@Getter
@Setter
private boolean isNotificationsDisabled;
public Subscription() { public Subscription() {
} }
public Subscription(String channelName, String url) { public Subscription(String channelName, String url, boolean isNotificationsDisabled) {
this.channelName = channelName; this.channelName = channelName;
this.url = url; this.url = url;
this.isNotificationsDisabled = isNotificationsDisabled;
} }
public static Subscription fromClaim(Claim claim) { public static Subscription fromClaim(Claim claim) {
return new Subscription(claim.getName(), claim.getPermanentUrl()); return new Subscription(claim.getName(), claim.getPermanentUrl(), false);
} }
public String toString() { public String toString() {
return url; return url;

View file

@ -0,0 +1,70 @@
package io.lbry.browser.tasks;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONObject;
import java.util.concurrent.TimeUnit;
import io.lbry.browser.utils.Helper;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class BufferEventTask extends AsyncTask<Void, Void, Void> {
private static final String TAG = "LbryBufferEvent";
private static final String ENDPOINT = "https://collector-service.api.lbry.tv/api/v1/events/video";
private final String streamUrl;
private final String userIdHash;
private final long streamDuration;
private final long streamPosition;
private final long bufferDuration;
public BufferEventTask(String streamUrl, long streamDuration, long streamPosition, long bufferDuration, String userIdHash) {
this.streamUrl = streamUrl;
this.bufferDuration = bufferDuration;
this.streamDuration = streamDuration;
this.streamPosition = streamPosition;
this.userIdHash = userIdHash;
}
protected Void doInBackground(Void... params) {
JSONObject requestBody = new JSONObject();
JSONObject data = new JSONObject();
try {
data.put("url", streamUrl);
data.put("position", streamPosition);
data.put("stream_duration", streamDuration);
//data.put("duration", bufferDuration);
requestBody.put("device", "android");
requestBody.put("type", "buffering");
requestBody.put("client", userIdHash);
requestBody.put("data", data);
RequestBody body = RequestBody.create(requestBody.toString(), Helper.JSON_MEDIA_TYPE);
Request request = new Request.Builder().url(ENDPOINT).post(body).build();
OkHttpClient client = new OkHttpClient.Builder().
writeTimeout(60, TimeUnit.SECONDS).
readTimeout(60, TimeUnit.SECONDS).
build();
Response response = client.newCall(request).execute();
ResponseBody resBody = response.body();
String responseString = "";
if (resBody != null) {
responseString = response.body().string();
}
Log.d(TAG, String.format("buffer event sent: %s", responseString));
} catch (Exception ex) {
// we don't want to fail if a buffer event fails to register
Log.d(TAG, String.format("buffer event log failed: %s", ex.getMessage()), ex);
}
return null;
}
}

View file

@ -0,0 +1,85 @@
package io.lbry.browser.tasks;
import android.os.AsyncTask;
import android.view.View;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.Objects;
import io.lbry.browser.exceptions.ApiCallException;
import io.lbry.browser.model.Comment;
import io.lbry.browser.utils.Comments;
import io.lbry.browser.utils.Helper;
import okhttp3.Response;
public class CommentCreateTask extends AsyncTask<Void, Void, Comment> {
private final Comment comment;
private final View progressView;
private final CommentCreateWithTipHandler handler;
private Exception error;
public CommentCreateTask(Comment comment, View progressView, CommentCreateWithTipHandler handler) {
this.comment = comment;
this.progressView = progressView;
this.handler = handler;
}
protected void onPreExecute() {
Helper.setViewVisibility(progressView, View.VISIBLE);
}
public Comment doInBackground(Void... params) {
Comment createdComment = null;
try {
// check comments status endpoint
Comments.checkCommentsEndpointStatus();
JSONObject comment_body = new JSONObject();
comment_body.put("comment", comment.getText());
comment_body.put("claim_id", comment.getClaimId());
if (!Helper.isNullOrEmpty(comment.getParentId())) {
comment_body.put("parent_id", comment.getParentId());
}
comment_body.put("channel_id", comment.getChannelId());
comment_body.put("channel_name", comment.getChannelName());
JSONObject jsonChannelSign = Comments.channelSign(comment_body, comment.getChannelId(), comment.getChannelName());
if (jsonChannelSign.has("signature") && jsonChannelSign.has("signing_ts")) {
comment_body.put("signature", jsonChannelSign.getString("signature"));
comment_body.put("signing_ts", jsonChannelSign.getString("signing_ts"));
}
Response resp = Comments.performRequest(comment_body, "comment.Create");
String responseString = Objects.requireNonNull(resp.body()).string();
resp.close();
JSONObject jsonResponse = new JSONObject(responseString);
if (jsonResponse.has("result"))
createdComment = Comment.fromJSONObject(jsonResponse.getJSONObject("result"));
} catch (ApiCallException | ClassCastException | IOException | JSONException ex) {
error = ex;
}
return createdComment;
}
protected void onPostExecute(Comment createdComment) {
Helper.setViewVisibility(progressView, View.GONE);
if (handler != null) {
if (createdComment != null) {
handler.onSuccess(createdComment);
} else {
handler.onError(error);
}
}
}
public interface CommentCreateWithTipHandler {
void onSuccess(Comment createdComment);
void onError(Exception error);
}
}

View file

@ -0,0 +1,10 @@
package io.lbry.browser.tasks;
import java.util.List;
import io.lbry.browser.model.Comment;
public interface CommentListHandler {
void onSuccess(List<Comment> comments, boolean hasReachedEnd);
void onError(Exception error);
}

View file

@ -0,0 +1,99 @@
package io.lbry.browser.tasks;
import android.os.AsyncTask;
import android.view.View;
import android.widget.ProgressBar;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.lbry.browser.model.Comment;
import io.lbry.browser.utils.Comments;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry;
public class CommentListTask extends AsyncTask<Void, Void, List<Comment>> {
private final int page;
private final int pageSize;
private final String claim;
private final ProgressBar progressBar;
private final CommentListHandler handler;
private Exception error;
public CommentListTask(int page, int pageSize, String claim, ProgressBar progressBar, CommentListHandler handler) {
this.page = page;
this.pageSize = pageSize;
this.claim = claim;
this.progressBar = progressBar;
this.handler = handler;
}
protected void onPreExecute() {
Helper.setViewVisibility(progressBar, View.VISIBLE);
}
protected List<Comment> doInBackground(Void... voids) {
List<Comment> comments = null;
try {
Map<String, Object> options = new HashMap<>();
options.put("claim_id", claim);
options.put("page", page);
options.put("page_size", pageSize);
options.put("hidden", false);
options.put("include_replies", false);
options.put("is_channel_signature_valid", true);
options.put("skip_validation", true);
options.put("visible", true);
JSONObject result = (JSONObject) Lbry.parseResponse(Comments.performRequest(Lbry.buildJsonParams(options), "comment.List"));
JSONArray items = result.getJSONArray("items");
List<Comment> children = new ArrayList<>();
comments = new ArrayList<>();
for (int i = 0; i < items.length(); i++) {
Comment comment = Comment.fromJSONObject(items.getJSONObject(i));
if (comment != null) {
if (!Helper.isNullOrEmpty(comment.getParentId())) {
children.add(comment);
} else {
comments.add(comment);
}
}
}
// Sort all replies from oldest to newest at once
Collections.sort(children);
for (Comment child : children) {
for (Comment parent : comments) {
if (parent.getId().equalsIgnoreCase(child.getParentId())) {
parent.addReply(child);
break;
}
}
}
} catch (Exception ex) {
error = ex;
}
return comments;
}
protected void onPostExecute(List<Comment> comments) {
Helper.setViewVisibility(progressBar, View.GONE);
if (handler != null) {
if (comments != null) {
handler.onSuccess(comments, comments.size() < pageSize);
} else {
handler.onError(error);
}
}
}
}

View file

@ -10,10 +10,10 @@ import io.lbry.browser.model.Tag;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
public class FollowUnfollowTagTask extends AsyncTask<Void, Void, Boolean> { public class FollowUnfollowTagTask extends AsyncTask<Void, Void, Boolean> {
private Tag tag; private final Tag tag;
private boolean unfollowing; private final boolean unfollowing;
private Context context; private final Context context;
private FollowUnfollowTagHandler handler; private final FollowUnfollowTagHandler handler;
private Exception error; private Exception error;
public FollowUnfollowTagTask(Tag tag, boolean unfollowing, Context context, FollowUnfollowTagHandler handler) { public FollowUnfollowTagTask(Tag tag, boolean unfollowing, Context context, FollowUnfollowTagHandler handler) {

View file

@ -12,9 +12,9 @@ import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lighthouse; import io.lbry.browser.utils.Lighthouse;
public class LighthouseAutoCompleteTask extends AsyncTask<Void, Void, List<UrlSuggestion>> { public class LighthouseAutoCompleteTask extends AsyncTask<Void, Void, List<UrlSuggestion>> {
private String text; private final String text;
private AutoCompleteResultHandler handler; private final AutoCompleteResultHandler handler;
private View progressView; private final View progressView;
private Exception error; private Exception error;
public LighthouseAutoCompleteTask(String text, View progressView, AutoCompleteResultHandler handler) { public LighthouseAutoCompleteTask(String text, View progressView, AutoCompleteResultHandler handler) {

View file

@ -14,13 +14,13 @@ import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lighthouse; import io.lbry.browser.utils.Lighthouse;
public class LighthouseSearchTask extends AsyncTask<Void, Void, List<Claim>> { public class LighthouseSearchTask extends AsyncTask<Void, Void, List<Claim>> {
private String rawQuery; private final String rawQuery;
private int size; private final int size;
private int from; private final int from;
private boolean nsfw; private final boolean nsfw;
private String relatedTo; private final String relatedTo;
private ClaimSearchResultHandler handler; private final ClaimSearchResultHandler handler;
private ProgressBar progressBar; private final ProgressBar progressBar;
private Exception error; private Exception error;
public LighthouseSearchTask(String rawQuery, int size, int from, boolean nsfw, String relatedTo, ProgressBar progressBar, ClaimSearchResultHandler handler) { public LighthouseSearchTask(String rawQuery, int size, int from, boolean nsfw, String relatedTo, ProgressBar progressBar, ClaimSearchResultHandler handler) {

View file

@ -4,22 +4,16 @@ import android.content.Context;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteException;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.view.View;
import android.widget.ProgressBar;
import java.util.List; import java.util.List;
import io.lbry.browser.MainActivity; import io.lbry.browser.MainActivity;
import io.lbry.browser.data.DatabaseHelper; import io.lbry.browser.data.DatabaseHelper;
import io.lbry.browser.exceptions.LbryRequestException;
import io.lbry.browser.exceptions.LbryResponseException;
import io.lbry.browser.model.Claim;
import io.lbry.browser.model.Tag; import io.lbry.browser.model.Tag;
import io.lbry.browser.utils.Helper;
public class LoadTagsTask extends AsyncTask<Void, Void, List<Tag>> { public class LoadTagsTask extends AsyncTask<Void, Void, List<Tag>> {
private Context context; private final Context context;
private LoadTagsHandler handler; private final LoadTagsHandler handler;
private Exception error; private Exception error;
public LoadTagsTask(Context context, LoadTagsHandler handler) { public LoadTagsTask(Context context, LoadTagsHandler handler) {

View file

@ -4,7 +4,6 @@ import android.content.Context;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteException;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
@ -17,26 +16,25 @@ import java.util.Map;
import io.lbry.browser.MainActivity; import io.lbry.browser.MainActivity;
import io.lbry.browser.data.DatabaseHelper; import io.lbry.browser.data.DatabaseHelper;
import io.lbry.browser.exceptions.LbryUriException;
import io.lbry.browser.exceptions.LbryioRequestException; import io.lbry.browser.exceptions.LbryioRequestException;
import io.lbry.browser.exceptions.LbryioResponseException; import io.lbry.browser.exceptions.LbryioResponseException;
import io.lbry.browser.model.lbryinc.Subscription; import io.lbry.browser.model.lbryinc.Subscription;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.LbryUri; import io.lbry.browser.utils.LbryUri;
import io.lbry.browser.utils.Lbryio; import io.lbry.browser.utils.Lbryio;
import okhttp3.Response;
// background task to create a diff of local and remote subscriptions and try to merge // background task to create a diff of local and remote subscriptions and try to merge
public class MergeSubscriptionsTask extends AsyncTask<Void, Void, List<Subscription>> { public class MergeSubscriptionsTask extends AsyncTask<Void, Void, List<Subscription>> {
private static final String TAG = "MergeSubscriptionsTask"; private static final String TAG = "MergeSubscriptionsTask";
private Context context; private final Context context;
private List<Subscription> base; private final List<Subscription> base;
private List<Subscription> diff; private List<Subscription> diff;
private MergeSubscriptionsHandler handler; private final MergeSubscriptionsHandler handler;
private Exception error; private Exception error;
private final boolean replaceLocal;
public MergeSubscriptionsTask(List<Subscription> base, Context context, MergeSubscriptionsHandler handler) { public MergeSubscriptionsTask(List<Subscription> base, boolean replaceLocal, Context context, MergeSubscriptionsHandler handler) {
this.base = base; this.base = base;
this.replaceLocal = replaceLocal;
this.context = context; this.context = context;
this.handler = handler; this.handler = handler;
} }
@ -53,6 +51,12 @@ public class MergeSubscriptionsTask extends AsyncTask<Void, Void, List<Subscript
db = ((MainActivity) context).getDbHelper().getWritableDatabase(); db = ((MainActivity) context).getDbHelper().getWritableDatabase();
} }
if (db != null) { if (db != null) {
if (replaceLocal) {
DatabaseHelper.clearSubscriptions(db);
for (Subscription sub : base) {
DatabaseHelper.createOrUpdateSubscription(sub, db);
}
} else {
localSubs = DatabaseHelper.getSubscriptions(db); localSubs = DatabaseHelper.getSubscriptions(db);
for (Subscription sub : localSubs) { for (Subscription sub : localSubs) {
if (!combined.contains(sub)) { if (!combined.contains(sub)) {
@ -60,48 +64,55 @@ public class MergeSubscriptionsTask extends AsyncTask<Void, Void, List<Subscript
} }
} }
} }
}
// fetch remote subscriptions // fetch remote subscriptions
JSONArray array = (JSONArray) Lbryio.parseResponse(Lbryio.call("subscription", "list", context)); JSONArray array = (JSONArray) Lbryio.parseResponse(Lbryio.call("subscription", "list", context));
if (array != null) { if (array != null) {
// check for any remote subs that may have been removed, and unsubscribe from them
for (int i = 0; i < array.length(); i++) { for (int i = 0; i < array.length(); i++) {
JSONObject item = array.getJSONObject(i); JSONObject item = array.getJSONObject(i);
// TODO: Refactor by creating static Subscription.fromJSON method
String claimId = item.getString("claim_id"); String claimId = item.getString("claim_id");
String channelName = item.getString("channel_name"); String channelName = item.getString("channel_name");
boolean isNotificationsDisabled = item.getBoolean("is_notifications_disabled");
LbryUri url = new LbryUri(); LbryUri url = new LbryUri();
url.setChannelName(channelName); url.setChannelName(channelName);
url.setClaimId(claimId); url.setClaimId(claimId);
Subscription subscription = new Subscription(channelName, url.toString()); Subscription subscription = new Subscription(channelName, url.toString(), isNotificationsDisabled);
remoteSubs.add(subscription); remoteSubs.add(subscription);
} }
} }
for (int i = 0; i < combined.size(); i++) { List<Subscription> remoteUnsubs = new ArrayList<>();
Subscription local = combined.get(i); List<Subscription> finalRemoteSubs = new ArrayList<>();
if (!remoteSubs.contains(local)) { if (remoteSubs.size() > 0) {
// add to remote subscriptions for (int i = 0; i < remoteSubs.size(); i++) {
try { Subscription sub = remoteSubs.get(i);
LbryUri uri = LbryUri.parse(local.getUrl()); if (!combined.contains(sub)) {
Map<String, String> options = new HashMap<>(); Map<String, String> options = new HashMap<>();
LbryUri uri = LbryUri.tryParse(sub.getUrl());
if (uri != null) {
options.put("claim_id", uri.getChannelClaimId()); options.put("claim_id", uri.getChannelClaimId());
options.put("channel_name", Helper.normalizeChannelName(local.getChannelName())); Lbryio.parseResponse(Lbryio.call("subscription", "delete", options, context));
Lbryio.parseResponse(Lbryio.call("subscription", "new", options, context)); remoteUnsubs.add(sub);
} catch (LbryUriException | LbryioRequestException | LbryioResponseException ex) { } else {
// pass finalRemoteSubs.add(sub);
Log.e(TAG, String.format("subscription/new failed: %s", ex.getMessage()), ex); }
} }
} }
} }
if (!replaceLocal) {
for (int i = 0; i < localSubs.size(); i++) { for (int i = 0; i < localSubs.size(); i++) {
Subscription local = localSubs.get(i); Subscription local = localSubs.get(i);
if (!base.contains(local) && !diff.contains(local)) { if (!base.contains(local) && !diff.contains(local)) {
diff.add(local); diff.add(local);
} }
} }
for (int i = 0; i < remoteSubs.size(); i++) { for (int i = 0; i < finalRemoteSubs.size(); i++) {
Subscription remote = remoteSubs.get(i); Subscription remote = finalRemoteSubs.get(i);
if (!combined.contains(remote)) { if (!combined.contains(remote)) {
combined.add(remote); combined.add(remote);
if (!diff.contains(remote)) { if (!diff.contains(remote)) {
@ -109,6 +120,7 @@ public class MergeSubscriptionsTask extends AsyncTask<Void, Void, List<Subscript
} }
} }
} }
}
} catch (ClassCastException | LbryioRequestException | LbryioResponseException | JSONException | IllegalStateException | SQLiteException ex) { } catch (ClassCastException | LbryioRequestException | LbryioResponseException | JSONException | IllegalStateException | SQLiteException ex) {
error = ex; error = ex;
return null; return null;

View file

@ -4,18 +4,15 @@ import android.os.AsyncTask;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.Buffer;
import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Helper;
public class ReadTextFileTask extends AsyncTask<Void, Void, String> { public class ReadTextFileTask extends AsyncTask<Void, Void, String> {
private String filePath; private final String filePath;
private Exception error; private Exception error;
private ReadTextFileHandler handler; private final ReadTextFileHandler handler;
public ReadTextFileTask(String filePath, ReadTextFileHandler handler) { public ReadTextFileTask(String filePath, ReadTextFileHandler handler) {
this.filePath = filePath; this.filePath = filePath;
this.handler = handler; this.handler = handler;

View file

@ -0,0 +1,8 @@
package io.lbry.browser.tasks;
import io.lbry.browser.model.lbryinc.RewardVerified;
public interface RewardVerifiedHandler {
void onSuccess(RewardVerified rewardVerified);
void onError(Exception error);
}

View file

@ -9,9 +9,9 @@ import io.lbry.browser.exceptions.ApiCallException;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
public class SetSdkSettingTask extends AsyncTask<Void, Void, Boolean> { public class SetSdkSettingTask extends AsyncTask<Void, Void, Boolean> {
private String key; private final String key;
private String value; private final String value;
private GenericTaskHandler handler; private final GenericTaskHandler handler;
private Exception error; private Exception error;
public SetSdkSettingTask(String key, String value, GenericTaskHandler handler) { public SetSdkSettingTask(String key, String value, GenericTaskHandler handler) {
this.key = key; this.key = key;

View file

@ -0,0 +1,8 @@
package io.lbry.browser.tasks;
import io.lbry.browser.model.TwitterOauth;
public interface TwitterOauthHandler {
void onSuccess(TwitterOauth twitterOauth);
void onError(Exception error);
}

View file

@ -13,13 +13,13 @@ import io.lbry.browser.utils.Lbry;
public class UpdateSuggestedTagsTask extends AsyncTask<Void, Void, List<Tag>> { public class UpdateSuggestedTagsTask extends AsyncTask<Void, Void, List<Tag>> {
private boolean clearPrevious; private final boolean clearPrevious;
private boolean excludeMature; private final boolean excludeMature;
private int limit; private final int limit;
private String filter; private final String filter;
private TagListAdapter addedTagsAdapter; private final TagListAdapter addedTagsAdapter;
private TagListAdapter suggestedTagsAdapter; private final TagListAdapter suggestedTagsAdapter;
private KnownTagsHandler handler; private final KnownTagsHandler handler;
public UpdateSuggestedTagsTask( public UpdateSuggestedTagsTask(
String filter, String filter,
@ -45,6 +45,7 @@ public class UpdateSuggestedTagsTask extends AsyncTask<Void, Void, List<Tag>> {
if (suggestedTagsAdapter != null && !clearPrevious) { if (suggestedTagsAdapter != null && !clearPrevious) {
tags = new ArrayList<>(suggestedTagsAdapter.getTags()); tags = new ArrayList<>(suggestedTagsAdapter.getTags());
} }
if (Lbry.knownTags.size() > 0) {
while (tags.size() < limit) { while (tags.size() < limit) {
Tag randomTag = Lbry.knownTags.get(random.nextInt(Lbry.knownTags.size())); Tag randomTag = Lbry.knownTags.get(random.nextInt(Lbry.knownTags.size()));
if (excludeMature && randomTag.isMature()) { if (excludeMature && randomTag.isMature()) {
@ -54,6 +55,7 @@ public class UpdateSuggestedTagsTask extends AsyncTask<Void, Void, List<Tag>> {
tags.add(randomTag); tags.add(randomTag);
} }
} }
}
} else { } else {
Tag filterTag = new Tag(filter); Tag filterTag = new Tag(filter);
if (addedTagsAdapter == null || !addedTagsAdapter.getTags().contains(filterTag)) { if (addedTagsAdapter == null || !addedTagsAdapter.getTags().contains(filterTag)) {
@ -64,11 +66,16 @@ public class UpdateSuggestedTagsTask extends AsyncTask<Void, Void, List<Tag>> {
if (excludeMature && knownTag.isMature()) { if (excludeMature && knownTag.isMature()) {
continue; continue;
} }
if ((knownTag.getLowercaseName().startsWith(filter) || knownTag.getLowercaseName().matches(filter)) && try {
(!tags.contains(knownTag) && if ((knownTag.getLowercaseName().startsWith(filter) ||
!Lbry.followedTags.contains(knownTag) && (addedTagsAdapter == null || !addedTagsAdapter.getTags().contains(knownTag)))) { knownTag.getLowercaseName().matches(filter)) &&
(!tags.contains(knownTag) && !Lbry.followedTags.contains(knownTag) && (addedTagsAdapter == null || !addedTagsAdapter.getTags().contains(knownTag)))
) {
tags.add(knownTag); tags.add(knownTag);
} }
} catch (Exception ex) {
// continue
}
} }
} }
return tags; return tags;

View file

@ -21,9 +21,9 @@ import okhttp3.RequestBody;
import okhttp3.Response; import okhttp3.Response;
public class UploadImageTask extends AsyncTask<Void, Void, String> { public class UploadImageTask extends AsyncTask<Void, Void, String> {
private String filePath; private final String filePath;
private View progressView; private final View progressView;
private UploadThumbnailHandler handler; private final UploadThumbnailHandler handler;
private Exception error; private Exception error;
public UploadImageTask(String filePath, View progressView, UploadThumbnailHandler handler) { public UploadImageTask(String filePath, View progressView, UploadThumbnailHandler handler) {

View file

@ -11,17 +11,16 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import io.lbry.browser.exceptions.ApiCallException; import io.lbry.browser.exceptions.ApiCallException;
import io.lbry.browser.tasks.GenericTaskHandler;
import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
public class AbandonChannelTask extends AsyncTask<Void, Void, Boolean> { public class AbandonChannelTask extends AsyncTask<Void, Void, Boolean> {
private List<String> claimIds; private final List<String> claimIds;
private List<String> successfulClaimIds; private List<String> successfulClaimIds;
private List<String> failedClaimIds; private List<String> failedClaimIds;
private List<Exception> failedExceptions; private List<Exception> failedExceptions;
private View progressView; private final View progressView;
private AbandonHandler handler; private final AbandonHandler handler;
public AbandonChannelTask(List<String> claimIds, View progressView, AbandonHandler handler) { public AbandonChannelTask(List<String> claimIds, View progressView, AbandonHandler handler) {
this.claimIds = claimIds; this.claimIds = claimIds;

View file

@ -11,17 +11,16 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import io.lbry.browser.exceptions.ApiCallException; import io.lbry.browser.exceptions.ApiCallException;
import io.lbry.browser.tasks.GenericTaskHandler;
import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
public class AbandonStreamTask extends AsyncTask<Void, Void, Boolean> { public class AbandonStreamTask extends AsyncTask<Void, Void, Boolean> {
private List<String> claimIds; private final List<String> claimIds;
private List<String> successfulClaimIds; private List<String> successfulClaimIds;
private List<String> failedClaimIds; private List<String> failedClaimIds;
private List<Exception> failedExceptions; private List<Exception> failedExceptions;
private View progressView; private final View progressView;
private AbandonHandler handler; private final AbandonHandler handler;
public AbandonStreamTask(List<String> claimIds, View progressView, AbandonHandler handler) { public AbandonStreamTask(List<String> claimIds, View progressView, AbandonHandler handler) {
this.claimIds = claimIds; this.claimIds = claimIds;

View file

@ -21,12 +21,12 @@ import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
public class ChannelCreateUpdateTask extends AsyncTask<Void, Void, Claim> { public class ChannelCreateUpdateTask extends AsyncTask<Void, Void, Claim> {
private Claim claim; private final Claim claim;
private BigDecimal deposit; private final BigDecimal deposit;
private boolean update; private final boolean update;
private Exception error; private Exception error;
private ClaimResultHandler handler; private final ClaimResultHandler handler;
private View progressView; private final View progressView;
public ChannelCreateUpdateTask(Claim claim, BigDecimal deposit, boolean update, View progressView, ClaimResultHandler handler) { public ChannelCreateUpdateTask(Claim claim, BigDecimal deposit, boolean update, View progressView, ClaimResultHandler handler) {
this.claim = claim; this.claim = claim;

View file

@ -19,9 +19,9 @@ import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
public class ClaimListTask extends AsyncTask<Void, Void, List<Claim>> { public class ClaimListTask extends AsyncTask<Void, Void, List<Claim>> {
private List<String> types; private final List<String> types;
private View progressView; private final View progressView;
private ClaimListResultHandler handler; private final ClaimListResultHandler handler;
private Exception error; private Exception error;
public ClaimListTask(String type, View progressView, ClaimListResultHandler handler) { public ClaimListTask(String type, View progressView, ClaimListResultHandler handler) {
@ -62,7 +62,8 @@ public class ClaimListTask extends AsyncTask<Void, Void, List<Claim>> {
Helper.setViewVisibility(progressView, View.GONE); Helper.setViewVisibility(progressView, View.GONE);
if (handler != null) { if (handler != null) {
if (claims != null) { if (claims != null) {
handler.onSuccess(claims); // TODO: Add fix for handling invalid reposts in ClaimListAdapter
handler.onSuccess(Helper.filterInvalidReposts(claims));
} else { } else {
handler.onError(error); handler.onError(error);
} }

View file

@ -12,10 +12,10 @@ import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
public class ClaimSearchTask extends AsyncTask<Void, Void, List<Claim>> { public class ClaimSearchTask extends AsyncTask<Void, Void, List<Claim>> {
private Map<String, Object> options; private final Map<String, Object> options;
private String connectionString; private final String connectionString;
private ClaimSearchResultHandler handler; private final ClaimSearchResultHandler handler;
private View progressView; private final View progressView;
private ApiCallException error; private ApiCallException error;
public ClaimSearchTask(Map<String, Object> options, String connectionString, View progressView, ClaimSearchResultHandler handler) { public ClaimSearchTask(Map<String, Object> options, String connectionString, View progressView, ClaimSearchResultHandler handler) {
@ -29,7 +29,7 @@ public class ClaimSearchTask extends AsyncTask<Void, Void, List<Claim>> {
} }
protected List<Claim> doInBackground(Void... params) { protected List<Claim> doInBackground(Void... params) {
try { try {
return Helper.filterInvalidReposts(Lbry.claimSearch(options, connectionString)); return Lbry.claimSearch(options, connectionString);
} catch (ApiCallException ex) { } catch (ApiCallException ex) {
error = ex; error = ex;
return null; return null;
@ -39,7 +39,7 @@ public class ClaimSearchTask extends AsyncTask<Void, Void, List<Claim>> {
Helper.setViewVisibility(progressView, View.GONE); Helper.setViewVisibility(progressView, View.GONE);
if (handler != null) { if (handler != null) {
if (claims != null) { if (claims != null) {
handler.onSuccess(claims, claims.size() < Helper.parseInt(options.get("page_size"), 0)); handler.onSuccess(Helper.filterInvalidReposts(claims), claims.size() < Helper.parseInt(options.get("page_size"), 0));
} else { } else {
handler.onError(error); handler.onError(error);
} }

View file

@ -21,10 +21,10 @@ import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
public class PublishClaimTask extends AsyncTask<Void, Void, Claim> { public class PublishClaimTask extends AsyncTask<Void, Void, Claim> {
private Claim claim; private final Claim claim;
private String filePath; private final String filePath;
private View progressView; private final View progressView;
private ClaimResultHandler handler; private final ClaimResultHandler handler;
private Exception error; private Exception error;
public PublishClaimTask(Claim claim, String filePath, View progressView, ClaimResultHandler handler) { public PublishClaimTask(Claim claim, String filePath, View progressView, ClaimResultHandler handler) {
this.claim = claim; this.claim = claim;
@ -73,6 +73,14 @@ public class PublishClaimTask extends AsyncTask<Void, Void, Claim> {
options.put("license_url", metadata.getLicenseUrl()); options.put("license_url", metadata.getLicenseUrl());
} }
if (metadata.getReleaseTime() > 0) {
options.put("release_time", metadata.getReleaseTime());
} else if (claim.getTimestamp() > 0) {
options.put("release_time", claim.getTimestamp());
} else {
options.put("release_time", Double.valueOf(Math.floor(System.currentTimeMillis() / 1000.0)).intValue());
}
Claim claimResult = null; Claim claimResult = null;
try { try {
JSONObject result = (JSONObject) Lbry.genericApiCall(Lbry.METHOD_PUBLISH, options); JSONObject result = (JSONObject) Lbry.genericApiCall(Lbry.METHOD_PUBLISH, options);

View file

@ -18,12 +18,19 @@ import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
public class PurchaseListTask extends AsyncTask<Void, Void, List<Claim>> { public class PurchaseListTask extends AsyncTask<Void, Void, List<Claim>> {
private String claimId;
private int page; private int page;
private int pageSize; private int pageSize;
private ClaimSearchResultHandler handler; private final ClaimSearchResultHandler handler;
private View progressView; private final View progressView;
private Exception error; private Exception error;
public PurchaseListTask(String claimId, View progressView, ClaimSearchResultHandler handler) {
this.claimId = claimId;
this.progressView = progressView;
this.handler = handler;
}
public PurchaseListTask(int page, int pageSize, View progressView, ClaimSearchResultHandler handler) { public PurchaseListTask(int page, int pageSize, View progressView, ClaimSearchResultHandler handler) {
this.page = page; this.page = page;
this.pageSize = pageSize; this.pageSize = pageSize;
@ -38,8 +45,15 @@ public class PurchaseListTask extends AsyncTask<Void, Void, List<Claim>> {
List<Claim> claims = null; List<Claim> claims = null;
try { try {
Map<String, Object> options = new HashMap<>(); Map<String, Object> options = new HashMap<>();
if (!Helper.isNullOrEmpty(claimId)) {
options.put("claim_id", claimId);
}
if (page > 0) {
options.put("page", page); options.put("page", page);
}
if (pageSize > 0) {
options.put("page_size", pageSize); options.put("page_size", pageSize);
}
options.put("resolve", true); options.put("resolve", true);
JSONObject result = (JSONObject) Lbry.genericApiCall(Lbry.METHOD_PURCHASE_LIST, options); JSONObject result = (JSONObject) Lbry.genericApiCall(Lbry.METHOD_PURCHASE_LIST, options);
@ -47,10 +61,11 @@ public class PurchaseListTask extends AsyncTask<Void, Void, List<Claim>> {
claims = new ArrayList<>(); claims = new ArrayList<>();
for (int i = 0; i < items.length(); i++) { for (int i = 0; i < items.length(); i++) {
Claim claim = Claim.fromJSONObject(items.getJSONObject(i).getJSONObject("claim")); Claim claim = Claim.fromJSONObject(items.getJSONObject(i).getJSONObject("claim"));
if (!Helper.isNullOrEmpty(claim.getClaimId())) {
claims.add(claim); claims.add(claim);
Lbry.addClaimToCache(claim); Lbry.addClaimToCache(claim);
} }
}
} catch (ApiCallException | JSONException | ClassCastException ex) { } catch (ApiCallException | JSONException | ClassCastException ex) {
error = ex; error = ex;
} }

View file

@ -13,10 +13,10 @@ import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
public class ResolveTask extends AsyncTask<Void, Void, List<Claim>> { public class ResolveTask extends AsyncTask<Void, Void, List<Claim>> {
private List<String> urls; private final List<String> urls;
private String connectionString; private final String connectionString;
private ClaimListResultHandler handler; private final ClaimListResultHandler handler;
private View progressView; private final View progressView;
private ApiCallException error; private ApiCallException error;
public ResolveTask(String url, String connectionString, View progressView, ClaimListResultHandler handler) { public ResolveTask(String url, String connectionString, View progressView, ClaimListResultHandler handler) {

View file

@ -20,12 +20,12 @@ import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
public class StreamRepostTask extends AsyncTask<Void, Void, Claim> { public class StreamRepostTask extends AsyncTask<Void, Void, Claim> {
private String name; private final String name;
private BigDecimal bid; private final BigDecimal bid;
private String claimId; private final String claimId;
private String channelId; private final String channelId;
private View progressView; private final View progressView;
private ClaimResultHandler handler; private final ClaimResultHandler handler;
private Exception error; private Exception error;
public StreamRepostTask(String name, BigDecimal bid, String claimId, String channelId, View progressView, ClaimResultHandler handler) { public StreamRepostTask(String name, BigDecimal bid, String claimId, String channelId, View progressView, ClaimResultHandler handler) {

View file

@ -7,12 +7,11 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import io.lbry.browser.exceptions.ApiCallException; import io.lbry.browser.exceptions.ApiCallException;
import io.lbry.browser.tasks.GenericTaskHandler;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
// Just run delete on the specified claim IDs (no need for a handler) // Just run delete on the specified claim IDs (no need for a handler)
public class BulkDeleteFilesTask extends AsyncTask<Void, Void, Boolean> { public class BulkDeleteFilesTask extends AsyncTask<Void, Void, Boolean> {
private List<String> claimIds; private final List<String> claimIds;
public BulkDeleteFilesTask(List<String> claimIds) { public BulkDeleteFilesTask(List<String> claimIds) {
this.claimIds = claimIds; this.claimIds = claimIds;
} }

View file

@ -10,9 +10,9 @@ import io.lbry.browser.tasks.GenericTaskHandler;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
public class DeleteFileTask extends AsyncTask<Void, Void, Boolean> { public class DeleteFileTask extends AsyncTask<Void, Void, Boolean> {
private String claimId; private final String claimId;
private Exception error; private Exception error;
private GenericTaskHandler handler; private final GenericTaskHandler handler;
public DeleteFileTask(String claimId, GenericTaskHandler handler) { public DeleteFileTask(String claimId, GenericTaskHandler handler) {
this.claimId = claimId; this.claimId = claimId;

View file

@ -11,12 +11,12 @@ import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
public class FileListTask extends AsyncTask<Void, Void, List<LbryFile>> { public class FileListTask extends AsyncTask<Void, Void, List<LbryFile>> {
private String claimId; private final String claimId;
private boolean downloads; private boolean downloads;
private int page; private int page;
private int pageSize; private int pageSize;
private FileListResultHandler handler; private final FileListResultHandler handler;
private View progressView; private final View progressView;
private ApiCallException error; private ApiCallException error;
public FileListTask(int page, int pageSize, boolean downloads, View progressView, FileListResultHandler handler) { public FileListTask(int page, int pageSize, boolean downloads, View progressView, FileListResultHandler handler) {

View file

@ -14,10 +14,10 @@ import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
public class GetFileTask extends AsyncTask<Void, Void, LbryFile> { public class GetFileTask extends AsyncTask<Void, Void, LbryFile> {
private String uri; private final String uri;
private boolean saveFile; private final boolean saveFile;
private View progressView; private final View progressView;
private GetFileHandler handler; private final GetFileHandler handler;
private Exception error; private Exception error;
public GetFileTask(String uri, boolean saveFile, View progressView, GetFileHandler handler) { public GetFileTask(String uri, boolean saveFile, View progressView, GetFileHandler handler) {

View file

@ -0,0 +1,66 @@
package io.lbry.browser.tasks.lbryinc;
import android.content.Context;
import android.os.AsyncTask;
import android.view.View;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import org.json.JSONObject;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import io.lbry.browser.model.lbryinc.RewardVerified;
import io.lbry.browser.tasks.RewardVerifiedHandler;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbryio;
public class AndroidPurchaseTask extends AsyncTask<Void, Void, RewardVerified> {
private final Context context;
private final View progressView;
private final String purchaseToken;
private final RewardVerifiedHandler handler;
private Exception error;
public AndroidPurchaseTask(String purchaseToken, View progressView, Context context, RewardVerifiedHandler handler) {
this.purchaseToken = purchaseToken;
this.progressView = progressView;
this.context = context;
this.handler = handler;
}
protected void onPreExecute() {
Helper.setViewVisibility(progressView, View.VISIBLE);
}
protected RewardVerified doInBackground(Void... params) {
try {
Map<String, String> options = new HashMap<>();
options.put("purchase_token", purchaseToken);
JSONObject object = (JSONObject) Lbryio.parseResponse(Lbryio.call("verification", "android_purchase", options, context));
Type type = new TypeToken<RewardVerified>(){}.getType();
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
return gson.fromJson(object.toString(), type);
} catch (Exception ex) {
error = ex;
return null;
}
}
protected void onPostExecute(RewardVerified result) {
Helper.setViewVisibility(progressView, View.GONE);
if (handler != null) {
if (result != null) {
handler.onSuccess(result);
} else {
handler.onError(error);
}
}
}
}

View file

@ -13,16 +13,15 @@ import io.lbry.browser.data.DatabaseHelper;
import io.lbry.browser.exceptions.LbryioRequestException; import io.lbry.browser.exceptions.LbryioRequestException;
import io.lbry.browser.exceptions.LbryioResponseException; import io.lbry.browser.exceptions.LbryioResponseException;
import io.lbry.browser.model.lbryinc.Subscription; import io.lbry.browser.model.lbryinc.Subscription;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbryio; import io.lbry.browser.utils.Lbryio;
public class ChannelSubscribeTask extends AsyncTask<Void, Void, Boolean> { public class ChannelSubscribeTask extends AsyncTask<Void, Void, Boolean> {
private Context context; private final Context context;
private String channelClaimId; private final String channelClaimId;
private Subscription subscription; private final Subscription subscription;
private ChannelSubscribeHandler handler; private final ChannelSubscribeHandler handler;
private Exception error; private Exception error;
private boolean isUnsubscribing; private final boolean isUnsubscribing;
public ChannelSubscribeTask(Context context, String channelClaimId, Subscription subscription, boolean isUnsubscribing, ChannelSubscribeHandler handler) { public ChannelSubscribeTask(Context context, String channelClaimId, Subscription subscription, boolean isUnsubscribing, ChannelSubscribeHandler handler) {
this.context = context; this.context = context;
@ -51,11 +50,11 @@ public class ChannelSubscribeTask extends AsyncTask<Void, Void, Boolean> {
options.put("claim_id", channelClaimId); options.put("claim_id", channelClaimId);
if (!isUnsubscribing) { if (!isUnsubscribing) {
options.put("channel_name", subscription.getChannelName()); options.put("channel_name", subscription.getChannelName());
options.put("notifications_disabled", String.valueOf(subscription.isNotificationsDisabled()).toLowerCase());
} }
String action = isUnsubscribing ? "delete" : "new"; String action = isUnsubscribing ? "delete" : "new";
Lbryio.call("subscription", action, options, context); Object response = Lbryio.parseResponse(Lbryio.call("subscription", action, options, context));
if (!isUnsubscribing) { if (!isUnsubscribing) {
Lbryio.addSubscription(subscription); Lbryio.addSubscription(subscription);
} else { } else {

View file

@ -4,6 +4,8 @@ import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.view.View; import android.view.View;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.text.DecimalFormat; import java.text.DecimalFormat;
@ -14,23 +16,25 @@ import io.lbry.browser.R;
import io.lbry.browser.exceptions.ApiCallException; import io.lbry.browser.exceptions.ApiCallException;
import io.lbry.browser.exceptions.LbryioRequestException; import io.lbry.browser.exceptions.LbryioRequestException;
import io.lbry.browser.exceptions.LbryioResponseException; import io.lbry.browser.exceptions.LbryioResponseException;
import io.lbry.browser.model.Claim;
import io.lbry.browser.model.lbryinc.Reward;
import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
import io.lbry.browser.utils.Lbryio; import io.lbry.browser.utils.Lbryio;
public class ClaimRewardTask extends AsyncTask<Void, Void, String> { public class ClaimRewardTask extends AsyncTask<Void, Void, String> {
private Context context; private final Context context;
private String type; private final String type;
private String claimCode; private final String rewardCode;
private View progressView; private final View progressView;
private double amountClaimed; private double amountClaimed;
private ClaimRewardHandler handler; private final ClaimRewardHandler handler;
private Exception error; private Exception error;
public ClaimRewardTask(String type, String claimCode, View progressView, Context context, ClaimRewardHandler handler) { public ClaimRewardTask(String type, String rewardCode, View progressView, Context context, ClaimRewardHandler handler) {
this.type = type; this.type = type;
this.claimCode = claimCode; this.rewardCode = rewardCode;
this.progressView = progressView; this.progressView = progressView;
this.context = context; this.context = context;
this.handler = handler; this.handler = handler;
@ -43,15 +47,27 @@ public class ClaimRewardTask extends AsyncTask<Void, Void, String> {
public String doInBackground(Void... params) { public String doInBackground(Void... params) {
String message = null; String message = null;
try { try {
String txid = null;
if (Reward.TYPE_FIRST_CHANNEL.equalsIgnoreCase(type)) {
// fetch a channel
txid = fetchSingleClaimTxid(Claim.TYPE_CHANNEL);
} else if (Reward.TYPE_FIRST_PUBLISH.equalsIgnoreCase(type)) {
// fetch a publish
txid = fetchSingleClaimTxid(Claim.TYPE_STREAM);
}
// Get a new wallet address for the reward // Get a new wallet address for the reward
String address = (String) Lbry.genericApiCall(Lbry.METHOD_ADDRESS_UNUSED); String address = (String) Lbry.genericApiCall(Lbry.METHOD_ADDRESS_UNUSED);
Map<String, String> options = new HashMap<>(); Map<String, String> options = new HashMap<>();
options.put("reward_type", type); options.put("reward_type", type);
options.put("wallet_address", address); options.put("wallet_address", address);
if (!Helper.isNullOrEmpty(claimCode)) { if (!Helper.isNullOrEmpty(rewardCode)) {
options.put("claim_code", claimCode); options.put("code", rewardCode);
} }
if (!Helper.isNullOrEmpty(txid)) {
options.put("transaction_id", txid);
}
JSONObject reward = (JSONObject) Lbryio.parseResponse( JSONObject reward = (JSONObject) Lbryio.parseResponse(
Lbryio.call("reward", "claim", options, Helper.METHOD_POST, null)); Lbryio.call("reward", "claim", options, Helper.METHOD_POST, null));
amountClaimed = Helper.getJSONDouble("reward_amount", 0, reward); amountClaimed = Helper.getJSONDouble("reward_amount", 0, reward);
@ -61,7 +77,7 @@ public class ClaimRewardTask extends AsyncTask<Void, Void, String> {
amountClaimed == 1 ? 1 : 2, amountClaimed == 1 ? 1 : 2,
new DecimalFormat(Helper.LBC_CURRENCY_FORMAT_PATTERN).format(amountClaimed)) : ""; new DecimalFormat(Helper.LBC_CURRENCY_FORMAT_PATTERN).format(amountClaimed)) : "";
message = Helper.getJSONString("reward_notification", defaultMessage, reward); message = Helper.getJSONString("reward_notification", defaultMessage, reward);
} catch (ApiCallException | LbryioRequestException | LbryioResponseException ex) { } catch (ApiCallException | JSONException | LbryioRequestException | LbryioResponseException ex) {
error = ex; error = ex;
} }
@ -79,6 +95,23 @@ public class ClaimRewardTask extends AsyncTask<Void, Void, String> {
} }
} }
private String fetchSingleClaimTxid(String claimType) throws ApiCallException, JSONException {
Map<String, Object> options = new HashMap<>();
options.put("claim_type", claimType);
options.put("page", 1);
options.put("page_size", 1);
options.put("resolve", true);
JSONObject result = (JSONObject) Lbry.genericApiCall(Lbry.METHOD_CLAIM_LIST, options);
JSONArray items = result.getJSONArray("items");
if (items.length() > 0) {
Claim claim = Claim.fromJSONObject(items.getJSONObject(0));
return claim.getTxid();
}
return null;
}
public interface ClaimRewardHandler { public interface ClaimRewardHandler {
void onSuccess(double amountClaimed, String message); void onSuccess(double amountClaimed, String message);
void onError(Exception error); void onError(Exception error);

View file

@ -1,20 +1,23 @@
package io.lbry.browser.tasks.lbryinc; package io.lbry.browser.tasks.lbryinc;
import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
import io.lbry.browser.model.lbryinc.User; import io.lbry.browser.model.lbryinc.User;
import io.lbry.browser.utils.Lbryio; import io.lbry.browser.utils.Lbryio;
public class FetchCurrentUserTask extends AsyncTask<Void, Void, User> { public class FetchCurrentUserTask extends AsyncTask<Void, Void, User> {
private final Context context;
private Exception error; private Exception error;
private FetchUserTaskHandler handler; private final FetchUserTaskHandler handler;
public FetchCurrentUserTask(FetchUserTaskHandler handler) { public FetchCurrentUserTask(Context context, FetchUserTaskHandler handler) {
this.context = context;
this.handler = handler; this.handler = handler;
} }
protected User doInBackground(Void... params) { protected User doInBackground(Void... params) {
try { try {
return Lbryio.fetchCurrentUser(null); return Lbryio.fetchCurrentUser(context);
} catch (Exception ex) { } catch (Exception ex) {
error = ex; error = ex;
return null; return null;

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