1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-02 15:53:04 +02:00

Compare commits

...

123 Commits

Author SHA1 Message Date
m2049r
41a7b7ba61 bump version 2025-06-17 19:27:03 +02:00
XD22
d739d8049e Fix node port input field (#943) 2025-06-15 22:48:39 +02:00
Lucas
91384b827a Weblate (#996)
* Update translation files

Updated by "Cleanup translation files" add-on in Weblate.

Translation: monerujo/App - Main
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-main/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 88.4% (329 of 372 strings)

Translation: monerujo/App - Main
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-main/pt_BR/

* Update translation files

Updated by "Cleanup translation files" add-on in Weblate.

Translation: monerujo/App - Help
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-help/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 75.0% (15 of 20 strings)

Translation: monerujo/App - Help
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-help/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (5 of 5 strings)

Translation: monerujo/App - About
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-about/pt_BR/

* Translated using Weblate (Spanish)

Currently translated at 95.1% (354 of 372 strings)

Translation: monerujo/App - Main
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-main/es/

* Translated using Weblate (Spanish)

Currently translated at 80.0% (4 of 5 strings)

Translation: monerujo/App - About
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-about/es/

* Translated using Weblate (Spanish)

Currently translated at 95.0% (19 of 20 strings)

Translation: monerujo/App - Help
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-help/es/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (5 of 5 strings)

Translation: monerujo/App - About
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-about/pt_BR/

* Translated using Weblate (French)

Currently translated at 100.0% (372 of 372 strings)

Translation: monerujo/App - Main
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-main/fr/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (372 of 372 strings)

Translation: monerujo/App - Main
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-main/tr/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (372 of 372 strings)

Translation: monerujo/App - Main
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-main/tr/

* Added translation using Weblate (Greek)

* Added translation using Weblate (Serbian)

* Added translation using Weblate (Estonian)

* Added translation using Weblate (Serbian)

* Translated using Weblate (French)

Currently translated at 100.0% (5 of 5 strings)

Translation: monerujo/App - About
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-about/fr/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (20 of 20 strings)

Translation: monerujo/App - Help
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-help/uk/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (372 of 372 strings)

Translation: monerujo/App - Main
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-main/uk/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (20 of 20 strings)

Translation: monerujo/App - Help
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-help/uk/

* Translated using Weblate (Tamil)

Currently translated at 100.0% (20 of 20 strings)

Translation: monerujo/App - Help
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-help/ta/

* Translated using Weblate (Tamil)

Currently translated at 100.0% (372 of 372 strings)

Translation: monerujo/App - Main
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-main/ta/

* Translated using Weblate (Tamil)

Currently translated at 100.0% (20 of 20 strings)

Translation: monerujo/App - Help
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-help/ta/

* Translated using Weblate (French)

Currently translated at 100.0% (20 of 20 strings)

Translation: monerujo/App - Help
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-help/fr/

* Address build errors

We can remove translations since Weblate will just tag as them 'previously translated', and they're broken so what can we do

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (20 of 20 strings)

Translation: monerujo/App - Help
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-help/uk/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (20 of 20 strings)

Translation: monerujo/App - Help
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-help/uk/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (20 of 20 strings)

Translation: monerujo/App - Help
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-help/uk/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (372 of 372 strings)

Translation: monerujo/App - Main
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-main/pt_BR/

* Translated using Weblate (Russian)

Currently translated at 94.8% (353 of 372 strings)

Translation: monerujo/App - Main
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-main/ru/

* Translated using Weblate (Russian)

Currently translated at 100.0% (372 of 372 strings)

Translation: monerujo/App - Main
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-main/ru/

* Added translation using Weblate (Czech)

* Added translation using Weblate (Czech)

* Added translation using Weblate (Czech)

* Translated using Weblate (Czech)

Currently translated at 80.0% (4 of 5 strings)

Translation: monerujo/App - About
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-about/cs/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (372 of 372 strings)

Translation: monerujo/App - Main
Translate-URL: https://hosted.weblate.org/projects/monerujo/app-main/pt_BR/

* Translated using Weblate (French)

Currently translated at 100.0% (20 of 20 strings)

Translation: monerujo/Help page
Translate-URL: https://hosted.weblate.org/projects/monerujo/app/app-help/fr/

* Translated using Weblate (German)

Currently translated at 80.0% (16 of 20 strings)

Translation: monerujo/Help page
Translate-URL: https://hosted.weblate.org/projects/monerujo/app/app-help/de/

* Translated using Weblate (German)

Currently translated at 97.3% (362 of 372 strings)

Translation: monerujo/Main strings
Translate-URL: https://hosted.weblate.org/projects/monerujo/app/app-main/de/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (20 of 20 strings)

Translation: monerujo/Help page
Translate-URL: https://hosted.weblate.org/projects/monerujo/app/app-help/pt_BR/

---------

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: John Doe <thraex@numericable.fr>
Co-authored-by: Languages add-on <noreply-addon-languages@weblate.org>
Co-authored-by: Максим Горпиніч <maksimgorpinic2005a@gmail.com>
Co-authored-by: தமிழ்நேரம் <anishprabu.t@gmail.com>
Co-authored-by: Yurt Page <yurtpage@gmail.com>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: tetic frisbook <tetic90548@frisbook.com>
2025-06-15 22:46:11 +02:00
m2049r
386f6e744b keep barcode data if unchanged (#1003) 2025-06-15 22:44:52 +02:00
m2049r
1f5ad86b07 update dependencies & gradle (#1002) 2025-06-15 22:42:57 +02:00
m2049r
ef3a19b66e upgrade dependecies; bump version 2025-02-06 08:19:18 +01:00
m2049r
9fd382e622 fix build errors 2025-02-06 08:09:13 +01:00
Lucas
f7c1cc9713 Update Swedish translation (#985)
Co-authored-by: ordtrogen <ordtrogen@users.noreply.github.com>
2025-01-20 18:38:18 +01:00
Pavel Haluza
30023e85bb Add monochrome launcher icon (#962) 2025-01-20 18:34:49 +01:00
m2049r
bf8780c874 update exolix api (#994) 2025-01-20 18:34:05 +01:00
m2049r
9379269f89 fix toolbar under notification bar (#995) 2025-01-20 18:26:44 +01:00
boldsuck
a9b4abc01f Update DefaultNodes.java (#987)
Change incorrect P2P port to RPC port.

Just created a gist for my nodes:
https://gist.github.com/boldsuck/0eee41138b724715b669f4c4961fb9ca
2025-01-20 18:25:53 +01:00
retrnull
899ac775b1 make node parsing and formatting ipv6-friendly (#939)
Co-authored-by: m2049r <m2049r@monerujo.io>
2024-12-10 09:57:46 +01:00
Lucas
8b7c5a2450 Update Turkish and French translations from cardpuncher (#984)
* Update French & Turkish translations

Mainly for the Sidekick part, also added a few missing strings to the French translation and reverted to use the original "Ledger" word in the Turkish one.

* Fix a dumb copy & paste error

Hopefully this will silence the error.

* Yet another copy & paste error... :/

* Complete Turkish translation

* Update French translations

and fix a few typos in the process.

* Squash some syntax errors, there are others

* Error hunting goes on

Hopefully there aren't many left.

* Still on it

* All errors should have been cleared now

Crossing fingers, click & pray.

* Update help.xml

* Add files via upload

Let's see if this resolves the conflicts.

* Add files via upload

Forgot to remove the  translatable="false" strings without which my diff tool freaked out, and fixed a few typos. Let's hope this will resolve the conflicts.

* Acutally remove the  translatable="false" strings

Forgot to save the file... :/

* Update strings.xml

Remove duplicate string.

* fix building ?

---------

Co-authored-by: cardpuncher <64688741+cardpuncher@users.noreply.github.com>
2024-12-08 20:14:29 +01:00
m2049r
05c1ca5082 fix toolbar under notification bar (#983) 2024-12-07 12:52:11 +01:00
Lucas
6abf11841e Fix translatable state of some strings (#978) 2024-12-07 10:44:32 +01:00
Lucas
d46b8bf79a Remove translations with duplicate English strings (#980)
Linter no longer complains about this, this causes confusion on how much of monerujo is translated, this breaks translation language fallback (e.g. get translations from pt if pt-br doesn't have them)

Includes mixed strings unfortunately, mostly found in the help strings, where there were a few that have english in the middle, unfortunately... having them would mean they would likely be skipped over, there's no way I know to fix this, a lot of these though need overall maintenance, and it's better to make the job easier for the translators than to keep a potentially confusing/breaking translation
2024-12-07 10:41:03 +01:00
Lucas
149fd6376e Update credits in translation files (#981)
Quick find and replace
2024-12-07 10:37:19 +01:00
XD22
de70d64eb8 Add Arabic Translation ! (#925)
* Add Arabic Translation

* improve some lines & fix out of context words

* Improves and fixes

* Fix wrong context

* Fix wrong typo in _en

* Fixes

* fix legal name "monerujo"

* Fix moneroj coins context

Dropped 90a0654a9c and changed to "moneroj" as anhdres  comment.

* Drop english Lowercase changes
2024-12-03 08:36:15 +01:00
Toyo
232f7b801e change some formatting and typos? (#970) 2024-12-03 08:33:29 +01:00
Lucas
8962bd3050 Minimum weblate requirements (#976)
* Remove tools:ignore flags

* Disable linting for MissingTranslation

* Point README translation to Weblate
2024-12-03 08:31:20 +01:00
m2049r
989d52b33d Option to remember and reuse selected fiat (#967) 2024-11-08 12:01:35 +01:00
m2049r
a3c0ca7ebe bump version 2024-11-05 08:46:32 +01:00
m2049r
c9132d7d97 more permission checks 2024-11-05 08:42:14 +01:00
m2049r
758b042680 fix some crashing if bluetooth pemissions are not given (#965)
* show error instead of crashing

* ignore if fragement is no longer

* bump version
2024-11-03 16:19:35 +01:00
m2049r
84ce392192 Use EXOLIX as exchange (#964) 2024-11-01 19:51:28 +01:00
m2049r
4ebcda2b14 bump version 2024-10-17 13:39:16 +02:00
m2049r
c49351a8a9 own orbothelper & fix receiver visibility (#961) 2024-10-17 13:01:12 +02:00
m2049r
41e84f2e29 fix receiver visibility 2024-10-17 12:42:26 +02:00
m2049r
f08ddf93c8 bump version 2024-10-16 21:31:09 +02:00
m2049r
4bfc9c1bdd fix RECEIVER_NOT_EXPORTED crash 2024-10-16 20:18:12 +02:00
m2049r
1a13bdde91 add some default nodes 2024-10-07 23:42:23 +02:00
m2049r
3e56d5a54b update default nodes (#959) 2024-09-24 09:00:22 +02:00
m2049r
c64fb1a769 Merge pull request #954 from m2049r/feature/show_connected
show when sidekick is connected
2024-09-19 22:44:00 +02:00
m2049r
2d2a7d2db0 show when sidekick is connected 2024-09-15 20:46:00 +02:00
m2049r
ac9e24ee9f Merge pull request #952 from m2049r/feature/sidekick_v
Support for Sidekick device
2024-09-06 00:45:48 +02:00
m2049r
059438a09e targetSdkVersion 35 2024-09-05 23:13:11 +02:00
m2049r
d22fdfe2dc Sidekick Support for Monerokon 2024-09-05 23:12:42 +02:00
m2049r
48577e46aa lock in background (#936) 2024-04-05 16:03:30 +02:00
m2049r
451371cd92 refactor onBackPressed to use the callback dispatcher (#937) 2024-04-05 15:57:37 +02:00
m2049r
cd6f646b63 bump version 2024-03-17 14:10:14 +01:00
m2049r
aa530caa28 improve german transaltion. remove permissions translations 2024-03-17 14:04:49 +01:00
m2049r
5df43f1274 fix translation errors 2024-03-17 13:38:36 +01:00
m2049r
3b0cb3ffdb upgrade gradle 2024-03-17 13:28:22 +01:00
m2049r
300551dbe4 update zlib to 1.3.1 2024-03-17 13:06:05 +01:00
m2049r
942519adb7 ignore monero native stuff 2024-03-17 12:53:51 +01:00
m2049r
0652a8ba14 upgrade dnsjava (#924) 2024-01-13 17:33:23 +01:00
m2049r
04d0bd2ffb fix tests 2024-01-13 16:14:05 +01:00
m2049r
8473e66c69 upgrade dependencies 2024-01-13 15:58:00 +01:00
m2049r
2218ff615c fix unescaped quotes 2024-01-13 15:41:23 +01:00
m2049r
54b55b9f8f optimize build time 2024-01-13 15:33:21 +01:00
m2049r
5abf84f62b optimize build time 2024-01-13 15:31:14 +01:00
m2049r
6ff75d221f add RSD (#923) 2024-01-13 14:28:30 +01:00
audioz
c90f107c3c Hebrew Translation (#895) 2024-01-13 14:13:56 +01:00
Digitale Souveränität - JETZT!
a3db18032e add ds-jetzt node (#905)
add ds-jetzt node as clear & onion
2024-01-13 14:13:18 +01:00
anhdres
9db2c10679 updated texts for eng and spa (#911)
Co-authored-by: m2049r <m2049r@monerujo.io>
2024-01-13 14:11:41 +01:00
XD22
eab85c7f0a Fix Docker libs Builds (#918)
Co-authored-by: m2049r <m2049r@monerujo.io>
2024-01-13 14:08:02 +01:00
kuznetsovgm
6bf3229e77 fixed a translation error (#912) 2024-01-13 14:05:52 +01:00
audioz
9f4f626acb Docker build fix. (#896)
* Hebrew Translation

* ~~ https://zlib.net/
Version 1.2.13 has these key updates from 1.2.12:

    Fix a bug when getting a gzip header extra field with inflateGetHeader(). This remedies CVE-2022-37434.
    Fix a bug in block type selection when Z_FIXED used. Now the smallest block type is selected, for better compression.
    Fix a configure issue that discarded the provided CC definition.
    Correct incorrect inputs provided to the CRC functions. This mitigates a bug in Java.
    Repair prototypes and exporting of the new CRC functions.
    Fix inflateBack to detect invalid input with distances too far.

Due to the first bug fix, any installations of 1.2.12 or earlier should be replaced with 1.2.13.

* Delete app/src/main/res/values-he directory
2024-01-13 14:05:22 +01:00
XD22
434dab55ba Fix TalkBack Screen reader (#917)
* Fixing TalkBack p1

* Fix TalkBack español translate p2

* Fix TalkBack français translate p3

* Escape some apostrophe, Fix wrong Locale codes
2024-01-13 14:03:31 +01:00
m2049r
59cc6b1864 upgrade dependencies 2024-01-13 13:59:38 +01:00
m2049r
a6e9d0e77c use yadio for pricing "exotic" fiat (#921)
* yadio api

* add more currencies

* fix es typo

* bump version & fix cicleci build
2023-12-10 14:24:35 +01:00
m2049r
17df7c3faf Pocket Change V2 (#914)
* show PC on btc confirm
* random slots to refill
2023-08-28 19:24:45 +02:00
m2049r
bf1829f775 PocketChange (#901) 2023-06-05 05:03:51 +02:00
m2049r
bc4aa0f772 Feature v0.18.2.2 (#900)
* add Ledger Stax

* update block heights

* reestimate restore height only for mainnet

* upgrade to gradle 8.0.1

* upgrade dependencies
2023-05-29 17:01:07 +02:00
m2049r
3f09e73df7 Merge pull request #898 from m2049r/feature_tweaks
UI tweaks
2023-05-03 23:13:21 +02:00
m2049r
11b7e23ad2 remove RUB as currency as we don't get an exchange rate for it 2023-05-03 22:49:18 +02:00
m2049r
ae48027689 new qr code logo 2023-05-03 22:43:42 +02:00
m2049r
a42f750fc4 center sync status 2023-05-03 08:22:23 +02:00
m2049r
3406f585f2 organize all imports 2023-04-30 17:46:58 +02:00
m2049r
7546637c89 remove NFC support 2023-04-30 17:43:51 +02:00
m2049r
4da2106f04 show active account in drawer 2023-04-30 17:16:20 +02:00
m2049r
93c11fb90e update build tools version 2023-04-30 17:04:05 +02:00
m2049r
9fa710f75b fix build warnings 2023-04-27 23:58:08 +02:00
m2049r
c53dd300bc update dependencies 2023-04-26 08:39:30 +02:00
m2049r
97f40648be make streetmode label better 2023-04-26 08:25:33 +02:00
m2049r
1ece6bfbeb move status above sum 2023-04-26 08:15:02 +02:00
m2049r
4b3b99ff2a fix colour of service logo & use it for receive qr code 2023-04-26 07:55:26 +02:00
m2049r
ca833d7017 show full subaddress in details 2023-04-26 07:38:14 +02:00
m2049r
f1b6f859de show subaddress info above label 2023-04-26 07:34:18 +02:00
m2049r
61d19c7066 remove next arrow 2023-04-26 07:25:04 +02:00
m2049r
ffda0e965b bump version 2023-01-07 11:07:19 +01:00
m2049r
cc7cdb383c tweak http timeouts so sideshift does not timeout (#881) 2023-01-07 10:22:21 +01:00
m2049r
ac1ea05ef6 add unlockTime (#880)
show lock icon for currently locked txs
2023-01-07 10:19:22 +01:00
m2049r
1ddd4f30b9 use monero v0.18.1.2 (#878) 2022-12-04 21:04:15 +01:00
m2049r
1dc081834f bump version 2022-10-22 11:33:11 +02:00
m2049r
ce084927e1 fix langauge list 2022-10-22 11:32:44 +02:00
Kartal Kaan Bozdoğan
3610781f43 Display the unconfirmed amount in the chosen currency (#844)
Changed the translations accordingly
2022-10-22 10:59:25 +02:00
m2049r
ef3ddbac71 fix versions 2022-08-13 23:24:57 +02:00
m2049r
0512af1496 Monero v0.18.1.0 2022-08-13 10:20:21 +02:00
m2049r
bd2c49669a Feature v18 (#860)
* versions

* remove x86 builds
2022-08-10 16:50:18 +02:00
Kartal Kaan Bozdoğan
ac7831d0f9 Upgrade to 0.18.0.0 Fluorine Fermi (#856)
* Upgrade to Monero wallet 0.18.0.0 Fluorine Fermi
2022-08-05 01:09:56 +02:00
Kartal Kaan Bozdoğan
0f0b9a38c7 Fix new wallet restore height (#858)
* New wallets: set current block height as the restore height.
  Go back 4 days for estimated heights
2022-08-05 01:09:10 +02:00
Kartal Kaan Bozdoğan
807db19603 Fixed zlib hashes (#845) 2022-08-05 01:02:34 +02:00
m2049r
c956f38899 remove S for Nano 2022-05-23 19:47:53 +02:00
m2049r
db68f517d3 bump version 2022-05-23 19:31:22 +02:00
m2049r
d4b293af80 new strings (#839) 2022-05-23 19:20:28 +02:00
m2049r
f7bbfc2fac remove untranslatables (#838) 2022-05-23 19:16:49 +02:00
m2049r
e08964749e Fix entities (#837)
* remove &check;

* resolve nbsp
2022-05-23 19:03:45 +02:00
Dave Scotese
a05fa9d177 Exchange rate was hard to find. (#763)
The FAQ I added uses both "exchange" and "rate," neither of which appeared in the FAQ otherwise.
2022-05-23 18:42:30 +02:00
kivojo
7fe2fbe37d Translation to Farsi/Persian (#815)
* about.xml translated to Farsi

* minor improvements in translation

* one small typo

* help.xml translated to Farsi

* <br/> tags fixed

* better translation of word Mnemonic to Farsi

* strings.xml translated to Farsi
READY for PR!

* oops, my bad! deleted the untranslatable strings.

Co-authored-by: kivojo <user@example.com>
2022-05-23 18:41:06 +02:00
kingoflove819
40e30fed08 Create about.xml (#809)
* Create about.xml

* Update about.xml

* Completed translating about.xml for tamil
2022-05-23 18:39:38 +02:00
kingoflove819
320c7865ff Create help.xml (#810)
* Create help.xml

* Update help.xml

* Update help.xml

* Update help.xml

* Update help.xml
2022-05-23 18:39:23 +02:00
kingoflove819
5e8cf8010e Create strings.xml (#811)
* Create strings.xml

* Update strings.xml

* Update strings.xml

* Update strings.xml

* Update strings.xml

* Completed strings.xml
2022-05-23 18:39:07 +02:00
Age Bosma
e671fa19e0 Specify wallet folder (#835)
* Specify wallet folder

* Update FAQ.md
2022-05-23 18:36:22 +02:00
m2049r
20d5b9a100 fix sweep to be invisible by default 2022-05-15 21:56:38 +02:00
m2049r
5d489a634b bump version 2022-05-15 17:25:28 +02:00
m2049r
59b6f484fd update to monero v0.17.3.2 2022-05-15 17:24:54 +02:00
m2049r
ecaa49d67d upgrade build files 2022-05-15 15:57:11 +02:00
jont4
d2dc53599e Update PT-rBR strings (#813)
* Update pt-rBR

* Update app/src/main/res/values-pt-rBR/strings.xml

Co-authored-by: netrik182 <30935310+netrik182@users.noreply.github.com>

* Update app/src/main/res/values-pt-rBR/strings.xml

Co-authored-by: netrik182 <30935310+netrik182@users.noreply.github.com>

* Update strings.xml

fix as suggested

Co-authored-by: jontaix <31804298+jontaix@users.noreply.github.com>
Co-authored-by: netrik182 <30935310+netrik182@users.noreply.github.com>
Co-authored-by: m2049r <m2049r@monerujo.io>
2022-05-01 11:51:49 +02:00
Лапки
4d8b26f97f Update strings.xml (#826) 2022-05-01 11:44:52 +02:00
Justin Berman
581c76e7be setName uses async network helper in case of reverse DNS lookup (#818, #827) (#828) 2022-05-01 11:44:27 +02:00
AnonimaUzanto
6f66862870 Adding support for Ledger Nano S Plus (#832) 2022-05-01 11:35:01 +02:00
m2049r
dd92f7bb36 bump version 2022-03-13 15:37:28 +01:00
m2049r
46808d306b clean transitions 2022-03-12 23:10:34 +01:00
m2049r
20503d2cbd remove send/receive transitions 2022-03-11 00:25:50 +01:00
m2049r
604691ca7e fix FABs 2022-03-10 21:06:19 +01:00
m2049r
1b626ba2b0 improve transitions 2022-03-10 20:57:57 +01:00
m2049r
0ed7bdfcee remove test Dockerfile 2022-03-10 09:07:14 +01:00
m2049r
524c3dd79f upgrade ci image to 2022.03-ndk (#822) 2022-03-10 09:04:43 +01:00
m2049r
197dffeae1 fix tests 2022-03-10 07:53:52 +01:00
m2049r
cdb29bbc2e themes (#821)
* Support multiple colour themes
* Fix sharing
* Add settings
* Fantastic UI tweaks
2022-03-09 19:10:30 +01:00
m2049r
7b96baeca7 fix watch only 2022-01-03 12:53:31 +01:00
434 changed files with 15669 additions and 12513 deletions

View File

@@ -3,13 +3,11 @@ jobs:
build:
working_directory: ~/code
docker:
- image: circleci/android:api-28-ndk
- image: cimg/android:2023.12-ndk
environment:
JVM_OPTS: -Xmx3200m
steps:
- checkout
- run: yes | sdkmanager --licenses || exit 0
- run: yes | sdkmanager --update || exit 0
- restore_cache:
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
- run:

10
.gitignore vendored
View File

@@ -8,11 +8,9 @@
.DS_Store
/app/build
/app/release
/app/alpha
/app/prod
/app/alphaMainnet
/app/prodMainnet
/app/alphaStagenet
/app/prodStagenet
/app/alpha*
/app/prod*
/app/.cxx
/monerujo.id
/external-libs/VERSION
/external-libs/include/wallet2_api.h

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.4.1)
project(monerujo)
message(STATUS ABI_INFO = ${ANDROID_ABI})
add_library( monerujo
@@ -121,7 +122,7 @@ set_target_properties(easylogging PROPERTIES IMPORTED_LOCATION
add_library(unbound STATIC IMPORTED)
set_target_properties(unbound PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libunbound.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libunbound.a)
add_library(epee STATIC IMPORTED)
set_target_properties(epee PROPERTIES IMPORTED_LOCATION

View File

@@ -1,15 +1,15 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 30
buildToolsVersion '30.0.3'
ndkVersion '17.2.4988734'
defaultConfig {
applicationId "com.m2049r.xmrwallet"
buildToolsVersion = '35.0.0'
compileSdk 35
minSdkVersion 21
targetSdkVersion 30
versionCode 1301
versionName "2.3.1 'Doménikos'"
targetSdkVersion 35
versionCode 4107
versionName "4.1.7 'Exolix'"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
@@ -24,7 +24,7 @@ android {
}
}
flavorDimensions 'type', 'net'
flavorDimensions = ['type', 'net']
productFlavors {
mainnet {
dimension 'net'
@@ -58,7 +58,7 @@ android {
applicationIdSuffix ".debug"
}
applicationVariants.all { variant ->
variant.buildConfigField "String", "ID_A", "\"" + getId("ID_A") + "\""
variant.buildConfigField "String", "ID_F", "\"" + getId("ID_F") + "\""
}
}
@@ -72,7 +72,7 @@ android {
abi {
enable true
reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
include 'armeabi-v7a', 'arm64-v8a', 'x86_64'
universalApk true
}
}
@@ -108,8 +108,24 @@ android {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
namespace 'com.m2049r.xmrwallet'
buildFeatures {
buildConfig true
}
testOptions {
unitTests {
all {
jvmArgs '--add-opens=java.base/java.lang=ALL-UNNAMED'
}
}
}
lint {
disable "MissingTranslation" // Translation is crowd-sourced.
}
}
@@ -120,38 +136,42 @@ static def getId(name) {
}
dependencies {
implementation 'androidx.core:core:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation(platform('org.jetbrains.kotlin:kotlin-bom:2.1.21'))
implementation 'androidx.core:core:1.16.0'
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.recyclerview:recyclerview:1.4.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'androidx.preference:preference:1.2.1'
implementation 'com.google.android.material:material:1.12.0'
implementation 'com.google.guava:guava:33.4.8-android'
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation "com.squareup.okhttp3:okhttp:4.9.0"
implementation "io.github.rburgst:okhttp-digest:2.5"
implementation "com.jakewharton.timber:timber:4.7.1"
implementation "com.squareup.okhttp3:okhttp:4.12.0"
implementation 'io.github.rburgst:okhttp-digest:3.1.1'
implementation "com.jakewharton.timber:timber:5.0.1"
implementation 'info.guardianproject.netcipher:netcipher:2.1.0'
//implementation 'info.guardianproject.netcipher:netcipher-okhttp3:2.1.0'
implementation fileTree(dir: 'libs/classes', include: ['*.jar'])
implementation 'com.nulab-inc:zxcvbn:1.3.0'
implementation 'com.nulab-inc:zxcvbn:1.9.0'
implementation 'dnsjava:dnsjava:2.1.9'
implementation 'org.jitsi:dnssecjava:1.2.0'
implementation 'org.slf4j:slf4j-nop:1.7.30'
implementation 'dnsjava:dnsjava:3.6.3'
implementation 'org.slf4j:slf4j-nop:2.0.17'
implementation 'com.github.brnunes:swipeablerecyclerview:1.0.2'
//noinspection GradleDependency
testImplementation "junit:junit:$rootProject.ext.junitVersion"
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.0"
testImplementation 'org.json:json:20180813'
testImplementation 'net.jodah:concurrentunit:0.4.4'
testImplementation "junit:junit:4.13.2"
testImplementation "org.mockito:mockito-all:1.10.19"
testImplementation "com.squareup.okhttp3:mockwebserver:4.12.0"
testImplementation 'org.json:json:20250517'
testImplementation 'net.jodah:concurrentunit:0.4.6'
compileOnly 'org.projectlombok:lombok:1.18.16'
annotationProcessor 'org.projectlombok:lombok:1.18.16'
compileOnly 'org.projectlombok:lombok:1.18.38'
annotationProcessor 'org.projectlombok:lombok:1.18.38'
}

View File

@@ -1,15 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.m2049r.xmrwallet">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<queries>
<intent>
@@ -18,7 +26,6 @@
<intent>
<action android:name="org.torproject.android.intent.action.STATUS" />
</intent>
<intent>
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
</intent>
@@ -36,15 +43,17 @@
android:label="@string/app_name"
android:preserveLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@style/MyMaterialTheme"
android:theme="@style/MyMaterialThemeClassic"
android:usesCleartextTraffic="true">
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|uiMode"
android:exported="true"
android:launchMode="singleTop"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
@@ -56,24 +65,25 @@
android:screenOrientation="behind" />
<activity
android:name=".LoginActivity"
android:configChanges="orientation|keyboardHidden|uiMode"
android:configChanges="orientation|keyboardHidden"
android:exported="true"
android:label="@string/app_name"
android:launchMode="singleTop"
android:screenOrientation="locked">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" />
<data android:scheme="monero" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" />
<data android:scheme="bitcoin" />
<category android:name="android.intent.category.DEFAULT" />
@@ -94,7 +104,12 @@
android:name=".service.WalletService"
android:description="@string/service_description"
android:exported="false"
android:label="Monero Wallet Service" />
android:foregroundServiceType="specialUse"
android:label="Monero Wallet Service">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="Keeps app in sync with the blockchain" />
</service>
<provider
android:name="androidx.core.content.FileProvider"
@@ -106,4 +121,5 @@
android:resource="@xml/filepaths" />
</provider>
</application>
</manifest>
</manifest>

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017 m2049r
* Copyright (c) 2017-2024 m2049r
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@
#include <jni.h>
#include <string>
/*
#include <android/log.h>
@@ -28,6 +30,10 @@
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
*/
void ThrowException(JNIEnv *jenv, const char* type, const char* msg) {
jenv->ThrowNew(jenv->FindClass(type), msg);
}
jfieldID getHandleField(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
jclass c = env->GetObjectClass(obj);
return env->GetFieldID(c, fieldName, "J"); // of type long
@@ -35,8 +41,16 @@ jfieldID getHandleField(JNIEnv *env, jobject obj, const char *fieldName = "handl
template<typename T>
T *getHandle(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
return reinterpret_cast<T *>(env->GetLongField(obj, getHandleField(env, obj, fieldName)));
}
template<typename T>
void destroyNativeObject(JNIEnv *env, T nativeObjectHandle, jobject obj, const char *fieldName = "handle") {
jlong handle = env->GetLongField(obj, getHandleField(env, obj, fieldName));
return reinterpret_cast<T *>(handle);
if (handle != 0) {
ThrowException(env, "java/lang/IllegalStateException", "invalid handle (destroy)");
}
delete reinterpret_cast<T *>(nativeObjectHandle);
}
void setHandleFromLong(JNIEnv *env, jobject obj, jlong handle) {
@@ -54,7 +68,7 @@ extern "C"
{
#endif
extern const char* const MONERO_VERSION; // the actual monero core version
extern const char *const MONERO_VERSION; // the actual monero core version
// from monero-core crypto/hash-ops.h - avoid #including monero code here
enum {
@@ -62,18 +76,40 @@ enum {
HASH_DATA_AREA = 136
};
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height);
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed,
uint64_t height);
inline void slow_hash(const void *data, const size_t length, char *hash) {
cn_slow_hash(data, length, hash, 0 /*variant*/, 0 /*prehashed*/, 0 /*height*/);
}
inline void slow_hash_broken(const void *data, char *hash, int variant) {
cn_slow_hash(data, 200 /*sizeof(union hash_state)*/, hash, variant, 1 /*prehashed*/, 0 /*height*/);
cn_slow_hash(data, 200 /*sizeof(union hash_state)*/, hash, variant, 1 /*prehashed*/,
0 /*height*/);
}
#ifdef __cplusplus
}
#endif
namespace Monerujo {
class SidekickWallet {
public:
enum Status {
Status_Ok,
Status_Error,
Status_Critical
};
SidekickWallet(uint8_t networkType, std::string a, std::string b);
~SidekickWallet();
std::string call(int commandId, const std::string &request);
void reset();
Status status() const;
};
}
#endif //XMRWALLET_WALLET_LIB_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -20,8 +20,6 @@
package com.btchip.comm;
import com.btchip.BTChipException;
public interface BTChipTransport {
byte[] exchange(byte[] command);

View File

@@ -28,7 +28,6 @@ import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbRequest;
import com.btchip.BTChipException;
import com.btchip.comm.BTChipTransport;
import com.btchip.comm.LedgerHelper;
import com.btchip.utils.Dump;
@@ -78,7 +77,7 @@ public class BTChipTransportAndroidHID implements BTChipTransport {
}
private static final int VID = 0x2C97;
private static final int[] PID_HIDS = {0x0001, 0x0004};
private static final int[] PID_HIDS = {0x0001, 0x0004, 0x0005};
private UsbDeviceConnection connection;
private UsbInterface dongleInterface;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -16,11 +16,8 @@
package com.m2049r.xmrwallet;
import androidx.annotation.NonNull;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.Editable;
import android.text.Html;
@@ -36,8 +33,8 @@ import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
@@ -45,6 +42,7 @@ import androidx.fragment.app.Fragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.switchmaterial.SwitchMaterial;
import com.google.android.material.textfield.TextInputLayout;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.FingerprintHelper;
@@ -58,7 +56,6 @@ import com.m2049r.xmrwallet.widget.Toolbar;
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Objects;
import timber.log.Timber;
@@ -69,8 +66,20 @@ public class GenerateFragment extends Fragment {
static final String TYPE_KEY = "key";
static final String TYPE_SEED = "seed";
static final String TYPE_LEDGER = "ledger";
static final String TYPE_SIDEKICK = "sidekick";
static final String TYPE_VIEWONLY = "view";
static Wallet.Device getDeviceType(String type) {
switch (type) {
case TYPE_SIDEKICK:
return Wallet.Device.Sidekick;
case TYPE_LEDGER:
return Wallet.Device.Ledger;
default:
return Wallet.Device.Software;
}
}
private TextInputLayout etWalletName;
private PasswordEntryView etWalletPassword;
private LinearLayout llFingerprintAuth;
@@ -197,6 +206,7 @@ public class GenerateFragment extends Fragment {
etWalletPassword.getEditText().setImeOptions(EditorInfo.IME_ACTION_UNSPECIFIED);
break;
case TYPE_LEDGER:
case TYPE_SIDEKICK:
etWalletPassword.getEditText().setImeOptions(EditorInfo.IME_ACTION_DONE);
etWalletPassword.getEditText().setOnEditorActionListener((v, actionId, event) -> {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
@@ -299,10 +309,10 @@ public class GenerateFragment extends Fragment {
if (etSeedOffset.getVisibility() == View.VISIBLE) {
etSeedOffset.getEditText().getText().clear();
etSeedOffset.setVisibility(View.GONE);
bSeedOffset.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_baseline_keyboard_arrow_down_24, 0, 0, 0);
bSeedOffset.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_keyboard_arrow_down, 0, 0, 0);
} else {
etSeedOffset.setVisibility(View.VISIBLE);
bSeedOffset.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_baseline_keyboard_arrow_up_24, 0, 0, 0);
bSeedOffset.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_keyboard_arrow_up, 0, 0, 0);
etSeedOffset.requestFocusFromTouch();
}
}
@@ -310,7 +320,7 @@ public class GenerateFragment extends Fragment {
private boolean checkName() {
String name = etWalletName.getEditText().getText().toString();
boolean ok = true;
if (name.length() == 0) {
if (name.isEmpty()) {
etWalletName.setError(getString(R.string.generate_wallet_name));
ok = false;
} else if (name.charAt(0) == '.') {
@@ -347,21 +357,23 @@ public class GenerateFragment extends Fragment {
String restoreHeight = etWalletRestoreHeight.getEditText().getText().toString().trim();
if (restoreHeight.isEmpty()) return -1;
try {
// is it a date?
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd");
parser.setLenient(false);
height = RestoreHeight.getInstance().getHeight(parser.parse(restoreHeight));
} catch (ParseException ignored) {
}
if ((height < 0) && (restoreHeight.length() == 8))
if (WalletManager.getInstance().getNetworkType() == NetworkType.NetworkType_Mainnet) {
try {
// is it a date without dashes?
SimpleDateFormat parser = new SimpleDateFormat("yyyyMMdd");
// is it a date?
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd");
parser.setLenient(false);
height = RestoreHeight.getInstance().getHeight(parser.parse(restoreHeight));
} catch (ParseException ignored) {
}
if ((height < 0) && (restoreHeight.length() == 8))
try {
// is it a date without dashes?
SimpleDateFormat parser = new SimpleDateFormat("yyyyMMdd");
parser.setLenient(false);
height = RestoreHeight.getInstance().getHeight(parser.parse(restoreHeight));
} catch (ParseException ignored) {
}
}
if (height < 0)
try {
// or is it a height?
@@ -450,11 +462,12 @@ public class GenerateFragment extends Fragment {
activityCallback.onGenerate(name, crazyPass, seed, offset, height);
break;
case TYPE_LEDGER:
case TYPE_SIDEKICK:
bGenerate.setEnabled(false);
if (fingerprintAuthAllowed) {
KeyStoreHelper.saveWalletUserPass(requireActivity(), name, password);
}
activityCallback.onGenerateLedger(name, crazyPass, height);
activityCallback.onGenerateDevice(getDeviceType(type), name, crazyPass, height);
break;
case TYPE_KEY:
case TYPE_VIEWONLY:
@@ -498,6 +511,8 @@ public class GenerateFragment extends Fragment {
return getString(R.string.generate_wallet_type_seed);
case TYPE_LEDGER:
return getString(R.string.generate_wallet_type_ledger);
case TYPE_SIDEKICK:
return getString(R.string.generate_wallet_type_sidekick);
case TYPE_VIEWONLY:
return getString(R.string.generate_wallet_type_view);
default:
@@ -515,7 +530,7 @@ public class GenerateFragment extends Fragment {
void onGenerate(String name, String password, String address, String viewKey, String spendKey, long height);
void onGenerateLedger(String name, String password, long height);
void onGenerateDevice(Wallet.Device device, String name, String password, long height);
void setTitle(String title);
@@ -555,6 +570,9 @@ public class GenerateFragment extends Fragment {
case TYPE_LEDGER:
inflater.inflate(R.menu.create_wallet_ledger, menu);
break;
case TYPE_SIDEKICK:
inflater.inflate(R.menu.create_wallet_sidekick, menu);
break;
case TYPE_VIEWONLY:
inflater.inflate(R.menu.create_wallet_view, menu);
break;
@@ -581,13 +599,11 @@ public class GenerateFragment extends Fragment {
.setCancelable(false)
.setPositiveButton(getString(R.string.label_ok), null)
.setNegativeButton(getString(R.string.label_cancel),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Helper.hideKeyboardAlways(activity);
etWalletMnemonic.getEditText().getText().clear();
dialog.cancel();
ledgerDialog = null;
}
(dialog, id) -> {
Helper.hideKeyboardAlways(activity);
etWalletMnemonic.getEditText().getText().clear();
dialog.cancel();
ledgerDialog = null;
});
ledgerDialog = alertDialogBuilder.create();

View File

@@ -40,13 +40,13 @@ import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.progressindicator.CircularProgressIndicator;
import com.google.android.material.switchmaterial.SwitchMaterial;
import com.google.android.material.textfield.TextInputLayout;
import com.m2049r.xmrwallet.ledger.Ledger;
@@ -99,6 +99,13 @@ public class GenerateReviewFragment extends Fragment {
private String walletPath;
private String walletName;
private final OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(false) {
@Override
public void handleOnBackPressed() {
// nothing
}
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -157,10 +164,14 @@ public class GenerateReviewFragment extends Fragment {
});
Bundle args = getArguments();
assert args != null;
type = args.getString(REQUEST_TYPE);
walletPath = args.getString(REQUEST_PATH);
localPassword = args.getString(REQUEST_PASSWORD);
showDetails();
if (type.equals(GenerateReviewFragment.VIEW_TYPE_ACCEPT)) {
backPressedCallback.setEnabled(true);
}
return view;
}
@@ -200,10 +211,10 @@ public class GenerateReviewFragment extends Fragment {
void toggleAdvancedInfo() {
if (llAdvancedInfo.getVisibility() == View.VISIBLE) {
llAdvancedInfo.setVisibility(View.GONE);
bAdvancedInfo.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_baseline_keyboard_arrow_down_24, 0, 0, 0);
bAdvancedInfo.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_keyboard_arrow_down, 0, 0, 0);
} else {
llAdvancedInfo.setVisibility(View.VISIBLE);
bAdvancedInfo.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_baseline_keyboard_arrow_up_24, 0, 0, 0);
bAdvancedInfo.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_keyboard_arrow_up, 0, 0, 0);
scrollview.post(() -> scrollview.fullScroll(ScrollView.FOCUS_DOWN));
}
}
@@ -212,10 +223,10 @@ public class GenerateReviewFragment extends Fragment {
if (etSeedOffset.getVisibility() == View.VISIBLE) {
etSeedOffset.getEditText().getText().clear();
etSeedOffset.setVisibility(View.GONE);
bSeedOffset.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_baseline_keyboard_arrow_down_24, 0, 0, 0);
bSeedOffset.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_keyboard_arrow_down, 0, 0, 0);
} else {
etSeedOffset.setVisibility(View.VISIBLE);
bSeedOffset.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_baseline_keyboard_arrow_up_24, 0, 0, 0);
bSeedOffset.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_keyboard_arrow_up, 0, 0, 0);
etSeedOffset.requestFocusFromTouch();
}
}
@@ -234,7 +245,6 @@ public class GenerateReviewFragment extends Fragment {
String seed;
String viewKey;
String spendKey;
boolean isWatchOnly;
Wallet.Status walletStatus;
boolean dialogOpened = false;
@@ -245,7 +255,7 @@ public class GenerateReviewFragment extends Fragment {
showProgress();
if ((walletPath != null)
&& (WalletManager.getInstance().queryWalletDevice(walletPath + ".keys", getPassword())
== Wallet.Device.Device_Ledger)
== Wallet.Device.Ledger)
&& (progressCallback != null)) {
progressCallback.showLedgerProgressDialog(LedgerProgressDialog.TYPE_RESTORE);
dialogOpened = true;
@@ -277,17 +287,17 @@ public class GenerateReviewFragment extends Fragment {
height = wallet.getRestoreHeight();
seed = wallet.getSeed(getSeedOffset());
switch (wallet.getDeviceType()) {
case Device_Ledger:
case Ledger:
viewKey = Ledger.Key();
break;
case Device_Software:
case Software:
case Sidekick:
viewKey = wallet.getSecretViewKey();
break;
default:
throw new IllegalStateException("Hardware backing not supported. At all!");
}
spendKey = isWatchOnly ? getActivity().getString(R.string.label_watchonly) : wallet.getSecretSpendKey();
isWatchOnly = wallet.isWatchOnly();
spendKey = wallet.isWatchOnly() ? getActivity().getString(R.string.label_watchonly) : wallet.getSecretSpendKey();
if (closeWallet) wallet.close();
return true;
}
@@ -423,14 +433,11 @@ public class GenerateReviewFragment extends Fragment {
pbProgress.setVisibility(View.INVISIBLE);
}
boolean backOk() {
return !type.equals(GenerateReviewFragment.VIEW_TYPE_ACCEPT);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
requireActivity().getOnBackPressedDispatcher().addCallback(this, backPressedCallback);
}
@Override

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2024 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import timber.log.Timber;
public class LockFragment extends Fragment {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Timber.d("onCreateView");
final FrameLayout frame = new FrameLayout(requireContext());
frame.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return frame;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -29,16 +29,15 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
@@ -47,12 +46,14 @@ import com.google.android.material.progressindicator.CircularProgressIndicator;
import com.m2049r.xmrwallet.data.NodeInfo;
import com.m2049r.xmrwallet.dialog.HelpFragment;
import com.m2049r.xmrwallet.layout.WalletInfoAdapter;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.KeyStoreHelper;
import com.m2049r.xmrwallet.util.NetCipherHelper;
import com.m2049r.xmrwallet.util.NodePinger;
import com.m2049r.xmrwallet.util.Notice;
import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.io.File;
@@ -61,6 +62,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Set;
import lombok.Getter;
import timber.log.Timber;
public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInteractionListener,
@@ -115,7 +117,9 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
Set<NodeInfo> getOrPopulateFavourites();
boolean hasLedger();
boolean hasDevice(Wallet.Device type);
boolean isNetworkAvailable();
void runOnNetCipher(Runnable runnable);
}
@@ -143,12 +147,18 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
super.onResume();
Timber.d("onResume() %s", activityCallback.getFavouriteNodes().size());
activityCallback.setTitle(null);
activityCallback.setToolbarButton(Toolbar.BUTTON_CREDITS);
activityCallback.setToolbarButton(Toolbar.BUTTON_SETTINGS);
activityCallback.showNet();
showNetwork();
//activityCallback.runOnNetCipher(this::pingSelectedNode);
}
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
@Override
public void handleOnBackPressed() {
animateFAB();
}
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -163,13 +173,17 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
fabView = view.findViewById(R.id.fabView);
fabKey = view.findViewById(R.id.fabKey);
fabSeed = view.findViewById(R.id.fabSeed);
fabImport = view.findViewById(R.id.fabImport);
fabLedger = view.findViewById(R.id.fabLedger);
fabSidekick = view.findViewById(R.id.fabSidekick);
fabNewL = view.findViewById(R.id.fabNewL);
fabViewL = view.findViewById(R.id.fabViewL);
fabKeyL = view.findViewById(R.id.fabKeyL);
fabSeedL = view.findViewById(R.id.fabSeedL);
fabImportL = view.findViewById(R.id.fabImportL);
fabLedgerL = view.findViewById(R.id.fabLedgerL);
fabSidekickL = view.findViewById(R.id.fabSidekickL);
fab_pulse = AnimationUtils.loadAnimation(getContext(), R.anim.fab_pulse);
fab_open_screen = AnimationUtils.loadAnimation(getContext(), R.anim.fab_open_screen);
@@ -183,7 +197,9 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
fabView.setOnClickListener(this);
fabKey.setOnClickListener(this);
fabSeed.setOnClickListener(this);
fabImport.setOnClickListener(this);
fabLedger.setOnClickListener(this);
fabSidekick.setOnClickListener(this);
fabScreen.setOnClickListener(this);
RecyclerView recyclerView = view.findViewById(R.id.list);
@@ -192,6 +208,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
recyclerView.setAdapter(adapter);
ViewGroup llNotice = view.findViewById(R.id.llNotice);
Notice.showAll(llNotice, ".*_login");
view.findViewById(R.id.llNode).setOnClickListener(v -> startNodePrefs());
@@ -266,7 +283,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
}
// remove information of non-existent wallet
Set<String> removedWallets = getActivity()
Set<String> removedWallets = requireActivity()
.getSharedPreferences(KeyStoreHelper.SecurityConstants.WALLET_PASS_PREFS_NAME, Context.MODE_PRIVATE)
.getAll().keySet();
for (WalletManager.WalletInfo s : walletList) {
@@ -285,6 +302,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
}
@Override
@@ -293,25 +311,30 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
super.onCreateOptionsMenu(menu, inflater);
}
private boolean isFabOpen = false;
private FloatingActionButton fab, fabNew, fabView, fabKey, fabSeed, fabLedger;
private FrameLayout fabScreen;
private RelativeLayout fabNewL, fabViewL, fabKeyL, fabSeedL, fabLedgerL;
@Getter
private boolean fabOpen = false;
private FloatingActionButton fab, fabNew, fabView, fabKey, fabSeed, fabImport, fabLedger, fabSidekick;
private RelativeLayout fabScreen;
private RelativeLayout fabNewL, fabViewL, fabKeyL, fabSeedL, fabImportL, fabLedgerL, fabSidekickL;
private Animation fab_open, fab_close, rotate_forward, rotate_backward, fab_open_screen, fab_close_screen;
private Animation fab_pulse;
public boolean isFabOpen() {
return isFabOpen;
private void setFabOpen(boolean value) {
fabOpen = value;
onBackPressedCallback.setEnabled(value);
}
public void animateFAB() {
if (isFabOpen) { // close the fab
if (isFabOpen()) { // close the fab
fabScreen.setClickable(false);
fabScreen.startAnimation(fab_close_screen);
fab.startAnimation(rotate_backward);
if (fabLedgerL.getVisibility() == View.VISIBLE) {
fabLedgerL.startAnimation(fab_close);
fabLedger.setClickable(false);
} else if (fabSidekickL.getVisibility() == View.VISIBLE) {
fabSidekickL.startAnimation(fab_close);
fabSidekick.setClickable(false);
} else {
fabNewL.startAnimation(fab_close);
fabNew.setClickable(false);
@@ -321,27 +344,39 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
fabKey.setClickable(false);
fabSeedL.startAnimation(fab_close);
fabSeed.setClickable(false);
fabImportL.startAnimation(fab_close);
fabImport.setClickable(false);
}
isFabOpen = false;
setFabOpen(false);
} else { // open the fab
fabScreen.setClickable(true);
fabScreen.startAnimation(fab_open_screen);
fab.startAnimation(rotate_forward);
if (activityCallback.hasLedger()) {
fabLedgerL.setVisibility(View.VISIBLE);
if ((activityCallback.hasDevice(Wallet.Device.Ledger)
|| activityCallback.hasDevice(Wallet.Device.Sidekick))) {
fabNewL.setVisibility(View.GONE);
fabViewL.setVisibility(View.GONE);
fabKeyL.setVisibility(View.GONE);
fabSeedL.setVisibility(View.GONE);
fabImportL.setVisibility(View.GONE);
fabLedgerL.startAnimation(fab_open);
fabLedger.setClickable(true);
if (activityCallback.hasDevice(Wallet.Device.Ledger)) {
fabLedgerL.setVisibility(View.VISIBLE);
fabLedgerL.startAnimation(fab_open);
fabLedger.setClickable(true);
} else { // Sidekick
fabSidekickL.setVisibility(View.VISIBLE);
fabSidekickL.startAnimation(fab_open);
fabSidekick.setClickable(true);
}
} else {
fabSidekickL.setVisibility(View.GONE);
fabLedgerL.setVisibility(View.GONE);
fabNewL.setVisibility(View.VISIBLE);
fabViewL.setVisibility(View.VISIBLE);
fabKeyL.setVisibility(View.VISIBLE);
fabSeedL.setVisibility(View.VISIBLE);
fabImportL.setVisibility(View.VISIBLE);
fabNewL.startAnimation(fab_open);
fabNew.setClickable(true);
@@ -351,8 +386,10 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
fabKey.setClickable(true);
fabSeedL.startAnimation(fab_open);
fabSeed.setClickable(true);
fabImportL.startAnimation(fab_open);
fabImport.setClickable(true);
}
isFabOpen = true;
setFabOpen(true);
}
}
@@ -364,7 +401,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
animateFAB();
} else if (id == R.id.fabNew) {
fabScreen.setVisibility(View.INVISIBLE);
isFabOpen = false;
setFabOpen(false);
activityCallback.onAddWallet(GenerateFragment.TYPE_NEW);
} else if (id == R.id.fabView) {
animateFAB();
@@ -375,10 +412,17 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
} else if (id == R.id.fabSeed) {
animateFAB();
activityCallback.onAddWallet(GenerateFragment.TYPE_SEED);
} else if (id == R.id.fabImport) {
animateFAB();
activityCallback.onWalletRestore();
} else if (id == R.id.fabLedger) {
Timber.d("FAB_LEDGER");
animateFAB();
activityCallback.onAddWallet(GenerateFragment.TYPE_LEDGER);
} else if (id == R.id.fabSidekick) {
Timber.d("FAB_SIDEKICK");
animateFAB();
activityCallback.onAddWallet(GenerateFragment.TYPE_SIDEKICK);
} else if (id == R.id.fabScreen) {
animateFAB();
}
@@ -401,10 +445,10 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
}
private void setSubtext(String status) {
final Context ctx = getContext();
final Context ctx = requireContext();
final Spanned text = Html.fromHtml(ctx.getString(R.string.status,
Integer.toHexString(ContextCompat.getColor(ctx, R.color.monerujoGreen) & 0xFFFFFF),
Integer.toHexString(ContextCompat.getColor(ctx, R.color.monerujoBackground) & 0xFFFFFF),
Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
Integer.toHexString(ThemeHelper.getThemedColor(ctx, android.R.attr.colorBackground) & 0xFFFFFF),
status, ""));
tvNodeInfo.setText(text);
}

View File

@@ -20,7 +20,6 @@ import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.m2049r.xmrwallet.onboarding.OnBoardingActivity;
import com.m2049r.xmrwallet.onboarding.OnBoardingManager;

View File

@@ -31,6 +31,8 @@ import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
@@ -67,7 +69,7 @@ public class NodeFragment extends Fragment
static private int NODES_TO_FIND = 10;
static private NumberFormat FORMATTER = NumberFormat.getInstance();
static private final NumberFormat FORMATTER = NumberFormat.getInstance();
private SwipeRefreshLayout pullToRefresh;
private TextView tvPull;
@@ -103,7 +105,7 @@ public class NodeFragment extends Fragment
}
@Override
public void onAttach(Context context) {
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof Listener) {
this.activityCallback = (Listener) context;
@@ -145,6 +147,13 @@ public class NodeFragment extends Fragment
}
}
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
@Override
public void handleOnBackPressed() {
Toast.makeText(requireActivity(), getString(R.string.node_refresh_wait), Toast.LENGTH_LONG).show();
}
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -187,6 +196,7 @@ public class NodeFragment extends Fragment
private boolean refresh(int type) {
if (asyncFindNodes != null) return false; // ignore refresh request as one is ongoing
onBackPressedCallback.setEnabled(true);
asyncFindNodes = new AsyncFindNodes();
updateRefreshElements();
asyncFindNodes.execute(type);
@@ -197,10 +207,11 @@ public class NodeFragment extends Fragment
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.node_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@@ -279,8 +290,7 @@ public class NodeFragment extends Fragment
} else if (params[0] == SCAN) {
// otherwise scan the network
Timber.d("scanning");
Set<NodeInfo> seedList = new HashSet<>();
seedList.addAll(nodeList);
Set<NodeInfo> seedList = new HashSet<>(nodeList);
nodeList.clear();
Timber.d("seed %d", seedList.size());
Dispatcher d = new Dispatcher(info -> publishProgress(info));
@@ -342,6 +352,7 @@ public class NodeFragment extends Fragment
private void complete() {
asyncFindNodes = null;
onBackPressedCallback.setEnabled(false);
if (!isAdded()) return;
//if (isCancelled()) return;
tvPull.setText(getString(R.string.node_pull_hint));
@@ -415,7 +426,14 @@ public class NodeFragment extends Fragment
}
etNodeHost.setError(null);
nodeInfo.setRpcPort(port);
nodeInfo.setName(etNodeName.getEditText().getText().toString().trim());
// setName() may trigger reverse DNS
Helper.runWithNetwork(new Helper.Action() {
@Override
public boolean run() {
nodeInfo.setName(etNodeName.getEditText().getText().toString().trim());
return true;
}
});
nodeInfo.setUsername(etNodeUser.getEditText().getText().toString().trim());
nodeInfo.setPassword(etNodePass.getEditText().getText().toString()); // no trim for pw
return true;
@@ -437,7 +455,7 @@ public class NodeFragment extends Fragment
private void closeDialog() {
if (editDialog == null) throw new IllegalStateException();
Helper.hideKeyboardAlways(getActivity());
Helper.hideKeyboardAlways(requireActivity());
editDialog.dismiss();
editDialog = null;
NodeFragment.this.editDialog = null;
@@ -568,6 +586,7 @@ public class NodeFragment extends Fragment
}
}
}
}
void restoreDefaultNodes() {

View File

@@ -22,7 +22,6 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.nfc.NfcManager;
import android.os.Bundle;
import android.text.Editable;
import android.text.Html;
@@ -33,12 +32,10 @@ import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -46,10 +43,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.ShareActionProvider;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.core.view.MenuItemCompat;
import androidx.fragment.app.Fragment;
import com.google.android.material.textfield.TextInputLayout;
@@ -88,7 +82,6 @@ public class ReceiveFragment extends Fragment {
private ImageView ivQrCode;
private ImageView ivQrCodeFull;
private EditText etDummy;
private ImageButton bCopyAddress;
private Wallet wallet = null;
private boolean isMyWallet = false;
@@ -119,15 +112,15 @@ public class ReceiveFragment extends Fragment {
tvQrCode = view.findViewById(R.id.tvQrCode);
ivQrCodeFull = view.findViewById(R.id.qrCodeFull);
etDummy = view.findViewById(R.id.etDummy);
bCopyAddress = view.findViewById(R.id.bCopyAddress);
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
bCopyAddress.setOnClickListener(v -> copyAddress());
view.findViewById(R.id.bCopyAddress).setOnClickListener(v -> copyAddress());
evAmount.setOnNewAmountListener(xmr -> {
Timber.d("new amount = %s", xmr);
generateQr();
if (shareRequested && (xmr != null)) share();
});
evAmount.setOnFailedExchangeListener(() -> {
@@ -192,11 +185,6 @@ public class ReceiveFragment extends Fragment {
throw new IllegalStateException("no wallet info");
}
View tvNfc = view.findViewById(R.id.tvNfc);
NfcManager manager = (NfcManager) getContext().getSystemService(Context.NFC_SERVICE);
if ((manager != null) && (manager.getDefaultAdapter() != null))
tvNfc.setVisibility(View.VISIBLE);
return view;
}
@@ -211,42 +199,40 @@ public class ReceiveFragment extends Fragment {
setSharedElementEnterTransition(transform);
}
private ShareActionProvider shareActionProvider;
private boolean shareRequested = false;
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, final MenuInflater inflater) {
inflater.inflate(R.menu.receive_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
// Locate MenuItem with ShareActionProvider
MenuItem item = menu.findItem(R.id.menu_item_share);
// Fetch and store ShareActionProvider
shareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(item);
shareActionProvider.setOnShareTargetSelectedListener(new ShareActionProvider.OnShareTargetSelectedListener() {
@Override
public boolean onShareTargetSelected(ShareActionProvider shareActionProvider, Intent intent) {
saveQrCode(); // save it only if we need it
return false;
menu.findItem(R.id.menu_item_share).setOnMenuItemClickListener(item -> {
if (shareRequested) return true;
shareRequested = true;
if (!qrValid) {
evAmount.doExchange();
} else {
share();
}
return true;
});
}
private void setShareIntent() {
if (shareActionProvider != null) {
if (qrValid) {
shareActionProvider.setShareIntent(getShareIntent());
} else {
shareActionProvider.setShareIntent(null);
}
private void share() {
shareRequested = false;
if (saveQrCode()) {
final Intent sendIntent = getSendIntent();
if (sendIntent != null)
startActivity(Intent.createChooser(sendIntent, null));
} else {
Toast.makeText(getActivity(), getString(R.string.message_qr_failed), Toast.LENGTH_SHORT).show();
}
}
private void saveQrCode() {
private boolean saveQrCode() {
if (!qrValid) throw new IllegalStateException("trying to save null qr code!");
File cachePath = new File(getActivity().getCacheDir(), "images");
File cachePath = new File(requireActivity().getCacheDir(), "images");
if (!cachePath.exists())
if (!cachePath.mkdirs()) throw new IllegalStateException("cannot create images folder");
File png = new File(cachePath, "QR.png");
@@ -255,33 +241,35 @@ public class ReceiveFragment extends Fragment {
Bitmap qrBitmap = ((BitmapDrawable) ivQrCode.getDrawable()).getBitmap();
qrBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
stream.close();
return true;
} catch (IOException ex) {
Timber.e(ex);
// make sure we don't share an old qr code
if (!png.delete()) throw new IllegalStateException("cannot delete old qr code");
// if we manage to delete it, the URI points to nothing and the user gets a toast with the error
}
return false;
}
private Intent getShareIntent() {
File imagePath = new File(getActivity().getCacheDir(), "images");
private Intent getSendIntent() {
File imagePath = new File(requireActivity().getCacheDir(), "images");
File png = new File(imagePath, "QR.png");
Uri contentUri = FileProvider.getUriForFile(getActivity(),
BuildConfig.APPLICATION_ID + ".fileprovider", png);
Uri contentUri = FileProvider.getUriForFile(requireActivity(), BuildConfig.APPLICATION_ID + ".fileprovider", png);
if (contentUri != null) {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // temp permission for receiving app to read this file
shareIntent.setDataAndType(contentUri, getActivity().getContentResolver().getType(contentUri));
shareIntent.setTypeAndNormalize("image/png");
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
shareIntent.putExtra(Intent.EXTRA_TEXT, bcData.getUriString());
if (bcData != null)
shareIntent.putExtra(Intent.EXTRA_TEXT, bcData.getUriString());
return shareIntent;
}
return null;
}
void copyAddress() {
Helper.clipBoardCopy(Objects.requireNonNull(getActivity()), getString(R.string.label_copy_address), subaddress.getAddress());
Helper.clipBoardCopy(requireActivity(), getString(R.string.label_copy_address), subaddress.getAddress());
Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show();
}
@@ -291,7 +279,6 @@ public class ReceiveFragment extends Fragment {
if (qrValid) {
ivQrCode.setImageBitmap(null);
qrValid = false;
setShareIntent();
if (isLoaded)
tvQrCode.setVisibility(View.VISIBLE);
}
@@ -300,7 +287,6 @@ public class ReceiveFragment extends Fragment {
void setQR(Bitmap qr) {
ivQrCode.setImageBitmap(qr);
qrValid = true;
setShareIntent();
tvQrCode.setVisibility(View.GONE);
}
@@ -405,7 +391,7 @@ public class ReceiveFragment extends Fragment {
private Bitmap getMoneroLogo() {
if (logo == null) {
logo = Helper.getBitmap(getContext(), R.drawable.ic_monero_logo_b);
logo = Helper.getBitmap(getContext(), R.drawable.ic_monerujo_qr);
}
return logo;
}
@@ -460,10 +446,10 @@ public class ReceiveFragment extends Fragment {
.withEndAction(resetSize).start();
}
subaddress = newSubaddress;
final Context context = getContext();
final Context context = requireContext();
Spanned label = Html.fromHtml(context.getString(R.string.receive_subaddress,
Integer.toHexString(ContextCompat.getColor(context, R.color.monerujoGreen) & 0xFFFFFF),
Integer.toHexString(ContextCompat.getColor(context, R.color.monerujoBackground) & 0xFFFFFF),
Integer.toHexString(ThemeHelper.getThemedColor(context, R.attr.positiveColor) & 0xFFFFFF),
Integer.toHexString(ThemeHelper.getThemedColor(context, android.R.attr.colorBackground) & 0xFFFFFF),
subaddress.getDisplayLabel(), subaddress.getAddress()));
tvAddress.setText(label);
generateQr();

View File

@@ -19,12 +19,14 @@ package com.m2049r.xmrwallet;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
@@ -42,7 +44,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result
private ZXingScannerView mScannerView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Timber.d("onCreateView");
mScannerView = new ZXingScannerView(getActivity());
return mScannerView;
@@ -84,7 +86,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result
}
@Override
public void onAttach(Context context) {
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof OnScannedListener) {
this.onScannedListener = (OnScannedListener) context;

View File

@@ -16,6 +16,8 @@
package com.m2049r.xmrwallet;
import static android.view.WindowManager.LayoutParams;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Build;
@@ -29,8 +31,6 @@ import com.m2049r.xmrwallet.util.LocaleHelper;
import java.util.Locale;
import static android.view.WindowManager.LayoutParams;
public abstract class SecureActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -66,11 +66,7 @@ public abstract class SecureActivity extends AppCompatActivity {
Locale locale = LocaleHelper.getPreferredLocale(this);
if (locale != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
config.setLocale(locale);
} else {
config.locale = locale;
}
config.setLocale(locale);
}
return config;
}

View File

@@ -0,0 +1,126 @@
package com.m2049r.xmrwallet;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.StyleRes;
import androidx.preference.ListPreference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
import com.m2049r.xmrwallet.dialog.AboutFragment;
import com.m2049r.xmrwallet.dialog.CreditsFragment;
import com.m2049r.xmrwallet.dialog.PrivacyFragment;
import com.m2049r.xmrwallet.util.DayNightMode;
import com.m2049r.xmrwallet.util.LocaleHelper;
import com.m2049r.xmrwallet.util.NightmodeHelper;
import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Locale;
import timber.log.Timber;
public class SettingsFragment extends PreferenceFragmentCompat
implements SharedPreferences.OnSharedPreferenceChangeListener {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.root_preferences, rootKey);
findPreference(getString(R.string.about_info)).setOnPreferenceClickListener(preference -> {
AboutFragment.display(getParentFragmentManager());
return true;
});
findPreference(getString(R.string.privacy_info)).setOnPreferenceClickListener(preference -> {
PrivacyFragment.display(getParentFragmentManager());
return true;
});
findPreference(getString(R.string.credits_info)).setOnPreferenceClickListener(preference -> {
CreditsFragment.display(getParentFragmentManager());
return true;
});
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(getString(R.string.preferred_locale))) {
activity.recreate();
} else if (key.equals(getString(R.string.preferred_nightmode))) {
NightmodeHelper.setNightMode(DayNightMode.valueOf(sharedPreferences.getString(key, "AUTO")));
} else if (key.equals(getString(R.string.preferred_theme))) {
ThemeHelper.setTheme((Activity) activity, sharedPreferences.getString(key, "Classic"));
activity.recreate();
}
}
private SettingsFragment.Listener activity;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof SettingsFragment.Listener) {
activity = (SettingsFragment.Listener) context;
} else {
throw new ClassCastException(context + " must implement Listener");
}
}
@Override
public void onResume() {
super.onResume();
Timber.d("onResume()");
activity.setSubtitle(getString(R.string.menu_settings));
activity.setToolbarButton(Toolbar.BUTTON_BACK);
populateLanguages();
PreferenceManager.getDefaultSharedPreferences(requireContext())
.registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onPause() {
super.onPause();
PreferenceManager.getDefaultSharedPreferences(requireContext())
.unregisterOnSharedPreferenceChangeListener(this);
}
public interface Listener {
void setToolbarButton(int type);
void setSubtitle(String title);
void recreate();
void setTheme(@StyleRes final int resId);
}
public void populateLanguages() {
ListPreference language = findPreference(getString(R.string.preferred_locale));
assert language != null;
final ArrayList<Locale> availableLocales = LocaleHelper.getAvailableLocales(requireContext());
Collections.sort(availableLocales, (locale1, locale2) -> {
String localeString1 = LocaleHelper.getDisplayName(locale1, true);
String localeString2 = LocaleHelper.getDisplayName(locale2, true);
return localeString1.compareTo(localeString2);
});
String[] localeDisplayNames = new String[1 + availableLocales.size()];
localeDisplayNames[0] = getString(R.string.language_system_default);
for (int i = 1; i < localeDisplayNames.length; i++) {
localeDisplayNames[i] = LocaleHelper.getDisplayName(availableLocales.get(i - 1), true);
}
language.setEntries(localeDisplayNames);
String[] languageTags = new String[1 + availableLocales.size()];
languageTags[0] = "";
for (int i = 1; i < languageTags.length; i++) {
languageTags[i] = availableLocales.get(i - 1).toLanguageTag();
}
language.setEntryValues(languageTags);
}
}

View File

@@ -0,0 +1,188 @@
/*
* Copyright (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.m2049r.xmrwallet.data.BluetoothInfo;
import com.m2049r.xmrwallet.layout.BluetoothInfoAdapter;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.util.ArrayList;
import java.util.List;
import timber.log.Timber;
public class SidekickConnectFragment extends Fragment
implements BluetoothInfoAdapter.OnInteractionListener {
private BluetoothAdapter bluetoothAdapter;
private SwipeRefreshLayout pullToRefresh;
private BluetoothInfoAdapter infoAdapter;
private Listener activityCallback;
public interface Listener {
void setToolbarButton(int type);
void setSubtitle(String title);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof Listener) {
this.activityCallback = (Listener) context;
} else {
throw new ClassCastException(context + " must implement Listener");
}
}
@Override
public void onPause() {
Timber.d("onPause()");
if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(requireContext(), "Bluetooth permission not granted", Toast.LENGTH_LONG).show();
} else {
if (bluetoothAdapter.isDiscovering()) {
bluetoothAdapter.cancelDiscovery();
}
}
super.onPause();
}
@Override
public void onResume() {
super.onResume();
Timber.d("onResume()");
activityCallback.setSubtitle(getString(R.string.label_bluetooth));
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
final BluetoothFragment btFragment = (BluetoothFragment) getChildFragmentManager().findFragmentById(R.id.bt_fragment);
assert btFragment != null;
btFragment.start();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Timber.d("onCreateView");
View view = inflater.inflate(R.layout.fragment_sidekick_connect, container, false);
RecyclerView recyclerView = view.findViewById(R.id.list);
infoAdapter = new BluetoothInfoAdapter(this);
recyclerView.setAdapter(infoAdapter);
pullToRefresh = view.findViewById(R.id.pullToRefresh);
pullToRefresh.setOnRefreshListener(() -> {
populateList();
pullToRefresh.setRefreshing(false);
});
return view;
}
private void populateList() {
List<BluetoothInfo> items = new ArrayList<>();
if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(requireContext(), "Bluetooth permission not granted", Toast.LENGTH_LONG).show();
return;
}
for (BluetoothDevice device : bluetoothAdapter.getBondedDevices()) {
final int deviceCLass = device.getBluetoothClass().getDeviceClass();
switch (deviceCLass) {
case BluetoothClass.Device.PHONE_SMART:
//TODO verify these are correct
case BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA:
case BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA:
items.add(new BluetoothInfo(device));
}
}
infoAdapter.setItems(items);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
Helper.hideKeyboard(getActivity());
// Get the local Bluetooth adapter
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
populateList();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.sidekick_connect_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public void onDestroy() {
super.onDestroy();
// Make sure we're not doing discovery anymore
if (bluetoothAdapter != null) {
if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(requireContext(), "Bluetooth permission not granted", Toast.LENGTH_LONG).show();
return;
}
bluetoothAdapter.cancelDiscovery();
}
}
@Override
public void onInteraction(final View view, final BluetoothInfo item) {
Timber.d("onInteraction %s", item);
if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED)
throw new IllegalStateException("Bluetooth permission not granted");
bluetoothAdapter.cancelDiscovery();
final BluetoothFragment btFragment = (BluetoothFragment) getChildFragmentManager().findFragmentById(R.id.bt_fragment);
assert btFragment != null;
btFragment.connectDevice(item.getAddress());
}
public void allowClick() {
infoAdapter.allowClick(true);
}
}

View File

@@ -30,7 +30,6 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.transition.MaterialElevationScale;
import com.m2049r.xmrwallet.data.Subaddress;
import com.m2049r.xmrwallet.layout.SubaddressInfoAdapter;
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
@@ -117,14 +116,6 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
managerMode = ((b != null) && (MODE_MANAGER.equals(b.getString(KEY_MODE))));
View view = inflater.inflate(R.layout.fragment_subaddress, container, false);
final MaterialElevationScale exitTransition = new MaterialElevationScale(false);
exitTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
setExitTransition(exitTransition);
final MaterialElevationScale reenterTransition = new MaterialElevationScale(true);
reenterTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
setReenterTransition(reenterTransition);
view.findViewById(R.id.fab).setOnClickListener(this);
if (managerMode) {
@@ -154,11 +145,6 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
postponeEnterTransition();
view.getViewTreeObserver().addOnPreDrawListener(() -> {
startPostponedEnterTransition();
return true;
});
}
public void loadList() {
@@ -209,7 +195,7 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
@Override
protected void onPreExecute() {
super.onPreExecute();
if ((wallet.getDeviceType() == Wallet.Device.Device_Ledger) && (progressCallback != null)) {
if ((wallet.getDeviceType() == Wallet.Device.Ledger) && (progressCallback != null)) {
progressCallback.showLedgerProgressDialog(LedgerProgressDialog.TYPE_SUBADDRESS);
dialogOpened = true;
}
@@ -240,7 +226,7 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
// Callbacks from SubaddressInfoAdapter
@Override
public void onInteraction(final View view, final Subaddress subaddress) {
public void onInteraction(final View view, @NonNull final Subaddress subaddress) {
if (managerMode)
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
else
@@ -252,4 +238,5 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
return false;
}
}

View File

@@ -29,15 +29,15 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import androidx.transition.Transition;
import androidx.transition.TransitionInflater;
import com.google.android.material.textfield.TextInputLayout;
import com.google.android.material.transition.MaterialContainerTransform;
import com.m2049r.xmrwallet.data.Subaddress;
import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.util.ArrayList;
@@ -46,13 +46,12 @@ import java.util.List;
import timber.log.Timber;
public class SubaddressInfoFragment extends Fragment
implements TransactionInfoAdapter.OnInteractionListener, OnBlockUpdateListener {
implements TransactionInfoAdapter.Listener, OnBlockUpdateListener {
private TransactionInfoAdapter adapter;
private Subaddress subaddress;
private TextInputLayout etName;
private TextView tvAddress;
private TextView tvTxLabel;
@Override
@@ -61,7 +60,6 @@ public class SubaddressInfoFragment extends Fragment
View view = inflater.inflate(R.layout.fragment_subaddressinfo, container, false);
etName = view.findViewById(R.id.etName);
tvAddress = view.findViewById(R.id.tvAddress);
tvTxLabel = view.findViewById(R.id.tvTxLabel);
final RecyclerView list = view.findViewById(R.id.list);
@@ -71,12 +69,14 @@ public class SubaddressInfoFragment extends Fragment
final Wallet wallet = activityCallback.getWallet();
Bundle b = getArguments();
assert b != null;
final int subaddressIndex = b.getInt("subaddressIndex");
subaddress = wallet.getSubaddressObject(subaddressIndex);
etName.getEditText().setText(subaddress.getDisplayLabel());
tvAddress.setText(getContext().getString(R.string.subbaddress_info_subtitle,
subaddress.getAddressIndex(), subaddress.getSquashedAddress()));
final TextView tvAddress = view.findViewById(R.id.tvAddress);
tvAddress.setText(requireContext().getString(R.string.subbaddress_info_subtitle,
subaddress.getAddressIndex(), subaddress.getAddress()));
etName.getEditText().setOnFocusChangeListener((v, hasFocus) -> {
if (!hasFocus) {
@@ -102,10 +102,8 @@ public class SubaddressInfoFragment extends Fragment
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final MaterialContainerTransform transform = new MaterialContainerTransform();
transform.setDrawingViewId(R.id.fragment_container);
transform.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
transform.setAllContainerColors(ThemeHelper.getThemedColor(getContext(), android.R.attr.colorBackground));
Transition transform = TransitionInflater.from(requireContext())
.inflateTransition(R.transition.details);
setSharedElementEnterTransition(transform);
}
@@ -147,6 +145,8 @@ public class SubaddressInfoFragment extends Fragment
void setTitle(String title, String subtitle);
void setSubtitle(String subtitle);
long getDaemonHeight();
}
@Override
@@ -172,4 +172,9 @@ public class SubaddressInfoFragment extends Fragment
public void onPause() {
super.onPause();
}
@Override
public long getDaemonHeight() {
return activityCallback.getDaemonHeight();
}
}

View File

@@ -20,7 +20,6 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.text.Html;
import android.text.InputType;
@@ -34,17 +33,19 @@ import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.transition.Transition;
import androidx.transition.TransitionInflater;
import com.google.android.material.transition.MaterialContainerTransform;
import com.google.android.material.transition.MaterialElevationScale;
import com.m2049r.xmrwallet.data.Subaddress;
import com.m2049r.xmrwallet.data.UserNotes;
import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.Transfer;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.service.shift.ShiftService;
import com.m2049r.xmrwallet.service.shift.api.ShiftApi;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.widget.Toolbar;
@@ -62,6 +63,7 @@ public class TxFragment extends Fragment {
static public final String ARG_INFO = "info";
@SuppressLint("SimpleDateFormat")
private final SimpleDateFormat TS_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
public TxFragment() {
@@ -80,10 +82,14 @@ public class TxFragment extends Fragment {
private TextView tvTxPaymentId;
private TextView tvTxBlockheight;
private TextView tvTxAmount;
private TextView tvTxPocketChangeAmount;
private TextView tvTxFee;
private TextView tvTxTransfers;
private TextView etTxNotes;
private View llWarning;
private TextView tvWarning;
// XMRTO stuff
private View cvXmrTo;
private TextView tvTxXmrToKey;
@@ -97,19 +103,13 @@ public class TxFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_tx_info, container, false);
final MaterialElevationScale exitTransition = new MaterialElevationScale(false);
exitTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
setExitTransition(exitTransition);
final MaterialElevationScale reenterTransition = new MaterialElevationScale(true);
reenterTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
setReenterTransition(reenterTransition);
cvXmrTo = view.findViewById(R.id.cvXmrTo);
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
tvDestinationBtc = view.findViewById(R.id.tvDestinationBtc);
tvTxAmountBtc = view.findViewById(R.id.tvTxAmountBtc);
tvXmrToSupport = view.findViewById(R.id.tvXmrToSupport);
tvXmrToKeyLabel = view.findViewById(R.id.tvXmrToKeyLabel);
tvXmrToLogo = view.findViewById(R.id.tvXmrToLogo);
tvAccount = view.findViewById(R.id.tvAccount);
@@ -121,25 +121,30 @@ public class TxFragment extends Fragment {
tvTxPaymentId = view.findViewById(R.id.tvTxPaymentId);
tvTxBlockheight = view.findViewById(R.id.tvTxBlockheight);
tvTxAmount = view.findViewById(R.id.tvTxAmount);
tvTxPocketChangeAmount = view.findViewById(R.id.tvTxPocketChangeAmount);
tvTxFee = view.findViewById(R.id.tvTxFee);
tvTxTransfers = view.findViewById(R.id.tvTxTransfers);
etTxNotes = view.findViewById(R.id.etTxNotes);
etTxNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);
llWarning = view.findViewById(R.id.llWarning);
tvWarning = view.findViewById(R.id.tvWarning);
tvTxXmrToKey.setOnClickListener(v -> {
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
Helper.clipBoardCopy(requireActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
});
info = getArguments().getParcelable(ARG_INFO);
final Bundle args = getArguments();
assert args != null;
info = args.getParcelable(ARG_INFO);
show();
return view;
}
void shareTxInfo() {
if (this.info == null) return;
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
sb.append(getString(R.string.tx_timestamp)).append(":\n");
sb.append(TS_FORMATTER.format(new Date(info.timestamp * 1000))).append("\n\n");
@@ -217,11 +222,11 @@ public class TxFragment extends Fragment {
private void showSubaddressLabel() {
final Subaddress subaddress = activityCallback.getWalletSubaddress(info.accountIndex, info.addressIndex);
final Context ctx = getContext();
final Context ctx = requireContext();
Spanned label = Html.fromHtml(ctx.getString(R.string.tx_account_formatted,
info.accountIndex, info.addressIndex,
Integer.toHexString(ContextCompat.getColor(ctx, R.color.monerujoGreen) & 0xFFFFFF),
Integer.toHexString(ContextCompat.getColor(ctx, R.color.monerujoBackground) & 0xFFFFFF),
Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
Integer.toHexString(ThemeHelper.getThemedColor(ctx, android.R.attr.colorBackground) & 0xFFFFFF),
subaddress.getDisplayLabel()));
tvAccount.setText(label);
tvAccount.setOnClickListener(v -> activityCallback.showSubaddress(v, info.addressIndex));
@@ -252,8 +257,10 @@ public class TxFragment extends Fragment {
}
String sign = (info.direction == TransactionInfo.Direction.Direction_In ? "+" : "-");
long realAmount = info.amount;
tvTxAmount.setText(sign + Wallet.getDisplayAmount(realAmount));
tvTxAmount.setText(sign + Wallet.getDisplayAmount(info.amount));
final long pcAmount = info.getPocketChangeAmount();
tvTxPocketChangeAmount.setVisibility(pcAmount > 0 ? View.VISIBLE : View.GONE);
tvTxPocketChangeAmount.setText(getString(R.string.pocketchange_tx_detail, Wallet.getDisplayAmount(pcAmount)));
if ((info.fee > 0)) {
String fee = Wallet.getDisplayAmount(info.fee);
@@ -263,16 +270,17 @@ public class TxFragment extends Fragment {
tvTxFee.setVisibility(View.GONE);
}
final Context ctx = requireContext();
if (info.isFailed) {
tvTxAmount.setText(getString(R.string.tx_list_amount_failed, Wallet.getDisplayAmount(info.amount)));
tvTxFee.setText(getString(R.string.tx_list_failed_text));
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_failed));
setTxColour(ThemeHelper.getThemedColor(ctx, R.attr.neutralColor));
} else if (info.isPending) {
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_pending));
setTxColour(ThemeHelper.getThemedColor(ctx, R.attr.neutralColor));
} else if (info.direction == TransactionInfo.Direction.Direction_In) {
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_plus));
setTxColour(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor));
} else {
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_minus));
setTxColour(ThemeHelper.getThemedColor(ctx, R.attr.negativeColor));
}
Set<String> destinations = new HashSet<>();
StringBuilder sb = new StringBuilder();
@@ -307,37 +315,49 @@ public class TxFragment extends Fragment {
tvTxTransfers.setText(sb.toString());
tvDestination.setText(dstSb.toString());
showBtcInfo();
showLock();
}
private void showLock() {
llWarning.setVisibility(View.GONE);
if (info.unlockTime == 0) return;
final long blockheight = activityCallback.getDaemonHeight();
final long blocks = info.unlockTime - blockheight;
final double unlockDays = blocks / (30. * 24);
if (unlockDays > 0) {
llWarning.setVisibility(View.VISIBLE);
tvWarning.setText(getString(R.string.tx_locked, info.unlockTime, blocks, unlockDays));
}
}
@SuppressLint("SetTextI18n")
void showBtcInfo() {
if (userNotes.xmrtoKey != null) {
cvXmrTo.setVisibility(View.VISIBLE);
String key = userNotes.xmrtoKey;
if ("xmrto".equals(userNotes.xmrtoTag)) { // legacy xmr.to service :(
key = "xmrto-" + key;
}
tvTxXmrToKey.setText(key);
ShiftService service = ShiftService.findWithTag(userNotes.xmrtoTag);
tvXmrToKeyLabel.setText(getString(R.string.label_send_btc_xmrto_key_lb, service.getLabel()));
if (service.getIconId() == 0)
tvXmrToLogo.setVisibility(View.GONE);
else
tvXmrToLogo.setImageResource(service.getLogoId());
tvTxXmrToKey.setText(userNotes.xmrtoKey);
tvDestinationBtc.setText(userNotes.xmrtoDestination);
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " " + userNotes.xmrtoCurrency);
switch (userNotes.xmrtoTag) {
case "xmrto":
tvXmrToSupport.setVisibility(View.GONE);
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
tvXmrToLogo.setImageResource(R.drawable.ic_xmrto_logo);
break;
case "side": // defaults in layout - just add underline
tvXmrToSupport.setPaintFlags(tvXmrToSupport.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
tvXmrToSupport.setOnClickListener(v -> {
Uri uri = Uri.parse("https://sideshift.ai/orders/" + userNotes.xmrtoKey);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
});
break;
default:
tvXmrToSupport.setVisibility(View.GONE);
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
tvXmrToLogo.setVisibility(View.GONE);
ShiftApi shiftApi = service.getShiftApi();
if (shiftApi != null) {
tvXmrToSupport.setText(getString(R.string.label_send_btc_xmrto_info, service.getLabel()));
tvXmrToSupport.setPaintFlags(tvXmrToSupport.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
tvXmrToSupport.setOnClickListener(v -> {
startActivity(new Intent(Intent.ACTION_VIEW, shiftApi.getQueryOrderUri(userNotes.xmrtoKey)));
});
} else {
tvXmrToSupport.setVisibility(View.GONE);
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
}
} else {
cvXmrTo.setVisibility(View.GONE);
@@ -348,15 +368,13 @@ public class TxFragment extends Fragment {
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
final MaterialContainerTransform transform = new MaterialContainerTransform();
transform.setDrawingViewId(R.id.fragment_container);
transform.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
transform.setAllContainerColors(ThemeHelper.getThemedColor(getContext(), android.R.attr.colorBackground));
Transition transform = TransitionInflater.from(requireContext())
.inflateTransition(R.transition.details);
setSharedElementEnterTransition(transform);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.tx_info_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@@ -380,10 +398,11 @@ public class TxFragment extends Fragment {
void showSubaddress(View view, final int subaddressIndex);
long getDaemonHeight();
}
@Override
public void onAttach(Context context) {
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof TxFragment.Listener) {
this.activityCallback = (TxFragment.Listener) context;

File diff suppressed because it is too large Load Diff

View File

@@ -43,7 +43,6 @@ import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import com.github.brnunes.swipeablerecyclerview.SwipeableRecyclerViewTouchListener;
import com.google.android.material.transition.MaterialElevationScale;
import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.Wallet;
@@ -52,6 +51,8 @@ import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.ServiceHelper;
import com.m2049r.xmrwallet.util.StickyFiatHelper;
import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.text.NumberFormat;
@@ -62,7 +63,7 @@ import java.util.List;
import timber.log.Timber;
public class WalletFragment extends Fragment
implements TransactionInfoAdapter.OnInteractionListener {
implements TransactionInfoAdapter.Listener {
private TransactionInfoAdapter adapter;
private final NumberFormat formatter = NumberFormat.getInstance();
@@ -111,15 +112,16 @@ public class WalletFragment extends Fragment
llBalance = view.findViewById(R.id.llBalance);
flExchange = view.findViewById(R.id.flExchange);
((ProgressBar) view.findViewById(R.id.pbExchange)).getIndeterminateDrawable().
setColorFilter(getResources().getColor(R.color.progress_circle),
setColorFilter(
ThemeHelper.getThemedColor(requireContext(), com.google.android.material.R.attr.colorPrimaryVariant),
android.graphics.PorterDuff.Mode.MULTIPLY);
tvProgress = view.findViewById(R.id.tvProgress);
pbProgress = view.findViewById(R.id.pbProgress);
tvBalance = view.findViewById(R.id.tvBalance);
showBalance(Helper.getDisplayAmount(0));
showBalance();
tvUnconfirmedAmount = view.findViewById(R.id.tvUnconfirmedAmount);
showUnconfirmed(0);
showUnconfirmed();
ivSynced = view.findViewById(R.id.ivSynced);
sCurrency = view.findViewById(R.id.sCurrency);
@@ -130,6 +132,7 @@ public class WalletFragment extends Fragment
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner_balance, currencies);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
sCurrency.setAdapter(spinnerAdapter);
StickyFiatHelper.setPreferredCurrencyPosition(sCurrency);
bSend = view.findViewById(R.id.bSend);
bReceive = view.findViewById(R.id.bReceive);
@@ -181,6 +184,7 @@ public class WalletFragment extends Fragment
sCurrency.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
StickyFiatHelper.setPreferredFiatSymbol(requireContext(), (String) sCurrency.getSelectedItem());
refreshBalance();
}
@@ -202,14 +206,20 @@ public class WalletFragment extends Fragment
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
postponeEnterTransition();
view.getViewTreeObserver().addOnPreDrawListener(() -> {
startPostponedEnterTransition();
return true;
});
}
void showBalance(String balance) {
String amountToString(double amount) {
if (!Helper.BASE_CRYPTO.equals(balanceCurrency)) { // not XMR
double amountB = amount * balanceRate;
return Helper.getFormattedAmount(amountB, false);
} else { // XMR
return Helper.getFormattedAmount(amount, true);
}
}
void showBalance() {
double amountA = Helper.getDecimalAmount(unlockedBalance).doubleValue();
String balance = amountToString(amountA);
tvBalance.setText(balance);
final boolean streetMode = activityCallback.isStreetMode();
if (!streetMode) {
@@ -222,13 +232,14 @@ public class WalletFragment extends Fragment
setStreetModeBackground(streetMode);
}
void showUnconfirmed(double unconfirmedAmount) {
void showUnconfirmed() {
double unconfirmedAmount = Helper.getDecimalAmount(balance - unlockedBalance).doubleValue();
if (activityCallback.isStreetMode() || unconfirmedAmount == 0) {
tvUnconfirmedAmount.setText(null);
tvUnconfirmedAmount.setVisibility(View.GONE);
} else {
String unconfirmed = Helper.getFormattedAmount(unconfirmedAmount, true);
tvUnconfirmedAmount.setText(getResources().getString(R.string.xmr_unconfirmed_amount, unconfirmed));
String unconfirmed = amountToString(unconfirmedAmount);
tvUnconfirmedAmount.setText(getResources().getString(R.string.xmr_unconfirmed_amount, unconfirmed, balanceCurrency));
tvUnconfirmedAmount.setVisibility(View.VISIBLE);
}
}
@@ -236,15 +247,8 @@ public class WalletFragment extends Fragment
void updateBalance() {
if (isExchanging) return; // wait for exchange to finish - it will fire this itself then.
// at this point selection is XMR in case of error
String displayB;
double amountA = Helper.getDecimalAmount(unlockedBalance).doubleValue();
if (!Helper.BASE_CRYPTO.equals(balanceCurrency)) { // not XMR
double amountB = amountA * balanceRate;
displayB = Helper.getFormattedAmount(amountB, false);
} else { // XMR
displayB = Helper.getFormattedAmount(amountA, true);
}
showBalance(displayB);
showBalance();
showUnconfirmed();
}
String balanceCurrency = Helper.BASE_CRYPTO;
@@ -253,11 +257,11 @@ public class WalletFragment extends Fragment
private final ExchangeApi exchangeApi = ServiceHelper.getExchangeApi();
void refreshBalance() {
double unconfirmedXmr = Helper.getDecimalAmount(balance - unlockedBalance).doubleValue();
showUnconfirmed(unconfirmedXmr);
if (sCurrency.getSelectedItemPosition() == 0) { // XMR
double amountXmr = Helper.getDecimalAmount(unlockedBalance).doubleValue();
showBalance(Helper.getFormattedAmount(amountXmr, true));
balanceCurrency = Helper.BASE_CRYPTO;
balanceRate = 1.0;
showBalance();
showUnconfirmed();
} else { // not XMR
String currency = (String) sCurrency.getSelectedItem();
Timber.d(currency);
@@ -302,8 +306,7 @@ public class WalletFragment extends Fragment
public void exchangeFailed() {
sCurrency.setSelection(0, true); // default to XMR
double amountXmr = Helper.getDecimalAmount(unlockedBalance).doubleValue();
showBalance(Helper.getFormattedAmount(amountXmr, true));
showBalance();
hideExchanging();
}
@@ -315,13 +318,7 @@ public class WalletFragment extends Fragment
balanceCurrency = Helper.BASE_CRYPTO;
balanceRate = 1.0;
} else {
int spinnerPosition = ((ArrayAdapter) sCurrency.getAdapter()).getPosition(exchangeRate.getQuoteCurrency());
if (spinnerPosition < 0) { // requested currency not in list
Timber.e("Requested currency not in list %s", exchangeRate.getQuoteCurrency());
sCurrency.setSelection(0, true);
} else {
sCurrency.setSelection(spinnerPosition, true);
}
StickyFiatHelper.setCurrencyPosition(sCurrency, exchangeRate.getQuoteCurrency());
balanceCurrency = exchangeRate.getQuoteCurrency();
balanceRate = exchangeRate.getRate();
}
@@ -331,19 +328,9 @@ public class WalletFragment extends Fragment
// Callbacks from TransactionInfoAdapter
@Override
public void onInteraction(final View view, final TransactionInfo infoItem) {
final MaterialElevationScale exitTransition = new MaterialElevationScale(false);
exitTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
setExitTransition(exitTransition);
final MaterialElevationScale reenterTransition = new MaterialElevationScale(true);
reenterTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
setReenterTransition(reenterTransition);
activityCallback.onTxDetailsRequest(view, infoItem);
}
// called from activity
// if account index has changed scroll to top?
private int accountIndex = 0;
@@ -457,12 +444,13 @@ public class WalletFragment extends Fragment
String sync;
if (!activityCallback.hasBoundService())
throw new IllegalStateException("WalletService not bound.");
ivSynced.setVisibility(View.GONE);
Wallet.ConnectionStatus daemonConnected = activityCallback.getConnectionStatus();
if (daemonConnected == Wallet.ConnectionStatus.ConnectionStatus_Connected) {
if (!wallet.isSynchronized()) {
long daemonHeight = activityCallback.getDaemonHeight();
long walletHeight = wallet.getBlockChainHeight();
long n = daemonHeight - walletHeight;
final long daemonHeight = getDaemonHeight();
final long walletHeight = wallet.getBlockChainHeight();
final long n = daemonHeight - walletHeight;
sync = getString(R.string.status_syncing) + " " + formatter.format(n) + " " + getString(R.string.status_remaining);
if (firstBlock == 0) {
firstBlock = walletHeight;
@@ -470,7 +458,6 @@ public class WalletFragment extends Fragment
int x = 100 - Math.round(100f * n / (1f * daemonHeight - firstBlock));
if (x == 0) x = 101; // indeterminate
setProgress(x);
ivSynced.setVisibility(View.GONE);
} else {
sync = getString(R.string.status_synced) + " " + formatter.format(wallet.getBlockChainHeight());
ivSynced.setVisibility(View.VISIBLE);
@@ -536,8 +523,6 @@ public class WalletFragment extends Fragment
@Override
public void onResume() {
super.onResume();
setExitTransition(null);
setReenterTransition(null);
Timber.d("onResume()");
activityCallback.setTitle(walletTitle, walletSubtitle);
activityCallback.setToolbarButton(Toolbar.BUTTON_NONE);
@@ -572,4 +557,9 @@ public class WalletFragment extends Fragment
} else
ivStreetGunther.setImageDrawable(null);
}
@Override
public long getDaemonHeight() {
return activityCallback.getDaemonHeight();
}
}

View File

@@ -35,7 +35,6 @@ public class XmrWalletApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
}

View File

@@ -24,43 +24,38 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.Getter;
import lombok.ToString;
import timber.log.Timber;
@Getter
@ToString
public class BarcodeData {
public enum Security {
NORMAL,
OA_NO_DNSSEC,
OA_DNSSEC
}
final public Crypto asset;
final public List<Crypto> ambiguousAssets;
final public String address;
final public String addressName;
final public String amount;
final public String description;
final public Security security;
final private Set<Crypto> possibleAssets = new HashSet<>();
private String address = null;
private String addressName = null;
private String amount = null;
private String description = null;
private Security security = null;
public BarcodeData(List<Crypto> assets, String address) {
if (assets.isEmpty())
throw new IllegalArgumentException("no assets specified");
this.addressName = null;
this.description = null;
this.amount = null;
this.security = Security.NORMAL;
security = Security.NORMAL;
this.address = address;
if (assets.size() == 1) {
this.asset = assets.get(0);
this.ambiguousAssets = null;
} else {
this.asset = null;
this.ambiguousAssets = assets;
}
possibleAssets.addAll(assets);
}
public BarcodeData(Crypto asset, String address, String description, String amount) {
@@ -68,8 +63,7 @@ public class BarcodeData {
}
public BarcodeData(Crypto asset, String address, String addressName, String description, String amount, Security security) {
this.ambiguousAssets = null;
this.asset = asset;
possibleAssets.add(asset);
this.address = address;
this.addressName = addressName;
this.description = description;
@@ -82,7 +76,7 @@ public class BarcodeData {
}
public String getUriString() {
if (asset != Crypto.XMR) throw new IllegalStateException("We can only do XMR stuff!");
if (getAsset() != Crypto.XMR) throw new IllegalStateException("We can only do XMR stuff!");
StringBuilder sb = new StringBuilder();
sb.append(Crypto.XMR.getUriScheme())
.append(':')
@@ -227,6 +221,20 @@ public class BarcodeData {
}
public boolean isAmbiguous() {
return ambiguousAssets != null;
return possibleAssets.size() > 1;
}
public Crypto getAsset() {
if (possibleAssets.size() == 1) {
return possibleAssets.iterator().next();
} else {
return null;
}
}
// return true if we still have possible assets
public boolean filter(Set<Crypto> assets) {
possibleAssets.retainAll(assets);
return !possibleAssets.isEmpty();
}
}

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