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

Compare commits

...

201 Commits

Author SHA1 Message Date
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
282 changed files with 25178 additions and 5486 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,4 +1,5 @@
[![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
@@ -7,6 +8,8 @@
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**
@@ -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!

View File

@@ -11,4 +11,8 @@ 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
- 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>
@@ -92,41 +91,30 @@
<Private>True</Private>
</Reference>
<Reference Include="FFImageLoading, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.FFImageLoading.2.2.8\lib\MonoAndroid10\FFImageLoading.dll</HintPath>
<Private>True</Private>
<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.8\lib\MonoAndroid10\FFImageLoading.Forms.dll</HintPath>
<Private>True</Private>
<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.8\lib\MonoAndroid10\FFImageLoading.Forms.Droid.dll</HintPath>
<Private>True</Private>
<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.8\lib\MonoAndroid10\FFImageLoading.Platform.dll</HintPath>
<Private>True</Private>
<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="mscorlib" />
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
@@ -153,33 +141,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>
@@ -189,6 +174,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>
@@ -231,6 +219,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>
@@ -269,20 +258,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>
@@ -308,9 +293,8 @@
<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>
</ItemGroup>
<ItemGroup>
@@ -321,7 +305,6 @@
<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" />
@@ -331,6 +314,7 @@
<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" />
@@ -347,6 +331,7 @@
<Compile Include="Services\ReflectionService.cs" />
<Compile Include="Services\SqlService.cs" />
<Compile Include="SplashActivity.cs" />
<Compile Include="Utilities.cs" />
</ItemGroup>
<ItemGroup>
<None Include="8bit.keystore.enc" />
@@ -762,9 +747,6 @@
<ItemGroup>
<AndroidResource Include="Resources\drawable\icon.png" />
</ItemGroup>
<ItemGroup>
<AndroidEnvironment Include="EnvironmentVariables.txt" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\notification_sm.png" />
</ItemGroup>
@@ -846,6 +828,36 @@
<ItemGroup>
<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>
<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')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
@@ -854,10 +866,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

@@ -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
{
@@ -32,6 +34,7 @@ namespace Bit.Android
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", "sbrowser_url_bar"),
new Browser("com.sec.android.app.sbrowser.beta", "location_bar_edit_text"),
@@ -52,6 +55,15 @@ namespace Bit.Android
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)
{
try
@@ -63,39 +75,90 @@ namespace Bit.Android
return;
}
/*
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();
*/
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
var cancelNotification = true;
switch(e.EventType)
{
case EventTypes.WindowContentChanged:
case EventTypes.WindowStateChanged:
var cancelNotification = true;
if(e.PackageName == BitwardenPackage)
case EventTypes.ViewFocused:
if(!e.Source.Password || !_appSettings.AutofillPasswordField)
{
notificationManager?.Cancel(AutoFillNotificationId);
break;
}
var passwordNodes = GetWindowNodes(root, e, n => n.Password, false);
if(passwordNodes.Count > 0)
if(e.PackageName == BitwardenPackage)
{
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))
{
if(NeedToAutofill(AutofillActivity.LastCredentials, uri))
var needToFill = NeedToAutofill(AutofillActivity.LastCredentials, uri);
if(needToFill)
{
var allEditTexts = GetWindowNodes(root, e, n => EditText(n), false);
var usernameEditText = allEditTexts.TakeWhile(n => !n.Password).LastOrDefault();
FillCredentials(usernameEditText, passwordNodes);
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();
allEditTexts.Dispose();
usernameEditText.Dispose();
}
passwordNodes.Dispose();
}
else
if(!needToFill)
{
NotifyToAutofill(uri, notificationManager);
cancelNotification = false;
@@ -104,12 +167,14 @@ namespace Bit.Android
AutofillActivity.LastCredentials = null;
}
passwordNodes.Dispose();
else
{
cancelNotification = ScanAndAutofill(root, e, notificationManager, cancelNotification);
}
if(cancelNotification)
{
notificationManager?.Cancel(AutoFillNotificationId);
CancelNotification(notificationManager);
}
break;
default:
@@ -120,8 +185,8 @@ namespace Bit.Android
root.Dispose();
e.Dispose();
}
// Some unknown condition is causing NullReferenceException's in production. Suppress it for now.
catch(NullReferenceException) { }
// Suppress exceptions so that service doesn't crash
catch { }
}
public override void OnInterrupt()
@@ -129,6 +194,48 @@ namespace Bit.Android
}
public bool ScanAndAutofill(AccessibilityNodeInfo root, AccessibilityEvent e,
NotificationManager notificationManager, bool cancelNotification)
{
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 void CancelNotification(NotificationManager notificationManager, long limit = 250)
{
if(Java.Lang.JavaSystem.CurrentTimeMillis() - _lastNotificationTime < limit)
{
return;
}
_lastNotificationUri = null;
notificationManager?.Cancel(AutoFillNotificationId);
}
private string GetUri(AccessibilityNodeInfo root)
{
var uri = string.Concat(App.Constants.AndroidAppProtocol, root.PackageName);
@@ -208,6 +315,7 @@ namespace Bit.Android
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);
@@ -218,7 +326,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)
@@ -228,6 +336,13 @@ namespace Bit.Android
Resource.Color.primary));
}
if(/*Build.VERSION.SdkInt <= BuildVersionCodes.N && */_appSettings.AutofillPersistNotification)
{
builder.SetPriority(-2);
}
_lastNotificationTime = now;
_lastNotificationUri = uri;
notificationManager.Notify(AutoFillNotificationId, builder.Build());
builder.Dispose();

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,7 @@ using Xamarin.Forms.Platform.Android;
using Xamarin.Forms;
using System.Threading.Tasks;
using Bit.App.Models.Page;
using Bit.App;
namespace Bit.Android
{
@@ -25,6 +25,7 @@ namespace Bit.Android
public class MainActivity : FormsAppCompatActivity
{
private const string HockeyAppId = "d3834185b4a643479047b86c65293d42";
private DateTime? _lastAction;
protected override void OnCreate(Bundle bundle)
{
@@ -68,12 +69,12 @@ namespace Bit.Android
Resolver.Resolve<IUserDialogs>(),
Resolver.Resolve<IDatabaseService>(),
Resolver.Resolve<ISyncService>(),
Resolver.Resolve<IFingerprint>(),
Resolver.Resolve<ISettings>(),
Resolver.Resolve<ILockService>(),
Resolver.Resolve<IGoogleAnalyticsService>(),
Resolver.Resolve<ILocalizeService>(),
Resolver.Resolve<IAppInfoService>()));
Resolver.Resolve<IAppInfoService>(),
Resolver.Resolve<IAppSettingsService>()));
MessagingCenter.Subscribe<Xamarin.Forms.Application>(Xamarin.Forms.Application.Current, "RateApp", (sender) =>
{
@@ -95,6 +96,12 @@ namespace Bit.Android
{
MoveTaskToBack(true);
});
MessagingCenter.Subscribe<Xamarin.Forms.Application, string>(
Xamarin.Forms.Application.Current, "LaunchApp", (sender, args) =>
{
LaunchApp(args);
});
}
private void ReturnCredentials(VaultListPageModel.Login login)
@@ -119,7 +126,7 @@ namespace Bit.Android
{
Parent.SetResult(Result.Ok, data);
}
Finish();
}
@@ -200,5 +207,26 @@ 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);
}
}
}
}

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,11 +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
{
@@ -39,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)
{
@@ -48,6 +46,23 @@ namespace Bit.Android
}
}
private void AndroidEnvironment_UnhandledExceptionRaiser(object sender, RaiseThrowableEventArgs e)
{
var message = AppendExceptionToMessage("", e.Exception);
Utilities.SendCrashEmail(message, false);
}
private 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 override void OnCreate()
{
base.OnCreate();
@@ -182,61 +197,68 @@ 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())
.RegisterType<IMemoryService, MemoryService>(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());
CachedImageRenderer.Init();
Resolver.SetResolver(new UnityResolver(container));
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<IClipboardService, ClipboardService>();
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<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>();
// 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

@@ -1,14 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.x8bit.bitwarden" android:versionName="1.4.4" android:installLocation="auto" android:versionCode="502">
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.x8bit.bitwarden" android:versionName="1.7.0" 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.WRITE_EXTERNAL_STORAGE" />
<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>
</manifest>
<application android:label="bitwarden" android:theme="@style/BitwardenTheme" android:allowBackup="false"></application>
</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)]

View File

