1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-05 23:53:33 +00:00

Compare commits

..

332 Commits

Author SHA1 Message Date
Kyle Spearrin
bae7d1fc1d version bump for android 2017-07-28 17:27:24 -04:00
Kyle Spearrin
e4d9dfc128 added broadcast receiver for when android updated 2017-07-28 17:21:39 -04:00
Kyle Spearrin
7490ba3179 copy update for itunes store, remove "free" 2017-07-26 22:05:26 -04:00
Kyle Spearrin
45da12ad55 catch exception when cannot create temp cam file 2017-07-26 16:19:58 -04:00
Kyle Spearrin
75f99bf899 Merge branch 'master' of github.com:bitwarden/mobile 2017-07-26 14:46:06 -04:00
Kyle Spearrin
bd2b1cc166 New Crowdin translations (#106)
* New translations AppResources.resx (Romanian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations copy.resx (Romanian)

* New translations copy.resx (Romanian)

* New translations copy.resx (Russian)

* New translations AppResources.resx (Russian)

* New translations copy.resx (Romanian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations copy.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations AppResources.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Russian)

* New translations copy.resx (Russian)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations AppResources.resx (Thai)

* New translations copy.resx (Thai)

* New translations copy.resx (Thai)

* New translations copy.resx (Thai)

* New translations AppResources.resx (Swedish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations AppResources.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations AppResources.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations AppResources.resx (Japanese)

* New translations copy.resx (Italian)

* New translations AppResources.resx (Czech)

* New translations copy.resx (Croatian)

* New translations copy.resx (Croatian)

* New translations copy.resx (Czech)

* New translations copy.resx (Czech)

* New translations AppResources.resx (Finnish)

* New translations copy.resx (Czech)

* New translations copy.resx (Croatian)

* New translations AppResources.resx (Croatian)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations AppResources.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Finnish)

* New translations copy.resx (Finnish)

* New translations copy.resx (Indonesian)

* New translations copy.resx (Indonesian)

* New translations AppResources.resx (Indonesian)

* New translations copy.resx (Indonesian)

* New translations AppResources.resx (Italian)

* New translations copy.resx (Italian)

* New translations copy.resx (Italian)

* New translations copy.resx (Hindi)

* New translations copy.resx (Hindi)

* New translations copy.resx (French)

* New translations AppResources.resx (French)

* New translations copy.resx (Finnish)

* New translations copy.resx (French)

* New translations copy.resx (French)

* New translations copy.resx (Hindi)

* New translations AppResources.resx (Hindi)

* New translations AppResources.resx (Chinese Simplified)
2017-07-26 14:44:46 -04:00
Kyle Spearrin
5fa8d64994 fix preview identity url 2017-07-26 10:48:04 -04:00
Kyle Spearrin
034957b556 version bump 2017-07-25 22:25:13 -04:00
Kyle Spearrin
a9d7c73b04 Revert "remove --nodevcodeshare"
This reverts commit b68a94c0e5.
2017-07-25 16:33:11 -04:00
Kyle Spearrin
b09fe05fe6 Revert "added forms reference to extension"
This reverts commit f239868299.
2017-07-25 16:32:52 -04:00
Kyle Spearrin
ed146549ef Revert "dialogs package installed to extension"
This reverts commit 83047558d5.
2017-07-25 16:32:40 -04:00
Kyle Spearrin
86c11db1a1 Revert "added push package to extension"
This reverts commit 5969e2d7ed.
2017-07-25 16:32:31 -04:00
Kyle Spearrin
580cd57433 Revert "added ffimage lib to extension"
This reverts commit de46c9ee36.
2017-07-25 16:32:23 -04:00
Kyle Spearrin
9854ce82bd Revert "zxing lib to extension"
This reverts commit f8bd7c2e64.
2017-07-25 16:32:15 -04:00
Kyle Spearrin
af11df97f5 Revert "update build package"
This reverts commit ef7fa5363a.
2017-07-25 16:32:07 -04:00
Kyle Spearrin
9b4e664908 Revert "--nodevcodeshare"
This reverts commit 567e1ee116.
2017-07-25 16:31:58 -04:00
Kyle Spearrin
567e1ee116 --nodevcodeshare 2017-07-25 12:30:20 -04:00
Kyle Spearrin
ef7fa5363a update build package 2017-07-25 11:16:47 -04:00
Kyle Spearrin
f8bd7c2e64 zxing lib to extension 2017-07-25 11:08:31 -04:00
Kyle Spearrin
de46c9ee36 added ffimage lib to extension 2017-07-25 11:01:19 -04:00
Kyle Spearrin
5969e2d7ed added push package to extension 2017-07-25 10:55:26 -04:00
Kyle Spearrin
83047558d5 dialogs package installed to extension 2017-07-25 10:48:07 -04:00
Kyle Spearrin
f239868299 added forms reference to extension 2017-07-25 10:40:15 -04:00
Kyle Spearrin
e4b962a3a6 update launch screen 2017-07-25 10:37:48 -04:00
Kyle Spearrin
b68a94c0e5 remove --nodevcodeshare 2017-07-25 10:37:39 -04:00
Kyle Spearrin
ec53ca8423 missing ioc registration 2017-07-25 09:52:07 -04:00
Kyle Spearrin
1ba0729e34 cleanup 2017-07-25 08:51:55 -04:00
Kyle Spearrin
73425c0052 debug check on screenshot protection 2017-07-24 15:04:31 -04:00
Kyle Spearrin
679859fb37 stop timer when page disappears. autofocus camera. 2017-07-24 12:33:07 -04:00
Kyle Spearrin
dbdc660464 properly init events when provider changes 2017-07-24 12:33:07 -04:00
Kyle Spearrin
aa22e7e952 New Crowdin translations (#105)
* New translations AppResources.resx (Romanian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations copy.resx (Romanian)

* New translations copy.resx (Romanian)

* New translations copy.resx (Russian)

* New translations AppResources.resx (Russian)

* New translations copy.resx (Romanian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations copy.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations AppResources.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Russian)

* New translations copy.resx (Russian)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations AppResources.resx (Thai)

* New translations copy.resx (Thai)

* New translations copy.resx (Thai)

* New translations copy.resx (Thai)

* New translations AppResources.resx (Swedish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations AppResources.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations AppResources.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations AppResources.resx (Japanese)

* New translations copy.resx (Italian)

* New translations AppResources.resx (Czech)

* New translations copy.resx (Croatian)

* New translations copy.resx (Croatian)

* New translations copy.resx (Czech)

* New translations copy.resx (Czech)

* New translations AppResources.resx (Finnish)

* New translations copy.resx (Czech)

* New translations copy.resx (Croatian)

* New translations AppResources.resx (Croatian)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations AppResources.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Finnish)

* New translations copy.resx (Finnish)

* New translations copy.resx (Indonesian)

* New translations copy.resx (Indonesian)

* New translations AppResources.resx (Indonesian)

* New translations copy.resx (Indonesian)

* New translations AppResources.resx (Italian)

* New translations copy.resx (Italian)

* New translations copy.resx (Italian)

* New translations copy.resx (Hindi)

* New translations copy.resx (Hindi)

* New translations copy.resx (French)

* New translations AppResources.resx (French)

* New translations copy.resx (Finnish)

* New translations copy.resx (French)

* New translations copy.resx (French)

* New translations copy.resx (Hindi)

* New translations AppResources.resx (Hindi)

* New translations AppResources.resx (Chinese Simplified)
2017-07-24 10:55:52 -04:00
Kyle Spearrin
b920e7e95c attachment updates 2017-07-24 10:34:22 -04:00
Kyle Spearrin
d14b23ca82 null check for mac 2017-07-23 00:10:32 -04:00
Kyle Spearrin
4e8f69f692 paperclip icon for attachments in listing 2017-07-23 00:09:24 -04:00
Kyle Spearrin
c96cf2b0e5 update samsung browser url view id 2017-07-22 23:31:38 -04:00
Kyle Spearrin
4921cfb593 mac is optional 2017-07-22 23:22:21 -04:00
Kyle Spearrin
395545f7b1 Add support for camera for android choose file 2017-07-22 21:06:53 -04:00
Kyle Spearrin
f9d336a3a6 attachments page with upload/delete 2017-07-22 15:38:08 -04:00
Kyle Spearrin
b32603b472 premium required for attachment download 2017-07-21 17:21:04 -04:00
Kyle Spearrin
1124c48c8d copy totp code on autofill 2017-07-21 11:39:22 -04:00
Kyle Spearrin
98e429505c get file data from document picker 2017-07-15 10:08:19 -04:00
Kyle Spearrin
d0b616ba24 select file for document picker 2017-07-15 01:09:30 -04:00
Kyle Spearrin
dac4ffcb98 new languages 2017-07-15 01:09:30 -04:00
Kyle Spearrin
680310cf70 New Crowdin translations (#103)
* New translations AppResources.resx (Romanian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations copy.resx (Romanian)

* New translations copy.resx (Romanian)

* New translations copy.resx (Russian)

* New translations AppResources.resx (Russian)

* New translations copy.resx (Romanian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations copy.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations AppResources.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Russian)

* New translations copy.resx (Russian)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations AppResources.resx (Thai)

* New translations copy.resx (Thai)

* New translations copy.resx (Thai)

* New translations copy.resx (Thai)

* New translations AppResources.resx (Swedish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations AppResources.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations AppResources.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations AppResources.resx (Japanese)

* New translations copy.resx (Italian)

* New translations AppResources.resx (Czech)

* New translations copy.resx (Croatian)

* New translations copy.resx (Croatian)

* New translations copy.resx (Czech)

* New translations copy.resx (Czech)

* New translations AppResources.resx (Finnish)

* New translations copy.resx (Czech)

* New translations copy.resx (Croatian)

* New translations AppResources.resx (Croatian)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations AppResources.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Finnish)

* New translations copy.resx (Finnish)

* New translations copy.resx (Indonesian)

* New translations copy.resx (Indonesian)

* New translations AppResources.resx (Indonesian)

* New translations copy.resx (Indonesian)

* New translations AppResources.resx (Italian)

* New translations copy.resx (Italian)

* New translations copy.resx (Italian)

* New translations copy.resx (Hindi)

* New translations copy.resx (Hindi)

* New translations copy.resx (French)

* New translations AppResources.resx (French)

* New translations copy.resx (Finnish)

* New translations copy.resx (French)

* New translations copy.resx (French)

* New translations copy.resx (Hindi)

* New translations AppResources.resx (Hindi)

* New translations AppResources.resx (Chinese Simplified)
2017-07-14 16:08:26 -04:00
Kyle Spearrin
67ff82810f min width on file size 2017-07-13 18:08:16 -04:00
Kyle Spearrin
87e71ea860 QR code scanning for authenticator keys 2017-07-13 17:23:18 -04:00
Kyle Spearrin
26c110291e totp code generation on view login 2017-07-13 14:44:02 -04:00
Kyle Spearrin
9879f074b4 decrypt with org id 2017-07-13 12:08:48 -04:00
Kyle Spearrin
65168c71c0 add/edit login totp key 2017-07-13 11:52:24 -04:00
Kyle Spearrin
4c4996ee2a dont add empty note section first 2017-07-13 11:16:00 -04:00
Kyle Spearrin
e0c67f87b0 only clear cache if it hasnt been done in a while 2017-07-13 11:11:04 -04:00
Kyle Spearrin
352c8ee867 clear cache and open file on iOS 2017-07-13 10:51:45 -04:00
Kyle Spearrin
fe5cc1f8f3 conditions around opening file 2017-07-13 09:01:00 -04:00
Kyle Spearrin
eec4be1845 label right detail cell for attachments 2017-07-13 00:02:37 -04:00
Kyle Spearrin
2f86b5c7b0 show indicator when downloading attachment 2017-07-12 23:45:05 -04:00
Kyle Spearrin
0d672c4f99 sync attachment removals 2017-07-12 23:36:27 -04:00
Kyle Spearrin
ac3fdbc2cd download, decrypt and open attachment files 2017-07-12 23:09:44 -04:00
Kyle Spearrin
0a7ad44d23 sync and display attachments on view login 2017-07-12 16:23:24 -04:00
Kyle Spearrin
18a86d3f12 model adjustments 2017-07-12 15:16:36 -04:00
Kyle Spearrin
665e66a9a6 prod url for duo connector 2017-06-29 15:03:48 -04:00
Kyle Spearrin
06dc4117c7 ios webview fix and language updates 2017-06-29 14:55:13 -04:00
Kyle Spearrin
2651afcef0 2fa corrections 2017-06-29 12:42:59 -04:00
Kyle Spearrin
ce4d828380 l10n for 2fa and dismiss keyboard message 2017-06-29 12:11:07 -04:00
Kyle Spearrin
74fba486bd two-factor other methods switching and send email 2017-06-29 11:22:06 -04:00
Kyle Spearrin
56075cb7d9 read yubikey and log in 2017-06-28 22:24:04 -04:00
Kyle Spearrin
d71bc775d5 hybrid web view and duo html/js 2017-06-28 13:10:47 -04:00
Kyle Spearrin
45c5801538 detect nfc enabled 2017-06-28 08:27:06 -04:00
Kyle Spearrin
cf41b524b0 read yubikey otp via nfc 2017-06-27 23:33:13 -04:00
Kyle Spearrin
e2a3e55a17 setup 2fa methods page 2017-06-27 17:10:40 -04:00
Kyle Spearrin
ae35bd2047 encode email for token service key 2017-06-27 16:51:16 -04:00
Kyle Spearrin
2f0ca6f7c0 user specific remember two factor 2017-06-27 16:45:12 -04:00
Kyle Spearrin
37428c01dd remeber two factor token 2017-06-27 16:35:29 -04:00
Kyle Spearrin
4116d95a3e refactors for new 2fa flows 2017-06-27 16:18:32 -04:00
Kyle Spearrin
35ae2b783f undo debugging work 2017-06-27 15:00:21 -04:00
Kyle Spearrin
8a24a6d192 Revert "generate facet id"
This reverts commit 19374a5df4.
2017-06-27 13:48:57 -04:00
Kyle Spearrin
19374a5df4 generate facet id 2017-06-27 12:53:20 -04:00
Kyle Spearrin
12da6fbd18 launch for main activity and catch exceptions 2017-06-23 23:21:39 -04:00
Kyle Spearrin
573ff15925 remove mail sends for crash reports 2017-06-22 21:54:57 -04:00
Kyle Spearrin
1b2abbe321 no param needed 2017-06-22 21:53:44 -04:00
Kyle Spearrin
4a03da6b96 fallback to old KeyStoreStorageService 2017-06-22 21:53:32 -04:00
Kyle Spearrin
cf3998942f save crash file to external storage instead 2017-06-22 15:33:37 -04:00
Kyle Spearrin
0c71f783fc make exceptiond available 2017-06-22 09:42:32 -04:00
Kyle Spearrin
d30b30b24f turn crash emails back on for testing 2017-06-22 09:39:02 -04:00
Kyle Spearrin
7823ec3fc8 hmac check on rsa decrypt 2017-06-19 11:57:37 -04:00
Kyle Spearrin
1e5883f028 clear settings via format as prefix 2017-06-12 13:06:46 -04:00
Kyle Spearrin
33c3cf4c4f just use SettingsFormat 2017-06-12 12:59:17 -04:00
Kyle Spearrin
f41ace4d7c clear settings for prefix when key is generated 2017-06-12 12:56:18 -04:00
Kyle Spearrin
65d2d45a82 manually set validity. no more crash emails 2017-06-12 11:51:43 -04:00
Kyle Spearrin
47ca483459 catch decrypt migrate exceptions 2017-06-12 10:45:57 -04:00
Kyle Spearrin
ee759af078 version bump 2017-06-10 22:44:35 -04:00
Kyle Spearrin
872037cf4d New Crowdin translations (#99)
* New translations copy.resx (Indonesian)

* New translations copy.resx (Indonesian)

* New translations AppResources.resx (Indonesian)

* New translations copy.resx (Indonesian)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations AppResources.resx (Portuguese)

* New translations copy.resx (Japanese)

* New translations copy.resx (Hindi)

* New translations copy.resx (Hindi)

* New translations copy.resx (Hindi)

* New translations AppResources.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations copy.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations AppResources.resx (Russian)

* New translations copy.resx (Thai)

* New translations copy.resx (Russian)

* New translations copy.resx (Russian)

* New translations AppResources.resx (Swedish)

* New translations copy.resx (Russian)

* New translations copy.resx (Thai)

* New translations copy.resx (Thai)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations copy.resx (Slovak)

* New translations copy.resx (Portuguese, Brazilian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Thai)

* New translations copy.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Hindi)

* New translations AppResources.resx (Slovak)

* New translations copy.resx (French)

* New translations copy.resx (Finnish)

* New translations AppResources.resx (Italian)

* New translations AppResources.resx (Chinese Traditional)

* New translations copy.resx (Swedish)

* New translations copy.resx (French)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Finnish)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations AppResources.resx (Czech)

* New translations copy.resx (Czech)

* New translations copy.resx (Czech)

* New translations copy.resx (Czech)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Italian)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Finnish)

* New translations copy.resx (French)

* New translations copy.resx (Italian)

* New translations copy.resx (Italian)

* New translations AppResources.resx (Chinese Simplified)
2017-06-10 22:42:53 -04:00
Kyle Spearrin
6aaa083157 use aes key in keystore on "new" android. migrate. 2017-06-10 22:18:34 -04:00
Kyle Spearrin
6a88524f8e rename to AndroidKeyStoreStorageService 2017-06-10 10:52:13 -04:00
Kyle Spearrin
82d93d2602 move variables in scope 2017-06-09 22:19:04 -04:00
Kyle Spearrin
d62037ef6a apparently manifest merge doesn't work in Xamarin 2017-06-09 21:49:20 -04:00
Kyle Spearrin
7314b5a339 manually remove contacts and account permissions 2017-06-09 16:18:21 -04:00
Kyle Spearrin
62bc230521 New Crowdin translations (#98)
* New translations AppResources.resx (Indonesian)

* New translations AppResources.resx (Portuguese)

* New translations copy.resx (Japanese)

* New translations copy.resx (Indonesian)

* New translations copy.resx (Indonesian)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Indonesian)

* New translations copy.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations AppResources.resx (Hindi)

* New translations AppResources.resx (Slovak)

* New translations copy.resx (Hindi)

* New translations copy.resx (Hindi)

* New translations AppResources.resx (Japanese)

* New translations copy.resx (Hindi)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Thai)

* New translations copy.resx (Thai)

* New translations copy.resx (Thai)

* New translations copy.resx (Russian)

* New translations copy.resx (Russian)

* New translations copy.resx (Russian)

* New translations AppResources.resx (Thai)

* New translations copy.resx (Portuguese, Brazilian)

* New translations copy.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Russian)

* New translations copy.resx (Czech)

* New translations copy.resx (Finnish)

* New translations copy.resx (Swedish)

* New translations copy.resx (French)

* New translations AppResources.resx (Italian)

* New translations copy.resx (French)

* New translations AppResources.resx (Chinese Traditional)

* New translations copy.resx (Swedish)

* New translations AppResources.resx (Swedish)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Finnish)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations AppResources.resx (Czech)

* New translations copy.resx (Czech)

* New translations copy.resx (Czech)

* New translations copy.resx (Italian)

* New translations copy.resx (Italian)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Finnish)

* New translations copy.resx (Italian)

* New translations copy.resx (French)

* New translations AppResources.resx (Chinese Simplified)
2017-06-09 10:02:55 -04:00
Kyle Spearrin
3e0d34d148 version bump. deprecate KeyStoreStorageService 2017-06-08 21:20:56 -04:00
Kyle Spearrin
aff1cc1cc3 fallback to "old" KeyStoreStorageService 2017-06-08 20:37:44 -04:00
Kyle Spearrin
6ddc7fa4cc version bump 2017-06-08 16:22:52 -04:00
Kyle Spearrin
957db1ec11 launch android app packages 2017-06-08 16:22:11 -04:00
Kyle Spearrin
ae806da3f1 ubdo debugging items 2017-06-08 15:57:07 -04:00
Kyle Spearrin
6a03c3e77d Do not show launch unless starts with HTTP 2017-06-08 15:43:01 -04:00
Kyle Spearrin
72b18eadf3 do not implement UnhandledExceptionRaiser 2017-06-08 15:13:58 -04:00
Kyle Spearrin
67aa583709 disable screenshot blocking 2017-06-08 14:33:52 -04:00
Kyle Spearrin
21f3755e44 version bump. all unhandled crash report email 2017-06-08 12:44:16 -04:00
Kyle Spearrin
c9b6df846e version bump 2017-06-08 11:52:54 -04:00
Kyle Spearrin
7e23a8169f make crash email util 2017-06-08 11:52:29 -04:00
Kyle Spearrin
b139eadf0b KeyStoreBackedStorageService email crash reports 2017-06-08 11:43:26 -04:00
Kyle Spearrin
71ad648331 version bump 2017-06-07 22:07:25 -04:00
Kyle Spearrin
b8c7752356 oaep spec only for "new android" 2017-06-07 21:44:53 -04:00
Kyle Spearrin
b157f2085f android:allowBackup false 2017-06-07 15:52:40 -04:00
Kyle Spearrin
2fda7b8011 safety checks for popping modals 2017-06-07 10:19:56 -04:00
Kyle Spearrin
5b24d19630 remove unnecessary prop setting from gen spec 2017-06-07 00:43:46 -04:00
Kyle Spearrin
76652f6c6b KeyGenParameterSpec options added back. cleanup. 2017-06-07 00:10:31 -04:00
Kyle Spearrin
724ae51110 RSA/ECB/OAEPWithSHA-1AndMGF1Padding 2017-06-06 23:52:52 -04:00
Kyle Spearrin
1503124108 OAEPParameterSpec and provider specified 2017-06-06 23:27:57 -04:00
Kyle Spearrin
007125a071 include crypto providers with crash reprot email 2017-06-06 23:09:19 -04:00
Kyle Spearrin
b5f5b0b4aa sha1 digest 2017-06-06 22:53:14 -04:00
Kyle Spearrin
cbda59e547 switch to default oaep padding 2017-06-06 22:50:20 -04:00
Kyle Spearrin
a885e16049 email crash report for key store service 2017-06-06 22:04:54 -04:00
Kyle Spearrin
07eabad18d throw exceptions for testing 2017-06-06 08:10:07 -04:00
Kyle Spearrin
cf079a159f cleanup rsa encryption 2017-06-05 22:25:59 -04:00
Kyle Spearrin
93176989fd centralized crypto utils. keystore with rsa. 2017-06-05 21:04:19 -04:00
Kyle Spearrin
7a56141894 Update AndroidManifest.xml 2017-06-04 21:07:45 -04:00
Kyle Spearrin
31cc0ff6e9 version bump 2017-06-04 21:04:27 -04:00
Kyle Spearrin
8719b3eb64 revert back to KeyPairGeneratorSpec
KeyGenParameterSpec crashes
2017-06-02 21:58:20 -04:00
Kyle Spearrin
5347624455 credits for translators 2017-06-02 21:07:18 -04:00
Kyle Spearrin
25210339d9 detect new languages properly 2017-06-02 17:49:56 -04:00
Kyle Spearrin
72c7cd2536 show launch fixes 2017-06-02 17:09:09 -04:00
Kyle Spearrin
d018eeb376 key on login bug 2017-06-02 16:53:43 -04:00
Kyle Spearrin
d1424276bc new localization resources 2017-06-02 16:28:56 -04:00
Kyle Spearrin
cf9696a8cf New Crowdin translations (#94)
* New translations copy.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations AppResources.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations AppResources.resx (Portuguese)

* New translations copy.resx (Indonesian)

* New translations AppResources.resx (Indonesian)

* New translations copy.resx (Hindi)

* New translations copy.resx (Hindi)

* New translations copy.resx (Russian)

* New translations copy.resx (Russian)

* New translations copy.resx (Russian)

* New translations AppResources.resx (Slovak)

* New translations copy.resx (Hindi)

* New translations AppResources.resx (Hindi)

* New translations copy.resx (Indonesian)

* New translations copy.resx (Indonesian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Thai)

* New translations copy.resx (Thai)

* New translations copy.resx (Thai)

* New translations copy.resx (Thai)

* New translations copy.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations AppResources.resx (Russian)

* New translations copy.resx (Czech)

* New translations copy.resx (Finnish)

* New translations copy.resx (Swedish)

* New translations copy.resx (French)

* New translations AppResources.resx (Italian)

* New translations copy.resx (French)

* New translations AppResources.resx (Chinese Traditional)

* New translations copy.resx (Swedish)

* New translations AppResources.resx (Swedish)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Finnish)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations AppResources.resx (Czech)

* New translations copy.resx (Czech)

* New translations copy.resx (Czech)

* New translations copy.resx (Italian)

* New translations copy.resx (Italian)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Finnish)

* New translations copy.resx (Italian)

* New translations copy.resx (French)

* New translations AppResources.resx (Chinese Simplified)
2017-06-02 16:01:38 -04:00
Kyle Spearrin
a7cbe526e3 autofill typo and update layout timer 2017-06-02 15:45:09 -04:00
Kyle Spearrin
fe1c58ad27 use AuthenticationRequestConfiguration 2017-06-02 15:16:26 -04:00
Kyle Spearrin
753d01d413 update fingerprint libraries 2017-06-02 14:46:10 -04:00
Kyle Spearrin
feacb3ed14 remove unity from linker. proper http handlers 2017-06-02 11:21:17 -04:00
Kyle Spearrin
f5b1e6d03a GET_ACCOUNTS permission no longer needed for gcm 2017-06-01 22:27:28 -04:00
Kyle Spearrin
46fc2dd8d0 simple injector instead of unity 2017-06-01 14:50:17 -04:00
Kyle Spearrin
b063aae130 Revert "convert nuget references"
This reverts commit 655a729143.
2017-06-01 14:23:20 -04:00
Kyle Spearrin
655a729143 convert nuget references 2017-06-01 14:21:54 -04:00
Kyle Spearrin
5d2138b95e resolve push JIT 2017-06-01 11:29:08 -04:00
Kyle Spearrin
0b24cc29c1 check security stamp when syncing profile 2017-05-31 23:09:21 -04:00
Kyle Spearrin
2fa7b532b1 new enc key implementation 2017-05-31 22:47:19 -04:00
Kyle Spearrin
aa1ed52f64 notification closes after 30 seconds in app 2017-05-31 08:29:58 -04:00
Kyle Spearrin
29dddd7d62 help site articles 2017-05-30 14:52:57 -04:00
Kyle Spearrin
6ddbea316a add close button for ios 2017-05-30 14:46:45 -04:00
Kyle Spearrin
8da80f0710 fix bug with button binding 2017-05-30 14:24:29 -04:00
Kyle Spearrin
24382b8607 resolving warnings for obsolete APIs 2017-05-30 14:13:53 -04:00
Kyle Spearrin
65438e837d check android for event wireup 2017-05-30 13:23:45 -04:00
Kyle Spearrin
1a3cb8b623 Revert "update ioc"
This reverts commit 9ae734672b.
2017-05-30 12:01:05 -04:00
Kyle Spearrin
6bf4a0d09d entitlements for adhoc simulator 2017-05-30 12:00:42 -04:00
Kyle Spearrin
9ae734672b update ioc 2017-05-30 11:36:06 -04:00
Kyle Spearrin
cfa84476c8 Revert "added forms package to ios libs"
This reverts commit e92d091cb3.
2017-05-30 10:29:38 -04:00
Kyle Spearrin
5b0d160df4 Revert "packages that are in ios must also be in extension"
This reverts commit 13ff0846b1.
2017-05-30 10:29:28 -04:00
Kyle Spearrin
41f858eb04 Revert "adjusted references to google analytics"
This reverts commit 5895b37965.
2017-05-30 10:29:16 -04:00
Kyle Spearrin
c5753b898a Revert "reinstall GA and Download packages"
This reverts commit 337382b7e6.
2017-05-30 10:28:51 -04:00
Kyle Spearrin
c6810409c7 Revert "update download package"
This reverts commit 98718c0693.
2017-05-30 10:28:33 -04:00
Kyle Spearrin
f6c16ec53d min priority notification for persist option 2017-05-30 09:24:29 -04:00
Kyle Spearrin
84a6ee8cbf autofill fixes for password focus 2017-05-30 08:35:57 -04:00
Kyle Spearrin
e651a13980 compare with _lastNotificationUri 2017-05-30 08:18:56 -04:00
Kyle Spearrin
f494570725 back to marshmallow again 2017-05-29 21:52:12 -04:00
Kyle Spearrin
5955ca74d2 api level N 2017-05-29 20:53:02 -04:00
Kyle Spearrin
eb4fa8620d dont skip if there is something to autofill 2017-05-29 20:45:42 -04:00
Kyle Spearrin
34fe7dd6d1 back to marshmallow 2017-05-29 20:15:11 -04:00
Kyle Spearrin
4cbb3cb43c priority min 2017-05-29 19:41:52 -04:00
Kyle Spearrin
050acdf580 target android N framework 2017-05-29 19:40:27 -04:00
Kyle Spearrin
358da4051e comment out BuildVersionCodes N check 2017-05-29 11:43:55 -04:00
Kyle Spearrin
ffb51c1515 new autofill feature settings 2017-05-29 11:38:03 -04:00
Kyle Spearrin
72d4952812 support for org.codeaurora.swe.browser 2017-05-29 08:35:36 -04:00
Kyle Spearrin
04bf86c21d key store cleanup 2017-05-27 22:21:45 -04:00
Kyle Spearrin
d392dc82a1 settings features page setup with analytics cell 2017-05-27 14:52:37 -04:00
Kyle Spearrin
f7f4289614 keystore fixes 2017-05-27 12:23:35 -04:00
Kyle Spearrin
72f9951cb1 key names 2017-05-27 11:46:42 -04:00
Kyle Spearrin
8450f56093 fix project refs 2017-05-27 11:45:03 -04:00
Kyle Spearrin
cb2a25ad46 migrate and cleanup old key store 2017-05-27 11:42:31 -04:00
Kyle Spearrin
a028172cf6 New Crowdin translations (#91)
* New translations AppResources.resx (Japanese)

* New translations copy.resx (Hindi)

* New translations copy.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations AppResources.resx (Indonesian)

* New translations copy.resx (Japanese)

* New translations copy.resx (Hindi)

* New translations copy.resx (Hindi)

* New translations AppResources.resx (Russian)

* New translations copy.resx (Czech)

* New translations copy.resx (Russian)

* New translations copy.resx (Russian)

* New translations AppResources.resx (Hindi)

* New translations copy.resx (Russian)

* New translations copy.resx (Indonesian)

* New translations copy.resx (Indonesian)

* New translations copy.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations copy.resx (Slovak)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Portuguese)

