Compare commits

...
Author SHA1 Message Date
Yevheniia Berdnyk 767be39673 e2e: fix for collectibles tests 2025-03-04 00:09:00 +02:00
Parvesh Monu 714d966d48 fix Some of the message longtap options disappear until relogin after unmuting chat (#22220) (#22221) 2025-03-04 00:05:41 +05:30
Siddarth Kumar 11d66e5bc2 ci: fix nix-cache-job with nix-export 2025-03-03 15:39:12 +05:30
jakub 9d5f6d0b06 nix: replace yet another openjdk with headless version
Signed-off-by: Jakub Sokołowski <jakub@status.im>
2025-02-27 14:21:18 +01:00
frank 48c83ef759 fix_: no log file (#22122)
* fix_: no log file

https://github.com/status-im/status-go/compare/c5e4eb1a...388fce10

* chore_: fix lint issue

* chore_: set error level for release build

https://github.com/status-im/status-go/compare/c5e4eb1a...ba8fd519
2025-02-27 19:46:28 +08:00
jakub c84fc71bf1 nix: use openjdk_headless to avoid pulling GTK
This saves a whole bunch of downloading.

Signed-off-by: Jakub Sokołowski <jakub@status.im>
2025-02-26 18:56:10 +01:00
AlexanderandYevheniia Berdnyk 550602db12 Update send summary to show network in a separate card (#21616) (#22067)
* Update send summary to show network in a separate card (#21616)

* Style updates

* Style updates

* Fixes; test updates

* Fixes

* Fixes

* e2e: updated send summary checks

* Spec fix

* Spec fix

* Small schema update

---------

Co-authored-by: Yevheniia Berdnyk <ie.berdnyk@gmail.com>
2025-02-25 16:20:17 +01:00
Parvesh Monu 5932f7dc99 Fix keycard login flow options while using empty keycard (#22182) 2025-02-25 17:58:48 +05:30
Icaro Motta b89dce3c2a chore(analytics): Integrate Sentry (#21682)
Integrate Sentry and implement a feature flagged feature (disabled by default)
to force crash the app using the function mobile/status.go#IntendedPanic()
provided by status-go. Because the feature is feature flagged and disabled, it
is not available in release builds.

Fixes: https://github.com/status-im/status-mobile/issues/21656

Documentation about Sentry in status-go:
https://github.com/status-im/status-go/blob/3466ac2661bb19cd0eaa27761551de3b4d31b393/internal/sentry/README.md#L60

Areas that may be impacted: Whenever a panic happens in status-go and usage data
collection is enabled we will send error data to Sentry. In theory, nothing
should change for the user, therefore nothing should be visibly different in the
app's behavior.

Steps to test:

Before following the steps below, the CI must be configured with the appropriate
SENTRY_DSN_STATUS_GO value. You can also use a free tier cloud instance to do
your own testing in case you want to play around with Sentry.

1. Login with any profile.
2. Enable usage data collection (for now, Sentry and analytics are tied to the
   same setting in status-go).
3. Go to Settings > Feature flags and enable app-monitoring > intentional-crash
4. Go to Settings > Advanced > and press on Force crash immediately

Usually you should see the error in the Sentry dashboard in a matter of seconds.
2025-02-24 19:28:38 -03:00
Parvesh Monu 8aba451be8 Update keycard Illustrations (#22171) 2025-02-24 20:00:39 +05:30
Icaro Motta 82dd805755 chore(analytics): Allow MixPanel to be configured per env (#21975)
Allows MixPanel to be configured per environment. The hardcoded MixPanel app ID
and token are removed from the code (see the parent issue for more details). The
token can be exploited and that’s by design in most analytics tools
https://developer.mixpanel.com/reference/project-token.

Fixes https://github.com/status-im/status-mobile/issues/21974

Areas that may be impacted

- Analytics
2025-02-24 00:06:59 -03:00
Ulises Manuel 6e30d9faa8 fix(communities): Add missing stats (#22143)
* fix(communities): Stats not shown in discover page
* fix issues in the discover screen related to the theme
2025-02-21 08:40:18 -06:00
Mohsen 070869b705 [#21999] fix: disable manual auto refresh as new endpoint include auto refresh (#22115)
* [#21999] fix: disable manual auto refresh as new endpoint include this logic

* [#21999] fix: move build transaction route to last step

* [#21999] fix: restart swap proposal fetch after approval confirmation

* [#21999] fix: remove redundant event

* [#21999] fix: resolve conflicts

* [#21999] fix: slide button disable issue
2025-02-21 16:39:15 +03:00
frank 9dfa1069e5 feat_: add pre login log (#21987)
* feat_: add pre login log

https://github.com/status-im/status-go/compare/1bfb0cef...7ae58a47

* fix_: pre_login log not get zipped

https://github.com/status-im/status-go/compare/1bfb0cef...c5e4eb1a
2025-02-21 18:42:23 +08:00
Volodymyr Kozieiev 99dd374c87 Quick transaction settings (#22019)
* Quick settings for sending transaction

* Moved to newer status-go, fixed mode saving

https://github.com/status-im/status-go/compare/b2cd7bd8...4b44d6d0

* Fixed non-pressable radio buttons

* Account color applied to controls

* Updated status-go version

https://github.com/status-im/status-go/compare/1bfb0cef...5a796959

* Fixed bug with not applying bridge quick tx setting
2025-02-21 09:46:31 +00:00
Siddarth Kumar bac5ace90a deps: dynamically get gradle-lint version
This commit generates the gradle-lint dependency version according to the rules specified here in hack script :
https://googlesamples.github.io/android-custom-lint-rules/api-guide.html
2025-02-21 11:32:03 +05:30
Yevheniia Berdnyk 34cb7d19e2 e2e: added tests for collectibles 2025-02-21 01:24:44 +02:00
Mohsen a98bbf6a71 [#22029] fix: bridge button when there is single account (#22031)
* [#22029] fix: bridge button when there is single account

* [#22029] fix: trigger start bridge if all condition met

* [#22029] docs: add clarification for bridge flow logic based on account selection
2025-02-19 21:58:11 +03:00
andrey 8116f7a7b1 [#21956] Biometrics are still active after profile migration to keycard 2025-02-19 17:40:27 +01:00
andrey b3fbed1a1b [#21812] "Invalid Hex Data for Private Key" and "Invalid PIN Code" errors after mobile attempts to be synced with Desktop imported on keycard
https://github.com/status-im/status-go/compare/b2cd7bd8...1bfb0cef
2025-02-18 13:23:23 +01:00
frank 4826bedba4 fix_: revert version (#22149) 2025-02-18 20:12:11 +08:00
frank b81ac07636 fix_: bad node config migration (#21952)
https://github.com/status-im/status-go/compare/dcce8e9b...6132a9e7
2025-02-18 19:12:18 +08:00
Siddarth Kumar 2da2dfdad7 android: add gradle-lint as dependency (#22124) 2025-02-18 13:12:28 +05:30
Andrey Bocharnikov 1036bff41b feat(wallet): integrate eth rpc proxy (#22034)
* feat(wallet): Integrate eth-rpc-proxy

closes #22033
2025-02-17 22:27:22 +04:00
Siddarth Kumar e15b9a224c ci: upgrade go to 1.22 (#21940)
- This PR upgrades `go` to `1.22`
- Brings in `gomobile` latest derivation at master of `nixpkgs` : https://github.com/NixOS/nixpkgs/commit/f5abef98e8b8c9f9e6da4bdab63f8be1e57ea8c0
- points to status-go changes where `go` is also upgraded to `1.22` -> https://github.com/status-im/status-go/pull/6258
2025-02-17 22:53:28 +05:30
Parvesh Monu 82ab32c028 Allow user to access recovery phrase after canceling 'Confirm Backup' drawer (#22108) 2025-02-17 21:26:36 +05:30
Siddarth Kumar 3541485338 deps: fix path to hack script and regenerate (#22119)
This PR fixes the path to hack deps script and re-generates `gradle` deps.
Previous PRs passed build phase because we already have the `gradle` deps we need.

However make `nix-update-gradle` would fail due to the incorrect path.
2025-02-17 14:13:21 +05:30
Mohamed Javid 8c82a84aee feat(wallet)_: Saved addresses limit (#22069)
This commit adds limit for adding saved addresses upto 20 to prevent any unwanted behaviour.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2025-02-15 00:34:56 +05:30
Brian Sztamfater 447945f46e fix: transaction fails when signed from auto-restored derived account after seed phrase recovery with biometric authentication enabled (#22040)
Signed-off-by: Brian Sztamfater <brian@status.im>
2025-02-14 11:16:50 -03:00
Brian Sztamfater 16b2c90040 feat: show swap proposal exchange rate (#22078)
Signed-off-by: Brian Sztamfater <brian@status.im>
2025-02-14 10:32:07 -03:00
Alexander a210d59c4f Improve activity loading to avoid sitations when activity tab becomes empty after auto-refresh or app backgrounding (#21988) 2025-02-14 09:49:34 +01:00
Parvesh Monu fe0c5c2da7 fix Incorrect name and image at Ready to import key pair screen (#22080) 2025-02-13 20:50:43 +05:30
Parvesh Monu 8c91fbdd49 fix Keycard UI issues (#22075) 2025-02-13 20:28:07 +05:30
Mohsen 93e0c1fc5e [#21483] fix: update collectible amounts after sending (#21867)
* [#21483] fix: update collectible amounts after sending

* [#21483] test: cover funcs with unit test

* [#21483] fix: add condition to update only pending collectible

* [#21483] fix: use parse int for conversion

* [#21483] fix: round up dispatch delay
2025-02-13 15:38:03 +03:00
Yevheniia Berdnyk 9c7d215217 e2e: added test for contact request, receiver is offline 2025-02-13 11:15:55 +02:00
frank 4668d6a285 chore_: specify kdfIterations explictly (#22079) 2025-02-13 16:42:47 +08:00
Parvesh Monu 6c9c92efc7 Update Keycard color in keycard flow (#22066) 2025-02-12 21:22:52 +05:30
Yevheniia Berdnyk eff252c7c0 e2e: added smoke tests for android 12 and 13 2025-02-12 15:06:12 +02:00
Sean Hagstrom ab12c8a17d fix: add version field to localSrc definition for go-mobile nix build (#22041) 2025-02-12 13:30:23 +01:00
vedran 8b21d6ab53 chore(deps)_: get missing deps versions dynamically
To remove hardcoding of specific versions of missing gradle
dependencies, we will use `get_versions.sh` to fetch them dynamically.

Also disable default lint checks to remove redundant
`lintVitalReportRelease` task and dependency.
2025-02-11 14:24:12 +01:00
Tetiana Churikova 36fa4bdb62 fix: update text for keycard (#22043) 2025-02-11 12:20:50 +01:00
flexsurfer ed10ed70f7 [#21993] Keycard - Unlock Keycard (#22030) 2025-02-10 13:09:47 +01:00
Parvesh Monu 8c089bb334 Fix keycard theme issue and add documentation for ready to add key pair screen (#22028) 2025-02-10 17:18:15 +05:30
Ulises Manuel 45b09603d2 Add reagent support to transform prop keys inside vectors (#21937)
- Remove now unnecessary wrapper for reanimated/view
- Use kebab-case keywords for the hole-view component
- Use rn/StyleSheet.absoluteFill along with ClojureScript styles
- Fix keys inside view's `:transform` property
- Remove some uses of `merge` to pass styles to components
- Add tests for `convert-prop-value`
2025-02-07 16:50:22 -06:00
Mohsen 857cb47ceb [#21939] fix: handle sign message after dapp pairing (#22026) 2025-02-06 19:44:59 +03:00
Parvesh Monu 4782b3eb2a Allow users to view the recovery phrase again when creating a profile for an empty keycard (#22009) 2025-02-05 20:58:16 +05:30
Parvesh Monu 1a5008bcda Changing the PIN of the Keycard (#22008) 2025-02-05 20:41:41 +05:30
vedran c5c7f2eb27 chore(ci): bump status-jenkins-lib to include alchemy base api keys 2025-02-05 15:17:28 +01:00
Icaro Motta 25072cff9f feat(messenger): Add option to copy active message ID (#22018)
Add option to copy message ID when long pressing on a chat message. Similar to
how it's implemented in the desktop client, the option is only visible if the
user enables the DEBUG log level.

Fixes https://github.com/status-im/status-mobile/issues/21984
2025-02-04 22:40:16 -03:00
Brian Sztamfater e38cb0dee6 feat: elevate and highlight wallet CTAs on wallet home screen (#21985)
Signed-off-by: Brian Sztamfater <brian@status.im>
2025-02-04 14:50:42 -03:00
Mohamed Javid ba4a8b7d81 fix(wallet)_: Clean send data on entering swap flow (#22007)
This commit fixes assets unsupported for the bridge and appears disabled on the swap flow.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2025-02-04 18:51:23 +05:30
Yevheniia Berdnyk 882e40d99f e2e: fix for wallet swap test 2025-02-03 18:01:36 +02:00
Mohamed Javid c304b1b623 feat(wallet)_: Remove EIP-3770 address format (#21995)
This commit:

- removes EIP-3770 address format (with chain prefix) from the UI and displays/uses only the address
- removes editing of preferred networks for an account and saved address
- copy/paste functions will remove the chain prefix from the address (if any)
- adds base chain explorer links to account and saved address


Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2025-02-03 21:24:24 +05:30
Brian Sztamfater 1da39e064d fix: prevent receiving routes for unsupported receiving chains on bridge flow (#21980)
https://github.com/status-im/status-go/compare/bec0c373...dcce8e9b

Signed-off-by: Brian Sztamfater <brian@status.im>
2025-02-03 00:46:42 -03:00
flexsurfer 3899368aa0 [#21930] Keycard - Back up keycard (#21990) 2025-01-31 18:42:08 +01:00
Yevheniia Berdnyk 5ff1c8f1e4 e2e: added wallet test for bridge flow 2025-01-30 20:13:34 +02:00
Brian Sztamfater 7289940653 fix: swap fees are not displayed if route estimated time is nil (#22001)
Signed-off-by: Brian Sztamfater <brian@status.im>
2025-01-30 10:08:37 -03:00
Yevheniia Berdnyk a253f8cedb e2e: updated tests with editing profile when starting chat 2025-01-30 11:49:58 +02:00
Ulises Manuel daf52102ce feat(onboarding): ask user to update their profile (#21862)
* Add `buttons-style` prop to bottom actions to allow same button dimensions

* Make events to update a profile more generic

* Fix tests

* Add modal screen to update the profile

* Add introduce yourself modal

* Ask user to update their profile before sending a message or contact request

* Fix color picker selected styles

* Remove some `:f>` now unnecessary

* No longer shows the modal if the user manually updates their profile
2025-01-29 19:50:01 -06:00
Yevheniia Berdnyk fa20d4d9c3 e2e: added wallet tests for send and swap flows on mainnet 2025-01-29 18:32:03 +02:00
Parvesh Monu dd26fcf42c Add Documentation drawer on Check your Keycard screen (#21997) 2025-01-29 19:25:34 +05:30
Lungu Cristian 8ac9cc08e1 Keycard signing for dapp interactions (#21785)
* feat: wip dapps signing with keycard

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* fix: hide the pin sheet before showing the connection sheet

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* fix: signing dapp request with password

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* fix: logic for getting the current dapp

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* fix: issues after endpoints update

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* feat: removed support for eth_sign and signTransaction

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* fix: removed unused event

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* feat: re-enable dapps interactions with keycard

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* fix: adapted to latest standard-auth changes

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* feat: enable connected dapps for keycard

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* ref: removed threading (->)

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* fix: get keycard keypair addresses manually

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* fix: wrong condition on utils/keycard-address?

---------

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>
2025-01-28 17:33:34 +02:00
Siddarth Kumar c1230319e1 nix: add fake git to status-go build derivation (#21989)
This commit adds a script that overrides `git` to echo version required by `status-go` derivation.

Also fixes error messages like this when building `status-go` :
```
fatal: not a git repository (or any of the parent directories): .git
```

These messages can be often misleading to some developers.
2025-01-27 18:14:27 +05:30
Lungu Cristian 1e48442d67 Fix dapps transactions instantly rejected (#21973)
* fix: upgraded status-go to fixed version

https://github.com/status-im/status-go/compare/4ec885a9...a2588ee5

* fix: use status-go develop branch

https://github.com/status-im/status-go/compare/4ec885a9...e71fdbc6

* build: using status-go v9.3.0

https://github.com/status-im/status-go/compare/4ec885a9...bec0c373
2025-01-27 13:20:40 +02:00
Sean Hagstrom 94fbbbcb3c fix: resolve UI glitches for chat, contacts, and keycard (#21946)
This change resolves some issues related to text-overflow in the app, and it also ensures we use the correct drawer title when editing a user nickname.
2025-01-24 15:51:46 +00:00
Yevheniia Berdnyk 4dfd616583 e2e: additional logging for errors in tests 2025-01-23 12:46:55 +02:00
Parvesh Monu 85d00e47af Keycard - Import a seed phrase into a new Keycard (#21947) 2025-01-23 12:23:33 +05:30
Ulises Manuel 438fd7a512 fix(chat): Same QR shown code for everyone in the Contacts tab (#21949) 2025-01-22 15:26:15 -06:00
Mohsen ee5f73f5ee [#21919] fix: remove on-ramp provider description and reinstate mecuryo (#21923)
* [#21919] fix: remove on-ramp provider description and reinstate mecuryo

https://github.com/status-im/status-go/compare/59caca61...07d07601

* [#21919] fix: rebase status-go

https://github.com/status-im/status-go/compare/4ec885a9...41743c6c
2025-01-22 17:59:08 +03:00
Volodymyr Kozieiev b49c0ae58a Fix local re-frisk launch (#21972) 2025-01-22 14:43:26 +00:00
Yevheniia Berdnyk f474edf67d e2e: fix for saving logs 2025-01-22 16:03:54 +02:00
Volodymyr Kozieiev dfd90617bf Backend server doc added to readme (#21966) 2025-01-21 12:14:27 +00:00
Yevheniia Berdnyk 9782ba2ac3 e2e: added verifications to fallback test 2025-01-21 11:54:44 +02:00
Brian Sztamfater f0e0c95fe0 feat: integrate base chain (#21876)
Signed-off-by: Brian Sztamfater <brian@status.im>
2025-01-21 01:44:02 -03:00
Mohsen ef7d977299 [#21305] fix: sort assets by value and collectibles by name (#21908)
* [#21305] fix: sort assets by value and collectibles by name

* [#21305] fix: use seq to check name is nil or empty

* [#21305] fix: normalize text to sort properly
2025-01-20 19:41:40 +03:00
Parvesh Monu 70a5ed255f fix keycard sign up ui issues (#21938) 2025-01-20 17:22:13 +05:30
flexsurfer be1347346c [#21578] Keycard - Factory reset a Keycard (#21914) 2025-01-20 12:35:06 +01:00
Volodymyr Kozieiev 803e83ae02 Screens for editing transaction settings (#21838)
* Screens for editing transaction settings

* extra lines removed

* review notes fixed
2025-01-20 11:21:55 +00:00
Yevheniia Berdnyk 64748c939b e2e: fix for test_community_mentions_push_notification 2025-01-20 03:33:27 +02:00
Sean Hagstrom 64fb344197 chore: add support for remote devices with re-frisk server (#21805)
This changes adds support for connecting remotely to re-frisk when running the app on a mobile device. This change is essentially aimed towards better support for introspecting on iOS devices, but these changes should also work for Android devices too. Note, that running in the Android simulator may still require port-forwarding with `adb reverse`.
2025-01-18 05:57:17 +00:00
Sean Hagstrom f27b402205 chore: update status-go with identity images fixes (#21931)
This change integrates status-go changes that fix an underlying issue with generating identity images. Now identity images will be generated with a populated `clock` field, so we no longer need our existing workaround.
2025-01-18 05:37:37 +00:00
Icaro Motta b22744f81e fix(settings): Show current mailserver in legacy settings (#21901)
A regression removed more code than we wanted in
https://github.com/status-im/status-mobile/pull/20730 and the result was
broken functionality in the Legacy settings.

We now show the current mailserver correctly. The data is kept in sync via
signals, just as the original code.

Fixes https://github.com/status-im/status-mobile/issues/21375
2025-01-17 14:26:20 -03:00
Mohamed Javid 174e44c77d fix(wallet)_: Dismiss collectible details screen after a is TX placed (#21909)
This commit fixes the user is not taken to the activity tab after the TX is placed if the user enters the send flow from collectible details screen (inside the account)

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2025-01-16 21:57:06 +05:30
Mohsen 35c5d5ae6a [#21853] fix: unregister event listener after logout (#21873)
* [#21853] fix: check deeplink pending request after wallet connect loaded

* [#21853] fix: isolate the usage of pending deeplink url

* [#21853] fix: remove redundant check for web3 object

* [#21853] fix: unregister event listener after logout

* [#21853] fix: extract handler
2025-01-15 20:33:31 +03:00
frank ae00ecb3e0 ci: support long awaited Xcode 16 (#21345) 2025-01-15 18:49:51 +05:30
flexsurfer 8687282d63 [#21836] Relative derivation paths are not supported by keycard (#21888) 2025-01-15 12:27:51 +01:00
frank 04a97faa43 chore_: remove EncodeTransfer/EncodeFunctionCall/VerifyAccountPassword (#21925) 2025-01-15 17:52:30 +08:00
Parvesh Monu 91d3cd8da8 fix "keycard is not empty" screen shown if keycard moved during create profile flow (#21918) 2025-01-15 13:14:32 +05:30
Parvesh Monu e1a7146f4c fix Keyboard overlaps suggestion bar and validation errors (#21917) 2025-01-15 12:57:35 +05:30
Sean Hagstrom 5dc1bfe03b chore: cleanup event definitions and tests for contact-requests and deleting messages (#21885)
* chore: add error logging events and effects

* chore: restructure tests and effects for delete-message-for-me

* chore: refactor events and effects and tests for chat-contacts

* chore: refactor and add tests for accepting and declining contact request

* chore: refactor chat-contacts rf/defn to defn

* chore: add :fx key as a mergeable effect for rf/merge when running tests

* chore: refactor rf/defn and add tests for delete-message-for-me

* chore: remove log effects for contacts

* chore: remove logs effects for delete-message-for-me

* fix: update chat usage of sync-all

* fix: resolve schema error with missing action-label for contact-request dismissed state

* fix: ensure chat-contacts event error handlers are compatible with re-frame

* fix: ensure delete-message-for-me event error handlers are compatible with re-frame

* chore: refactor and test delete-message events

* chore: refactor log events and effects

* chore: use log effect in delete-message

* chore: use log events and effects inside chat-contacts

* chore: use log effects inside delete-message-for-me

* chore: remove unneeded test for delete-message-for-me

* chore: format bottom-drawer component

* chore: format delete-message

* chore: format delete-message-for-me
2025-01-14 14:41:47 +00:00
Mohamed Javid 2d8ba41d89 fix(wallet)_: General send flow for saved address and scanned address (#21899)
This commit:

- fixes send flow from saved addresses
- adds back send flow from general QR scanner

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2025-01-14 17:57:21 +05:30
Sean Hagstrom 457ed15e0b chore: resolve deprecation warnings related to custom-color and touchable-without-feeback (#21874)
This change refactors the codebase to avoid using deprecated functions and components related to the `custom-color` function (now removed) and the touchable-without-feedback component.
2025-01-14 12:09:49 +00:00
Alexander c11cf22bbb 2.32.x: Update metadata for F-Droid (#21912)
* 2.32.x: Update metadata for F-Droid

* Rollback
2025-01-14 11:43:11 +01:00
Siddarth Kumar 6ffdaa4e25 nix: fix warnings and upgrade to 2.24.11 (#21547)
These warnings are fixed:

```
warning: ignoring the client-specified setting 'keep-derivations', because it is a restricted setting and you are not a trusted user
warning: ignoring the client-specified setting 'keep-outputs', because it is a restricted setting and you are not a trusted user
warning: ignoring the client-specified setting 'sandbox', because it is a restricted setting and you are not a trusted user
warning: ignoring the client-specified setting 'stalled-download-timeout', because it is a restricted setting and you are not a trusted user
warning: ignoring the client-specified setting 'trusted-public-keys', because it is a restricted setting and you are not a trusted user
warning: ignoring the client-specified setting 'trusted-substituters', because it is a restricted setting and you are not a trusted user
```
2025-01-14 09:42:14 +05:30
flexsurfer f56bd54fd3 [#21915] Wrong address generated after creation a new keypair (#21921) 2025-01-13 17:06:59 +01:00
Sean HagstromandAlexander 394648b377 fix: use image clock when updating profile images to cause re-render (#21795) (#21830)
Co-authored-by: Alexander <alwxndr@gmail.com>
2025-01-10 15:01:57 +01:00
Yevheniia Berdnyk 22a606cbab e2e: additional fix for requirements 2025-01-10 14:03:23 +02:00
Sean HagstromandAlexander 1b30b3be80 chore: update legal-docs submodule and resources with updated terms-of-use (#21821) (#21832)
Co-authored-by: Alexander <alwxndr@gmail.com>
2025-01-10 12:27:10 +01:00
Yevheniia Berdnyk 10434b53e0 e2e: fix for requirements and getting username 2025-01-10 03:14:31 +02:00
flexsurfer 0472523a7f [#21652] The main part of a backup flow is missing at Keycard migrati… (#21893) 2025-01-09 19:20:45 +01:00
Volodymyr Kozieiev 770963447a Simplified pull request template (#21900) 2025-01-09 13:48:10 +00:00
Parvesh Monu 874aa9866d On-boarding: re add bio-metric screen to syncing flow (#21890) 2025-01-09 18:22:37 +05:30
Ulises Manuel 32a3f85694 fix(avatars) - Blinking all over the app and improvement in UX (#21782)
* Change multiple uses of `merge` to `assoc` in flat-list wrapper

* Fix fast-image wrapper to avoid blinking

* merge -> assoc usage in chat-item component

* Fix avatar not being updated when the user changes their username

* Fix scalable avatar issues

- On Android, the border was inconsistent depending on the theme.
- Fix animation, simplify the calculation, and now it matches designs

* Avoid blinking by using rn/memo
2025-01-08 18:16:19 -06:00
Volodymyr Kozieiev b88bbda3c5 Reverted some code after cleanup (#21892) 2025-01-08 11:32:44 +00:00
flexsurfer fad5119ceb [#21825] [Android] Java error at the and of faild Migration to a Keycard flow (#21889) 2025-01-08 11:47:53 +01:00
Parvesh Monu 3697f63ab0 Fix syncing flow navigation and designs (#21884) 2025-01-08 11:22:19 +05:30
Mohamed Javid ba8cf8e863 fix(wallet)_: collectible route cleanup on navigating back from TX confirmation page (#21883)
This commit fixes routes not cleaned properly ("Context Canceled" and "No Routes Found" errors thrown on generating the next route) when the user navigates back from the TX confirmation screen using the device's back button/gesture.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2025-01-07 19:04:01 +05:30
Siddarth Kumar ce1d2c4ed8 android: Kotlinify push notifications module (#21881) 2025-01-07 15:45:54 +05:30
Yevheniia Berdnyk 813c60f9e1 e2e: requirements cleanup 2025-01-07 03:07:01 +02:00
Volodymyr Kozieiev 28a81f2dd8 🎄 🧹 New Year cleanup (#21868)
* big cleannup: warnings fixed, unused code removed
2025-01-06 12:03:36 +00:00
Yevheniia Berdnyk 826410b687 e2e: cleanup base_test_case and conftest 2025-01-03 18:14:20 +02:00
Siddarth Kumar 1aa7594c41 android: kotlinify encryption utils (#21878) 2025-01-03 21:21:11 +05:30
Siddarth Kumar 5ff1d70357 android: kotlinify ui helper (#21879) 2025-01-03 21:03:06 +05:30
Siddarth Kumar 2a95f7cfac android: kotlinify root util (#21877) 2025-01-03 20:30:48 +05:30
frank 4654c4a543 fix(sha3)_: support hex string (#21837)
* fix(sha3)_: support hex string

https://github.com/status-im/status-go/compare/84493728...e01e489e
2025-01-03 08:44:02 +08:00
Mohamed Javid baf3b8c9ed fix(wallet)_: Send flow for collectibles (#21871)
This commit fixes

- the send flow for collectibles (ERC721 and ERC1155)
- picking the right balance of the ERC1155 in the send flow from the account page, based on the account viewing by the user
- the balance of ERC1155 is sometimes incorrect if the collectible is present in more than one account of the user

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2025-01-02 23:40:11 +05:30
Brian Sztamfater c6ff90954f fix_: add token and selected network on "Select network to receive" screen (#21872) 2025-01-02 11:40:04 -03:00
Tetiana Churikova c37498a6b2 e2e: big cleanup (#21875) 2024-12-30 21:10:54 +01:00
Brian Sztamfater c1b0f3ae98 fix_: include assets only related to selected network on edit asset drawer on send and bridge flows (#21869)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-12-30 11:36:43 -03:00
Icaro Motta 5007dc757f chore: Bump status-go version to include fix for bad peer check for lightmode (#21865)
Upgrades status-go to version v8.0.0, which includes the fix from PR
https://github.com/status-im/status-go/pull/5964 fix_: bad peer check logic for
lightmode and some fixes.

Might resolve https://github.com/status-im/status-mobile/issues/18031

Areas that may be impacted: we are moving from status-go version 7.0.0 to 8.0.0.
These are the changes:

- https://github.com/status-im/status-go/commit/a4e36d49c * fix_: missing message verifier start-stop ,go-waku updates, lightpush service rate limits (#5964)
- https://github.com/status-im/status-go/commit/810468a57 * test_: create private group tests (#6225)
- https://github.com/status-im/status-go/commit/0cf556bdb * test_: unskip test initialize logging (#6229)
- https://github.com/status-im/status-go/commit/b803918c2 * ci_: pytest-lint correct target pr branch (#6230)
- https://github.com/status-im/status-go/commit/d29120447 * chore(block)!: remove desktop BlockContact function for parity (#6172)
- https://github.com/status-im/status-go/commit/309d17ae5 * feat_: status-backend health endpoint (#6201)
- https://github.com/status-im/status-go/commit/6a5623bac * test_: set proxy creds (#6211)
- https://github.com/status-im/status-go/commit/2172abe95 * fix_: add infuraSecret and statusProxyBlockchainPassword to secrets (#6217)
- https://github.com/status-im/status-go/commit/0f7c26dc9 * test_: skip flaky TestService_IncrementalUpdateMixed (#6205)
- https://github.com/status-im/status-go/commit/75bdda271 * fix_: correct sensitive regex (#6188)
- https://github.com/status-im/status-go/commit/08eee8a64 * test_: add python linters (#6212)
- https://github.com/status-im/status-go/commit/66850321e * fix_: remove logging of rpc endpoint (#6195)
- https://github.com/status-im/status-go/commit/c27c773c2 * chore(wallet)_: from and to chains added to send details
- https://github.com/status-im/status-go/commit/74db63143 * test_: contact requests tests (#6167)
- https://github.com/status-im/status-go/commit/5a8310d5a * fix(pins)_: delete pins when the og message is deleted (#6173)
- https://github.com/status-im/status-go/commit/137698e02 * ci_: bring back cmd coverage (#6204)
- https://github.com/status-im/status-go/commit/1795620df * test_: run functional tests on host (no container) (#6159)
- https://github.com/status-im/status-go/commit/ef177c1c6 * feat_: SensitiveString type (#6190)
- https://github.com/status-im/status-go/commit/8b95c8148 * feat_: graceful shutdown with status-backend (#6189)
- https://github.com/status-im/status-go/commit/e6c2f89f0 * chore_: remove endpoint InitLogging (#6182)
- https://github.com/status-im/status-go/commit/4ccb08f11 * test_: WalletService class (#6171)
- https://github.com/status-im/status-go/commit/943ae1335 * fix(functional-tests)_: disable testnet (#6181)
- https://github.com/status-im/status-go/commit/616a76098 * feat_: api logging signals (#6150)
- https://github.com/status-im/status-go/commit/dfb591874 * fix_: check group error in GetBalancesAtByChain (#6169)
- https://github.com/status-im/status-go/commit/3a0080281 * fix_: create/restore account error signal (#6174)
- https://github.com/status-im/status-go/commit/e7cc53529 * feat(telem)_: track total waku message bandwidth (#6157)
- https://github.com/status-im/status-go/commit/d07e61f61 * fix_: nil *MessengerResponse is a valid result (#6175))
2024-12-26 22:20:43 -03:00
Tetiana Churikova b09f3fa6da logs: enable nightly (#21864) 2024-12-26 13:46:21 +01:00
Yevheniia Berdnyk b11c817392 e2e: LambdaTest 2024-12-26 11:44:48 +02:00
Parvesh Monu 247674b280 Onboarding remove identifiers code and refactor create_or_sync_profile screen (#21668) 2024-12-24 21:34:45 +05:30
Mohsen 07ea097bb4 [#21336] feat: upgrade wallet connect (#21833) 2024-12-24 13:13:52 +03:00
841 changed files with 11426 additions and 27947 deletions
-185
View File
@@ -1,185 +0,0 @@
legacy.status-im.utils.build/warning-handler
legacy.status-im.utils.build/get-current-sha
status-im.constants/spacing-char
status-im.constants/arg-wrapping-char
legacy.status-im.ios.core/init
legacy.status-im.ui.components.camera/aspects
legacy.status-im.ui.components.camera/capture-targets
legacy.status-im.ui.components.camera/set-torch
legacy.status-im.ui.components.camera/request-access-ios
legacy.status-im.chat.models.message-content/stylings
legacy.status-im.chat.models.message-content/actions
legacy.status-im.chat.models.message-content/blank-string
legacy.status-im.chat.models.message-content/sorted-ranges
legacy.status-im.ethereum.mnemonic/words->passphrase
native-module.core/listener
native-module.core/multiaccount-reset
native-module.core/extract-group-membership-signatures
native-module.core/sign-group-membership
native-module.core/update-mailservers
legacy.status-im.ethereum.abi-spec/bytes-to-hex
legacy.status-im.android.core/init
legacy.status-im.chat.models.message/transport-keys
legacy.status-im.utils.datetime/week
legacy.status-im.utils.datetime/time-ago
legacy.status-im.utils.datetime/format-date
legacy.status-im.utils.datetime/get-ordinal-date
reagent.dom/render
reagent.dom/unmount-component-at-node
reagent.dom/dom-node
reagent.dom/force-update-all
legacy.status-im.multiaccounts.create.core/get-new-key-code
legacy.status-im.ethereum.decode/string
legacy.status-im.ui.screens.network.views/mainnet?
legacy.status-im.data-store.chats/event->string
legacy.status-im.mailserver.constants/seven-days
legacy.status-im.mailserver.constants/connection-timeout
legacy.status-im.hardwallet.card/install-applet
legacy.status-im.hardwallet.card/install-cash-applet
legacy.status-im.multiaccount.login.data-test/all-contacts
legacy.status-im.multiaccount.login.data-test/multiaccounts
legacy.status-im.multiaccount.login.data-test/get-chats
legacy.status-im.multiaccount.login.data-test/transport
legacy.status-im.multiaccount.login.data-test/topics
legacy.status-im.utils.utils/clear-timeout
legacy.status-im.utils.handlers/logged-in
legacy.status-im.multiaccounts.model/credentials
legacy.status-im.multiaccounts.login.core/contract-fleet?
legacy.status-im.multiaccounts.login.core/fetch-nodes
status-im.config/rpc-networks-only?
status-im.config/waku-enabled?
legacy.status-im.utils.pairing/has-paired-installations?
legacy.status-im.tribute-to-talk.core-test/user-cofx
mocks.js-dependencies/action-button
mocks.js-dependencies/camera
mocks.js-dependencies/dismiss-keyboard
mocks.js-dependencies/emoji-picker
mocks.js-dependencies/i18n
mocks.js-dependencies/qr-code
mocks.js-dependencies/svg
mocks.js-dependencies/icons
mocks.js-dependencies/webview
mocks.js-dependencies/desktop-linking
mocks.js-dependencies/desktop-shortcuts
mocks.js-dependencies/snoopy
mocks.js-dependencies/snoopy-filter
mocks.js-dependencies/snoopy-bars
mocks.js-dependencies/snoopy-buffer
mocks.js-dependencies/desktop-menu
mocks.js-dependencies/desktop-config
mocks.js-dependencies/react-native-navigation-twopane
mocks.js-dependencies/react-native-screens
mocks.js-dependencies/react-native-shake
mocks.js-dependencies/back-handler
mocks.js-dependencies/react
mocks.js-dependencies/react-navigation-native
mocks.js-dependencies/react-navigation-stack
mocks.js-dependencies/react-navigation-bottom-tabs
mocks.js-dependencies/react-native-haptic-feedback
mocks.js-dependencies/mock
quo.previews.main/init
legacy.status-im.chat.models.message-list-test/descending-range
legacy.status-im.chat.models.message-list-test/random-range
legacy.status-im.ethereum.eip165/supports?
legacy.status-im.utils.hex/valid-hex?
legacy.status-im.multiaccounts.core/chat-send?
legacy.status-im.hardwallet.simulated-keycard/install-cash-applet
legacy.status-im.hardwallet.simulated-keycard/sign-typed-data
legacy.status-im.utils.core/safe-read-message-content
legacy.status-im.ui.components.react/native-modules
legacy.status-im.ui.components.react/progress-bar
react-native.fs/move-file
react-native.fs/read-dir
react-native.fs/mkdir
react-native.fs/unlink
react-native.fs/file-exists?
legacy.status-im.ui.components.colors/white
legacy.status-im.ui.components.colors/black
legacy.status-im.ui.components.core/animated-header
legacy.status-im.ui.components.core/safe-area-provider
legacy.status-im.ui.components.core/safe-area-consumer
legacy.status-im.ui.components.core/safe-area-view
legacy.status-im.utils.universal-links.core/open!
legacy.status-im.transport.filters.core-test/member-2
legacy.status-im.ui.screens.chat.ttt/one-to-one-chat-description-container
legacy.status-im.ethereum.ens/ttl
legacy.status-im.ethereum.ens/addr-hash
legacy.status-im.ethereum.ens/name-hash
legacy.status-im.ethereum.ens/ABI-hash
legacy.status-im.ethereum.ens/pubkey-hash
legacy.status-im.network.core/get-network
status-im.constants/desktop-content-types
status-im.constants/blocks-per-hour
status-im.constants/one-earth-day
status-im.constants/left-pane-min-width
status-im.constants/system
status-im.constants/contact-discovery
status-im.constants/send-transaction-failed-parse-response
status-im.constants/send-transaction-failed-parse-params
status-im.constants/send-transaction-no-account-selected
status-im.constants/send-transaction-invalid-tx-sender
status-im.constants/web3-get-logs
status-im.constants/web3-transaction-receipt
status-im.constants/web3-new-filter
status-im.constants/web3-new-pending-transaction-filter
status-im.constants/web3-new-block-filter
status-im.constants/web3-uninstall-filter
status-im.constants/web3-get-filter-changes
status-im.constants/web3-shh-post
status-im.constants/web3-shh-new-identity
status-im.constants/web3-shh-has-identity
status-im.constants/web3-shh-new-group
status-im.constants/web3-shh-add-to-group
status-im.constants/web3-shh-new-filter
status-im.constants/web3-shh-uninstall-filter
status-im.constants/web3-shh-get-filter-changes
status-im.constants/web3-shh-get-messages
status-im.constants/status-create-address
status-im.constants/event-transfer-hash
status-im.constants/regx-rtl-characters
status-im.constants/desktop-msg-chars-hard-limit
status-im.constants/debug-metrics
status-im.constants/scan-qr-code
status-im.constants/ipfs-proto-code
status-im.constants/swarm-proto-code
legacy.status-im.multiaccounts.update.publisher/publish-update!
legacy.status-im.utils.async/task-queue
legacy.status-im.utils.async/async-periodic-run!
legacy.status-im.desktop.core/app-root
legacy.status-im.desktop.core/init
legacy.status-im.chat.models-test/test-db
legacy.status-im.ui.components.spacing/padding-vertical
legacy.status-im.utils.money/percent-change
utils.debounce/clear-all
legacy.status-im.transport.db/create-chat
legacy.status-im.utils.priority-map/priority-map
legacy.status-im.utils.priority-map/priority-map-by
legacy.status-im.utils.priority-map/priority-map-keyfn
legacy.status-im.utils.priority-map/empty-message-map
legacy.status-im.wallet.choose-recipient.core/use-default-eth-gas
legacy.status-im.ui.components.animation/decay
legacy.status-im.ui.components.animation/remove-all-listeners
legacy.status-im.ui.components.animation/stop-animation
legacy.status-im.ui.components.animation/animated
legacy.status-im.ui.components.animation/add
legacy.status-im.ui.components.animation/subtract
legacy.status-im.ui.components.animation/get-layout
legacy.status-im.wallet.transactions-test/uri-query-data
legacy.status-im.utils.name/shortened-name
legacy.status-im.ui.components.button.haptic/trigger
legacy.status-im.contact.db/filter-dapps
legacy.status-im.contact.db/filter-group-contacts
legacy.status-im.contact.db/legacy-pending?
legacy.status-im.utils.transducers-test/preview-call-1
legacy.status-im.utils.transducers-test/preview-call-2
legacy.status-im.utils.types/to-string
legacy.status-im.chat.db/datemark?
legacy.status-im.chat.db/gap?
legacy.status-im.chat.db/map->sorted-seq
legacy.status-im.utils.snoopy/status-module-filter
legacy.status-im.utils.snoopy/websocket-filter
legacy.status-im.utils.snoopy/touches-filter
legacy.status-im.utils.snoopy/native-animation-filter
legacy.status-im.utils.snoopy/keyboard-observer-filter
legacy.status-im.utils.multihash/hex
legacy.status-im.utils.multihash/decode-array
+28 -3
View File
@@ -4,7 +4,7 @@
legacy.status-im.utils.views/letsubs clojure.core/let
reagent.core/with-let clojure.core/let
legacy.status-im.utils.fx/defn clj-kondo.lint-as/def-catch-all
utils.re-frame/defn clj-kondo.lint-as/def-catch-all
utils.re-frame/defn clojure.core/defn
quo.react/with-deps-check clojure.core/fn
quo.previews.preview/list-comp clojure.core/for
legacy.status-im.utils.styles/def clojure.core/def
@@ -13,7 +13,29 @@
test-helpers.unit/deftest-event clojure.core/defn
taoensso.tufte/defnp clojure.core/defn}
:linters {:case-duplicate-test {:level :error}
:linters {:clojure-lsp/unused-public-var {:exclude-when-defined-by #{utils.re-frame/defn}
:exclude #{
;; even if we are not
;; using color now, we
;; will need it later
quo.foundations.colors
;; same for funcs from
;; native-module
native-module.core
;; some funcs used
;; temporarily during
;; development
test-helpers.component
test-helpers.unit
;; not sure if unused
;; functions here should
;; be removed:
keycard.keycard
react-native.fs}}
:case-duplicate-test {:level :error}
:case-quoted-test {:level :error}
:case-symbol-test {:level :error}
:clj-kondo-config {:level :error}
@@ -40,7 +62,8 @@
:equals-true {:level :error}
:inline-def {:level :error}
:invalid-arity {:skip-args [legacy.status-im.utils.fx/defn
utils.re-frame/defn]}
utils.re-frame/defn
utils.re-frame/merge]}
:loop-without-recur {:level :error}
:minus-one {:level :error}
:misplaced-docstring {:level :error}
@@ -53,6 +76,8 @@
:redundant-do {:level :error}
:redundant-let {:level :error}
:refer-all {:level :error}
;; ignore error in config itself saying that `:shadowed-fn-param` is unknown
#_{:clj-kondo/ignore [:clj-kondo-config]}
:shadowed-fn-param {:level :error}
:shadowed-var {:level :error
;; We temporarily use :include to define an
+2
View File
@@ -36,3 +36,5 @@ SHOW_NOT_IMPLEMENTED_FEATURES=0
ENABLE_ALERT_BANNER=0
FLAG_WALLET_CONNECT_ENABLED=1
API_LOGGING_ENABLED=1
SENTRY_ENABLED=0
SENTRY_ENVIRONMENT=ci-main
+2
View File
@@ -40,3 +40,5 @@ ENABLE_ALERT_BANNER=0
FLAG_WALLET_CONNECT_ENABLED=1
MOBILE_DATA_SYNCING_TOGGLE_ENABLE=0
API_LOGGING_ENABLED=1
SENTRY_ENABLED=0
SENTRY_ENVIRONMENT=ci-main
+2
View File
@@ -37,3 +37,5 @@ TEST_NETWORKS_ENABLED=1
ENABLE_ALERT_BANNER=1
FLAG_WALLET_CONNECT_ENABLED=1
API_LOGGING_ENABLED=1
SENTRY_ENABLED=0
SENTRY_ENVIRONMENT=ci-main
+4 -2
View File
@@ -2,7 +2,7 @@ DEBUG_WEBVIEW=1
ETHEREUM_DEV_CLUSTER=1
EXTENSIONS=0
GROUP_CHATS_ENABLED=1
LOG_LEVEL=info
LOG_LEVEL=debug
MAILSERVER_CONFIRMATIONS_ENABLED=1
MAINNET_WARNING_ENABLED=1
PFS_ENCRYPTION_ENABLED=1
@@ -23,4 +23,6 @@ FAST_CREATE_COMMUNITY_ENABLED=0
TEST_NETWORKS_ENABLED=0
ENABLE_ALERT_BANNER=1
FLAG_WALLET_CONNECT_ENABLED=1
API_LOGGING_ENABLED=0
API_LOGGING_ENABLED=1
SENTRY_ENABLED=1
SENTRY_ENVIRONMENT=ci-nightly
+3 -1
View File
@@ -2,7 +2,7 @@ DEBUG_WEBVIEW=0
ETHEREUM_DEV_CLUSTER=0
EXTENSIONS=0
GROUP_CHATS_ENABLED=1
LOG_LEVEL=
LOG_LEVEL=error
MAILSERVER_CONFIRMATIONS_ENABLED=1
MAINNET_WARNING_ENABLED=1
PFS_ENCRYPTION_ENABLED=1
@@ -21,3 +21,5 @@ TEST_NETWORKS_ENABLED=0
STATUS_PROXY_STAGE_NAME=prod
FLAG_WALLET_CONNECT_ENABLED=1
API_LOGGING_ENABLED=0
SENTRY_ENABLED=1
SENTRY_ENVIRONMENT=production
+16 -34
View File
@@ -1,39 +1,26 @@
[comment]: # (Please replace ... with your information. Remove < and >)
[comment]: # (To auto-close issue on merge, please insert the related issue number after # i.e fixes #566)
If you submit PR for issue with bounty then write here Fixes #NN where NN is issue number
*otherwise*
fixes #...
### Summary
## Summary
[comment]: # (Summarise the problem and how the pull request solves it)
...
<!-- (Optional, remove if no changes to documentation) -->
Documentation change PR (review please): https://github.com/status-im/status.im/pull/xxx
## Review notes
[comment]: # (Optional. Specify if something in particular should be looked at, or ignored, during review)
### Review notes
<!-- (Optional. Specify if something in particular should be looked at, or ignored, during review) -->
## Testing notes
[comment]: # (Optional)
### Testing notes
<!-- (Optional) -->
#### Platforms
<!-- (Optional. Specify which platforms should be tested) -->
### Platforms
[comment]: # (Optional. Specify which platforms should be tested)
- Android
- iOS
- macOS
- Linux
- Windows
#### Areas that maybe impacted
<!-- (Optional. Specify if some specific areas has to be tested, for example 1-1 chats) -->
### Areas that may be impacted
[comment]: # (Optional. Specify if some specific areas need to be tested, such as 1-1 chats)
##### Functional
#### Functional
- 1-1 chats
- public chats
@@ -48,32 +35,27 @@ Documentation change PR (review please): https://github.com/status-im/status.im/
- fleet
- bootnodes
##### Non-functional
#### Non-functional
- battery performance
- CPU performance / speed of the app
- network consumption
### Steps to test
<!-- (Specify exact steps to test if there are such) -->
## Steps to test
[comment]: # (Specify exact steps to test if there are such)
- Open Status
- ...
- Step 3, etc.
<!-- (PRs will only be accepted if squashed into single commit.) -->
### Before and after screenshots comparison
[comment]: # (Can be ready or wip)
status: ready
| Figma (if available) | iOS (if available) | Android (if available)
| --- | --- | --- |
| Please embed Image/Video here of the before and after. | Please embed Image/Video here of the before and after. | Please embed Image/Video here of the before and after. |
status: ready <!-- Can be ready or wip -->
<!-- Uncomment this section for status-go upgrade/dogfooding pull requests
- Specify potentially impacted user flows in _Areas that maybe impacted*.
- Specify potentially impacted user flows in _Areas that may be impacted*.
- Ensure that _Steps to test_ is filled in.
### Risk
+4 -4
View File
@@ -43,8 +43,6 @@ export NODE_OPTIONS += --openssl-legacy-provider
# The path can be anything, but home is usually safest.
export KEYSTORE_PATH ?= $(HOME)/.gradle/status-im.keystore
# Our custom config is located in nix/nix.conf
export NIX_USER_CONF_FILES = $(PWD)/nix/nix.conf
# Location of symlinks to derivations that should not be garbage collected
export _NIX_GCROOTS = ./.nix-gcroots
# Defines which variables will be kept for Nix pure shell, use semicolon as divider
@@ -269,9 +267,11 @@ run-metro: export TARGET := clojure
run-metro: ##@run Start Metro to build React Native changes
@scripts/run-metro.sh
export RE_FRISK_PORT ?= 4567
run-re-frisk: export TARGET := clojure
run-re-frisk: ##@run Start re-frisk server
yarn shadow-cljs run re-frisk-remote.core/start
yarn shadow-cljs run re-frisk-remote.core/start ${RE_FRISK_PORT}
# TODO: Migrate this to a Nix recipe, much the same way as nix/mobile/android/targets/release-android.nix
run-android: export TARGET := android
@@ -445,7 +445,7 @@ android-ports: export RCT_METRO_PORT ?= 8081
android-ports: ##@other Add proxies to Android Device/Simulator
adb reverse tcp:$(RCT_METRO_PORT) tcp:$(RCT_METRO_PORT) && \
adb reverse tcp:3449 tcp:3449 && \
adb reverse tcp:4567 tcp:4567 && \
adb reverse tcp:$(RE_FRISK_PORT) tcp:$(RE_FRISK_PORT) && \
adb reverse tcp:$(FLOWSTORM_PORT) tcp:$(FLOWSTORM_PORT) && \
adb forward tcp:5561 tcp:5561
+4
View File
@@ -143,6 +143,10 @@ android {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17
}
// Disable default lint checks to remove redundant lintVitalReportRelease task
lintOptions {
checkReleaseBuilds false
}
// https://developer.android.com/studio/projects/install-ndk#vanilla_cmake
externalNativeBuild {
@@ -1,40 +0,0 @@
package im.status.ethereum;
import java.io.File;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/** @author Kevin Kowalewski */
public class RootUtil {
public static boolean isDeviceRooted() {
return checkRootMethod1() || checkRootMethod2() || checkRootMethod3();
}
private static boolean checkRootMethod1() {
String buildTags = android.os.Build.TAGS;
return buildTags != null && buildTags.contains("test-keys");
}
private static boolean checkRootMethod2() {
String[] paths = { "/system/app/Superuser.apk", "/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su",
"/system/bin/failsafe/su", "/data/local/su" };
for (String path : paths) {
if (new File(path).exists()) return true;
}
return false;
}
private static boolean checkRootMethod3() {
Process process = null;
try {
process = Runtime.getRuntime().exec(new String[] { "/system/xbin/which", "su" });
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
return in.readLine() != null;
} catch (Throwable t) {
return false;
} finally {
if (process != null) process.destroy();
}
}
}
@@ -0,0 +1,42 @@
package im.status.ethereum
import android.os.Build
import java.io.BufferedReader
import java.io.File
import java.io.InputStreamReader
object RootUtil {
fun isDeviceRooted(): Boolean =
checkRootMethod1() || checkRootMethod2() || checkRootMethod3()
private fun checkRootMethod1(): Boolean =
Build.TAGS?.contains("test-keys") ?: false
private fun checkRootMethod2(): Boolean {
val paths = arrayOf(
"/system/app/Superuser.apk",
"/sbin/su",
"/system/bin/su",
"/system/xbin/su",
"/data/local/xbin/su",
"/data/local/bin/su",
"/system/sd/xbin/su",
"/system/bin/failsafe/su",
"/data/local/su"
)
return paths.any { File(it).exists() }
}
private fun checkRootMethod3(): Boolean {
var process: Process? = null
return try {
process = Runtime.getRuntime().exec(arrayOf("/system/xbin/which", "su"))
BufferedReader(InputStreamReader(process.inputStream)).readLine() != null
} catch (t: Throwable) {
false
} finally {
process?.destroy()
}
}
}
+3 -2
View File
@@ -1,11 +1,11 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.13'
library 'status-jenkins-lib@v1.9.20'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
pipeline {
agent { label 'linux && x86_64 && nix-2.19' }
agent { label 'linux && x86_64 && nix-2.24' }
options {
timestamps()
@@ -41,6 +41,7 @@ pipeline {
BUILD_ENV = 'prod'
NIX_CONF_DIR = "${env.WORKSPACE}/nix"
FASTLANE_DISABLE_COLORS = 1
SENTRY_PRODUCTION = "${utils.isReleaseBuild() ? 'true' : 'false'}"
}
stages {
+1 -2
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.13'
library 'status-jenkins-lib@v1.9.20'
import groovy.json.JsonBuilder
@@ -134,4 +134,3 @@ def updateBucketJSON(urls, fileName) {
writeFile(file: filePath, text: contentJson)
return s5cmd.upload(filePath)
}
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.13'
library 'status-jenkins-lib@v1.9.20'
pipeline {
agent { label 'linux' }
+3 -2
View File
@@ -1,11 +1,11 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.13'
library 'status-jenkins-lib@v1.9.20'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
pipeline {
agent { label 'macos && arm64 && nix-2.19 && xcode-15.1' }
agent { label 'macos && arm64 && nix-2.24 && xcode-16.2' }
parameters {
string(
@@ -41,6 +41,7 @@ pipeline {
NIX_CONF_DIR = "${env.WORKSPACE}/nix"
FASTLANE_DISABLE_COLORS = 1
BUNDLE_PATH = "${HOME}/.bundle"
SENTRY_PRODUCTION = "${utils.isReleaseBuild() ? 'true' : 'false'}"
}
stages {
+2 -2
View File
@@ -1,11 +1,11 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.13'
library 'status-jenkins-lib@v1.9.20'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
pipeline {
agent { label 'linux && x86_64 && nix-2.19' }
agent { label 'linux && x86_64 && nix-2.24' }
options {
timestamps()
+5 -21
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.13'
library 'status-jenkins-lib@v1.9.20'
pipeline {
@@ -73,9 +73,9 @@ pipeline {
passwordVariable: 'TESTRAIL_PASS'
),
usernamePassword(
credentialsId: 'sauce-labs-api',
usernameVariable: 'SAUCE_USERNAME',
passwordVariable: 'SAUCE_ACCESS_KEY'
credentialsId: 'lambda-test-api',
usernameVariable: 'LAMBDA_TEST_USERNAME',
passwordVariable: 'LAMBDA_TEST_ACCESS_KEY'
),
string(
credentialsId: 'etherscan-api-key',
@@ -95,8 +95,7 @@ pipeline {
sh 'cp -f $TEST_ETH_ACCOUNTS_FILE users.py'
sh """
python3 -m pytest \
--numprocesses 8 \
--rerun_count=2 \
--numprocesses 9 \
--testrail_report=True \
-m testrail_id \
-m \"nightly\" \
@@ -110,21 +109,6 @@ pipeline {
}
post {
always {
script {
sauce('sauce-labs-cred') {
saucePublisher()
}
}
}
success {
script {
junit(
testDataPublishers: [[$class: 'SauceOnDemandReportPublisher', jobVisibility: 'public']],
testResults: 'test/appium/tests/*.xml'
)
}
}
cleanup {
sh 'make purge'
}
+5 -6
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.13'
library 'status-jenkins-lib@v1.9.20'
pipeline {
@@ -111,9 +111,9 @@ pipeline {
passwordVariable: 'TESTRAIL_PASS'
),
usernamePassword(
credentialsId: 'sauce-labs-api',
usernameVariable: 'SAUCE_USERNAME',
passwordVariable: 'SAUCE_ACCESS_KEY'
credentialsId: 'lambda-test-api',
usernameVariable: 'LAMBDA_TEST_USERNAME',
passwordVariable: 'LAMBDA_TEST_ACCESS_KEY'
),
string(
credentialsId: 'etherscan-api-key',
@@ -133,8 +133,7 @@ pipeline {
sh 'cp -f $TEST_ETH_ACCOUNTS_FILE users.py'
sh """
python3 -m pytest \
--numprocesses 8 \
--rerun_count=2 \
--numprocesses 9 \
--testrail_report=True \
-k \"${params.KEYWORD_EXPRESSION}\" \
--apk=${params.APK_URL ?: apk_path} \
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.13'
library 'status-jenkins-lib@v1.9.20'
pipeline {
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.13'
library 'status-jenkins-lib@v1.9.20'
pipeline {
agent { label 'macos' }
+13 -4
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.13'
library 'status-jenkins-lib@v1.9.20'
pipeline {
agent { label params.AGENT_LABEL }
@@ -109,10 +109,19 @@ pipeline {
stage('Upload') {
steps { script {
sshagent(credentials: ['nix-cache-ssh']) {
nix.shell("""
nix.shell(
"""
find /nix/store/ -mindepth 1 -maxdepth 1 -type d \
-not -name '*.links' -and -not -name '*-status-mobile-*' \
| xargs nix copy --to ${NIX_SSH_REMOTE}
-not -name "*.links" -and -not -name "*-status-mobile-*" \
-and -not -name "tmp-*" \
-print0 | xargs -0 nix-store -qR | sort -u > "${WORKSPACE_TMP}"/store-paths.txt
""",
pure: false
)
nix.shell(
"""
nix-store --export < "${WORKSPACE_TMP}"/store-paths.txt | \\
ssh ${env.NIX_SSHOPTS} ${params.NIX_CACHE_USER}@${params.NIX_CACHE_HOST} ${env.NIX_STORE_CMD} --import
""",
pure: false
)
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.13'
library 'status-jenkins-lib@v1.9.20'
pipeline {
agent { label 'linux' }
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.13'
library 'status-jenkins-lib@v1.9.20'
pipeline {
agent {
+1
View File
@@ -16,6 +16,7 @@
- [Patching](patching.md)
- [Creating a pixel perfect UI](pixel-perfection.md)
- [Contributing to status-go](status-go-changes.md)
- [Status Backend Server](use-status-backend-server.md)
- [Malli schemas (recorded demo)](https://www.youtube.com/watch?v=SlRio70aYVI) ([slides](files/forging-code-with-schemas-sep-2023-slides.pdf))
## Testing
+1 -1
View File
@@ -1 +1 @@
Status: محفظة تشفير اجتماعية
Status: محفظة إيثريوم كريبتو
+1 -1
View File
@@ -1 +1 @@
Status: Social Crypto Wallet
Status: Ethereum Crypto Wallet
+1 -1
View File
@@ -1 +1 @@
Status: Billetera social cripto
Status: Billetera Ethereum
+1 -1
View File
@@ -1 +1 @@
Status : Portef. crypto social
Status: Portefeuille Ethereum
+1 -1
View File
@@ -1 +1 @@
Status: ソーシャル クリプト ウォレット
Status: イーサリアム クリプト ウォレット
+1 -1
View File
@@ -1 +1 @@
Status: 소셜 크립토 지갑
Status: 이더리움 크립토 지갑
+1 -1
View File
@@ -1 +1 @@
Status: Carteira social cripto
Status: Carteira Ethereum
+1 -1
View File
@@ -1 +1 @@
Status: Криптокошелек
Status: Ethereum Криптокошелек
+1 -1
View File
@@ -1 +1 @@
Status: Sosyal Kripto Cüzdanı
Status Ethereum Kripto Cüzdanı
+1 -1
View File
@@ -1 +1 @@
Status: Криптогаманець
Status Ethereum Криптогаманець
+1 -1
View File
@@ -1 +1 @@
Status: 可以社交的加密货币钱包
Status: 以太坊加密钱包
+23 -20
View File
@@ -23,15 +23,18 @@ PODS:
- secp256k1
- SSZipArchive
- libevent (2.1.12)
- libwebp (1.2.4):
- libwebp/demux (= 1.2.4)
- libwebp/mux (= 1.2.4)
- libwebp/webp (= 1.2.4)
- libwebp/demux (1.2.4):
- libwebp (1.3.2):
- libwebp/demux (= 1.3.2)
- libwebp/mux (= 1.3.2)
- libwebp/sharpyuv (= 1.3.2)
- libwebp/webp (= 1.3.2)
- libwebp/demux (1.3.2):
- libwebp/webp
- libwebp/mux (1.2.4):
- libwebp/mux (1.3.2):
- libwebp/demux
- libwebp/webp (1.2.4)
- libwebp/sharpyuv (1.3.2)
- libwebp/webp (1.3.2):
- libwebp/sharpyuv
- RCT-Folly (2022.05.16.00):
- boost
- DoubleConversion
@@ -917,7 +920,7 @@ PODS:
- glog
- RCT-Folly (= 2022.05.16.00)
- React-Core
- react-native-compat (2.11.2):
- react-native-compat (2.17.3):
- glog
- RCT-Folly (= 2022.05.16.00)
- React-Core
@@ -1152,15 +1155,15 @@ PODS:
- glog
- RCT-Folly (= 2022.05.16.00)
- React-Core
- RNImageCropPicker (0.40.0):
- RNImageCropPicker (0.41.2):
- React-Core
- React-RCTImage
- RNImageCropPicker/QBImagePickerController (= 0.40.0)
- TOCropViewController
- RNImageCropPicker/QBImagePickerController (0.40.0):
- RNImageCropPicker/QBImagePickerController (= 0.41.2)
- TOCropViewController (~> 2.7.4)
- RNImageCropPicker/QBImagePickerController (0.41.2):
- React-Core
- React-RCTImage
- TOCropViewController
- TOCropViewController (~> 2.7.4)
- RNKeychain (8.1.2):
- React-Core
- RNLinearGradient (3.0.0-alpha.1):
@@ -1189,7 +1192,7 @@ PODS:
- secp256k1 (0.1.6)
- SocketRocket (0.6.1)
- SSZipArchive (2.4.3)
- TOCropViewController (2.6.1)
- TOCropViewController (2.7.4)
- Yoga (1.14.0)
DEPENDENCIES:
@@ -1489,7 +1492,7 @@ CHECKOUT OPTIONS:
:git: https://github.com/status-im/Keycard.swift.git
:tag: 3.1.1
secp256k1:
:commit: 46a1fa30d9b8babeae85ff519050f42394ab5fcc
:commit: 4ab977cc2b2d7319be858bcb30a5d189bb149884
:git: https://github.com/status-im/secp256k1.swift.git
:submodules: true
@@ -1506,7 +1509,7 @@ SPEC CHECKSUMS:
HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352
Keycard: 3bd24405b9a875b3b3cf1ab1be69b5c30c5f28bc
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
RCT-Folly: 7169b2b1c44399c76a47b5deaaba715eeeb476c0
RCTRequired: 2544c0f1081a5fa12e108bb8cb40e5f4581ccd87
RCTTypeSafety: 50efabe2b115c11ed03fbf3fd79e2f163ddb5d7c
@@ -1533,7 +1536,7 @@ SPEC CHECKSUMS:
react-native-blob-util: 600972b1782380a5a7d5db61a3817ea32349dae9
react-native-blur: b2b140d3d65077a1d5d26be62d9415f895592344
react-native-cameraroll: af8eec1e585d053ff485d98ec837f9a8a11b5745
react-native-compat: 3af9add14d349701306d3d052638435f6795ac2c
react-native-compat: fd639220cca206df6c74b95df56a1809d78ba5a3
react-native-config: 5330c8258265c1e5fdb8c009d2cabd6badd96727
react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06
react-native-hole-view: 6935448993bac79f2b5a4ad7e9741094cf810679
@@ -1577,7 +1580,7 @@ SPEC CHECKSUMS:
RNFastImage: 1f2cab428712a4baaf78d6169eaec7f622556dd7
RNFS: 2bd9eb49dc82fa9676382f0585b992c424cd59df
RNGestureHandler: 15c6ef51acba34c49ff03003806cf5dd6098f383
RNImageCropPicker: 486e2f7e2b0461ce24321f751410dce1b3b49e6d
RNImageCropPicker: 771e2ca319d2cf92e04ebf334ece892ee9a6728f
RNKeychain: a65256b6ca6ba6976132cc4124b238a5b13b3d9c
RNLinearGradient: ccaaebce083b54180da61d992e7fa56abfe000d6
RNPermissions: 08e619529cced22695f4b6d0efcc0a233e278903
@@ -1590,9 +1593,9 @@ SPEC CHECKSUMS:
secp256k1: f61d67e6fdcb85fd727acf1bf35ace6036db540c
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654
Yoga: a716eea57d0d3430219c0a5a233e1e93ee931eb7
PODFILE CHECKSUM: 81a66b45adddb083171ade882c343deb4faf2a8b
PODFILE CHECKSUM: 56d9f695324306d6db368d1172d211dfab55b7cb
COCOAPODS: 1.15.2
+6
View File
@@ -572,6 +572,7 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Status-StatusIm-StatusImTests/Pods-Status-StatusIm-StatusImTests-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/RNImageCropPickerPrivacyInfo.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNPermissions/RNPermissionsPrivacyInfo.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle",
@@ -579,6 +580,7 @@
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNImageCropPickerPrivacyInfo.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNPermissionsPrivacyInfo.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
@@ -614,6 +616,7 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Status-StatusIm/Pods-Status-StatusIm-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/RNImageCropPickerPrivacyInfo.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNPermissions/RNPermissionsPrivacyInfo.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle",
@@ -621,6 +624,7 @@
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNImageCropPickerPrivacyInfo.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNPermissionsPrivacyInfo.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
@@ -728,6 +732,7 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Status-StatusImPR/Pods-Status-StatusImPR-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/RNImageCropPickerPrivacyInfo.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNPermissions/RNPermissionsPrivacyInfo.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle",
@@ -735,6 +740,7 @@
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNImageCropPickerPrivacyInfo.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNPermissionsPrivacyInfo.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
@@ -203,24 +203,6 @@ class AccountManager(private val reactContext: ReactApplicationContext) : ReactC
)
}
@ReactMethod
fun verify(address: String, password: String, callback: Callback) {
val absRootDirPath = utils.getNoBackupDirectory()
val newKeystoreDir = utils.pathCombine(absRootDirPath, "keystore")
val jsonParams = JSONObject()
jsonParams.put("keyStoreDir", newKeystoreDir)
jsonParams.put("address", address)
jsonParams.put("password", password)
StatusBackendClient.executeStatusGoRequestWithCallback(
endpoint = "VerifyAccountPasswordV2",
requestBody = jsonParams.toString(),
statusgoFunction = { Statusgo.verifyAccountPasswordV2(jsonParams.toString()) },
callback
)
}
@ReactMethod
fun verifyDatabasePassword(keyUID: String, password: String, callback: Callback) {
val jsonParams = JSONObject()
@@ -1,297 +0,0 @@
package im.status.ethereum.module;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.Callback;
import android.util.Log;
import statusgo.Statusgo;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.function.Function;
import android.app.Activity;
import android.view.WindowManager;
import android.os.Build;
import android.view.Window;
import android.preference.PreferenceManager;
import android.content.SharedPreferences;
public class EncryptionUtils extends ReactContextBaseJavaModule {
private static final String TAG = "EncryptionUtils";
private ReactApplicationContext reactContext;
private Utils utils;
public EncryptionUtils(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
this.utils = new Utils(reactContext);
}
@Override
public String getName() {
return "EncryptionUtils";
}
@ReactMethod
private void initKeystore(final String keyUID, final Callback callback) throws JSONException {
Log.d(TAG, "initKeystore");
final String commonKeydir = this.utils.pathCombine(this.utils.getNoBackupDirectory(), "/keystore");
final String keydir = this.utils.pathCombine(commonKeydir, keyUID);
StatusBackendClient.executeStatusGoRequestWithCallback(
"InitKeystore",
keydir,
() -> Statusgo.initKeystore(keydir),
callback
);
}
@ReactMethod
public void reEncryptDbAndKeystore(final String keyUID, final String password, final String newPassword, final Callback callback) throws JSONException {
JSONObject params = new JSONObject();
params.put("keyUID", keyUID);
params.put("oldPassword", password);
params.put("newPassword", newPassword);
String jsonParams = params.toString();
StatusBackendClient.executeStatusGoRequestWithCallback(
"ChangeDatabasePasswordV2",
jsonParams,
() -> Statusgo.changeDatabasePasswordV2(jsonParams),
callback
);
}
@ReactMethod
public void convertToKeycardAccount(final String keyUID, final String accountData, final String options, final String keycardUID, final String password,
final String newPassword, final Callback callback) throws JSONException {
final String keyStoreDir = this.utils.getKeyStorePath(keyUID);
JSONObject params = new JSONObject();
params.put("keyUID", keyUID);
params.put("account", new JSONObject(accountData));
params.put("settings", new JSONObject(options));
params.put("keycardUID", keycardUID);
params.put("oldPassword", password);
params.put("newPassword", newPassword);
final String jsonParams = params.toString();
StatusBackendClient.executeStatusGoRequest(
"InitKeystore",
keyStoreDir,
() -> Statusgo.initKeystore(keyStoreDir)
);
StatusBackendClient.executeStatusGoRequestWithCallback(
"ConvertToKeycardAccountV2",
jsonParams,
() -> Statusgo.convertToKeycardAccountV2(jsonParams),
callback
);
}
@ReactMethod(isBlockingSynchronousMethod = true)
public String encodeTransfer(final String to, final String value) {
try {
JSONObject params = new JSONObject();
params.put("to", to);
params.put("value", value);
String jsonParams = params.toString();
return StatusBackendClient.executeStatusGoRequestWithResult(
"EncodeTransferV2",
jsonParams,
() -> Statusgo.encodeTransferV2(jsonParams)
);
} catch (JSONException e) {
Log.e(TAG, "Error creating JSON for encodeTransfer: " + e.getMessage());
return null;
}
}
@ReactMethod(isBlockingSynchronousMethod = true)
public String encodeFunctionCall(final String method, final String paramsJSON) {
try {
JSONObject params = new JSONObject();
params.put("method", method);
params.put("paramsJSON", new JSONObject(paramsJSON));
String jsonString = params.toString();
return StatusBackendClient.executeStatusGoRequestWithResult(
"EncodeFunctionCallV2",
jsonString,
() -> Statusgo.encodeFunctionCallV2(jsonString)
);
} catch (JSONException e) {
Log.e(TAG, "Error creating JSON for encodeFunctionCall: " + e.getMessage());
return null;
}
}
@ReactMethod(isBlockingSynchronousMethod = true)
public String decodeParameters(final String decodeParamJSON) {
return StatusBackendClient.executeStatusGoRequestWithResult(
"DecodeParameters",
decodeParamJSON,
() -> Statusgo.decodeParameters(decodeParamJSON)
);
}
@ReactMethod(isBlockingSynchronousMethod = true)
public String hexToNumber(final String hex) {
return StatusBackendClient.executeStatusGoRequestWithResult(
"HexToNumber",
hex,
() -> Statusgo.hexToNumber(hex)
);
}
@ReactMethod(isBlockingSynchronousMethod = true)
public String numberToHex(final String numString) {
return StatusBackendClient.executeStatusGoRequestWithResult(
"NumberToHex",
numString,
() -> Statusgo.numberToHex(numString)
);
}
@ReactMethod(isBlockingSynchronousMethod = true)
public String sha3(final String str) {
return StatusBackendClient.executeStatusGoRequestWithResult(
"Sha3",
str,
() -> Statusgo.sha3(str)
);
}
@ReactMethod(isBlockingSynchronousMethod = true)
public String utf8ToHex(final String str) {
return StatusBackendClient.executeStatusGoRequestWithResult(
"Utf8ToHex",
str,
() -> Statusgo.utf8ToHex(str)
);
}
@ReactMethod(isBlockingSynchronousMethod = true)
public String hexToUtf8(final String str) {
return StatusBackendClient.executeStatusGoRequestWithResult(
"HexToUtf8",
str,
() -> Statusgo.hexToUtf8(str)
);
}
@ReactMethod(isBlockingSynchronousMethod = true)
public String serializeLegacyKey(final String publicKey) {
return StatusBackendClient.executeStatusGoRequestWithResult(
"SerializeLegacyKey",
publicKey,
() -> Statusgo.serializeLegacyKey(publicKey)
);
}
@ReactMethod
public void setBlankPreviewFlag(final Boolean blankPreview) {
final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this.reactContext);
sharedPrefs.edit().putBoolean("BLANK_PREVIEW", blankPreview).commit();
setSecureFlag();
}
private void setSecureFlag() {
final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this.reactContext);
final boolean setSecure = sharedPrefs.getBoolean("BLANK_PREVIEW", true);
final Activity activity = this.reactContext.getCurrentActivity();
if (activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
final Window window = activity.getWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && setSecure) {
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE);
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
}
}
});
}
}
@ReactMethod
public void hashTransaction(final String txArgsJSON, final Callback callback) throws JSONException {
StatusBackendClient.executeStatusGoRequestWithCallback(
"HashTransaction",
txArgsJSON,
() -> Statusgo.hashTransaction(txArgsJSON),
callback
);
}
@ReactMethod
public void hashMessage(final String message, final Callback callback) throws JSONException {
StatusBackendClient.executeStatusGoRequestWithCallback(
"HashMessage",
message,
() -> Statusgo.hashMessage(message),
callback
);
}
@ReactMethod
public void multiformatDeserializePublicKey(final String multiCodecKey, final String base58btc, final Callback callback) throws JSONException {
JSONObject params = new JSONObject();
params.put("key", multiCodecKey);
params.put("outBase", base58btc);
String jsonParams = params.toString();
StatusBackendClient.executeStatusGoRequestWithCallback(
"MultiformatDeserializePublicKeyV2",
jsonParams,
() -> Statusgo.multiformatDeserializePublicKeyV2(jsonParams),
callback
);
}
@ReactMethod
public void deserializeAndCompressKey(final String desktopKey, final Callback callback) throws JSONException {
StatusBackendClient.executeStatusGoRequestWithCallback(
"DeserializeAndCompressKey",
desktopKey,
() -> Statusgo.deserializeAndCompressKey(desktopKey),
callback
);
}
@ReactMethod
public void hashTypedData(final String data, final Callback callback) throws JSONException {
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.hashTypedData(data), callback);
}
@ReactMethod
public void hashTypedDataV4(final String data, final Callback callback) throws JSONException {
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.hashTypedDataV4(data), callback);
}
@ReactMethod
public void signMessage(final String rpcParams, final Callback callback) throws JSONException {
StatusBackendClient.executeStatusGoRequestWithCallback(
"SignMessage",
rpcParams,
() -> Statusgo.signMessage(rpcParams),
callback
);
}
@ReactMethod
public void signTypedData(final String data, final String account, final String password, final Callback callback) throws JSONException {
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.signTypedData(data, account, password), callback);
}
@ReactMethod
public void signTypedDataV4(final String data, final String account, final String password, final Callback callback) throws JSONException {
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.signTypedDataV4(data, account, password), callback);
}
@ReactMethod
public void signGroupMembership(final String content, final Callback callback) throws JSONException {
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.signGroupMembership(content), callback);
}
}
@@ -0,0 +1,258 @@
package im.status.ethereum.module
import android.app.Activity
import android.os.Build
import android.preference.PreferenceManager
import android.util.Log
import android.view.WindowManager
import com.facebook.react.bridge.*
import org.json.JSONException
import org.json.JSONObject
import statusgo.Statusgo
import java.lang.Exception
import java.lang.RuntimeException
class EncryptionUtils(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
private val utils = Utils(reactContext)
companion object {
private const val TAG = "EncryptionUtils"
}
override fun getName(): String = "EncryptionUtils"
@ReactMethod
@Throws(Exception::class)
private fun initKeystore(keyUID: String, callback: Callback) {
Log.d(TAG, "initKeystore")
val commonKeydir = utils.pathCombine(utils.getNoBackupDirectory(), "/keystore")
val keydir = utils.pathCombine(commonKeydir, keyUID)
StatusBackendClient.executeStatusGoRequestWithCallback(
"InitKeystore",
keydir,
{ Statusgo.initKeystore(keydir) },
callback
)
}
@ReactMethod
@Throws(Exception::class)
fun reEncryptDbAndKeystore(keyUID: String, password: String, newPassword: String, callback: Callback) {
val params = JSONObject().apply {
put("keyUID", keyUID)
put("oldPassword", password)
put("newPassword", newPassword)
}
StatusBackendClient.executeStatusGoRequestWithCallback(
"ChangeDatabasePasswordV2",
params.toString(),
{ Statusgo.changeDatabasePasswordV2(params.toString()) },
callback
)
}
@ReactMethod
@Throws(Exception::class)
fun convertToKeycardAccount(
keyUID: String,
accountData: String,
options: String,
keycardUID: String,
password: String,
newPassword: String,
callback: Callback
) {
val keyStoreDir = utils.getKeyStorePath(keyUID)
val params = JSONObject().apply {
put("keyUID", keyUID)
put("account", JSONObject(accountData))
put("settings", JSONObject(options))
put("keycardUID", keycardUID)
put("oldPassword", password)
put("newPassword", newPassword)
}
val jsonParams = params.toString()
StatusBackendClient.executeStatusGoRequest(
"InitKeystore",
keyStoreDir,
{ Statusgo.initKeystore(keyStoreDir) }
)
StatusBackendClient.executeStatusGoRequestWithCallback(
"ConvertToKeycardAccountV2",
jsonParams,
{ Statusgo.convertToKeycardAccountV2(jsonParams) },
callback
)
}
@ReactMethod(isBlockingSynchronousMethod = true)
fun decodeParameters(decodeParamJSON: String): String =
StatusBackendClient.executeStatusGoRequestWithResult(
"DecodeParameters",
decodeParamJSON,
{ Statusgo.decodeParameters(decodeParamJSON) }
)
@ReactMethod(isBlockingSynchronousMethod = true)
fun hexToNumber(hex: String): String =
StatusBackendClient.executeStatusGoRequestWithResult(
"HexToNumber",
hex,
{ Statusgo.hexToNumber(hex) }
)
@ReactMethod(isBlockingSynchronousMethod = true)
fun numberToHex(numString: String): String =
StatusBackendClient.executeStatusGoRequestWithResult(
"NumberToHex",
numString,
{ Statusgo.numberToHex(numString) }
)
@ReactMethod(isBlockingSynchronousMethod = true)
fun sha3(str: String): String =
StatusBackendClient.executeStatusGoRequestWithResult(
"Sha3",
str,
{ Statusgo.sha3(str) }
)
@ReactMethod(isBlockingSynchronousMethod = true)
fun utf8ToHex(str: String): String =
StatusBackendClient.executeStatusGoRequestWithResult(
"Utf8ToHex",
str,
{ Statusgo.utf8ToHex(str) }
)
@ReactMethod(isBlockingSynchronousMethod = true)
fun hexToUtf8(str: String): String =
StatusBackendClient.executeStatusGoRequestWithResult(
"HexToUtf8",
str,
{ Statusgo.hexToUtf8(str) }
)
@ReactMethod(isBlockingSynchronousMethod = true)
fun serializeLegacyKey(publicKey: String): String =
StatusBackendClient.executeStatusGoRequestWithResult(
"SerializeLegacyKey",
publicKey,
{ Statusgo.serializeLegacyKey(publicKey) }
)
@ReactMethod
fun setBlankPreviewFlag(blankPreview: Boolean) {
PreferenceManager.getDefaultSharedPreferences(reactContext)
.edit()
.putBoolean("BLANK_PREVIEW", blankPreview)
.commit()
setSecureFlag()
}
private fun setSecureFlag() {
val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(reactContext)
val setSecure = sharedPrefs.getBoolean("BLANK_PREVIEW", true)
reactContext.currentActivity?.run {
runOnUiThread {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && setSecure) {
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
}
}
}
}
@ReactMethod
@Throws(Exception::class)
fun hashTransaction(txArgsJSON: String, callback: Callback) {
StatusBackendClient.executeStatusGoRequestWithCallback(
"HashTransaction",
txArgsJSON,
{ Statusgo.hashTransaction(txArgsJSON) },
callback
)
}
@ReactMethod
@Throws(Exception::class)
fun hashMessage(message: String, callback: Callback) {
StatusBackendClient.executeStatusGoRequestWithCallback(
"HashMessage",
message,
{ Statusgo.hashMessage(message) },
callback
)
}
@ReactMethod
@Throws(Exception::class)
fun multiformatDeserializePublicKey(multiCodecKey: String, base58btc: String, callback: Callback) {
val params = JSONObject().apply {
put("key", multiCodecKey)
put("outBase", base58btc)
}
StatusBackendClient.executeStatusGoRequestWithCallback(
"MultiformatDeserializePublicKeyV2",
params.toString(),
{ Statusgo.multiformatDeserializePublicKeyV2(params.toString()) },
callback
)
}
@ReactMethod
@Throws(Exception::class)
fun deserializeAndCompressKey(desktopKey: String, callback: Callback) {
StatusBackendClient.executeStatusGoRequestWithCallback(
"DeserializeAndCompressKey",
desktopKey,
{ Statusgo.deserializeAndCompressKey(desktopKey) },
callback
)
}
@ReactMethod
@Throws(Exception::class)
fun hashTypedData(data: String, callback: Callback) {
utils.executeRunnableStatusGoMethod({ Statusgo.hashTypedData(data) }, callback)
}
@ReactMethod
@Throws(Exception::class)
fun hashTypedDataV4(data: String, callback: Callback) {
utils.executeRunnableStatusGoMethod({ Statusgo.hashTypedDataV4(data) }, callback)
}
@ReactMethod
@Throws(Exception::class)
fun signMessage(rpcParams: String, callback: Callback) {
StatusBackendClient.executeStatusGoRequestWithCallback(
"SignMessage",
rpcParams,
{ Statusgo.signMessage(rpcParams) },
callback
)
}
@ReactMethod
@Throws(Exception::class)
fun signTypedData(data: String, account: String, password: String, callback: Callback) {
utils.executeRunnableStatusGoMethod({ Statusgo.signTypedData(data, account, password) }, callback)
}
@ReactMethod
@Throws(Exception::class)
fun signTypedDataV4(data: String, account: String, password: String, callback: Callback) {
utils.executeRunnableStatusGoMethod({ Statusgo.signTypedDataV4(data, account, password) }, callback)
}
@ReactMethod
@Throws(Exception::class)
fun signGroupMembership(content: String, callback: Callback) {
utils.executeRunnableStatusGoMethod({ Statusgo.signGroupMembership(content) }, callback)
}
}
@@ -35,6 +35,11 @@ class LogManager(private val reactContext: ReactApplicationContext) : ReactConte
return File(pubDirectory, gethLogFileName)
}
private fun getPreLoginLogFile(): File {
val pubDirectory = utils.getPublicStorageDirectory()
return File(pubDirectory, preLoginLogFileName)
}
fun prepareLogsFile(context: Context): File? {
val logFile = getGethLogFile()
@@ -156,6 +161,7 @@ class LogManager(private val reactContext: ReactApplicationContext) : ReactConte
val statusLogFile = File(logsTempDir, statusLogFileName)
val gethLogFile = getGethLogFile()
val requestLogFile = getRequestLogFile()
val preLoginLogFile = getPreLoginLogFile()
try {
if (zipFile.exists() || zipFile.createNewFile()) {
@@ -175,6 +181,9 @@ class LogManager(private val reactContext: ReactApplicationContext) : ReactConte
if (requestLogFile.exists()) {
filesToZip.add(requestLogFile)
}
if (preLoginLogFile.exists()) {
filesToZip.add(preLoginLogFile)
}
val zipped = zip(filesToZip.toTypedArray(), zipFile, errorList)
if (zipped && zipFile.exists()) {
zipFile.setReadable(true, false)
@@ -205,6 +214,7 @@ class LogManager(private val reactContext: ReactApplicationContext) : ReactConte
private const val gethLogFileName = "geth.log"
private const val statusLogFileName = "Status.log"
private const val requestsLogFileName = "api.log"
private const val preLoginLogFileName = "pre_login.log"
private const val logsZipFileName = "Status-debug-logs.zip"
}
}
@@ -108,6 +108,15 @@ class StatusModule(private val reactContext: ReactApplicationContext, private va
)
}
@ReactMethod
fun intendedPanic(message: String) {
StatusBackendClient.executeStatusGoRequest(
endpoint = "IntendedPanic",
requestBody = message,
statusgoFunction = { Statusgo.intendedPanic(message) },
)
}
@ReactMethod
fun addCentralizedMetric(request: String, callback: Callback) {
StatusBackendClient.executeStatusGoRequestWithCallback(
@@ -1,124 +0,0 @@
package im.status.ethereum.module;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import android.app.Activity;
import android.util.Log;
import android.os.Build;
import android.webkit.WebView;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.WebStorage;
import android.view.WindowManager;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.UIBlock;
import com.facebook.react.uimanager.NativeViewHierarchyManager;
import android.view.inputmethod.InputMethodManager;
import android.view.View;
import android.widget.EditText;
import android.content.Context;
public class UIHelper extends ReactContextBaseJavaModule {
private static final String TAG = "UIHelper";
private ReactApplicationContext reactContext;
public UIHelper(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
}
@Override
public String getName() {
return "UIHelper";
}
@ReactMethod
public void setSoftInputMode(final int mode) {
Log.d(TAG, "setSoftInputMode");
final Activity activity = getCurrentActivity();
if (activity == null) {
return;
}
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
activity.getWindow().setSoftInputMode(mode);
}
});
}
@SuppressWarnings("deprecation")
@ReactMethod
public void clearCookies() {
Log.d(TAG, "clearCookies");
final Activity activity = getCurrentActivity();
if (activity == null) {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
CookieManager.getInstance().removeAllCookies(null);
CookieManager.getInstance().flush();
} else {
CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(activity);
cookieSyncManager.startSync();
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
cookieManager.removeSessionCookie();
cookieSyncManager.stopSync();
cookieSyncManager.sync();
}
}
@ReactMethod
public void toggleWebviewDebug(final boolean val) {
Log.d(TAG, "toggleWebviewDebug");
final Activity activity = getCurrentActivity();
if (activity == null) {
return;
}
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
WebView.setWebContentsDebuggingEnabled(val);
}
});
}
@ReactMethod
public void clearStorageAPIs() {
Log.d(TAG, "clearStorageAPIs");
final Activity activity = getCurrentActivity();
if (activity == null) {
return;
}
WebStorage storage = WebStorage.getInstance();
if (storage != null) {
storage.deleteAllData();
}
}
@ReactMethod
public void resetKeyboardInputCursor(final int reactTagToReset, final int selection) {
UIManagerModule uiManager = getReactApplicationContext().getNativeModule(UIManagerModule.class);
uiManager.addUIBlock(new UIBlock() {
@Override
public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
InputMethodManager imm = (InputMethodManager) getReactApplicationContext().getBaseContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
View viewToReset = nativeViewHierarchyManager.resolveView(reactTagToReset);
imm.restartInput(viewToReset);
try {
EditText textView = (EditText) viewToReset;
textView.setSelection(selection);
} catch (Exception e) {}
}
}
});
}
}
@@ -0,0 +1,97 @@
package im.status.ethereum.module
import android.app.Activity
import android.content.Context
import android.os.Build
import android.util.Log
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.webkit.CookieManager
import android.webkit.CookieSyncManager
import android.webkit.WebStorage
import android.webkit.WebView
import android.widget.EditText
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.uimanager.NativeViewHierarchyManager
import com.facebook.react.uimanager.UIBlock
import com.facebook.react.uimanager.UIManagerModule
class UIHelper(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
companion object {
private const val TAG = "UIHelper"
}
override fun getName(): String = "UIHelper"
@ReactMethod
fun setSoftInputMode(mode: Int) {
Log.d(TAG, "setSoftInputMode")
getCurrentActivity()?.run {
runOnUiThread {
window.setSoftInputMode(mode)
}
}
}
@Suppress("DEPRECATION")
@ReactMethod
fun clearCookies() {
Log.d(TAG, "clearCookies")
getCurrentActivity()?.let { activity ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
CookieManager.getInstance().apply {
removeAllCookies(null)
flush()
}
} else {
CookieSyncManager.createInstance(activity).also { cookieSyncManager ->
cookieSyncManager.startSync()
CookieManager.getInstance().apply {
removeAllCookie()
removeSessionCookie()
}
cookieSyncManager.stopSync()
cookieSyncManager.sync()
}
}
}
}
@ReactMethod
fun toggleWebviewDebug(enabled: Boolean) {
Log.d(TAG, "toggleWebviewDebug")
getCurrentActivity()?.run {
runOnUiThread {
WebView.setWebContentsDebuggingEnabled(enabled)
}
}
}
@ReactMethod
fun clearStorageAPIs() {
Log.d(TAG, "clearStorageAPIs")
getCurrentActivity()?.let {
WebStorage.getInstance()?.deleteAllData()
}
}
@ReactMethod
fun resetKeyboardInputCursor(reactTagToReset: Int, selection: Int) {
reactApplicationContext.getNativeModule(UIManagerModule::class.java)?.addUIBlock(object : UIBlock {
override fun execute(nativeViewHierarchyManager: NativeViewHierarchyManager) {
(reactApplicationContext.baseContext.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager)?.let { imm ->
val viewToReset = nativeViewHierarchyManager.resolveView(reactTagToReset)
imm.restartInput(viewToReset)
try {
(viewToReset as? EditText)?.setSelection(selection)
} catch (e: Exception) {
// Ignore exceptions during selection setting
}
}
}
})
}
}
@@ -1,109 +0,0 @@
package im.status.ethereum.pushnotifications;
import android.content.Context;
import android.content.Intent;
import android.app.Service;
import android.content.pm.ServiceInfo;
import android.os.IBinder;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.provider.Settings;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import android.os.Build;
import im.status.ethereum.module.R;
public class ForegroundService extends Service {
private static final String CHANNEL_ID = "status-service";
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent i, int flags, int startId) {
// NOTE: recent versions of Android require the service to display
// a sticky notification to inform the user that the service is running
Context context = getApplicationContext();
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
Intent intent = null;
String notificationContentText = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager notificationManager =
context.getSystemService(NotificationManager.class);
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
context.getResources().getString(R.string.status_service),
NotificationManager.IMPORTANCE_HIGH);
channel.setShowBadge(false);
notificationManager.createNotificationChannel(channel);
// Create intent that takes the user to the notification channel settings so they can hide it
intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
intent.putExtra(Settings.EXTRA_CHANNEL_ID, CHANNEL_ID);
notificationContentText = context.getResources().getString(R.string.tap_to_hide_notification);
} else {
// For older versions of android intent takes the user to the Status app
Class intentClass;
String packageName = context.getPackageName();
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
String className = launchIntent.getComponent().getClassName();
try {
intentClass = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return 0;
}
intent = new Intent(context, intentClass);
intent.addCategory(Intent.CATEGORY_BROWSABLE);
intent.setAction(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
notificationContentText = context.getResources().getString(R.string.keep_status_running);
}
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
Intent stopIntent = new Intent(PushNotificationHelper.ACTION_TAP_STOP);
PendingIntent stopPendingIntent = PendingIntent.getBroadcast(context, 0, stopIntent,
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
Notification notification = new NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_stat_notify_status)
.setContentTitle(context.getResources().getString(R.string.background_service_opened))
.setContentText(notificationContentText)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setContentIntent(pendingIntent)
.setNumber(0)
.addAction(R.drawable.ic_stat_notify_status,
context.getResources().getString(R.string.stop),
stopPendingIntent)
.build();
// the id of the foreground notification MUST NOT be 0
if (Build.VERSION.SDK_INT >= 33) {
startForeground(1, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE);
} else {
startForeground(1, notification);
}
return START_STICKY;
}
}
@@ -0,0 +1,122 @@
package im.status.ethereum.pushnotifications
import android.content.Context
import android.content.Intent
import android.app.Service
import android.content.pm.ServiceInfo
import android.os.IBinder
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.provider.Settings
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import android.os.Build
import im.status.ethereum.module.R
class ForegroundService : Service() {
companion object {
private const val CHANNEL_ID = "status-service"
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(i: Intent?, flags: Int, startId: Int): Int {
// NOTE: recent versions of Android require the service to display
// a sticky notification to inform the user that the service is running
val context = applicationContext
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
// Create notification channel for Android O and above
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = context.getSystemService(NotificationManager::class.java)
val channel = NotificationChannel(
CHANNEL_ID,
context.resources.getString(R.string.status_service),
NotificationManager.IMPORTANCE_HIGH
).apply {
setShowBadge(false)
}
notificationManager.createNotificationChannel(channel)
}
// Initialize intent and notification text based on Android version
val notificationIntent = createNotificationIntent(context)
val notificationText = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.resources.getString(R.string.tap_to_hide_notification)
} else {
context.resources.getString(R.string.keep_status_running)
}
val pendingIntent = PendingIntent.getActivity(
this,
0,
notificationIntent,
PendingIntent.FLAG_IMMUTABLE
)
val stopIntent = Intent(PushNotificationHelper.ACTION_TAP_STOP)
val stopPendingIntent = PendingIntent.getBroadcast(
context,
0,
stopIntent,
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_stat_notify_status)
.setContentTitle(context.resources.getString(R.string.background_service_opened))
.setContentText(notificationText)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setContentIntent(pendingIntent)
.setNumber(0)
.addAction(
R.drawable.ic_stat_notify_status,
context.resources.getString(R.string.stop),
stopPendingIntent
)
.build()
// the id of the foreground notification MUST NOT be 0
if (Build.VERSION.SDK_INT >= 33) {
startForeground(1, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
} else {
startForeground(1, notification)
}
return START_STICKY
}
private fun createNotificationIntent(context: Context): Intent {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create intent that takes the user to the notification channel settings
Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
putExtra(Settings.EXTRA_CHANNEL_ID, CHANNEL_ID)
}
} else {
// For older versions of android, intent takes the user to the Status app
val packageName = context.packageName
val launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)
val className = launchIntent?.component?.className
try {
val intentClass = Class.forName(className!!)
Intent(context, intentClass).apply {
addCategory(Intent.CATEGORY_BROWSABLE)
action = Intent.ACTION_VIEW
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
}
} catch (e: ClassNotFoundException) {
e.printStackTrace()
// Return an empty intent as fallback
Intent()
}
}
}
}
@@ -1,146 +0,0 @@
package im.status.ethereum.pushnotifications;
import android.app.Activity;
import android.app.Application;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationManagerCompat;
import java.security.SecureRandom;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import android.util.Log;
import im.status.ethereum.pushnotifications.PushNotificationJsDelivery;
public class PushNotification extends ReactContextBaseJavaModule implements ActivityEventListener {
public static final String LOG_TAG = "PushNotification";
private final SecureRandom mRandomNumberGenerator = new SecureRandom();
private PushNotificationHelper pushNotificationHelper;
private PushNotificationJsDelivery delivery;
private ReactApplicationContext reactContext;
private boolean started;
public PushNotification(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
reactContext.addActivityEventListener(this);
Application applicationContext = (Application) reactContext.getApplicationContext();
IntentFilter intentFilter = new IntentFilter();
pushNotificationHelper = new PushNotificationHelper(applicationContext, intentFilter);
delivery = new PushNotificationJsDelivery(reactContext);
}
@Override
public String getName() {
return "PushNotification";
}
// removed @Override temporarily just to get it working on different versions of RN
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
onActivityResult(requestCode, resultCode, data);
}
// removed @Override temporarily just to get it working on different versions of RN
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Ignored, required to implement ActivityEventListener for RN 0.33
}
private Bundle getBundleFromIntent(Intent intent) {
Bundle bundle = null;
if (intent.hasExtra("notification")) {
bundle = intent.getBundleExtra("notification");
} else if (intent.hasExtra("google.message_id")) {
bundle = new Bundle();
bundle.putBundle("data", intent.getExtras());
}
if(null != bundle && !bundle.getBoolean("foreground", false) && !bundle.containsKey("userInteraction")) {
bundle.putBoolean("userInteraction", true);
}
return bundle;
}
@Override
public void onNewIntent(Intent intent) {
Bundle bundle = this.getBundleFromIntent(intent);
if (bundle != null) {
delivery.notifyNotification(bundle);
}
}
@ReactMethod
/**
* Creates a channel if it does not already exist. Returns whether the channel was created.
*/
public void createChannel(ReadableMap channelInfo, Callback callback) {
boolean created = pushNotificationHelper.createChannel(channelInfo);
if(callback != null) {
callback.invoke(created);
}
}
@ReactMethod
public void presentLocalNotification(ReadableMap details) {
if (!this.started) {
return;
}
Bundle bundle = Arguments.toBundle(details);
// If notification ID is not provided by the user, generate one at random
if (bundle.getString("id") == null) {
bundle.putString("id", String.valueOf(mRandomNumberGenerator.nextInt()));
}
pushNotificationHelper.sendToNotificationCentre(bundle);
}
@ReactMethod
public void clearMessageNotifications(String conversationId) {
if (this.started) {
pushNotificationHelper.clearMessageNotifications(conversationId);
}
}
@ReactMethod
public void clearAllMessageNotifications() {
pushNotificationHelper.clearAllMessageNotifications();
}
@ReactMethod
public void enableNotifications() {
this.started = true;
this.pushNotificationHelper.start();
}
@ReactMethod
public void disableNotifications() {
if (this.started) {
this.started = false;
this.pushNotificationHelper.stop();
}
}
}
@@ -0,0 +1,118 @@
package im.status.ethereum.pushnotifications
import android.app.Activity
import android.app.Application
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import android.util.Log
import androidx.annotation.NonNull
import androidx.core.app.NotificationManagerCompat
import com.facebook.react.bridge.*
import im.status.ethereum.pushnotifications.PushNotificationJsDelivery
import java.security.SecureRandom
class PushNotification(reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext), ActivityEventListener {
companion object {
const val LOG_TAG = "PushNotification"
}
private val mRandomNumberGenerator = SecureRandom()
private val pushNotificationHelper: PushNotificationHelper
private val delivery: PushNotificationJsDelivery
private val reactContext: ReactApplicationContext = reactContext
private var started: Boolean = false
init {
reactContext.addActivityEventListener(this)
val applicationContext = reactContext.applicationContext as Application
val intentFilter = IntentFilter()
pushNotificationHelper = PushNotificationHelper(applicationContext, intentFilter)
delivery = PushNotificationJsDelivery(reactContext)
}
override fun getName(): String = "PushNotification"
// Primary implementation of ActivityEventListener interface
override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
// Call the helper method with the correct parameter order
handleActivityResult(requestCode, resultCode, data)
}
// Private helper method for handling activity results
private fun handleActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
// Ignored, required to implement ActivityEventListener for RN 0.33
}
private fun getBundleFromIntent(intent: Intent): Bundle? {
return when {
intent.hasExtra("notification") -> intent.getBundleExtra("notification")
intent.hasExtra("google.message_id") -> Bundle().apply {
putBundle("data", intent.extras)
}
else -> null
}?.apply {
if (!getBoolean("foreground", false) && !containsKey("userInteraction")) {
putBoolean("userInteraction", true)
}
}
}
override fun onNewIntent(intent: Intent) {
getBundleFromIntent(intent)?.let { bundle ->
delivery.notifyNotification(bundle)
}
}
@ReactMethod
fun createChannel(channelInfo: ReadableMap, callback: Callback?) {
val created = pushNotificationHelper.createChannel(channelInfo)
callback?.invoke(created)
}
@ReactMethod
fun presentLocalNotification(details: ReadableMap) {
if (!started) return
Arguments.toBundle(details)?.apply {
// If notification ID is not provided by the user, generate one at random
if (getString("id") == null) {
putString("id", mRandomNumberGenerator.nextInt().toString())
}
pushNotificationHelper.sendToNotificationCentre(this)
}
}
@ReactMethod
fun clearMessageNotifications(conversationId: String) {
if (started) {
pushNotificationHelper.clearMessageNotifications(conversationId)
}
}
@ReactMethod
fun clearAllMessageNotifications() {
pushNotificationHelper.clearAllMessageNotifications()
}
@ReactMethod
fun enableNotifications() {
started = true
pushNotificationHelper.start()
}
@ReactMethod
fun disableNotifications() {
if (started) {
started = false
pushNotificationHelper.stop()
}
}
}
@@ -1,93 +0,0 @@
package im.status.ethereum.pushnotifications;
import android.os.Build;
import android.app.Application;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
import im.status.ethereum.pushnotifications.PushNotificationJsDelivery;
import static im.status.ethereum.pushnotifications.PushNotification.LOG_TAG;
public class PushNotificationActions extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
String intentActionPrefix = context.getPackageName() + ".ACTION_";
Log.i(LOG_TAG, "PushNotificationBootEventReceiver loading scheduled notifications");
if (null == intent.getAction() || !intent.getAction().startsWith(intentActionPrefix)) {
return;
}
final Bundle bundle = intent.getBundleExtra("notification");
// Dismiss the notification popup.
NotificationManager manager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
int notificationID = Integer.parseInt(bundle.getString("id"));
boolean autoCancel = bundle.getBoolean("autoCancel", true);
if(autoCancel) {
if (bundle.containsKey("tag")) {
String tag = bundle.getString("tag");
manager.cancel(tag, notificationID);
} else {
manager.cancel(notificationID);
}
}
boolean invokeApp = bundle.getBoolean("invokeApp", true);
// Notify the action.
if(invokeApp) {
IntentFilter intentFilter = new IntentFilter();
PushNotificationHelper helper = new PushNotificationHelper((Application) context.getApplicationContext(), intentFilter);
helper.invokeApp(bundle);
} else {
// We need to run this on the main thread, as the React code assumes that is true.
// Namely, DevServerHelper constructs a Handler() without a Looper, which triggers:
// "Can't create handler inside thread that has not called Looper.prepare()"
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
public void run() {
// Construct and load our normal React JS code bundle
final ReactInstanceManager mReactInstanceManager = ((ReactApplication) context.getApplicationContext()).getReactNativeHost().getReactInstanceManager();
ReactContext context = mReactInstanceManager.getCurrentReactContext();
PushNotificationJsDelivery delivery = new PushNotificationJsDelivery(context);
// If it's constructed, send a notification
if (context != null) {
delivery.notifyNotificationAction(bundle);
} else {
// Otherwise wait for construction, then send the notification
mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
public void onReactContextInitialized(ReactContext context) {
PushNotificationJsDelivery delivery = new PushNotificationJsDelivery(context);
delivery.notifyNotificationAction(bundle);
mReactInstanceManager.removeReactInstanceEventListener(this);
}
});
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
// Construct it in the background
mReactInstanceManager.createReactContextInBackground();
}
}
}
});
}
}
}
@@ -0,0 +1,100 @@
package im.status.ethereum.pushnotifications
import android.os.Build
import android.app.Application
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import com.facebook.react.ReactApplication
import com.facebook.react.ReactInstanceManager
import com.facebook.react.bridge.ReactContext
import im.status.ethereum.pushnotifications.PushNotificationJsDelivery
import im.status.ethereum.pushnotifications.PushNotification.Companion.LOG_TAG
/**
* BroadcastReceiver responsible for handling push notification actions.
* This class processes notification interactions and manages their lifecycle.
*/
class PushNotificationActions : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val intentActionPrefix = "${context.packageName}.ACTION_"
Log.i(LOG_TAG, "PushNotificationBootEventReceiver loading scheduled notifications")
if (intent.action == null || !intent.action!!.startsWith(intentActionPrefix)) {
return
}
val bundle = intent.getBundleExtra("notification") ?: return
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notificationID = bundle.getString("id")?.toInt() ?: return
// Dismiss the notification popup.
val autoCancel = bundle.getBoolean("autoCancel", true)
if (autoCancel) {
bundle.getString("tag")?.let { tag ->
manager.cancel(tag, notificationID)
} ?: run {
manager.cancel(notificationID)
}
}
// Notify the action.
val invokeApp = bundle.getBoolean("invokeApp", true)
if (invokeApp) {
val intentFilter = IntentFilter()
val helper = PushNotificationHelper(
context.applicationContext as Application,
intentFilter
)
helper.invokeApp(bundle)
} else {
// We need to run this on the main thread, as the React code assumes that is true.
// Namely, DevServerHelper constructs a Handler() without a Looper, which triggers:
// "Can't create handler inside thread that has not called Looper.prepare()"
Handler(Looper.getMainLooper()).post {
// Construct and load our normal React JS code bundle
val reactInstanceManager = (context.applicationContext as ReactApplication)
.reactNativeHost
.reactInstanceManager
val currentContext = reactInstanceManager.currentReactContext
when {
// If context exists, deliver notification immediately
currentContext != null -> {
PushNotificationJsDelivery(currentContext)
.notifyNotificationAction(bundle)
}
// Otherwise wait for construction, then send the notification
else -> {
val listener = object : ReactInstanceManager.ReactInstanceEventListener {
override fun onReactContextInitialized(reactContext: ReactContext) {
PushNotificationJsDelivery(reactContext)
.notifyNotificationAction(bundle)
reactInstanceManager.removeReactInstanceEventListener(this)
}
}
// Add the listener and create context if needed
reactInstanceManager.addReactInstanceEventListener(listener)
if (!reactInstanceManager.hasStartedCreatingInitialContext()) {
// Construct it in the background
reactInstanceManager.createReactContextInBackground()
}
}
}
}
}
}
}
@@ -1,890 +0,0 @@
// https://github.com/zo0r/react-native-push-notification/blob/bedc8f646aab67d594f291449fbfa24e07b64fe8/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationHelper.java Copy-Paste with removed firebase
package im.status.ethereum.pushnotifications;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.AlarmManager;
import android.app.Application;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.BroadcastReceiver;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.media.AudioAttributes;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.util.Base64;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import androidx.core.app.Person;
import androidx.core.graphics.drawable.IconCompat;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import org.json.JSONArray;
import org.json.JSONException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import im.status.ethereum.module.R;
import static im.status.ethereum.pushnotifications.PushNotification.LOG_TAG;
public class PushNotificationHelper {
private Context context;
private static final long DEFAULT_VIBRATION = 300L;
private static final String CHANNEL_ID = "status-im-notifications";
public static final String ACTION_DELETE_NOTIFICATION = "im.status.ethereum.module.DELETE_NOTIFICATION";
public static final String ACTION_TAP_STOP = "im.status.ethereum.module.TAP_STOP";
final int flag = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE : PendingIntent.FLAG_CANCEL_CURRENT;
private NotificationManager notificationManager;
private HashMap<String, Person> persons;
private HashMap<String, StatusMessageGroup> messageGroups;
private IntentFilter intentFilter;
public PushNotificationHelper(Application context, IntentFilter intentFilter) {
this.context = context;
this.intentFilter = intentFilter;
this.persons = new HashMap<String, Person>();
this.messageGroups = new HashMap<String, StatusMessageGroup>();
this.notificationManager = context.getSystemService(NotificationManager.class);
this.registerBroadcastReceiver();
}
public Intent getOpenAppIntent(String deepLink) {
Class intentClass;
String packageName = context.getPackageName();
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
String className = launchIntent.getComponent().getClassName();
try {
intentClass = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
Intent intent = new Intent(context, intentClass);
intent.addCategory(Intent.CATEGORY_BROWSABLE);
intent.setAction(Intent.ACTION_VIEW);
//NOTE: you might wonder, why the heck did he decide to set these flags in particular. Well,
//the answer is a simple as it can get in the Android native development world. I noticed
//that my initial setup was opening the app but wasn't triggering any events on the js side, like
//the links do from the browser. So I compared both intents and noticed that the link from
//the browser produces an intent with the flag 0x14000000. I found out that it was the following
//flags in this link:
//https://stackoverflow.com/questions/52390129/android-intent-setflags-issue
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
if (deepLink != null) {
intent.setData(Uri.parse(deepLink));
}
return intent;
}
//NOTE: we use a dynamically created BroadcastReceiver here so that we can capture
//intents from notifications and act on them. For instance when tapping/dismissing
//a chat notification we want to clear the chat so that next messages don't show
//the messages that we have seen again
private final BroadcastReceiver notificationActionReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() == ACTION_DELETE_NOTIFICATION) {
String groupId = intent.getExtras().getString("im.status.ethereum.groupId");
if (groupId != null) {
cleanGroup(groupId);
}
}
if (intent.getAction() == ACTION_TAP_STOP) {
stop();
System.exit(0);
}
Log.e(LOG_TAG, "intent received: " + intent.getAction());
}
};
public void registerBroadcastReceiver() {
this.intentFilter.addAction(ACTION_DELETE_NOTIFICATION);
this.intentFilter.addAction(ACTION_TAP_STOP);
context.registerReceiver(notificationActionReceiver, this.intentFilter, Context.RECEIVER_EXPORTED);
Log.e(LOG_TAG, "Broadcast Receiver registered");
}
private NotificationManager notificationManager() {
return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
public void invokeApp(Bundle bundle) {
String packageName = context.getPackageName();
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
String className = launchIntent.getComponent().getClassName();
try {
Class<?> activityClass = Class.forName(className);
Intent activityIntent = new Intent(context, activityClass);
if(bundle != null) {
activityIntent.putExtra("notification", bundle);
}
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(activityIntent);
} catch(Exception e) {
Log.e(LOG_TAG, "Class not found", e);
return;
}
}
public void sendToNotificationCentre(final Bundle bundle) {
PushNotificationPicturesAggregator aggregator = new PushNotificationPicturesAggregator(new PushNotificationPicturesAggregator.Callback() {
public void call(Bitmap largeIconImage, Bitmap bigPictureImage) {
sendToNotificationCentreWithPicture(bundle, largeIconImage, bigPictureImage);
}
});
aggregator.setLargeIconUrl(context, bundle.getString("largeIconUrl"));
aggregator.setBigPictureUrl(context, bundle.getString("bigPictureUrl"));
}
public void handleConversation(final Bundle bundle) {
if (bundle.getBoolean("deleted")){
this.removeStatusMessage(bundle);
} else {
this.addStatusMessage(bundle);
}
}
public void clearMessageNotifications(String conversationId) {
notificationManager.cancel(conversationId.hashCode());
cleanGroup(conversationId);
}
public void clearAllMessageNotifications() {
notificationManager.cancelAll();
}
public void sendToNotificationCentreWithPicture(final Bundle bundle, Bitmap largeIconBitmap, Bitmap bigPictureBitmap) {
try {
Class intentClass = getMainActivityClass();
if (intentClass == null) {
Log.e(LOG_TAG, "No activity class found for the notification");
return;
}
if (bundle.getBoolean("isConversation")) {
this.handleConversation(bundle);
return;
}
if (bundle.getString("message") == null) {
// this happens when a 'data' notification is received - we do not synthesize a local notification in this case
Log.d(LOG_TAG, "Ignore this message if you sent data-only notification. Cannot send to notification centre because there is no 'message' field in: " + bundle);
return;
}
String notificationIdString = bundle.getString("id");
if (notificationIdString == null) {
Log.e(LOG_TAG, "No notification ID specified for the notification");
return;
}
Resources res = context.getResources();
String packageName = context.getPackageName();
String title = bundle.getString("title");
if (title == null) {
ApplicationInfo appInfo = context.getApplicationInfo();
title = context.getPackageManager().getApplicationLabel(appInfo).toString();
}
int priority = NotificationCompat.PRIORITY_HIGH;
final String priorityString = bundle.getString("priority");
if (priorityString != null) {
switch (priorityString.toLowerCase()) {
case "max":
priority = NotificationCompat.PRIORITY_MAX;
break;
case "high":
priority = NotificationCompat.PRIORITY_HIGH;
break;
case "low":
priority = NotificationCompat.PRIORITY_LOW;
break;
case "min":
priority = NotificationCompat.PRIORITY_MIN;
break;
case "default":
priority = NotificationCompat.PRIORITY_DEFAULT;
break;
default:
priority = NotificationCompat.PRIORITY_HIGH;
}
}
int visibility = NotificationCompat.VISIBILITY_PRIVATE;
final String visibilityString = bundle.getString("visibility");
if (visibilityString != null) {
switch (visibilityString.toLowerCase()) {
case "private":
visibility = NotificationCompat.VISIBILITY_PRIVATE;
break;
case "public":
visibility = NotificationCompat.VISIBILITY_PUBLIC;
break;
case "secret":
visibility = NotificationCompat.VISIBILITY_SECRET;
break;
default:
visibility = NotificationCompat.VISIBILITY_PRIVATE;
}
}
String channel_id = bundle.getString("channelId");
if(channel_id == null) {
channel_id = this.getNotificationDefaultChannelId();
}
NotificationCompat.Builder notification = new NotificationCompat.Builder(context, channel_id)
.setContentTitle(title)
.setTicker(bundle.getString("ticker"))
.setVisibility(visibility)
.setPriority(priority)
.setAutoCancel(bundle.getBoolean("autoCancel", true))
.setOnlyAlertOnce(bundle.getBoolean("onlyAlertOnce", false));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // API 24 and higher
// Restore showing timestamp on Android 7+
// Source: https://developer.android.com/reference/android/app/Notification.Builder.html#setShowWhen(boolean)
boolean showWhen = bundle.getBoolean("showWhen", true);
notification.setShowWhen(showWhen);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // API 26 and higher
// Changing Default mode of notification
notification.setDefaults(Notification.DEFAULT_LIGHTS);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { // API 20 and higher
String group = bundle.getString("group");
if (group != null) {
notification.setGroup(group);
}
if (bundle.containsKey("groupSummary") || bundle.getBoolean("groupSummary")) {
notification.setGroupSummary(bundle.getBoolean("groupSummary"));
}
}
String numberString = bundle.getString("number");
if (numberString != null) {
notification.setNumber(Integer.parseInt(numberString));
}
// Small icon
int smallIconResId = 0;
String smallIcon = bundle.getString("smallIcon");
if (smallIcon != null && !smallIcon.isEmpty()) {
smallIconResId = res.getIdentifier(smallIcon, "mipmap", packageName);
} else if(smallIcon == null) {
smallIconResId = res.getIdentifier("ic_stat_notify_status", "drawable", packageName);
}
if (smallIconResId == 0) {
smallIconResId = res.getIdentifier("ic_launcher", "mipmap", packageName);
if (smallIconResId == 0) {
smallIconResId = android.R.drawable.ic_dialog_info;
}
}
notification.setSmallIcon(smallIconResId);
// Large icon
if(largeIconBitmap == null) {
int largeIconResId = 0;
String largeIcon = bundle.getString("largeIcon");
if (largeIcon != null && !largeIcon.isEmpty()) {
largeIconResId = res.getIdentifier(largeIcon, "mipmap", packageName);
} else if(largeIcon == null) {
largeIconResId = res.getIdentifier("ic_launcher", "mipmap", packageName);
}
// Before Lolipop there was no large icon for notifications.
if (largeIconResId != 0 && (largeIcon != null || Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)) {
largeIconBitmap = BitmapFactory.decodeResource(res, largeIconResId);
}
}
Bundle author = bundle.getBundle("notificationAuthor");
if (largeIconBitmap == null && author != null) {
String base64Image = author.getString("icon").split(",")[1];
byte[] decodedString = Base64.decode(base64Image, Base64.DEFAULT);
Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
notification.setLargeIcon(getCircleBitmap(decodedByte));
} else if (largeIconBitmap != null){
notification.setLargeIcon(largeIconBitmap);
}
String message = bundle.getString("message");
notification.setContentText(message);
String subText = bundle.getString("subText");
if (subText != null) {
notification.setSubText(subText);
}
String bigText = bundle.getString("bigText");
if (bigText == null) {
bigText = message;
}
NotificationCompat.Style style;
if(bigPictureBitmap != null) {
style = new NotificationCompat.BigPictureStyle()
.bigPicture(bigPictureBitmap)
.setBigContentTitle(title)
.setSummaryText(message);
} else {
style = new NotificationCompat.BigTextStyle().bigText(bigText);
}
notification.setStyle(style);
Intent intent = new Intent(context, intentClass);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
bundle.putBoolean("foreground", this.isApplicationInForeground());
bundle.putBoolean("userInteraction", true);
intent.putExtra("notification", bundle);
Uri soundUri = null;
if (!bundle.containsKey("playSound") || bundle.getBoolean("playSound")) {
String soundName = bundle.getString("soundName");
if (soundName == null) {
soundName = "default";
}
soundUri = getSoundUri(soundName);
notification.setSound(soundUri);
}
if (soundUri == null || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notification.setSound(null);
}
if (bundle.containsKey("ongoing") || bundle.getBoolean("ongoing")) {
notification.setOngoing(bundle.getBoolean("ongoing"));
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
notification.setCategory(NotificationCompat.CATEGORY_CALL);
String color = bundle.getString("color");
int defaultColor = -1;
if (color != null) {
notification.setColor(Color.parseColor(color));
} else if (defaultColor != -1) {
notification.setColor(defaultColor);
}
}
int notificationID = notificationIdString.hashCode();
notification.setContentIntent(createOnTapIntent(context, notificationID, bundle.getString("deepLink")))
.setDeleteIntent(createOnDismissedIntent(context, notificationID, bundle.getString("deepLink")));
NotificationManager notificationManager = notificationManager();
long[] vibratePattern = new long[]{0};
if (!bundle.containsKey("vibrate") || bundle.getBoolean("vibrate")) {
long vibration = bundle.containsKey("vibration") ? (long) bundle.getDouble("vibration") : DEFAULT_VIBRATION;
if (vibration == 0)
vibration = DEFAULT_VIBRATION;
vibratePattern = new long[]{0, vibration};
notification.setVibrate(vibratePattern);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Define the shortcutId
String shortcutId = bundle.getString("shortcutId");
if (shortcutId != null) {
notification.setShortcutId(shortcutId);
}
Long timeoutAfter = (long) bundle.getDouble("timeoutAfter");
if (timeoutAfter != null && timeoutAfter >= 0) {
notification.setTimeoutAfter(timeoutAfter);
}
}
Long when = (long) bundle.getDouble("when");
if (when != null && when >= 0) {
notification.setWhen(when);
}
notification.setUsesChronometer(bundle.getBoolean("usesChronometer", false));
notification.setChannelId(channel_id);
JSONArray actionsArray = null;
try {
actionsArray = bundle.getString("actions") != null ? new JSONArray(bundle.getString("actions")) : null;
} catch (JSONException e) {
Log.e(LOG_TAG, "Exception while converting actions to JSON object.", e);
}
if (actionsArray != null) {
// No icon for now. The icon value of 0 shows no icon.
int icon = 0;
// Add button for each actions.
for (int i = 0; i < actionsArray.length(); i++) {
String action;
try {
action = actionsArray.getString(i);
} catch (JSONException e) {
Log.e(LOG_TAG, "Exception while getting action from actionsArray.", e);
continue;
}
Intent actionIntent = new Intent(context, PushNotificationActions.class);
actionIntent.setAction(packageName + ".ACTION_" + i);
actionIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
// Add "action" for later identifying which button gets pressed.
bundle.putString("action", action);
actionIntent.putExtra("notification", bundle);
actionIntent.setPackage(packageName);
PendingIntent pendingActionIntent = PendingIntent.getBroadcast(context, notificationID, actionIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
notification.addAction(new NotificationCompat.Action.Builder(icon, action, pendingActionIntent).build());
} else {
notification.addAction(icon, action, pendingActionIntent);
}
}
}
if (!(this.isApplicationInForeground() && bundle.getBoolean("ignoreInForeground"))) {
Notification info = notification.build();
info.defaults |= Notification.DEFAULT_LIGHTS;
if (bundle.containsKey("tag")) {
String tag = bundle.getString("tag");
notificationManager.notify(tag, notificationID, info);
} else {
notificationManager.notify(notificationID, info);
}
}
} catch (Exception e) {
Log.e(LOG_TAG, "failed to send push notification", e);
}
}
private boolean checkOrCreateChannel(NotificationManager manager, String channel_id, String channel_name, String channel_description, Uri soundUri, int importance, long[] vibratePattern, boolean showBadge) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
return false;
if (manager == null)
return false;
NotificationChannel channel = manager.getNotificationChannel(channel_id);
if (
channel == null && channel_name != null && channel_description != null ||
channel != null &&
(
channel_name != null && !channel.getName().equals(channel_name) ||
channel_description != null && !channel.getDescription().equals(channel_description)
)
) {
// If channel doesn't exist create a new one.
// If channel name or description is updated then update the existing channel.
channel = new NotificationChannel(channel_id, channel_name, importance);
channel.setDescription(channel_description);
channel.enableLights(true);
channel.enableVibration(vibratePattern != null);
channel.setVibrationPattern(vibratePattern);
channel.setShowBadge(showBadge);
if (soundUri != null) {
AudioAttributes audioAttributes = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.build();
channel.setSound(soundUri, audioAttributes);
} else {
channel.setSound(null, null);
}
manager.createNotificationChannel(channel);
return true;
}
return false;
}
public boolean createChannel(ReadableMap channelInfo) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
return false;
String channelId = channelInfo.getString("channelId");
String channelName = channelInfo.getString("channelName");
String channelDescription = channelInfo.hasKey("channelDescription") ? channelInfo.getString("channelDescription") : "";
String soundName = channelInfo.hasKey("soundName") ? channelInfo.getString("soundName") : "default";
int importance = channelInfo.hasKey("importance") ? channelInfo.getInt("importance") : 4;
boolean vibrate = channelInfo.hasKey("vibrate") && channelInfo.getBoolean("vibrate");
long[] vibratePattern = vibrate ? new long[] { DEFAULT_VIBRATION } : null;
boolean showBadge = channelInfo.hasKey("showBadge") && channelInfo.getBoolean("showBadge");
NotificationManager manager = notificationManager();
Uri soundUri = getSoundUri(soundName);
return checkOrCreateChannel(manager, channelId, channelName, channelDescription, soundUri, importance, vibratePattern, showBadge);
}
public String getNotificationDefaultChannelId() {
return this.CHANNEL_ID;
}
private Uri getSoundUri(String soundName) {
if (soundName == null || "default".equalsIgnoreCase(soundName)) {
return RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
} else {
// sound name can be full filename, or just the resource name.
// So the strings 'my_sound.mp3' AND 'my_sound' are accepted
// The reason is to make the iOS and android javascript interfaces compatible
int resId;
if (context.getResources().getIdentifier(soundName, "raw", context.getPackageName()) != 0) {
resId = context.getResources().getIdentifier(soundName, "raw", context.getPackageName());
} else {
soundName = soundName.substring(0, soundName.lastIndexOf('.'));
resId = context.getResources().getIdentifier(soundName, "raw", context.getPackageName());
}
return Uri.parse("android.resource://" + context.getPackageName() + "/" + resId);
}
}
public Class getMainActivityClass() {
String packageName = context.getPackageName();
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
String className = launchIntent.getComponent().getClassName();
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
public boolean isApplicationInForeground() {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> processInfos = activityManager.getRunningAppProcesses();
if (processInfos != null) {
for (RunningAppProcessInfo processInfo : processInfos) {
if (processInfo.processName.equals(context.getPackageName())
&& processInfo.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND
&& processInfo.pkgList.length > 0) {
return true;
}
}
}
return false;
}
private Bitmap getCircleBitmap(Bitmap bitmap) {
final Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final int color = Color.RED;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(rect);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawOval(rectF, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
bitmap.recycle();
return output;
}
private Person getPerson(Bundle bundle) {
String name = bundle.getString("name");
return new Person.Builder().setName(name).build();
}
private StatusMessage createMessage(Bundle data) {
Person author = getPerson(data.getBundle("notificationAuthor"));
long timeStampLongValue = (long) data.getDouble("timestamp");
return new StatusMessage(data.getString("id"), author, timeStampLongValue, data.getString("message"));
}
private PendingIntent createGroupOnDismissedIntent(Context context, int notificationId, String groupId, String deepLink) {
Intent intent = new Intent(ACTION_DELETE_NOTIFICATION);
intent.putExtra("im.status.ethereum.deepLink", deepLink);
intent.putExtra("im.status.ethereum.groupId", groupId);
return PendingIntent.getBroadcast(context.getApplicationContext(), notificationId, intent, flag);
}
private PendingIntent createGroupOnTapIntent(Context context, int notificationId, String groupId, String deepLink) {
Intent intent = getOpenAppIntent(deepLink);
return PendingIntent.getActivity(context.getApplicationContext(), notificationId, intent, flag);
}
private PendingIntent createOnTapIntent(Context context, int notificationId, String deepLink) {
Intent intent = getOpenAppIntent(deepLink);
return PendingIntent.getActivity(context.getApplicationContext(), notificationId, intent, flag);
}
private PendingIntent createOnDismissedIntent(Context context, int notificationId, String deepLink) {
Intent intent = new Intent(ACTION_DELETE_NOTIFICATION);
intent.putExtra("im.status.ethereum.deepLink", deepLink);
return PendingIntent.getBroadcast(context.getApplicationContext(), notificationId, intent, flag);
}
public void removeStatusMessage(Bundle bundle) {
String conversationId = bundle.getString("conversationId");
StatusMessageGroup group = this.messageGroups.get(conversationId);
NotificationManager notificationManager = notificationManager();
if (group == null) {
group = new StatusMessageGroup(conversationId);
}
this.messageGroups.put(conversationId, group);
String id = bundle.getString("id");
group.removeMessage(id);
this.showMessages(bundle);
}
public StatusMessageGroup getMessageGroup(String conversationId) {
return this.messageGroups.get(conversationId);
}
public void addStatusMessage(Bundle bundle) {
String conversationId = bundle.getString("conversationId");
StatusMessageGroup group = this.messageGroups.get(conversationId);
NotificationManager notificationManager = notificationManager();
if (group == null) {
group = new StatusMessageGroup(conversationId);
}
this.messageGroups.put(conversationId, group);
group.addMessage(createMessage(bundle));
this.showMessages(bundle);
}
public void showMessages(Bundle bundle) {
String conversationId = bundle.getString("conversationId");
StatusMessageGroup group = this.messageGroups.get(conversationId);
NotificationManager notificationManager = notificationManager();
NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle("Me");
ArrayList<StatusMessage> messages = group.getMessages();
if (messages.size() == 0) {
notificationManager.cancel(conversationId.hashCode());
return;
}
for (int i = 0; i < messages.size(); i++) {
StatusMessage message = messages.get(i);
messagingStyle.addMessage(message.getText(),
message.getTimestamp(),
message.getAuthor());
}
if (bundle.getString("title") != null) {
messagingStyle.setConversationTitle(bundle.getString("title"));
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_stat_notify_status)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setStyle(messagingStyle)
.setGroup(conversationId)
.setOnlyAlertOnce(true)
.setGroupSummary(true)
.setContentIntent(createGroupOnTapIntent(context, conversationId.hashCode(), conversationId, bundle.getString("deepLink")))
.setDeleteIntent(createGroupOnDismissedIntent(context, conversationId.hashCode(), conversationId, bundle.getString("deepLink")))
.setNumber(messages.size() + 1)
.setAutoCancel(true);
if (Build.VERSION.SDK_INT >= 21) {
builder.setVibrate(new long[0]);
}
notificationManager.notify(conversationId.hashCode(), builder.build());
}
class StatusMessageGroup {
private ArrayList<StatusMessage> messages;
private String id;
StatusMessageGroup(String id) {
this.id = id;
this.messages = new ArrayList<StatusMessage>();
}
public ArrayList<StatusMessage> getMessages() {
return messages;
}
public void addMessage(StatusMessage message) {
this.messages.add(message);
}
public void removeMessage(String id) {
ArrayList<StatusMessage> newMessages = new ArrayList<StatusMessage>();
for(StatusMessage message: this.messages) {
if(!message.id.equals(id)) {
newMessages.add(message);
}
}
this.messages = newMessages;
}
public String getId() {
return this.id;
}
}
class StatusMessage {
public Person getAuthor() {
return author;
}
public long getTimestamp() {
return timestamp;
}
public String getText() {
return text;
}
private String id;
private Person author;
private long timestamp;
private String text;
StatusMessage(String id, Person author, long timestamp, String text) {
this.id = id;
this.author = author;
this.timestamp = timestamp;
this.text = text;
}
}
private void removeGroup(String groupId) {
this.messageGroups.remove(groupId);
}
private void cleanGroup(String groupId) {
removeGroup(groupId);
if (messageGroups.size() == 0) {
notificationManager.cancelAll();
}
}
public void start() {
Log.e(LOG_TAG, "Starting Foreground Service");
Intent serviceIntent = new Intent(context, ForegroundService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(serviceIntent);
} else {
context.startService(serviceIntent);
}
this.registerBroadcastReceiver();
}
public void stop() {
Log.e(LOG_TAG, "Stopping Foreground Service");
//NOTE: we cancel all the current notifications, because the intents can't be used anymore
//since the broadcast receiver will be killed as well and won't be able to handle any intent
notificationManager.cancelAll();
Intent serviceIntent = new Intent(context, ForegroundService.class);
context.stopService(serviceIntent);
context.unregisterReceiver(notificationActionReceiver);
}
}
@@ -0,0 +1,750 @@
// https://github.com/zo0r/react-native-push-notification/blob/bedc8f646aab67d594f291449fbfa24e07b64fe8/android/src/main/
// java/com/dieam/reactnativepushnotification/modules/RNPushNotificationHelper.java Copy-Paste with removed firebase
package im.status.ethereum.pushnotifications
import android.app.ActivityManager
import android.app.ActivityManager.RunningAppProcessInfo
import android.app.AlarmManager
import android.app.Application
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.BroadcastReceiver
import android.content.SharedPreferences
import android.content.pm.ApplicationInfo
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.Rect
import android.graphics.RectF
import android.media.AudioAttributes
import android.media.RingtoneManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.service.notification.StatusBarNotification
import android.util.Log
import android.util.Base64
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.app.Person
import androidx.core.graphics.drawable.IconCompat
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.bridge.WritableArray
import com.facebook.react.bridge.WritableMap
import org.json.JSONArray
import org.json.JSONException
import im.status.ethereum.module.R
import im.status.ethereum.pushnotifications.PushNotification.Companion.LOG_TAG
class PushNotificationHelper(private val context: Application, private val intentFilter: IntentFilter) {
companion object {
private const val DEFAULT_VIBRATION: Long = 300L
private const val CHANNEL_ID = "status-im-notifications"
const val ACTION_DELETE_NOTIFICATION = "im.status.ethereum.module.DELETE_NOTIFICATION"
const val ACTION_TAP_STOP = "im.status.ethereum.module.TAP_STOP"
}
private val flag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
} else {
PendingIntent.FLAG_CANCEL_CURRENT
}
private val notificationManager: NotificationManager by lazy {
context.getSystemService(NotificationManager::class.java)
}
private val persons: MutableMap<String, Person> = mutableMapOf()
private val messageGroups: MutableMap<String, StatusMessageGroup> = mutableMapOf()
init {
registerBroadcastReceiver()
}
private val notificationActionReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
ACTION_DELETE_NOTIFICATION -> {
intent.extras?.getString("im.status.ethereum.groupId")?.let { groupId ->
cleanGroup(groupId)
}
}
ACTION_TAP_STOP -> {
stop()
System.exit(0)
}
}
Log.e(LOG_TAG, "intent received: ${intent.action}")
}
}
private fun registerBroadcastReceiver() {
intentFilter.apply {
addAction(ACTION_DELETE_NOTIFICATION)
addAction(ACTION_TAP_STOP)
}
context.registerReceiver(notificationActionReceiver, intentFilter, Context.RECEIVER_EXPORTED)
Log.e(LOG_TAG, "Broadcast Receiver registered")
}
fun getOpenAppIntent(deepLink: String?): Intent? {
val packageName = context.packageName
val launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)
val className = launchIntent?.component?.className ?: return null
return try {
val intentClass = Class.forName(className)
Intent(context, intentClass).apply {
addCategory(Intent.CATEGORY_BROWSABLE)
action = Intent.ACTION_VIEW
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
deepLink?.let { data = Uri.parse(it) }
}
} catch (e: ClassNotFoundException) {
e.printStackTrace()
null
}
}
fun invokeApp(bundle: Bundle?) {
val packageName = context.packageName
val launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)
val className = launchIntent?.component?.className ?: return
try {
val activityClass = Class.forName(className)
Intent(context, activityClass).apply {
bundle?.let { putExtra("notification", it) }
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(this)
}
} catch (e: ClassNotFoundException) {
Log.e(LOG_TAG, "Class not found", e)
}
}
fun getMainActivityClass(): Class<*>? {
val packageName = context.packageName
val launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)
val className = launchIntent?.component?.className ?: return null
return try {
Class.forName(className)
} catch (e: ClassNotFoundException) {
e.printStackTrace()
null
}
}
fun isApplicationInForeground(): Boolean {
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val processInfos = activityManager.runningAppProcesses ?: return false
return processInfos.any { processInfo ->
processInfo.processName == context.packageName &&
processInfo.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
processInfo.pkgList.isNotEmpty()
}
}
private fun getCircleBitmap(bitmap: Bitmap): Bitmap {
val output = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(output)
val paint = Paint().apply {
isAntiAlias = true
color = Color.RED
}
val rect = Rect(0, 0, bitmap.width, bitmap.height)
val rectF = RectF(rect)
canvas.drawARGB(0, 0, 0, 0)
canvas.drawOval(rectF, paint)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
canvas.drawBitmap(bitmap, rect, rect, paint)
bitmap.recycle()
return output
}
// Represents a message in a Status conversation
data class StatusMessage(
val id: String,
val author: Person,
val timestamp: Long,
val text: String
)
// Represents a group of messages in a Status conversation
inner class StatusMessageGroup(val id: String) {
private val _messages = mutableListOf<StatusMessage>()
val messages: List<StatusMessage> get() = _messages.toList()
fun addMessage(message: StatusMessage) {
_messages.add(message)
}
fun removeMessage(id: String) {
_messages.removeAll { it.id == id }
}
}
private fun getPerson(bundle: Bundle): Person {
return Person.Builder()
.setName(bundle.getString("name"))
.build()
}
private fun createMessage(data: Bundle): StatusMessage {
val author = getPerson(data.getBundle("notificationAuthor") ?: Bundle())
val timeStampLongValue = data.getDouble("timestamp").toLong()
return StatusMessage(
id = data.getString("id") ?: "",
author = author,
timestamp = timeStampLongValue,
text = data.getString("message") ?: ""
)
}
private fun createGroupOnDismissedIntent(
context: Context,
notificationId: Int,
groupId: String,
deepLink: String?
): PendingIntent {
return Intent(ACTION_DELETE_NOTIFICATION).apply {
putExtra("im.status.ethereum.deepLink", deepLink)
putExtra("im.status.ethereum.groupId", groupId)
}.let { intent ->
PendingIntent.getBroadcast(
context.applicationContext,
notificationId,
intent,
flag
)
}
}
private fun createGroupOnTapIntent(
context: Context,
notificationId: Int,
groupId: String,
deepLink: String?
): PendingIntent {
return getOpenAppIntent(deepLink)?.let { intent ->
PendingIntent.getActivity(
context.applicationContext,
notificationId,
intent,
flag
)
} ?: throw IllegalStateException("Could not create open app intent")
}
private fun createOnTapIntent(
context: Context,
notificationId: Int,
deepLink: String?
): PendingIntent {
return getOpenAppIntent(deepLink)?.let { intent ->
PendingIntent.getActivity(
context.applicationContext,
notificationId,
intent,
flag
)
} ?: throw IllegalStateException("Could not create open app intent")
}
private fun createOnDismissedIntent(
context: Context,
notificationId: Int,
deepLink: String?
): PendingIntent {
return Intent(ACTION_DELETE_NOTIFICATION).apply {
putExtra("im.status.ethereum.deepLink", deepLink)
}.let { intent ->
PendingIntent.getBroadcast(
context.applicationContext,
notificationId,
intent,
flag
)
}
}
fun removeStatusMessage(bundle: Bundle) {
val conversationId = bundle.getString("conversationId") ?: return
val group = messageGroups.getOrPut(conversationId) {
StatusMessageGroup(conversationId)
}
bundle.getString("id")?.let { id ->
group.removeMessage(id)
}
showMessages(bundle)
}
fun getMessageGroup(conversationId: String): StatusMessageGroup? {
return messageGroups[conversationId]
}
fun addStatusMessage(bundle: Bundle) {
val conversationId = bundle.getString("conversationId") ?: return
val group = messageGroups.getOrPut(conversationId) {
StatusMessageGroup(conversationId)
}
group.addMessage(createMessage(bundle))
showMessages(bundle)
}
fun showMessages(bundle: Bundle) {
val conversationId = bundle.getString("conversationId") ?: return
val group = messageGroups[conversationId] ?: return
val messagingStyle = NotificationCompat.MessagingStyle("Me")
// If there are no messages, cancel the notification and return
if (group.messages.isEmpty()) {
notificationManager.cancel(conversationId.hashCode())
return
}
group.messages.forEach { message ->
messagingStyle.addMessage(
message.text,
message.timestamp,
message.author
)
}
// Set conversation title if available
bundle.getString("title")?.let { title ->
messagingStyle.conversationTitle = title
}
val builder = NotificationCompat.Builder(context, CHANNEL_ID).apply {
setSmallIcon(R.drawable.ic_stat_notify_status)
setPriority(NotificationCompat.PRIORITY_HIGH)
setCategory(NotificationCompat.CATEGORY_MESSAGE)
setStyle(messagingStyle)
setGroup(conversationId)
setOnlyAlertOnce(true)
setGroupSummary(true)
setContentIntent(createGroupOnTapIntent(context, conversationId.hashCode(), conversationId, bundle.getString("deepLink")))
setDeleteIntent(createGroupOnDismissedIntent(context, conversationId.hashCode(), conversationId, bundle.getString("deepLink")))
setNumber(group.messages.size + 1)
setAutoCancel(true)
// Set empty vibration pattern for Android 5.0 and above
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setVibrate(LongArray(0))
}
}
// Show the notification
notificationManager.notify(conversationId.hashCode(), builder.build())
}
fun checkOrCreateChannel(
manager: NotificationManager?,
channelId: String,
channelName: String?,
channelDescription: String?,
soundUri: Uri?,
importance: Int,
vibratePattern: LongArray?,
showBadge: Boolean
): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || manager == null) {
return false
}
val existingChannel = manager.getNotificationChannel(channelId)
val shouldCreateOrUpdate = when {
// Channel doesn't exist and we have required info
existingChannel == null && channelName != null && channelDescription != null -> true
// Channel exists but needs update
existingChannel != null && (
(channelName != null && existingChannel.name != channelName) ||
(channelDescription != null && existingChannel.description != channelDescription)
) -> true
else -> false
}
if (shouldCreateOrUpdate) {
NotificationChannel(channelId, channelName, importance).apply {
description = channelDescription
enableLights(true)
enableVibration(vibratePattern != null)
vibrationPattern = vibratePattern
setShowBadge(showBadge)
// Configure sound
if (soundUri != null) {
val audioAttributes = AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.build()
setSound(soundUri, audioAttributes)
} else {
setSound(null, null)
}
manager.createNotificationChannel(this)
}
return true
}
return false
}
fun createChannel(channelInfo: ReadableMap): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return false
}
return checkOrCreateChannel(
manager = notificationManager,
channelId = channelInfo.getString("channelId") ?: return false,
channelName = channelInfo.getString("channelName"),
channelDescription = if (channelInfo.hasKey("channelDescription")) {
channelInfo.getString("channelDescription")
} else "",
soundUri = getSoundUri(
if (channelInfo.hasKey("soundName")) {
channelInfo.getString("soundName")
} else "default"
),
importance = if (channelInfo.hasKey("importance")) {
channelInfo.getInt("importance")
} else 4,
vibratePattern = if (channelInfo.hasKey("vibrate") && channelInfo.getBoolean("vibrate")) {
longArrayOf(DEFAULT_VIBRATION)
} else null,
showBadge = channelInfo.hasKey("showBadge") && channelInfo.getBoolean("showBadge")
)
}
fun getNotificationDefaultChannelId(): String = CHANNEL_ID
private fun getSoundUri(soundName: String?): Uri {
return when {
soundName == null || soundName.equals("default", ignoreCase = true) -> {
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
}
else -> {
val resources = context.resources
val packageName = context.packageName
val resId = when {
// Try to find the resource as is
resources.getIdentifier(soundName, "raw", packageName) != 0 -> {
resources.getIdentifier(soundName, "raw", packageName)
}
// Try without file extension
else -> {
val nameWithoutExtension = soundName.substringBeforeLast('.')
resources.getIdentifier(nameWithoutExtension, "raw", packageName)
}
}
Uri.parse("android.resource://$packageName/$resId")
}
}
}
private fun removeGroup(groupId: String) {
messageGroups.remove(groupId)
}
private fun cleanGroup(groupId: String) {
removeGroup(groupId)
if (messageGroups.isEmpty()) {
notificationManager.cancelAll()
}
}
fun start() {
Log.e(LOG_TAG, "Starting Foreground Service")
Intent(context, ForegroundService::class.java).also { serviceIntent ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(serviceIntent)
} else {
context.startService(serviceIntent)
}
}
registerBroadcastReceiver()
}
fun stop() {
Log.e(LOG_TAG, "Stopping Foreground Service")
// Cancel all current notifications since their intents won't be valid
// after the broadcast receiver is killed
notificationManager.cancelAll()
Intent(context, ForegroundService::class.java).also { serviceIntent ->
context.stopService(serviceIntent)
}
context.unregisterReceiver(notificationActionReceiver)
}
fun sendToNotificationCentre(bundle: Bundle) {
val aggregator = PushNotificationPicturesAggregator { largeIconImage, bigPictureImage ->
sendToNotificationCentreWithPicture(bundle, largeIconImage, bigPictureImage)
}
aggregator.apply {
setLargeIconUrl(context, bundle.getString("largeIconUrl"))
setBigPictureUrl(context, bundle.getString("bigPictureUrl"))
}
}
fun handleConversation(bundle: Bundle) {
if (bundle.getBoolean("deleted")) {
removeStatusMessage(bundle)
} else {
addStatusMessage(bundle)
}
}
fun clearMessageNotifications(conversationId: String) {
notificationManager.cancel(conversationId.hashCode())
cleanGroup(conversationId)
}
fun clearAllMessageNotifications() {
notificationManager.cancelAll()
}
fun sendToNotificationCentreWithPicture(
bundle: Bundle,
largeIconBitmap: Bitmap?,
bigPictureBitmap: Bitmap?
) {
try {
val intentClass = getMainActivityClass() ?: run {
Log.e(LOG_TAG, "No activity class found for the notification")
return
}
if (bundle.getBoolean("isConversation")) {
handleConversation(bundle)
return
}
if (bundle.getString("message") == null) {
Log.d(LOG_TAG, "Ignore this message if you sent data-only notification. " +
"Cannot send to notification centre because there is no 'message' field in: $bundle")
return
}
val notificationIdString = bundle.getString("id") ?: run {
Log.e(LOG_TAG, "No notification ID specified for the notification")
return
}
val resources = context.resources
val packageName = context.packageName
val title = bundle.getString("title") ?: run {
val appInfo = context.applicationInfo
context.packageManager.getApplicationLabel(appInfo).toString()
}
val priority = when (bundle.getString("priority")?.toLowerCase()) {
"max" -> NotificationCompat.PRIORITY_MAX
"high" -> NotificationCompat.PRIORITY_HIGH
"low" -> NotificationCompat.PRIORITY_LOW
"min" -> NotificationCompat.PRIORITY_MIN
"default" -> NotificationCompat.PRIORITY_DEFAULT
else -> NotificationCompat.PRIORITY_HIGH
}
val visibility = when (bundle.getString("visibility")?.toLowerCase()) {
"private" -> NotificationCompat.VISIBILITY_PRIVATE
"public" -> NotificationCompat.VISIBILITY_PUBLIC
"secret" -> NotificationCompat.VISIBILITY_SECRET
else -> NotificationCompat.VISIBILITY_PRIVATE
}
val channelId = bundle.getString("channelId") ?: getNotificationDefaultChannelId()
NotificationCompat.Builder(context, channelId).apply {
setContentTitle(title)
setTicker(bundle.getString("ticker"))
setVisibility(visibility)
setPriority(priority)
setAutoCancel(bundle.getBoolean("autoCancel", true))
setOnlyAlertOnce(bundle.getBoolean("onlyAlertOnce", false))
// Configure notification based on Android version
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
setShowWhen(bundle.getBoolean("showWhen", true))
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
setDefaults(Notification.DEFAULT_LIGHTS)
}
// Handle group settings for Android KitKat Watch and above
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
bundle.getString("group")?.let { group ->
setGroup(group)
}
if (bundle.containsKey("groupSummary") || bundle.getBoolean("groupSummary")) {
setGroupSummary(bundle.getBoolean("groupSummary"))
}
}
// Set notification number if provided
bundle.getString("number")?.toIntOrNull()?.let { number ->
setNumber(number)
}
val smallIconResId = findSmallIconResourceId(resources, packageName, bundle)
setSmallIcon(smallIconResId)
configureLargeIcon(this, largeIconBitmap, bundle, resources, packageName)
val message = bundle.getString("message") ?: ""
setContentText(message)
bundle.getString("subText")?.let { subText ->
setSubText(subText)
}
val bigText = bundle.getString("bigText") ?: message
val style = when {
bigPictureBitmap != null -> NotificationCompat.BigPictureStyle()
.bigPicture(bigPictureBitmap)
.setBigContentTitle(title)
.setSummaryText(message)
else -> NotificationCompat.BigTextStyle().bigText(bigText)
}
setStyle(style)
val notificationId = notificationIdString.hashCode()
setContentIntent(createOnTapIntent(context, notificationId, bundle.getString("deepLink")))
setDeleteIntent(createOnDismissedIntent(context, notificationId, bundle.getString("deepLink")))
configureSoundAndVibration(this, bundle)
setOngoing(bundle.getBoolean("ongoing", false))
// Handle color for Lollipop and above
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setCategory(NotificationCompat.CATEGORY_CALL)
bundle.getString("color")?.let { color ->
setColor(Color.parseColor(color))
}
}
// Finally show the notification if conditions are met
if (!(isApplicationInForeground() && bundle.getBoolean("ignoreInForeground"))) {
val notification = build()
notification.defaults = notification.defaults or Notification.DEFAULT_LIGHTS
if (bundle.containsKey("tag")) {
notificationManager.notify(bundle.getString("tag"), notificationId, notification)
} else {
notificationManager.notify(notificationId, notification)
}
}
}
} catch (e: Exception) {
Log.e(LOG_TAG, "Failed to send push notification", e)
}
}
private fun findSmallIconResourceId(resources: Resources, packageName: String, bundle: Bundle): Int {
val smallIcon = bundle.getString("smallIcon")
return when {
!smallIcon.isNullOrEmpty() ->
resources.getIdentifier(smallIcon, "mipmap", packageName)
smallIcon == null ->
resources.getIdentifier("ic_stat_notify_status", "drawable", packageName)
else -> {
val defaultIcon = resources.getIdentifier("ic_launcher", "mipmap", packageName)
defaultIcon.takeIf { it != 0 } ?: android.R.drawable.ic_dialog_info
}
}
}
private fun configureLargeIcon(
builder: NotificationCompat.Builder,
largeIconBitmap: Bitmap?,
bundle: Bundle,
resources: Resources,
packageName: String
) {
var finalLargeIcon = largeIconBitmap
if (finalLargeIcon == null) {
val largeIcon = bundle.getString("largeIcon")
val largeIconResId = when {
!largeIcon.isNullOrEmpty() ->
resources.getIdentifier(largeIcon, "mipmap", packageName)
largeIcon == null ->
resources.getIdentifier("ic_launcher", "mipmap", packageName)
else -> 0
}
if (largeIconResId != 0 && (largeIcon != null || Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)) {
finalLargeIcon = BitmapFactory.decodeResource(resources, largeIconResId)
}
}
bundle.getBundle("notificationAuthor")?.getString("icon")?.let { icon ->
val base64Image = icon.split(",")[1]
val decodedString = Base64.decode(base64Image, Base64.DEFAULT)
val decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.size)
builder.setLargeIcon(getCircleBitmap(decodedByte))
} ?: finalLargeIcon?.let { icon ->
builder.setLargeIcon(icon)
}
}
private fun configureSoundAndVibration(
builder: NotificationCompat.Builder,
bundle: Bundle
) {
var soundUri: Uri? = null
if (!bundle.containsKey("playSound") || bundle.getBoolean("playSound")) {
val soundName = bundle.getString("soundName") ?: "default"
soundUri = getSoundUri(soundName)
builder.setSound(soundUri)
}
if (soundUri == null || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setSound(null)
}
if (!bundle.containsKey("vibrate") || bundle.getBoolean("vibrate")) {
val vibration = if (bundle.containsKey("vibration")) {
bundle.getDouble("vibration").toLong()
} else DEFAULT_VIBRATION
val finalVibration = if (vibration == 0L) DEFAULT_VIBRATION else vibration
builder.setVibrate(longArrayOf(0, finalVibration))
}
}
}
@@ -1,86 +0,0 @@
package im.status.ethereum.pushnotifications;
import android.os.Build;
import android.app.Application;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Set;
import static im.status.ethereum.pushnotifications.PushNotification.LOG_TAG;
class PushNotificationJsDelivery {
private ReactContext reactContext;
PushNotificationJsDelivery(ReactContext context){
reactContext = context;
}
String convertJSON(Bundle bundle) {
try {
JSONObject json = convertJSONObject(bundle);
return json.toString();
} catch (JSONException e) {
return null;
}
}
// a Bundle is not a map, so we have to convert it explicitly
private JSONObject convertJSONObject(Bundle bundle) throws JSONException {
JSONObject json = new JSONObject();
Set<String> keys = bundle.keySet();
for (String key : keys) {
Object value = bundle.get(key);
if (value instanceof Bundle) {
json.put(key, convertJSONObject((Bundle)value));
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
json.put(key, JSONObject.wrap(value));
} else {
json.put(key, value);
}
}
return json;
}
void notifyNotification(Bundle bundle) {
String bundleString = convertJSON(bundle);
WritableMap params = Arguments.createMap();
params.putString("dataJSON", bundleString);
sendEvent("remoteNotificationReceived", params);
}
void notifyNotificationAction(Bundle bundle) {
String bundleString = convertJSON(bundle);
WritableMap params = Arguments.createMap();
params.putString("dataJSON", bundleString);
sendEvent("notificationActionReceived", params);
}
void sendEvent(String eventName, Object params) {
if (reactContext.hasActiveCatalystInstance()) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
}
}
@@ -0,0 +1,93 @@
package im.status.ethereum.pushnotifications
import android.os.Build
import android.app.Application
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.modules.core.DeviceEventManagerModule
import org.json.JSONException
import org.json.JSONObject
import im.status.ethereum.pushnotifications.PushNotification.Companion.LOG_TAG
class PushNotificationJsDelivery(
private val reactContext: ReactContext
) {
/**
* Converts a Bundle to its JSON string representation.
* Returns null if conversion fails.
*/
fun convertJSON(bundle: Bundle): String? {
return try {
convertJSONObject(bundle).toString()
} catch (e: JSONException) {
null
}
}
/**
* Converts a Bundle to a JSONObject, handling nested bundles and different value types.
* This explicit conversion is necessary because Bundle is not a standard map.
*/
@Throws(JSONException::class)
private fun convertJSONObject(bundle: Bundle): JSONObject {
return JSONObject().apply {
// Using Kotlin's for loop syntax and 'apply' scope function
bundle.keySet().forEach { key ->
bundle.get(key)?.let { value ->
when (value) {
is Bundle -> put(key, convertJSONObject(value))
else -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
put(key, JSONObject.wrap(value))
} else {
put(key, value)
}
}
}
}
}
}
/**
* Notifies about a received remote notification by sending an event to JavaScript
*/
fun notifyNotification(bundle: Bundle) {
convertJSON(bundle)?.let { bundleString ->
Arguments.createMap().apply {
putString("dataJSON", bundleString)
sendEvent("remoteNotificationReceived", this)
}
}
}
/**
* Notifies about a notification action by sending an event to JavaScript
*/
fun notifyNotificationAction(bundle: Bundle) {
convertJSON(bundle)?.let { bundleString ->
Arguments.createMap().apply {
putString("dataJSON", bundleString)
sendEvent("notificationActionReceived", this)
}
}
}
/**
* Sends an event to JavaScript if there's an active Catalyst instance
*/
fun sendEvent(eventName: String, params: Any) {
if (reactContext.hasActiveCatalystInstance()) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit(eventName, params)
}
}
}
@@ -1,27 +0,0 @@
package im.status.ethereum.pushnotifications;
import im.status.ethereum.pushnotifications.PushNotification;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.Collections;
import java.util.List;
public class PushNotificationPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.<NativeModule>singletonList(new PushNotification(reactContext));
}
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
@@ -0,0 +1,24 @@
package im.status.ethereum.pushnotifications
import im.status.ethereum.pushnotifications.PushNotification
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.JavaScriptModule
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
class PushNotificationPackage : ReactPackage {
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
return listOf(PushNotification(reactContext))
}
// Note: This function is deprecated in newer versions of React Native
// but kept for backwards compatibility
fun createJSModules(): List<Class<out JavaScriptModule>> {
return emptyList()
}
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList()
}
}
@@ -1,136 +0,0 @@
package im.status.ethereum.pushnotifications;
import androidx.annotation.Nullable;
import com.facebook.common.executors.CallerThreadExecutor;
import com.facebook.common.references.CloseableReference;
import com.facebook.datasource.DataSource;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.common.Priority;
import com.facebook.imagepipeline.core.ImagePipeline;
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import android.util.Log;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import java.util.concurrent.atomic.AtomicInteger;
import static im.status.ethereum.pushnotifications.PushNotification.LOG_TAG;
public class PushNotificationPicturesAggregator {
interface Callback {
public void call(Bitmap largeIconImage, Bitmap bigPictureImage);
}
private AtomicInteger count = new AtomicInteger(0);
private Bitmap largeIconImage;
private Bitmap bigPictureImage;
private Callback callback;
public PushNotificationPicturesAggregator(Callback callback) {
this.callback = callback;
}
public void setBigPicture(Bitmap bitmap) {
this.bigPictureImage = bitmap;
this.finished();
}
public void setBigPictureUrl(Context context, String url) {
if(null == url) {
this.setBigPicture(null);
return;
}
Uri uri = null;
try {
uri = Uri.parse(url);
} catch(Exception ex) {
Log.e(LOG_TAG, "Failed to parse bigPictureUrl", ex);
this.setBigPicture(null);
return;
}
final PushNotificationPicturesAggregator aggregator = this;
this.downloadRequest(context, uri, new BaseBitmapDataSubscriber() {
@Override
public void onNewResultImpl(@Nullable Bitmap bitmap) {
aggregator.setBigPicture(bitmap);
}
@Override
public void onFailureImpl(DataSource dataSource) {
aggregator.setBigPicture(null);
}
});
}
public void setLargeIcon(Bitmap bitmap) {
this.largeIconImage = bitmap;
this.finished();
}
public void setLargeIconUrl(Context context, String url) {
if(null == url) {
this.setLargeIcon(null);
return;
}
Uri uri = null;
try {
uri = Uri.parse(url);
} catch(Exception ex) {
Log.e(LOG_TAG, "Failed to parse largeIconUrl", ex);
this.setLargeIcon(null);
return;
}
final PushNotificationPicturesAggregator aggregator = this;
this.downloadRequest(context, uri, new BaseBitmapDataSubscriber() {
@Override
public void onNewResultImpl(@Nullable Bitmap bitmap) {
aggregator.setLargeIcon(bitmap);
}
@Override
public void onFailureImpl(DataSource dataSource) {
aggregator.setLargeIcon(null);
}
});
}
private void downloadRequest(Context context, Uri uri, BaseBitmapDataSubscriber subscriber) {
ImageRequest imageRequest = ImageRequestBuilder
.newBuilderWithSource(uri)
.setRequestPriority(Priority.HIGH)
.setLowestPermittedRequestLevel(ImageRequest.RequestLevel.FULL_FETCH)
.build();
if(!Fresco.hasBeenInitialized()) {
Fresco.initialize(context);
}
DataSource<CloseableReference<CloseableImage>> dataSource = Fresco.getImagePipeline().fetchDecodedImage(imageRequest, context);
dataSource.subscribe(subscriber, CallerThreadExecutor.getInstance());
}
private void finished() {
synchronized(this.count) {
int val = this.count.incrementAndGet();
if(val >= 2 && this.callback != null) {
this.callback.call(this.largeIconImage, this.bigPictureImage);
}
}
}
}
@@ -0,0 +1,109 @@
package im.status.ethereum.pushnotifications
import androidx.annotation.Nullable
import com.facebook.common.executors.CallerThreadExecutor
import com.facebook.common.references.CloseableReference
import com.facebook.datasource.DataSource
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.imagepipeline.common.Priority
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber
import com.facebook.imagepipeline.image.CloseableImage
import com.facebook.imagepipeline.request.ImageRequest
import com.facebook.imagepipeline.request.ImageRequestBuilder
import android.util.Log
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import java.util.concurrent.atomic.AtomicInteger
import im.status.ethereum.pushnotifications.PushNotification.Companion.LOG_TAG
class PushNotificationPicturesAggregator(
private val callback: (Bitmap?, Bitmap?) -> Unit
) {
private val count = AtomicInteger(0)
private var largeIconImage: Bitmap? = null
private var bigPictureImage: Bitmap? = null
fun setBigPicture(bitmap: Bitmap?) {
bigPictureImage = bitmap
finished()
}
fun setBigPictureUrl(context: Context, url: String?) {
if (url == null) {
setBigPicture(null)
return
}
val uri = try {
Uri.parse(url)
} catch (ex: Exception) {
Log.e(LOG_TAG, "Failed to parse bigPictureUrl", ex)
setBigPicture(null)
return
}
downloadRequest(context, uri, object : BaseBitmapDataSubscriber() {
override fun onNewResultImpl(@Nullable bitmap: Bitmap?) {
setBigPicture(bitmap)
}
override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
setBigPicture(null)
}
})
}
fun setLargeIcon(bitmap: Bitmap?) {
largeIconImage = bitmap
finished()
}
fun setLargeIconUrl(context: Context, url: String?) {
if (url == null) {
setLargeIcon(null)
return
}
val uri = try {
Uri.parse(url)
} catch (ex: Exception) {
Log.e(LOG_TAG, "Failed to parse largeIconUrl", ex)
setLargeIcon(null)
return
}
downloadRequest(context, uri, object : BaseBitmapDataSubscriber() {
override fun onNewResultImpl(@Nullable bitmap: Bitmap?) {
setLargeIcon(bitmap)
}
override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
setLargeIcon(null)
}
})
}
private fun downloadRequest(context: Context, uri: Uri, subscriber: BaseBitmapDataSubscriber) {
val imageRequest = ImageRequestBuilder
.newBuilderWithSource(uri)
.setRequestPriority(Priority.HIGH)
.setLowestPermittedRequestLevel(ImageRequest.RequestLevel.FULL_FETCH)
.build()
if (!Fresco.hasBeenInitialized()) {
Fresco.initialize(context)
}
val dataSource = Fresco.getImagePipeline().fetchDecodedImage(imageRequest, context)
dataSource.subscribe(subscriber, CallerThreadExecutor.getInstance())
}
private fun finished() {
synchronized(count) {
if (count.incrementAndGet() >= 2) {
callback(largeIconImage, bigPictureImage)
}
}
}
}
@@ -1,54 +0,0 @@
package im.status.ethereum.pushnotifications;
import org.junit.Test;
import static org.junit.Assert.*;
import android.app.Application;
import android.app.NotificationManager;
import android.os.Bundle;
import android.content.Intent;
import android.app.PendingIntent;
import android.content.IntentFilter;
import static org.mockito.Mockito.*;
import org.mockito.Spy;
public class PushNotificationHelperTest {
PushNotificationHelper helper;
@Test
public void testAddStatusMessage() {
final Application context = mock(Application.class);
final NotificationManager manager = mock(NotificationManager.class);
final IntentFilter filter = mock(IntentFilter.class);
when(context.getSystemService(NotificationManager.class)).thenReturn(manager);
// Create a spy for PushNotificationHelper
PushNotificationHelper realHelper = new PushNotificationHelper(context, filter);
helper = spy(realHelper);
Bundle bundle = mock(Bundle.class);
Bundle personBundle = mock(Bundle.class);
String conversationId = "conversation-id";
String deepLink = "deep-link";
when(bundle.getString("conversationId")).thenReturn(conversationId);
when(bundle.getString("id")).thenReturn("id");
when(bundle.getString("message")).thenReturn("message");
when(bundle.getDouble("timestamp")).thenReturn(100000.0);
when(bundle.getString("deepLink")).thenReturn(deepLink);
when(personBundle.getString("name")).thenReturn("name");
when(bundle.getBundle("notificationAuthor")).thenReturn(personBundle);
doNothing().when(helper).showMessages(bundle);
helper.addStatusMessage(bundle);
assertNotNull(helper.getMessageGroup(conversationId));
}
}
@@ -0,0 +1,49 @@
package im.status.ethereum.pushnotifications
import android.app.Application
import android.app.NotificationManager
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import org.junit.Assert.assertNotNull
import org.junit.Test
import org.mockito.Mockito.*
import org.mockito.Spy
class PushNotificationHelperTest {
private lateinit var helper: PushNotificationHelper
@Test
fun testAddStatusMessage() {
val context = mock(Application::class.java)
val manager = mock(NotificationManager::class.java)
val filter = mock(IntentFilter::class.java)
`when`(context.getSystemService(NotificationManager::class.java)).thenReturn(manager)
// Create a spy for PushNotificationHelper
val realHelper = PushNotificationHelper(context, filter)
helper = spy(realHelper)
val bundle = mock(Bundle::class.java)
val personBundle = mock(Bundle::class.java)
val conversationId = "conversation-id"
val deepLink = "deep-link"
`when`(bundle.getString("conversationId")).thenReturn(conversationId)
`when`(bundle.getString("id")).thenReturn("id")
`when`(bundle.getString("message")).thenReturn("message")
`when`(bundle.getDouble("timestamp")).thenReturn(100000.0)
`when`(bundle.getString("deepLink")).thenReturn(deepLink)
`when`(personBundle.getString("name")).thenReturn("name")
`when`(bundle.getBundle("notificationAuthor")).thenReturn(personBundle)
doNothing().`when`(helper).showMessages(bundle)
helper.addStatusMessage(bundle)
assertNotNull(helper.getMessageGroup(conversationId))
}
}
@@ -195,36 +195,6 @@ RCT_EXPORT_METHOD(loginAccount:(NSString *)request) {
}];
}
RCT_EXPORT_METHOD(verify:(NSString *)address
password:(NSString *)password
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"VerifyAccountPasswordV2() method called");
#endif
NSURL *rootUrl = [Utils getRootUrl];
NSString *keystorePath = [rootUrl.path stringByAppendingPathComponent:@"keystore"];
NSDictionary *params = @{
@"keyStoreDir": keystorePath,
@"address": address,
@"password": password
};
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:params options:0 error:&error];
if (error) {
NSLog(@"Error creating JSON: %@", error);
return;
}
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
[StatusBackendClient executeStatusGoRequestWithCallback:@"VerifyAccountPasswordV2"
body:jsonString
statusgoFunction:^NSString *{
return StatusgoVerifyAccountPasswordV2(jsonString);
}
callback:callback];
}
RCT_EXPORT_METHOD(verifyDatabasePassword:(NSString *)keyUID
password:(NSString *)password
callback:(RCTResponseSenderBlock)callback) {
@@ -105,48 +105,6 @@ RCT_EXPORT_METHOD(convertToKeycardAccount:(NSString *)keyUID
callback:callback];
}
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(encodeTransfer:(NSString *)to
value:(NSString *)value) {
NSDictionary *params = @{
@"to": to,
@"value": value
};
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:params options:0 error:&error];
if (error) {
NSLog(@"Error creating JSON: %@", [error localizedDescription]);
return nil;
}
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
return [StatusBackendClient executeStatusGoRequestWithResult:@"EncodeTransferV2"
body:jsonString
statusgoFunction:^NSString *{
return StatusgoEncodeTransferV2(jsonString);
}];
}
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(encodeFunctionCall:(NSString *)method
paramsJSON:(NSString *)paramsJSON) {
NSDictionary *params = @{
@"method": method,
@"paramsJSON": paramsJSON
};
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:params options:0 error:&error];
if (error) {
NSLog(@"Error creating JSON: %@", [error localizedDescription]);
return nil;
}
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
return [StatusBackendClient executeStatusGoRequestWithResult:@"EncodeFunctionCallV2"
body:jsonString
statusgoFunction:^NSString *{
return StatusgoEncodeFunctionCallV2(jsonString);
}];
}
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(decodeParameters:(NSString *)decodeParamJSON) {
return [StatusBackendClient executeStatusGoRequestWithResult:@"DecodeParameters"
body:decodeParamJSON
@@ -35,6 +35,7 @@ RCT_EXPORT_METHOD(sendLogs:(NSString *)dbJson
NSURL *mainGethLogsFile = [rootUrl URLByAppendingPathComponent:@"geth.log"];
NSURL *mainLogsFile = [logsFolderName URLByAppendingPathComponent:@"geth.log"];
NSURL *preLoginLogFile = [rootUrl URLByAppendingPathComponent:@"pre_login.log"];
NSURL *requestsLogFile = [rootUrl URLByAppendingPathComponent:@"api.log"];
@@ -46,6 +47,10 @@ RCT_EXPORT_METHOD(sendLogs:(NSString *)dbJson
if ([fileManager fileExistsAtPath:requestsLogFile.path]) {
[fileManager copyItemAtPath:requestsLogFile.path toPath:[logsFolderName URLByAppendingPathComponent:@"api.log"].path error:nil];
}
if ([fileManager fileExistsAtPath:preLoginLogFile.path]) {
[fileManager copyItemAtPath:preLoginLogFile.path toPath:[logsFolderName URLByAppendingPathComponent:@"pre_login.log"].path error:nil];
}
[SSZipArchive createZipFileAtPath:zipFile.path withContentsOfDirectory:logsFolderName.path];
[fileManager removeItemAtPath:logsFolderName.path error:nil];
@@ -109,6 +109,17 @@ RCT_EXPORT_METHOD(getNodeConfig:(RCTResponseSenderBlock)callback) {
callback:callback];
}
RCT_EXPORT_METHOD(intendedPanic:(NSString *)message) {
#if DEBUG
NSLog(@"IntendedPanic() method called");
#endif
[StatusBackendClient executeStatusGoRequest:@"IntendedPanic"
body:message
statusgoFunction:^NSString *{
return StatusgoIntendedPanic(message);
}];
}
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(fleets) {
return [StatusBackendClient executeStatusGoRequestWithResult:@"Fleets"
body:@""
@@ -389,86 +389,6 @@ void _StopCPUProfiling(const FunctionCallbackInfo<Value>& args) {
}
void _EncodeTransfer(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
if (args.Length() != 2) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong number of arguments for EncodeTransfer")));
return;
}
// Check the argument types
if (!args[0]->IsString()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong argument type for 'to'")));
return;
}
if (!args[1]->IsString()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong argument type for 'value'")));
return;
}
String::Utf8Value arg0Obj(isolate, args[0]->ToString(context).ToLocalChecked());
char *arg0 = *arg0Obj;
String::Utf8Value arg1Obj(isolate, args[1]->ToString(context).ToLocalChecked());
char *arg1 = *arg1Obj;
// Call exported Go function, which returns a C string
char *c = EncodeTransfer(arg0, arg1);
Local<String> ret = String::NewFromUtf8(isolate, c).ToLocalChecked();
args.GetReturnValue().Set(ret);
delete c;
}
void _EncodeFunctionCall(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
if (args.Length() != 2) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong number of arguments for EncodeFunctionCall")));
return;
}
// Check the argument types
if (!args[0]->IsString()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong argument type for 'method'")));
return;
}
if (!args[1]->IsString()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong argument type for 'paramsJSON'")));
return;
}
String::Utf8Value arg0Obj(isolate, args[0]->ToString(context).ToLocalChecked());
char *arg0 = *arg0Obj;
String::Utf8Value arg1Obj(isolate, args[1]->ToString(context).ToLocalChecked());
char *arg1 = *arg1Obj;
// Call exported Go function, which returns a C string
char *c = EncodeFunctionCall(arg0, arg1);
Local<String> ret = String::NewFromUtf8(isolate, c).ToLocalChecked();
args.GetReturnValue().Set(ret);
delete c;
}
void _DecodeParameters(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
@@ -1380,53 +1300,6 @@ void _CallPrivateRPC(const FunctionCallbackInfo<Value>& args) {
}
void _VerifyAccountPassword(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
if (args.Length() != 3) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong number of arguments for VerifyAccountPassword")));
return;
}
// Check the argument types
if (!args[0]->IsString()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong argument type for 'keyStoreDir'")));
return;
}
if (!args[1]->IsString()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong argument type for 'address'")));
return;
}
if (!args[2]->IsString()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong argument type for 'password'")));
return;
}
String::Utf8Value arg0Obj(isolate, args[0]->ToString(context).ToLocalChecked());
char *arg0 = *arg0Obj;
String::Utf8Value arg1Obj(isolate, args[1]->ToString(context).ToLocalChecked());
char *arg1 = *arg1Obj;
String::Utf8Value arg2Obj(isolate, args[2]->ToString(context).ToLocalChecked());
char *arg2 = *arg2Obj;
// Call exported Go function, which returns a C string
char *c = VerifyAccountPassword(arg0, arg1, arg2);
Local<String> ret = String::NewFromUtf8(isolate, c).ToLocalChecked();
args.GetReturnValue().Set(ret);
delete c;
}
void _SendTransactionWithSignature(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
@@ -1992,8 +1865,6 @@ void init(Local<Object> exports) {
NODE_SET_METHOD(exports, "acceptTerms", _AcceptTerms);
NODE_SET_METHOD(exports, "fleets", _Fleets);
NODE_SET_METHOD(exports, "stopCPUProfiling", _StopCPUProfiling);
NODE_SET_METHOD(exports, "encodeTransfer", _EncodeTransfer);
NODE_SET_METHOD(exports, "encodeFunctionCall", _EncodeFunctionCall);
NODE_SET_METHOD(exports, "decodeParameters", _DecodeParameters);
NODE_SET_METHOD(exports, "hexToNumber", _HexToNumber);
NODE_SET_METHOD(exports, "numberToHex", _NumberToHex);
@@ -2017,7 +1888,6 @@ void init(Local<Object> exports) {
NODE_SET_METHOD(exports, "openAccounts", _OpenAccounts);
NODE_SET_METHOD(exports, "extractGroupMembershipSignatures", _ExtractGroupMembershipSignatures);
NODE_SET_METHOD(exports, "callPrivateRPC", _CallPrivateRPC);
NODE_SET_METHOD(exports, "verifyAccountPassword", _VerifyAccountPassword);
NODE_SET_METHOD(exports, "sendTransactionWithSignature", _SendTransactionWithSignature);
NODE_SET_METHOD(exports, "writeHeapProfile", _WriteHeapProfile);
NODE_SET_METHOD(exports, "addPeer", _AddPeer);
+229 -270
View File
@@ -8838,8 +8838,8 @@
"repo": "https://jitpack.io",
"files": {
"reflow-animator-1.0.6.pom": {
"sha1": "4fe3d33c764beebdc477d93a590f63dfa38a5f6b",
"sha256": "sha256-MEINlM+8v6LDFGREWswamEl17l26xa4eQHeKPcxbSI8="
"sha1": "d2c5089ae74a366676d9432c401e373b4895858e",
"sha256": "sha256-uanUNC0CwC2YnlQ4rpZBMdp+Is6UF36Y/Iwn6YLLHno="
},
"reflow-animator-1.0.6.aar": {
"sha1": "36074a3fcb77c2ba12bd2de7dde81441e0c7504f",
@@ -8898,20 +8898,20 @@
},
{
"path": "org/gradle/github-dependency-graph-gradle-plugin/1.3.1",
"path": "org/gradle/github-dependency-graph-gradle-plugin/1.3.2",
"repo": "https://plugins.gradle.org/m2",
"files": {
"github-dependency-graph-gradle-plugin-1.3.1.pom": {
"sha1": "5d95862731c88c1e9190fa27a7bd2ffe3ab549d7",
"sha256": "sha256-Zk7PNeEBetpWAp4e2rN5A+zn0NDsG3G5kU62uPssiJo="
"github-dependency-graph-gradle-plugin-1.3.2.pom": {
"sha1": "0740a44f4c99d3e2d9fbc2c89afcb195ec66dbdf",
"sha256": "sha256-xaVDODswVgtoaoIAtgCELvnd8IAx8AYdJA+FE3a0Jxs="
},
"github-dependency-graph-gradle-plugin-1.3.1.jar": {
"sha1": "c905642b753938ef0fa1cda1fc330fae1c4016aa",
"sha256": "sha256-0uYWjgzaasT18c62W6IElG2jW1Oe+X0O/4dflT5E61k="
"github-dependency-graph-gradle-plugin-1.3.2.jar": {
"sha1": "ba3d0995475c6aa79048e865d7923bbed20f36a0",
"sha256": "sha256-VdNKG2xBu75P6hSlZaERE3ZxOSSwAaPMjD2EnGSejQA="
},
"github-dependency-graph-gradle-plugin-1.3.1.module": {
"sha1": "818778b46a93f8d60f11cfa83c7ecbb2481706d8",
"sha256": "sha256-agfZPNoTEZDEU5USp5slvtvCfm5OxZucX7XuZ9xugzU="
"github-dependency-graph-gradle-plugin-1.3.2.module": {
"sha1": "879866cccedf304d8c484d3bb70eb6d699e9e118",
"sha256": "sha256-nBqz4Xbqelt3YhEAEG9aPzYC0+2MBcM1M+1Ys5PumhQ="
}
}
},
@@ -9052,16 +9052,16 @@
},
{
"path": "commons-codec/commons-codec/1.17.1",
"path": "commons-codec/commons-codec/1.18.0",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"commons-codec-1.17.1.pom": {
"sha1": "d187e2d50ab520895df0dcb9d88980ffea50bfa3",
"sha256": "sha256-f6DbTYFQ2vkylYuK6onuJKu00Y4jFqXeU1J4/BMVEqA="
"commons-codec-1.18.0.pom": {
"sha1": "2aa7395e4e4d63be2fd4439da7a01527235f76bf",
"sha256": "sha256-dLkW2ksDhMYZ5t1MGN7+iqQ4f3lSBSU8+0u7L0WM3c4="
},
"commons-codec-1.17.1.jar": {
"sha1": "973638b7149d333563584137ebf13a691bb60579",
"sha256": "sha256-+fbLED8t3DyZqdgK2irnvwaFER/Wv/zLcgM9HaTm/yM="
"commons-codec-1.18.0.jar": {
"sha1": "ee45d1cf6ec2cc2b809ff04b4dc7aec858e0df8f",
"sha256": "sha256-ugBfMEzvkqPe3iSjitWsm4r8zw2PdYOdbBM4Y0z39uQ="
}
}
},
@@ -9135,20 +9135,20 @@
},
{
"path": "commons-logging/commons-logging/1.3.4",
"path": "commons-logging/commons-logging/1.3.5",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"commons-logging-1.3.4.pom": {
"sha1": "e919eab34a66d31a9bbe15ddc563d7d2e6770826",
"sha256": "sha256-1L2jSJKqzL9PrTP8MjqKqQfvnXmYRlnaJJRMCoa/CGU="
"commons-logging-1.3.5.pom": {
"sha1": "378e7ead6461eabd50a97f6503d061cad0dcae9d",
"sha256": "sha256-zPSjA0b1AnuUlqvVYpCsJtFZq/K+ZN8SZWEdyUALnWg="
},
"commons-logging-1.3.4-api.jar": {
"sha1": "de4050262dfa7522365a9e3c76ed8cf4683fcffa",
"sha256": "sha256-X1zVkiUzQHDbaxctBaaFuEei+tY+WxjtAjqqpeErgh8="
"commons-logging-1.3.5-api.jar": {
"sha1": "2e234baf9b0fd82e4e2f0db3b9083900fbfe2f36",
"sha256": "sha256-6WjRzl2r6cQzxHbTd+ZSJzD2FttdtdKO2sfEWMtVMdw="
},
"commons-logging-1.3.4.jar": {
"sha1": "b9fc14968d63a8b8a8a2c1885fe3e90564239708",
"sha256": "sha256-vC3+MvHvBlCeagZRRMGt97Qg6r8RqH8wvRJ/j6ozIBY="
"commons-logging-1.3.5.jar": {
"sha1": "a3fcc5d3c29b2b03433aa2d2f2d2c1b1638924a1",
"sha256": "sha256-bXp0TkAnZJ+7UIld+Ul9EJ+Yx2amNwYv6NLqu7MUC6Q="
}
}
},
@@ -11836,12 +11836,12 @@
},
{
"path": "com/google/code/gson/gson-parent/2.11.0",
"path": "com/google/code/gson/gson-parent/2.12.1",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"gson-parent-2.11.0.pom": {
"sha1": "b32d3bc5c89c852efe4c176df6c3fdeccbe150ef",
"sha256": "sha256-issfO3Km8CaRasBzW62aqwKT1Sftt7NlMn3vE6k2e3o="
"gson-parent-2.12.1.pom": {
"sha1": "660107b2e76095ef86bbd15c503afe11e5260bfb",
"sha256": "sha256-yeewt+Mb574iaEl5wGgAHGUssRPE5u2JTjm2Q97gf8E="
}
}
},
@@ -11967,24 +11967,24 @@
},
{
"path": "com/google/code/gson/gson/2.11.0",
"path": "com/google/code/gson/gson/2.12.1",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"gson-2.11.0.pom": {
"sha1": "55796dd7cc329027df79e96c4e6631e3c02c93c5",
"sha256": "sha256-wOVHvqmYiI5uJcWIapDnYicryItSdTQ90sBd7Wyi42A="
"gson-2.12.1.pom": {
"sha1": "d2c3993ff96e5da39a57e5e0b695eda560949b57",
"sha256": "sha256-C1c17IX0UoLx4sdpd5gAQnsVCoFj9AUJOpKAtxyrGXg="
},
"gson-2.11.0.buildinfo": {
"sha1": "4d1c42ee3c07943d8e07fab98bf0491877b4c0c6",
"sha256": "sha256-d6O/J0uIwA+ieS0kkb+yLkeL3OUzOMUcMBHW7nDr0pI="
"gson-2.12.1.buildinfo": {
"sha1": "c2e219d4a63eebf78ce409ee8303eb930248f486",
"sha256": "sha256-fNm4d7ep72MLsQq/BeBRdEuam/9hzGJafTURb48yeuY="
},
"gson-2.11.0.buildinfo.asc": {
"sha1": "ba50162dd390a51e11a706d62ef9a5d120138337",
"sha256": "sha256-blLhhVcT0w8htC6V6rrqU+CZ3egMLBv5AgE76D+zGIY="
"gson-2.12.1.buildinfo.asc": {
"sha1": "01ba63402708ab4efbd2fa2f4dc5ee7f136b9479",
"sha256": "sha256-M+At44sk0y+mblm7UnhRzYzeO36NPGTpP3rTID/wJjU="
},
"gson-2.11.0.jar": {
"sha1": "527175ca6d81050b53bdd4c457a6d6e017626b0e",
"sha256": "sha256-V5KNblpu3rKr03cKj5W6RNzkXzsjt6ncKzCcWBVSp4s="
"gson-2.12.1.jar": {
"sha1": "4e773a317740b83b43cfc3d652962856041697cb",
"sha256": "sha256-6+4T1ft0d81/HMAQ4MNW34yoBwlxUkjal/eeNcy0++w="
}
}
},
@@ -12199,21 +12199,6 @@
}
},
{
"path": "com/google/errorprone/error_prone_annotations/2.27.0",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"error_prone_annotations-2.27.0.pom": {
"sha1": "85103026aa50b0e4efafee8f486e3ecd9ebfde31",
"sha256": "sha256-TKWjXWEjXhZUmsNG0eNFUc3w/ifoSqV+A8vrJV6k5do="
},
"error_prone_annotations-2.27.0.jar": {
"sha1": "91b2c29d8a6148b5e2e4930f070d4840e2e48e34",
"sha256": "sha256-JMkjNyxY410LnxagKJKbua7cd1IYZ8J08r0HNd9bofU="
}
}
},
{
"path": "com/google/errorprone/error_prone_annotations/2.36.0",
"repo": "https://repo.maven.apache.org/maven2",
@@ -12350,17 +12335,6 @@
}
},
{
"path": "com/google/errorprone/error_prone_parent/2.27.0",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"error_prone_parent-2.27.0.pom": {
"sha1": "336f77abf2668b10cae4ab136ea0c8258875de00",
"sha256": "sha256-+oGCnQSVWd9pJ/nJpv1rvQn4tQ5tRzaucsgwC2w9dlQ="
}
}
},
{
"path": "com/google/errorprone/error_prone_parent/2.36.0",
"repo": "https://repo.maven.apache.org/maven2",
@@ -12561,27 +12535,27 @@
},
{
"path": "com/google/guava/guava-parent/33.3.1-jre",
"path": "com/google/guava/guava-parent/33.4.0-jre",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"guava-parent-33.3.1-jre.pom": {
"sha1": "94729a0ed1dc35f623edd13afa6c1c2fe9a15d7c",
"sha256": "sha256-VUQdsn6Iad/v4FMFm99Hi9x+lVhWQr85HwAjNF/VYoc="
"guava-parent-33.4.0-jre.pom": {
"sha1": "ee79b87abc4ef9b3591db13c59de368c7b03dc79",
"sha256": "sha256-Okme00oNnuDxvMOSMAIaHNTi990EJqtoRPWFRl1B3Nc="
}
}
},
{
"path": "com/google/guava/guava-testlib/33.3.1-jre",
"path": "com/google/guava/guava-testlib/33.4.0-jre",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"guava-testlib-33.3.1-jre.pom": {
"sha1": "12fbde61e46674436b0ec48f9b54fbd5f78f8256",
"sha256": "sha256-4n6bu2lrvSCUeAF1YUO9EazSCFBFSnEILoceA+27PSw="
"guava-testlib-33.4.0-jre.pom": {
"sha1": "f92730965d606702deb41eca3afb47396b7a5c6a",
"sha256": "sha256-p+EQNjDV99XYCN3NnbnVhLvVldRW2UxpHOwJDQ7qZ/A="
},
"guava-testlib-33.3.1-jre.jar": {
"sha1": "a2eeefe29ae8a5b2b6d3a318295fa9a1925bdc61",
"sha256": "sha256-j943uKx8GbgmwhbfKvPXEd9JRb/baLPx5qHD9KUng1w="
"guava-testlib-33.4.0-jre.jar": {
"sha1": "e849ea71846b5ca96387d543c7ac862f18fe2513",
"sha256": "sha256-JF+agJa8uth2DSga3QKf4klh/pOZ3f6as3srsLCe7WI="
}
}
},
@@ -12767,20 +12741,20 @@
},
{
"path": "com/google/guava/guava/33.3.1-jre",
"path": "com/google/guava/guava/33.4.0-jre",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"guava-33.3.1-jre.pom": {
"sha1": "5a5628b34628748abcb73144a909ddbea2265ed9",
"sha256": "sha256-MTtn/BPrOwY07acVoSKZcfXem4GIvCgHYoFbg6J18ZM="
"guava-33.4.0-jre.pom": {
"sha1": "5c671f0be7aa66b15ed0ec2e1afb4ee99c8c78e8",
"sha256": "sha256-+pTbQAIt38d1r57PsTDM5RW5b3QNr4LyCvhG2VBUE0s="
},
"guava-33.3.1-jre.jar": {
"sha1": "852f8b363da0111e819460021ca693cacca3e8db",
"sha256": "sha256-S/Dixa+ORSXJbo/eF6T3MH+X+EePEcTI41oOMpiuTpA="
"guava-33.4.0-jre.jar": {
"sha1": "03fcc0a259f724c7de54a6a55ea7e26d3d5c0cac",
"sha256": "sha256-uRjJin5E2+lOvZ/j5Azdqttak+anjrYAi0LfI3JB5Tg="
},
"guava-33.3.1-jre.module": {
"sha1": "c99293bbf166d01849489fc6f94a101f79072aa7",
"sha256": "sha256-QYWMhHU/2WprfFESL8zvOVWMkcwIJk4IUGvPIODmNzM="
"guava-33.4.0-jre.module": {
"sha1": "edaf59b47aef1afba209dfafe1481ed86ff07f04",
"sha256": "sha256-gg6BfobEk6p6/9bLuZHuYJJbbIt0VB90LLIgcPbyBFk="
}
}
},
@@ -12953,12 +12927,12 @@
},
{
"path": "com/google/protobuf/protobuf-bom/4.29.0",
"path": "com/google/protobuf/protobuf-bom/4.30.0-RC1",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"protobuf-bom-4.29.0.pom": {
"sha1": "5542df7ac3708da8bf30756f4ee6d804d04b8c9b",
"sha256": "sha256-3L/Apl6DmDAE0HFOmL8NIGBwEUVbYhk5cowAkmvIyZk="
"protobuf-bom-4.30.0-RC1.pom": {
"sha1": "da26e5286a508eb4c29c3294f34d2fb932916cce",
"sha256": "sha256-Gs5LZwG6EhofKfwcjoPgc9pKFKCgex2GkjeMwK+GDYM="
}
}
},
@@ -13204,16 +13178,16 @@
},
{
"path": "com/google/protobuf/protobuf-java/4.29.0",
"path": "com/google/protobuf/protobuf-java/4.30.0-RC1",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"protobuf-java-4.29.0.pom": {
"sha1": "27c4abe9c47f4aed31cf7acff86853a05f7e85f0",
"sha256": "sha256-7cHftEVG5yel+sU/CQUGv6aN2DB65vEHbv+tVy+S16Q="
"protobuf-java-4.30.0-RC1.pom": {
"sha1": "9b1a4031569c7b624e2e7e0d7bb12fd752f37e39",
"sha256": "sha256-3A0yPW415z4X6ICF0obJblY2xk8N8WcLCULsC8MYass="
},
"protobuf-java-4.29.0.jar": {
"sha1": "ffea90ab158435d68de76951857ef5f8b0f98365",
"sha256": "sha256-FpAYUevl6J/oiqrTwmhmNzaVvC4wYnu4kyhH4vX8LnY="
"protobuf-java-4.30.0-RC1.jar": {
"sha1": "fe024780d64bf9edeb16d33974fd06799758a9e5",
"sha256": "sha256-ywRRWEELxFcCkN+AagbwIbYvLB4m7X4dtlczp8PzXoU="
}
}
},
@@ -13333,12 +13307,12 @@
},
{
"path": "com/google/protobuf/protobuf-parent/4.29.0",
"path": "com/google/protobuf/protobuf-parent/4.30.0-RC1",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"protobuf-parent-4.29.0.pom": {
"sha1": "c35e0844930aea715cc8f4335fee49223b1fe35c",
"sha256": "sha256-jU6O2nh45revZuPCtvWHE839P7bNsHGS3My2HN0uSTA="
"protobuf-parent-4.30.0-RC1.pom": {
"sha1": "bfd3c39ebcb48274c73c80b82a2925b26a2aea80",
"sha256": "sha256-vBZlQhUkJXKX06FCuoKNYc5/hD86HDSonJ4agltkvxQ="
}
}
},
@@ -13652,20 +13626,20 @@
},
{
"path": "com/squareup/okio/okio-jvm/3.9.1",
"path": "com/squareup/okio/okio-jvm/3.10.2",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"okio-jvm-3.9.1.pom": {
"sha1": "32a1fc013a444bd5d7c45346973c102d02dbeb1e",
"sha256": "sha256-VXZInO8kBTNoAPxt6VhUU18zVxpO/lpa0OybmwkNIdU="
"okio-jvm-3.10.2.pom": {
"sha1": "bf3c29f09bf646535ba5a72206280bd5d9631d90",
"sha256": "sha256-AP000Iv0YxNiofVSLKpXyuKMosfpOS76My72Vs/anUM="
},
"okio-jvm-3.9.1.jar": {
"sha1": "79696e3f4ff05d41ba2b3dc68adb5808a2e5dc53",
"sha256": "sha256-/m/pE3j5v6ewjDhkgozkGABc8orMoS6IR+tlxWXDdQA="
"okio-jvm-3.10.2.jar": {
"sha1": "6a2cc3acd5a83e66075a2f6c5b0608b6043fa34c",
"sha256": "sha256-/Qp+dsZzHwDpILe8EcBdgjqTIEVDGt1Ujgld4CCmnt4="
},
"okio-jvm-3.9.1.module": {
"sha1": "470544322c29b112668d2018c05fd94107b05e26",
"sha256": "sha256-sK+pGSxC18Rj3jjWMlk2xpAdnjtxSXNf/RihHfMQNKs="
"okio-jvm-3.10.2.module": {
"sha1": "e1125ef565c7f5fd6fbf0f925188c81febe1d0ee",
"sha256": "sha256-p4CkJMVx4odVASiuADMjVibf/iFsuNs7ICRkmWrZaPA="
}
}
},
@@ -13758,20 +13732,20 @@
},
{
"path": "com/squareup/okio/okio/3.9.1",
"path": "com/squareup/okio/okio/3.10.2",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"okio-3.9.1.pom": {
"sha1": "1cd169f2bbe6215607a62447b9e4b64a125b5e56",
"sha256": "sha256-06DDd+epr8zbsCqrXgUNwhL/2pQNvUcOo5cSpDQt8I4="
"okio-3.10.2.pom": {
"sha1": "3d4a51279ef5bc796682828afd74ef3ab54ad543",
"sha256": "sha256-7lbAIUoPqfER2nExxVDo3ICvDL9WCVbBzNosZtdQa0E="
},
"okio-3.9.1.jar": {
"sha1": "3f1067fc8f61b89e013ea121198c49c991d74cbb",
"sha256": "sha256-mcjrvEmV8p3ViwXXVYP8yLlX2yHu64xdiXM/DTSVWJc="
"okio-3.10.2.jar": {
"sha1": "5117e7c820a89b5722cc37fc9152d54131d4cd36",
"sha256": "sha256-3iJdy5APSY2cdVGHpEx043V2vYk62pa4bbYRDiellRM="
},
"okio-3.9.1.module": {
"sha1": "eaa18fe21ddaef8640880e11315ef39757fbc75f",
"sha256": "sha256-m5C0J0pa1gLdV01tS0iQNmOy3ppgufw0AiSCk9hD4SE="
"okio-3.10.2.module": {
"sha1": "0e038c29ef477f04b63b0f29847815c52decf3be",
"sha256": "sha256-P94fn79yxsMm1eiktTL0/Z/aLdDLFEK8pODHl9FBI4c="
}
}
},
@@ -15259,16 +15233,16 @@
},
{
"path": "io/netty/netty-tcnative-classes/2.0.69.Final",
"path": "io/netty/netty-tcnative-classes/2.0.70.Final",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"netty-tcnative-classes-2.0.69.Final.pom": {
"sha1": "ee65d639d3c5312a4b6369f0d59504ad0c8dfe0b",
"sha256": "sha256-WdoPlJ4r7on1cR6z4Mb9jPeRTmtwwAfza56waIJDAVw="
"netty-tcnative-classes-2.0.70.Final.pom": {
"sha1": "c1e1a8fdfb72675d90886ad28a6949529f798b1a",
"sha256": "sha256-QRtGiEv20l66H2RTU5HsWxCH6Rjg5T0iTkIgoE+zJnU="
},
"netty-tcnative-classes-2.0.69.Final.jar": {
"sha1": "0f018f36b688bafabe4573af230a8b59cf3c0713",
"sha256": "sha256-C7woSMsJnrP29Ow2UBs2AOGChFfNpB1TMGh/YB7gS+8="
"netty-tcnative-classes-2.0.70.Final.jar": {
"sha1": "5f75accc769e69f2e6c1e56ba3a08b6c4bcc25f8",
"sha256": "sha256-p5wVeTE9StSKPswdAaJdoG0i1kScO8w2nCMYdJvPVbw="
}
}
},
@@ -15285,12 +15259,12 @@
},
{
"path": "io/netty/netty-tcnative-parent/2.0.69.Final",
"path": "io/netty/netty-tcnative-parent/2.0.70.Final",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"netty-tcnative-parent-2.0.69.Final.pom": {
"sha1": "ba3c1cc69d48f0196ee84ecd3c5157c52a987931",
"sha256": "sha256-KVMCcZZjdhDIlPMr2nBQk82HaDFcnxpXOVGnXxeGmvw="
"netty-tcnative-parent-2.0.70.Final.pom": {
"sha1": "b5573ffca02a2c092bdb0f7e5efb7da495523ea4",
"sha256": "sha256-gHWl5UlncBGgcXXZuNqWOli4hLMujhAbdeeBUhc/3DU="
}
}
},
@@ -15768,16 +15742,16 @@
},
{
"path": "net/bytebuddy/byte-buddy-agent/1.15.4",
"path": "net/bytebuddy/byte-buddy-agent/1.15.11",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"byte-buddy-agent-1.15.4.pom": {
"sha1": "9440b88e2dfa9fb64979d9150453e5535c45e7d3",
"sha256": "sha256-UJEHS07O1v8WbjlDlHRk/0Lac5Y4BC34aDuj6MIJ51U="
"byte-buddy-agent-1.15.11.pom": {
"sha1": "4015ca3530951c4336155d578de83f0e6b900f8c",
"sha256": "sha256-tfoTlvFHl7jYCIJ+d0O6il8gO0iJvjLklj1EvV7XWag="
},
"byte-buddy-agent-1.15.4.jar": {
"sha1": "58e850dde88f3cf20f41f659440bef33f6c4fe02",
"sha256": "sha256-HXbe/RWaVkuct6lo0N6ic2e4tw6951qWjn7xkhvHXuQ="
"byte-buddy-agent-1.15.11.jar": {
"sha1": "a38b16385e867f59a641330f0362ebe742788ed8",
"sha256": "sha256-MW0sB5XCpNTEdW8ub5NJg3x0MKw04Ed+rYdNBfXMGeU="
}
}
},
@@ -15804,17 +15778,6 @@
}
},
{
"path": "net/bytebuddy/byte-buddy-parent/1.15.4",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"byte-buddy-parent-1.15.4.pom": {
"sha1": "d8cc6c53d8cda7c416764d4e4a74183546e1e5a2",
"sha256": "sha256-lwLCIT4/23CHv9M0tjQ80F9EUIqT64+Uqn2mVKwftI8="
}
}
},
{
"path": "net/bytebuddy/byte-buddy-parent/1.15.10",
"repo": "https://repo.maven.apache.org/maven2",
@@ -15826,6 +15789,17 @@
}
},
{
"path": "net/bytebuddy/byte-buddy-parent/1.15.11",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"byte-buddy-parent-1.15.11.pom": {
"sha1": "80e27b5d9cf8ba40dfd0b8ee8f7915d7237aec6d",
"sha256": "sha256-jcUZ16PnkhEqfNhB6vvsTwDbxjPQha3SDEXwq0dspJY="
}
}
},
{
"path": "net/bytebuddy/byte-buddy/1.11.13",
"repo": "https://repo.maven.apache.org/maven2",
@@ -15856,21 +15830,6 @@
}
},
{
"path": "net/bytebuddy/byte-buddy/1.15.4",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"byte-buddy-1.15.4.pom": {
"sha1": "1bab74a93db543cc550920d0cd7d58d8734bda4d",
"sha256": "sha256-L7uet+1bcChb8EOXvnjcKoUdDcLnSGXYFJfGGomOIb0="
},
"byte-buddy-1.15.4.jar": {
"sha1": "e8bd42992701720649765383ff570f415190b83f",
"sha256": "sha256-Smg9g/8hnyDUTzXKMC2CHgxoQuP6j58e6RPrFvSc7Gw="
}
}
},
{
"path": "net/bytebuddy/byte-buddy/1.15.10",
"repo": "https://repo.maven.apache.org/maven2",
@@ -15886,6 +15845,21 @@
}
},
{
"path": "net/bytebuddy/byte-buddy/1.15.11",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"byte-buddy-1.15.11.pom": {
"sha1": "c625e547e59a502e99eba31c1a5b1c514e8c64f5",
"sha256": "sha256-IFuLJUGWcX6B2tZyu4aacZr8lt8pf5fYEe/+H0NlPa4="
},
"byte-buddy-1.15.11.jar": {
"sha1": "f61886478e0f9ee4c21d09574736f0ff45e0a46c",
"sha256": "sha256-+giZiq4ee9roO94HEsUOhETXHA4MGWuyJHrejUrQ65A="
}
}
},
{
"path": "net/java/dev/jna/jna-platform/5.6.0",
"repo": "https://repo.maven.apache.org/maven2",
@@ -16459,17 +16433,6 @@
}
},
{
"path": "org/apache/apache/32",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"apache-32.pom": {
"sha1": "2e08f841a6fbc946452701faaf5e39a17ac97ef3",
"sha256": "sha256-z9hywOwn9Trmj0PbwP7N7YrddzB5pTr705DkB7Qs5y8="
}
}
},
{
"path": "org/apache/apache/33",
"repo": "https://repo.maven.apache.org/maven2",
@@ -16703,28 +16666,6 @@
}
},
{
"path": "org/apache/commons/commons-parent/71",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"commons-parent-71.pom": {
"sha1": "91d35791f5037779fb320d054ed09cb14925dc32",
"sha256": "sha256-lbe+cPMWrkyiL2+90I3iGC6HzYdKZQ3nw9M4anR6gqM="
}
}
},
{
"path": "org/apache/commons/commons-parent/72",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"commons-parent-72.pom": {
"sha1": "cdfbaff82e756be4cb74d3f09b8fbf8f6b06c79d",
"sha256": "sha256-Q0Xev8dnsa6saKvdcvxn0YtSHUs5A3KhG2P/DFhrIyA="
}
}
},
{
"path": "org/apache/commons/commons-parent/78",
"repo": "https://repo.maven.apache.org/maven2",
@@ -16736,6 +16677,28 @@
}
},
{
"path": "org/apache/commons/commons-parent/79",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"commons-parent-79.pom": {
"sha1": "a2f13e0fa1f22588976e02c39b840653986ba685",
"sha256": "sha256-Yo3zAUis08SRz8trc8euS1mJ5VJqsTovQo3qXUrRDXo="
}
}
},
{
"path": "org/apache/commons/commons-parent/81",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"commons-parent-81.pom": {
"sha1": "4a342190fb243a221636ac0e0b02d3eed07a9908",
"sha256": "sha256-NI1OfBMb5hFMhUpxnOekQwenw5vTZghJd7JP0prQ7bQ="
}
}
},
{
"path": "org/apache/httpcomponents/httpclient/4.1.1",
"repo": "https://repo.maven.apache.org/maven2",
@@ -17471,20 +17434,20 @@
},
{
"path": "org/checkerframework/checker-qual/3.48.2",
"path": "org/checkerframework/checker-qual/3.49.0",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"checker-qual-3.48.2.pom": {
"sha1": "95acb8356a028eeb22171b7330f7bb78cf0f9506",
"sha256": "sha256-Sk+LlzEYgyx4SrUIpzAJ2tAt5Nq/0dFJLWa45MC6HRE="
"checker-qual-3.49.0.pom": {
"sha1": "d13e380978f7ccc023bfef031f9d664d52c3877e",
"sha256": "sha256-yEUftI7+1jgbMpFG1PrvtvTYq/E79XLCaosawoCW54A="
},
"checker-qual-3.48.2.jar": {
"sha1": "2a4797df796b23026b8215545b0d2936bacc52af",
"sha256": "sha256-ryPyDJlbL+3jWx66gRd6mmaUTsZ7jk3uGapMwLcQxrU="
"checker-qual-3.49.0.jar": {
"sha1": "54be36cb42c9b991c109e467e2bfa82af4cda44e",
"sha256": "sha256-i52aNuqvfA/CZQPIPNl9jJwPnikTzCpukqwmxzXU3L4="
},
"checker-qual-3.48.2.module": {
"sha1": "cdf30d8d4b97e12fc85e65e1bd55210a1a084b10",
"sha256": "sha256-D2RjlMmz5KL1aitsZ6C0NAkqxrq3pJvC/wGtjKf08NU="
"checker-qual-3.49.0.module": {
"sha1": "b6e4941f8d6af0d46a496f72d1b8944e0ed8fd5d",
"sha256": "sha256-YA0Z+9XjfemEh8OYBF4UCmUc9brRx5xcl88MyYRMQuQ="
}
}
},
@@ -19901,16 +19864,16 @@
},
{
"path": "org/jetbrains/kotlin/kotlin-reflect/2.1.0",
"path": "org/jetbrains/kotlin/kotlin-reflect/2.1.20-Beta2",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"kotlin-reflect-2.1.0.pom": {
"sha1": "6c72cb76dbf12249eec091d5a7d6ea27a27fcc87",
"sha256": "sha256-nNhLPU9xfJYjXkujtydolcz/1V3kEcfOCz97q1Yu/kI="
"kotlin-reflect-2.1.20-Beta2.pom": {
"sha1": "1f22f7def1b72d86f0122e94963556693dcd9054",
"sha256": "sha256-TSmv4VZwoDfXO+C2ohrnzhcbhR5hQPWOERDtz2j/Djo="
},
"kotlin-reflect-2.1.0.jar": {
"sha1": "dc3753b59b67d79b7d2f546d51e22a0ec1f082d8",
"sha256": "sha256-tfYI7fqYqM+iNyzBLRitqXS+HFbBCT7/BswGH0/AiLI="
"kotlin-reflect-2.1.20-Beta2.jar": {
"sha1": "9747e5ae1b3b47d83f7a810fbdd603c90cd5b0ad",
"sha256": "sha256-MsWsLXYMzUpOX6IMg2JMSqx4DN7upMURajx3sgHgs4E="
}
}
},
@@ -21551,33 +21514,6 @@
}
},
{
"path": "org/jetbrains/kotlin/kotlin-stdlib/1.9.25",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"kotlin-stdlib-1.9.25.pom": {
"sha1": "cd80842137758a376e09fd243351e10abb0fa13c",
"sha256": "sha256-fcMxikdte5AtDSevm1b2Y+jMQmOi0zrQUMertlpbZ6E="
},
"kotlin-stdlib-1.9.25-all.jar": {
"sha1": "6c69e7b26179394dafd3c90e630ef763b8c1a267",
"sha256": "sha256-zsOLwzAucqiq+c3kNrWpBx7gMx4q0F6E2LuJczTX6dQ="
},
"kotlin-stdlib-1.9.25-common.jar": {
"sha1": "4c7276a1529397ee6389483a408c57a9c6d4ca7d",
"sha256": "sha256-zBF0wWk6JC3Wy8gWpSfEU+BBhJTjbFDNxleL9AFxrNs="
},
"kotlin-stdlib-1.9.25.jar": {
"sha1": "f700a2f2b8f0d6d0fde48f56d894dc722fb029d7",
"sha256": "sha256-+c3Nv/H13oU4CuUml35oNybCqkLbHtbm5Qronklulf0="
},
"kotlin-stdlib-1.9.25.module": {
"sha1": "b36cccb05e35f533bf4a44ce7ba25dca4d356a2b",
"sha256": "sha256-Q+BqZsO3XKPkxcswSWjqg1ImJHuj9WExK9Gepi3oRqc="
}
}
},
{
"path": "org/jetbrains/kotlin/kotlin-stdlib/2.1.0",
"repo": "https://repo.maven.apache.org/maven2",
@@ -21601,6 +21537,29 @@
}
},
{
"path": "org/jetbrains/kotlin/kotlin-stdlib/2.1.20-Beta2",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"kotlin-stdlib-2.1.20-Beta2.pom": {
"sha1": "4f06272e28b5e94f321d4f5e999110a95d93ea05",
"sha256": "sha256-MO2EkKrOoo71CWbghKGSZ/P1JqCL87tkkKUHe7fIgn4="
},
"kotlin-stdlib-2.1.20-Beta2-all.jar": {
"sha1": "919751264e79f13e6ec79f4eef1d5222e6520d31",
"sha256": "sha256-geB9406Esp4U8/3vkC9LxM8dXalZuGffaD++HMVM4eE="
},
"kotlin-stdlib-2.1.20-Beta2.jar": {
"sha1": "98e06078584a3b816315d9baee83d04022f3271c",
"sha256": "sha256-dDREYP6u8bxGO6aeUtUOfbAEn3E4uECxzz+yKom8/Vk="
},
"kotlin-stdlib-2.1.20-Beta2.module": {
"sha1": "42afc0b12293bb6474c45f25c4da99ae6fb16103",
"sha256": "sha256-SnA8ORhx/5f6h1qdImY6A3werSKT5SZ+CDK7U2pMHrY="
}
}
},
{
"path": "org/jetbrains/kotlin/kotlin-tooling-core/1.8.0",
"repo": "https://repo.maven.apache.org/maven2",
@@ -21801,16 +21760,16 @@
},
{
"path": "org/json/json/20240303",
"path": "org/json/json/20250107",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"json-20240303.pom": {
"sha1": "78c0fa2a26888461731e26603ef4b19cc53239ca",
"sha256": "sha256-mUuvXYm/TKpc6WjkxOqxNcj5eD7PTSt7Dp0cik3fHk8="
"json-20250107.pom": {
"sha1": "91b62e77c1b5a92b6dc79f8c0c154311e1477c85",
"sha256": "sha256-ph+87jbeXMAb7ZMEYseLwEvBZBCwHZtfEABq8UoRvFY="
},
"json-20240303.jar": {
"sha1": "0ebb88e8fb5122b7506d5cf1d69f1ccdb790d22a",
"sha256": "sha256-PPbNaJLjLitMHDng9S9SSKL1s3ZG/fu3mma0a2GEFO0="
"json-20250107.jar": {
"sha1": "a1b5f0b5a1ce018b93a691c57ffc7d141b3a10f1",
"sha256": "sha256-hdTBqxktMRf9Asf/8ewP5jreRc9W3vf+lQ7wYM8G6Z8="
}
}
},
@@ -21850,16 +21809,16 @@
},
{
"path": "org/junit/junit-bom/5.11.3",
"path": "org/junit/junit-bom/5.12.0-RC2",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"junit-bom-5.11.3.pom": {
"sha1": "ff35120d66bcbdd05c8b94b57b08b9d30f15c343",
"sha256": "sha256-8T3y5Mrx/rzlZ2Z+fDeBAaAzHVPRMk1uLf467Psfd3Q="
"junit-bom-5.12.0-RC2.pom": {
"sha1": "f7f31a9a73bd214137a5e22c45da37aa8fa893a1",
"sha256": "sha256-9H9ulor+J1iVBQAkFvwfbn78IHHc1HMNSDTUUp/XY+E="
},
"junit-bom-5.11.3.module": {
"sha1": "cbe8d3c987c55bc57869f232f052c0df0042c32b",
"sha256": "sha256-S/D1PO6nx5D9+9JNujyeBM3FGGQnnuv8V6qkc+vKA4A="
"junit-bom-5.12.0-RC2.module": {
"sha1": "49aa327f7c618b489654a7ac980a5647adf95be4",
"sha256": "sha256-5S09WIMqPhrcc3ky5x9gjbj5lefgLRpDmNjdhKDH/2o="
}
}
},
@@ -21955,16 +21914,16 @@
},
{
"path": "org/mockito/mockito-core/5.14.2",
"path": "org/mockito/mockito-core/5.15.2",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"mockito-core-5.14.2.pom": {
"sha1": "2f6476e2e7da6a275ebac685ed983d3f7bee84e3",
"sha256": "sha256-Y45Wpo9JaHm8ig9Tz735xMw5pFtuQ4ozOl6oPAnunP0="
"mockito-core-5.15.2.pom": {
"sha1": "199967c4a9531dd9b29ed2ef72fc4b427192466c",
"sha256": "sha256-/tgS0qLznJhHSyoUxjxl+PRxeDtggtyGfAwbYFPGlIA="
},
"mockito-core-5.14.2.jar": {
"sha1": "f7bf936008d7664e2002c3faf0c02071c8d10e7c",
"sha256": "sha256-IpYUHB4fLhrjXAjTapq0Vj7NZuA1M/6CYwp2TnqkkYI="
"mockito-core-5.15.2.jar": {
"sha1": "87be4b1e0cc5febc07ab3197a8ff3ede56b37a79",
"sha256": "sha256-v0i3Ny2UkdXsiuu0zdGH0VZjkxWZwPvnQQFmzg4eWP8="
}
}
},
+2 -2
View File
@@ -807,7 +807,7 @@ org.glassfish.jaxb:txw2:2.2.11
org.glassfish.jaxb:txw2:2.3.1
org.glassfish.jaxb:txw2:2.3.2
org.gradle.toolchains:foojay-resolver:0.5.0
org.gradle:github-dependency-graph-gradle-plugin:1.3.1
org.gradle:github-dependency-graph-gradle-plugin:1.3.2
org.hamcrest:hamcrest-core:1.3
org.hamcrest:hamcrest:2.2
org.jacoco:org.jacoco.core:0.7.4.201502262128
@@ -1021,5 +1021,5 @@ xml-apis:xml-apis:1.4.01
org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin:0.5.0
org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.8.0
com.android.tools.build:gradle:3.5.4
com.facebook.react:hermes-android:0.73.5
com.android.tools.lint:lint-gradle:31.1.1
com.facebook.react:hermes-android:0.73.5
+26 -29
View File
@@ -552,7 +552,7 @@ https://jitpack.io/com/github/wix-playground/reflow-animator/1.0.6/reflow-animat
https://jitpack.io/com/github/yalantis/ucrop/2.2.6-native/ucrop-2.2.6-native.pom
https://jitpack.io/com/github/zacharee/AndroidPdfViewer/4.0.1/AndroidPdfViewer-4.0.1.pom
https://plugins.gradle.org/m2/com/adarshr/gradle-test-logger-plugin/2.0.0/gradle-test-logger-plugin-2.0.0.pom
https://plugins.gradle.org/m2/org/gradle/github-dependency-graph-gradle-plugin/1.3.1/github-dependency-graph-gradle-plugin-1.3.1.pom
https://plugins.gradle.org/m2/org/gradle/github-dependency-graph-gradle-plugin/1.3.2/github-dependency-graph-gradle-plugin-1.3.2.pom
https://plugins.gradle.org/m2/org/gradle/toolchains/foojay-resolver-convention/org.gradle.toolchains.foojay-resolver-convention.gradle.plugin/0.5.0/org.gradle.toolchains.foojay-resolver-convention.gradle.plugin-0.5.0.pom
https://plugins.gradle.org/m2/org/gradle/toolchains/foojay-resolver/0.5.0/foojay-resolver-0.5.0.pom
https://repo.maven.apache.org/maven2/antlr/antlr/2.7.1/antlr-2.7.1.pom
@@ -562,12 +562,12 @@ https://repo.maven.apache.org/maven2/commons-codec/commons-codec/1.6/commons-cod
https://repo.maven.apache.org/maven2/commons-codec/commons-codec/1.9/commons-codec-1.9.pom
https://repo.maven.apache.org/maven2/commons-codec/commons-codec/1.10/commons-codec-1.10.pom
https://repo.maven.apache.org/maven2/commons-codec/commons-codec/1.11/commons-codec-1.11.pom
https://repo.maven.apache.org/maven2/commons-codec/commons-codec/1.17.1/commons-codec-1.17.1.pom
https://repo.maven.apache.org/maven2/commons-codec/commons-codec/1.18.0/commons-codec-1.18.0.pom
https://repo.maven.apache.org/maven2/commons-io/commons-io/2.4/commons-io-2.4.pom
https://repo.maven.apache.org/maven2/commons-io/commons-io/2.18.0/commons-io-2.18.0.pom
https://repo.maven.apache.org/maven2/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.pom
https://repo.maven.apache.org/maven2/commons-logging/commons-logging/1.2/commons-logging-1.2.pom
https://repo.maven.apache.org/maven2/commons-logging/commons-logging/1.3.4/commons-logging-1.3.4.pom
https://repo.maven.apache.org/maven2/commons-logging/commons-logging/1.3.5/commons-logging-1.3.5.pom
https://repo.maven.apache.org/maven2/com/adobe/xmp/xmpcore/6.1.11/xmpcore-6.1.11.pom
https://repo.maven.apache.org/maven2/com/afollestad/material-dialogs/commons/0.9.6.0/commons-0.9.6.0.pom
https://repo.maven.apache.org/maven2/com/afollestad/material-dialogs/core/0.9.6.0/core-0.9.6.0.pom
@@ -726,7 +726,7 @@ https://repo.maven.apache.org/maven2/com/google/code/gson/gson-parent/2.8.5/gson
https://repo.maven.apache.org/maven2/com/google/code/gson/gson-parent/2.8.6/gson-parent-2.8.6.pom
https://repo.maven.apache.org/maven2/com/google/code/gson/gson-parent/2.8.9/gson-parent-2.8.9.pom
https://repo.maven.apache.org/maven2/com/google/code/gson/gson-parent/2.9.1/gson-parent-2.9.1.pom
https://repo.maven.apache.org/maven2/com/google/code/gson/gson-parent/2.11.0/gson-parent-2.11.0.pom
https://repo.maven.apache.org/maven2/com/google/code/gson/gson-parent/2.12.1/gson-parent-2.12.1.pom
https://repo.maven.apache.org/maven2/com/google/code/gson/gson/2.2.4/gson-2.2.4.pom
https://repo.maven.apache.org/maven2/com/google/code/gson/gson/2.3/gson-2.3.pom
https://repo.maven.apache.org/maven2/com/google/code/gson/gson/2.7/gson-2.7.pom
@@ -735,7 +735,7 @@ https://repo.maven.apache.org/maven2/com/google/code/gson/gson/2.8.5/gson-2.8.5.
https://repo.maven.apache.org/maven2/com/google/code/gson/gson/2.8.6/gson-2.8.6.pom
https://repo.maven.apache.org/maven2/com/google/code/gson/gson/2.8.9/gson-2.8.9.pom
https://repo.maven.apache.org/maven2/com/google/code/gson/gson/2.9.1/gson-2.9.1.pom
https://repo.maven.apache.org/maven2/com/google/code/gson/gson/2.11.0/gson-2.11.0.pom
https://repo.maven.apache.org/maven2/com/google/code/gson/gson/2.12.1/gson-2.12.1.pom
https://repo.maven.apache.org/maven2/com/google/crypto/tink/tink/1.3.0-rc2/tink-1.3.0-rc2.pom
https://repo.maven.apache.org/maven2/com/google/crypto/tink/tink/1.7.0/tink-1.7.0.pom
https://repo.maven.apache.org/maven2/com/google/dagger/dagger/2.28.3/dagger-2.28.3.pom
@@ -750,7 +750,6 @@ https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotatio
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.9.0/error_prone_annotations-2.9.0.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.10.0/error_prone_annotations-2.10.0.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.11.0/error_prone_annotations-2.11.0.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.27.0/error_prone_annotations-2.27.0.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.36.0/error_prone_annotations-2.36.0.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.0.18/error_prone_parent-2.0.18.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.2.0/error_prone_parent-2.2.0.pom
@@ -763,7 +762,6 @@ https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.9.0/error_prone_parent-2.9.0.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.10.0/error_prone_parent-2.10.0.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.11.0/error_prone_parent-2.11.0.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.27.0/error_prone_parent-2.27.0.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.36.0/error_prone_parent-2.36.0.pom
https://repo.maven.apache.org/maven2/com/google/flatbuffers/flatbuffers-java/1.12.0/flatbuffers-java-1.12.0.pom
https://repo.maven.apache.org/maven2/com/google/google/1/google-1.pom
@@ -781,8 +779,8 @@ https://repo.maven.apache.org/maven2/com/google/guava/guava-parent/30.1-jre/guav
https://repo.maven.apache.org/maven2/com/google/guava/guava-parent/31.0.1-android/guava-parent-31.0.1-android.pom
https://repo.maven.apache.org/maven2/com/google/guava/guava-parent/31.0.1-jre/guava-parent-31.0.1-jre.pom
https://repo.maven.apache.org/maven2/com/google/guava/guava-parent/31.1-jre/guava-parent-31.1-jre.pom
https://repo.maven.apache.org/maven2/com/google/guava/guava-parent/33.3.1-jre/guava-parent-33.3.1-jre.pom
https://repo.maven.apache.org/maven2/com/google/guava/guava-testlib/33.3.1-jre/guava-testlib-33.3.1-jre.pom
https://repo.maven.apache.org/maven2/com/google/guava/guava-parent/33.4.0-jre/guava-parent-33.4.0-jre.pom
https://repo.maven.apache.org/maven2/com/google/guava/guava-testlib/33.4.0-jre/guava-testlib-33.4.0-jre.pom
https://repo.maven.apache.org/maven2/com/google/guava/guava/17.0/guava-17.0.pom
https://repo.maven.apache.org/maven2/com/google/guava/guava/22.0/guava-22.0.pom
https://repo.maven.apache.org/maven2/com/google/guava/guava/23.0/guava-23.0.pom
@@ -795,7 +793,7 @@ https://repo.maven.apache.org/maven2/com/google/guava/guava/30.1-jre/guava-30.1-
https://repo.maven.apache.org/maven2/com/google/guava/guava/31.0.1-android/guava-31.0.1-android.pom
https://repo.maven.apache.org/maven2/com/google/guava/guava/31.0.1-jre/guava-31.0.1-jre.pom
https://repo.maven.apache.org/maven2/com/google/guava/guava/31.1-jre/guava-31.1-jre.pom
https://repo.maven.apache.org/maven2/com/google/guava/guava/33.3.1-jre/guava-33.3.1-jre.pom
https://repo.maven.apache.org/maven2/com/google/guava/guava/33.4.0-jre/guava-33.4.0-jre.pom
https://repo.maven.apache.org/maven2/com/google/guava/listenablefuture/1.0/listenablefuture-1.0.pom
https://repo.maven.apache.org/maven2/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.pom
https://repo.maven.apache.org/maven2/com/google/j2objc/j2objc-annotations/1.1/j2objc-annotations-1.1.pom
@@ -809,7 +807,7 @@ https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-bom/3.13.0/pro
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-bom/3.17.2/protobuf-bom-3.17.2.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-bom/3.19.2/protobuf-bom-3.19.2.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-bom/3.19.3/protobuf-bom-3.19.3.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-bom/4.29.0/protobuf-bom-4.29.0.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-bom/4.30.0-RC1/protobuf-bom-4.30.0-RC1.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-javalite/3.17.2/protobuf-javalite-3.17.2.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-javalite/3.19.2/protobuf-javalite-3.19.2.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-java-util/3.4.0/protobuf-java-util-3.4.0.pom
@@ -826,7 +824,7 @@ https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-java/3.13.0/pr
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-java/3.17.2/protobuf-java-3.17.2.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-java/3.19.2/protobuf-java-3.19.2.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-java/3.19.3/protobuf-java-3.19.3.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-java/4.29.0/protobuf-java-4.29.0.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-java/4.30.0-RC1/protobuf-java-4.30.0-RC1.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-lite/3.0.1/protobuf-lite-3.0.1.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-parent/3.0.0/protobuf-parent-3.0.0.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-parent/3.4.0/protobuf-parent-3.4.0.pom
@@ -837,7 +835,7 @@ https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-parent/3.13.0/
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-parent/3.17.2/protobuf-parent-3.17.2.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-parent/3.19.2/protobuf-parent-3.19.2.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-parent/3.19.3/protobuf-parent-3.19.3.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-parent/4.29.0/protobuf-parent-4.29.0.pom
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-parent/4.30.0-RC1/protobuf-parent-4.30.0-RC1.pom
https://repo.maven.apache.org/maven2/com/google/truth/truth-parent/1.4.4/truth-parent-1.4.4.pom
https://repo.maven.apache.org/maven2/com/google/truth/truth/1.4.4/truth-1.4.4.pom
https://repo.maven.apache.org/maven2/com/google/zxing/core/3.4.1/core-3.4.1.pom
@@ -858,13 +856,13 @@ https://repo.maven.apache.org/maven2/com/squareup/okhttp3/okhttp/4.11.0/okhttp-4
https://repo.maven.apache.org/maven2/com/squareup/okhttp3/parent/3.9.1/parent-3.9.1.pom
https://repo.maven.apache.org/maven2/com/squareup/okhttp3/parent/3.12.1/parent-3.12.1.pom
https://repo.maven.apache.org/maven2/com/squareup/okio/okio-jvm/3.2.0/okio-jvm-3.2.0.pom
https://repo.maven.apache.org/maven2/com/squareup/okio/okio-jvm/3.9.1/okio-jvm-3.9.1.pom
https://repo.maven.apache.org/maven2/com/squareup/okio/okio-jvm/3.10.2/okio-jvm-3.10.2.pom
https://repo.maven.apache.org/maven2/com/squareup/okio/okio-parent/1.17.4/okio-parent-1.17.4.pom
https://repo.maven.apache.org/maven2/com/squareup/okio/okio/1.17.4/okio-1.17.4.pom
https://repo.maven.apache.org/maven2/com/squareup/okio/okio/2.8.0/okio-2.8.0.pom
https://repo.maven.apache.org/maven2/com/squareup/okio/okio/2.9.0/okio-2.9.0.pom
https://repo.maven.apache.org/maven2/com/squareup/okio/okio/3.2.0/okio-3.2.0.pom
https://repo.maven.apache.org/maven2/com/squareup/okio/okio/3.9.1/okio-3.9.1.pom
https://repo.maven.apache.org/maven2/com/squareup/okio/okio/3.10.2/okio-3.10.2.pom
https://repo.maven.apache.org/maven2/com/sun/activation/all/1.2.0/all-1.2.0.pom
https://repo.maven.apache.org/maven2/com/sun/activation/all/1.2.1/all-1.2.1.pom
https://repo.maven.apache.org/maven2/com/sun/activation/all/1.2.2/all-1.2.2.pom
@@ -971,9 +969,9 @@ https://repo.maven.apache.org/maven2/io/netty/netty-resolver/4.1.52.Final/netty-
https://repo.maven.apache.org/maven2/io/netty/netty-resolver/4.1.72.Final/netty-resolver-4.1.72.Final.pom
https://repo.maven.apache.org/maven2/io/netty/netty-resolver/5.0.0.Alpha2/netty-resolver-5.0.0.Alpha2.pom
https://repo.maven.apache.org/maven2/io/netty/netty-tcnative-classes/2.0.46.Final/netty-tcnative-classes-2.0.46.Final.pom
https://repo.maven.apache.org/maven2/io/netty/netty-tcnative-classes/2.0.69.Final/netty-tcnative-classes-2.0.69.Final.pom
https://repo.maven.apache.org/maven2/io/netty/netty-tcnative-classes/2.0.70.Final/netty-tcnative-classes-2.0.70.Final.pom
https://repo.maven.apache.org/maven2/io/netty/netty-tcnative-parent/2.0.46.Final/netty-tcnative-parent-2.0.46.Final.pom
https://repo.maven.apache.org/maven2/io/netty/netty-tcnative-parent/2.0.69.Final/netty-tcnative-parent-2.0.69.Final.pom
https://repo.maven.apache.org/maven2/io/netty/netty-tcnative-parent/2.0.70.Final/netty-tcnative-parent-2.0.70.Final.pom
https://repo.maven.apache.org/maven2/io/netty/netty-transport/4.1.34.Final/netty-transport-4.1.34.Final.pom
https://repo.maven.apache.org/maven2/io/netty/netty-transport/4.1.52.Final/netty-transport-4.1.52.Final.pom
https://repo.maven.apache.org/maven2/io/netty/netty-transport/4.1.72.Final/netty-transport-4.1.72.Final.pom
@@ -1006,15 +1004,15 @@ https://repo.maven.apache.org/maven2/junit/junit/4.13.2/junit-4.13.2.pom
https://repo.maven.apache.org/maven2/me/zhanghai/android/materialprogressbar/library/1.4.2/library-1.4.2.pom
https://repo.maven.apache.org/maven2/net/bytebuddy/byte-buddy-agent/1.11.13/byte-buddy-agent-1.11.13.pom
https://repo.maven.apache.org/maven2/net/bytebuddy/byte-buddy-agent/1.11.19/byte-buddy-agent-1.11.19.pom
https://repo.maven.apache.org/maven2/net/bytebuddy/byte-buddy-agent/1.15.4/byte-buddy-agent-1.15.4.pom
https://repo.maven.apache.org/maven2/net/bytebuddy/byte-buddy-agent/1.15.11/byte-buddy-agent-1.15.11.pom
https://repo.maven.apache.org/maven2/net/bytebuddy/byte-buddy-parent/1.11.13/byte-buddy-parent-1.11.13.pom
https://repo.maven.apache.org/maven2/net/bytebuddy/byte-buddy-parent/1.11.19/byte-buddy-parent-1.11.19.pom
https://repo.maven.apache.org/maven2/net/bytebuddy/byte-buddy-parent/1.15.4/byte-buddy-parent-1.15.4.pom
https://repo.maven.apache.org/maven2/net/bytebuddy/byte-buddy-parent/1.15.10/byte-buddy-parent-1.15.10.pom
https://repo.maven.apache.org/maven2/net/bytebuddy/byte-buddy-parent/1.15.11/byte-buddy-parent-1.15.11.pom
https://repo.maven.apache.org/maven2/net/bytebuddy/byte-buddy/1.11.13/byte-buddy-1.11.13.pom
https://repo.maven.apache.org/maven2/net/bytebuddy/byte-buddy/1.11.19/byte-buddy-1.11.19.pom
https://repo.maven.apache.org/maven2/net/bytebuddy/byte-buddy/1.15.4/byte-buddy-1.15.4.pom
https://repo.maven.apache.org/maven2/net/bytebuddy/byte-buddy/1.15.10/byte-buddy-1.15.10.pom
https://repo.maven.apache.org/maven2/net/bytebuddy/byte-buddy/1.15.11/byte-buddy-1.15.11.pom
https://repo.maven.apache.org/maven2/net/java/dev/jna/jna-platform/5.6.0/jna-platform-5.6.0.pom
https://repo.maven.apache.org/maven2/net/java/dev/jna/jna/5.6.0/jna-5.6.0.pom
https://repo.maven.apache.org/maven2/net/java/jvnet-parent/1/jvnet-parent-1.pom
@@ -1058,7 +1056,6 @@ https://repo.maven.apache.org/maven2/org/apache/apache/16/apache-16.pom
https://repo.maven.apache.org/maven2/org/apache/apache/18/apache-18.pom
https://repo.maven.apache.org/maven2/org/apache/apache/21/apache-21.pom
https://repo.maven.apache.org/maven2/org/apache/apache/23/apache-23.pom
https://repo.maven.apache.org/maven2/org/apache/apache/32/apache-32.pom
https://repo.maven.apache.org/maven2/org/apache/apache/33/apache-33.pom
https://repo.maven.apache.org/maven2/org/apache/commons/commons-compress/1.8.1/commons-compress-1.8.1.pom
https://repo.maven.apache.org/maven2/org/apache/commons/commons-compress/1.12/commons-compress-1.12.pom
@@ -1078,9 +1075,9 @@ https://repo.maven.apache.org/maven2/org/apache/commons/commons-parent/39/common
https://repo.maven.apache.org/maven2/org/apache/commons/commons-parent/42/commons-parent-42.pom
https://repo.maven.apache.org/maven2/org/apache/commons/commons-parent/48/commons-parent-48.pom
https://repo.maven.apache.org/maven2/org/apache/commons/commons-parent/52/commons-parent-52.pom
https://repo.maven.apache.org/maven2/org/apache/commons/commons-parent/71/commons-parent-71.pom
https://repo.maven.apache.org/maven2/org/apache/commons/commons-parent/72/commons-parent-72.pom
https://repo.maven.apache.org/maven2/org/apache/commons/commons-parent/78/commons-parent-78.pom
https://repo.maven.apache.org/maven2/org/apache/commons/commons-parent/79/commons-parent-79.pom
https://repo.maven.apache.org/maven2/org/apache/commons/commons-parent/81/commons-parent-81.pom
https://repo.maven.apache.org/maven2/org/apache/httpcomponents/httpclient/4.1.1/httpclient-4.1.1.pom
https://repo.maven.apache.org/maven2/org/apache/httpcomponents/httpclient/4.2.6/httpclient-4.2.6.pom
https://repo.maven.apache.org/maven2/org/apache/httpcomponents/httpclient/4.5.2/httpclient-4.5.2.pom
@@ -1135,7 +1132,7 @@ https://repo.maven.apache.org/maven2/org/checkerframework/checker-qual/2.8.1/che
https://repo.maven.apache.org/maven2/org/checkerframework/checker-qual/2.11.1/checker-qual-2.11.1.pom
https://repo.maven.apache.org/maven2/org/checkerframework/checker-qual/3.5.0/checker-qual-3.5.0.pom
https://repo.maven.apache.org/maven2/org/checkerframework/checker-qual/3.12.0/checker-qual-3.12.0.pom
https://repo.maven.apache.org/maven2/org/checkerframework/checker-qual/3.48.2/checker-qual-3.48.2.pom
https://repo.maven.apache.org/maven2/org/checkerframework/checker-qual/3.49.0/checker-qual-3.49.0.pom
https://repo.maven.apache.org/maven2/org/codehaus/codehaus-parent/4/codehaus-parent-4.pom
https://repo.maven.apache.org/maven2/org/codehaus/groovy/groovy-xml/3.0.9/groovy-xml-3.0.9.pom
https://repo.maven.apache.org/maven2/org/codehaus/groovy/groovy-xml/3.0.10/groovy-xml-3.0.10.pom
@@ -1285,7 +1282,7 @@ https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.6.10/
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.6.20/kotlin-reflect-1.6.20.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.7.10/kotlin-reflect-1.7.10.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.8.20-RC2/kotlin-reflect-1.8.20-RC2.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/2.1.0/kotlin-reflect-2.1.0.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/2.1.20-Beta2/kotlin-reflect-2.1.20-Beta2.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-common/1.6.20/kotlin-scripting-common-1.6.20.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-common/1.8.0/kotlin-scripting-common-1.8.0.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-common/1.9.0/kotlin-scripting-common-1.9.0.pom
@@ -1394,8 +1391,8 @@ https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.8.21/k
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.8.22/kotlin-stdlib-1.8.22.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.9.0/kotlin-stdlib-1.9.0.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.9.25/kotlin-stdlib-1.9.25.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/2.1.20-Beta2/kotlin-stdlib-2.1.20-Beta2.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-tooling-core/1.8.0/kotlin-tooling-core-1.8.0.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-tooling-core/1.9.0/kotlin-tooling-core-1.9.0.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-tooling-metadata/1.6.20/kotlin-tooling-metadata-1.6.20.pom
@@ -1409,17 +1406,17 @@ https://repo.maven.apache.org/maven2/org/jetbrains/markdown-jvm/0.2.1/markdown-j
https://repo.maven.apache.org/maven2/org/jetbrains/markdown/0.2.1/markdown-0.2.1.pom
https://repo.maven.apache.org/maven2/org/jetbrains/trove4j/trove4j/20160824/trove4j-20160824.pom
https://repo.maven.apache.org/maven2/org/json/json/20180813/json-20180813.pom
https://repo.maven.apache.org/maven2/org/json/json/20240303/json-20240303.pom
https://repo.maven.apache.org/maven2/org/json/json/20250107/json-20250107.pom
https://repo.maven.apache.org/maven2/org/jsoup/jsoup/1.13.1/jsoup-1.13.1.pom
https://repo.maven.apache.org/maven2/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.pom
https://repo.maven.apache.org/maven2/org/junit/junit-bom/5.11.3/junit-bom-5.11.3.pom
https://repo.maven.apache.org/maven2/org/junit/junit-bom/5.12.0-RC2/junit-bom-5.12.0-RC2.pom
https://repo.maven.apache.org/maven2/org/jvnet/staxex/stax-ex/1.7.7/stax-ex-1.7.7.pom
https://repo.maven.apache.org/maven2/org/jvnet/staxex/stax-ex/1.8.1/stax-ex-1.8.1.pom
https://repo.maven.apache.org/maven2/org/jvnet/staxex/stax-ex/1.8/stax-ex-1.8.pom
https://repo.maven.apache.org/maven2/org/jvnet/staxex/stax-ex/2.1.0/stax-ex-2.1.0.pom
https://repo.maven.apache.org/maven2/org/mockito/mockito-core/3.12.4/mockito-core-3.12.4.pom
https://repo.maven.apache.org/maven2/org/mockito/mockito-core/4.0.0/mockito-core-4.0.0.pom
https://repo.maven.apache.org/maven2/org/mockito/mockito-core/5.14.2/mockito-core-5.14.2.pom
https://repo.maven.apache.org/maven2/org/mockito/mockito-core/5.15.2/mockito-core-5.15.2.pom
https://repo.maven.apache.org/maven2/org/mockito/mockito-inline/4.0.0/mockito-inline-4.0.0.pom
https://repo.maven.apache.org/maven2/org/objenesis/objenesis-parent/3.2/objenesis-parent-3.2.pom
https://repo.maven.apache.org/maven2/org/objenesis/objenesis-parent/3.3/objenesis-parent-3.3.pom
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
set -euo pipefail
# Fallback versions for each dependency
readonly FOOJAY_FALLBACK="0.5.0"
readonly KOTLIN_FALLBACK="1.8.0"
readonly TOOLS_FALLBACK="3.5.4"
readonly HERMES_FALLBACK="0.73.5"
readonly GRADLE_LINT_FALLBACK="31.1.1"
GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel)
cd "${GIT_ROOT}"
get_version() {
local file="$1"
local pattern="$2"
local fallback="$3"
local version
if [[ ! -f "$file" ]]; then
echo "$fallback"
return 0
fi
version=$(grep "$pattern" "$file" 2>/dev/null | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.*[0-9]*" | tail -n1 || echo "$fallback")
echo "$version"
}
# https://github.com/googlesamples/android-custom-lint-rules/blob/be672cd747ecf13844918e1afccadb935f856a72/docs/api-guide/example.md.html#L112-L129
get_gradle_lint_version() {
gradlePluginVersion=$(get_version "./nix/pkgs/aapt2/default.nix" "version =" $GRADLE_LINT_FALLBACK)
major=$(echo "$gradlePluginVersion" | grep -o "^[0-9]*")
# lintVersion = gradlePluginVersion + 23.0.0
major=$((major + 23))
minor=$(echo "$gradlePluginVersion" | grep -o "\.[0-9]*\." | grep -o "[0-9]*")
patch=$(echo "$gradlePluginVersion" | grep -o "[0-9]*$")
echo "$major.$minor.$patch"
}
foojay_version=$(get_version "./node_modules/@react-native/gradle-plugin/settings.gradle.kts" "foojay.*version" "$FOOJAY_FALLBACK")
kotlin_version=$(get_version "./node_modules/@react-native/gradle-plugin/gradle/libs.versions.toml" "kotlin =" "$KOTLIN_FALLBACK")
tools_version=$(get_version "./patches/BlurView-build.gradle.patch" "gradle:" "$TOOLS_FALLBACK")
hermes_version=$(get_version "./node_modules/react-native/ReactAndroid/gradle.properties" "VERSION_NAME" "$HERMES_FALLBACK")
lint_version=$(get_gradle_lint_version)
cat << EOF
org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin:$foojay_version
org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:$kotlin_version
com.android.tools.build:gradle:$tools_version
com.facebook.react:hermes-android:$hermes_version
com.android.tools.lint:lint-gradle:$lint_version
EOF
+2 -8
View File
@@ -38,16 +38,10 @@ function gen_deps_list() {
echo -e "${CLR}Found ${GRN}$(wc -l < "${DEPS_LIST}")${RST} direct dependencies..."
}
# FIXME: Temporary fix for missing packages.
# FIXME: Fix for missing packages.
# https://github.com/status-im/status-mobile/issues/15447
function add_deps_hack() {
echo -n \
'org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin:0.5.0
org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.8.0
com.android.tools.build:gradle:3.5.4
com.android.tools.lint:lint-gradle:31.1.1
com.facebook.react:hermes-android:0.73.5' \
>> "${DEPS_LIST}"
"${CUR_DIR}/deps_hack.sh" >> "${DEPS_LIST}"
}
# Find download URLs for each dependency.
+2 -2
View File
@@ -1,8 +1,8 @@
#!/usr/bin/env bash
# This script generates a list of dependencies for the main project and its
# sub-projects defined using Gradle config files. It parses Gradle output of
# 'dependencies' and 'buildEnvironment` tasks using AWK.
# sub-projects defined using Gradle config files. It uses github-dependency-graph-gradle-plugin:
# https://github.com/gradle/github-dependency-graph-gradle-plugin?tab=readme-ov-file#using-the-plugin-to-generate-dependency-reports
set -Eeuo pipefail
+1 -1
View File
@@ -71,7 +71,7 @@ in stdenv.mkDerivation rec {
};
};
buildInputs = with pkgs; [ nodejs openjdk ];
buildInputs = with pkgs; [ nodejs openjdk_headless ];
nativeBuildInputs = with pkgs; [ bash gradle unzip ]
++ lib.optionals stdenv.isDarwin [ file gnumake ];
+1 -1
View File
@@ -8,7 +8,7 @@ rec {
shell = mkShell {
buildInputs = with pkgs; [
openjdk
openjdk_headless
gradle
lsof # used in start-react-native.sh
flock # used in nix/scripts/node_modules.sh
+1 -1
View File
@@ -38,7 +38,7 @@ stdenv.mkDerivation {
];
};
};
buildInputs = with pkgs; [ clojure nodejs bash git openjdk];
buildInputs = with pkgs; [ clojure nodejs bash git openjdk_headless ];
phases = [
"unpackPhase" "secretsPhase" "patchPhase"
"configurePhase" "buildPhase" "installPhase"
+8 -19
View File
@@ -49,29 +49,18 @@ in {
nodejs = super.nodejs_20;
ruby = super.ruby_3_1;
yarn = super.yarn.override { nodejs = super.nodejs_20; };
openjdk = super.openjdk17_headless;
openjdk_headless = super.openjdk17_headless;
xcodeWrapper = callPackage ./pkgs/xcodeenv/compose-xcodewrapper.nix { } {
versions = ["15.1" "15.2" "15.3" "15.4"];
versions = ["16.0" "16.1" "16.2"];
};
go = super.go_1_21;
go = super.go_1_22;
clang = super.clang_15;
buildGoPackage = super.buildGo121Package;
buildGoModule = super.buildGo121Module;
gomobile = (super.gomobile.overrideAttrs (old: {
patches = [
(self.fetchurl { # https://github.com/golang/mobile/pull/84
url = "https://github.com/golang/mobile/commit/f20e966e05b8f7e06bed500fa0da81cf6ebca307.patch";
sha256 = "sha256-TZ/Yhe8gMRQUZFAs9G5/cf2b9QGtTHRSObBFD5Pbh7Y=";
})
(self.fetchurl { # https://github.com/golang/go/issues/58426
url = "https://github.com/golang/mobile/commit/406ed3a7b8e44dc32844953647b49696d8847d51.patch";
sha256 = "sha256-dqbYukHkQEw8npOkKykOAzMC3ot/Y4DEuh7fE+ptlr8=";
})
];
})).override {
# FIXME: No Android SDK packages for aarch64-darwin.
withAndroidPkgs = stdenv.system != "aarch64-darwin";
buildGoPackage = super.buildGo122Package;
buildGoModule = super.buildGo122Module;
gomobile = callPackage ./pkgs/gomobile {
#FIXME: No Android SDK packages for aarch64-darwin.
androidPkgs = self.androidEnvCustom.compose;
withAndroidPkgs = stdenv.system != "aarch64-darwin";
};
# Android environment
+1
View File
@@ -10,6 +10,7 @@ let
pname = "aapt2";
# Warning: This must be the same as gradlePluginVersion android/gradle.properties
# also referenced in nix/deps/gradle/deps_hack.sh
version = "8.1.1-10154469";
pkgPath = "com/android/tools/build/aapt2";
+2 -2
View File
@@ -1,8 +1,8 @@
{ mkShell, openjdk, androidPkgs }:
{ mkShell, openjdk_headless, androidPkgs }:
mkShell {
name = "android-sdk-shell";
buildInputs = [ openjdk ];
buildInputs = [ openjdk_headless ];
shellHook = ''
export ANDROID_HOME="${androidPkgs.sdk}"
+85
View File
@@ -0,0 +1,85 @@
{
stdenv,
lib,
fetchgit,
fetchurl,
buildGo122Module,
zlib,
makeWrapper,
xcodeenv,
androidenv,
xcodeWrapperArgs ? { },
xcodeWrapper ? xcodeenv.composeXcodeWrapper xcodeWrapperArgs,
withAndroidPkgs ? stdenv.system != "aarch64-darwin",
androidPkgs ? (
androidenv.composeAndroidPackages {
includeNDK = true;
}
),
}:
buildGo122Module {
pname = "gomobile";
version = "0-unstable-2024-12-13";
src = fetchgit {
name = "gomobile";
url = "https://go.googlesource.com/mobile";
rev = "a87c1cf6cf463f0d4476cfe0fcf67c2953d76e7c";
hash = "sha256-7j4rdmCZMC8tn4vAsC9x/mMNkom/+Tl7uAY+5gkSvfY=";
};
vendorHash = "sha256-6ycxEDEE0/i6Lxo0gb8wq3U2U7Q49AJj+PdzSl57wwI=";
CGO_ENABLED = "1";
subPackages = [
"bind"
"cmd/gobind"
"cmd/gomobile"
];
# Fails with: go: cannot find GOROOT directory
doCheck = false;
nativeBuildInputs = [ makeWrapper ] ++ lib.optionals stdenv.hostPlatform.isDarwin [ xcodeWrapper ];
# Prevent a non-deterministic temporary directory from polluting the resulting object files
postPatch = ''
substituteInPlace cmd/gomobile/env.go --replace-quiet \
'tmpdir, err = ioutil.TempDir("", "gomobile-work-")' \
'tmpdir = filepath.Join(os.Getenv("NIX_BUILD_TOP"), "gomobile-work")'
substituteInPlace cmd/gomobile/init.go --replace-quiet \
'tmpdir, err = ioutil.TempDir(gomobilepath, "work-")' \
'tmpdir = filepath.Join(os.Getenv("NIX_BUILD_TOP"), "work")'
# To fix unable to import bind: no Go package in golang.org/x/mobile/bind
substituteInPlace cmd/gomobile/init.go --replace \
'golang.org/x/mobile/cmd/gobind@latest' \
'golang.org/x/mobile/cmd/gobind'
'';
# Necessary for GOPATH when using gomobile.
postInstall = ''
mkdir -p $out/src/golang.org/x
ln -s $src $out/src/golang.org/x/mobile
'';
postFixup = ''
for prog in gomobile gobind; do
wrapProgram $out/bin/$prog \
--suffix GOPATH : $out \
--prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath [ zlib ]}" \
${lib.optionalString withAndroidPkgs ''
--prefix PATH : "${androidPkgs.androidsdk}/bin" \
--set-default ANDROID_HOME "${androidPkgs.androidsdk}/libexec/android-sdk"
''}
done
'';
meta = {
description = "Tool for building and running mobile apps written in Go";
homepage = "https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile";
license = with lib.licenses; [ bsd3 ];
maintainers = with lib.maintainers; [ jakubgs ];
};
}
+2 -2
View File
@@ -38,8 +38,8 @@ nix_install_type() {
"${UID}") echo "single";;
"(${UID})") echo "single";;
"") echo "none";
echo "No Nix installtion detected!" >&2;;
*) echo "Unknown Nix installtion type!" >&2; exit 1;;
echo "No Nix installation detected!" >&2;;
*) echo "Unknown Nix installation type!" >&2; exit 1;;
esac
fi
}
+3 -3
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
export NIX_VERSION="2.19.3"
export NIX_PACKAGE="nixVersions.nix_2_19"
export NIX_VERSION="2.24.11"
export NIX_PACKAGE="nixVersions.nix_2_24"
export NIX_INSTALL_URL="https://nixos.org/releases/nix/nix-${NIX_VERSION}/install"
export NIX_INSTALL_SHA256="73d47b0ab783fddca1b2d44a03d52a74c97c28a1fc8ff5a29419079302ef9c3d"
export NIX_INSTALL_SHA256="5c610a0e97c8a2dd48c1a630608502e43fdecd7d2572463dd4166126106d9188"
export NIX_INSTALL_PATH="/tmp/nix-install-${NIX_VERSION}"
+3
View File
@@ -38,6 +38,9 @@ in mkShell {
LANG="en_US.UTF-8";
LANGUAGE="en_US.UTF-8";
# Important to load our own Nix binary cache.
NIX_CONFIG = builtins.readFile ./nix.conf;
# just a nicety for easy access to node scripts
shellHook = ''
export STATUS_MOBILE_HOME=$(git rev-parse --show-toplevel)
+2 -2
View File
@@ -19,7 +19,7 @@ let
# for calling clojure targets in CI or Makefile
clojure = mkShell {
buildInputs = with pkgs; [
clojure flock maven openjdk
clojure flock maven openjdk_headless
# lint specific utilities
babashka clj-kondo clojure-lsp ripgrep zprint
];
@@ -61,7 +61,7 @@ let
# for 'scripts/generate-keystore.sh'
keytool = mkShell {
buildInputs = with pkgs; [ openjdk apksigner ];
buildInputs = with pkgs; [ openjdk_headless apksigner ];
};
# for targets needing 'adb', 'apkanalyzer' and other SDK/NDK tools
+10 -4
View File
@@ -1,5 +1,5 @@
{ callPackage, lib, buildGoPackage
, androidPkgs, openjdk, gomobile, xcodeWrapper, removeReferencesTo
{ callPackage, lib, buildGoPackage, pkgs
, androidPkgs, openjdk_headless, gomobile, xcodeWrapper, removeReferencesTo
, go-bindata, mockgen, protobuf3_20, protoc-gen-go
, meta
, source
@@ -15,6 +15,8 @@ let
isIOS = platform == "ios";
isAndroid = platform == "android";
enforceXCodeAvailable = callPackage ./enforceXCodeAvailable.nix { };
# Fixes fatal: not a git repository (or any of the parent directories): .git
fakeGit = pkgs.writeScriptBin "git" "echo ${source.version}";
in buildGoPackage rec {
pname = source.repo;
@@ -29,8 +31,8 @@ in buildGoPackage rec {
extraSrcPaths = [ gomobile ];
nativeBuildInputs = [
gomobile removeReferencesTo go-bindata mockgen protoc-gen-go protobuf3_20
] ++ optional isAndroid openjdk
gomobile removeReferencesTo go-bindata mockgen protoc-gen-go protobuf3_20 fakeGit
] ++ optional isAndroid openjdk_headless
++ optional isIOS xcodeWrapper;
ldflags = goBuildLdFlags;
@@ -46,6 +48,10 @@ in buildGoPackage rec {
# TODO: try removing when go is upgraded to 1.22
GODEBUG = "netdns=cgo+2";
# Sentry for status-go
SENTRY_CONTEXT_NAME = "status-mobile";
SENTRY_CONTEXT_VERSION = version;
preBuild = ''
echo 'Generate static files'
pushd go/src/$goPackagePath
+2 -3
View File
@@ -13,8 +13,8 @@ let
repo = "status-go";
rev = "unknown";
shortRev = rev;
rawVersion = "develop";
cleanVersion = rawVersion;
version = "unknown";
cleanVersion = version;
goPackagePath = "github.com/${owner}/${repo}";
# We use builtins.path so that we can name the resulting derivation,
# Normally the name would not be deterministic, taken from the checkout directory.
@@ -43,7 +43,6 @@ let
inherit (versionJSON) owner repo version;
rev = versionJSON.commit-sha1;
shortRev = strings.substring 0 7 rev;
rawVersion = versionJSON.version;
cleanVersion = lib.sanitizeVersion versionJSON.version;
# Need to pretend this is from status-im to let Go build it.
goPackagePath = "github.com/status-im/${repo}";
+4 -4
View File
@@ -20,8 +20,8 @@
"@react-native-community/netinfo": "^4.4.0",
"@react-native-community/push-notification-ios": "^1.4.1",
"@react-native-community/slider": "^3.0.0",
"@walletconnect/react-native-compat": "2.11.2",
"@walletconnect/web3wallet": "1.10.2",
"@walletconnect/react-native-compat": "2.17.3",
"@reown/walletkit": "^1.0.0",
"base-64": "^1.0.0",
"big-integer": "1.6.51",
"bignumber.js": "git+https://github.com/status-im/bignumber.js.git#refs/tags/v4.0.2-status",
@@ -51,7 +51,7 @@
"react-native-get-random-values": "^1.11.0",
"react-native-gifted-charts": "^1.3.2",
"react-native-hole-view": "^3.0.0-alpha4",
"react-native-image-crop-picker": "0.40.0",
"react-native-image-crop-picker": "0.41.2",
"react-native-image-resizer": "^1.2.3",
"react-native-keychain": "8.1.2",
"react-native-linear-gradient": "3.0.0-alpha.1",
@@ -66,7 +66,7 @@
"react-native-shake": "^3.3.1",
"react-native-share": "10.0.2",
"react-native-static-safe-area-insets": "^2.2.0",
"react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#refs/tags/v2.6.4",
"react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#refs/tags/v2.6.5",
"react-native-svg": "13.10.0",
"react-native-webview": "13.6.3",
"react-syntax-highlighter": "^15.5.0"
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

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