@@ -187,6 +187,8 @@ namespace Bit.Android
global::PCLCrypto.Resource.String.Hello = global::Bit.Android.Resource.String.Hello;
global::Plugin.Fingerprint.Resource.Drawable.fingerprint_white = global::Bit.Android.Resource.Drawable.fingerprint_white;
global::Plugin.Fingerprint.Resource.Id.fingerprint_btnCancel = global::Bit.Android.Resource.Id.fingerprint_btnCancel;
global::Plugin.Fingerprint.Resource.Id.fingerprint_btnFallback = global::Bit.Android.Resource.Id.fingerprint_btnFallback;
global::Plugin.Fingerprint.Resource.Id.fingerprint_imgFingerprint = global::Bit.Android.Resource.Id.fingerprint_imgFingerprint;
global::Plugin.Fingerprint.Resource.Id.fingerprint_txtReason = global::Bit.Android.Resource.Id.fingerprint_txtReason;
global::Plugin.Fingerprint.Resource.Layout.FingerprintDialog = global::Bit.Android.Resource.Layout.FingerprintDialog;
global::Splat.Resource.String.library_name = global::Bit.Android.Resource.String.library_name;
@@ -2742,8 +2744,8 @@ namespace Bit.Android
// aapt resource value: 0x7f0200e4
public const int notification_sm = 2130837732;
// aapt resource value: 0x7f0200f1
public const int notification_template_icon_bg = 2130837745;
// aapt resource value: 0x7f0200f3
public const int notification_template_icon_bg = 2130837747;
// aapt resource value: 0x7f0200e5
public const int plus = 2130837733;
@@ -2761,25 +2763,31 @@ namespace Bit.Android
public const int search = 2130837737;
// aapt resource value: 0x7f0200ea
public const int splash_screen = 2130837738;
public const int share = 2130837738;
// aapt resource value: 0x7f0200eb
public const int star = 2130837739;
public const int share_tools = 2130837739;
// aapt resource value: 0x7f0200ec
public const int star_selected = 2130837740;
public const int splash_screen = 2130837740;
// aapt resource value: 0x7f0200ed
public const int tools = 2130837741;
public const int star = 2130837741;
// aapt resource value: 0x7f0200ee
public const int tools_selected = 2130837742;
public const int star_selected = 2130837742;
// aapt resource value: 0x7f0200ef
public const int upload = 2130837743;
public const int tools = 2130837743;
// aapt resource value: 0x7f0200f0
public const int user = 2130837744;
public const int tools_selected = 2130837744;
// aapt resource value: 0x7f0200f1
public const int upload = 2130837745;
// aapt resource value: 0x7f0200f2
public const int user = 2130837746;
static Drawable()
{
@@ -2794,8 +2802,8 @@ namespace Bit.Android
public partial class Id
{
// aapt resource value: 0x7f0c00b5
public const int action0 = 2131493045;
// aapt resource value: 0x7f0c00b7
public const int action0 = 2131493047;
// aapt resource value: 0x7f0c0062
public const int action_bar = 2131492962;
@@ -2821,8 +2829,8 @@ namespace Bit.Android
// aapt resource value: 0x7f0c0063
public const int action_context_bar = 2131492963;
// aapt resource value: 0x7f0c00b9
public const int action_divider = 2131493049;
// aapt resource value: 0x7f0c00bb
public const int action_divider = 2131493051;
// aapt resource value: 0x7f0c0003
public const int action_menu_divider = 2131492867;
@@ -2866,26 +2874,26 @@ namespace Bit.Android
// aapt resource value: 0x7f0c004c
public const int buttonPanel = 2131492940;
// aapt resource value: 0x7f0c0089
public const int button_add_response = 2131493001;
// aapt resource value: 0x7f0c008b
public const int button_add_response = 2131493003;
// aapt resource value: 0x7f0c0084
public const int button_attachment = 2131492996;
// aapt resource value: 0x7f0c0086
public const int button_attachment = 2131492998;
// aapt resource value: 0x7f0c008e
public const int button_login = 2131493006;
// aapt resource value: 0x7f0c0090
public const int button_login = 2131493008;
// aapt resource value: 0x7f0c008a
public const int button_refresh = 2131493002;
// aapt resource value: 0x7f0c008c
public const int button_refresh = 2131493004;
// aapt resource value: 0x7f0c0085
public const int button_send = 2131492997;
// aapt resource value: 0x7f0c0087
public const int button_send = 2131492999;
// aapt resource value: 0x7f0c0092
public const int button_update = 2131493010;
// aapt resource value: 0x7f0c0094
public const int button_update = 2131493012;
// aapt resource value: 0x7f0c00b6
public const int cancel_action = 2131493046;
// aapt resource value: 0x7f0c00b8
public const int cancel_action = 2131493048;
// aapt resource value: 0x7f0c0033
public const int center = 2131492915;
@@ -2899,8 +2907,8 @@ namespace Bit.Android
// aapt resource value: 0x7f0c005a
public const int checkbox = 2131492954;
// aapt resource value: 0x7f0c00bc
public const int chronometer = 2131493052;
// aapt resource value: 0x7f0c00be
public const int chronometer = 2131493054;
// aapt resource value: 0x7f0c003b
public const int clip_horizontal = 2131492923;
@@ -2953,8 +2961,8 @@ namespace Bit.Android
// aapt resource value: 0x7f0c0024
public const int end = 2131492900;
// aapt resource value: 0x7f0c00c1
public const int end_padder = 2131493057;
// aapt resource value: 0x7f0c00c3
public const int end_padder = 2131493059;
// aapt resource value: 0x7f0c002b
public const int enterAlways = 2131492907;
@@ -2980,11 +2988,17 @@ namespace Bit.Android
// aapt resource value: 0x7f0c0036
public const int fill_vertical = 2131492918;
// aapt resource value: 0x7f0c007b
public const int fingerprint_btnCancel = 2131492987;
// aapt resource value: 0x7f0c007c
public const int fingerprint_btnCancel = 2131492988;
// aapt resource value: 0x7f0c007d
public const int fingerprint_btnFallback = 2131492989;
// aapt resource value: 0x7f0c007a
public const int fingerprint_txtReason = 2131492986;
public const int fingerprint_imgFingerprint = 2131492986;
// aapt resource value: 0x7f0c007b
public const int fingerprint_txtReason = 2131492987;
// aapt resource value: 0x7f0c0040
public const int @fixed = 2131492928;
@@ -3007,47 +3021,47 @@ namespace Bit.Android
// aapt resource value: 0x7f0c0047
public const int image = 2131492935;
// aapt resource value: 0x7f0c00c0
public const int info = 2131493056;
// aapt resource value: 0x7f0c0080
public const int input_email = 2131492992;
// aapt resource value: 0x7f0c00c2
public const int info = 2131493058;
// aapt resource value: 0x7f0c0082
public const int input_message = 2131492994;
public const int input_email = 2131492994;
// aapt resource value: 0x7f0c007f
public const int input_name = 2131492991;
// aapt resource value: 0x7f0c008d
public const int input_password = 2131493005;
// aapt resource value: 0x7f0c0084
public const int input_message = 2131492996;
// aapt resource value: 0x7f0c0081
public const int input_subject = 2131492993;
public const int input_name = 2131492993;
// aapt resource value: 0x7f0c008f
public const int input_password = 2131493007;
// aapt resource value: 0x7f0c0083
public const int input_subject = 2131492995;
// aapt resource value: 0x7f0c0000
public const int item_touch_helper_previous_elevation = 2131492864;
// aapt resource value: 0x7f0c0094
public const int label_author = 2131493012;
// aapt resource value: 0x7f0c0095
public const int label_date = 2131493013;
// aapt resource value: 0x7f0c0087
public const int label_last_updated = 2131492999;
// aapt resource value: 0x7f0c007c
public const int label_message = 2131492988;
// aapt resource value: 0x7f0c0096
public const int label_text = 2131493014;
public const int label_author = 2131493014;
// aapt resource value: 0x7f0c0090
public const int label_title = 2131493008;
// aapt resource value: 0x7f0c0097
public const int label_date = 2131493015;
// aapt resource value: 0x7f0c0091
public const int label_version = 2131493009;
// aapt resource value: 0x7f0c0089
public const int label_last_updated = 2131493001;
// aapt resource value: 0x7f0c007e
public const int label_message = 2131492990;
// aapt resource value: 0x7f0c0098
public const int label_text = 2131493016;
// aapt resource value: 0x7f0c0092
public const int label_title = 2131493010;
// aapt resource value: 0x7f0c0093
public const int label_version = 2131493011;
// aapt resource value: 0x7f0c0037
public const int left = 2131492919;
@@ -3055,35 +3069,35 @@ namespace Bit.Android
// aapt resource value: 0x7f0c0013
public const int light = 2131492883;
// aapt resource value: 0x7f0c00ba
public const int line1 = 2131493050;
// aapt resource value: 0x7f0c00bc
public const int line1 = 2131493052;
// aapt resource value: 0x7f0c00be
public const int line3 = 2131493054;
// aapt resource value: 0x7f0c00c0
public const int line3 = 2131493056;
// aapt resource value: 0x7f0c0014
public const int listMode = 2131492884;
// aapt resource value: 0x7f0c0097
public const int list_attachments = 2131493015;
// aapt resource value: 0x7f0c0099
public const int list_attachments = 2131493017;
// aapt resource value: 0x7f0c008b
public const int list_feedback_messages = 2131493003;
// aapt resource value: 0x7f0c008d
public const int list_feedback_messages = 2131493005;
// aapt resource value: 0x7f0c0049
public const int list_item = 2131492937;
// aapt resource value: 0x7f0c009c
public const int loadingImage = 2131493020;
// aapt resource value: 0x7f0c009a
public const int loadingImage = 2131493018;
public const int loadingProgressBar = 2131493018;
// aapt resource value: 0x7f0c0098
public const int loadingProgressBar = 2131493016;
// aapt resource value: 0x7f0c009d
public const int loadingProgressWheel = 2131493021;
// aapt resource value: 0x7f0c009b
public const int loadingProgressWheel = 2131493019;
// aapt resource value: 0x7f0c00b8
public const int media_actions = 2131493048;
// aapt resource value: 0x7f0c00ba
public const int media_actions = 2131493050;
// aapt resource value: 0x7f0c0025
public const int middle = 2131492901;
@@ -3091,77 +3105,77 @@ namespace Bit.Android
// aapt resource value: 0x7f0c003f
public const int mini = 2131492927;
// aapt resource value: 0x7f0c00a7
public const int mr_art = 2131493031;
// aapt resource value: 0x7f0c009c
public const int mr_chooser_list = 2131493020;
// aapt resource value: 0x7f0c009f
public const int mr_chooser_route_desc = 2131493023;
// aapt resource value: 0x7f0c009d
public const int mr_chooser_route_icon = 2131493021;
// aapt resource value: 0x7f0c00a9
public const int mr_art = 2131493033;
// aapt resource value: 0x7f0c009e
public const int mr_chooser_route_name = 2131493022;
// aapt resource value: 0x7f0c00a4
public const int mr_close = 2131493028;
// aapt resource value: 0x7f0c00aa
public const int mr_control_divider = 2131493034;
// aapt resource value: 0x7f0c00b0
public const int mr_control_play_pause = 2131493040;
// aapt resource value: 0x7f0c00b3
public const int mr_control_subtitle = 2131493043;
// aapt resource value: 0x7f0c00b2
public const int mr_control_title = 2131493042;
// aapt resource value: 0x7f0c00b1
public const int mr_control_title_container = 2131493041;
// aapt resource value: 0x7f0c00a5
public const int mr_custom_control = 2131493029;
// aapt resource value: 0x7f0c00a6
public const int mr_default_control = 2131493030;
public const int mr_chooser_list = 2131493022;
// aapt resource value: 0x7f0c00a1
public const int mr_dialog_area = 2131493025;
public const int mr_chooser_route_desc = 2131493025;
// aapt resource value: 0x7f0c009f
public const int mr_chooser_route_icon = 2131493023;
// aapt resource value: 0x7f0c00a0
public const int mr_expandable_area = 2131493024;
public const int mr_chooser_route_name = 2131493024;
// aapt resource value: 0x7f0c00b4
public const int mr_group_expand_collapse = 2131493044;
// aapt resource value: 0x7f0c00a8
public const int mr_media_main_control = 2131493032;
// aapt resource value: 0x7f0c00a3
public const int mr_name = 2131493027;
// aapt resource value: 0x7f0c00a9
public const int mr_playback_control = 2131493033;
// aapt resource value: 0x7f0c00a2
public const int mr_title_bar = 2131493026;
// aapt resource value: 0x7f0c00ab
public const int mr_volume_control = 2131493035;
// aapt resource value: 0x7f0c00a6
public const int mr_close = 2131493030;
// aapt resource value: 0x7f0c00ac
public const int mr_volume_group_list = 2131493036;
public const int mr_control_divider = 2131493036;
// aapt resource value: 0x7f0c00b2
public const int mr_control_play_pause = 2131493042;
// aapt resource value: 0x7f0c00b5
public const int mr_control_subtitle = 2131493045;
// aapt resource value: 0x7f0c00b4
public const int mr_control_title = 2131493044;
// aapt resource value: 0x7f0c00b3
public const int mr_control_title_container = 2131493043;
// aapt resource value: 0x7f0c00a7
public const int mr_custom_control = 2131493031;
// aapt resource value: 0x7f0c00a8
public const int mr_default_control = 2131493032;
// aapt resource value: 0x7f0c00a3
public const int mr_dialog_area = 2131493027;
// aapt resource value: 0x7f0c00a2
public const int mr_expandable_area = 2131493026;
// aapt resource value: 0x7f0c00b6
public const int mr_group_expand_collapse = 2131493046;
// aapt resource value: 0x7f0c00aa
public const int mr_media_main_control = 2131493034;
// aapt resource value: 0x7f0c00a5
public const int mr_name = 2131493029;
// aapt resource value: 0x7f0c00ab
public const int mr_playback_control = 2131493035;
// aapt resource value: 0x7f0c00a4
public const int mr_title_bar = 2131493028;
// aapt resource value: 0x7f0c00ad
public const int mr_volume_control = 2131493037;
// aapt resource value: 0x7f0c00ae
public const int mr_volume_item_icon = 2131493038;
public const int mr_volume_group_list = 2131493038;
// aapt resource value: 0x7f0c00af
public const int mr_volume_slider = 2131493039;
// aapt resource value: 0x7f0c00b0
public const int mr_volume_item_icon = 2131493040;
// aapt resource value: 0x7f0c00b1
public const int mr_volume_slider = 2131493041;
// aapt resource value: 0x7f0c001e
public const int multiply = 2131492894;
@@ -3262,8 +3276,8 @@ namespace Bit.Android
// aapt resource value: 0x7f0c001b
public const int showTitle = 2131492891;
// aapt resource value: 0x7f0c00c2
public const int sliding_tabs = 2131493058;
// aapt resource value: 0x7f0c00c4
public const int sliding_tabs = 2131493060;
// aapt resource value: 0x7f0c0074
public const int snackbar_action = 2131492980;
@@ -3295,8 +3309,8 @@ namespace Bit.Android
// aapt resource value: 0x7f0c0039
public const int start = 2131492921;
// aapt resource value: 0x7f0c00b7
public const int status_bar_latest_event_content = 2131493047;
// aapt resource value: 0x7f0c00b9
public const int status_bar_latest_event_content = 2131493049;
// aapt resource value: 0x7f0c006d
public const int submit_area = 2131492973;
@@ -3304,23 +3318,23 @@ namespace Bit.Android
// aapt resource value: 0x7f0c0016
public const int tabMode = 2131492886;
// aapt resource value: 0x7f0c00bf
public const int text = 2131493055;
// aapt resource value: 0x7f0c00c1
public const int text = 2131493057;
// aapt resource value: 0x7f0c00bd
public const int text2 = 2131493053;
// aapt resource value: 0x7f0c00bf
public const int text2 = 2131493055;
// aapt resource value: 0x7f0c0055
public const int textSpacerNoButtons = 2131492949;
// aapt resource value: 0x7f0c0099
public const int textViewStatus = 2131493017;
// aapt resource value: 0x7f0c009b
public const int textViewStatus = 2131493019;
// aapt resource value: 0x7f0c008c
public const int text_headline = 2131493004;
// aapt resource value: 0x7f0c008e
public const int text_headline = 2131493006;
// aapt resource value: 0x7f0c00bb
public const int time = 2131493051;
// aapt resource value: 0x7f0c00bd
public const int time = 2131493053;
// aapt resource value: 0x7f0c004b
public const int title = 2131492939;
@@ -3328,8 +3342,8 @@ namespace Bit.Android
// aapt resource value: 0x7f0c0050
public const int title_template = 2131492944;
// aapt resource value: 0x7f0c00c3
public const int toolbar = 2131493059;
// aapt resource value: 0x7f0c00c5
public const int toolbar = 2131493061;
// aapt resource value: 0x7f0c003a
public const int top = 2131492922;
@@ -3346,17 +3360,17 @@ namespace Bit.Android
// aapt resource value: 0x7f0c001c
public const int useLogo = 2131492892;
// aapt resource value: 0x7f0c008f
public const int view_header = 2131493007;
// aapt resource value: 0x7f0c0091
public const int view_header = 2131493009;
// aapt resource value: 0x7f0c000a
public const int view_offset_helper = 2131492874;
// aapt resource value: 0x7f0c00ad
public const int volume_item_container = 2131493037;
// aapt resource value: 0x7f0c00af
public const int volume_item_container = 2131493039;
// aapt resource value: 0x7f0c0093
public const int web_update_details = 2131493011;
// aapt resource value: 0x7f0c0095
public const int web_update_details = 2131493013;
// aapt resource value: 0x7f0c0010
public const int wide = 2131492880;
@@ -3367,20 +3381,20 @@ namespace Bit.Android
// aapt resource value: 0x7f0c001d
public const int wrap_content = 2131492893;
// aapt resource value: 0x7f0c0083
public const int wrapper_attachments = 2131492995;
// aapt resource value: 0x7f0c0085
public const int wrapper_attachments = 2131492997;
// aapt resource value: 0x7f0c007e
public const int wrapper_feedback = 2131492990;
// aapt resource value: 0x7f0c0080
public const int wrapper_feedback = 2131492992;
// aapt resource value: 0x7f0c007d
public const int wrapper_feedback_scroll = 2131492989;
// aapt resource value: 0x7f0c0086
public const int wrapper_messages = 2131492998;
// aapt resource value: 0x7f0c007f
public const int wrapper_feedback_scroll = 2131492991;
// aapt resource value: 0x7f0c0088
public const int wrapper_messages_buttons = 2131493000;
public const int wrapper_messages = 2131493000;
// aapt resource value: 0x7f0c008a
public const int wrapper_messages_buttons = 2131493002;
static Id()
{

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: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

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: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

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,7 +1,7 @@
<?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="flagReportViewIds"
android:notificationTimeout="100"

View File

@@ -0,0 +1,367 @@
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
{
Console.WriteLine("Failed to decrypt from secure storage.");
_settings.Remove(formattedKey);
//Utilities.SendCrashEmail(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
{
Console.WriteLine("Failed to encrypt to secure storage.");
//Utilities.SendCrashEmail(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
{
Console.WriteLine("Cannot get AesKey.");
_keyStore.DeleteEntry(KeyAlias);
_settings.Remove(AesKey);
if(!v1)
{
//Utilities.SendCrashEmail(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,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
{

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

@@ -0,0 +1,50 @@
using System;
using Android.App;
using Android.Content;
using Java.Security;
namespace Bit.Android
{
public static class Utilities
{
public static void SendCrashEmail(Exception e, bool includeSecurityProviders = true)
{
SendCrashEmail(e.Message + "\n\n" + e.StackTrace, includeSecurityProviders);
}
public static void SendCrashEmail(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;
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, text);
Application.Context.StartActivity(Intent.CreateChooser(emailIntent, "Send mail..."));
}
}
}

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,14 +82,14 @@
<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.FFImageLoading" version="2.2.8" targetFramework="monoandroid60" />
<package id="Xamarin.FFImageLoading.Forms" version="2.2.8" 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" />
</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

@@ -8,6 +8,5 @@ namespace Bit.App.Abstractions
{
Task<ApiResult<CipherResponse>> GetByIdAsync(string id);
Task<ApiResult<ListResponse<CipherResponse>>> GetAsync();
Task<ApiResult<CipherHistoryResponse>> GetByRevisionDateWithHistoryAsync(DateTime since);
}
}

View File

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

View File

@@ -1,6 +1,5 @@
using System.Threading.Tasks;
using Bit.App.Models.Api;
using System;
using Bit.App.Models;
using System.Threading.Tasks;
namespace Bit.App.Abstractions
{
@@ -13,7 +12,9 @@ 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(string token, string email, string masterPasswordHash, SymmetricCryptoKey key);
}
}

View File

@@ -1,19 +1,30 @@
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[] RsaDecryptToBytes(CipherString encyptedValue, byte[] privateKey);
CipherString Encrypt(string plaintextValue, 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

@@ -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

@@ -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

@@ -9,6 +9,7 @@ namespace Bit.App.Abstractions
[Obsolete("Old auth scheme")]
string AuthBearer { get; set; }
DateTime TokenExpiration { get; }
string TokenIssuer { get; }
bool TokenExpired { get; }
TimeSpan TokenTimeRemaining { get; }
bool TokenNeedsRefresh { 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;
@@ -27,12 +26,12 @@ namespace Bit.App
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;
public App(
string uri,
@@ -41,12 +40,12 @@ 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)
{
_uri = uri;
_databaseService = databaseService;
@@ -54,12 +53,12 @@ namespace Bit.App
_userDialogs = userDialogs;
_syncService = syncService;
_authService = authService;
_fingerprint = fingerprint;
_settings = settings;
_lockService = lockService;
_googleAnalyticsService = googleAnalyticsService;
_localizeService = localizeService;
_appInfoService = appInfoService;
_appSettingsService = appSettingsService;
SetCulture();
SetStyles();
@@ -121,9 +120,9 @@ namespace Bit.App
SetMainPageFromAutofill();
if(Device.OS == TargetPlatform.Android && !TopPageIsLock())
if(Device.RuntimePlatform == Device.Android && !TopPageIsLock())
{
_settings.AddOrUpdateValue(Constants.LastActivityDate, DateTime.UtcNow);
_lockService.UpdateLastActivity();
}
}
@@ -138,7 +137,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);
}
@@ -149,7 +148,7 @@ namespace Bit.App
lockPinPage.PinControl.Entry.FocusWithDelay();
}
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
await Task.Run(() => FullSyncAsync()).ConfigureAwait(false);
}
@@ -157,7 +156,7 @@ namespace Bit.App
private void SetMainPageFromAutofill()
{
if(Device.OS == TargetPlatform.Android && !string.IsNullOrWhiteSpace(_uri))
if(Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(_uri))
{
Task.Run(() =>
{
@@ -226,7 +225,6 @@ namespace Bit.App
await Task.Run(() => deviceApiRepository.PutClearTokenAsync(appIdService.AppId)).ConfigureAwait(false);
_googleAnalyticsService.TrackAppEvent("LoggedOut");
_googleAnalyticsService.RefreshUserId();
Device.BeginInvokeOnMainThread(() => Current.MainPage = new ExtendedNavigationPage(new HomePage()));
if(!string.IsNullOrWhiteSpace(logoutMessage))
@@ -243,13 +241,13 @@ namespace Bit.App
return;
}
var lockType = _lockService.GetLockType(forceLock);
var lockType = await _lockService.GetLockTypeAsync(forceLock);
if(lockType == Enums.LockType.None)
{
return;
}
_settings.AddOrUpdateValue(Constants.Locked, true);
_appSettingsService.Locked = true;
switch(lockType)
{
case Enums.LockType.Fingerprint:
@@ -384,7 +382,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

@@ -39,6 +39,7 @@
<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" />
@@ -80,8 +81,11 @@
<Compile Include="Controls\FormEntryCell.cs" />
<Compile Include="Controls\PinControl.cs" />
<Compile Include="Controls\VaultListViewCell.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" />
@@ -103,17 +107,21 @@
<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\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" />
@@ -130,6 +138,7 @@
<Compile Include="Pages\RegisterPage.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" />
@@ -166,6 +175,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>
@@ -186,16 +200,67 @@
<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.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.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" />
@@ -220,6 +285,9 @@
<Compile Include="Pages\Vault\VaultEditLoginPage.cs" />
<Compile Include="Pages\Vault\VaultListLoginsPage.cs" />
<Compile Include="Services\PasswordGenerationService.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" />
@@ -230,6 +298,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>
@@ -242,49 +314,79 @@
<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.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.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="FFImageLoading, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.FFImageLoading.2.2.8\lib\portable-net45+win8+wpa81+wp8\FFImageLoading.dll</HintPath>
<Private>True</Private>
<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.8\lib\portable-net45+win8+wpa81+wp8\FFImageLoading.Forms.dll</HintPath>
<Private>True</Private>
<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.8\lib\portable-net45+win8+wpa81+wp8\FFImageLoading.Platform.dll</HintPath>
<Private>True</Private>
<HintPath>..\..\packages\Xamarin.FFImageLoading.2.2.9\lib\portable-net45+win8+wpa81+wp8\FFImageLoading.Platform.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="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>
@@ -312,29 +414,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>
@@ -377,16 +473,13 @@
<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>
@@ -403,12 +496,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,9 @@
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 AutofillPersistNotification = "setting:persistNotification";
public const string AutofillPasswordField = "setting:autofillPasswordField";
public const string PasswordGeneratorLength = "pwGenerator:length";
public const string PasswordGeneratorUppercase = "pwGenerator:uppercase";
@@ -23,7 +26,7 @@
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 Locked = "other:locked";
public const string LastLoginEmail = "other:lastLoginEmail";

View File

@@ -1,4 +1,5 @@
using Bit.App.Enums;
using Bit.App.Utilities;
using System;
using Xamarin.Forms;
@@ -10,7 +11,7 @@ namespace Bit.App.Controls
BindableProperty.Create(nameof(Padding), typeof(Thickness), typeof(ExtendedButton), default(Thickness));
public static readonly BindableProperty UppercaseProperty =
BindableProperty.Create(nameof(Uppercase), typeof(bool), typeof(ExtendedButton),
Device.OnPlatform(iOS: false, Android: true, WinPhone: false));
Helpers.OnPlatform(iOS: false, Android: true, WinPhone: false));
public Thickness Padding
{

View File

@@ -10,7 +10,7 @@ namespace Bit.App.Controls
{
private ISyncService _syncService;
private IGoogleAnalyticsService _googleAnalyticsService;
private ISettings _settings;
private ILockService _lockService;
private bool _syncIndicator;
private bool _updateActivity;
@@ -20,7 +20,7 @@ namespace Bit.App.Controls
_updateActivity = updateActivity;
_syncService = Resolver.Resolve<ISyncService>();
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
_settings = Resolver.Resolve<ISettings>();
_lockService = Resolver.Resolve<ILockService>();
BackgroundColor = Color.FromHex("efeff4");
@@ -45,11 +45,6 @@ namespace Bit.App.Controls
IsBusy = _syncService.SyncInProgress;
}
if(_updateActivity)
{
_settings.AddOrUpdateValue(Constants.LastActivityDate, DateTime.UtcNow);
}
_googleAnalyticsService.TrackPage(GetType().Name);
base.OnAppearing();
}
@@ -61,6 +56,11 @@ namespace Bit.App.Controls
IsBusy = false;
}
if(_updateActivity)
{
_lockService.UpdateLastActivity();
}
base.OnDisappearing();
}
}

View File

@@ -8,7 +8,7 @@ namespace Bit.App.Controls
{
public ExtendedEntry()
{
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
PlaceholderColor = Color.FromHex("c7c7cd");
}

View File

@@ -7,7 +7,7 @@ namespace Bit.App.Controls
{
public ExtendedTextCell()
{
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
TextColor = Color.Black;
}

View File

@@ -87,7 +87,7 @@ namespace Bit.App.Controls
VerticalOptions = LayoutOptions.CenterAndExpand
};
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
var deviceInfo = Resolver.Resolve<IDeviceInfoService>();
if(useLabelAsPlaceholder)
@@ -125,7 +125,7 @@ namespace Bit.App.Controls
Button = new ExtendedButton();
imageStackLayout.Children.Add(Button);
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
Button.Padding = new Thickness(0);
Button.BackgroundColor = Color.Transparent;

View File

@@ -34,7 +34,7 @@ namespace Bit.App.Controls
stackLayout.Children.Add(Label);
stackLayout.Children.Add(Picker);
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
stackLayout.Spacing = 0;
}

View File

@@ -1,4 +1,5 @@
using Xamarin.Forms;
using FFImageLoading.Forms;
using Xamarin.Forms;
namespace Bit.App.Controls
{
@@ -19,6 +20,14 @@ namespace Bit.App.Controls
Style = (Style)Application.Current.Resources["text-muted"]
};
LabelIcon = new CachedImage
{
WidthRequest = 16,
HeightRequest = 16,
HorizontalOptions = LayoutOptions.Start,
Margin = new Thickness(5, 0, 0, 0)
};
Button = new ExtendedButton
{
WidthRequest = 60
@@ -32,14 +41,17 @@ namespace Bit.App.Controls
};
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(60, GridUnitType.Absolute) });
grid.Children.Add(Label, 0, 0);
grid.Children.Add(Detail, 0, 1);
grid.Children.Add(Button, 1, 0);
grid.Children.Add(LabelIcon, 1, 0);
grid.Children.Add(Button, 2, 0);
Grid.SetColumnSpan(Detail, 2);
Grid.SetRowSpan(Button, 2);
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
Label.TextColor = Color.Black;
}
@@ -49,6 +61,7 @@ namespace Bit.App.Controls
public Label Label { get; private set; }
public Label Detail { get; private set; }
public CachedImage LabelIcon { get; private set; }
public Button Button { get; private set; }
}
}