* New translations copy.resx (Indonesian)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Czech)

* New translations copy.resx (Czech)

* New translations copy.resx (French)

* New translations AppResources.resx (Chinese Simplified)

* New translations copy.resx (Swedish)

* New translations AppResources.resx (Swedish)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Finnish)

* New translations copy.resx (French)

* New translations copy.resx (Finnish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Chinese Simplified)

* New translations AppResources.resx (Finnish)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations AppResources.resx (Czech)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Simplified)

* New translations AppResources.resx (French)

* New translations copy.resx (Finnish)

* New translations copy.resx (French)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (Spanish)
2017-05-27 11:29:48 -04:00
Kyle Spearrin
91aa48ac9a Update Crowdin configuration file 2017-05-27 11:27:24 -04:00
Kyle Spearrin
218320749f KeyStoreBackedStorageService 2017-05-27 01:05:12 -04:00
Kyle Spearrin
6a1ff56e7b support new push models 2017-05-26 22:55:48 -04:00
Kyle Spearrin
8f7c4951b8 help article for lost 2fa 2017-05-25 23:33:50 -04:00
Kyle Spearrin
6215a7d65e on demand password node scans for autofill 2017-05-25 23:16:48 -04:00
Kyle Spearrin
0e28b1ffe1 add id resources 2017-05-25 22:20:39 -04:00
Kyle Spearrin
7dd435d677 New Crowdin translations (#90)
* New translations copy.resx (Hindi)

* New translations AppResources.resx (Hindi)

* New translations copy.resx (Hindi)

* New translations copy.resx (Hindi)

* New translations copy.resx (Japanese)

* New translations AppResources.resx (Japanese)

* New translations copy.resx (Russian)

* New translations copy.resx (Russian)

* New translations copy.resx (Czech)

* New translations copy.resx (Czech)

* New translations copy.resx (Czech)

* New translations AppResources.resx (Russian)

* New translations copy.resx (Russian)

* New translations copy.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations AppResources.resx (Slovak)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Indonesian)

* New translations AppResources.resx (Indonesian)

* New translations copy.resx (Indonesian)

* New translations copy.resx (Indonesian)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Czech)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (French)

* New translations AppResources.resx (Chinese Simplified)

* New translations copy.resx (Swedish)

* New translations AppResources.resx (Swedish)

* New translations copy.resx (Finnish)

* New translations copy.resx (French)

* New translations copy.resx (Finnish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Finnish)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Finnish)

* New translations copy.resx (French)

* New translations AppResources.resx (Spanish)
2017-05-25 22:18:45 -04:00
Kyle Spearrin
7fd5209cdb always set backing key values 2017-05-25 12:50:39 -04:00
Kyle Spearrin
ed5b6962d7 include new resource files 2017-05-24 17:11:21 -04:00
Kyle Spearrin
98718c0693 update download package 2017-05-24 17:09:24 -04:00
Kyle Spearrin
f02588855a New Crowdin translations (#89)
* New translations copy.resx (French)

* New translations AppResources.resx (Chinese Traditional)

* New translations copy.resx (Finnish)

* New translations copy.resx (Chinese Simplified)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (French)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Finnish)

* New translations copy.resx (French)

* New translations AppResources.resx (Swedish)

* New translations copy.resx (Finnish)

* New translations copy.resx (Swedish)

* New translations copy.resx (French)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Spanish)

* New translations AppResources.resx (Czech)

* New translations copy.resx (Czech)

* New translations copy.resx (Czech)

* New translations copy.resx (Czech)

* New translations copy.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations AppResources.resx (Slovak)

* New translations copy.resx (Russian)

* New translations AppResources.resx (Russian)

* New translations copy.resx (Russian)

* New translations copy.resx (Russian)

* New translations AppResources.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations copy.resx (Hindi)

* New translations copy.resx (Hindi)

* New translations AppResources.resx (Hindi)

* New translations copy.resx (Hindi)

* New translations copy.resx (Chinese Traditional)

* New translations AppResources.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations AppResources.resx (Chinese Simplified)

* New translations copy.resx (French)

* New translations copy.resx (French)

* New translations copy.resx (Swedish)

* New translations AppResources.resx (Swedish)

* New translations copy.resx (Finnish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Finnish)

* New translations copy.resx (Chinese Simplified)

* New translations AppResources.resx (Chinese Traditional)

* New translations copy.resx (French)

* New translations copy.resx (Finnish)

* New translations copy.resx (Chinese Simplified)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (French)

* New translations copy.resx (Chinese Simplified)

* New translations AppResources.resx (Spanish)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Slovak)
2017-05-24 09:03:52 -04:00
Kyle Spearrin
c63841fdee Update Crowdin configuration file 2017-05-23 12:49:00 -04:00
Kyle Spearrin
b49b4601cd New Crowdin translations (#88)
* New translations copy.resx (French)

* New translations AppResources.resx (Chinese Traditional)

* New translations copy.resx (Finnish)

* New translations copy.resx (Chinese Simplified)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (French)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Finnish)

* New translations copy.resx (French)

* New translations AppResources.resx (Swedish)

* New translations copy.resx (Finnish)

* New translations copy.resx (Swedish)

* New translations copy.resx (French)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Spanish)
2017-05-23 10:29:59 -04:00
Kyle Spearrin
ed352d2d21 New Crowdin translations (#86)
* New translations copy.resx (Finnish)

* New translations copy.resx (Finnish)

* New translations AppResources.resx (Finnish)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations AppResources.resx (Chinese Traditional)

* New translations copy.resx (Chinese Simplified)

* New translations AppResources.resx (Chinese Simplified)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations AppResources.resx (Spanish)

* New translations copy.resx (French)

* New translations copy.resx (French)

* New translations copy.resx (Spanish)

* New translations AppResources.resx (Swedish)

* New translations copy.resx (Finnish)

* New translations copy.resx (French)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Swedish)

* New translations AppResources.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations AppResources.resx (French)

* New translations copy.resx (French)

* New translations copy.resx (French)