View File

@@ -41,7 +41,7 @@ namespace Bit.App.Controls
LineBreakMode = LineBreakMode.TailTruncation
};
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
Value.TextColor = Color.Black;
}
@@ -82,7 +82,7 @@ namespace Bit.App.Controls
buttonStackLayout.Children.Add(Button2);
}
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
buttonStackLayout.Spacing = 5;

View File

@@ -1,4 +1,5 @@
using System;
using Bit.App.Utilities;
using System;
using Xamarin.Forms;
namespace Bit.App.Controls
@@ -13,7 +14,7 @@ namespace Bit.App.Controls
{
HorizontalTextAlignment = TextAlignment.Center,
FontSize = 35,
FontFamily = Device.OnPlatform(iOS: "Courier", Android: "monospace", WinPhone: "Courier")
FontFamily = Helpers.OnPlatform(iOS: "Courier", Android: "monospace", WinPhone: "Courier")
};
Entry = new ExtendedEntry
@@ -23,7 +24,7 @@ namespace Bit.App.Controls
Margin = new Thickness(0, int.MaxValue, 0, 0)
};
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
Label.TextColor = Color.Black;
}

View File

@@ -1,4 +1,5 @@
using Xamarin.Forms;
using Bit.App.Utilities;
using Xamarin.Forms;
namespace Bit.App.Controls
{
@@ -39,13 +40,13 @@ namespace Bit.App.Controls
Orientation = StackOrientation.Horizontal,
Children = { Label, StepperValueLabel, Stepper },
Spacing = 15,
Padding = Device.OnPlatform(
Padding = Helpers.OnPlatform(
iOS: new Thickness(15, 8),
Android: new Thickness(15, 2),
WinPhone: new Thickness(15, 8))
};
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
Label.TextColor = Color.Black;
}

View File

@@ -12,13 +12,16 @@ namespace Bit.App.Controls
public VaultListViewCell(Action<VaultListPageModel.Login> moreClickedAction)
{
SetBinding(LoginParameterProperty, new Binding("."));
Label.SetBinding<VaultListPageModel.Login>(Label.TextProperty, s => s.Name);
Detail.SetBinding<VaultListPageModel.Login>(Label.TextProperty, s => s.Username);
Label.SetBinding(Label.TextProperty, nameof(VaultListPageModel.Login.Name));
Detail.SetBinding(Label.TextProperty, nameof(VaultListPageModel.Login.Username));
LabelIcon.SetBinding(VisualElement.IsVisibleProperty, nameof(VaultListPageModel.Login.Shared));
Button.Image = "more";
Button.Command = new Command(() => moreClickedAction?.Invoke(LoginParameter));
Button.BackgroundColor = Color.Transparent;
LabelIcon.Source = "share";
BackgroundColor = Color.White;
}

View File

@@ -2,7 +2,8 @@
{
public enum CipherType : short
{
Folder = 0,
// Folder deprecated
//Folder = 0,
Login = 1
}
}

View File

@@ -0,0 +1,11 @@
namespace Bit.App.Enums
{
public enum EncryptionType : byte
{
AesCbc256_B64 = 0,
AesCbc128_HmacSha256_B64 = 1,
AesCbc256_HmacSha256_B64 = 2,
Rsa2048_OaepSha256_B64 = 3,
Rsa2048_OaepSha1_B64 = 4
}
}

View File

@@ -0,0 +1,9 @@
namespace Bit.App.Enums
{
public enum OrganizationUserStatusType : byte
{
Invited = 0,
Accepted = 1,
Confirmed = 2
}
}

View File

@@ -0,0 +1,9 @@
namespace Bit.App.Enums
{
public enum OrganizationUserType : byte
{
Owner = 0,
Admin = 1,
User = 2
}
}

View File

@@ -6,6 +6,13 @@
SyncCipherCreate = 1,
SyncLoginDelete = 2,
SyncFolderDelete = 3,
SyncCiphers = 4
SyncCiphers = 4,
SyncVault = 5,
SyncOrgKeys = 6,
SyncFolderCreate = 7,
SyncFolderUpdate = 8,
SyncCipherDelete = 9,
SyncSettings = 10
}
}

View File

@@ -12,7 +12,7 @@ namespace Bit.App.Models.Api
{
Identifier = appIdService.AppId;
Name = deviceInfoService.Model;
Type = Device.OS == TargetPlatform.Android ? DeviceType.Android : DeviceType.iOS;
Type = Device.RuntimePlatform == Device.Android ? DeviceType.Android : DeviceType.iOS;
}
public DeviceType Type { get; set; }

View File

@@ -4,6 +4,7 @@
{
public LoginRequest(Login login)
{
OrganizationId = login.OrganizationId;
FolderId = login.FolderId;
Name = login.Name?.EncryptedString;
Uri = login.Uri?.EncryptedString;
@@ -13,6 +14,7 @@
Favorite = login.Favorite;
}
public string OrganizationId { get; set; }
public string FolderId { get; set; }
public string Name { get; set; }
public string Uri { get; set; }

View File

@@ -6,5 +6,6 @@
public string Email { get; set; }
public string MasterPasswordHash { get; set; }
public string MasterPasswordHint { get; set; }
public string Key { get; set; }
}
}

View File