* New translations copy.resx (French)
2017-05-22 08:18:44 -04:00
Kyle Spearrin
dab56121a2 New Crowdin translations (#85)
* New translations copy.resx (Finnish)

* New translations copy.resx (Finnish)

* New translations AppResources.resx (Finnish)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations AppResources.resx (Chinese Traditional)

* New translations copy.resx (Chinese Simplified)

* New translations AppResources.resx (Chinese Simplified)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations AppResources.resx (Spanish)

* New translations copy.resx (French)

* New translations copy.resx (French)

* New translations copy.resx (Spanish)

* New translations AppResources.resx (Swedish)

* New translations copy.resx (Finnish)

* New translations copy.resx (French)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations AppResources.resx (French)
2017-05-21 13:36:42 -04:00
Kyle Spearrin
df02afaed9 Update appveyor.yml 2017-05-21 13:31:55 -04:00
Kyle Spearrin
e6fe121c1e Update appveyor.yml 2017-05-21 13:24:44 -04:00
Thomas
b37fbc1284 Create AppResources.zh-Hant.resx (#64) 2017-05-21 13:23:27 -04:00
Kyle Spearrin
f5ac554022 Update Crowdin configuration file 2017-05-21 13:21:16 -04:00
Kyle Spearrin
df76c82449 Update CONTRIBUTING.md 2017-05-20 23:29:39 -04:00
Kyle Spearrin
8f1f978800 Update CONTRIBUTING.md 2017-05-20 23:28:29 -04:00
Kyle Spearrin
bf3cc6690c Update README.md 2017-05-20 23:19:26 -04:00
Kyle Spearrin
20ba7eaaf3 New Crowdin translations (#79)
* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations AppResources.resx (Spanish)

* New translations copy.resx (French)

* New translations copy.resx (Spanish)

* New translations AppResources.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations copy.resx (French)

* New translations copy.resx (French)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations AppResources.resx (Finnish)

* New translations copy.resx (Finnish)

* New translations AppResources.resx (French)

* New translations copy.resx (Finnish)

* New translations copy.resx (Finnish)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (Chinese Simplified)
2017-05-20 22:58:50 -04:00
Kyle Spearrin
95708cd5ab Update Crowdin configuration file 2017-05-20 22:45:35 -04:00
Kyle Spearrin
fba58bba71 moving all store copy into resx files 2017-05-20 22:30:39 -04:00
Kyle Spearrin
bc6ff3e3bc prevent rapid clicking actions that crash app 2017-05-20 12:36:27 -04:00
Kyle Spearrin
3415be4c56 handle some crash cases 2017-05-20 12:36:27 -04:00
Kyle Spearrin
05d6f5d806 Update README.md 2017-05-19 13:39:37 -04:00
Kyle Spearrin
ed8266bb43 Update README.md 2017-05-19 13:23:52 -04:00
Kyle Spearrin
759f35ff9c Update SECURITY.md 2017-05-19 12:03:49 -04:00
Kyle Spearrin
bcac8f1599 Update SECURITY.md 2017-05-19 12:03:11 -04:00
Kyle Spearrin
0abf054825 null checks on _tableItems 2017-05-19 11:21:25 -04:00
Kyle Spearrin
825b76e28e en file not needed 2017-05-19 11:21:25 -04:00
Peter Karlsson
0a13ac142e Additional swedish strings (#78)
* Additional swedish strings

Additional translated strings for search, auto-fill and organisations.

* Added sneaky string

Sneaky analytics string in the middle added.
2017-05-18 20:18:13 -04:00
Kyle Spearrin
566e6634a6 Create SECURITY.md 2017-05-17 11:35:06 -04:00
Kyle Spearrin
4b888e6911 en resources file 2017-05-16 21:02:06 -04:00
Kyle Spearrin
0cfc89f574 en as first language 2017-05-16 10:01:36 -04:00
Kyle Spearrin
337382b7e6 reinstall GA and Download packages 2017-05-16 10:00:38 -04:00
Kyle Spearrin
4ed657c930 Merge branch 'master' of github.com:bitwarden/mobile 2017-05-16 09:54:56 -04:00
Kyle Spearrin
5895b37965 adjusted references to google analytics 2017-05-16 09:54:39 -04:00
Igetin
6979b8e11e Correct a typo (#74) 2017-05-15 07:53:26 -04:00
Primokorn
20713cf158 Update AppResources.fr.resx (#75) 2017-05-15 07:51:11 -04:00
Kyle Spearrin
13ff0846b1 packages that are in ios must also be in extension 2017-05-13 11:02:56 -04:00
Kyle Spearrin
e92d091cb3 added forms package to ios libs 2017-05-13 10:40:58 -04:00
Kyle Spearrin
6b1a435cdc init _tableItems as empty list 2017-05-11 00:34:54 -04:00
Kyle Spearrin
3f04b465f3 version bump 2017-05-06 21:31:09 -04:00
Kyle Spearrin
3f5c8fe2cb IdentityHttpClient url fix 2017-05-06 21:21:20 -04:00
Kyle Spearrin
d1cf6c68f3 identity server client for auth 2017-05-06 20:20:57 -04:00
Kyle Spearrin
7117f00480 bump build number 2017-05-03 22:35:45 -04:00
Kyle Spearrin
d4f37343a2 added required NSPhotoLibraryUsageDescription key 2017-05-03 22:25:00 -04:00
Kyle Spearrin
71e15e9cab remove CFBundleExecutable again 2017-05-03 22:01:20 -04:00
Kyle Spearrin
eadf00feba testing add back CFBundleExecutable 2017-05-03 15:37:32 -04:00
Kyle Spearrin
95f28fad2b remove CFBundleExecutable 2017-05-03 15:11:21 -04:00
Kyle Spearrin
9753137a72 version bump 2017-05-03 11:43:56 -04:00
Kyle Spearrin
e4f7436dfb do not animate autofill on mobile
- Animate seems to now cause a JS error when autofilling on iOS, which
stops the password from autofilling. Turn the option off.
2017-05-03 09:53:40 -04:00
Kyle Spearrin
d39211310d make notes taller. autofocus name field on add 2017-05-02 19:41:57 -04:00
Kyle Spearrin
0a6fb3ec0a Comment out test nodes 2017-04-30 17:17:40 -04:00
Kyle Spearrin
5232cf7cec remove beta tag from autofill in tools listing 2017-04-28 12:27:03 -04:00
Kyle Spearrin
6e16ffe05f autofill listing page name for android app fixes 2017-04-28 12:25:29 -04:00
Kyle Spearrin
2d6895aeea android app match fixes 2017-04-28 12:14:53 -04:00
Kyle Spearrin
b5311e1448 moved locked sets to AppSettingsService 2017-04-28 11:34:02 -04:00
Kyle Spearrin
cc63eb383d check that now is > LastActivity Date 2017-04-28 11:19:43 -04:00
Kyle Spearrin
01736ca685 Lock Screen Fixes
- Move settings to AppSettingsService
- Update activity on page disappaearing
- Always check if app is currently locked before updating last activity
date
2017-04-28 11:07:26 -04:00
Kyle Spearrin
be47bb7263 (none) => No folder 2017-04-27 16:46:50 -04:00
Kyle Spearrin
bcb7d88ed7 Double HMAC comparison to prevent timing attacks 2017-04-27 12:14:45 -04:00
Kyle Spearrin
cf58c1b4b5 only fetch keys if there are some orgs 2017-04-26 11:58:52 -04:00
Kyle Spearrin
70c57928e7 Compat. - no header for AesCbc256_B64 cipherstring 2017-04-26 11:28:03 -04:00
Kyle Spearrin
c8219b29c0 encrypted private key and org keys at rest 2017-04-25 16:05:13 -04:00
Kyle Spearrin
15a9f80430 Tools share cell 2017-04-25 14:48:42 -04:00
Kyle Spearrin
0684dfe869 only parse list as URL if dict case fails 2017-04-24 22:22:12 -04:00
Kyle Spearrin
83a89566ac parse url for older extension 2017-04-24 18:05:23 -04:00
Kyle Spearrin
04f486b003 process string for firefox browsers 2017-04-24 17:40:17 -04:00
Kyle Spearrin
f135c92434 add new dependencies to extention container 2017-04-24 16:56:34 -04:00
Kyle Spearrin
78b095d01a new share icon size/color 2017-04-24 16:05:16 -04:00
Kyle Spearrin
1b8bd494e2 disable GA exception reporting 2017-04-24 16:04:54 -04:00
Kyle Spearrin
481925ac78 added share icons to ios project 2017-04-24 15:17:11 -04:00
Kyle Spearrin
4854b2b1c0 share icon on vault listing 2017-04-24 15:00:55 -04:00
Kyle Spearrin
2d7b33459e remove refs to "google" 2017-04-24 14:26:16 -04:00
Kyle Spearrin
27e0c7421b rename CryptoKey to SymmetricCryptoKey 2017-04-22 14:37:01 -04:00
Kyle Spearrin
b26c3d050c sync org keys and refactors 2017-04-21 22:33:09 -04:00
Kyle Spearrin
439370e25a new push notification changes and syncing 2017-04-21 14:57:23 -04:00
Kyle Spearrin
1be4f6e20c add support for rsa oaep sha1 enc type 2017-04-21 13:40:29 -04:00
Kyle Spearrin
2714c7cce9 Added more api uris. lock screen bug in ios ext 2017-04-21 09:12:30 -04:00
Kyle Spearrin
952935de23 copy notes when tapped 2017-04-20 16:07:34 -04:00
Kyle Spearrin
bdb8b5ea39 fix crypto tests 2017-04-20 15:53:17 -04:00
Kyle Spearrin
3ad4e28a2c no inline out 2017-04-20 15:49:25 -04:00
Kyle Spearrin
48d0d068d1 try inline out again 2017-04-20 15:44:51 -04:00
Kyle Spearrin
56e166d61a build engine doesnt like inline out params 2017-04-20 15:38:31 -04:00
Kyle Spearrin
672d753adf update libs 2017-04-20 14:54:39 -04:00
Kyle Spearrin
0d9ba92db4 null check on key retrievals 2017-04-20 14:23:58 -04:00
Kyle Spearrin
8cf25d3602 remove old, unnecessary refs for client handler 2017-04-20 14:23:40 -04:00
Kyle Spearrin
a6bc44dc10 No need for custom handler anymore - xam bug fixed 2017-04-20 14:22:11 -04:00
Kyle Spearrin
408d66ee74 update xamarin forms. empty string section titles
There appears to be a bug regression introduced with the new xamarin
forms that removes headers if there is no title. Hack to fix it for now
is to include a empty string header title.
2017-04-20 14:18:16 -04:00
Kyle Spearrin
b136bb74b8 encrypt with org key if needed 2017-04-20 11:40:39 -04:00
Kyle Spearrin
18b2b6f447 set org keys on login and decrypt org ciphers 2017-04-20 11:23:30 -04:00
Kyle Spearrin
490d1775a2 sync folders & added org id for ciphers/logins 2017-04-20 10:47:14 -04:00
Kyle Spearrin
458de2d2e0 set private key on login 2017-04-20 10:29:18 -04:00
Kyle Spearrin
51ae3fc62f clear keys on logout 2017-04-20 10:20:50 -04:00
Kyle Spearrin
58c5c55d09 extend crypto service for org keys 2017-04-20 10:20:24 -04:00
Kyle Spearrin
4c2bcb9e6b IsNullOrWhiteSpace InitializationVector 2017-04-20 00:10:36 -04:00
Kyle Spearrin
498379bb7e privatekey, rsa decryption, org key management 2017-04-20 00:06:11 -04:00
Kyle Spearrin
e7f3b115a4 refactor for enc type header and cryptokey 2017-04-19 23:16:09 -04:00
Kyle Spearrin
0ebfe85d8e centralize login code into auth service 2017-04-19 22:04:43 -04:00
Kyle Spearrin
8e29a990cb Remove userid from Ga service, not being used 2017-04-19 21:05:03 -04:00
Kyle Spearrin
a960ccd786 IP for desktop from emulator 2017-04-19 20:57:40 -04:00
Kyle Spearrin
6b86e836d7 update target framework 2017-04-19 17:11:06 -04:00
Shan
fb35b9b10a Fixes #8 Disable Google Analytics (#55)
* Opt-out of Google Analytics

* Move OptOut to Other in Settings

* Change OptOut Order and Resource key
2017-03-30 18:22:14 -04:00
Kyle Spearrin
a45773e1ca Update README.md 2017-03-21 18:12:21 -04:00
Kyle Spearrin
2405a6f21e Android version bump 2017-03-11 15:05:09 -05:00
Kyle Spearrin
533dd6135e revert test project to target framework 6.0 2017-03-11 12:36:46 -05:00
Kyle Spearrin
efc25543ca revert to target framework 6 2017-03-11 12:32:44 -05:00
Kyle Spearrin
82d4745da3 catch sql crashes in sync service 2017-03-11 12:26:10 -05:00
Kyle Spearrin
ac6e95c442 Added sbrowser beta to supported browsers list 2017-03-09 20:22:06 -05:00
Kyle Spearrin
375f23ac9e parse uri with Uri.TryCreate 2017-02-25 22:03:18 -05:00
Kyle Spearrin
8e5a01d82c More null checks. Catch null exception in accessibility service. 2017-02-25 16:10:18 -05:00
Kyle Spearrin
910658aa93 android version bump 2017-02-23 23:29:00 -05:00
Kyle Spearrin
d766ffa040 dont null out disposed objects 2017-02-23 23:12:39 -05:00
Kyle Spearrin
b960640e03 version bump 2017-02-22 22:56:08 -05:00
Kyle Spearrin
c984617b1c null out testNodesData 2017-02-22 22:54:18 -05:00
Kyle Spearrin
a12a7127c0 Update README.md
amazon app badge
2017-02-22 19:49:25 -05:00
Kyle Spearrin
98a6a5c93d Added null checking throughout autofill service 2017-02-22 19:00:50 -05:00
Kyle Spearrin
27202fd740 amazon store information 2017-02-22 00:19:42 -05:00
Kyle Spearrin
c01d02de27 dispose nodes instead of manual GC 2017-02-20 18:22:24 -05:00
Kyle Spearrin
1d23bcc979 Update README.md 2017-02-19 23:20:51 -05:00
Kyle Spearrin
ac8abdaa17 readme store badge updates 2017-02-19 23:19:55 -05:00
Kyle Spearrin
613977c6f9 updates to manual GC 2017-02-19 17:29:00 -05:00
Kyle Spearrin
54159c9d05 Do some manual GCing 2017-02-18 21:33:06 -05:00
Kyle Spearrin
8d5d477b4a version bump 2017-02-18 17:47:27 -05:00
Kyle Spearrin
2c73906ad3 fix GetWindowNodes recusion 2017-02-18 15:48:24 -05:00
Kyle Spearrin
079fb34120 pass nodes as reference 2017-02-18 10:50:27 -05:00
Kyle Spearrin
17ed1cdc00 increase autofill toast timeout to 10 seconds 2017-02-18 00:23:47 -05:00
Kyle Spearrin
d53ea584ba Better way of checking for autofill sevrice running 2017-02-17 23:22:02 -05:00
Kyle Spearrin
b435256911 handle locked status better. once locked, stay locked. 2017-02-17 23:03:54 -05:00
Kyle Spearrin
27e996dba0 Detach events 2017-02-17 21:18:59 -05:00
Kyle Spearrin
22f3bd1073 tearing down event handlers on page disappears 2017-02-17 00:16:09 -05:00
Kyle Spearrin
fb564fa817 add support for CM browser 2017-02-16 23:09:40 -05:00
Kyle Spearrin
be9db2930f autofill intent fixes 2017-02-16 22:22:19 -05:00
Kyle Spearrin
5bce95a686 Added support for Yandex browser. Turned push sevrice back on 2017-02-16 21:14:37 -05:00
maxlandry
f6ca9b9d0f Corrections + new french translations (#46)
Various grammar and spelling corrections.
Replace all «sites» mentions with «logins» (identifiants) mentions.
Add and translate lines 760 to 819.
2017-02-16 20:57:06 -05:00
Kyle Spearrin
88f186907b backets for PS commands 2017-02-16 20:05:20 -05:00
Kyle Spearrin
9faf1d9de5 appveyor env checks for powershell commands 2017-02-16 19:57:35 -05:00
Kyle Spearrin
8b1d1d0f6d http ref for ios core 2017-02-15 23:06:26 -05:00
Kyle Spearrin
8c19e2c3f2 system.net.http ref for ios 2017-02-15 23:02:33 -05:00
Kyle Spearrin
d2d8ee504d cached images 2017-02-15 21:56:02 -05:00
Kyle Spearrin
d96b279beb disable push service 2017-02-15 19:55:52 -05:00
Kyle Spearrin
f5e7f9249c attach and detach event handlers onappearing and ondisappearing to free up views for GC 2017-02-15 00:28:05 -05:00
Kyle Spearrin
56c33ee82b Aitpfill fixes for main page set. Memory service for monitoring memory use on Android. 2017-02-14 19:47:00 -05:00
Kyle Spearrin
b05dd4cc2c autofill search UX improvements 2017-02-13 22:35:16 -05:00
Kyle Spearrin
36d4ce8718 more updates to autofill flow 2017-02-13 22:10:34 -05:00
Kyle Spearrin
ddec7ab643 app veyor env checks 2017-02-13 21:57:11 -05:00
Kyle Spearrin
75201c9b30 Added fuzzy matches to autofill listing page. Allow autofilling from main vault search page when arriving from autofill service 2017-02-13 19:12:02 -05:00
Peter Karlsson
99c81e5a5d Swedish translation additional strings update (#39)
* Add files via upload

* Sync

* Swedish translation additional strings update

* Updated short description (too long)
2017-02-11 12:58:14 -05:00
Kyle Spearrin
b84ad39133 appveyor.yml 2017-02-11 01:13:03 -05:00
Kyle Spearrin
4a19e2b673 added max character info to captions 2017-02-11 00:31:23 -05:00
Kyle Spearrin
475c3559f6 new screenshots and caption for google store 2017-02-11 00:14:16 -05:00
406 changed files with 36460 additions and 6373 deletions

View File

@@ -1,33 +1,13 @@
Code contributions are welcome! Please commit any pull requests against the `master` branch.
# Internationalization (i18n)
# Localization (l10n)
If you are interested in helping translate the bitwarden mobile app into another language, please follow these steps
when creating your pull request:
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/bitwarden-mobile/localized.svg)](https://crowdin.com/project/bitwarden-mobile)
1. Create a new resource file under `/src/App/Resources` by copy/pasting the master English file, `AppResources.resx`.
2. Rename your `AppResources.resx` copy to include the proper .NET culture code for your language (typically the two
letter code, ex. `sv`). You can find a list of culture codes here: <http://timtrott.co.uk/culture-codes/>. For
example, if I want to create a new translation for Swedish, I will rename my `AppResources.resx` copy to
`AppResources.sv.resx`.
3. Open the `AppResources.XX.resx` file for your newly created language and start translating the `<value>` tags for
each `<data>` element. The `<data>` and `<comment>` properties should not be translated and remain in English.
4. Repeat the same process for the store `COPY.md` and `CAPTIONS.md` files in `/store/apple` and `/store/google` by
creating a new folder for your language in each. Do not copy over the `assets` and `screenshots` folders to your new
language. We will update these based on your translations provided in `CAPTIONS.md`. Finally, do not translate the
titles in the markdown files (ex. `# Name` and `# Screenshot 1`). These are only for reference.
5. If you have a Xamarin development environment setup, test your translations to make sure they look correct in the
app on iOS and Android. Sometimes the UI can break due to translations taking up more space than the original UI was
built for. If possible, use a shorter or abbreviated version of the word/sentence to accomedate the available space.
If you are unable to accomedate the avaialable space for a particular translation, just let us know in your pull
request comments. If you are unable to test your translations, just let us know in your pull request comments so
that we can check it for you.
6. Be sure to watch for [future changes](https://github.com/bitwarden/mobile/commits/master/src/App/Resources/AppResources.resx)
to the `/src/App/Resources/AppResources.resx` file so that your translation will stay up to date.
We use a translation tool called [Crowdin](https://crowdin.com) to help manage our localization efforts across many different languages.
You can find an example of a proper translation pull request here: <https://github.com/bitwarden/mobile/pull/22/files>
If you are interested in helping translate the bitwarden mobile app into another language (or make a translation correction), please register an account at Crowdin and join our project here: https://crowdin.com/project/bitwarden-mobile
You can read more about localizing a Xamarin.Forms app here:
<https://developer.xamarin.com/guides/xamarin-forms/advanced/localization/>
If the language that you are interested in translating is not already listed, create a new account on Crowdin, join the project, and contact the project owner (https://crowdin.com/mail/compose/kspearrin).
TIP: If you have Visual Studio installed, it provides a nice tabular UI for editing `resx` XML files.
You can read Crowdin's getting started guide for translators here: https://support.crowdin.com/crowdin-intro/

View File

@@ -1,12 +1,15 @@
[![appveyor build](https://ci.appveyor.com/api/projects/status/github/bitwarden/mobile?branch=master&svg=true)] (https://ci.appveyor.com/project/bitwarden/mobile)
[![appveyor build](https://ci.appveyor.com/api/projects/status/github/bitwarden/mobile?branch=master&svg=true)](https://ci.appveyor.com/project/bitwarden/mobile)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/bitwarden-mobile/localized.svg)](https://crowdin.com/project/bitwarden-mobile)
[![Join the chat at https://gitter.im/bitwarden/Lobby](https://badges.gitter.im/bitwarden/Lobby.svg)](https://gitter.im/bitwarden/Lobby)
# bitwarden mobile
<a href="https://play.google.com/store/apps/details?id=com.x8bit.bitwarden" target="_blank"><img alt="Get it on Google Play" src="https://developer.android.com/images/brand/en_generic_rgb_wo_45.png"width="129" height="45"></a> <a href="https://itunes.apple.com/us/app/bitwarden-free-password-manager/id1137397744?mt=8" target="_blank"><img src="https://linkmaker.itunes.apple.com/images/badges/en-us/badge_appstore-lrg.svg" width="165" height="40"></a>
<a href="https://play.google.com/store/apps/details?id=com.x8bit.bitwarden" target="_blank"><img alt="Get it on Google Play" src="https://imgur.com/YQzmZi9.png" width="153" height="46"></a> <a href="https://itunes.apple.com/us/app/bitwarden-free-password-manager/id1137397744?mt=8" target="_blank"><img src="https://imgur.com/GdGqPMY.png" width="135" height="40"></a> <a href="https://www.amazon.com/dp/B06XMYGPMV" target="_blank"><img src="https://imgur.com/f75uYeM.png" width="132" height="45"></a>
The bitwarden mobile application is written in C# with Xamarin Android, Xamarin iOS, and Xamarin Forms.
<img src="https://i.imgur.com/Ty8wkSO.png" alt="" width="300" height="533" /> <img src="https://i.imgur.com/BYb4gVc.png" alt="" width="300" height="533" />
# Build/Run
**Requirements**
@@ -14,7 +17,7 @@ The bitwarden mobile application is written in C# with Xamarin Android, Xamarin
- [Visual Studio w/ Xamarin -or- Xamarin Studio](https://store.xamarin.com/)
By default the app is targeting the production API. If you are running the [Core](https://github.com/bitwarden/core) API locally,
you'll need to switch the extension to target your local API. Open `src/App/Utilities/ApiHttpClient.cs` and set `BaseAddress` to your local
you'll need to switch the app to target your local API. Open `src/App/Utilities/ApiHttpClient.cs` and set `BaseAddress` to your local
API instance (ex. `new Uri("http://localhost:4000")`).
After restoring the nuget packages, you can now build and run the app.
@@ -24,4 +27,4 @@ After restoring the nuget packages, you can now build and run the app.
Code contributions are welcome! Visual Studio or Xamarin Studio is required to work on this project. Please commit any pull requests against the `master` branch.
Learn more about how to contribute by reading the [`CONTRIBUTING.md`](CONTRIBUTING.md) file.
Security audits and feedback are welcome. Please open an issue or email us privately if the report is sensitive in nature.
Security audits and feedback are welcome. Please open an issue or email us privately if the report is sensitive in nature. You can read our security policy in the [`SECURITY.md`](SECURITY.md) file.

45
SECURITY.md Normal file
View File

@@ -0,0 +1,45 @@
bitwarden believes that working with security researchers across the globe is crucial to keeping our
users safe. If you believe you've found a security issue in our product or service, we encourage you to
notify us. We welcome working with you to resolve the issue promptly. Thanks in advance!
# Disclosure Policy
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every
effort to quickly resolve the issue.
- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a
third-party. We may publicly disclose the issue before resolving it, if appropriate.
- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or
degradation of our service. Only interact with accounts you own or with explicit permission of the
account holder.
- If you would like to encrypt your report, please use the PGP key with long ID
`0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
# In-scope
- Security issues in any current release of bitwarden. This includes the web vault, browser extension,
and mobile apps (iOS and Android). Product downloads are available at https://bitwarden.com. Source
code is available at https://github.com/bitwarden.
# Exclusions
The following bug classes are out-of scope:
- Bugs that are already reported on any of bitwarden's issue trackers (https://github.com/bitwarden),
or that we already know of. Note that some of our issue tracking is private.
- Issues in an upstream software dependency (ex: Xamarin, ASP.NET) which are already reported to the
upstream maintainer.
- Attacks requiring physical access to a user's device.
- Self-XSS
- Issues related to software or protocols not under bitwarden's control
- Vulnerabilities in outdated versions of bitwarden
- Missing security best practices that do not directly lead to a vulnerability
- Issues that do not have any impact on the general public
While researching, we'd like to ask you to refrain from:
- Denial of service
- Spamming
- Social engineering (including phishing) of bitwarden staff or contractors
- Any physical attempts against bitwarden property or data centers
Thank you for helping keep bitwarden and our users safe!

18
appveyor.yml Normal file
View File

@@ -0,0 +1,18 @@
skip_tags: true
before_build:
- nuget restore
- IF DEFINED keystore_dec_secret nuget install secure-file -ExcludeVersion
after_build:
- ps: IF($env:keystore_dec_secret) { .\src\Android\increment-version.ps1 $($env:APPVEYOR_BUILD_FOLDER) $($env:APPVEYOR_BUILD_NUMBER) }
- IF DEFINED keystore_dec_secret secure-file\tools\secure-file -decrypt src\Android\8bit.keystore.enc -secret %keystore_dec_secret%
- IF DEFINED keystore_password msbuild "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" "/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=%keystore_password%" "/p:AndroidSigningKeyStore=8bit.keystore" "/p:AndroidSigningStorePass=%keystore_password%" "src\Android\Android.csproj"
- ps: IF($env:keystore_dec_secret) { copy-item src\Android\bin\Release\com.x8bit.bitwarden-Signed.apk .\com.x8bit.bitwarden-$($env:APPVEYOR_BUILD_NUMBER).apk }
on_success:
- IF DEFINED play_dec_secret secure-file\tools\secure-file -decrypt store\google\Publisher\play_creds.json.enc -secret %play_dec_secret%
- IF DEFINED play_dec_secret store\google\Publisher\bin\Debug\Publisher.exe %APPVEYOR_BUILD_FOLDER%\store\google\Publisher\play_creds.json %APPVEYOR_BUILD_FOLDER%\src\Android\bin\Release\com.x8bit.bitwarden-Signed.apk alpha
artifacts:
- path: com.x8bit.bitwarden-%APPVEYOR_BUILD_NUMBER%.apk
branches:
except:
- l10n_master
image: Visual Studio 2017

View File

@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
# Visual Studio 15
VisualStudioVersion = 15.0.26430.13
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Android", "src\Android\Android.csproj", "{04B18ED2-B76D-4947-8474-191F8FD2B5E0}"
EndProject

33
crowdin.yml Normal file
View File

@@ -0,0 +1,33 @@
files:
- source: /src/App/Resources/AppResources.resx
translation: /src/App/Resources/AppResources.%two_letters_code%.resx
languages_mapping:
two_letters_code:
zh-CN: zh-Hans
zh-TW: zh-Hant
pt-PT: pt-PT
pt-BR: pt-BR
- source: /store/amazon/en/copy.resx
translation: /store/amazon/%two_letters_code%/copy.resx
languages_mapping:
two_letters_code:
zh-CN: zh-Hans
zh-TW: zh-Hant
pt-PT: pt-PT
pt-BR: pt-BR
- source: /store/apple/en/copy.resx
translation: /store/apple/%two_letters_code%/copy.resx
languages_mapping:
two_letters_code:
zh-CN: zh-Hans
zh-TW: zh-Hant
pt-PT: pt-PT
pt-BR: pt-BR
- source: /store/google/en/copy.resx
translation: /store/google/%two_letters_code%/copy.resx
languages_mapping:
two_letters_code:
zh-CN: zh-Hans
zh-TW: zh-Hant
pt-BR: pt-BR
pt-PT: pt-PT

View File

@@ -17,8 +17,9 @@
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v6.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v7.1</TargetFrameworkVersion>
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis>
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
<AndroidStoreUncompressedFileExtensions />
<MandroidI18n />
<JavaMaximumHeapSize />
@@ -36,7 +37,7 @@
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
<AndroidLinkMode>None</AndroidLinkMode>
<AndroidLinkSkip>PushNotification.Plugin;PushNotification.Plugin.Abstractions;Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;Microsoft.Practices.Unity;SQLite-net;Xamarin.Android.Net</AndroidLinkSkip>
<AndroidLinkSkip>PushNotification.Plugin;PushNotification.Plugin.Abstractions;Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;SQLite-net;Xamarin.Android.Net</AndroidLinkSkip>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
@@ -67,7 +68,7 @@
<EnableProguard>False</EnableProguard>
<DebugSymbols>False</DebugSymbols>
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
<AndroidLinkSkip>PushNotification.Plugin;PushNotification.Plugin.Abstractions;Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;Microsoft.Practices.Unity;SQLite-net;Xamarin.Android.Net</AndroidLinkSkip>
<AndroidLinkSkip>PushNotification.Plugin;PushNotification.Plugin.Abstractions;Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;SQLite-net;Xamarin.Android.Net</AndroidLinkSkip>
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
</PropertyGroup>
<ItemGroup>
@@ -76,12 +77,10 @@
<Private>True</Private>
</Reference>
<Reference Include="Acr.UserDialogs, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Acr.UserDialogs.6.3.3\lib\MonoAndroid10\Acr.UserDialogs.dll</HintPath>
<Private>True</Private>
<HintPath>..\..\packages\Acr.UserDialogs.6.3.10\lib\MonoAndroid10\Acr.UserDialogs.dll</HintPath>
</Reference>
<Reference Include="Acr.UserDialogs.Interface, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Acr.UserDialogs.6.3.3\lib\MonoAndroid10\Acr.UserDialogs.Interface.dll</HintPath>
<Private>True</Private>
<HintPath>..\..\packages\Acr.UserDialogs.6.3.10\lib\MonoAndroid10\Acr.UserDialogs.Interface.dll</HintPath>
</Reference>
<Reference Include="AndHUD, Version=1.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\AndHUD.1.2.0\lib\MonoAndroid\AndHUD.dll</HintPath>
@@ -91,27 +90,33 @@
<HintPath>..\..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FFImageLoading, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.FFImageLoading.2.2.9\lib\MonoAndroid10\FFImageLoading.dll</HintPath>
</Reference>
<Reference Include="FFImageLoading.Forms, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.FFImageLoading.Forms.2.2.9\lib\MonoAndroid10\FFImageLoading.Forms.dll</HintPath>
</Reference>
<Reference Include="FFImageLoading.Forms.Droid, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.FFImageLoading.Forms.2.2.9\lib\MonoAndroid10\FFImageLoading.Forms.Droid.dll</HintPath>
</Reference>
<Reference Include="FFImageLoading.Platform, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.FFImageLoading.2.2.9\lib\MonoAndroid10\FFImageLoading.Platform.dll</HintPath>
</Reference>
<Reference Include="FormsViewGroup, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.Forms.2.3.3.180\lib\MonoAndroid10\FormsViewGroup.dll</HintPath>
<Private>True</Private>
<HintPath>..\..\packages\Xamarin.Forms.2.3.4.231\lib\MonoAndroid10\FormsViewGroup.dll</HintPath>
</Reference>
<Reference Include="HockeySDK, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\HockeySDK.Xamarin.4.1.0\lib\MonoAndroid403\HockeySDK.dll</HintPath>
<Private>True</Private>
<HintPath>..\..\packages\HockeySDK.Xamarin.4.1.2\lib\MonoAndroid403\HockeySDK.dll</HintPath>
</Reference>
<Reference Include="HockeySDK.AndroidBindings, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\HockeySDK.Xamarin.4.1.0\lib\MonoAndroid403\HockeySDK.AndroidBindings.dll</HintPath>
<Private>True</Private>
<HintPath>..\..\packages\HockeySDK.Xamarin.4.1.2\lib\MonoAndroid403\HockeySDK.AndroidBindings.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Practices.ServiceLocation, Version=1.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Practices.Unity, Version=3.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Unity.3.5.1405-prerelease\lib\portable-net45+wp80+win8+wpa81+MonoAndroid10+MonoTouch10\Microsoft.Practices.Unity.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Android" />
<Reference Include="Mono.Android.Export" />
<Reference Include="mscorlib" />
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll</HintPath>
@@ -137,33 +142,30 @@
<HintPath>..\..\packages\PInvoke.Windows.Core.0.3.152\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Windows.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Plugin.Connectivity, Version=2.2.12.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugin.Connectivity.2.2.12\lib\MonoAndroid10\Plugin.Connectivity.dll</HintPath>
<Private>True</Private>
<Reference Include="Plugin.Connectivity, Version=2.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugin.Connectivity.2.3.0\lib\MonoAndroid10\Plugin.Connectivity.dll</HintPath>
</Reference>
<Reference Include="Plugin.Connectivity.Abstractions, Version=2.2.12.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugin.Connectivity.2.2.12\lib\MonoAndroid10\Plugin.Connectivity.Abstractions.dll</HintPath>
<Private>True</Private>
<Reference Include="Plugin.Connectivity.Abstractions, Version=2.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugin.Connectivity.2.3.0\lib\MonoAndroid10\Plugin.Connectivity.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Plugin.CurrentActivity, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Plugin.CurrentActivity.1.0.1\lib\MonoAndroid10\Plugin.CurrentActivity.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Plugin.Fingerprint, Version=1.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Plugin.Fingerprint.1.2.0\lib\MonoAndroid\Plugin.Fingerprint.dll</HintPath>
<Private>True</Private>
<Reference Include="Plugin.Fingerprint, Version=1.4.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Plugin.Fingerprint.1.4.4\lib\MonoAndroid\Plugin.Fingerprint.dll</HintPath>
</Reference>
<Reference Include="Plugin.Fingerprint.Abstractions, Version=1.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Plugin.Fingerprint.1.2.0\lib\MonoAndroid\Plugin.Fingerprint.Abstractions.dll</HintPath>
<Private>True</Private>
<Reference Include="Plugin.Fingerprint.Abstractions, Version=1.4.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Plugin.Fingerprint.1.4.4\lib\MonoAndroid\Plugin.Fingerprint.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Plugin.Settings, Version=2.5.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugins.Settings.2.5.1.0\lib\MonoAndroid10\Plugin.Settings.dll</HintPath>
<Private>True</Private>
<Reference Include="Plugin.Fingerprint.Android.Samsung, Version=1.4.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Plugin.Fingerprint.1.4.4\lib\MonoAndroid\Plugin.Fingerprint.Android.Samsung.dll</HintPath>
</Reference>
<Reference Include="Plugin.Settings.Abstractions, Version=2.5.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugins.Settings.2.5.1.0\lib\MonoAndroid10\Plugin.Settings.Abstractions.dll</HintPath>
<Private>True</Private>
<Reference Include="Plugin.Settings, Version=2.5.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugins.Settings.2.5.4\lib\MonoAndroid10\Plugin.Settings.dll</HintPath>
</Reference>
<Reference Include="Plugin.Settings.Abstractions, Version=2.5.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugins.Settings.2.5.4\lib\MonoAndroid10\Plugin.Settings.Abstractions.dll</HintPath>
</Reference>
<Reference Include="PushNotification.Plugin, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugin.PushNotification.1.2.4\lib\MonoAndroid10\PushNotification.Plugin.dll</HintPath>
@@ -173,6 +175,9 @@
<HintPath>..\..\packages\Xam.Plugin.PushNotification.1.2.4\lib\MonoAndroid10\PushNotification.Plugin.Abstractions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SimpleInjector, Version=4.0.7.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<HintPath>..\..\packages\SimpleInjector.4.0.7\lib\netstandard1.3\SimpleInjector.dll</HintPath>
</Reference>
<Reference Include="Splat, Version=1.6.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Splat.1.6.2\lib\monoandroid\Splat.dll</HintPath>
<Private>True</Private>
@@ -215,6 +220,7 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Net.Http" />
<Reference Include="Validation, Version=2.3.0.0, Culture=neutral, PublicKeyToken=2fc06f0d701809a7, processorArchitecture=MSIL">
<HintPath>..\..\packages\Validation.2.3.7\lib\dotnet\Validation.dll</HintPath>
@@ -253,20 +259,16 @@
<Private>True</Private>
</Reference>
<Reference Include="Xamarin.Forms.Core, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.Forms.2.3.3.180\lib\MonoAndroid10\Xamarin.Forms.Core.dll</HintPath>
<Private>True</Private>
<HintPath>..\..\packages\Xamarin.Forms.2.3.4.231\lib\MonoAndroid10\Xamarin.Forms.Core.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Forms.Platform, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.Forms.2.3.3.180\lib\MonoAndroid10\Xamarin.Forms.Platform.dll</HintPath>
<Private>True</Private>
<HintPath>..\..\packages\Xamarin.Forms.2.3.4.231\lib\MonoAndroid10\Xamarin.Forms.Platform.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Forms.Platform.Android, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.Forms.2.3.3.180\lib\MonoAndroid10\Xamarin.Forms.Platform.Android.dll</HintPath>
<Private>True</Private>
<HintPath>..\..\packages\Xamarin.Forms.2.3.4.231\lib\MonoAndroid10\Xamarin.Forms.Platform.Android.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Forms.Xaml, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.Forms.2.3.3.180\lib\MonoAndroid10\Xamarin.Forms.Xaml.dll</HintPath>
<Private>True</Private>
<HintPath>..\..\packages\Xamarin.Forms.2.3.4.231\lib\MonoAndroid10\Xamarin.Forms.Xaml.dll</HintPath>
</Reference>
<Reference Include="Xamarin.GooglePlayServices.Analytics, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.GooglePlayServices.Analytics.29.0.0.2\lib\MonoAndroid41\Xamarin.GooglePlayServices.Analytics.dll</HintPath>
@@ -292,9 +294,23 @@
<HintPath>..\..\packages\XLabs.IoC.2.0.5782\lib\portable-net45+netcore45+wp8+MonoAndroid1+MonoTouch1\XLabs.Ioc.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="XLabs.Ioc.Unity, Version=2.0.5782.12230, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\XLabs.IoC.Unity.2.0.5782\lib\portable-net45+netcore45+wp8+MonoAndroid1+MonoTouch1\XLabs.Ioc.Unity.dll</HintPath>
<Private>True</Private>
<Reference Include="XLabs.Ioc.SimpleInjector, Version=2.0.5782.12229, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\XLabs.IoC.SimpleInjector.2.0.5782\lib\portable-net45+netcore45+wp8+MonoAndroid1+MonoTouch1\XLabs.Ioc.SimpleInjector.dll</HintPath>
</Reference>
<Reference Include="ZXing.Net.Mobile.Core, Version=2.1.47.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\ZXing.Net.Mobile.2.1.47\lib\MonoAndroid403\ZXing.Net.Mobile.Core.dll</HintPath>
</Reference>
<Reference Include="ZXing.Net.Mobile.Forms, Version=2.1.47.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\ZXing.Net.Mobile.Forms.2.1.47\lib\MonoAndroid403\ZXing.Net.Mobile.Forms.dll</HintPath>
</Reference>
<Reference Include="ZXing.Net.Mobile.Forms.Android, Version=2.1.47.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\ZXing.Net.Mobile.Forms.2.1.47\lib\MonoAndroid403\ZXing.Net.Mobile.Forms.Android.dll</HintPath>
</Reference>
<Reference Include="zxing.portable, Version=2.1.47.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\ZXing.Net.Mobile.2.1.47\lib\MonoAndroid403\zxing.portable.dll</HintPath>
</Reference>
<Reference Include="ZXingNetMobile, Version=2.1.47.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\ZXing.Net.Mobile.2.1.47\lib\MonoAndroid403\ZXingNetMobile.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
@@ -302,10 +318,10 @@
<Compile Include="AutofillCredentials.cs" />
<Compile Include="Controls\CustomSearchBarRenderer.cs" />
<Compile Include="Controls\CustomButtonRenderer.cs" />
<Compile Include="Controls\HybridWebViewRenderer.cs" />
<Compile Include="Controls\ExtendedButtonRenderer.cs" />
<Compile Include="Controls\ExtendedTabbedPageRenderer.cs" />
<Compile Include="Controls\ExtendedTableViewRenderer.cs" />
<Compile Include="CustomAndroidClientHandler.cs" />
<Compile Include="HockeyAppCrashManagerListener.cs" />
<Compile Include="AutofillService.cs" />
<Compile Include="Controls\ExtendedEditorRenderer.cs" />
@@ -315,21 +331,25 @@
<Compile Include="Controls\ExtendedPickerRenderer.cs" />
<Compile Include="Controls\ExtendedEntryRenderer.cs" />
<Compile Include="Services\HttpService.cs" />
<Compile Include="Services\AndroidKeyStoreStorageService.cs" />
<Compile Include="Services\LocalizeService.cs" />
<Compile Include="MainApplication.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Services\DeviceInfoService.cs" />
<Compile Include="Services\GoogleAnalyticsService.cs" />
<Compile Include="Services\AppInfoService.cs" />
<Compile Include="Services\ClipboardService.cs" />
<Compile Include="Services\DeviceActionService.cs" />
<Compile Include="Services\BouncyCastleKeyDerivationService.cs" />
<Compile Include="Services\KeyStoreStorageService.cs" />
<Compile Include="MainActivity.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\LogService.cs" />
<Compile Include="Services\MemoryService.cs" />
<Compile Include="Services\ReflectionService.cs" />
<Compile Include="Services\SqlService.cs" />
<Compile Include="SplashActivity.cs" />
<Compile Include="PackageReplacedReceiver.cs" />
<Compile Include="Utilities.cs" />
</ItemGroup>
<ItemGroup>
<None Include="8bit.keystore.enc" />
@@ -745,9 +765,6 @@
<ItemGroup>
<AndroidResource Include="Resources\drawable\icon.png" />
</ItemGroup>
<ItemGroup>
<AndroidEnvironment Include="EnvironmentVariables.txt" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\notification_sm.png" />
</ItemGroup>
@@ -815,19 +832,124 @@
<AndroidResource Include="Resources\values\strings.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxxhdpi\close.png" />
<AndroidResource Include="Resources\drawable\search.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\close.png" />
<AndroidResource Include="Resources\drawable-hdpi\search.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\close.png" />
<AndroidResource Include="Resources\drawable-xhdpi\search.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\close.png" />
<AndroidResource Include="Resources\drawable-xxhdpi\search.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\close.png" />
<AndroidResource Include="Resources\drawable-xxxhdpi\search.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\share.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\share.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\share.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\share.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxxhdpi\share.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\share_tools.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\share_tools.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\share_tools.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\share_tools.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxxhdpi\share_tools.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\yubikey.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\yubikey.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\yubikey.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\yubikey.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\xml\filepaths.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\download.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\download.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\download.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\download.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxxhdpi\download.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\camera.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\camera.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\camera.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\camera.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxxhdpi\camera.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\paperclip.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\paperclip.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\paperclip.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\paperclip.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxxhdpi\paperclip.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\trash.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\trash.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\trash.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\trash.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxxhdpi\trash.png" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Import Project="..\..\packages\Xamarin.Android.Support.Vector.Drawable.23.3.0\build\Xamarin.Android.Support.Vector.Drawable.targets" Condition="Exists('..\..\packages\Xamarin.Android.Support.Vector.Drawable.23.3.0\build\Xamarin.Android.Support.Vector.Drawable.targets')" />
@@ -837,10 +959,10 @@
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\Xamarin.Android.Support.Vector.Drawable.23.3.0\build\Xamarin.Android.Support.Vector.Drawable.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Xamarin.Android.Support.Vector.Drawable.23.3.0\build\Xamarin.Android.Support.Vector.Drawable.targets'))" />
<Error Condition="!Exists('..\..\packages\Xamarin.GooglePlayServices.Basement.29.0.0.2\build\Xamarin.GooglePlayServices.Basement.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Xamarin.GooglePlayServices.Basement.29.0.0.2\build\Xamarin.GooglePlayServices.Basement.targets'))" />
<Error Condition="!Exists('..\..\packages\Xamarin.Forms.2.3.3.180\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Xamarin.Forms.2.3.3.180\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets'))" />
<Error Condition="!Exists('..\..\packages\Xamarin.Forms.2.3.4.231\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Xamarin.Forms.2.3.4.231\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets'))" />
</Target>
<Import Project="..\..\packages\Xamarin.GooglePlayServices.Basement.29.0.0.2\build\Xamarin.GooglePlayServices.Basement.targets" Condition="Exists('..\..\packages\Xamarin.GooglePlayServices.Basement.29.0.0.2\build\Xamarin.GooglePlayServices.Basement.targets')" />
<Import Project="..\..\packages\Xamarin.Forms.2.3.3.180\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets" Condition="Exists('..\..\packages\Xamarin.Forms.2.3.3.180\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets')" />
<Import Project="..\..\packages\Xamarin.Forms.2.3.4.231\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets" Condition="Exists('..\..\packages\Xamarin.Forms.2.3.4.231\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@@ -94,8 +94,10 @@ namespace Bit.Android
}
var intent = new Intent(this, typeof(MainActivity));
intent.PutExtra("uri", _lastQueriedUri);
intent.PutExtra("ts", Java.Lang.JavaSystem.CurrentTimeMillis());
if(!callingIntent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory))
{
intent.PutExtra("uri", _lastQueriedUri);
}
StartActivityForResult(intent, requestCode);
}
}

View File

@@ -6,6 +6,8 @@ using Android.App;
using Android.Content;
using Android.OS;
using Android.Views.Accessibility;
using Bit.App.Abstractions;
using XLabs.Ioc;
namespace Bit.Android
{
@@ -19,91 +21,170 @@ namespace Bit.Android
private const string BitwardenPackage = "com.x8bit.bitwarden";
private const string BitwardenWebsite = "bitwarden.com";
public static bool Enabled { get; set; } = false;
private static Dictionary<string, string[]> BrowserPackages => new Dictionary<string, string[]>
private static Dictionary<string, Browser> SupportedBrowsers => new List<Browser>
{
{ "com.android.chrome", new string[] { "url_bar" } },
{ "com.chrome.beta", new string[] { "url_bar" } },
{ "com.android.browser", new string[] { "url" } },
{ "com.brave.browser", new string[] { "url_bar" } },
{ "com.opera.browser", new string[] { "url_field" } },
{ "com.opera.browser.beta", new string[] { "url_field" } },
{ "com.opera.mini.native", new string[] { "url_field" } },
{ "com.chrome.dev", new string[] { "url_bar" } },
{ "com.chrome.canary", new string[] { "url_bar" } },
{ "com.google.android.apps.chrome", new string[] { "url_bar" } },
{ "com.google.android.apps.chrome_dev", new string[] { "url_bar" } },
{ "org.iron.srware", new string[] { "url_bar" } },
{ "com.sec.android.app.sbrowser", new string[] { "sbrowser_url_bar" } },
{ "com.yandex.browser", new string[] { "bro_common_omnibox_host", "bro_common_omnibox_edit_text" } },
{ "org.mozilla.firefox", new string[] { "url_bar_title" } },
{ "org.mozilla.firefox_beta", new string[] { "url_bar_title" } },
{ "com.ghostery.android.ghostery",new string[] { "search_field" } },
{ "org.adblockplus.browser", new string[] { "url_bar_title" } },
{ "com.htc.sense.browser", new string[] { "title" } },
{ "com.amazon.cloud9", new string[] { "url" } },
{ "mobi.mgeek.TunnyBrowser", new string[] { "title" } },
{ "com.nubelacorp.javelin", new string[] { "enterUrl" } },
{ "com.jerky.browser2", new string[] { "enterUrl" } },
{ "com.mx.browser", new string[] { "address_editor_with_progress" } },
{ "com.mx.browser.tablet", new string[] { "address_editor_with_progress"} },
{ "com.linkbubble.playstore", new string[] { "url_text" }}
};
new Browser("com.android.chrome", "url_bar"),
new Browser("com.chrome.beta", "url_bar"),
new Browser("com.android.browser", "url"),
new Browser("com.brave.browser", "url_bar"),
new Browser("com.opera.browser", "url_field"),
new Browser("com.opera.browser.beta", "url_field"),
new Browser("com.opera.mini.native", "url_field"),
new Browser("com.chrome.dev", "url_bar"),
new Browser("com.chrome.canary", "url_bar"),
new Browser("com.google.android.apps.chrome", "url_bar"),
new Browser("com.google.android.apps.chrome_dev", "url_bar"),
new Browser("org.codeaurora.swe.browser", "url_bar"),
new Browser("org.iron.srware", "url_bar"),
new Browser("com.sec.android.app.sbrowser", "location_bar_edit_text"),
new Browser("com.sec.android.app.sbrowser.beta", "location_bar_edit_text"),
new Browser("com.yandex.browser", "bro_omnibar_address_title_text",
(s) => s.Split(' ').FirstOrDefault()),
new Browser("org.mozilla.firefox", "url_bar_title"),
new Browser("org.mozilla.firefox_beta", "url_bar_title"),
new Browser("com.ghostery.android.ghostery", "search_field"),
new Browser("org.adblockplus.browser", "url_bar_title"),
new Browser("com.htc.sense.browser", "title"),
new Browser("com.amazon.cloud9", "url"),
new Browser("mobi.mgeek.TunnyBrowser", "title"),
new Browser("com.nubelacorp.javelin", "enterUrl"),
new Browser("com.jerky.browser2", "enterUrl"),
new Browser("com.mx.browser", "address_editor_with_progress"),
new Browser("com.mx.browser.tablet", "address_editor_with_progress"),
new Browser("com.linkbubble.playstore", "url_text"),
new Browser("com.ksmobile.cb", "address_bar_edit_text")
}.ToDictionary(n => n.PackageName);
private readonly IAppSettingsService _appSettings;
private long _lastNotificationTime = 0;
private string _lastNotificationUri = null;
public AutofillService()
{
_appSettings = Resolver.Resolve<IAppSettingsService>();
}
public override void OnAccessibilityEvent(AccessibilityEvent e)
{
Enabled = true;
var root = RootInActiveWindow;
if(string.IsNullOrWhiteSpace(e.PackageName) || e.PackageName == SystemUiPackage ||
root?.PackageName != e.PackageName)
try
{
return;
}
var root = RootInActiveWindow;
if(e == null || root == null || string.IsNullOrWhiteSpace(e.PackageName) ||
e.PackageName == SystemUiPackage || root.PackageName != e.PackageName)
{
return;
}
switch(e.EventType)
{
case EventTypes.WindowContentChanged:
case EventTypes.WindowStateChanged:
var cancelNotification = true;
//var testNodes = GetWindowNodes(root, e, n => n.ViewIdResourceName != null && n.Text != null, false);
//var testNodesData = testNodes.Select(n => new { id = n.ViewIdResourceName, text = n.Text });
//testNodes.Dispose();
if(e.PackageName == BitwardenPackage)
{
CancelNotification();
break;
}
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
var cancelNotification = true;
var passwordNodes = GetWindowNodes(root, e, n => n.Password);
if(passwordNodes.Any())
{
var uri = GetUri(root);
if(uri.Contains(BitwardenWebsite))
switch(e.EventType)
{
case EventTypes.ViewFocused:
if(!e.Source.Password || !_appSettings.AutofillPasswordField)
{
break;
}
if(NeedToAutofill(AutofillActivity.LastCredentials, uri))
if(e.PackageName == BitwardenPackage)
{
var allEditTexts = GetWindowNodes(root, e, n => EditText(n));
var usernameEditText = allEditTexts.TakeWhile(n => !n.Password).LastOrDefault();
FillCredentials(usernameEditText, passwordNodes);
CancelNotification(notificationManager);
break;
}
if(ScanAndAutofill(root, e, notificationManager, cancelNotification))
{
CancelNotification(notificationManager);
}
break;
case EventTypes.WindowContentChanged:
case EventTypes.WindowStateChanged:
if(_appSettings.AutofillPasswordField && e.Source.Password)
{
break;
}
else if(_appSettings.AutofillPasswordField && AutofillActivity.LastCredentials == null)
{
if(string.IsNullOrWhiteSpace(_lastNotificationUri))
{
CancelNotification(notificationManager);
break;
}
var uri = GetUri(root);
if(uri != _lastNotificationUri)
{
CancelNotification(notificationManager);
}
else if(uri.StartsWith(App.Constants.AndroidAppProtocol))
{
CancelNotification(notificationManager, 30000);
}
break;
}
if(e.PackageName == BitwardenPackage)
{
CancelNotification(notificationManager);
break;
}
if(_appSettings.AutofillPersistNotification)
{
var uri = GetUri(root);
if(uri != null && !uri.Contains(BitwardenWebsite))
{
var needToFill = NeedToAutofill(AutofillActivity.LastCredentials, uri);
if(needToFill)
{
var passwordNodes = GetWindowNodes(root, e, n => n.Password, false);
needToFill = passwordNodes.Any();
if(needToFill)
{
var allEditTexts = GetWindowNodes(root, e, n => EditText(n), false);
var usernameEditText = allEditTexts.TakeWhile(n => !n.Password).LastOrDefault();
FillCredentials(usernameEditText, passwordNodes);
allEditTexts.Dispose();
usernameEditText.Dispose();
}
passwordNodes.Dispose();
}
if(!needToFill)
{
NotifyToAutofill(uri, notificationManager);
cancelNotification = false;
}
}
AutofillActivity.LastCredentials = null;
}
else
{
NotifyToAutofill(uri);
cancelNotification = false;
cancelNotification = ScanAndAutofill(root, e, notificationManager, cancelNotification);
}
AutofillActivity.LastCredentials = null;
}
if(cancelNotification)
{
CancelNotification(notificationManager);
}
break;
default:
break;
}
if(cancelNotification)
{
CancelNotification();
}
break;
default:
break;
notificationManager?.Dispose();
root.Dispose();
e.Dispose();
}
// Suppress exceptions so that service doesn't crash
catch { }
}
public override void OnInterrupt()
@@ -111,64 +192,86 @@ namespace Bit.Android
}
protected override void OnServiceConnected()
public bool ScanAndAutofill(AccessibilityNodeInfo root, AccessibilityEvent e,
NotificationManager notificationManager, bool cancelNotification)
{
base.OnServiceConnected();
Enabled = true;
var passwordNodes = GetWindowNodes(root, e, n => n.Password, false);
if(passwordNodes.Count > 0)
{
var uri = GetUri(root);
if(uri != null && !uri.Contains(BitwardenWebsite))
{
if(NeedToAutofill(AutofillActivity.LastCredentials, uri))
{
var allEditTexts = GetWindowNodes(root, e, n => EditText(n), false);
var usernameEditText = allEditTexts.TakeWhile(n => !n.Password).LastOrDefault();
FillCredentials(usernameEditText, passwordNodes);
allEditTexts.Dispose();
usernameEditText.Dispose();
}
else
{
NotifyToAutofill(uri, notificationManager);
cancelNotification = false;
}
}
}
AutofillActivity.LastCredentials = null;
passwordNodes.Dispose();
return cancelNotification;
}
public override void OnDestroy()
public void CancelNotification(NotificationManager notificationManager, long limit = 250)
{
base.OnDestroy();
Enabled = false;
}
if(Java.Lang.JavaSystem.CurrentTimeMillis() - _lastNotificationTime < limit)
{
return;
}
private void CancelNotification()
{
var notificationManager = ((NotificationManager)GetSystemService(NotificationService));
notificationManager.Cancel(AutoFillNotificationId);
_lastNotificationUri = null;
notificationManager?.Cancel(AutoFillNotificationId);
}
private string GetUri(AccessibilityNodeInfo root)
{
var uri = string.Concat(App.Constants.AndroidAppProtocol, root.PackageName);
if(BrowserPackages.ContainsKey(root.PackageName))
if(SupportedBrowsers.ContainsKey(root.PackageName))
{
foreach(var addressViewId in BrowserPackages[root.PackageName])
var addressNode = root.FindAccessibilityNodeInfosByViewId(
$"{root.PackageName}:id/{SupportedBrowsers[root.PackageName].UriViewId}").FirstOrDefault();
if(addressNode != null)
{
var addressNode = root.FindAccessibilityNodeInfosByViewId(
$"{root.PackageName}:id/{addressViewId}").FirstOrDefault();
if(addressNode == null)
{
continue;
}
uri = ExtractUri(uri, addressNode);
break;
uri = ExtractUri(uri, addressNode, SupportedBrowsers[root.PackageName]);
addressNode.Dispose();
}
}
return uri;
}
private string ExtractUri(string uri, AccessibilityNodeInfo addressNode)
private string ExtractUri(string uri, AccessibilityNodeInfo addressNode, Browser browser)
{
if(addressNode != null)
if(addressNode?.Text != null)
{
uri = addressNode.Text;
if(!uri.Contains("://"))
uri = browser.GetUriFunction(addressNode.Text).Trim();
if(uri != null && uri.Contains("."))
{
uri = string.Concat("http://", uri);
}
else if(Build.VERSION.SdkInt <= BuildVersionCodes.KitkatWatch)
{
var parts = uri.Split(new string[] { ". " }, StringSplitOptions.None);
if(parts.Length > 1)
if(!uri.Contains("://") && !uri.Contains(" "))
{
var urlPart = parts.FirstOrDefault(p => p.StartsWith("http"));
if(urlPart != null)
uri = string.Concat("http://", uri);
}
else if(Build.VERSION.SdkInt <= BuildVersionCodes.KitkatWatch)
{
var parts = uri.Split(new string[] { ". " }, StringSplitOptions.None);
if(parts.Length > 1)
{
uri = urlPart.Trim();
var urlPart = parts.FirstOrDefault(p => p.StartsWith("http"));
if(urlPart != null)
{
uri = urlPart.Trim();
}
}
}
}
@@ -200,11 +303,17 @@ namespace Bit.Android
private static bool EditText(AccessibilityNodeInfo n)
{
return n.ClassName != null && n.ClassName.Contains("EditText");
return n?.ClassName?.Contains("EditText") ?? false;
}
private void NotifyToAutofill(string uri)
private void NotifyToAutofill(string uri, NotificationManager notificationManager)
{
if(notificationManager == null || string.IsNullOrWhiteSpace(uri))
{
return;
}
var now = Java.Lang.JavaSystem.CurrentTimeMillis();
var intent = new Intent(this, typeof(AutofillActivity));
intent.PutExtra("uri", uri);
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
@@ -215,7 +324,7 @@ namespace Bit.Android
.SetContentTitle(App.Resources.AppResources.BitwardenAutofillService)
.SetContentText(App.Resources.AppResources.BitwardenAutofillServiceNotificationContent)
.SetTicker(App.Resources.AppResources.BitwardenAutofillServiceNotificationContent)
.SetWhen(Java.Lang.JavaSystem.CurrentTimeMillis())
.SetWhen(now)
.SetContentIntent(pendingIntent);
if(Build.VERSION.SdkInt > BuildVersionCodes.KitkatWatch)
@@ -225,16 +334,24 @@ namespace Bit.Android
Resource.Color.primary));
}
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
if(/*Build.VERSION.SdkInt <= BuildVersionCodes.N && */_appSettings.AutofillPersistNotification)
{
builder.SetPriority(-2);
}
_lastNotificationTime = now;
_lastNotificationUri = uri;
notificationManager.Notify(AutoFillNotificationId, builder.Build());
builder.Dispose();
}
private void FillCredentials(AccessibilityNodeInfo usernameNode, IEnumerable<AccessibilityNodeInfo> passwordNodes)
{
FillEditText(usernameNode, AutofillActivity.LastCredentials.Username);
FillEditText(usernameNode, AutofillActivity.LastCredentials?.Username);
foreach(var n in passwordNodes)
{
FillEditText(n, AutofillActivity.LastCredentials.Password);
FillEditText(n, AutofillActivity.LastCredentials?.Password);
}
}
@@ -250,22 +367,63 @@ namespace Bit.Android
editTextNode.PerformAction(global::Android.Views.Accessibility.Action.SetText, bundle);
}
private IEnumerable<AccessibilityNodeInfo> GetWindowNodes(AccessibilityNodeInfo n,
AccessibilityEvent e, Func<AccessibilityNodeInfo, bool> p)
private NodeList GetWindowNodes(AccessibilityNodeInfo n, AccessibilityEvent e,
Func<AccessibilityNodeInfo, bool> condition, bool disposeIfUnused, NodeList nodes = null)
{
if(nodes == null)
{
nodes = new NodeList();
}
if(n != null)
{
if(n.WindowId == e.WindowId && !(n.ViewIdResourceName?.StartsWith(SystemUiPackage) ?? false) && p(n))
var dispose = disposeIfUnused;
if(n.WindowId == e.WindowId && !(n.ViewIdResourceName?.StartsWith(SystemUiPackage) ?? false) && condition(n))
{
yield return n;
dispose = false;
nodes.Add(n);
}
for(int i = 0; i < n.ChildCount; i++)
for(var i = 0; i < n.ChildCount; i++)
{
foreach(var node in GetWindowNodes(n.GetChild(i), e, p))
{
yield return node;
}
GetWindowNodes(n.GetChild(i), e, condition, true, nodes);
}
if(dispose)
{
n.Dispose();
}
}
return nodes;
}
public class Browser
{
public Browser(string packageName, string uriViewId)
{
PackageName = packageName;
UriViewId = uriViewId;
}
public Browser(string packageName, string uriViewId, Func<string, string> getUriFunction)
: this(packageName, uriViewId)
{
GetUriFunction = getUriFunction;
}
public string PackageName { get; set; }
public string UriViewId { get; set; }
public Func<string, string> GetUriFunction { get; set; } = (s) => s;
}
public class NodeList : List<AccessibilityNodeInfo>, IDisposable
{
public void Dispose()
{
foreach(var item in this)
{
item.Dispose();
}
}
}

View File

@@ -18,13 +18,15 @@ namespace Bit.Android.Controls
{
private bool _isPassword;
private bool _toggledPassword;
private bool _isDisposed;
private ExtendedEntry _view;
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
var view = (ExtendedEntry)Element;
_isPassword = view.IsPassword;
_view = (ExtendedEntry)Element;
_isPassword = _view.IsPassword;
if(Control != null)
{
@@ -36,69 +38,74 @@ namespace Bit.Android.Controls
}
}
SetBorder(view);
SetMaxLength(view);
SetReturnType(view);
SetBorder(_view);
SetMaxLength(_view);
SetReturnType(_view);
// Editor Action is called when the return button is pressed
Control.EditorAction += (object sender, TextView.EditorActionEventArgs args) =>
{
if(view.ReturnType != ReturnType.Next)
{
view.Unfocus();
}
Control.EditorAction += Control_EditorAction;
// Call all the methods attached to base_entry event handler Completed
view.InvokeCompleted();
};
if(view.DisableAutocapitalize)
if(_view.DisableAutocapitalize)
{
Control.SetRawInputType(Control.InputType |= InputTypes.TextVariationEmailAddress);
}
if(view.Autocorrect.HasValue)
if(_view.Autocorrect.HasValue)
{
Control.SetRawInputType(Control.InputType |= InputTypes.TextFlagNoSuggestions);
}
view.ToggleIsPassword += (object sender, EventArgs args) =>
{
var cursorStart = Control.SelectionStart;
var cursorEnd = Control.SelectionEnd;
_view.ToggleIsPassword += ToggleIsPassword;
Control.TransformationMethod = _isPassword ? null : new PasswordTransformationMethod();
// set focus
Control.RequestFocus();
if(_toggledPassword)
{
// restore cursor position
Control.SetSelection(cursorStart, cursorEnd);
}
else
{
// set cursor to end
Control.SetSelection(Control.Text.Length);
}
// show keyboard
var app = XLabs.Ioc.Resolver.Resolve<global::Android.App.Application>();
var inputMethodManager =
app.GetSystemService(global::Android.Content.Context.InputMethodService) as InputMethodManager;
inputMethodManager.ShowSoftInput(Control, ShowFlags.Forced);
inputMethodManager.ToggleSoftInput(ShowFlags.Forced, HideSoftInputFlags.ImplicitOnly);
_isPassword = view.IsPasswordFromToggled = !_isPassword;
_toggledPassword = true;
};
if(view.FontFamily == "monospace")
if(_view.FontFamily == "monospace")
{
Control.Typeface = Typeface.Monospace;
}
}
private void ToggleIsPassword(object sender, EventArgs e)
{
var cursorStart = Control.SelectionStart;
var cursorEnd = Control.SelectionEnd;
Control.TransformationMethod = _isPassword ? null : new PasswordTransformationMethod();
// set focus
Control.RequestFocus();
if(_toggledPassword)
{
// restore cursor position
Control.SetSelection(cursorStart, cursorEnd);
}
else
{
// set cursor to end
Control.SetSelection(Control.Text.Length);
}
// show keyboard
var app = XLabs.Ioc.Resolver.Resolve<global::Android.App.Application>();
var inputMethodManager =
app.GetSystemService(global::Android.Content.Context.InputMethodService) as InputMethodManager;
inputMethodManager.ShowSoftInput(Control, ShowFlags.Forced);
inputMethodManager.ToggleSoftInput(ShowFlags.Forced, HideSoftInputFlags.ImplicitOnly);
_isPassword = _view.IsPasswordFromToggled = !_isPassword;
_toggledPassword = true;
}
private void Control_EditorAction(object sender, TextView.EditorActionEventArgs e)
{
if(_view.ReturnType != ReturnType.Next)
{
_view.Unfocus();
}
// Call all the methods attached to base_entry event handler Completed
_view.InvokeCompleted();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var view = (ExtendedEntry)Element;
@@ -124,6 +131,23 @@ namespace Bit.Android.Controls
}
}
protected override void Dispose(bool disposing)
{
if(_isDisposed)
{
return;
}
_isDisposed = true;
if(disposing && Control != null)
{
_view.ToggleIsPassword -= ToggleIsPassword;
Control.EditorAction -= Control_EditorAction;
}
base.Dispose(disposing);
}
private void SetReturnType(ExtendedEntry view)
{
if(view.ReturnType.HasValue)

View File

@@ -1,5 +1,4 @@
using System;
using System.ComponentModel;
using Bit.Android.Controls;
using Bit.App.Controls;
using Xamarin.Forms;

View File

@@ -0,0 +1,72 @@
using System;
using Bit.Android.Controls;
using Bit.App.Controls;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Android.Webkit;
using AWebkit = Android.Webkit;
using Java.Interop;
[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
namespace Bit.Android.Controls
{
public class HybridWebViewRenderer : ViewRenderer<HybridWebView, AWebkit.WebView>
{
private const string JSFunction = "function invokeCSharpAction(data){jsBridge.invokeAction(data);}";
protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e)
{
base.OnElementChanged(e);
if(Control == null)
{
var webView = new AWebkit.WebView(Forms.Context);
webView.Settings.JavaScriptEnabled = true;
SetNativeControl(webView);
}
if(e.OldElement != null)
{
Control.RemoveJavascriptInterface("jsBridge");
var hybridWebView = e.OldElement as HybridWebView;
hybridWebView.Cleanup();
}
if(e.NewElement != null)
{
Control.AddJavascriptInterface(new JSBridge(this), "jsBridge");
Control.LoadUrl(Element.Uri);
InjectJS(JSFunction);
}
}
private void InjectJS(string script)
{
if(Control != null)
{
Control.LoadUrl(string.Format("javascript: {0}", script));
}
}
public class JSBridge : Java.Lang.Object
{
private readonly WeakReference<HybridWebViewRenderer> _hybridWebViewRenderer;
public JSBridge(HybridWebViewRenderer hybridRenderer)
{
_hybridWebViewRenderer = new WeakReference<HybridWebViewRenderer>(hybridRenderer);
}
[JavascriptInterface]
[Export("invokeAction")]
public void InvokeAction(string data)
{
HybridWebViewRenderer hybridRenderer;
if(_hybridWebViewRenderer != null && _hybridWebViewRenderer.TryGetTarget(out hybridRenderer))
{
hybridRenderer.Element.InvokeAction(data);
}
}
}
}
}

View File

@@ -1,774 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Android.Runtime;
using Java.IO;
using Java.Net;
using Java.Security;
using Java.Security.Cert;
using Javax.Net.Ssl;
namespace Xamarin.Android.Net
{
/// <summary>
/// A custom implementation of <see cref="System.Net.Http.HttpClientHandler"/> which internally uses <see cref="Java.Net.HttpURLConnection"/>
/// (or its HTTPS incarnation) to send HTTP requests.
/// </summary>
/// <remarks>
/// <para>Instance of this class is used to configure <see cref="System.Net.Http.HttpClient"/> instance
/// in the following way:
///
/// <example>
/// var handler = new AndroidClientHandler {
/// UseCookies = true,
/// AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
/// };
///
/// var httpClient = new HttpClient (handler);
/// var response = httpClient.GetAsync ("http://example.com")?.Result as AndroidHttpResponseMessage;
/// </example></para>
/// <para>
/// The class supports pre-authentication of requests albeit in a slightly "manual" way. Namely, whenever a request to a server requiring authentication
/// is made and no authentication credentials are provided in the <see cref="PreAuthenticationData"/> property (which is usually the case on the first
/// request), the <see cref="RequestNeedsAuthorization"/> property will return <c>true</c> and the <see cref="RequestedAuthentication"/> property will
/// contain all the authentication information gathered from the server. The application must then fill in the blanks (i.e. the credentials) and re-send
/// the request configured to perform pre-authentication. The reason for this manual process is that the underlying Java HTTP client API supports only a
/// single, VM-wide, authentication handler which cannot be configured to handle credentials for several requests. AndroidClientHandler, therefore, implements
/// the authentication in managed .NET code. Message handler supports both Basic and Digest authentication. If an authentication scheme that's not supported
/// by AndroidClientHandler is requested by the server, the application can provide its own authentication module (<see cref="AuthenticationData"/>,
/// <see cref="PreAuthenticationData"/>) to handle the protocol authorization.</para>
/// <para>AndroidClientHandler also supports requests to servers with "invalid" (e.g. self-signed) SSL certificates. Since this process is a bit convoluted using
/// the Java APIs, AndroidClientHandler defines two ways to handle the situation. First, easier, is to store the necessary certificates (either CA or server certificates)
/// in the <see cref="TrustedCerts"/> collection or, after deriving a custom class from AndroidClientHandler, by overriding one or more methods provided for this purpose
/// (<see cref="ConfigureTrustManagerFactory"/>, <see cref="ConfigureKeyManagerFactory"/> and <see cref="ConfigureKeyStore"/>). The former method should be sufficient
/// for most use cases, the latter allows the application to provide fully customized key store, trust manager and key manager, if needed. Note that the instance of
/// AndroidClientHandler configured to accept an "invalid" certificate from the particular server will most likely fail to validate certificates from other servers (even
/// if they use a certificate with a fully validated trust chain) unless you store the CA certificates from your Android system in <see cref="TrustedCerts"/> along with
/// the self-signed certificate(s).</para>
/// </remarks>
public class CustomAndroidClientHandler : HttpClientHandler
{
sealed class RequestRedirectionState
{
public Uri NewUrl;
public int RedirectCounter;
public HttpMethod Method;
}
internal const string LOG_APP = "monodroid-net";
const string GZIP_ENCODING = "gzip";
const string DEFLATE_ENCODING = "deflate";
const string IDENTITY_ENCODING = "identity";
static readonly HashSet<string> known_content_headers = new HashSet<string>(StringComparer.OrdinalIgnoreCase) {
"Allow",
"Content-Disposition",
"Content-Encoding",
"Content-Language",
"Content-Length",
"Content-Location",
"Content-MD5",
"Content-Range",
"Content-Type",
"Expires",
"Last-Modified"
};
static readonly List<IAndroidAuthenticationModule> authModules = new List<IAndroidAuthenticationModule> {
new AuthModuleBasic (),
// COMMENTED OUT: Kyle
//new AuthModuleDigest ()
};
bool disposed;
// Now all hail Java developers! Get this... HttpURLClient defaults to accepting AND
// uncompressing the gzip content encoding UNLESS you set the Accept-Encoding header to ANY
// value. So if we set it to 'gzip' below we WILL get gzipped stream but HttpURLClient will NOT
// uncompress it any longer, doh. And they don't support 'deflate' so we need to handle it ourselves.
bool decompress_here;
/// <summary>
/// <para>
/// Gets or sets the pre authentication data for the request. This property must be set by the application
/// before the request is made. Generally the value can be taken from <see cref="RequestedAuthentication"/>
/// after the initial request, without any authentication data, receives the authorization request from the
/// server. The application must then store credentials in instance of <see cref="AuthenticationData"/> and
/// assign the instance to this propery before retrying the request.
/// </para>
/// <para>
/// The property is never set by AndroidClientHandler.
/// </para>
/// </summary>
/// <value>The pre authentication data.</value>
public AuthenticationData PreAuthenticationData { get; set; }
/// <summary>
/// If the website requires authentication, this property will contain data about each scheme supported
/// by the server after the response. Note that unauthorized request will return a valid response - you
/// need to check the status code and and (re)configure AndroidClientHandler instance accordingly by providing
/// both the credentials and the authentication scheme by setting the <see cref="PreAuthenticationData"/>
/// property. If AndroidClientHandler is not able to detect the kind of authentication scheme it will store an
/// instance of <see cref="AuthenticationData"/> with its <see cref="AuthenticationData.Scheme"/> property
/// set to <c>AuthenticationScheme.Unsupported</c> and the application will be responsible for providing an
/// instance of <see cref="IAndroidAuthenticationModule"/> which handles this kind of authorization scheme
/// (<see cref="AuthenticationData.AuthModule"/>
/// </summary>
public IList<AuthenticationData> RequestedAuthentication { get; private set; }
/// <summary>
/// Server authentication response indicates that the request to authorize comes from a proxy if this property is <c>true</c>.
/// All the instances of <see cref="AuthenticationData"/> stored in the <see cref="RequestedAuthentication"/> property will
/// have their <see cref="AuthenticationData.UseProxyAuthentication"/> preset to the same value as this property.
/// </summary>
public bool ProxyAuthenticationRequested { get; private set; }
/// <summary>
/// If <c>true</c> then the server requested authorization and the application must use information
/// found in <see cref="RequestedAuthentication"/> to set the value of <see cref="PreAuthenticationData"/>
/// </summary>
public bool RequestNeedsAuthorization
{
get { return RequestedAuthentication?.Count > 0; }
}
/// <summary>
/// <para>
/// If the request is to the server protected with a self-signed (or otherwise untrusted) SSL certificate, the request will
/// fail security chain verification unless the application provides either the CA certificate of the entity which issued the
/// server's certificate or, alternatively, provides the server public key. Whichever the case, the certificate(s) must be stored
/// in this property in order for AndroidClientHandler to configure the request to accept the server certificate.</para>
/// <para>AndroidClientHandler uses a custom <see cref="KeyStore"/> and <see cref="TrustManagerFactory"/> to configure the connection.
/// If, however, the application requires finer control over the SSL configuration (e.g. it implements its own TrustManager) then
/// it should leave this property empty and instead derive a custom class from AndroidClientHandler and override, as needed, the
/// <see cref="ConfigureTrustManagerFactory"/>, <see cref="ConfigureKeyManagerFactory"/> and <see cref="ConfigureKeyStore"/> methods
/// instead</para>
/// </summary>
/// <value>The trusted certs.</value>
public IList<Certificate> TrustedCerts { get; set; }
protected override void Dispose(bool disposing)
{
disposed = true;
base.Dispose(disposing);
}
protected void AssertSelf()
{
if(!disposed)
return;
throw new ObjectDisposedException(nameof(AndroidClientHandler));
}
string EncodeUrl(Uri url)
{
if(url == null)
return String.Empty;
if(String.IsNullOrEmpty(url.Query))
return Uri.EscapeUriString(url.ToString());
// UriBuilder takes care of encoding everything properly
var bldr = new UriBuilder(url);
if(url.IsDefaultPort)
bldr.Port = -1; // Avoids adding :80 or :443 to the host name in the result
// bldr.Uri.ToString () would ruin the good job UriBuilder did
return bldr.ToString();
}
/// <summary>
/// Creates, configures and processes an asynchronous request to the indicated resource.
/// </summary>
/// <returns>Task in which the request is executed</returns>
/// <param name="request">Request provided by <see cref="System.Net.Http.HttpClient"/></param>
/// <param name="cancellationToken">Cancellation token.</param>
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
AssertSelf();
if(request == null)
throw new ArgumentNullException(nameof(request));
if(!request.RequestUri.IsAbsoluteUri)
throw new ArgumentException("Must represent an absolute URI", "request");
var redirectState = new RequestRedirectionState
{
NewUrl = request.RequestUri,
RedirectCounter = 0,
Method = request.Method
};
while(true)
{
URL java_url = new URL(EncodeUrl(redirectState.NewUrl));
URLConnection java_connection = java_url.OpenConnection();
HttpURLConnection httpConnection = await SetupRequestInternal(request, java_connection);
HttpResponseMessage response = await ProcessRequest(request, java_url, httpConnection, cancellationToken, redirectState);
if(response != null)
return response;
if(redirectState.NewUrl == null)
throw new InvalidOperationException("Request redirected but no new URI specified");
request.Method = redirectState.Method;
}
}
Task<HttpResponseMessage> ProcessRequest(HttpRequestMessage request, URL javaUrl, HttpURLConnection httpConnection, CancellationToken cancellationToken, RequestRedirectionState redirectState)
{
cancellationToken.ThrowIfCancellationRequested();
httpConnection.InstanceFollowRedirects = false; // We handle it ourselves
RequestedAuthentication = null;
ProxyAuthenticationRequested = false;
return DoProcessRequest(request, javaUrl, httpConnection, cancellationToken, redirectState);
}
async Task<HttpResponseMessage> DoProcessRequest(HttpRequestMessage request, URL javaUrl, HttpURLConnection httpConnection, CancellationToken cancellationToken, RequestRedirectionState redirectState)
{
if(cancellationToken.IsCancellationRequested)
{
cancellationToken.ThrowIfCancellationRequested();
}
try
{
await Task.WhenAny(
httpConnection.ConnectAsync(),
Task.Run(() => { cancellationToken.WaitHandle.WaitOne(); }))
.ConfigureAwait(false);
}
catch(Java.Net.ConnectException ex)
{
// Wrap it nicely in a "standard" exception so that it's compatible with HttpClientHandler
throw new WebException(ex.Message, ex, WebExceptionStatus.ConnectFailure, null);
}
if(cancellationToken.IsCancellationRequested)
{
httpConnection.Disconnect();
cancellationToken.ThrowIfCancellationRequested();
}
cancellationToken.Register(httpConnection.Disconnect);
if(httpConnection.DoOutput)
{
using(var stream = await request.Content.ReadAsStreamAsync())
{
await stream.CopyToAsync(httpConnection.OutputStream, 4096, cancellationToken)
.ConfigureAwait(false);
}
}
if(cancellationToken.IsCancellationRequested)
{
httpConnection.Disconnect();
cancellationToken.ThrowIfCancellationRequested();
}
var statusCode = await Task.Run(() => (HttpStatusCode)httpConnection.ResponseCode).ConfigureAwait(false);
var connectionUri = new Uri(httpConnection.URL.ToString());
// If the request was redirected we need to put the new URL in the request
request.RequestUri = connectionUri;
var ret = new AndroidHttpResponseMessage(javaUrl, httpConnection)
{
RequestMessage = request,
ReasonPhrase = httpConnection.ResponseMessage,
StatusCode = statusCode,
};
bool disposeRet;
if(HandleRedirect(statusCode, httpConnection, redirectState, out disposeRet))
{
if(disposeRet)
{
ret.Dispose();
ret = null;
}
return ret;
}
switch(statusCode)
{
case HttpStatusCode.Unauthorized:
case HttpStatusCode.ProxyAuthenticationRequired:
// We don't resend the request since that would require new set of credentials if the
// ones provided in Credentials are invalid (or null) and that, in turn, may require asking the
// user which is not something that should be taken care of by us and in this
// context. The application should be responsible for this.
// HttpClientHandler throws an exception in this instance, but I think it's not a good
// idea. We'll return the response message with all the information required by the
// application to fill in the blanks and provide the requested credentials instead.
//
// We return the body of the response too, but the Java client will throw
// a FileNotFound exception if we attempt to access the input stream.
// Instead we try to read the error stream and return an default message if the error stream isn't readable.
ret.Content = GetErrorContent(httpConnection, new StringContent("Unauthorized", Encoding.ASCII));
CopyHeaders(httpConnection, ret);
if(ret.Headers.WwwAuthenticate != null)
{
ProxyAuthenticationRequested = false;
CollectAuthInfo(ret.Headers.WwwAuthenticate);
}
else if(ret.Headers.ProxyAuthenticate != null)
{
ProxyAuthenticationRequested = true;
CollectAuthInfo(ret.Headers.ProxyAuthenticate);
}
// COMMENTED OUT: Kyle
//ret.RequestedAuthentication = RequestedAuthentication;
return ret;
}
if(!IsErrorStatusCode(statusCode))
{
ret.Content = GetContent(httpConnection, httpConnection.InputStream);
}
else
{
// For 400 >= response code <= 599 the Java client throws the FileNotFound exception when attempting to read from the input stream.
// Instead we try to read the error stream and return an empty string if the error stream isn't readable.
ret.Content = GetErrorContent(httpConnection, new StringContent(String.Empty, Encoding.ASCII));
}
CopyHeaders(httpConnection, ret);
IEnumerable<string> cookieHeaderValue;
if(!UseCookies || CookieContainer == null || !ret.Headers.TryGetValues("Set-Cookie", out cookieHeaderValue) || cookieHeaderValue == null)
{
return ret;
}
try
{
CookieContainer.SetCookies(connectionUri, String.Join(",", cookieHeaderValue));
}
catch(Exception ex)
{
// We don't want to terminate the response because of a bad cookie, hence just reporting
// the issue. We might consider adding a virtual method to let the user handle the
// issue, but not sure if it's really needed. Set-Cookie header will be part of the
// header collection so the user can always examine it if they spot an error.
}
return ret;
}
HttpContent GetErrorContent(HttpURLConnection httpConnection, HttpContent fallbackContent)
{
var contentStream = httpConnection.ErrorStream;
if(contentStream != null)
{
return GetContent(httpConnection, contentStream);
}
return fallbackContent;
}
HttpContent GetContent(URLConnection httpConnection, Stream contentStream)
{
Stream inputStream = new BufferedStream(contentStream);
if(decompress_here)
{
string[] encodings = httpConnection.ContentEncoding?.Split(',');
if(encodings != null)
{
if(encodings.Contains(GZIP_ENCODING, StringComparer.OrdinalIgnoreCase))
inputStream = new GZipStream(inputStream, CompressionMode.Decompress);
else if(encodings.Contains(DEFLATE_ENCODING, StringComparer.OrdinalIgnoreCase))
inputStream = new DeflateStream(inputStream, CompressionMode.Decompress);
}
}
return new StreamContent(inputStream);
}
bool HandleRedirect(HttpStatusCode redirectCode, HttpURLConnection httpConnection, RequestRedirectionState redirectState, out bool disposeRet)
{
if(!AllowAutoRedirect)
{
disposeRet = false;
return true; // We shouldn't follow and there's no data to fetch, just return
}
disposeRet = true;
redirectState.NewUrl = null;
switch(redirectCode)
{
case HttpStatusCode.MultipleChoices: // 300
break;
case HttpStatusCode.Moved: // 301
case HttpStatusCode.Redirect: // 302
case HttpStatusCode.SeeOther: // 303
redirectState.Method = HttpMethod.Get;
break;
case HttpStatusCode.TemporaryRedirect: // 307
break;
default:
if((int)redirectCode >= 300 && (int)redirectCode < 400)
throw new InvalidOperationException($"HTTP Redirection status code {redirectCode} ({(int)redirectCode}) not supported");
return false;
}
IDictionary<string, IList<string>> headers = httpConnection.HeaderFields;
IList<string> locationHeader;
if(!headers.TryGetValue("Location", out locationHeader) || locationHeader == null || locationHeader.Count == 0)
throw new InvalidOperationException($"HTTP connection redirected with code {redirectCode} ({(int)redirectCode}) but no Location header found in response");
redirectState.RedirectCounter++;
if(redirectState.RedirectCounter >= MaxAutomaticRedirections)
throw new WebException($"Maximum automatic redirections exceeded (allowed {MaxAutomaticRedirections}, redirected {redirectState.RedirectCounter} times)");
Uri location = new Uri(locationHeader[0], UriKind.Absolute);
redirectState.NewUrl = location;
return true;
}
bool IsErrorStatusCode(HttpStatusCode statusCode)
{
return (int)statusCode >= 400 && (int)statusCode <= 599;
}
void CollectAuthInfo(HttpHeaderValueCollection<AuthenticationHeaderValue> headers)
{
var authData = new List<AuthenticationData>(headers.Count);
foreach(AuthenticationHeaderValue ahv in headers)
{
var data = new AuthenticationData
{
Scheme = GetAuthScheme(ahv.Scheme),
// COMMENTED OUT: Kyle
//Challenge = $"{ahv.Scheme} {ahv.Parameter}",
UseProxyAuthentication = ProxyAuthenticationRequested
};
authData.Add(data);
}
RequestedAuthentication = authData.AsReadOnly();
}
AuthenticationScheme GetAuthScheme(string scheme)
{
if(String.Compare("basic", scheme, StringComparison.OrdinalIgnoreCase) == 0)
return AuthenticationScheme.Basic;
if(String.Compare("digest", scheme, StringComparison.OrdinalIgnoreCase) == 0)
return AuthenticationScheme.Digest;
return AuthenticationScheme.Unsupported;
}
void CopyHeaders(HttpURLConnection httpConnection, HttpResponseMessage response)
{
IDictionary<string, IList<string>> headers = httpConnection.HeaderFields;
foreach(string key in headers.Keys)
{
if(key == null) // First header entry has null key, it corresponds to the response message
continue;
HttpHeaders item_headers;
string kind;
if(known_content_headers.Contains(key))
{
kind = "content";
item_headers = response.Content.Headers;
}
else
{
kind = "response";
item_headers = response.Headers;
}
item_headers.TryAddWithoutValidation(key, headers[key]);
}
}
/// <summary>
/// Configure the <see cref="HttpURLConnection"/> before the request is sent. This method is meant to be overriden
/// by applications which need to perform some extra configuration steps on the connection. It is called with all
/// the request headers set, pre-authentication performed (if applicable) but before the request body is set
/// (e.g. for POST requests). The default implementation in AndroidClientHandler does nothing.
/// </summary>
/// <param name="request">Request data</param>
/// <param name="conn">Pre-configured connection instance</param>
protected virtual Task SetupRequest(HttpRequestMessage request, HttpURLConnection conn)
{
return Task.Factory.StartNew(AssertSelf);
}
/// <summary>
/// Configures the key store. The <paramref name="keyStore"/> parameter is set to instance of <see cref="KeyStore"/>
/// created using the <see cref="KeyStore.DefaultType"/> type and with populated with certificates provided in the <see cref="TrustedCerts"/>
/// property. AndroidClientHandler implementation simply returns the instance passed in the <paramref name="keyStore"/> parameter
/// </summary>
/// <returns>The key store.</returns>
/// <param name="keyStore">Key store to configure.</param>
protected virtual KeyStore ConfigureKeyStore(KeyStore keyStore)
{
AssertSelf();
return keyStore;
}
/// <summary>
/// Create and configure an instance of <see cref="KeyManagerFactory"/>. The <paramref name="keyStore"/> parameter is set to the
/// return value of the <see cref="ConfigureKeyStore"/> method, so it might be null if the application overrode the method and provided
/// no key store. It will not be <c>null</c> when the default implementation is used. The application can return <c>null</c> here since
/// KeyManagerFactory is not required for the custom SSL configuration, but it might be used by the application to implement a more advanced
/// mechanism of key management.
/// </summary>
/// <returns>The key manager factory or <c>null</c>.</returns>
/// <param name="keyStore">Key store.</param>
protected virtual KeyManagerFactory ConfigureKeyManagerFactory(KeyStore keyStore)
{
AssertSelf();
return null;
}
/// <summary>
/// Create and configure an instance of <see cref="TrustManagerFactory"/>. The <paramref name="keyStore"/> parameter is set to the
/// return value of the <see cref="ConfigureKeyStore"/> method, so it might be null if the application overrode the method and provided
/// no key store. It will not be <c>null</c> when the default implementation is used. The application can return <c>null</c> from this
/// method in which case AndroidClientHandler will create its own instance of the trust manager factory provided that the <see cref="TrustCerts"/>
/// list contains at least one valid certificate. If there are no valid certificates and this method returns <c>null</c>, no custom
/// trust manager will be created since that would make all the HTTPS requests fail.
/// </summary>
/// <returns>The trust manager factory.</returns>
/// <param name="keyStore">Key store.</param>
protected virtual TrustManagerFactory ConfigureTrustManagerFactory(KeyStore keyStore)
{
AssertSelf();
return null;
}
void AppendEncoding(string encoding, ref List<string> list)
{
if(list == null)
list = new List<string>();
if(list.Contains(encoding))
return;
list.Add(encoding);
}
async Task<HttpURLConnection> SetupRequestInternal(HttpRequestMessage request, URLConnection conn)
{
if(conn == null)
throw new ArgumentNullException(nameof(conn));
var httpConnection = conn.JavaCast<HttpURLConnection>();
if(httpConnection == null)
throw new InvalidOperationException($"Unsupported URL scheme {conn.URL.Protocol}");
httpConnection.RequestMethod = request.Method.ToString();
// SSL context must be set up as soon as possible, before adding any content or
// headers. Otherwise Java won't use the socket factory
SetupSSL(httpConnection as HttpsURLConnection);
if(request.Content != null)
AddHeaders(httpConnection, request.Content.Headers);
AddHeaders(httpConnection, request.Headers);
List<string> accept_encoding = null;
decompress_here = false;
if((AutomaticDecompression & DecompressionMethods.GZip) != 0)
{
AppendEncoding(GZIP_ENCODING, ref accept_encoding);
decompress_here = true;
}
if((AutomaticDecompression & DecompressionMethods.Deflate) != 0)
{
AppendEncoding(DEFLATE_ENCODING, ref accept_encoding);
decompress_here = true;
}
if(AutomaticDecompression == DecompressionMethods.None)
{
accept_encoding?.Clear();
AppendEncoding(IDENTITY_ENCODING, ref accept_encoding); // Turns off compression for the Java client
}
if(accept_encoding?.Count > 0)
httpConnection.SetRequestProperty("Accept-Encoding", String.Join(",", accept_encoding));
if(UseCookies && CookieContainer != null)
{
string cookieHeaderValue = CookieContainer.GetCookieHeader(request.RequestUri);
if(!String.IsNullOrEmpty(cookieHeaderValue))
httpConnection.SetRequestProperty("Cookie", cookieHeaderValue);
}
HandlePreAuthentication(httpConnection);
await SetupRequest(request, httpConnection);
SetupRequestBody(httpConnection, request);
return httpConnection;
}
void SetupSSL(HttpsURLConnection httpsConnection)
{
if(httpsConnection == null)
return;
KeyStore keyStore = KeyStore.GetInstance(KeyStore.DefaultType);
keyStore.Load(null, null);
bool gotCerts = TrustedCerts?.Count > 0;
if(gotCerts)
{
for(int i = 0; i < TrustedCerts.Count; i++)
{
Certificate cert = TrustedCerts[i];
if(cert == null)
continue;
keyStore.SetCertificateEntry($"ca{i}", cert);
}
}
keyStore = ConfigureKeyStore(keyStore);
KeyManagerFactory kmf = ConfigureKeyManagerFactory(keyStore);
TrustManagerFactory tmf = ConfigureTrustManagerFactory(keyStore);
if(tmf == null)
{
// If there are no certs and no trust manager factory, we can't use a custom manager
// because it will cause all the HTTPS requests to fail because of unverified trust
// chain
if(!gotCerts)
return;
tmf = TrustManagerFactory.GetInstance(TrustManagerFactory.DefaultAlgorithm);
tmf.Init(keyStore);
}
SSLContext context = SSLContext.GetInstance("TLS");
context.Init(kmf?.GetKeyManagers(), tmf.GetTrustManagers(), null);
httpsConnection.SSLSocketFactory = context.SocketFactory;
}
void HandlePreAuthentication(HttpURLConnection httpConnection)
{
AuthenticationData data = PreAuthenticationData;
if(!PreAuthenticate || data == null)
return;
ICredentials creds = data.UseProxyAuthentication ? Proxy?.Credentials : Credentials;
if(creds == null)
{
return;
}
IAndroidAuthenticationModule auth = data.Scheme == AuthenticationScheme.Unsupported ? data.AuthModule : authModules.Find(m => m?.Scheme == data.Scheme);
if(auth == null)
{
return;
}
Authorization authorization = auth.Authenticate(data.Challenge, httpConnection, creds);
if(authorization == null)
{
return;
}
httpConnection.SetRequestProperty(data.UseProxyAuthentication ? "Proxy-Authorization" : "Authorization", authorization.Message);
}
void AddHeaders(HttpURLConnection conn, HttpHeaders headers)
{
if(headers == null)
return;
foreach(KeyValuePair<string, IEnumerable<string>> header in headers)
{
conn.SetRequestProperty(header.Key, header.Value != null ? String.Join(",", header.Value) : String.Empty);
}
}
void SetupRequestBody(HttpURLConnection httpConnection, HttpRequestMessage request)
{
if(request.Content == null)
{
// Pilfered from System.Net.Http.HttpClientHandler:SendAync
if(HttpMethod.Post.Equals(request.Method) || HttpMethod.Put.Equals(request.Method) || HttpMethod.Delete.Equals(request.Method))
{
// Explicitly set this to make sure we're sending a "Content-Length: 0" header.
// This fixes the issue that's been reported on the forums:
// http://forums.xamarin.com/discussion/17770/length-required-error-in-http-post-since-latest-release
httpConnection.SetRequestProperty("Content-Length", "0");
}
return;
}
httpConnection.DoOutput = true;
long? contentLength = request.Content.Headers.ContentLength;
if(contentLength != null)
httpConnection.SetFixedLengthStreamingMode((int)contentLength);
else
httpConnection.SetChunkedStreamingMode(0);
}
}
sealed class AuthModuleBasic : IAndroidAuthenticationModule
{
public AuthenticationScheme Scheme { get; } = AuthenticationScheme.Basic;
public string AuthenticationType { get; } = "Basic";
public bool CanPreAuthenticate { get; } = true;
public Authorization Authenticate(string challenge, HttpURLConnection request, ICredentials credentials)
{
string header = challenge?.Trim();
if(credentials == null || String.IsNullOrEmpty(header))
return null;
if(header.IndexOf("basic", StringComparison.OrdinalIgnoreCase) == -1)
return null;
return InternalAuthenticate(request, credentials);
}
public Authorization PreAuthenticate(HttpURLConnection request, ICredentials credentials)
{
return InternalAuthenticate(request, credentials);
}
Authorization InternalAuthenticate(HttpURLConnection request, ICredentials credentials)
{
if(request == null || credentials == null)
return null;
NetworkCredential cred = credentials.GetCredential(new Uri(request.URL.ToString()), AuthenticationType.ToLowerInvariant());
if(cred == null)
return null;
if(String.IsNullOrEmpty(cred.UserName))
return null;
string domain = cred.Domain?.Trim();
string response = String.Empty;
// If domain is set, MS sends "domain\user:password".
if(!String.IsNullOrEmpty(domain))
response = domain + "\\";
response += cred.UserName + ":" + cred.Password;
return new Authorization($"{AuthenticationType} {Convert.ToBase64String(Encoding.ASCII.GetBytes(response))}");
}
}
}

View File

@@ -1 +0,0 @@
XA_HTTP_CLIENT_HANDLER_TYPE=Xamarin.Android.Net.AndroidClientHandler

View File

@@ -5,7 +5,6 @@ using Android.Views;
using Android.OS;
using Bit.App.Abstractions;
using XLabs.Ioc;
using Plugin.Fingerprint.Abstractions;
using Plugin.Settings.Abstractions;
using Plugin.Connectivity.Abstractions;
using Acr.UserDialogs;
@@ -15,6 +14,11 @@ using Xamarin.Forms.Platform.Android;
using Xamarin.Forms;
using System.Threading.Tasks;
using Bit.App.Models.Page;
using Bit.App;
using Android.Nfc;
using Android.Views.InputMethods;
using System.IO;
using System.Linq;
namespace Bit.Android
{
@@ -25,25 +29,14 @@ namespace Bit.Android
public class MainActivity : FormsAppCompatActivity
{
private const string HockeyAppId = "d3834185b4a643479047b86c65293d42";
private DateTime? _lastAction;
private Java.Util.Regex.Pattern _otpPattern = Java.Util.Regex.Pattern.Compile("^.*?([cbdefghijklnrtuv]{32,64})$");
private IDeviceActionService _deviceActionService;
private ISettings _settings;
protected override void OnCreate(Bundle bundle)
{
string uri = null;
if(!Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory) && Intent.HasExtra("uri") && Intent.HasExtra("ts"))
{
var tsDiff = Java.Lang.JavaSystem.CurrentTimeMillis() - Intent.GetLongExtra("ts", 0);
if(tsDiff < 5000)
{
uri = Intent.GetStringExtra("uri");
}
// Attempt to clear intent for future
Intent.ReplaceExtras(new Bundle());
Intent.SetAction(string.Empty);
Intent.SetData(null);
Intent.SetFlags(0);
}
var uri = Intent.GetStringExtra("uri");
if(!Resolver.IsSet)
{
MainApplication.SetIoc(Application);
@@ -63,7 +56,10 @@ namespace Bit.Android
Console.WriteLine("A OnCreate");
Window.SetSoftInputMode(SoftInput.StateHidden);
Window.AddFlags(WindowManagerFlags.Secure);
if(!App.Utilities.Helpers.InDebugMode())
{
Window.AddFlags(WindowManagerFlags.Secure);
}
var appIdService = Resolver.Resolve<IAppIdService>();
var authService = Resolver.Resolve<IAuthService>();
@@ -76,6 +72,8 @@ namespace Bit.Android
typeof(Color).GetProperty("Accent", BindingFlags.Public | BindingFlags.Static)
.SetValue(null, Color.FromHex("d2d6de"));
_deviceActionService = Resolver.Resolve<IDeviceActionService>();
_settings = Resolver.Resolve<ISettings>();
LoadApplication(new App.App(
uri,
Resolver.Resolve<IAuthService>(),
@@ -83,12 +81,19 @@ namespace Bit.Android
Resolver.Resolve<IUserDialogs>(),
Resolver.Resolve<IDatabaseService>(),
Resolver.Resolve<ISyncService>(),
Resolver.Resolve<IFingerprint>(),
Resolver.Resolve<ISettings>(),
_settings,
Resolver.Resolve<ILockService>(),
Resolver.Resolve<IGoogleAnalyticsService>(),
Resolver.Resolve<ILocalizeService>(),
Resolver.Resolve<IAppInfoService>()));
Resolver.Resolve<IAppInfoService>(),
Resolver.Resolve<IAppSettingsService>(),
_deviceActionService));
MessagingCenter.Subscribe<Xamarin.Forms.Application>(
Xamarin.Forms.Application.Current, "DismissKeyboard", (sender) =>
{
DismissKeyboard();
});
MessagingCenter.Subscribe<Xamarin.Forms.Application>(Xamarin.Forms.Application.Current, "RateApp", (sender) =>
{
@@ -110,6 +115,18 @@ namespace Bit.Android
{
MoveTaskToBack(true);
});
MessagingCenter.Subscribe<Xamarin.Forms.Application, string>(
Xamarin.Forms.Application.Current, "LaunchApp", (sender, args) =>
{
LaunchApp(args);
});
MessagingCenter.Subscribe<Xamarin.Forms.Application, bool>(
Xamarin.Forms.Application.Current, "ListenYubiKeyOTP", (sender, listen) =>
{
ListenYubiKey(listen);
});
}
private void ReturnCredentials(VaultListPageModel.Login login)
@@ -121,6 +138,13 @@ namespace Bit.Android
}
else
{
var isPremium = Resolver.Resolve<ITokenService>()?.TokenPremium ?? false;
var autoCopyEnabled = !_settings.GetValueOrDefault(Constants.SettingDisableTotpCopy, false);
if(isPremium && autoCopyEnabled && _deviceActionService != null && login.Totp.Value != null)
{
_deviceActionService.CopyToClipboard(App.Utilities.Crypto.Totp(login.Totp.Value));
}
data.PutExtra("uri", login.Uri.Value);
data.PutExtra("username", login.Username);
data.PutExtra("password", login.Password.Value);
@@ -142,6 +166,7 @@ namespace Bit.Android
{
Console.WriteLine("A OnPause");
base.OnPause();
ListenYubiKey(false);
}
protected override void OnDestroy()
@@ -176,6 +201,68 @@ namespace Bit.Android
// workaround for app compat bug
// ref https://bugzilla.xamarin.com/show_bug.cgi?id=36907
Task.Delay(10).Wait();
if(Utilities.NfcEnabled())
{
MessagingCenter.Send(Xamarin.Forms.Application.Current, "ResumeYubiKey");
}
}
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
Console.WriteLine("A OnNewIntent");
ParseYubiKey(intent.DataString);
}
public async override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
if(requestCode == Constants.SelectFilePermissionRequestCode)
{
if(grantResults.Any(r => r != Permission.Granted))
{
MessagingCenter.Send(Xamarin.Forms.Application.Current, "SelectFileCameraPermissionDenied");
}
await _deviceActionService.SelectFileAsync();
return;
}
ZXing.Net.Mobile.Forms.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
if(requestCode == Constants.SelectFileRequestCode && resultCode == Result.Ok)
{
global::Android.Net.Uri uri = null;
string fileName = null;
if(data != null && data.Data != null)
{
uri = data.Data;
fileName = Utilities.GetFileName(ApplicationContext, uri);
}
else
{
// camera
var root = new Java.IO.File(global::Android.OS.Environment.ExternalStorageDirectory, "bitwarden");
var file = new Java.IO.File(root, "temp_camera_photo.jpg");
uri = global::Android.Net.Uri.FromFile(file);
fileName = $"photo_{DateTime.UtcNow.ToString("yyyyMMddHHmmss")}.jpg";
}
if(uri == null)
{
return;
}
using(var stream = ContentResolver.OpenInputStream(uri))
using(var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
MessagingCenter.Send(Xamarin.Forms.Application.Current, "SelectFileResult",
new Tuple<byte[], string>(memoryStream.ToArray(), fileName ?? "unknown_file_name"));
}
}
}
public void RateApp()
@@ -215,5 +302,80 @@ namespace Bit.Android
var intent = new Intent(global::Android.Provider.Settings.ActionAccessibilitySettings);
StartActivity(intent);
}
private void LaunchApp(string packageName)
{
if(_lastAction.LastActionWasRecent())
{
return;
}
_lastAction = DateTime.UtcNow;
packageName = packageName.Replace("androidapp://", string.Empty);
var launchIntent = PackageManager.GetLaunchIntentForPackage(packageName);
if(launchIntent == null)
{
var dialog = Resolver.Resolve<IUserDialogs>();
dialog.Alert(string.Format(App.Resources.AppResources.CannotOpenApp, packageName));
}
else
{
StartActivity(launchIntent);
}
}
private void ListenYubiKey(bool listen)
{
if(!Utilities.NfcEnabled())
{
return;
}
var adapter = NfcAdapter.GetDefaultAdapter(this);
if(listen)
{
var intent = new Intent(this, Class);
intent.AddFlags(ActivityFlags.SingleTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, 0);
// register for all NDEF tags starting with http och https
var ndef = new IntentFilter(NfcAdapter.ActionNdefDiscovered);
ndef.AddDataScheme("http");
ndef.AddDataScheme("https");
var filters = new IntentFilter[] { ndef };
// register for foreground dispatch so we'll receive tags according to our intent filters
adapter.EnableForegroundDispatch(this, pendingIntent, filters, null);
}
else
{
adapter.DisableForegroundDispatch(this);
}
}
private void ParseYubiKey(string data)
{
if(data == null)
{
return;
}
var otpMatch = _otpPattern.Matcher(data);
if(otpMatch.Matches())
{
var otp = otpMatch.Group(1);
MessagingCenter.Send(Xamarin.Forms.Application.Current, "GotYubiKeyOTP", otp);
}
}
private void DismissKeyboard()
{
try
{
var imm = (InputMethodManager)GetSystemService(InputMethodService);
imm.HideSoftInputFromWindow(CurrentFocus.WindowToken, 0);
}
catch { }
}
}
}

View File

@@ -8,7 +8,6 @@ using Bit.Android.Services;
using Bit.App.Abstractions;
using Bit.App.Repositories;
using Bit.App.Services;
using Microsoft.Practices.Unity;
using Plugin.Connectivity;
using Plugin.CurrentActivity;
using Plugin.Fingerprint;
@@ -16,10 +15,11 @@ using Plugin.Settings;
using PushNotification.Plugin;
using PushNotification.Plugin.Abstractions;
using XLabs.Ioc;
using XLabs.Ioc.Unity;
using System.Threading.Tasks;
using Plugin.Settings.Abstractions;
using Xamarin.Android.Net;
using FFImageLoading.Forms.Droid;
using XLabs.Ioc.SimpleInjectorContainer;
using SimpleInjector;
namespace Bit.Android
{
@@ -38,8 +38,7 @@ namespace Bit.Android
public MainApplication(IntPtr handle, JniHandleOwnership transer)
: base(handle, transer)
{
// NOTE: This is just here to stop the linker from removing AndroidClientHandler references
var handler = new AndroidClientHandler();
//AndroidEnvironment.UnhandledExceptionRaiser += AndroidEnvironment_UnhandledExceptionRaiser;
if(!Resolver.IsSet)
{
@@ -47,6 +46,13 @@ namespace Bit.Android
}
}
private void AndroidEnvironment_UnhandledExceptionRaiser(object sender, RaiseThrowableEventArgs e)
{
var message = Utilities.AppendExceptionToMessage("", e.Exception);
//Utilities.SaveCrashFile(message, true);
Utilities.SendCrashEmail(message, false);
}
public override void OnCreate()
{
base.OnCreate();
@@ -87,7 +93,7 @@ namespace Bit.Android
}
// 3. In debug mode
if(InDebugMode())
if(App.Utilities.Helpers.InDebugMode())
{
reregister = true;
}
@@ -108,15 +114,6 @@ namespace Bit.Android
}
}
private bool InDebugMode()
{
#if DEBUG
return true;
#else
return false;
#endif
}
public override void OnTerminate()
{
base.OnTerminate();
@@ -181,59 +178,71 @@ namespace Bit.Android
public static void SetIoc(Application application)
{
UserDialogs.Init(application);
var container = new UnityContainer();
container
// Android Stuff
.RegisterInstance(application.ApplicationContext)
.RegisterInstance<Application>(application)
// Services
.RegisterType<IDatabaseService, DatabaseService>(new ContainerControlledLifetimeManager())
.RegisterType<ISqlService, SqlService>(new ContainerControlledLifetimeManager())
.RegisterType<ISecureStorageService, KeyStoreStorageService>(new ContainerControlledLifetimeManager())
.RegisterType<ICryptoService, CryptoService>(new ContainerControlledLifetimeManager())
.RegisterType<IKeyDerivationService, BouncyCastleKeyDerivationService>(new ContainerControlledLifetimeManager())
.RegisterType<IAuthService, AuthService>(new ContainerControlledLifetimeManager())
.RegisterType<IFolderService, FolderService>(new ContainerControlledLifetimeManager())
.RegisterType<ILoginService, LoginService>(new ContainerControlledLifetimeManager())
.RegisterType<ISyncService, SyncService>(new ContainerControlledLifetimeManager())
.RegisterType<IClipboardService, ClipboardService>(new ContainerControlledLifetimeManager())
.RegisterType<IPushNotificationListener, PushNotificationListener>(new ContainerControlledLifetimeManager())
.RegisterType<IAppIdService, AppIdService>(new ContainerControlledLifetimeManager())
.RegisterType<IPasswordGenerationService, PasswordGenerationService>(new ContainerControlledLifetimeManager())
.RegisterType<IReflectionService, ReflectionService>(new ContainerControlledLifetimeManager())
.RegisterType<ILockService, LockService>(new ContainerControlledLifetimeManager())
.RegisterType<IAppInfoService, AppInfoService>(new ContainerControlledLifetimeManager())
.RegisterType<IGoogleAnalyticsService, GoogleAnalyticsService>(new ContainerControlledLifetimeManager())
.RegisterType<IDeviceInfoService, DeviceInfoService>(new ContainerControlledLifetimeManager())
.RegisterType<ILocalizeService, LocalizeService>(new ContainerControlledLifetimeManager())
.RegisterType<ILogService, LogService>(new ContainerControlledLifetimeManager())
.RegisterType<IHttpService, HttpService>(new ContainerControlledLifetimeManager())
.RegisterType<ITokenService, TokenService>(new ContainerControlledLifetimeManager())
.RegisterType<ISettingsService, SettingsService>(new ContainerControlledLifetimeManager())
// Repositories
.RegisterType<IFolderRepository, FolderRepository>(new ContainerControlledLifetimeManager())
.RegisterType<IFolderApiRepository, FolderApiRepository>(new ContainerControlledLifetimeManager())
.RegisterType<ILoginRepository, LoginRepository>(new ContainerControlledLifetimeManager())
.RegisterType<ILoginApiRepository, LoginApiRepository>(new ContainerControlledLifetimeManager())
.RegisterType<IConnectApiRepository, ConnectApiRepository>(new ContainerControlledLifetimeManager())
.RegisterType<IDeviceApiRepository, DeviceApiRepository>(new ContainerControlledLifetimeManager())
.RegisterType<IAccountsApiRepository, AccountsApiRepository>(new ContainerControlledLifetimeManager())
.RegisterType<ICipherApiRepository, CipherApiRepository>(new ContainerControlledLifetimeManager())
.RegisterType<ISettingsRepository, SettingsRepository>(new ContainerControlledLifetimeManager())
.RegisterType<ISettingsApiRepository, SettingsApiRepository>(new ContainerControlledLifetimeManager())
// Other
.RegisterInstance(CrossSettings.Current, new ContainerControlledLifetimeManager())
.RegisterInstance(CrossConnectivity.Current, new ContainerControlledLifetimeManager())
.RegisterInstance(UserDialogs.Instance, new ContainerControlledLifetimeManager())
.RegisterInstance(CrossFingerprint.Current, new ContainerControlledLifetimeManager());
CrossPushNotification.Initialize(container.Resolve<IPushNotificationListener>(), "962181367620");
container.RegisterInstance(CrossPushNotification.Current, new ContainerControlledLifetimeManager());
Resolver.SetResolver(new UnityResolver(container));
CachedImageRenderer.Init();
ZXing.Net.Mobile.Forms.Android.Platform.Init();
CrossFingerprint.SetCurrentActivityResolver(() => CrossCurrentActivity.Current.Activity);
//var container = new UnityContainer();
var container = new Container();
// Android Stuff
container.RegisterSingleton(application.ApplicationContext);
container.RegisterSingleton<Application>(application);
// Services
container.RegisterSingleton<IDatabaseService, DatabaseService>();
container.RegisterSingleton<ISqlService, SqlService>();
container.RegisterSingleton<ISecureStorageService, AndroidKeyStoreStorageService>();
container.RegisterSingleton<ICryptoService, CryptoService>();
container.RegisterSingleton<IKeyDerivationService, BouncyCastleKeyDerivationService>();
container.RegisterSingleton<IAuthService, AuthService>();
container.RegisterSingleton<IFolderService, FolderService>();
container.RegisterSingleton<ILoginService, LoginService>();
container.RegisterSingleton<ISyncService, SyncService>();
container.RegisterSingleton<IDeviceActionService, DeviceActionService>();
container.RegisterSingleton<IAppIdService, AppIdService>();
container.RegisterSingleton<IPasswordGenerationService, PasswordGenerationService>();
container.RegisterSingleton<IReflectionService, ReflectionService>();
container.RegisterSingleton<ILockService, LockService>();
container.RegisterSingleton<IAppInfoService, AppInfoService>();
container.RegisterSingleton<IGoogleAnalyticsService, GoogleAnalyticsService>();
container.RegisterSingleton<IDeviceInfoService, DeviceInfoService>();
container.RegisterSingleton<ILocalizeService, LocalizeService>();
container.RegisterSingleton<ILogService, LogService>();
container.RegisterSingleton<IHttpService, HttpService>();
container.RegisterSingleton<ITokenService, TokenService>();
container.RegisterSingleton<ISettingsService, SettingsService>();
container.RegisterSingleton<IMemoryService, MemoryService>();
container.RegisterSingleton<IAppSettingsService, AppSettingsService>();
// Repositories
container.RegisterSingleton<IFolderRepository, FolderRepository>();
container.RegisterSingleton<IFolderApiRepository, FolderApiRepository>();
container.RegisterSingleton<ILoginRepository, LoginRepository>();
container.RegisterSingleton<IAttachmentRepository, AttachmentRepository>();
container.RegisterSingleton<ILoginApiRepository, LoginApiRepository>();
container.RegisterSingleton<IConnectApiRepository, ConnectApiRepository>();
container.RegisterSingleton<IDeviceApiRepository, DeviceApiRepository>();
container.RegisterSingleton<IAccountsApiRepository, AccountsApiRepository>();
container.RegisterSingleton<ICipherApiRepository, CipherApiRepository>();
container.RegisterSingleton<ISettingsRepository, SettingsRepository>();
container.RegisterSingleton<ISettingsApiRepository, SettingsApiRepository>();
container.RegisterSingleton<ITwoFactorApiRepository, TwoFactorApiRepository>();
// Other
container.RegisterSingleton(CrossSettings.Current);
container.RegisterSingleton(CrossConnectivity.Current);
container.RegisterSingleton(UserDialogs.Instance);
container.RegisterSingleton(CrossFingerprint.Current);
// Push
var pushListener = new PushNotificationListener();
container.RegisterSingleton<IPushNotificationListener>(pushListener);
CrossPushNotification.Initialize(pushListener, "962181367620");
container.RegisterSingleton(CrossPushNotification.Current);
container.Verify();
Resolver.SetResolver(new SimpleInjectorResolver(container));
}
}
}

View File

@@ -0,0 +1,22 @@
using Android.App;
using Android.Content;
using Bit.App.Abstractions;
using Bit.App.Utilities;
using Plugin.Settings.Abstractions;
using System.Diagnostics;
using XLabs.Ioc;
namespace Bit.Android
{
[BroadcastReceiver(Name = "com.x8bit.bitwarden.PackageReplacedReceiver", Exported = true)]
[IntentFilter(new[] { Intent.ActionMyPackageReplaced })]
public class PackageReplacedReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
Debug.WriteLine("App updated!");
Helpers.PerformUpdateTasks(Resolver.Resolve<ISettings>(), Resolver.Resolve<IAppInfoService>(),
Resolver.Resolve<IDatabaseService>());
}
}
}

View File

@@ -1,14 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.x8bit.bitwarden" android:versionName="1.3.0" android:installLocation="auto" android:versionCode="101">
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.x8bit.bitwarden" android:versionName="1.8.1" android:installLocation="auto" android:versionCode="502" xmlns:tools="http://schemas.android.com/tools">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="23" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="com.x8bit.bitwarden.permission.C2D_MESSAGE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<application android:label="bitwarden" android:theme="@style/BitwardenTheme">
<application android:label="bitwarden" android:theme="@style/BitwardenTheme" android:allowBackup="false">
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.x8bit.bitwarden.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
</manifest>
</manifest>

View File

@@ -28,7 +28,3 @@ using Android.App;
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
// Add some common permissions, these can be removed if not needed
[assembly: UsesPermission(Android.Manifest.Permission.Internet)]
[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)]

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1009 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 693 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="AutoFillServiceDescription">
The allow bitwarden to auto-fill into other Android apps and on the web through your browser, enable the bitwarden
To allow bitwarden to auto-fill into other Android apps and on the web through your browser, enable the bitwarden
accessibility service by tapping the toggle switch above, then press OK on the confirmation pop-up. You can then press
the back button twice to return to the main bitwarden app.
</string>

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/AutoFillServiceDescription"
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged"
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged|typeViewFocused"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault"
android:accessibilityFlags="flagReportViewIds"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"/>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="cache" path="." />
</paths>

View File

@@ -0,0 +1,370 @@
using Java.Security;
using Javax.Crypto;
using Android.OS;
using Bit.App.Abstractions;
using System;
using Android.Security;
using Javax.Security.Auth.X500;
using Java.Math;
using Android.Security.Keystore;
using Android.App;
using Plugin.Settings.Abstractions;
using Java.Util;
using Javax.Crypto.Spec;
using Android.Preferences;
namespace Bit.Android.Services
{
public class AndroidKeyStoreStorageService : ISecureStorageService
{
private const string AndroidKeyStore = "AndroidKeyStore";
private const string AesMode = "AES/GCM/NoPadding";
private const string KeyAlias = "bitwardenKey2";
private const string KeyAliasV1 = "bitwardenKey";
private const string SettingsFormat = "ksSecured2:{0}";
private const string SettingsFormatV1 = "ksSecured:{0}";
private const string AesKey = "ksSecured2:aesKeyForService";
private const string AesKeyV1 = "ksSecured:aesKeyForService";
private readonly string _rsaMode;
private readonly bool _oldAndroid;
private readonly ISettings _settings;
private readonly KeyStore _keyStore;
private readonly ISecureStorageService _oldKeyStorageService;
public AndroidKeyStoreStorageService(ISettings settings)
{
_oldAndroid = Build.VERSION.SdkInt < BuildVersionCodes.M;
_rsaMode = _oldAndroid ? "RSA/ECB/PKCS1Padding" : "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
_oldKeyStorageService = new KeyStoreStorageService(new char[] { });
_settings = settings;
_keyStore = KeyStore.GetInstance(AndroidKeyStore);
_keyStore.Load(null);
GenerateStoreKey();
GenerateAesKey();
}
public bool Contains(string key)
{
return _settings.Contains(string.Format(SettingsFormat, key)) ||
_settings.Contains(string.Format(SettingsFormatV1, key)) ||
_oldKeyStorageService.Contains(key);
}
public void Delete(string key)
{
CleanupOld(key);
var formattedKey = string.Format(SettingsFormat, key);
if(_settings.Contains(formattedKey))
{
_settings.Remove(formattedKey);
}
}
public byte[] Retrieve(string key)
{
var formattedKey = string.Format(SettingsFormat, key);
if(!_settings.Contains(formattedKey))
{
return TryGetAndMigrate(key);
}
var cs = _settings.GetValueOrDefault<string>(formattedKey);
if(string.IsNullOrWhiteSpace(cs))
{
return null;
}
var aesKey = GetAesKey();
if(aesKey == null)
{
return null;
}
try
{
return App.Utilities.Crypto.AesCbcDecrypt(new App.Models.CipherString(cs), aesKey);
}
catch(Exception e)
{
Console.WriteLine("Failed to decrypt from secure storage.");
_settings.Remove(formattedKey);
//Utilities.SendCrashEmail(e);
//Utilities.SaveCrashFile(e);
return null;
}
}
public void Store(string key, byte[] dataBytes)
{
var formattedKey = string.Format(SettingsFormat, key);
CleanupOld(key);
if(dataBytes == null)
{
_settings.Remove(formattedKey);
return;
}
var aesKey = GetAesKey();
if(aesKey == null)
{
return;
}
try
{
var cipherString = App.Utilities.Crypto.AesCbcEncrypt(dataBytes, aesKey);
_settings.AddOrUpdateValue(formattedKey, cipherString.EncryptedString);
}
catch(Exception e)
{
Console.WriteLine("Failed to encrypt to secure storage.");
//Utilities.SendCrashEmail(e);
//Utilities.SaveCrashFile(e);
}
}
private void GenerateStoreKey()
{
if(_keyStore.ContainsAlias(KeyAlias))
{
return;
}
ClearSettings();
var end = Calendar.Instance;
end.Add(CalendarField.Year, 99);
if(_oldAndroid)
{
var subject = new X500Principal($"CN={KeyAlias}");
var spec = new KeyPairGeneratorSpec.Builder(Application.Context)
.SetAlias(KeyAlias)
.SetSubject(subject)
.SetSerialNumber(BigInteger.Ten)
.SetStartDate(new Date(0))
.SetEndDate(end.Time)
.Build();
var gen = KeyPairGenerator.GetInstance(KeyProperties.KeyAlgorithmRsa, AndroidKeyStore);
gen.Initialize(spec);
gen.GenerateKeyPair();
}
else
{
var spec = new KeyGenParameterSpec.Builder(KeyAlias, KeyStorePurpose.Decrypt | KeyStorePurpose.Encrypt)
.SetBlockModes(KeyProperties.BlockModeGcm)
.SetEncryptionPaddings(KeyProperties.EncryptionPaddingNone)
.SetKeyValidityStart(new Date(0))
.SetKeyValidityEnd(end.Time)
.Build();
var gen = KeyGenerator.GetInstance(KeyProperties.KeyAlgorithmAes, AndroidKeyStore);
gen.Init(spec);
gen.GenerateKey();
}
}
private KeyStore.PrivateKeyEntry GetRsaKeyEntry(string alias)
{
return _keyStore.GetEntry(alias, null) as KeyStore.PrivateKeyEntry;
}
private void GenerateAesKey()
{
if(_settings.Contains(AesKey))
{
return;
}
var key = App.Utilities.Crypto.RandomBytes(512 / 8);
var encKey = _oldAndroid ? RsaEncrypt(key) : AesEncrypt(key);
_settings.AddOrUpdateValue(AesKey, encKey);
}
private App.Models.SymmetricCryptoKey GetAesKey(bool v1 = false)
{
try
{
var aesKey = v1 ? AesKeyV1 : AesKey;
if(!_settings.Contains(aesKey))
{
return null;
}
var encKey = _settings.GetValueOrDefault<string>(aesKey);
if(string.IsNullOrWhiteSpace(encKey))
{
return null;
}
if(_oldAndroid || v1)
{
var encKeyBytes = Convert.FromBase64String(encKey);
var key = RsaDecrypt(encKeyBytes, v1);
return new App.Models.SymmetricCryptoKey(key);
}
else
{
var parts = encKey.Split('|');
if(parts.Length < 2)
{
return null;
}
var ivBytes = Convert.FromBase64String(parts[0]);
var encKeyBytes = Convert.FromBase64String(parts[1]);
var key = AesDecrypt(ivBytes, encKeyBytes);
return new App.Models.SymmetricCryptoKey(key);
}
}
catch(Exception e)
{
Console.WriteLine("Cannot get AesKey.");
_keyStore.DeleteEntry(KeyAlias);
_settings.Remove(AesKey);
if(!v1)
{
//Utilities.SendCrashEmail(e);
//Utilities.SaveCrashFile(e);
}
return null;
}
}
private string AesEncrypt(byte[] input)
{
using(var entry = _keyStore.GetKey(KeyAlias, null))
using(var cipher = Cipher.GetInstance(AesMode))
{
cipher.Init(CipherMode.EncryptMode, entry);
var encBytes = cipher.DoFinal(input);
var ivBytes = cipher.GetIV();
return $"{Convert.ToBase64String(ivBytes)}|{Convert.ToBase64String(encBytes)}";
}
}
private byte[] AesDecrypt(byte[] iv, byte[] encData)
{
using(var entry = _keyStore.GetKey(KeyAlias, null))
using(var cipher = Cipher.GetInstance(AesMode))
{
var spec = new GCMParameterSpec(128, iv);
cipher.Init(CipherMode.DecryptMode, entry, spec);
var decBytes = cipher.DoFinal(encData);
return decBytes;
}
}
private string RsaEncrypt(byte[] data)
{
using(var entry = GetRsaKeyEntry(KeyAlias))
using(var cipher = Cipher.GetInstance(_rsaMode))
{
cipher.Init(CipherMode.EncryptMode, entry.Certificate.PublicKey);
var cipherText = cipher.DoFinal(data);
return Convert.ToBase64String(cipherText);
}
}
private byte[] RsaDecrypt(byte[] encData, bool v1)
{
using(var entry = GetRsaKeyEntry(v1 ? KeyAliasV1 : KeyAlias))
using(var cipher = Cipher.GetInstance(_rsaMode))
{
if(_oldAndroid)
{
cipher.Init(CipherMode.DecryptMode, entry.PrivateKey);
}
else
{
cipher.Init(CipherMode.DecryptMode, entry.PrivateKey, OAEPParameterSpec.Default);
}
var plainText = cipher.DoFinal(encData);
return plainText;
}
}
private byte[] TryGetAndMigrate(string key)
{
if(_oldKeyStorageService.Contains(key))
{
var value = _oldKeyStorageService.Retrieve(key);
Store(key, value);
return value;
}
var formattedKeyV1 = string.Format(SettingsFormatV1, key);
if(_settings.Contains(formattedKeyV1))
{
var aesKeyV1 = GetAesKey(true);
if(aesKeyV1 != null)
{
try
{
var cs = _settings.GetValueOrDefault<string>(formattedKeyV1);
var value = App.Utilities.Crypto.AesCbcDecrypt(new App.Models.CipherString(cs), aesKeyV1);
Store(key, value);
return value;
}
catch
{
Console.WriteLine("Failed to decrypt v1 from secure storage.");
}
}
_settings.Remove(formattedKeyV1);
}
return null;
}
private void CleanupOld(string key)
{
if(_oldKeyStorageService.Contains(key))
{
_oldKeyStorageService.Delete(key);
}
var formattedKeyV1 = string.Format(SettingsFormatV1, key);
if(_settings.Contains(formattedKeyV1))
{
_settings.Remove(formattedKeyV1);
}
}
private void ClearSettings(string format = SettingsFormat)
{
var prefix = string.Format(format, string.Empty);
using(var sharedPreferences = PreferenceManager.GetDefaultSharedPreferences(Application.Context))
using(var sharedPreferencesEditor = sharedPreferences.Edit())
{
var removed = false;
foreach(var pref in sharedPreferences.All)
{
if(pref.Key.StartsWith(prefix))
{
removed = true;
sharedPreferencesEditor.Remove(pref.Key);
}
}
if(removed)
{
sharedPreferencesEditor.Commit();
}
}
}
}
}

View File

@@ -1,4 +1,6 @@
using Bit.App.Abstractions;
using Android.App;
using Bit.App.Abstractions;
using System.Linq;
using AndroidApp = Android.App.Application;
namespace Bit.Android.Services
@@ -11,6 +13,14 @@ namespace Bit.Android.Services
public string Build => AndroidApp.Context.ApplicationContext.PackageManager
.GetPackageInfo(AndroidApp.Context.PackageName, 0).VersionCode.ToString();
public bool AutofillServiceEnabled => AutofillService.Enabled;
public bool AutofillServiceEnabled => AutofillRunning();
private bool AutofillRunning()
{
var manager = ((ActivityManager)Xamarin.Forms.Forms.Context.GetSystemService("activity"));
var services = manager.GetRunningServices(int.MaxValue);
return services.Any(s => s.Process.ToLowerInvariant().Contains("bitwarden") &&
s.Service.ClassName.ToLowerInvariant().Contains("autofill"));
}
}
}

View File

@@ -1,15 +0,0 @@
using Android.Content;
using Bit.App.Abstractions;
using Xamarin.Forms;
namespace Bit.Android.Services
{
public class ClipboardService : IClipboardService
{
public void CopyToClipboard(string text)
{
var clipboardManager = (ClipboardManager)Forms.Context.GetSystemService(Context.ClipboardService);
clipboardManager.Text = text;
}
}
}

View File

@@ -0,0 +1,229 @@
using System;
using Android.Content;
using Bit.App.Abstractions;
using Xamarin.Forms;
using Android.Webkit;
using Plugin.CurrentActivity;
using System.IO;
using Android.Support.V4.Content;
using Bit.App;
using Bit.App.Resources;
using Android.Provider;
using System.Threading.Tasks;
using Android.OS;
using System.Collections.Generic;
using Android;
using Android.Content.PM;
using Android.Support.V4.App;
namespace Bit.Android.Services
{
public class DeviceActionService : IDeviceActionService
{
private readonly IAppSettingsService _appSettingsService;
private bool _cameraPermissionsDenied;
public DeviceActionService(
IAppSettingsService appSettingsService)
{
_appSettingsService = appSettingsService;
}
public void CopyToClipboard(string text)
{
var clipboardManager = (ClipboardManager)Forms.Context.GetSystemService(Context.ClipboardService);
clipboardManager.Text = text;
}
public bool OpenFile(byte[] fileData, string id, string fileName)
{
if(!CanOpenFile(fileName))
{
return false;
}
var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName.Replace(' ', '_').ToLower());
if(extension == null)
{
return false;
}
var mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
if(mimeType == null)
{
return false;
}
var cachePath = CrossCurrentActivity.Current.Activity.CacheDir;
var filePath = Path.Combine(cachePath.Path, fileName);
File.WriteAllBytes(filePath, fileData);
var file = new Java.IO.File(cachePath, fileName);
if(!file.IsFile)
{
return false;
}
try
{
var intent = new Intent(Intent.ActionView);
var uri = FileProvider.GetUriForFile(CrossCurrentActivity.Current.Activity.ApplicationContext,
"com.x8bit.bitwarden.fileprovider", file);
intent.SetDataAndType(uri, mimeType);
intent.SetFlags(ActivityFlags.GrantReadUriPermission);
CrossCurrentActivity.Current.Activity.StartActivity(intent);
return true;
}
catch { }
return false;
}
public bool CanOpenFile(string fileName)
{
var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName.Replace(' ', '_').ToLower());
if(extension == null)
{
return false;
}
var mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
if(mimeType == null)
{
return false;
}
var pm = CrossCurrentActivity.Current.Activity.PackageManager;
var intent = new Intent(Intent.ActionView);
intent.SetType(mimeType);
var activities = pm.QueryIntentActivities(intent, PackageInfoFlags.MatchDefaultOnly);
return (activities?.Count ?? 0) > 0;
}
public void ClearCache()
{
try
{
DeleteDir(CrossCurrentActivity.Current.Activity.CacheDir);
_appSettingsService.LastCacheClear = DateTime.UtcNow;
}
catch(Exception) { }
}
private bool DeleteDir(Java.IO.File dir)
{
if(dir != null && dir.IsDirectory)
{
var children = dir.List();
for(int i = 0; i < children.Length; i++)
{
var success = DeleteDir(new Java.IO.File(dir, children[i]));
if(!success)
{
return false;
}
}
return dir.Delete();
}
else if(dir != null && dir.IsFile)
{
return dir.Delete();
}
else
{
return false;
}
}
public Task SelectFileAsync()
{
MessagingCenter.Unsubscribe<Application>(Application.Current, "SelectFileCameraPermissionDenied");
var hasStorageWritePermission = !_cameraPermissionsDenied && HasPermission(Manifest.Permission.WriteExternalStorage);
var hasCameraPermission = !_cameraPermissionsDenied && HasPermission(Manifest.Permission.Camera);
if(!_cameraPermissionsDenied && !hasStorageWritePermission)
{
AskCameraPermission(Manifest.Permission.WriteExternalStorage);
return Task.FromResult(0);
}
if(!_cameraPermissionsDenied && !hasCameraPermission)
{
AskCameraPermission(Manifest.Permission.Camera);
return Task.FromResult(0);
}
var additionalIntents = new List<IParcelable>();
var docIntent = new Intent(Intent.ActionOpenDocument);
docIntent.AddCategory(Intent.CategoryOpenable);
docIntent.SetType("*/*");
var chooserIntent = Intent.CreateChooser(docIntent, AppResources.FileSource);
if(!_cameraPermissionsDenied && hasCameraPermission && hasStorageWritePermission)
{
try
{
var root = new Java.IO.File(global::Android.OS.Environment.ExternalStorageDirectory, "bitwarden");
var file = new Java.IO.File(root, "temp_camera_photo.jpg");
if(!file.Exists())
{
file.ParentFile.Mkdirs();
file.CreateNewFile();
}
var outputFileUri = global::Android.Net.Uri.FromFile(file);
additionalIntents.AddRange(GetCameraIntents(outputFileUri));
}
catch(Java.IO.IOException) { }
}
if(additionalIntents.Count > 0)
{
chooserIntent.PutExtra(Intent.ExtraInitialIntents, additionalIntents.ToArray());
}
CrossCurrentActivity.Current.Activity.StartActivityForResult(chooserIntent, Constants.SelectFileRequestCode);
return Task.FromResult(0);
}
private List<IParcelable> GetCameraIntents(global::Android.Net.Uri outputUri)
{
var intents = new List<IParcelable>();
var pm = CrossCurrentActivity.Current.Activity.PackageManager;
var captureIntent = new Intent(MediaStore.ActionImageCapture);
var listCam = pm.QueryIntentActivities(captureIntent, 0);
foreach(var res in listCam)
{
var packageName = res.ActivityInfo.PackageName;
var intent = new Intent(captureIntent);
intent.SetComponent(new ComponentName(packageName, res.ActivityInfo.Name));
intent.SetPackage(packageName);
intent.PutExtra(MediaStore.ExtraOutput, outputUri);
intents.Add(intent);
}
return intents;
}
private bool HasPermission(string permission)
{
return ContextCompat.CheckSelfPermission(CrossCurrentActivity.Current.Activity, permission) == Permission.Granted;
}
private void AskCameraPermission(string permission)
{
MessagingCenter.Subscribe<Application>(Application.Current, "SelectFileCameraPermissionDenied", (sender) =>
{
_cameraPermissionsDenied = true;
});
AskPermission(permission);
}
private void AskPermission(string permission)
{
ActivityCompat.RequestPermissions(CrossCurrentActivity.Current.Activity, new string[] { permission },
Constants.SelectFilePermissionRequestCode);
}
}
}

View File

@@ -1,6 +1,5 @@
using Android.App;
using Android.OS;
using Android.Util;
using Bit.App.Abstractions;
namespace Bit.Android.Services
@@ -42,5 +41,6 @@ namespace Bit.Android.Services
return 1f;
}
}
public bool NfcEnabled => Utilities.NfcEnabled();
}
}

View File

@@ -1,5 +1,7 @@
using System;
using Bit.App;
using Bit.App.Abstractions;
using Plugin.Settings.Abstractions;
using Android.Gms.Analytics;
using Android.Content;
@@ -7,17 +9,15 @@ namespace Bit.Android.Services
{
public class GoogleAnalyticsService : IGoogleAnalyticsService
{
private const string UserId = "&uid";
private readonly GoogleAnalytics _instance;
private readonly IAuthService _authService;
private readonly Tracker _tracker;
private bool _setUserId = true;
public GoogleAnalyticsService(
Context appContext,
IAppIdService appIdService,
IAuthService authService)
IAuthService authService,
ISettings settings)
{
_authService = authService;
@@ -25,16 +25,13 @@ namespace Bit.Android.Services
_instance.SetLocalDispatchPeriod(10);
_tracker = _instance.NewTracker("UA-81915606-2");
_tracker.EnableExceptionReporting(true);
_tracker.EnableExceptionReporting(false);
_tracker.EnableAdvertisingIdCollection(true);
_tracker.EnableAutoActivityTracking(true);
_tracker.SetClientId(appIdService.AnonymousAppId);
}
public void RefreshUserId()
{
_tracker.Set(UserId, null);
_setUserId = true;
var gaOptOut = settings.GetValueOrDefault(Constants.SettingGaOptOut, false);
SetAppOptOut(gaOptOut);
}
public void TrackAppEvent(string eventName, string label = null)
@@ -57,7 +54,6 @@ namespace Bit.Android.Services
builder.SetLabel(label);
}
SetUserId();
_tracker.Send(builder.Build());
}
@@ -67,30 +63,24 @@ namespace Bit.Android.Services
builder.SetDescription(message);
builder.SetFatal(fatal);
SetUserId();
_tracker.Send(builder.Build());
}
public void TrackPage(string pageName)
{
SetUserId();
_tracker.SetScreenName(pageName);
_tracker.Send(new HitBuilders.ScreenViewBuilder().Build());
}
private void SetUserId()
{
if(_setUserId && _authService.IsAuthenticated)
{
_tracker.Set(UserId, _authService.UserId);
_setUserId = false;
}
}
public void Dispatch(Action completionHandler = null)
{
_instance.DispatchLocalHits();
completionHandler?.Invoke();
}
public void SetAppOptOut(bool optOut)
{
_instance.AppOptOut = optOut;
}
}
}

View File

@@ -7,6 +7,7 @@ namespace Bit.Android.Services
{
public class HttpService : IHttpService
{
public ApiHttpClient Client => new ApiHttpClient(new CustomAndroidClientHandler());
public ApiHttpClient ApiClient => new ApiHttpClient(new AndroidClientHandler());
public IdentityHttpClient IdentityClient => new IdentityHttpClient(new AndroidClientHandler());
}
}

View File

@@ -8,6 +8,7 @@ using Bit.App.Abstractions;
namespace Bit.Android.Services
{
[System.Obsolete]
public class KeyStoreStorageService : ISecureStorageService
{
private const string StorageFile = "Bit.Android.KeyStoreStorageService";

View File

@@ -52,10 +52,21 @@ namespace Bit.Android.Services
Console.WriteLine("Android Language:" + androidLanguage);
var netLanguage = androidLanguage;
if(netLanguage.StartsWith("zh"))
if(androidLanguage.StartsWith("zh"))
{
// simplified chinese used for all for now
netLanguage = "zh-Hans";
if(androidLanguage.Contains("Hant"))
{
netLanguage = "zh-Hant";
}
else
{
netLanguage = "zh-Hans";
}
}
else if(androidLanguage.StartsWith("pt"))
{
// only Portuguese Europe for now
netLanguage = "pt-PT";
}
else
{

View File

@@ -0,0 +1,44 @@
using System;
using Android.Content;
using Bit.App.Abstractions;
using Xamarin.Forms;
namespace Bit.Android.Services
{
public class MemoryService : IMemoryService
{
public MemoryInfo GetInfo()
{
return MemoryHelper.GetMemoryInfo(Forms.Context);
}
public void Check()
{
MemoryHelper.MemoryCheck(Forms.Context);
}
public static class MemoryHelper
{
public static void MemoryCheck(Context context)
{
Console.WriteLine("MemoryHelper.MemoryCheck.{0} - {1}", "Start", context.ToString());
var maxMemory = Java.Lang.Runtime.GetRuntime().MaxMemory();
var freeMemory = Java.Lang.Runtime.GetRuntime().FreeMemory();
var percentUsed = (maxMemory - freeMemory) / (double)maxMemory;
Console.WriteLine("Free memory: {0:N}", freeMemory);
Console.WriteLine("Max memory: {0:N}", maxMemory);
Console.WriteLine("% used: {0:P}", percentUsed);
Console.WriteLine("MemoryHelper.MemoryCheck.{0} {3:P} {1} out of {2}", "End", freeMemory, maxMemory, percentUsed);
}
public static MemoryInfo GetMemoryInfo(Context context)
{
var retVal = new MemoryInfo();
retVal.MaxMemory = Java.Lang.Runtime.GetRuntime().MaxMemory();
retVal.FreeMemory = Java.Lang.Runtime.GetRuntime().FreeMemory();
retVal.TotalMemory = Java.Lang.Runtime.GetRuntime().TotalMemory();
return retVal;
}
}
}
}

129
src/Android/Utilities.cs Normal file
View File

@@ -0,0 +1,129 @@
using System;
using Android.App;
using Android.Content;
using Java.Security;
using System.IO;
using Android.Nfc;
using Android.Provider;
namespace Bit.Android
{
public static class Utilities
{
public static bool NfcEnabled()
{
var manager = (NfcManager)Application.Context.GetSystemService("nfc");
var adapter = manager.DefaultAdapter;
return adapter != null && adapter.IsEnabled;
}
public static void SendCrashEmail(Exception e, bool includeSecurityProviders = true)
{
SendCrashEmail(e.Message + "\n\n" + e.StackTrace, includeSecurityProviders);
}
public static void SendCrashEmail(Activity act, Exception e, bool includeSecurityProviders = true)
{
SendCrashEmail(act, e.Message + "\n\n" + e.StackTrace, includeSecurityProviders);
}
public static void SaveCrashFile(Exception e, bool includeSecurityProviders = true)
{
SaveCrashFile(e.Message + "\n\n" + e.StackTrace, includeSecurityProviders);
}
public static void SendCrashEmail(string text, bool includeSecurityProviders = true)
{
var emailIntent = new Intent(Intent.ActionSend);
emailIntent.SetType("plain/text");
emailIntent.PutExtra(Intent.ExtraEmail, new String[] { "hello@bitwarden.com" });
emailIntent.PutExtra(Intent.ExtraSubject, "bitwarden Crash Report");
emailIntent.PutExtra(Intent.ExtraText, FormatText(text, includeSecurityProviders));
Application.Context.StartActivity(Intent.CreateChooser(emailIntent, "Send mail..."));
}
public static void SendCrashEmail(Activity act, string text, bool includeSecurityProviders = true)
{
var emailIntent = new Intent(Intent.ActionSend);
emailIntent.SetType("plain/text");
emailIntent.PutExtra(Intent.ExtraEmail, new String[] { "hello@bitwarden.com" });
emailIntent.PutExtra(Intent.ExtraSubject, "bitwarden Crash Report");
emailIntent.PutExtra(Intent.ExtraText, FormatText(text, includeSecurityProviders));
act.StartActivity(Intent.CreateChooser(emailIntent, "Send mail..."));
}
public static void SaveCrashFile(string text, bool includeSecurityProviders = true)
{
var path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
var filename = Path.Combine(path, $"crash-{Java.Lang.JavaSystem.CurrentTimeMillis()}.txt");
using(var streamWriter = new StreamWriter(filename, true))
{
streamWriter.WriteLine(FormatText(text, includeSecurityProviders));
}
}
private static string FormatText(string text, bool includeSecurityProviders = true)
{
var crashMessage = "bitwarden has crashed. Please send this email to our support team so that we can help " +
"resolve the problem for you. Thank you.";
text = crashMessage + "\n\n =============================================== \n\n" + text;
if(includeSecurityProviders)
{
text += "\n\n";
var providers = Security.GetProviders();
foreach(var provider in providers)
{
text += ("provider: " + provider.Name + "\n");
var services = provider.Services;
foreach(var service in provider.Services)
{
text += ("- alg: " + service.Algorithm + "\n");
}
}
}
text += "\n\n ==================================================== \n\n" + crashMessage;
return text;
}
public static string AppendExceptionToMessage(string message, Exception ex)
{
message += ("\n\n" + ex.Message + "\n\n" + ex.StackTrace);
if(ex.InnerException != null)
{
return AppendExceptionToMessage(message, ex.InnerException);
}
return message;
}
public static string GetFileName(Context context, global::Android.Net.Uri uri)
{
string name = null;
string[] projection = { MediaStore.MediaColumns.DisplayName };
var metaCursor = context.ContentResolver.Query(uri, projection, null, null, null);
if(metaCursor != null)
{
try
{
if(metaCursor.MoveToFirst())
{
name = metaCursor.GetString(0);
}
}
finally
{
metaCursor.Close();
}
}
return name;
}
}
}

View File

@@ -10,6 +10,10 @@
<assemblyIdentity name="Validation" publicKeyToken="2fc06f0d701809a7" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.3.0.0" newVersion="2.3.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SimpleInjector" publicKeyToken="984cb50dea722e99" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.7.0" newVersion="4.0.7.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@@ -1,11 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Acr.Support" version="2.1.0" targetFramework="monoandroid60" />
<package id="Acr.UserDialogs" version="6.3.3" targetFramework="monoandroid60" />
<package id="Acr.UserDialogs" version="6.3.10" targetFramework="monoandroid71" />
<package id="AndHUD" version="1.2.0" targetFramework="monoandroid60" />
<package id="BouncyCastle" version="1.8.1" targetFramework="monoandroid60" />
<package id="CommonServiceLocator" version="1.3" targetFramework="monoandroid60" />
<package id="HockeySDK.Xamarin" version="4.1.0" targetFramework="monoandroid60" />
<package id="HockeySDK.Xamarin" version="4.1.2" targetFramework="monoandroid71" />
<package id="Microsoft.NETCore.Platforms" version="1.0.1" targetFramework="monoandroid71" />
<package id="Microsoft.Win32.Primitives" version="4.0.1" targetFramework="monoandroid71" />
<package id="NETStandard.Library" version="1.6.0" targetFramework="monoandroid71" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="monoandroid60" />
<package id="PCLCrypto" version="2.0.147" targetFramework="monoandroid60" />
<package id="PInvoke.BCrypt" version="0.3.152" targetFramework="monoandroid60" />
@@ -13,7 +16,8 @@
<package id="PInvoke.NCrypt" version="0.3.152" targetFramework="monoandroid60" />
<package id="PInvoke.Windows.Core" version="0.3.152" targetFramework="monoandroid60" />
<package id="Plugin.CurrentActivity" version="1.0.1" targetFramework="monoandroid60" />
<package id="Plugin.Fingerprint" version="1.2.0" targetFramework="monoandroid60" />
<package id="Plugin.Fingerprint" version="1.4.4" targetFramework="monoandroid71" />
<package id="SimpleInjector" version="4.0.7" targetFramework="monoandroid71" />
<package id="Splat" version="1.6.2" targetFramework="monoandroid60" />
<package id="sqlite-net-pcl" version="1.2.1" targetFramework="monoandroid60" />
<package id="SQLitePCL.bundle_green" version="0.9.3" targetFramework="monoandroid60" />
@@ -23,11 +27,53 @@
<package id="SQLitePCLRaw.core" version="1.1.2" targetFramework="monoandroid60" />
<package id="SQLitePCLRaw.lib.e_sqlite3.android" version="1.1.2" targetFramework="monoandroid60" />
<package id="SQLitePCLRaw.provider.e_sqlite3.android" version="1.1.2" targetFramework="monoandroid60" />
<package id="Unity" version="3.5.1405-prerelease" targetFramework="monoandroid60" />
<package id="System.AppContext" version="4.1.0" targetFramework="monoandroid71" />
<package id="System.Collections" version="4.0.11" targetFramework="monoandroid71" />
<package id="System.Collections.Concurrent" version="4.0.12" targetFramework="monoandroid71" />
<package id="System.ComponentModel" version="4.0.1" targetFramework="monoandroid71" />
<package id="System.Console" version="4.0.0" targetFramework="monoandroid71" />
<package id="System.Diagnostics.Debug" version="4.0.11" targetFramework="monoandroid71" />
<package id="System.Diagnostics.Tools" version="4.0.1" targetFramework="monoandroid71" />
<package id="System.Diagnostics.Tracing" version="4.1.0" targetFramework="monoandroid71" />
<package id="System.Globalization" version="4.0.11" targetFramework="monoandroid71" />
<package id="System.Globalization.Calendars" version="4.0.1" targetFramework="monoandroid71" />
<package id="System.IO" version="4.1.0" targetFramework="monoandroid71" />
<package id="System.IO.Compression" version="4.1.0" targetFramework="monoandroid71" />
<package id="System.IO.Compression.ZipFile" version="4.0.1" targetFramework="monoandroid71" />
<package id="System.IO.FileSystem" version="4.0.1" targetFramework="monoandroid71" />
<package id="System.IO.FileSystem.Primitives" version="4.0.1" targetFramework="monoandroid71" />
<package id="System.Linq" version="4.1.0" targetFramework="monoandroid71" />
<package id="System.Linq.Expressions" version="4.1.0" targetFramework="monoandroid71" />
<package id="System.Net.Http" version="4.1.0" targetFramework="monoandroid71" />
<package id="System.Net.Primitives" version="4.0.11" targetFramework="monoandroid71" />
<package id="System.Net.Sockets" version="4.1.0" targetFramework="monoandroid71" />
<package id="System.ObjectModel" version="4.0.12" targetFramework="monoandroid71" />
<package id="System.Reflection" version="4.1.0" targetFramework="monoandroid71" />
<package id="System.Reflection.Extensions" version="4.0.1" targetFramework="monoandroid71" />
<package id="System.Reflection.Primitives" version="4.0.1" targetFramework="monoandroid71" />
<package id="System.Resources.ResourceManager" version="4.0.1" targetFramework="monoandroid71" />
<package id="System.Runtime" version="4.1.0" targetFramework="monoandroid71" />
<package id="System.Runtime.Extensions" version="4.1.0" targetFramework="monoandroid71" />
<package id="System.Runtime.Handles" version="4.0.1" targetFramework="monoandroid71" />
<package id="System.Runtime.InteropServices" version="4.1.0" targetFramework="monoandroid71" />
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.0.0" targetFramework="monoandroid71" />
<package id="System.Runtime.Numerics" version="4.0.1" targetFramework="monoandroid71" />
<package id="System.Security.Cryptography.Algorithms" version="4.2.0" targetFramework="monoandroid71" />
<package id="System.Security.Cryptography.Encoding" version="4.0.0" targetFramework="monoandroid71" />
<package id="System.Security.Cryptography.Primitives" version="4.0.0" targetFramework="monoandroid71" />
<package id="System.Security.Cryptography.X509Certificates" version="4.1.0" targetFramework="monoandroid71" />
<package id="System.Text.Encoding" version="4.0.11" targetFramework="monoandroid71" />
<package id="System.Text.Encoding.Extensions" version="4.0.11" targetFramework="monoandroid71" />
<package id="System.Text.RegularExpressions" version="4.1.0" targetFramework="monoandroid71" />
<package id="System.Threading" version="4.0.11" targetFramework="monoandroid71" />
<package id="System.Threading.Tasks" version="4.0.11" targetFramework="monoandroid71" />
<package id="System.Threading.Timer" version="4.0.1" targetFramework="monoandroid71" />
<package id="System.Xml.ReaderWriter" version="4.0.11" targetFramework="monoandroid71" />
<package id="System.Xml.XDocument" version="4.0.11" targetFramework="monoandroid71" />
<package id="Validation" version="2.3.7" targetFramework="monoandroid60" />
<package id="Xam.Plugin.Connectivity" version="2.2.12" targetFramework="monoandroid60" />
<package id="Xam.Plugin.Connectivity" version="2.3.0" targetFramework="monoandroid71" />
<package id="Xam.Plugin.PushNotification" version="1.2.4" targetFramework="monoandroid60" developmentDependency="true" />
<package id="Xam.Plugins.Settings" version="2.5.1.0" targetFramework="monoandroid60" />
<package id="Xam.Plugins.Settings" version="2.5.4" targetFramework="monoandroid71" />
<package id="Xamarin.Android.Support.Animated.Vector.Drawable" version="23.3.0" targetFramework="monoandroid60" />
<package id="Xamarin.Android.Support.Design" version="23.3.0" targetFramework="monoandroid60" />
<package id="Xamarin.Android.Support.v4" version="23.3.0" targetFramework="monoandroid60" />
@@ -36,12 +82,16 @@
<package id="Xamarin.Android.Support.v7.MediaRouter" version="23.3.0" targetFramework="monoandroid60" />
<package id="Xamarin.Android.Support.v7.RecyclerView" version="23.3.0" targetFramework="monoandroid60" />
<package id="Xamarin.Android.Support.Vector.Drawable" version="23.3.0" targetFramework="monoandroid60" />
<package id="Xamarin.Forms" version="2.3.3.180" targetFramework="monoandroid60" />
<package id="Xamarin.FFImageLoading" version="2.2.9" targetFramework="monoandroid71" />
<package id="Xamarin.FFImageLoading.Forms" version="2.2.9" targetFramework="monoandroid71" />
<package id="Xamarin.Forms" version="2.3.4.231" targetFramework="monoandroid71" />
<package id="Xamarin.GooglePlayServices.Analytics" version="29.0.0.2" targetFramework="monoandroid60" />
<package id="Xamarin.GooglePlayServices.Base" version="29.0.0.2" targetFramework="monoandroid60" />
<package id="Xamarin.GooglePlayServices.Basement" version="29.0.0.2" targetFramework="monoandroid60" />
<package id="Xamarin.GooglePlayServices.Gcm" version="29.0.0.2" targetFramework="monoandroid60" />
<package id="Xamarin.GooglePlayServices.Measurement" version="29.0.0.2" targetFramework="monoandroid60" />
<package id="XLabs.IoC" version="2.0.5782" targetFramework="monoandroid60" />
<package id="XLabs.IoC.Unity" version="2.0.5782" targetFramework="monoandroid60" />
<package id="XLabs.IoC.SimpleInjector" version="2.0.5782" targetFramework="monoandroid71" />
<package id="ZXing.Net.Mobile" version="2.1.47" targetFramework="monoandroid71" />
<package id="ZXing.Net.Mobile.Forms" version="2.1.47" targetFramework="monoandroid71" />
</packages>

View File

@@ -8,6 +8,8 @@ namespace Bit.App.Abstractions
{
Task<ApiResult> PostRegisterAsync(RegisterRequest requestObj);
Task<ApiResult> PostPasswordHintAsync(PasswordHintRequest requestObj);
Task<ApiResult<DateTime?>> GetAccountRevisionDate();
Task<ApiResult<DateTime?>> GetAccountRevisionDateAsync();
Task<ApiResult<ProfileResponse>> GetProfileAsync();
Task<ApiResult<KeysResponse>> GetKeys();
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.App.Models.Data;
namespace Bit.App.Abstractions
{
public interface IAttachmentRepository : IRepository<AttachmentData, string>
{
Task<IEnumerable<AttachmentData>> GetAllByLoginIdAsync(string loginId);
Task<IEnumerable<AttachmentData>> GetAllByUserIdAsync(string userId);
}
}

View File

@@ -8,6 +8,7 @@ namespace Bit.App.Abstractions
{
Task<ApiResult<CipherResponse>> GetByIdAsync(string id);
Task<ApiResult<ListResponse<CipherResponse>>> GetAsync();
Task<ApiResult<CipherHistoryResponse>> GetByRevisionDateWithHistoryAsync(DateTime since);
Task<ApiResult<CipherResponse>> PostAttachmentAsync(string cipherId, byte[] data, string fileName);
Task<ApiResult> DeleteAttachmentAsync(string cipherId, string attachmentId);
}
}

View File

@@ -0,0 +1,10 @@
using System.Threading.Tasks;
using Bit.App.Models.Api;
namespace Bit.App.Abstractions
{
public interface ITwoFactorApiRepository
{
Task<ApiResult> PostSendEmailLoginAsync(TwoFactorEmailRequest requestObj);
}
}

View File

@@ -0,0 +1,14 @@
using System;
namespace Bit.App.Abstractions
{
public interface IAppSettingsService
{
bool Locked { get; set; }
DateTime LastActivity { get; set; }
DateTime LastCacheClear { get; set; }
bool AutofillPersistNotification { get; set; }
bool AutofillPasswordField { get; set; }
string SecurityStamp { get; set; }
}
}

View File

@@ -1,6 +1,6 @@
using System.Threading.Tasks;
using Bit.App.Models.Api;
using System;
using Bit.App.Enums;
using Bit.App.Models;
using System.Threading.Tasks;
namespace Bit.App.Abstractions
{
@@ -13,7 +13,10 @@ namespace Bit.App.Abstractions
string Email { get; set; }
string PIN { get; set; }
bool BelongsToOrganization(string orgId);
void LogOut();
Task<ApiResult<TokenResponse>> TokenPostAsync(TokenRequest request);
Task<FullLoginResult> TokenPostAsync(string email, string masterPassword);
Task<LoginResult> TokenPostTwoFactorAsync(TwoFactorProviderType type, string token, bool remember, string email,
string masterPasswordHash, SymmetricCryptoKey key);
}
}

View File

@@ -1,7 +0,0 @@
namespace Bit.App.Abstractions
{
public interface IClipboardService
{
void CopyToClipboard(string text);
}
}

View File

@@ -1,19 +1,32 @@
using Bit.App.Models;
using Bit.App.Models.Api;
using System;
using System.Collections.Generic;
namespace Bit.App.Abstractions
{
public interface ICryptoService
{
string Base64Key { get; }
byte[] Key { get; set; }
byte[] PreviousKey { get; }
bool KeyChanged { get; }
string Decrypt(CipherString encyptedValue);
CipherString Encrypt(string plaintextValue);
byte[] MakeKeyFromPassword(string password, string salt);
SymmetricCryptoKey Key { get; set; }
SymmetricCryptoKey EncKey { get; }
byte[] PrivateKey { get; }
IDictionary<string, SymmetricCryptoKey> OrgKeys { get; }
void SetEncKey(CipherString encKeyEnc);
void SetPrivateKey(CipherString privateKeyEnc);
void SetOrgKeys(ProfileResponse profile);
void SetOrgKeys(Dictionary<string, string> orgKeysEncDict);
SymmetricCryptoKey GetOrgKey(string orgId);
void ClearKeys();
string Decrypt(CipherString encyptedValue, SymmetricCryptoKey key = null);
byte[] DecryptToBytes(CipherString encyptedValue, SymmetricCryptoKey key = null);
byte[] DecryptToBytes(byte[] encyptedValue, SymmetricCryptoKey key = null);
byte[] RsaDecryptToBytes(CipherString encyptedValue, byte[] privateKey);
CipherString Encrypt(string plaintextValue, SymmetricCryptoKey key = null);
byte[] EncryptToBytes(byte[] plainBytes, SymmetricCryptoKey key = null);
SymmetricCryptoKey MakeKeyFromPassword(string password, string salt);
string MakeKeyFromPasswordBase64(string password, string salt);
byte[] HashPassword(byte[] key, string password);
string HashPasswordBase64(byte[] key, string password);
byte[] HashPassword(SymmetricCryptoKey key, string password);
string HashPasswordBase64(SymmetricCryptoKey key, string password);
CipherString MakeEncKey(SymmetricCryptoKey key);
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Threading.Tasks;
namespace Bit.App.Abstractions
{
public interface IDeviceActionService
{
void CopyToClipboard(string text);
bool OpenFile(byte[] fileData, string id, string fileName);
bool CanOpenFile(string fileName);
Task SelectFileAsync();
void ClearCache();
}
}

View File

@@ -5,5 +5,6 @@
string Model { get; }
int Version { get; }
float Scale { get; }
bool NfcEnabled { get; }
}
}

View File

@@ -4,12 +4,12 @@ namespace Bit.App.Abstractions
{
public interface IGoogleAnalyticsService
{
void RefreshUserId();
void TrackPage(string pageName);
void TrackAppEvent(string eventName, string label = null);
void TrackExtensionEvent(string eventName, string label = null);
void TrackEvent(string category, string eventName, string label = null);
void TrackException(string message, bool fatal);
void Dispatch(Action completionHandler = null);
void SetAppOptOut(bool optOut);
}
}

View File

@@ -2,6 +2,7 @@
{
public interface IHttpService
{
ApiHttpClient Client { get; }
ApiHttpClient ApiClient { get; }
IdentityHttpClient IdentityClient { get; }
}
}

View File

@@ -1,9 +1,12 @@
using Bit.App.Enums;
using System;
using System.Threading.Tasks;
namespace Bit.App.Abstractions
{
public interface ILockService
{
LockType GetLockType(bool forceLock);
void UpdateLastActivity(DateTime? activityDate = null);
Task<LockType> GetLockTypeAsync(bool forceLock);
}
}

View File

@@ -2,6 +2,7 @@
using System.Threading.Tasks;
using Bit.App.Models;
using Bit.App.Models.Api;
using System;
namespace Bit.App.Abstractions
{
@@ -10,8 +11,11 @@ namespace Bit.App.Abstractions
Task<Login> GetByIdAsync(string id);
Task<IEnumerable<Login>> GetAllAsync();
Task<IEnumerable<Login>> GetAllAsync(bool favorites);
Task<IEnumerable<Login>> GetAllAsync(string uriString);
Task<Tuple<IEnumerable<Login>, IEnumerable<Login>>> GetAllAsync(string uriString);
Task<ApiResult<LoginResponse>> SaveAsync(Login login);
Task<ApiResult> DeleteAsync(string id);
Task<byte[]> DownloadAndDecryptAttachmentAsync(string url, string orgId = null);
Task<ApiResult<CipherResponse>> EncryptAndSaveAttachmentAsync(Login login, byte[] data, string fileName);
Task<ApiResult> DeleteAttachmentAsync(Login login, string attachmentId);
}
}

View File

@@ -0,0 +1,26 @@
namespace Bit.App.Abstractions
{
public interface IMemoryService
{
MemoryInfo GetInfo();
void Check();
}
public class MemoryInfo
{
public long FreeMemory { get; set; }
public long MaxMemory { get; set; }
public long TotalMemory { get; set; }
public long UsedMemory => TotalMemory - FreeMemory;
public double HeapUsage()
{
return UsedMemory / (double)TotalMemory;
}
public double Usage()
{
return UsedMemory / (double)MaxMemory;
}
}
}

View File

@@ -6,9 +6,12 @@ namespace Bit.App.Abstractions
public interface ISyncService
{
bool SyncInProgress { get; }
Task<bool> SyncAsync(string id);
Task<bool> SyncCipherAsync(string id);
Task<bool> SyncFolderAsync(string id);
Task<bool> SyncDeleteFolderAsync(string id, DateTime revisionDate);
Task<bool> SyncDeleteLoginAsync(string id);
Task<bool> SyncSettingsAsync();
Task<bool> SyncProfileAsync();
Task<bool> FullSyncAsync(bool forceSync = false);
Task<bool> FullSyncAsync(TimeSpan syncThreshold, bool forceSync = false);
}

View File

@@ -8,12 +8,16 @@ namespace Bit.App.Abstractions
string RefreshToken { get; set; }
[Obsolete("Old auth scheme")]
string AuthBearer { get; set; }
string GetTwoFactorToken(string email);
void SetTwoFactorToken(string email, string token);
DateTime TokenExpiration { get; }
string TokenIssuer { get; }
bool TokenExpired { get; }
TimeSpan TokenTimeRemaining { get; }
bool TokenNeedsRefresh { get; }
string TokenUserId { get; }
string TokenEmail { get; }
string TokenName { get; }
bool TokenPremium { get; }
}
}

View File

@@ -4,7 +4,6 @@ using Bit.App.Abstractions;
using Bit.App.Pages;
using Xamarin.Forms;
using System.Diagnostics;
using Plugin.Fingerprint.Abstractions;
using System.Threading.Tasks;
using Plugin.Settings.Abstractions;
using Bit.App.Controls;
@@ -14,26 +13,25 @@ using Acr.UserDialogs;
using XLabs.Ioc;
using System.Reflection;
using Bit.App.Resources;
using System.Threading;
using Bit.App.Utilities;
namespace Bit.App
{
public class App : Application
{
private const string LastBuildKey = "LastBuild";
private string _uri;
private readonly IDatabaseService _databaseService;
private readonly IConnectivity _connectivity;
private readonly IUserDialogs _userDialogs;
private readonly ISyncService _syncService;
private readonly IAuthService _authService;
private readonly IFingerprint _fingerprint;
private readonly ISettings _settings;
private readonly ILockService _lockService;
private readonly IGoogleAnalyticsService _googleAnalyticsService;
private readonly ILocalizeService _localizeService;
private readonly IAppInfoService _appInfoService;
private readonly IAppSettingsService _appSettingsService;
private readonly IDeviceActionService _deviceActionService;
public App(
string uri,
@@ -42,12 +40,13 @@ namespace Bit.App
IUserDialogs userDialogs,
IDatabaseService databaseService,
ISyncService syncService,
IFingerprint fingerprint,
ISettings settings,
ILockService lockService,
IGoogleAnalyticsService googleAnalyticsService,
ILocalizeService localizeService,
IAppInfoService appInfoService)
IAppInfoService appInfoService,
IAppSettingsService appSettingsService,
IDeviceActionService deviceActionService)
{
_uri = uri;
_databaseService = databaseService;
@@ -55,12 +54,13 @@ namespace Bit.App
_userDialogs = userDialogs;
_syncService = syncService;
_authService = authService;
_fingerprint = fingerprint;
_settings = settings;
_lockService = lockService;
_googleAnalyticsService = googleAnalyticsService;
_localizeService = localizeService;
_appInfoService = appInfoService;
_appSettingsService = appSettingsService;
_deviceActionService = deviceActionService;
SetCulture();
SetStyles();
@@ -80,7 +80,7 @@ namespace Bit.App
MessagingCenter.Subscribe<Application, bool>(Current, "Resumed", async (sender, args) =>
{
await CheckLockAsync(args);
Device.BeginInvokeOnMainThread(async () => await CheckLockAsync(args));
await Task.Run(() => FullSyncAsync()).ConfigureAwait(false);
});
@@ -91,12 +91,7 @@ namespace Bit.App
MessagingCenter.Subscribe<Application, string>(Current, "Logout", (sender, args) =>
{
Device.BeginInvokeOnMainThread(() => Logout(args));
});
MessagingCenter.Subscribe<Application>(Current, "SetMainPage", (sender) =>
{
SetMainPageFromAutofill();
Logout(args);
});
}
@@ -107,16 +102,15 @@ namespace Bit.App
if(string.IsNullOrWhiteSpace(_uri))
{
var lastBuild = _settings.GetValueOrDefault<string>(LastBuildKey);
if(InDebugMode() || lastBuild == null || lastBuild != _appInfoService.Build)
{
_settings.AddOrUpdateValue(LastBuildKey, _appInfoService.Build);
_databaseService.CreateTables();
}
Helpers.PerformUpdateTasks(_settings, _appInfoService, _databaseService);
await Task.Run(() => FullSyncAsync()).ConfigureAwait(false);
}
if((DateTime.UtcNow - _appSettingsService.LastCacheClear).TotalDays >= 1)
{
await Task.Run(() => _deviceActionService.ClearCache()).ConfigureAwait(false);
}
Debug.WriteLine("OnStart");
}
@@ -126,9 +120,10 @@ namespace Bit.App
Debug.WriteLine("OnSleep");
SetMainPageFromAutofill();
if(Device.OS == TargetPlatform.Android && !TopPageIsLock())
if(Device.RuntimePlatform == Device.Android && !TopPageIsLock())
{
_settings.AddOrUpdateValue(Constants.LastActivityDate, DateTime.UtcNow);
_lockService.UpdateLastActivity();
}
}
@@ -143,7 +138,7 @@ namespace Bit.App
// Handle when your app resumes
Debug.WriteLine("OnResume");
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
await CheckLockAsync(false);
}
@@ -154,30 +149,32 @@ namespace Bit.App
lockPinPage.PinControl.Entry.FocusWithDelay();
}
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
await Task.Run(() => FullSyncAsync()).ConfigureAwait(false);
}
}
private bool InDebugMode()
{
#if DEBUG
return true;
#else
return false;
#endif
var now = DateTime.UtcNow;
if((now - _appSettingsService.LastCacheClear).TotalDays >= 1
&& (now - _appSettingsService.LastActivity).TotalHours >= 1)
{
await Task.Run(() => _deviceActionService.ClearCache()).ConfigureAwait(false);
}
}
private void SetMainPageFromAutofill()
{
if(Device.OS != TargetPlatform.Android || string.IsNullOrWhiteSpace(_uri))
if(Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(_uri))
{
return;
Task.Run(() =>
{
Device.BeginInvokeOnMainThread(() =>
{
Current.MainPage = new MainPage();
_uri = null;
});
});
}
MainPage = new MainPage();
_uri = null;
}
private async Task FullSyncAsync()
@@ -222,19 +219,17 @@ namespace Bit.App
{
_authService.LogOut();
_googleAnalyticsService.TrackAppEvent("LoggedOut");
_googleAnalyticsService.RefreshUserId();
var deviceApiRepository = Resolver.Resolve<IDeviceApiRepository>();
var appIdService = Resolver.Resolve<IAppIdService>();
await Task.Run(() => deviceApiRepository.PutClearTokenAsync(appIdService.AppId)).ConfigureAwait(false);
Current.MainPage = new ExtendedNavigationPage(new HomePage());
_googleAnalyticsService.TrackAppEvent("LoggedOut");
Device.BeginInvokeOnMainThread(() => Current.MainPage = new ExtendedNavigationPage(new HomePage()));
if(!string.IsNullOrWhiteSpace(logoutMessage))
{
_userDialogs.Toast(logoutMessage);
}
var deviceApiRepository = Resolver.Resolve<IDeviceApiRepository>();
var appIdService = Resolver.Resolve<IAppIdService>();
_settings.Remove(Constants.PushLastRegistrationDate);
await Task.Run(() => deviceApiRepository.PutClearTokenAsync(appIdService.AppId)).ConfigureAwait(false);
}
private async Task CheckLockAsync(bool forceLock)
@@ -245,8 +240,13 @@ namespace Bit.App
return;
}
var lockType = _lockService.GetLockType(forceLock);
var currentPage = Current.MainPage.Navigation.ModalStack.LastOrDefault() as ExtendedNavigationPage;
var lockType = await _lockService.GetLockTypeAsync(forceLock);
if(lockType == Enums.LockType.None)
{
return;
}
_appSettingsService.Locked = true;
switch(lockType)
{
case Enums.LockType.Fingerprint:
@@ -381,7 +381,7 @@ namespace Bit.App
Debug.WriteLine("====================================");
// This lookup NOT required for Windows platforms - the Culture will be automatically set
if(Device.OS == TargetPlatform.iOS || Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.iOS || Device.RuntimePlatform == Device.Android)
{
var ci = _localizeService.GetCurrentCultureInfo();
AppResources.Culture = ci;

View File

@@ -35,10 +35,14 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Compile Include="Abstractions\Repositories\IAttachmentRepository.cs" />
<Compile Include="Abstractions\Repositories\ITwoFactorApiRepository.cs" />
<Compile Include="Abstractions\Repositories\ISettingsApiRepository.cs" />
<Compile Include="Abstractions\Repositories\IAccountsApiRepository.cs" />
<Compile Include="Abstractions\Repositories\IDeviceApiRepository.cs" />
<Compile Include="Abstractions\Repositories\ISettingsRepository.cs" />
<Compile Include="Abstractions\Services\IAppSettingsService.cs" />
<Compile Include="Abstractions\Services\IMemoryService.cs" />
<Compile Include="Abstractions\Services\ISettingsService.cs" />
<Compile Include="Abstractions\Services\ITokenService.cs" />
<Compile Include="Abstractions\Services\IHttpService.cs" />
@@ -47,7 +51,7 @@
<Compile Include="Abstractions\Services\IAppInfoService.cs" />
<Compile Include="Abstractions\Services\IAppIdService.cs" />
<Compile Include="Abstractions\Services\IAuthService.cs" />
<Compile Include="Abstractions\Services\IClipboardService.cs" />
<Compile Include="Abstractions\Services\IDeviceActionService.cs" />
<Compile Include="Abstractions\Services\IKeyDerivationService.cs" />
<Compile Include="Abstractions\Services\ILogService.cs" />
<Compile Include="Abstractions\Services\IReflectionService.cs" />
@@ -57,11 +61,15 @@
<Compile Include="Abstractions\Services\ISecureStorageService.cs" />
<Compile Include="Abstractions\Services\ISqlService.cs" />
<Compile Include="Constants.cs" />
<Compile Include="Controls\HybridWebView.cs" />
<Compile Include="Controls\ExtendedToolbarItem.cs" />
<Compile Include="Controls\DismissModalToolBarItem.cs" />
<Compile Include="Controls\ExtendedEditor.cs" />
<Compile Include="Controls\ExtendedButton.cs" />
<Compile Include="Controls\ExtendedNavigationPage.cs" />
<Compile Include="Controls\ExtendedContentPage.cs" />
<Compile Include="Controls\LabeledRightDetailCell.cs" />
<Compile Include="Controls\MemoryContentView.cs" />
<Compile Include="Controls\StepperCell.cs" />
<Compile Include="Controls\ExtendedTableView.cs" />
<Compile Include="Controls\ExtendedPicker.cs" />
@@ -76,9 +84,14 @@
<Compile Include="Controls\FormPickerCell.cs" />
<Compile Include="Controls\FormEntryCell.cs" />
<Compile Include="Controls\PinControl.cs" />
<Compile Include="Controls\VaultAttachmentsViewCell.cs" />
<Compile Include="Controls\VaultListViewCell.cs" />
<Compile Include="Enums\TwoFactorProviderType.cs" />
<Compile Include="Enums\EncryptionType.cs" />
<Compile Include="Enums\OrganizationUserType.cs" />
<Compile Include="Enums\LockType.cs" />
<Compile Include="Enums\CipherType.cs" />
<Compile Include="Enums\OrganizationUserStatusType.cs" />
<Compile Include="Enums\PushType.cs" />
<Compile Include="Enums\ReturnType.cs" />
<Compile Include="Abstractions\Services\ILocalizeService.cs" />
@@ -88,11 +101,12 @@
<Compile Include="Models\Api\Request\DeviceTokenRequest.cs" />
<Compile Include="Models\Api\Request\FolderRequest.cs" />
<Compile Include="Models\Api\Request\DeviceRequest.cs" />
<Compile Include="Models\Api\Request\TwoFactorEmailRequest.cs" />
<Compile Include="Models\Api\Request\RegisterRequest.cs" />
<Compile Include="Models\Api\Request\LoginRequest.cs" />
<Compile Include="Models\Api\Request\PasswordHintRequest.cs" />
<Compile Include="Models\Api\Request\TokenRequest.cs" />
<Compile Include="Models\Api\Response\CipherHistoryResponse.cs" />
<Compile Include="Models\Api\Response\AttachmentResponse.cs" />
<Compile Include="Models\Api\Response\CipherResponse.cs" />
<Compile Include="Models\Api\Response\DomainsResponse.cs" />
<Compile Include="Models\Api\Response\ErrorResponse.cs" />
@@ -100,17 +114,23 @@
<Compile Include="Models\Api\Response\ListResponse.cs" />
<Compile Include="Models\Api\Response\DeviceResponse.cs" />
<Compile Include="Models\Api\Response\LoginResponse.cs" />
<Compile Include="Models\Api\Response\ProfileOrganizationResponse.cs" />
<Compile Include="Models\Api\Response\KeysResponse.cs" />
<Compile Include="Models\Api\Response\TokenResponse.cs" />
<Compile Include="Models\Api\Response\ProfileResponse.cs" />
<Compile Include="Models\Api\LoginDataModel.cs" />
<Compile Include="Models\Cipher.cs" />
<Compile Include="Models\CipherString.cs" />
<Compile Include="Models\Data\AttachmentData.cs" />
<Compile Include="Models\Attachment.cs" />
<Compile Include="Models\Page\VaultAttachmentsPageModel.cs" />
<Compile Include="Models\SymmetricCryptoKey.cs" />
<Compile Include="Models\Data\SettingsData.cs" />
<Compile Include="Models\Data\FolderData.cs" />
<Compile Include="Abstractions\IDataObject.cs" />
<Compile Include="Models\Data\LoginData.cs" />
<Compile Include="Models\DomainName.cs" />
<Compile Include="Models\Folder.cs" />
<Compile Include="Models\LoginResult.cs" />
<Compile Include="Models\Page\AppExtensionPageModel.cs" />
<Compile Include="Models\Page\SettingsFolderPageModel.cs" />
<Compile Include="Models\Page\PinPageModel.cs" />
@@ -125,8 +145,10 @@
<Compile Include="Pages\LoginTwoFactorPage.cs" />
<Compile Include="Pages\PasswordHintPage.cs" />
<Compile Include="Pages\RegisterPage.cs" />
<Compile Include="Pages\ScanPage.cs" />
<Compile Include="Pages\Settings\SettingsCreditsPage.cs" />
<Compile Include="Pages\Settings\SettingsHelpPage.cs" />
<Compile Include="Pages\Settings\SettingsFeaturesPage.cs" />
<Compile Include="Pages\Settings\SettingsPinPage.cs" />
<Compile Include="Pages\Lock\LockPinPage.cs" />
<Compile Include="Pages\MainPage.cs" />
@@ -142,8 +164,11 @@
<Compile Include="Pages\Settings\SettingsPage.cs" />
<Compile Include="Pages\Settings\SettingsListFoldersPage.cs" />
<Compile Include="Pages\Vault\VaultAutofillListLoginsPage.cs" />
<Compile Include="Pages\Vault\VaultAttachmentsPage.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Abstractions\Repositories\ILoginRepository.cs" />
<Compile Include="Repositories\AttachmentRepository.cs" />
<Compile Include="Repositories\TwoFactorApiRepository.cs" />
<Compile Include="Repositories\SettingsApiRepository.cs" />
<Compile Include="Repositories\ApiRepository.cs" />
<Compile Include="Repositories\AccountsApiRepository.cs" />
@@ -163,6 +188,11 @@
<Compile Include="Repositories\FolderRepository.cs" />
<Compile Include="Abstractions\Repositories\IFolderRepository.cs" />
<Compile Include="Abstractions\Repositories\IRepository.cs" />
<Compile Include="Resources\AppResources.cs.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.cs.resx</DependentUpon>
</Compile>
<Compile Include="Resources\AppResources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
@@ -183,16 +213,77 @@
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.fr.resx</DependentUpon>
</Compile>
<Compile Include="Resources\AppResources.hi.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.hi.resx</DependentUpon>
</Compile>
<Compile Include="Resources\AppResources.hr.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.hr.resx</DependentUpon>
</Compile>
<Compile Include="Resources\AppResources.id.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.id.resx</DependentUpon>
</Compile>
<Compile Include="Resources\AppResources.it.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.it.resx</DependentUpon>
</Compile>
<Compile Include="Resources\AppResources.ja.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.ja.resx</DependentUpon>
</Compile>
<Compile Include="Resources\AppResources.pt-BR.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.pt-BR.resx</DependentUpon>
</Compile>
<Compile Include="Resources\AppResources.pt-PT.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.pt-PT.resx</DependentUpon>
</Compile>
<Compile Include="Resources\AppResources.ro.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.ro.resx</DependentUpon>
</Compile>
<Compile Include="Resources\AppResources.ru.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.ru.resx</DependentUpon>
</Compile>
<Compile Include="Resources\AppResources.sk.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.sk.resx</DependentUpon>
</Compile>
<Compile Include="Resources\AppResources.sv.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.sv.resx</DependentUpon>
</Compile>
<Compile Include="Resources\AppResources.th.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.th.resx</DependentUpon>
</Compile>
<Compile Include="Resources\AppResources.zh-Hans.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.zh-Hans.resx</DependentUpon>
</Compile>
<Compile Include="Resources\AppResources.zh-Hant.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.zh-Hant.resx</DependentUpon>
</Compile>
<Compile Include="Services\AppSettingsService.cs" />
<Compile Include="Services\SettingsService.cs" />
<Compile Include="Services\TokenService.cs" />
<Compile Include="Services\AppIdService.cs" />
@@ -217,6 +308,10 @@
<Compile Include="Pages\Vault\VaultEditLoginPage.cs" />
<Compile Include="Pages\Vault\VaultListLoginsPage.cs" />
<Compile Include="Services\PasswordGenerationService.cs" />
<Compile Include="Utilities\Base32.cs" />
<Compile Include="Utilities\Crypto.cs" />
<Compile Include="Utilities\Helpers.cs" />
<Compile Include="Utilities\IdentityHttpClient.cs" />
<Compile Include="Utilities\Extentions.cs" />
<Compile Include="Utilities\ExtendedObservableCollection.cs" />
<Compile Include="Utilities\ApiHttpClient.cs" />
@@ -227,6 +322,10 @@
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
<EmbeddedResource Include="Resources\AppResources.cs.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.cs.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\AppResources.es.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.es.Designer.cs</LastGenOutput>
@@ -239,37 +338,87 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.fr.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\AppResources.hi.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.hi.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\AppResources.hr.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.hr.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\AppResources.id.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.id.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\AppResources.it.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.it.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\AppResources.ja.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.ja.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\AppResources.pt-BR.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.pt-BR.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\AppResources.pt-PT.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.pt-PT.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\AppResources.ro.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.ro.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\AppResources.ru.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.ru.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\AppResources.sk.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.sk.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\AppResources.sv.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.sv.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\AppResources.th.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.th.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\AppResources.zh-Hans.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.zh-Hans.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\AppResources.zh-Hant.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.zh-Hant.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\public_suffix_list.dat" />
</ItemGroup>
<ItemGroup>
<Reference Include="Acr.UserDialogs, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Acr.UserDialogs.6.3.3\lib\portable-win+net45+wp8+win8+wpa81\Acr.UserDialogs.dll</HintPath>
<Private>True</Private>
<HintPath>..\..\packages\Acr.UserDialogs.6.3.10\lib\portable-win+net45+wp8+win8+wpa81\Acr.UserDialogs.dll</HintPath>
</Reference>
<Reference Include="Acr.UserDialogs.Interface, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Acr.UserDialogs.6.3.3\lib\portable-win+net45+wp8+win8+wpa81\Acr.UserDialogs.Interface.dll</HintPath>
<Private>True</Private>
<HintPath>..\..\packages\Acr.UserDialogs.6.3.10\lib\portable-win+net45+wp8+win8+wpa81\Acr.UserDialogs.Interface.dll</HintPath>
</Reference>
<Reference Include="HockeySDK, Version=1.0.6103.22141, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\HockeySDK.Xamarin.4.1.0\lib\portable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10\HockeySDK.dll</HintPath>
<Private>True</Private>
<Reference Include="FFImageLoading, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.FFImageLoading.2.2.9\lib\portable-net45+win8+wpa81+wp8\FFImageLoading.dll</HintPath>
</Reference>
<Reference Include="FFImageLoading.Forms, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.FFImageLoading.Forms.2.2.9\lib\portable-net45+win8+wpa81+wp8\FFImageLoading.Forms.dll</HintPath>
</Reference>
<Reference Include="FFImageLoading.Platform, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.FFImageLoading.2.2.9\lib\portable-net45+win8+wpa81+wp8\FFImageLoading.Platform.dll</HintPath>
</Reference>
<Reference Include="HockeySDK, Version=1.0.6288.33979, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\HockeySDK.Xamarin.4.1.2\lib\portable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10\HockeySDK.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Practices.ServiceLocation, Version=1.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Practices.Unity, Version=3.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Unity.3.5.1405-prerelease\lib\portable-net45+wp80+win8+wpa81+MonoAndroid10+MonoTouch10\Microsoft.Practices.Unity.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Android">
<HintPath>..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\MonoAndroid\v6.0\Mono.Android.dll</HintPath>
</Reference>
@@ -297,29 +446,23 @@
<HintPath>..\..\packages\PInvoke.Windows.Core.0.3.152\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Windows.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Plugin.Connectivity, Version=2.2.12.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugin.Connectivity.2.2.12\lib\portable-net45+wp80+wp81+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10+Xamarin.Mac20+UAP10\Plugin.Connectivity.dll</HintPath>
<Private>True</Private>
<Reference Include="Plugin.Connectivity, Version=2.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugin.Connectivity.2.3.0\lib\portable-net45+wp80+win8+wpa81\Plugin.Connectivity.dll</HintPath>
</Reference>
<Reference Include="Plugin.Connectivity.Abstractions, Version=2.2.12.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugin.Connectivity.2.2.12\lib\portable-net45+wp80+wp81+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10+Xamarin.Mac20+UAP10\Plugin.Connectivity.Abstractions.dll</HintPath>
<Private>True</Private>
<Reference Include="Plugin.Connectivity.Abstractions, Version=2.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugin.Connectivity.2.3.0\lib\portable-net45+wp80+win8+wpa81\Plugin.Connectivity.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Plugin.Fingerprint, Version=1.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Plugin.Fingerprint.1.2.0\lib\portable-net45+win8+wpa81+wp8\Plugin.Fingerprint.dll</HintPath>
<Private>True</Private>
<Reference Include="Plugin.Fingerprint, Version=1.4.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Plugin.Fingerprint.1.4.4\lib\portable-net45+win8+wpa81+wp8\Plugin.Fingerprint.dll</HintPath>
</Reference>
<Reference Include="Plugin.Fingerprint.Abstractions, Version=1.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Plugin.Fingerprint.1.2.0\lib\portable-net45+win8+wpa81+wp8\Plugin.Fingerprint.Abstractions.dll</HintPath>
<Private>True</Private>
<Reference Include="Plugin.Fingerprint.Abstractions, Version=1.4.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Plugin.Fingerprint.1.4.4\lib\portable-net45+win8+wpa81+wp8\Plugin.Fingerprint.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Plugin.Settings, Version=2.5.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugins.Settings.2.5.1.0\lib\portable-net45+wp80+win8+wpa81\Plugin.Settings.dll</HintPath>
<Private>True</Private>
<Reference Include="Plugin.Settings, Version=2.5.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugins.Settings.2.5.4\lib\portable-net45+wp80+win8+wpa81\Plugin.Settings.dll</HintPath>
</Reference>
<Reference Include="Plugin.Settings.Abstractions, Version=2.5.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugins.Settings.2.5.1.0\lib\portable-net45+wp80+win8+wpa81\Plugin.Settings.Abstractions.dll</HintPath>
<Private>True</Private>
<Reference Include="Plugin.Settings.Abstractions, Version=2.5.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugins.Settings.2.5.4\lib\portable-net45+wp80+win8+wpa81\Plugin.Settings.Abstractions.dll</HintPath>
</Reference>
<Reference Include="PushNotification.Plugin, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugin.PushNotification.1.2.4\lib\portable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10+UAP10\PushNotification.Plugin.dll</HintPath>
@@ -362,21 +505,30 @@
<Private>True</Private>
</Reference>
<Reference Include="Xamarin.Forms.Core, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.Forms.2.3.3.180\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.Core.dll</HintPath>
<Private>True</Private>
<HintPath>..\..\packages\Xamarin.Forms.2.3.4.231\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.Core.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Forms.Platform, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.Forms.2.3.3.180\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.Platform.dll</HintPath>
<Private>True</Private>
<HintPath>..\..\packages\Xamarin.Forms.2.3.4.231\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.Platform.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Forms.Xaml, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.Forms.2.3.3.180\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.Xaml.dll</HintPath>
<Private>True</Private>
<HintPath>..\..\packages\Xamarin.Forms.2.3.4.231\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.Xaml.dll</HintPath>
</Reference>
<Reference Include="XLabs.Ioc, Version=2.0.5782.12218, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\XLabs.IoC.2.0.5782\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1+Xamarin.iOS10\XLabs.Ioc.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ZXing.Net.Mobile.Core, Version=2.1.47.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\ZXing.Net.Mobile.2.1.47\lib\portable-net45+netcore45+wpa81+wp8\ZXing.Net.Mobile.Core.dll</HintPath>
</Reference>
<Reference Include="ZXing.Net.Mobile.Forms, Version=2.1.47.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\ZXing.Net.Mobile.Forms.2.1.47\lib\portable-net45+netcore45+wpa81+wp8\ZXing.Net.Mobile.Forms.dll</HintPath>
</Reference>
<Reference Include="zxing.portable, Version=2.1.47.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\ZXing.Net.Mobile.2.1.47\lib\portable-net45+netcore45+wpa81+wp8\zxing.portable.dll</HintPath>
</Reference>
<Reference Include="ZXingNetMobile, Version=2.1.47.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\ZXing.Net.Mobile.2.1.47\lib\portable-net45+netcore45+wpa81+wp8\ZXingNetMobile.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\AppResources.resx">
@@ -388,12 +540,12 @@
<Compile Include="Services\PushNotificationListener.cs" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<Import Project="..\..\packages\Xamarin.Forms.2.3.3.180\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets" Condition="Exists('..\..\packages\Xamarin.Forms.2.3.3.180\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets')" />
<Import Project="..\..\packages\Xamarin.Forms.2.3.4.231\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets" Condition="Exists('..\..\packages\Xamarin.Forms.2.3.4.231\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\Xamarin.Forms.2.3.3.180\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Xamarin.Forms.2.3.3.180\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets'))" />
<Error Condition="!Exists('..\..\packages\Xamarin.Forms.2.3.4.231\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Xamarin.Forms.2.3.4.231\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -7,6 +7,10 @@
public const string SettingFingerprintUnlockOn = "setting:fingerprintUnlockOn";
public const string SettingPinUnlockOn = "setting:pinUnlockOn";
public const string SettingLockSeconds = "setting:lockSeconds";
public const string SettingGaOptOut = "setting:googleAnalyticsOptOut";
public const string SettingDisableTotpCopy = "setting:disableAutoCopyTotp";
public const string AutofillPersistNotification = "setting:persistNotification";
public const string AutofillPasswordField = "setting:autofillPasswordField";
public const string PasswordGeneratorLength = "pwGenerator:length";
public const string PasswordGeneratorUppercase = "pwGenerator:uppercase";
@@ -23,10 +27,15 @@
public const string ExtensionStarted = "extension:started";
public const string ExtensionActivated = "extension:activated";
public const string FirstVaultLoad = "other:firstVaultLoad";
public const string SecurityStamp = "other:securityStamp";
public const string LastActivityDate = "other:lastActivityDate";
public const string LastCacheClearDate = "other:cacheClearDate";
public const string Locked = "other:locked";
public const string LastLoginEmail = "other:lastLoginEmail";
public const string LastSync = "other:lastSync";
public const string LastBuildKey = "LastBuild";
public const int SelectFileRequestCode = 42;
public const int SelectFilePermissionRequestCode = 43;
}
}

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