@@ -8,6 +8,8 @@ namespace Bit.App.Models.Api
{
public string Id { get; set; }
public string FolderId { get; set; }
public string UserId { get; set; }
public string OrganizationId { get; set; }
public CipherType Type { get; set; }
public bool Favorite { get; set; }
public JObject Data { get; set; }

View File

@@ -0,0 +1,8 @@
namespace Bit.App.Models.Api
{
public class KeysResponse
{
public string PublicKey { get; set; }
public string PrivateKey { get; set; }
}
}

View File

@@ -6,6 +6,8 @@ namespace Bit.App.Models.Api
{
public string Id { get; set; }
public string FolderId { get; set; }
public string UserId { get; set; }
public string OrganizationId { get; set; }
public string Name { get; set; }
public string Uri { get; set; }
public string Username { get; set; }
@@ -13,8 +15,5 @@ namespace Bit.App.Models.Api
public string Notes { get; set; }
public bool Favorite { get; set; }
public DateTime RevisionDate { get; set; }
// Expandables
public FolderResponse Folder { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
using Bit.App.Enums;
namespace Bit.App.Models.Api
{
public class ProfileOrganizationResponseModel
{
public string Id { get; set; }
public string Name { get; set; }
public string Key { get; set; }
public OrganizationUserStatusType Status { get; set; }
public OrganizationUserType Type { get; set; }
public bool Enabled { get; set; }
}
}

View File

@@ -1,4 +1,6 @@
namespace Bit.App.Models.Api
using System.Collections.Generic;
namespace Bit.App.Models.Api
{
public class ProfileResponse
{
@@ -8,5 +10,9 @@
public string MasterPasswordHint { get; set; }
public string Culture { get; set; }
public bool TwoFactorEnabled { get; set; }
public string Key { get; set; }
public string PrivateKey { get; set; }
public string SecurityStamp { get; set; }
public IEnumerable<ProfileOrganizationResponseModel> Organizations { get; set; }
}
}

View File

@@ -14,5 +14,7 @@ namespace Bit.App.Models.Api
[JsonProperty("token_type")]
public string TokenType { get; set; }
public List<int> TwoFactorProviders { get; set; }
public string PrivateKey { get; set; }
public string Key { get; set; }
}
}

View File

@@ -1,6 +1,7 @@
using System;
using Bit.App.Abstractions;
using XLabs.Ioc;
using Bit.App.Enums;
namespace Bit.App.Models
{
@@ -10,15 +11,66 @@ namespace Bit.App.Models
public CipherString(string encryptedString)
{
if(string.IsNullOrWhiteSpace(encryptedString) || !encryptedString.Contains("|"))
if(string.IsNullOrWhiteSpace(encryptedString))
{
throw new ArgumentException(nameof(encryptedString));
}
var headerPieces = encryptedString.Split('.');
string[] encPieces;
EncryptionType encType;
if(headerPieces.Length == 2 && Enum.TryParse(headerPieces[0], out encType))
{
EncryptionType = encType;
encPieces = headerPieces[1].Split('|');
}
else if(headerPieces.Length == 1)
{
encPieces = headerPieces[0].Split('|');
EncryptionType = encPieces.Length == 3 ? EncryptionType.AesCbc128_HmacSha256_B64 : EncryptionType.AesCbc256_B64;
}
else
{
throw new ArgumentException("Malformed header.");
}
switch(EncryptionType)
{
case EncryptionType.AesCbc256_B64:
if(encPieces.Length != 2)
{
throw new ArgumentException("Malformed encPieces.");
}
InitializationVector = encPieces[0];
CipherText = encPieces[1];
break;
case EncryptionType.AesCbc128_HmacSha256_B64:
case EncryptionType.AesCbc256_HmacSha256_B64:
if(encPieces.Length != 3)
{
throw new ArgumentException("Malformed encPieces.");
}
InitializationVector = encPieces[0];
CipherText = encPieces[1];
Mac = encPieces[2];
break;
case EncryptionType.Rsa2048_OaepSha256_B64:
case EncryptionType.Rsa2048_OaepSha1_B64:
if(encPieces.Length != 1)
{
throw new ArgumentException("Malformed encPieces.");
}
CipherText = encPieces[0];
break;
default:
throw new ArgumentException("Unknown encType.");
}
EncryptedString = encryptedString;
}
public CipherString(string initializationVector, string cipherText, string mac = null)
public CipherString(EncryptionType encryptionType, string initializationVector, string cipherText, string mac = null)
{
if(string.IsNullOrWhiteSpace(initializationVector))
{
@@ -30,40 +82,42 @@ namespace Bit.App.Models
throw new ArgumentNullException(nameof(cipherText));
}
EncryptedString = string.Format("{0}|{1}", initializationVector, cipherText);
EncryptionType = encryptionType;
EncryptedString = string.Format("{0}.{1}|{2}", (byte)EncryptionType, initializationVector, cipherText);
if(!string.IsNullOrWhiteSpace(mac))
{
EncryptedString = string.Format("{0}|{1}", EncryptedString, mac);
}
CipherText = cipherText;
InitializationVector = initializationVector;
Mac = mac;
}
public EncryptionType EncryptionType { get; private set; }
public string EncryptedString { get; private set; }
public string InitializationVector => EncryptedString?.Split('|')[0] ?? null;
public string CipherText => EncryptedString?.Split('|')[1] ?? null;
public string Mac
{
get
{
var pieces = EncryptedString?.Split('|') ?? new string[0];
if(pieces.Length > 2)
{
return pieces[2];
}
return null;
}
}
public byte[] InitializationVectorBytes => Convert.FromBase64String(InitializationVector);
public string InitializationVector { get; private set; }
public string CipherText { get; private set; }
public string Mac { get; private set; }
public byte[] InitializationVectorBytes => string.IsNullOrWhiteSpace(InitializationVector) ?
null : Convert.FromBase64String(InitializationVector);
public byte[] CipherTextBytes => Convert.FromBase64String(CipherText);
public byte[] MacBytes => Mac == null ? null : Convert.FromBase64String(Mac);
public string Decrypt()
public string Decrypt(string orgId = null)
{
if(_decryptedValue == null)
{
var cryptoService = Resolver.Resolve<ICryptoService>();
_decryptedValue = cryptoService.Decrypt(this);
if(!string.IsNullOrWhiteSpace(orgId))
{
_decryptedValue = cryptoService.Decrypt(this, cryptoService.GetOrgKey(orgId));
}
else
{
_decryptedValue = cryptoService.Decrypt(this);
}
}
return _decryptedValue;

View File

@@ -26,21 +26,6 @@ namespace Bit.App.Models.Data
RevisionDateTime = folder.RevisionDate;
}
public FolderData(CipherResponse cipher, string userId)
{
if(cipher.Type != Enums.CipherType.Folder)
{
throw new ArgumentException(nameof(cipher.Type));
}
var data = cipher.Data.ToObject<LoginDataModel>();
Id = cipher.Id;
UserId = userId;
Name = data.Name;
RevisionDateTime = cipher.RevisionDate;
}
[PrimaryKey]
public string Id { get; set; }
[Indexed]

View File

@@ -16,6 +16,7 @@ namespace Bit.App.Models.Data
Id = login.Id;
FolderId = login.FolderId;
UserId = userId;
OrganizationId = login.OrganizationId;
Name = login.Name?.EncryptedString;
Uri = login.Uri?.EncryptedString;
Username = login.Username?.EncryptedString;
@@ -29,6 +30,7 @@ namespace Bit.App.Models.Data
Id = login.Id;
FolderId = login.FolderId;
UserId = userId;
OrganizationId = login.OrganizationId;
Name = login.Name;
Uri = login.Uri;
Username = login.Username;
@@ -50,6 +52,7 @@ namespace Bit.App.Models.Data
Id = cipher.Id;
FolderId = cipher.FolderId;
UserId = userId;
OrganizationId = cipher.OrganizationId;
Name = data.Name;
Uri = data.Uri;
Username = data.Username;
@@ -64,6 +67,7 @@ namespace Bit.App.Models.Data
public string FolderId { get; set; }
[Indexed]
public string UserId { get; set; }
public string OrganizationId { get; set; }
public string Name { get; set; }
public string Uri { get; set; }
public string Username { get; set; }

View File

@@ -11,6 +11,8 @@ namespace Bit.App.Models
public Login(LoginData data)
{
Id = data.Id;
UserId = data.UserId;
OrganizationId = data.OrganizationId;
FolderId = data.FolderId;
Name = data.Name != null ? new CipherString(data.Name) : null;
Uri = data.Uri != null ? new CipherString(data.Uri) : null;
@@ -23,6 +25,8 @@ namespace Bit.App.Models
public Login(LoginResponse response)
{
Id = response.Id;
UserId = response.UserId;
OrganizationId = response.OrganizationId;
FolderId = response.FolderId;
Name = response.Name != null ? new CipherString(response.Name) : null;
Uri = response.Uri != null ? new CipherString(response.Uri) : null;
@@ -32,6 +36,8 @@ namespace Bit.App.Models
Favorite = response.Favorite;
}
public string UserId { get; set; }
public string OrganizationId { get; set; }
public string FolderId { get; set; }
public CipherString Uri { get; set; }
public CipherString Username { get; set; }

View File

@@ -0,0 +1,15 @@
namespace Bit.App.Models
{
public class LoginResult
{
public bool Success { get; set; }
public string ErrorMessage { get; set; }
}
public class FullLoginResult : LoginResult
{
public bool TwoFactorRequired { get; set; }
public SymmetricCryptoKey Key { get; set; }
public string MasterPasswordHash { get; set; }
}
}

View File

@@ -11,14 +11,16 @@ namespace Bit.App.Models.Page
public Login(Models.Login login)
{
Id = login.Id;
Shared = !string.IsNullOrWhiteSpace(login.OrganizationId);
FolderId = login.FolderId;
Name = login.Name?.Decrypt();
Username = login.Username?.Decrypt() ?? " ";
Password = new Lazy<string>(() => login.Password?.Decrypt());
Uri = new Lazy<string>(() => login.Uri?.Decrypt());
Name = login.Name?.Decrypt(login.OrganizationId);
Username = login.Username?.Decrypt(login.OrganizationId) ?? " ";
Password = new Lazy<string>(() => login.Password?.Decrypt(login.OrganizationId));
Uri = new Lazy<string>(() => login.Uri?.Decrypt(login.OrganizationId));
}
public string Id { get; set; }
public bool Shared { get; set; }
public string FolderId { get; set; }
public string Name { get; set; }
public string Username { get; set; }

View File

@@ -13,7 +13,6 @@ namespace Bit.App.Models.Page
private string _uri;
private string _notes;
private bool _revealPassword;
private string _uriHost;
public VaultViewLoginPageModel() { }
@@ -44,7 +43,7 @@ namespace Bit.App.Models.Page
{
get
{
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
var length = Username?.Length ?? 0;
@@ -79,7 +78,7 @@ namespace Bit.App.Models.Page
{
get
{
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
var length = Password?.Length ?? 0;
@@ -110,7 +109,36 @@ namespace Bit.App.Models.Page
}
}
public bool ShowUri => !string.IsNullOrWhiteSpace(Uri);
public bool ShowLaunch => Uri.StartsWith("http://") || Uri.StartsWith("https://");
public bool ShowLaunch
{
get
{
if(!ShowUri)
{
return false;
}
if(Device.RuntimePlatform == Device.Android && !Uri.StartsWith("http") &&
!Uri.StartsWith("androidapp://"))
{
return false;
}
if(Device.RuntimePlatform != Device.Android && !Uri.StartsWith("http"))
{
return false;
}
Uri uri;
if(!System.Uri.TryCreate(Uri, UriKind.Absolute, out uri))
{
return false;
}
return true;
}
}
public string UriHost
{
@@ -121,11 +149,6 @@ namespace Bit.App.Models.Page
return null;
}
if(_uriHost != null)
{
return _uriHost;
}
Uri uri;
if(!System.Uri.TryCreate(Uri, UriKind.Absolute, out uri))
{
@@ -171,11 +194,11 @@ namespace Bit.App.Models.Page
public void Update(Login login)
{
Name = login.Name?.Decrypt();
Username = login.Username?.Decrypt();
Password = login.Password?.Decrypt();
Uri = login.Uri?.Decrypt();
Notes = login.Notes?.Decrypt();
Name = login.Name?.Decrypt(login.OrganizationId);
Username = login.Username?.Decrypt(login.OrganizationId);
Password = login.Password?.Decrypt(login.OrganizationId);
Uri = login.Uri?.Decrypt(login.OrganizationId);
Notes = login.Notes?.Decrypt(login.OrganizationId);
}
}
}

View File

@@ -1,26 +1,37 @@
using System;
using Bit.App.Enums;
using Newtonsoft.Json.Linq;
namespace Bit.App.Models
{
public class PushNotification
public class PushNotificationData
{
public PushType Type { get; set; }
}
public abstract class SyncPushNotification : PushNotification
public class PushNotificationDataPayload : PushNotificationData
{
public string UserId { get; set; }
public string Payload { get; set; }
}
public class SyncCipherPushNotification : SyncPushNotification
public class SyncCipherPushNotification
{
public string Id { get; set; }
public string UserId { get; set; }
public string OrganizationId { get; set; }
public DateTime RevisionDate { get; set; }
}
public class SyncCiphersPushNotification : SyncPushNotification
public class SyncFolderPushNotification
{
public string Id { get; set; }
public string UserId { get; set; }
public DateTime RevisionDate { get; set; }
}
public class SyncUserPushNotification
{
public string UserId { get; set; }
public DateTime Date { get; set; }
}
}

View File

@@ -0,0 +1,62 @@
using Bit.App.Enums;
using System;
using System.Linq;
namespace Bit.App.Models
{
public class SymmetricCryptoKey
{
public SymmetricCryptoKey(byte[] rawBytes, EncryptionType? encType = null)
{
if(rawBytes == null || rawBytes.Length == 0)
{
throw new Exception("Must provide keyBytes.");
}
if(encType == null)
{
if(rawBytes.Length == 32)
{
encType = EncryptionType.AesCbc256_B64;
}
else if(rawBytes.Length == 64)
{
encType = EncryptionType.AesCbc256_HmacSha256_B64;
}
else
{
throw new Exception("Unable to determine encType.");
}
}
EncryptionType = encType.Value;
Key = rawBytes;
if(EncryptionType == EncryptionType.AesCbc256_B64 && Key.Length == 32)
{
EncKey = Key;
MacKey = null;
}
else if(EncryptionType == EncryptionType.AesCbc128_HmacSha256_B64 && Key.Length == 32)
{
EncKey = Key.Take(16).ToArray();
MacKey = Key.Skip(16).Take(16).ToArray();
}
else if(EncryptionType == EncryptionType.AesCbc256_HmacSha256_B64 && Key.Length == 64)
{
EncKey = Key.Take(32).ToArray();
MacKey = Key.Skip(32).Take(32).ToArray();
}
else
{
throw new Exception("Unsupported encType/key length.");
}
}
public byte[] Key { get; set; }
public string B64Key => Convert.ToBase64String(Key);
public byte[] EncKey { get; set; }
public byte[] MacKey { get; set; }
public EncryptionType EncryptionType { get; set; }
}
}

View File

@@ -16,6 +16,7 @@ namespace Bit.App.Pages
private readonly IAuthService _authService;
private readonly IUserDialogs _userDialogs;
private readonly ISettings _settings;
private DateTime? _lastAction;
public HomePage()
: base(updateActivity: false)
@@ -92,11 +93,23 @@ namespace Bit.App.Pages
public async Task LoginAsync()
{
if(_lastAction.LastActionWasRecent())
{
return;
}
_lastAction = DateTime.UtcNow;
await Navigation.PushForDeviceAsync(new LoginPage());
}
public async Task RegisterAsync()
{
if(_lastAction.LastActionWasRecent())
{
return;
}
_lastAction = DateTime.UtcNow;
await Navigation.PushForDeviceAsync(new RegisterPage(this));
}

View File

@@ -20,7 +20,7 @@ namespace Bit.App.Pages
protected override bool OnBackButtonPressed()
{
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
MessagingCenter.Send(Application.Current, "BackgroundApp");
}

View File

@@ -6,6 +6,7 @@ using Xamarin.Forms;
using XLabs.Ioc;
using Plugin.Fingerprint.Abstractions;
using Plugin.Settings.Abstractions;
using Bit.App.Abstractions;
namespace Bit.App.Pages
{
@@ -13,13 +14,16 @@ namespace Bit.App.Pages
{
private readonly IFingerprint _fingerprint;
private readonly ISettings _settings;
private readonly IAppSettingsService _appSettings;
private readonly bool _checkFingerprintImmediately;
private DateTime? _lastAction;
public LockFingerprintPage(bool checkFingerprintImmediately)
{
_checkFingerprintImmediately = checkFingerprintImmediately;
_fingerprint = Resolver.Resolve<IFingerprint>();
_settings = Resolver.Resolve<ISettings>();
_appSettings = Resolver.Resolve<IAppSettingsService>();
Init();
}
@@ -76,10 +80,22 @@ namespace Bit.App.Pages
public async Task CheckFingerprintAsync()
{
var result = await _fingerprint.AuthenticateAsync(AppResources.FingerprintDirection);
if(_lastAction.LastActionWasRecent())
{
return;
}
_lastAction = DateTime.UtcNow;
var fingerprintRequest = new AuthenticationRequestConfiguration(AppResources.FingerprintDirection)
{
AllowAlternativeAuthentication = true,
CancelTitle = AppResources.Cancel,
FallbackTitle = AppResources.LogOut
};
var result = await _fingerprint.AuthenticateAsync(fingerprintRequest);
if(result.Authenticated)
{
_settings.AddOrUpdateValue(Constants.Locked, false);
_appSettings.Locked = false;
await Navigation.PopModalAsync();
}
else if(result.Status == FingerprintAuthenticationResultStatus.FallbackRequested)

View File

@@ -6,20 +6,21 @@ using Xamarin.Forms;
using XLabs.Ioc;
using Bit.App.Controls;
using System.Linq;
using Plugin.Settings.Abstractions;
using Bit.App.Utilities;
namespace Bit.App.Pages
{
public class LockPasswordPage : BaseLockPage
{
private readonly IAuthService _authService;
private readonly ISettings _settings;
private readonly IAppSettingsService _appSettingsService;
private readonly ICryptoService _cryptoService;
private DateTime? _lastAction;
public LockPasswordPage()
{
_authService = Resolver.Resolve<IAuthService>();
_settings = Resolver.Resolve<ISettings>();
_appSettingsService = Resolver.Resolve<IAppSettingsService>();
_cryptoService = Resolver.Resolve<ICryptoService>();
Init();
@@ -29,7 +30,7 @@ namespace Bit.App.Pages
public void Init()
{
var padding = Device.OnPlatform(
var padding = Helpers.OnPlatform(
iOS: new Thickness(15, 20),
Android: new Thickness(15, 8),
WinPhone: new Thickness(15, 20));
@@ -49,7 +50,7 @@ namespace Bit.App.Pages
NoFooter = true,
Root = new TableRoot
{
new TableSection
new TableSection(" ")
{
PasswordCell
}
@@ -74,7 +75,7 @@ namespace Bit.App.Pages
var scrollView = new ScrollView { Content = stackLayout };
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
table.RowHeight = -1;
table.EstimatedRowHeight = 70;
@@ -112,6 +113,12 @@ namespace Bit.App.Pages
protected async Task CheckPasswordAsync()
{
if(_lastAction.LastActionWasRecent())
{
return;
}
_lastAction = DateTime.UtcNow;
if(string.IsNullOrWhiteSpace(PasswordCell.Entry.Text))
{
await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired,
@@ -120,9 +127,9 @@ namespace Bit.App.Pages
}
var key = _cryptoService.MakeKeyFromPassword(PasswordCell.Entry.Text, _authService.Email);
if(key.SequenceEqual(_cryptoService.Key))
if(key.Key.SequenceEqual(_cryptoService.Key.Key))
{
_settings.AddOrUpdateValue(Constants.Locked, false);
_appSettingsService.Locked = false;
await Navigation.PopModalAsync();
}
else

View File

@@ -1,11 +1,8 @@
using System;
using System.Threading.Tasks;
using Acr.UserDialogs;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Xamarin.Forms;
using XLabs.Ioc;
using Plugin.Settings.Abstractions;
using Bit.App.Models.Page;
using Bit.App.Controls;
@@ -14,13 +11,14 @@ namespace Bit.App.Pages
public class LockPinPage : BaseLockPage
{
private readonly IAuthService _authService;
private readonly ISettings _settings;
private readonly IAppSettingsService _appSettingsService;
private TapGestureRecognizer _tgr;
private DateTime? _lastAction;
public LockPinPage()
{
_authService = Resolver.Resolve<IAuthService>();
_settings = Resolver.Resolve<ISettings>();
_appSettingsService = Resolver.Resolve<IAppSettingsService>();
Init();
}
@@ -40,8 +38,8 @@ namespace Bit.App.Pages
};
PinControl = new PinControl();
PinControl.Label.SetBinding<PinPageModel>(Label.TextProperty, s => s.LabelText);
PinControl.Entry.SetBinding<PinPageModel>(Entry.TextProperty, s => s.PIN);
PinControl.Label.SetBinding(Label.TextProperty, nameof(PinPageModel.LabelText));
PinControl.Entry.SetBinding(Entry.TextProperty, nameof(PinPageModel.PIN));
var logoutButton = new ExtendedButton
{
@@ -94,9 +92,15 @@ namespace Bit.App.Pages
protected void PinEntered(object sender, EventArgs args)
{
if(_lastAction.LastActionWasRecent())
{
return;
}
_lastAction = DateTime.UtcNow;
if(Model.PIN == _authService.PIN)
{
_settings.AddOrUpdateValue(Constants.Locked, false);
_appSettingsService.Locked = false;
PinControl.Entry.Unfocus();
Navigation.PopModalAsync();
}

View File

@@ -1,8 +1,6 @@
using System;
using System.Linq;
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.App.Models.Api;
using Bit.App.Resources;
using Xamarin.Forms;
using XLabs.Ioc;
@@ -10,16 +8,13 @@ using Acr.UserDialogs;
using System.Threading.Tasks;
using Plugin.Settings.Abstractions;
using PushNotification.Plugin.Abstractions;
using Bit.App.Utilities;
namespace Bit.App.Pages
{
public class LoginPage : ExtendedContentPage
{
private ICryptoService _cryptoService;
private IAuthService _authService;
private ITokenService _tokenService;
private IDeviceInfoService _deviceInfoService;
private IAppIdService _appIdService;
private IUserDialogs _userDialogs;
private ISyncService _syncService;
private ISettings _settings;
@@ -31,11 +26,7 @@ namespace Bit.App.Pages
: base(updateActivity: false)
{
_email = email;
_cryptoService = Resolver.Resolve<ICryptoService>();
_authService = Resolver.Resolve<IAuthService>();
_tokenService = Resolver.Resolve<ITokenService>();
_deviceInfoService = Resolver.Resolve<IDeviceInfoService>();
_appIdService = Resolver.Resolve<IAppIdService>();
_userDialogs = Resolver.Resolve<IUserDialogs>();
_syncService = Resolver.Resolve<ISyncService>();
_settings = Resolver.Resolve<ISettings>();
@@ -52,7 +43,7 @@ namespace Bit.App.Pages
{
MessagingCenter.Send(Application.Current, "ShowStatusBar", true);
var padding = Device.OnPlatform(
var padding = Helpers.OnPlatform(
iOS: new Thickness(15, 20),
Android: new Thickness(15, 8),
WinPhone: new Thickness(15, 20));
@@ -82,10 +73,11 @@ namespace Bit.App.Pages
HasUnevenRows = true,
EnableSelection = true,
NoFooter = true,
//NoHeader = true,
VerticalOptions = LayoutOptions.Start,
Root = new TableRoot
{
new TableSection()
new TableSection(" ")
{
EmailCell,
PasswordCell
@@ -106,12 +98,12 @@ namespace Bit.App.Pages
var layout = new StackLayout
{
Children = { table, forgotPasswordButton },
Spacing = Device.OnPlatform(iOS: 0, Android: 10, WinPhone: 0)
Spacing = Helpers.OnPlatform(iOS: 0, Android: 10, WinPhone: 0)
};
var scrollView = new ScrollView { Content = layout };
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
table.RowHeight = -1;
table.EstimatedRowHeight = 70;
@@ -188,43 +180,25 @@ namespace Bit.App.Pages
return;
}
var normalizedEmail = EmailCell.Entry.Text.ToLower();
var key = _cryptoService.MakeKeyFromPassword(PasswordCell.Entry.Text, normalizedEmail);
var request = new TokenRequest
{
Email = normalizedEmail,
MasterPasswordHash = _cryptoService.HashPasswordBase64(key, PasswordCell.Entry.Text),
Device = new DeviceRequest(_appIdService, _deviceInfoService)
};
_userDialogs.ShowLoading(AppResources.LoggingIn, MaskType.Black);
var response = await _authService.TokenPostAsync(request);
var result = await _authService.TokenPostAsync(EmailCell.Entry.Text, PasswordCell.Entry.Text);
_userDialogs.HideLoading();
if(!response.Succeeded)
if(!result.Success)
{
await DisplayAlert(AppResources.AnErrorHasOccurred, response.Errors.FirstOrDefault()?.Message, AppResources.Ok);
await DisplayAlert(AppResources.AnErrorHasOccurred, result.ErrorMessage, AppResources.Ok);
return;
}
if(response.Result.TwoFactorProviders != null && response.Result.TwoFactorProviders.Count > 0)
if(result.TwoFactorRequired)
{
_googleAnalyticsService.TrackAppEvent("LoggedIn To Two-step");
await Navigation.PushAsync(new LoginTwoFactorPage(request.Email, request.MasterPasswordHash, key));
await Navigation.PushAsync(new LoginTwoFactorPage(EmailCell.Entry.Text, result.MasterPasswordHash, result.Key));
return;
}
_cryptoService.Key = key;
_tokenService.Token = response.Result.AccessToken;
_tokenService.RefreshToken = response.Result.RefreshToken;
_authService.UserId = _tokenService.TokenUserId;
_authService.Email = _tokenService.TokenEmail;
_settings.AddOrUpdateValue(Constants.LastLoginEmail, _authService.Email);
_googleAnalyticsService.RefreshUserId();
_googleAnalyticsService.TrackAppEvent("LoggedIn");
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
_pushNotification.Register();
}

View File

@@ -1,49 +1,38 @@
using System;
using System.Linq;
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.App.Models.Api;
using Bit.App.Resources;
using Xamarin.Forms;
using XLabs.Ioc;
using Acr.UserDialogs;
using System.Threading.Tasks;
using Plugin.Settings.Abstractions;
using PushNotification.Plugin.Abstractions;
using Bit.App.Models;
using Bit.App.Utilities;
namespace Bit.App.Pages
{
public class LoginTwoFactorPage : ExtendedContentPage
{
private ICryptoService _cryptoService;
private IAuthService _authService;
private ITokenService _tokenService;
private IDeviceInfoService _deviceInfoService;
private IAppIdService _appIdService;
private IUserDialogs _userDialogs;
private ISyncService _syncService;
private ISettings _settings;
private IGoogleAnalyticsService _googleAnalyticsService;
private IPushNotification _pushNotification;
private readonly string _email;
private readonly string _masterPasswordHash;
private readonly byte[] _key;
private readonly SymmetricCryptoKey _key;
public LoginTwoFactorPage(string email, string masterPasswordHash, byte[] key)
public LoginTwoFactorPage(string email, string masterPasswordHash, SymmetricCryptoKey key)
: base(updateActivity: false)
{
_email = email;
_masterPasswordHash = masterPasswordHash;
_key = key;
_cryptoService = Resolver.Resolve<ICryptoService>();
_authService = Resolver.Resolve<IAuthService>();
_tokenService = Resolver.Resolve<ITokenService>();
_deviceInfoService = Resolver.Resolve<IDeviceInfoService>();
_appIdService = Resolver.Resolve<IAppIdService>();
_userDialogs = Resolver.Resolve<IUserDialogs>();
_syncService = Resolver.Resolve<ISyncService>();
_settings = Resolver.Resolve<ISettings>();
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
_pushNotification = Resolver.Resolve<IPushNotification>();
@@ -54,7 +43,7 @@ namespace Bit.App.Pages
private void Init()
{
var padding = Device.OnPlatform(
var padding = Helpers.OnPlatform(
iOS: new Thickness(15, 20),
Android: new Thickness(15, 8),
WinPhone: new Thickness(15, 20));
@@ -75,7 +64,7 @@ namespace Bit.App.Pages
VerticalOptions = LayoutOptions.Start,
Root = new TableRoot
{
new TableSection()
new TableSection(" ")
{
CodeCell
}
@@ -109,7 +98,7 @@ namespace Bit.App.Pages
var scrollView = new ScrollView { Content = layout };
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
table.RowHeight = -1;
table.EstimatedRowHeight = 70;
@@ -117,7 +106,7 @@ namespace Bit.App.Pages
var continueToolbarItem = new ToolbarItem(AppResources.Continue, null, async () =>
{
await LogIn();
await LogInAsync();
}, ToolbarItemOrder.Default, 0);
ToolbarItems.Add(continueToolbarItem);
@@ -142,15 +131,15 @@ namespace Bit.App.Pages
private void Lost2FAApp()
{
Device.OpenUri(new Uri("https://vault.bitwarden.com/#/recover"));
Device.OpenUri(new Uri("https://help.bitwarden.com/article/lost-two-step-device/"));
}
private async void Entry_Completed(object sender, EventArgs e)
{
await LogIn();
await LogInAsync();
}
private async Task LogIn()
private async Task LogInAsync()
{
if(string.IsNullOrWhiteSpace(CodeCell.Entry.Text))
{
@@ -159,34 +148,18 @@ namespace Bit.App.Pages
return;
}
var request = new TokenRequest
{
Email = _email,
MasterPasswordHash = _masterPasswordHash,
Token = CodeCell.Entry.Text.Replace(" ", ""),
Provider = 0, // Authenticator app (only 1 provider for now, so hard coded)
Device = new DeviceRequest(_appIdService, _deviceInfoService)
};
_userDialogs.ShowLoading(AppResources.ValidatingCode, MaskType.Black);
var response = await _authService.TokenPostAsync(request);
var response = await _authService.TokenPostTwoFactorAsync(CodeCell.Entry.Text, _email, _masterPasswordHash, _key);
_userDialogs.HideLoading();
if(!response.Succeeded)
if(!response.Success)
{
await DisplayAlert(AppResources.AnErrorHasOccurred, response.Errors.FirstOrDefault()?.Message, AppResources.Ok);
await DisplayAlert(AppResources.AnErrorHasOccurred, response.ErrorMessage, AppResources.Ok);
return;
}
_cryptoService.Key = _key;
_tokenService.Token = response.Result.AccessToken;
_tokenService.RefreshToken = response.Result.RefreshToken;
_authService.UserId = _tokenService.TokenUserId;
_authService.Email = _tokenService.TokenEmail;
_settings.AddOrUpdateValue(Constants.LastLoginEmail, _authService.Email);
_googleAnalyticsService.RefreshUserId();
_googleAnalyticsService.TrackAppEvent("LoggedIn From Two-step");
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
_pushNotification.Register();
}

View File

@@ -8,6 +8,7 @@ using Xamarin.Forms;
using XLabs.Ioc;
using Acr.UserDialogs;
using System.Threading.Tasks;
using Bit.App.Utilities;
namespace Bit.App.Pages
{
@@ -29,7 +30,7 @@ namespace Bit.App.Pages
private void Init()
{
var padding = Device.OnPlatform(
var padding = Helpers.OnPlatform(
iOS: new Thickness(15, 20),
Android: new Thickness(15, 8),
WinPhone: new Thickness(15, 20));
@@ -49,7 +50,7 @@ namespace Bit.App.Pages
VerticalOptions = LayoutOptions.Start,
Root = new TableRoot
{
new TableSection()
new TableSection(" ")
{
EmailCell
}
@@ -73,7 +74,7 @@ namespace Bit.App.Pages
var scrollView = new ScrollView { Content = layout };
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
table.RowHeight = -1;
table.EstimatedRowHeight = 70;

View File

@@ -8,6 +8,7 @@ using Xamarin.Forms;
using XLabs.Ioc;
using Acr.UserDialogs;
using System.Threading.Tasks;
using Bit.App.Utilities;
namespace Bit.App.Pages
{
@@ -43,7 +44,7 @@ namespace Bit.App.Pages
{
MessagingCenter.Send(Application.Current, "ShowStatusBar", true);
var padding = Device.OnPlatform(
var padding = Helpers.OnPlatform(
iOS: new Thickness(15, 20),
Android: new Thickness(15, 8),
WinPhone: new Thickness(15, 20));
@@ -66,7 +67,7 @@ namespace Bit.App.Pages
{
Root = new TableRoot
{
new TableSection
new TableSection(" ")
{
EmailCell,
PasswordCell
@@ -88,7 +89,7 @@ namespace Bit.App.Pages
NoHeader = true,
Root = new TableRoot
{
new TableSection
new TableSection(" ")
{
ConfirmPasswordCell,
PasswordHintCell
@@ -121,10 +122,10 @@ namespace Bit.App.Pages
await Register();
}, ToolbarItemOrder.Default, 0);
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
table.RowHeight = table2.RowHeight = table2.RowHeight = -1;
table.EstimatedRowHeight = table2.EstimatedRowHeight = table2.EstimatedRowHeight = 70;
table.RowHeight = table2.RowHeight = -1;
table.EstimatedRowHeight = table2.EstimatedRowHeight = 70;
ToolbarItems.Add(new DismissModalToolBarItem(this, AppResources.Cancel, () =>
{
MessagingCenter.Send(Application.Current, "ShowStatusBar", false);
@@ -202,12 +203,14 @@ namespace Bit.App.Pages
var normalizedEmail = EmailCell.Entry.Text.ToLower();
var key = _cryptoService.MakeKeyFromPassword(PasswordCell.Entry.Text, normalizedEmail);
var encKey = _cryptoService.MakeEncKey(key);
var request = new RegisterRequest
{
Email = normalizedEmail,
MasterPasswordHash = _cryptoService.HashPasswordBase64(key, PasswordCell.Entry.Text),
MasterPasswordHint = !string.IsNullOrWhiteSpace(PasswordHintCell.Entry.Text)
? PasswordHintCell.Entry.Text : null
? PasswordHintCell.Entry.Text : null,
Key = encKey.EncryptedString
};
_userDialogs.ShowLoading(AppResources.CreatingAccount, MaskType.Black);

View File

@@ -59,14 +59,14 @@ namespace Bit.App.Pages
HasUnevenRows = true,
Root = new TableRoot
{
new TableSection
new TableSection(" ")
{
CreditsCell
}
}
};
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
table.RowHeight = -1;
table.EstimatedRowHeight = 44;
@@ -78,7 +78,7 @@ namespace Bit.App.Pages
Spacing = 0
};
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
ToolbarItems.Add(new DismissModalToolBarItem(this, AppResources.Cancel));
}

View File

@@ -17,6 +17,7 @@ namespace Bit.App.Pages
private readonly IUserDialogs _userDialogs;
private readonly IConnectivity _connectivity;
private readonly IGoogleAnalyticsService _googleAnalyticsService;
private DateTime? _lastAction;
public SettingsAddFolderPage()
{
@@ -41,14 +42,14 @@ namespace Bit.App.Pages
HasUnevenRows = true,
Root = new TableRoot
{
new TableSection()
new TableSection(" ")
{
NameCell
}
}
};
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
table.RowHeight = -1;
table.EstimatedRowHeight = 70;
@@ -56,6 +57,12 @@ namespace Bit.App.Pages
var saveToolBarItem = new ToolbarItem(AppResources.Save, null, async () =>
{
if(_lastAction.LastActionWasRecent())
{
return;
}
_lastAction = DateTime.UtcNow;
if(!_connectivity.IsConnected)
{
AlertNoConnection();
@@ -81,9 +88,9 @@ namespace Bit.App.Pages
if(saveResult.Succeeded)
{
await Navigation.PopForDeviceAsync();
_userDialogs.Toast(AppResources.FolderCreated);
_googleAnalyticsService.TrackAppEvent("CreatedFolder");
await Navigation.PopForDeviceAsync();
}
else if(saveResult.Errors.Count() > 0)
{
@@ -98,7 +105,7 @@ namespace Bit.App.Pages
Title = AppResources.AddFolder;
Content = table;
ToolbarItems.Add(saveToolBarItem);
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
ToolbarItems.Add(new DismissModalToolBarItem(this, AppResources.Cancel));
}

View File

@@ -2,6 +2,7 @@
using Bit.App.Controls;
using Xamarin.Forms;
using Bit.App.Resources;
using Bit.App.Utilities;
namespace Bit.App.Pages
{
@@ -24,8 +25,12 @@ namespace Bit.App.Pages
{
new TableSection(AppResources.Translations)
{
new CustomViewCell(@"@felixqu - Chinese
new CustomViewCell(@"@felixqu - Chinese Simplified
@thomassth - Chinese Traditional
@Primokorn, @maxlandry - French
@bestHippos - Italian
@SW1FT - Portuguese
@majod - Slovak
@King-Tut-Tut - Swedish
@Igetin - Finnish")
},
@@ -37,7 +42,7 @@ Fingerprint by masterpage.com from the Noun Project")
}
};
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
table.RowHeight = -1;
table.EstimatedRowHeight = 100;
@@ -61,14 +66,14 @@ Fingerprint by masterpage.com from the Noun Project")
var layout = new StackLayout
{
Children = { label },
Padding = Device.OnPlatform(
Padding = Helpers.OnPlatform(
iOS: new Thickness(15, 20),
Android: new Thickness(16, 20),
WinPhone: new Thickness(15, 20)),
BackgroundColor = Color.White
};
if(Device.OS == TargetPlatform.Android)
if(Device.RuntimePlatform == Device.Android)
{
label.TextColor = Color.Black;
}

View File

@@ -17,6 +17,7 @@ namespace Bit.App.Pages
private readonly IUserDialogs _userDialogs;
private readonly IConnectivity _connectivity;
private readonly IGoogleAnalyticsService _googleAnalyticsService;
private DateTime? _lastAction;
public SettingsEditFolderPage(string folderId)
{
@@ -54,18 +55,18 @@ namespace Bit.App.Pages
VerticalOptions = LayoutOptions.Start,
Root = new TableRoot
{
new TableSection
new TableSection(" ")
{
NameCell
},
new TableSection
new TableSection(" ")
{
DeleteCell
}
}
};
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
mainTable.RowHeight = -1;
mainTable.EstimatedRowHeight = 70;
@@ -73,6 +74,12 @@ namespace Bit.App.Pages
var saveToolBarItem = new ToolbarItem(AppResources.Save, null, async () =>
{
if(_lastAction.LastActionWasRecent())
{
return;
}
_lastAction = DateTime.UtcNow;
if(!_connectivity.IsConnected)
{
AlertNoConnection();
@@ -95,9 +102,9 @@ namespace Bit.App.Pages
if(saveResult.Succeeded)
{
await Navigation.PopForDeviceAsync();
_userDialogs.Toast(AppResources.FolderUpdated);
_googleAnalyticsService.TrackAppEvent("EditedFolder");
await Navigation.PopForDeviceAsync();
}
else if(saveResult.Errors.Count() > 0)
{
@@ -112,7 +119,7 @@ namespace Bit.App.Pages
Title = AppResources.EditFolder;
Content = mainTable;
ToolbarItems.Add(saveToolBarItem);
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
ToolbarItems.Add(new DismissModalToolBarItem(this, AppResources.Cancel));
}
@@ -152,15 +159,14 @@ namespace Bit.App.Pages
return;
}
_userDialogs.ShowLoading(AppResources.Deleting, MaskType.Black);
var deleteTask = await _folderService.DeleteAsync(_folderId);
_userDialogs.HideLoading();
if(deleteTask.Succeeded)
{
await Navigation.PopForDeviceAsync();
_userDialogs.Toast(AppResources.FolderDeleted);
await Navigation.PopForDeviceAsync();
}
else if(deleteTask.Errors.Count() > 0)
{

View File

@@ -0,0 +1,287 @@
using System;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Xamarin.Forms;
using XLabs.Ioc;
using Bit.App.Controls;
using Acr.UserDialogs;
using Plugin.Settings.Abstractions;
using Plugin.Fingerprint.Abstractions;
using PushNotification.Plugin.Abstractions;
namespace Bit.App.Pages
{
public class SettingsFeaturesPage : ExtendedContentPage
{
private readonly ISettings _settings;
private readonly IAppSettingsService _appSettings;
private readonly IGoogleAnalyticsService _googleAnalyticsService;
public SettingsFeaturesPage()
{
_settings = Resolver.Resolve<ISettings>();
_appSettings = Resolver.Resolve<IAppSettingsService>();
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
Init();
}
private StackLayout StackLayout { get; set; }
private ExtendedSwitchCell AnalyticsCell { get; set; }
private Label AnalyticsLabel { get; set; }
private ExtendedSwitchCell AutofillPersistNotificationCell { get; set; }
private Label AutofillPersistNotificationLabel { get; set; }
private ExtendedSwitchCell AutofillPasswordFieldCell { get; set; }
private Label AutofillPasswordFieldLabel { get; set; }
private ExtendedSwitchCell AutofillAlwaysCell { get; set; }
private Label AutofillAlwaysLabel { get; set; }
private void Init()
{
AnalyticsCell = new ExtendedSwitchCell
{
Text = AppResources.DisableGA,
On = _settings.GetValueOrDefault(Constants.SettingGaOptOut, false)
};
var analyticsTable = new FormTableView
{
Root = new TableRoot
{
new TableSection(" ")
{
AnalyticsCell
}
}
};
AnalyticsLabel = new Label
{
Text = AppResources.DisbaleGADescription,
LineBreakMode = LineBreakMode.WordWrap,
FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)),
Style = (Style)Application.Current.Resources["text-muted"],
Margin = new Thickness(15, (this.IsLandscape() ? 5 : 0), 15, 25)
};
StackLayout = new StackLayout
{
Children = { analyticsTable, AnalyticsLabel },
Spacing = 0
};
if(Device.RuntimePlatform == Device.Android)
{
AutofillAlwaysCell = new ExtendedSwitchCell
{
Text = AppResources.AutofillAlways,
On = !_appSettings.AutofillPersistNotification && !_appSettings.AutofillPasswordField
};
var autofillAlwaysTable = new FormTableView
{
Root = new TableRoot
{
new TableSection(AppResources.AutofillService)
{
AutofillAlwaysCell
}
}
};
AutofillAlwaysLabel = new FormTableLabel(this)
{
Text = AppResources.AutofillAlwaysDescription
};
AutofillPersistNotificationCell = new ExtendedSwitchCell
{
Text = AppResources.AutofillPersistNotification,
On = _appSettings.AutofillPersistNotification
};
var autofillPersistNotificationTable = new FormTableView
{
NoHeader = true,
Root = new TableRoot
{
new TableSection(" ")
{
AutofillPersistNotificationCell
}
}
};
AutofillPersistNotificationLabel = new FormTableLabel(this)
{
Text = AppResources.AutofillPersistNotificationDescription
};
AutofillPasswordFieldCell = new ExtendedSwitchCell
{
Text = AppResources.AutofillPasswordField,
On = _appSettings.AutofillPasswordField
};
var autofillPasswordFieldTable = new FormTableView
{
NoHeader = true,
Root = new TableRoot
{
new TableSection(" ")
{
AutofillPasswordFieldCell
}
}
};
AutofillPasswordFieldLabel = new FormTableLabel(this)
{
Text = AppResources.AutofillPasswordFieldDescription
};
StackLayout.Children.Add(autofillAlwaysTable);
StackLayout.Children.Add(AutofillAlwaysLabel);
StackLayout.Children.Add(autofillPasswordFieldTable);
StackLayout.Children.Add(AutofillPasswordFieldLabel);
StackLayout.Children.Add(autofillPersistNotificationTable);
StackLayout.Children.Add(AutofillPersistNotificationLabel);
}
var scrollView = new ScrollView
{
Content = StackLayout
};
if(Device.RuntimePlatform == Device.iOS)
{
analyticsTable.RowHeight = -1;
analyticsTable.EstimatedRowHeight = 70;
ToolbarItems.Add(new DismissModalToolBarItem(this, AppResources.Cancel));
}
Title = AppResources.Features;
Content = scrollView;
}
protected override void OnAppearing()
{
base.OnAppearing();
AnalyticsCell.OnChanged += AnalyticsCell_Changed;
StackLayout.LayoutChanged += Layout_LayoutChanged;
if(Device.RuntimePlatform == Device.Android)
{
AutofillAlwaysCell.OnChanged += AutofillAlwaysCell_OnChanged;
AutofillPasswordFieldCell.OnChanged += AutofillPasswordFieldCell_OnChanged;
AutofillPersistNotificationCell.OnChanged += AutofillPersistNotificationCell_OnChanged;
}
}
protected override void OnDisappearing()
{
base.OnDisappearing();
AnalyticsCell.OnChanged -= AnalyticsCell_Changed;
StackLayout.LayoutChanged -= Layout_LayoutChanged;
if(Device.RuntimePlatform == Device.Android)
{
AutofillAlwaysCell.OnChanged -= AutofillAlwaysCell_OnChanged;
AutofillPasswordFieldCell.OnChanged -= AutofillPasswordFieldCell_OnChanged;
AutofillPersistNotificationCell.OnChanged -= AutofillPersistNotificationCell_OnChanged;
}
}
private void Layout_LayoutChanged(object sender, EventArgs e)
{
AnalyticsLabel.WidthRequest = StackLayout.Bounds.Width - AnalyticsLabel.Bounds.Left * 2;
}
private void AnalyticsCell_Changed(object sender, ToggledEventArgs e)
{
var cell = sender as ExtendedSwitchCell;
if(cell == null)
{
return;
}
_settings.AddOrUpdateValue(Constants.SettingGaOptOut, cell.On);
_googleAnalyticsService.SetAppOptOut(cell.On);
}
private void AutofillAlwaysCell_OnChanged(object sender, ToggledEventArgs e)
{
var cell = sender as ExtendedSwitchCell;
if(cell == null)
{
return;
}
if(cell.On)
{
AutofillPasswordFieldCell.On = false;
AutofillPersistNotificationCell.On = false;
_appSettings.AutofillPersistNotification = false;
_appSettings.AutofillPasswordField = false;
}
}
private void AutofillPersistNotificationCell_OnChanged(object sender, ToggledEventArgs e)
{
var cell = sender as ExtendedSwitchCell;
if(cell == null)
{
return;
}
_appSettings.AutofillPersistNotification = cell.On;
if(cell.On)
{
AutofillPasswordFieldCell.On = false;
AutofillAlwaysCell.On = false;
}
}
private void AutofillPasswordFieldCell_OnChanged(object sender, ToggledEventArgs e)
{
var cell = sender as ExtendedSwitchCell;
if(cell == null)
{
return;
}
_appSettings.AutofillPasswordField = cell.On;
if(cell.On)
{
AutofillPersistNotificationCell.On = false;
AutofillAlwaysCell.On = false;
}
}
private class FormTableView : ExtendedTableView
{
public FormTableView()
{
Intent = TableIntent.Settings;
EnableScrolling = false;
HasUnevenRows = true;
EnableSelection = true;
VerticalOptions = LayoutOptions.Start;
NoFooter = true;
}
}
private class FormTableLabel : Label
{
public FormTableLabel(Page page)
{
LineBreakMode = LineBreakMode.WordWrap;
FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label));
Style = (Style)Application.Current.Resources["text-muted"];
Margin = new Thickness(15, (page.IsLandscape() ? 5 : 0), 15, 25);
}
}
}
}

View File

@@ -38,7 +38,7 @@ namespace Bit.App.Pages
{
Root = new TableRoot
{
new TableSection
new TableSection(" ")
{
EmailCell
}
@@ -61,7 +61,7 @@ namespace Bit.App.Pages
NoHeader = true,
Root = new TableRoot
{
new TableSection
new TableSection(" ")
{
WebsiteCell
}
@@ -84,7 +84,7 @@ namespace Bit.App.Pages
NoHeader = true,
Root = new TableRoot
{
new TableSection
new TableSection(" ")
{
BugCell
}
@@ -102,7 +102,7 @@ namespace Bit.App.Pages
Spacing = 0
};
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
ToolbarItems.Add(new DismissModalToolBarItem(this, AppResources.Cancel));
}
@@ -165,7 +165,7 @@ namespace Bit.App.Pages
VerticalOptions = LayoutOptions.Start;
NoFooter = true;
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
RowHeight = -1;
EstimatedRowHeight = 44;

View File

@@ -41,7 +41,7 @@ namespace Bit.App.Pages
ItemTemplate = new DataTemplate(() => new SettingsFolderListViewCell(this))
};
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
ToolbarItems.Add(new DismissModalToolBarItem(this, AppResources.Cancel));
}
@@ -102,7 +102,7 @@ namespace Bit.App.Pages
{
public SettingsFolderListViewCell(SettingsListFoldersPage page)
{
this.SetBinding<SettingsFolderPageModel>(TextProperty, s => s.Name);
this.SetBinding(TextProperty, nameof(SettingsFolderPageModel.Name));
}
}
}

View File

@@ -8,6 +8,7 @@ using Acr.UserDialogs;
using Plugin.Settings.Abstractions;
using Plugin.Fingerprint.Abstractions;
using PushNotification.Plugin.Abstractions;
using Bit.App.Utilities;
namespace Bit.App.Pages
{
@@ -47,10 +48,11 @@ namespace Bit.App.Pages
private ExtendedTextCell AboutCell { get; set; }
private ExtendedTextCell HelpCell { get; set; }
private ExtendedTextCell RateCell { get; set; }
private ExtendedTextCell FeaturesCell { get; set; }
private LongDetailViewCell RateCellLong { get; set; }
private ExtendedTableView Table { get; set; }
private void Init()
private async void Init()
{
PinCell = new ExtendedSwitchCell
{
@@ -78,15 +80,15 @@ namespace Bit.App.Pages
TwoStepCell
};
if(_fingerprint.IsAvailable)
if((await _fingerprint.GetAvailabilityAsync()) == FingerprintAvailability.Available)
{
var fingerprintName = Device.OnPlatform(iOS: AppResources.TouchID, Android: AppResources.Fingerprint,
var fingerprintName = Helpers.OnPlatform(iOS: AppResources.TouchID, Android: AppResources.Fingerprint,
WinPhone: AppResources.Fingerprint);
FingerprintCell = new ExtendedSwitchCell
{
Text = string.Format(AppResources.UnlockWith, fingerprintName),
On = _settings.GetValueOrDefault(Constants.SettingFingerprintUnlockOn, false),
IsEnabled = _fingerprint.IsAvailable
IsEnabled = true
};
securitySecion.Insert(1, FingerprintCell);
}
@@ -137,13 +139,20 @@ namespace Bit.App.Pages
ShowDisclousure = true
};
FeaturesCell = new ExtendedTextCell
{
Text = AppResources.Features,
ShowDisclousure = true
};
var otherSection = new TableSection(AppResources.Other)
{
FeaturesCell,
AboutCell,
HelpCell
};
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
RateCellLong = new LongDetailViewCell(AppResources.RateTheApp, AppResources.RateTheAppDescriptionAppStore);
otherSection.Add(RateCellLong);
@@ -207,8 +216,9 @@ namespace Bit.App.Pages
SyncCell.Tapped += SyncCell_Tapped;
LockCell.Tapped += LockCell_Tapped;
LogOutCell.Tapped += LogOutCell_Tapped;
AboutCell.Tapped += AboutCell_Tapped;
AboutCell.Tapped += AboutCell_Tapped;
HelpCell.Tapped += HelpCell_Tapped;
FeaturesCell.Tapped += FeaturesCell_Tapped;
if(RateCellLong != null)
{
@@ -242,6 +252,7 @@ namespace Bit.App.Pages
LogOutCell.Tapped -= LogOutCell_Tapped;
AboutCell.Tapped -= AboutCell_Tapped;
HelpCell.Tapped -= HelpCell_Tapped;
FeaturesCell.Tapped -= FeaturesCell_Tapped;
if(RateCellLong != null)
{
@@ -263,7 +274,7 @@ namespace Bit.App.Pages
}
_googleAnalyticsService.TrackAppEvent("OpenedSetting", "TwoStep");
Device.OpenUri(new Uri("https://vault.bitwarden.com"));
Device.OpenUri(new Uri("https://help.bitwarden.com/article/setup-two-step-login/"));
}
private async void LockOptionsCell_Tapped(object sender, EventArgs e)
@@ -318,12 +329,12 @@ namespace Bit.App.Pages
private void RateCell_Tapped(object sender, EventArgs e)
{
_googleAnalyticsService.TrackAppEvent("OpenedSetting", "RateApp");
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
Device.OpenUri(new Uri($"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews" +
"?id=1137397744&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software"));
}
else if(Device.OS == TargetPlatform.Android)
else if(Device.RuntimePlatform == Device.Android)
{
MessagingCenter.Send(Application.Current, "RateApp");
}
@@ -359,7 +370,7 @@ namespace Bit.App.Pages
}
_googleAnalyticsService.TrackAppEvent("OpenedSetting", "ChangePassword");
Device.OpenUri(new Uri("https://vault.bitwarden.com"));
Device.OpenUri(new Uri("https://help.bitwarden.com/article/change-your-master-password/"));
}
private async void ChangeEmailCell_Tapped(object sender, EventArgs e)
@@ -371,7 +382,7 @@ namespace Bit.App.Pages
}
_googleAnalyticsService.TrackAppEvent("OpenedSetting", "ChangeEmail");
Device.OpenUri(new Uri("https://vault.bitwarden.com"));
Device.OpenUri(new Uri("https://help.bitwarden.com/article/change-your-email/"));
}
private void FingerprintCell_Changed(object sender, EventArgs e)
@@ -428,6 +439,11 @@ namespace Bit.App.Pages
}
}
private void FeaturesCell_Tapped(object sender, EventArgs e)
{
Navigation.PushModalAsync(new ExtendedNavigationPage(new SettingsFeaturesPage()));
}
private void FoldersCell_Tapped(object sender, EventArgs e)
{
Navigation.PushModalAsync(new ExtendedNavigationPage(new SettingsListFoldersPage()));
@@ -469,7 +485,7 @@ namespace Bit.App.Pages
Intent = TableIntent.Settings;
HasUnevenRows = true;
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
RowHeight = -1;
EstimatedRowHeight = 44;

View File

@@ -42,8 +42,8 @@ namespace Bit.App.Pages
};
PinControl = new PinControl();
PinControl.Label.SetBinding<PinPageModel>(Label.TextProperty, s => s.LabelText);
PinControl.Entry.SetBinding<PinPageModel>(Entry.TextProperty, s => s.PIN);
PinControl.Label.SetBinding(Label.TextProperty, nameof(PinPageModel.LabelText));
PinControl.Entry.SetBinding(Entry.TextProperty, nameof(PinPageModel.PIN));
var stackLayout = new StackLayout
{
@@ -56,7 +56,7 @@ namespace Bit.App.Pages
PinControl.Label.GestureRecognizers.Add(Tgr);
instructionLabel.GestureRecognizers.Add(Tgr);
if(Device.OS == TargetPlatform.iOS)
if(Device.RuntimePlatform == Device.iOS)
{
ToolbarItems.Add(new DismissModalToolBarItem(this, AppResources.Cancel));
}

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