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

Compare commits

...

315 Commits

Author SHA1 Message Date
Kyle Spearrin
cfe84963fa switch to access token, bump version 2020-07-06 13:54:44 -04:00
Kyle Spearrin
d908a599b1 bump version and build fixes 2020-07-06 13:28:19 -04:00
Kyle Spearrin
9b3ddb8da3 bump version via hub release command 2020-07-06 11:56:00 -04:00
Kyle Spearrin
56d994a69d bump version 2020-07-06 11:27:19 -04:00
Kyle Spearrin
67cd17c604 do actions on master push or release event 2020-07-06 11:26:51 -04:00
Kyle Spearrin
f4fb7eb8b7 New Crowdin updates (#1003)
* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Slovak)
2020-07-06 10:39:38 -04:00
Kyle Spearrin
b9021e4331 bump version 2020-07-06 10:36:23 -04:00
Kyle Spearrin
3583836d3e Upload release assets for ios too 2020-07-06 09:55:16 -04:00
Kyle Spearrin
278815119f update workflow for release tasks 2020-07-06 09:50:26 -04:00
Contribucious
1bb678e455 [KnownUsernameField] Compatibility with more browsers (#1000) 2020-07-01 10:31:12 -04:00
Kyle Spearrin
b9e5fc604b New Crowdin updates (#999)
* New translations AppResources.resx (Spanish)

* New translations AppResources.resx (Bulgarian)

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (German)

* New translations AppResources.resx (Polish)

* New translations copy.resx (Polish)

* New translations copy.resx (Polish)
2020-06-29 11:30:02 -04:00
Kyle Spearrin
b65e8c48ce New Crowdin updates (#996)
* New translations AppResources.resx (Romanian)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Ukrainian)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (Vietnamese)

* New translations AppResources.resx (Indonesian)

* New translations AppResources.resx (Thai)

* New translations AppResources.resx (Hindi)

* New translations AppResources.resx (English, United Kingdom)

* New translations AppResources.resx (Sinhala)

* New translations copy.resx (Sinhala)

* New translations copy.resx (Sinhala)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Portuguese)

* New translations copy.resx (Romanian)

* New translations copy.resx (Romanian)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Afrikaans)

* New translations AppResources.resx (Belarusian)

* New translations AppResources.resx (Bulgarian)

* New translations AppResources.resx (German)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (Hebrew)

* New translations AppResources.resx (Korean)

* New translations AppResources.resx (Norwegian Bokmal)
2020-06-26 21:28:06 -04:00
Chad Scharf
e59bc1a08e version bump (#994)
* version bump

* version bump + fix manifest XML formatting
2020-06-25 17:30:14 -04:00
Kyle Spearrin
ff994629de Do not process hostname for regex logins (#987) 2020-06-18 11:58:19 -04:00
Matt Portune
a458b9bc88 Workaround for disabling predictive text in visible password fields (#983)
* Workaround for disabling predictive text in visible password fields

* Fix for non-master branch iOS builds (#984)

* Enable extra workflow steps to allow iOS builds to succeed from non-master branch

* re-enable provisioning profile setup
2020-06-16 11:09:26 -04:00
Matt Portune
2834e25151 Fix for non-master branch iOS builds (#984)
* Enable extra workflow steps to allow iOS builds to succeed from non-master branch

* re-enable provisioning profile setup
2020-06-16 09:55:08 -04:00
Oscar Hinton
c2582fe055 Resolve new ciphers defaulting to ViewPassword = false, causing passwords to be non editable. (#982) 2020-06-15 12:33:36 -04:00
Matt Portune
0980219c8d Add autofill/a11y support for ungoogled.chromium browser (#978) 2020-06-12 15:01:23 -04:00
Oscar Hinton
c5f158f1cf Resolve all passwords being hidden after upgrade (#977) 2020-06-12 09:50:36 -04:00
Kyle Spearrin
62afc023c8 print github ref 2020-06-11 16:26:10 -04:00
Oscar Hinton
52ca84946b Add support for hidden passwords (#920)
* Add support for hidden passwords

* Hide TOTP, hide hidden fields.

* Change _cipher to Cipher.
2020-06-11 16:16:43 -04:00
Kyle Spearrin
e64fb39c32 bump version 2020-06-10 13:49:58 -04:00
Kyle Spearrin
1066598150 fix ipa path 2020-06-10 13:30:08 -04:00
Kyle Spearrin
caed8c2cf0 explore paths 2020-06-10 13:13:03 -04:00
Kyle Spearrin
663be2402d list file system 2020-06-10 12:43:52 -04:00
Kyle Spearrin
6fd24c842f get proper ipa for app store 2020-06-10 12:24:52 -04:00
Kyle Spearrin
302631c4fa set CFBundlePackageType 2020-06-10 11:54:21 -04:00
Kyle Spearrin
8a94623b2b type ios 2020-06-10 11:48:02 -04:00
Kyle Spearrin
016dfdb455 dont archive for app store if not master 2020-06-10 11:33:43 -04:00
Kyle Spearrin
097415385e deploy to app store 2020-06-10 11:32:16 -04:00
Kyle Spearrin
8e6c6e04a3 cleanup export, ipa artifact 2020-06-10 10:53:57 -04:00
Kyle Spearrin
81a30e580e export ipa for app store 2020-06-10 10:02:15 -04:00
Kyle Spearrin
676efe7253 ls archive folder 2020-06-10 09:24:27 -04:00
Kyle Spearrin
39e0e77824 archive on build 2020-06-10 09:03:12 -04:00
Kyle Spearrin
363f5be8ff increment version for ios 2020-06-09 21:23:10 -04:00
Kyle Spearrin
df15fa2f0e move stuff to setup keychain script 2020-06-09 20:47:46 -04:00
Kyle Spearrin
e8c1fbb86f allow codesign to access certs and key 2020-06-09 20:36:44 -04:00
Kyle Spearrin
df986b9ecf timeout settings are for build.keychain 2020-06-09 20:32:23 -04:00
Kyle Spearrin
daabf4bab9 build again 2020-06-09 20:16:03 -04:00
Kyle Spearrin
3095948024 set keychain timeout 2020-06-09 20:14:40 -04:00
Kyle Spearrin
fad289305f add codesign to set-key-partition-list 2020-06-09 17:43:40 -04:00
Kyle Spearrin
97d8f07e0d Merge branch 'master' of github.com:bitwarden/mobile 2020-06-09 17:33:03 -04:00
Kyle Spearrin
dc374c7ce9 try unlocking keychain 2020-06-09 17:32:53 -04:00
Matt Portune
1584475bc3 added hi-res icon for google play (#966)
* added hi-res icon for google play

* moved to proper location
2020-06-09 17:24:19 -04:00
Kyle Spearrin
8f1db25c5c move cloc to its own job 2020-06-09 13:18:22 -04:00
Kyle Spearrin
092b536009 remove debug 2020-06-09 13:14:11 -04:00
Kyle Spearrin
f95bbaa0f7 resolve path of ~ 2020-06-09 13:09:00 -04:00
Kyle Spearrin
bc1f6464d3 change home path to ~ 2020-06-09 13:00:43 -04:00
Kyle Spearrin
b828cd5975 write out homePath 2020-06-09 12:56:53 -04:00
Kyle Spearrin
766b4f7971 create profiles dir path if it doesnt exist 2020-06-09 12:35:39 -04:00
Kyle Spearrin
45e2ffd71e properly escape space 2020-06-09 12:31:18 -04:00
Kyle Spearrin
98757c3f11 use proper params for copy-item 2020-06-09 12:27:40 -04:00
Kyle Spearrin
9aed6d350b setup provisioning profiles 2020-06-09 12:25:09 -04:00
Kyle Spearrin
ca6ce6db32 add mobile profiles 2020-06-09 12:15:06 -04:00
Kyle Spearrin
1c9a6a02af change all seps to / so that it works x-platform 2020-06-09 11:52:23 -04:00
Kyle Spearrin
ba9bafcb5f fix sep character 2020-06-09 11:48:11 -04:00
Kyle Spearrin
0628394122 update path sep character 2020-06-09 11:46:16 -04:00
Kyle Spearrin
99b2cd2ad0 fix pathing 2020-06-09 11:41:03 -04:00
Kyle Spearrin
707a6ecbaa setup ios keychain 2020-06-09 11:37:40 -04:00
Kyle Spearrin
aa2bc40f03 decrypt ios secrets 2020-06-09 11:19:02 -04:00
Kyle Spearrin
5e00e76c4b ios secrets 2020-06-09 11:16:13 -04:00
Kyle Spearrin
0dba992dd4 fix build for app store script path 2020-06-09 10:51:35 -04:00
Kyle Spearrin
b6f61cac9b build play store publisher 2020-06-09 10:47:20 -04:00
Kyle Spearrin
0e41945a8a build ios 2020-06-09 10:47:09 -04:00
Kyle Spearrin
a05e037308 deploy to play store 2020-06-09 10:21:46 -04:00
Kyle Spearrin
f40576c39d print lines of code from CLOC 2020-06-09 10:17:26 -04:00
Kyle Spearrin
9fc810182a print dotnet info 2020-06-09 09:58:28 -04:00
Kyle Spearrin
dd7a52ba08 print version code 2020-06-08 16:10:17 -04:00
Kyle Spearrin
fa6d2a3080 break steps out a bit more 2020-06-08 16:04:04 -04:00
Laurent Tréguier
36efc0c877 Tweak icons to better blend with Android and iOS (#962)
* Add shadow to Android icon

* Add gradient to iOS icon
2020-06-08 15:02:20 -04:00
Kyle Spearrin
e9efcf1b92 rework build scripts 2020-06-08 13:19:44 -04:00
Kyle Spearrin
f4ad1ec8e7 fix secretOutputPath 2020-06-08 13:02:38 -04:00
Kyle Spearrin
0027c21630 more pathing fixes 2020-06-08 13:00:52 -04:00
Kyle Spearrin
6173cab99f fix decryptSecretPath 2020-06-08 12:56:58 -04:00
Kyle Spearrin
80c8097a71 fix paths to ps scripts 2020-06-08 12:55:02 -04:00
Kyle Spearrin
ba3d577125 try shell: pwsh 2020-06-08 12:29:57 -04:00
Kyle Spearrin
8ce4ebb16e powershell Invoke-Expression 2020-06-08 12:25:39 -04:00
Kyle Spearrin
4358ff2338 run powershell commands 2020-06-08 12:21:43 -04:00
Kyle Spearrin
53f9eb083d branches ignore and only on master 2020-06-08 12:17:53 -04:00
Kyle Spearrin
0a3a982cb9 github action android build steps 2020-06-08 12:14:22 -04:00
Kyle Spearrin
c30239b3a8 encrypted GPG files for github actions 2020-06-08 12:14:22 -04:00
Matt Portune
183834689d Fix for accessibility overlay on Android 5 & 6 (#965)
* Fix for accessibility overlay on Android 5 & 6

* changed to skip check outright until API 26
2020-06-08 08:25:24 -04:00
Matt Portune
5da2f3279b biometrics cleanup (#964) 2020-06-08 08:25:13 -04:00
Kyle Spearrin
ec7d87e757 bump version 2020-06-07 11:56:16 -04:00
Kyle Spearrin
4155f69e3c formatting fixes 2020-06-07 11:54:14 -04:00
Matt Portune
473e93ea16 Fix for deadlock in iOS autofill & share extensions (#960) 2020-06-07 00:15:51 -04:00
Kyle Spearrin
fd1941cc3e target iPhone 2020-06-05 15:31:30 -04:00
Kyle Spearrin
dbb51b58db update platforms for ios core builds 2020-06-05 15:30:24 -04:00
Kyle Spearrin
e7d00cfe54 set iphone and sim platforms for ios core 2020-06-05 15:23:15 -04:00
Kyle Spearrin
63453fa962 fix syntax 2020-06-05 14:40:44 -04:00
Kyle Spearrin
689eb7f87b CI updates 2020-06-05 14:39:29 -04:00
Kyle Spearrin
67a4646a50 try out github actions 2020-06-05 13:51:57 -04:00
Kyle Spearrin
a4e0535464 null check all the things (#957) 2020-06-05 13:30:53 -04:00
Kyle Spearrin
61495cd428 bumping ios version to match what was released 2020-06-04 09:48:10 -04:00
Carl
9e7b7415a5 Add support for Bromite's vanilla build of Chromium (#951) 2020-06-02 23:18:30 -04:00
Kyle Spearrin
06a462d48c skip litedb from ios linker 2020-06-02 14:06:53 -04:00
Kyle Spearrin
d3e6e415b9 bump version 2020-06-02 13:08:04 -04:00
Kyle Spearrin
7e633d31a0 sign fdroid builds with a different keystore (#949) 2020-06-02 10:36:16 -04:00
Kyle Spearrin
0fcf006484 Send vaultTimeoutActionChanged event after save (#948) 2020-06-02 09:50:08 -04:00
Kyle Spearrin
48e5b1686f bump version 2020-06-02 09:21:58 -04:00
Kyle Spearrin
608d879c80 New Crowdin translations (#947)
* New translations AppResources.resx (French)

* New translations AppResources.resx (Catalan)

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (German)

* New translations AppResources.resx (Greek)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Swedish)

* New translations AppResources.resx (Turkish)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Estonian)

* New translations AppResources.resx (Hindi)
2020-06-02 09:20:55 -04:00
Kyle Spearrin
66055f1d7c Singleton LiteDatabase (#946)
* update litedb. initialize db as a static singleton instance.

* dont need to dispose anymore
2020-06-02 09:13:57 -04:00
Kyle Spearrin
1120bff34d Don't build the keyboard index for autofill if using logout action (#943)
* Don't build the keyboard index for autofill if using logout action

* trigger index rebuild on vault timeout changed event
2020-06-01 14:46:53 -04:00
Kyle Spearrin
24547e67bf check for empty string on malformed URL (#944)
* treat empty string host as null

* use `string.IsNullOrEmpty`
2020-06-01 14:46:37 -04:00
Kyle Spearrin
f8c7285f56 update ios language metadata. 2020-06-01 09:45:15 -04:00
Kyle Spearrin
82eb6a4568 New Crowdin translations (#942)
* New translations AppResources.resx (Ukrainian)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Italian)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Ukrainian)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Polish)

* New translations AppResources.resx (Italian)

* New translations AppResources.resx (Dutch)
2020-06-01 09:32:26 -04:00
Kyle Spearrin
a3f1f7c78d New Crowdin translations (#941)
* New translations AppResources.resx (Romanian)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Swedish)

* New translations AppResources.resx (Turkish)

* New translations AppResources.resx (Ukrainian)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (Vietnamese)

* New translations AppResources.resx (Indonesian)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Thai)

* New translations AppResources.resx (Croatian)

* New translations AppResources.resx (Estonian)

* New translations AppResources.resx (Hindi)

* New translations copy.resx (Hindi)

* New translations AppResources.resx (English, United Kingdom)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (French)

* New translations AppResources.resx (German)

* New translations AppResources.resx (Spanish)

* New translations AppResources.resx (Afrikaans)

* New translations AppResources.resx (Belarusian)

* New translations AppResources.resx (Bulgarian)

* New translations AppResources.resx (Catalan)

* New translations AppResources.resx (Czech)

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (Greek)

* New translations AppResources.resx (Polish)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (Hebrew)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (Italian)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Korean)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Norwegian Bokmal)
2020-06-01 08:53:20 -04:00
Kyle Spearrin
c377c4a52b bump version 2020-05-30 10:49:51 -04:00
Kyle Spearrin
e5a74cf43c downgrade litedb 2020-05-29 15:26:00 -04:00
Kyle Spearrin
ccf2bf84da allow ios projects to deploy 2020-05-29 15:25:18 -04:00
Kyle Spearrin
fbf3d97d57 check app options ios extension on nfc check (#933) 2020-05-29 15:25:06 -04:00
Chad Scharf
6da0f82ddd Avoid Task.Result usage (#930) 2020-05-29 12:38:26 -04:00
Vincent Salucci
4c3df2e1e1 [Auto Logout] Final review of feature (#932)
* Initial commit of LockService name refactor (#831)

* [Auto-Logout] Update Service layer logic (#835)

* Initial commit of service logic update

* Added default value for action

* Updated ToggleTokensAsync conditional

* Removed unused variables, updated action conditional

* Initial commit: lockOption/lock refactor app layer (#840)

* [Auto-Logout] Settings Refactor - Application Layer Part 2 (#844)

* Initial commit of app layer part 2

* Updated biometrics position

* Reverted resource name refactor

* LockOptions refactor revert

* Updated method casing :: Removed VaultTimeout prefix for timeouts

* Fixed dupe string resource (#854)

* Updated dependency to use VaultTimeoutService (#896)

* [Auto Logout] Xamarin Forms in AutoFill flow (iOS) (#902)

* fix typo in PINRequireMasterPasswordRestart (#900)

* initial commit for xf usage in autofill

* Fixed databinding for hint button

* Updated Two Factor page launch - removed unused imports

* First pass at broadcast/messenger implentation for autofill

* setting theme in extension using theme manager

* extension app resources

* App resources from main app

* fix ref to twoFactorPage

* apply resources to page

* load empty app for sytling in extension

* move ios renderers to ios core

* static ref to resources and GetResourceColor helper

* fix method ref

* move application.current.resources refs to helper

* switch login page alerts to device action dialogs

* run on main thread

* showDialog with device action service

* abstract action sheet to device action service

* add support for yubikey

* add yubikey iimages to extension

* support close button action

* add support to action extension

* remove empty lines

Co-authored-by: Jonas Kittner <54631600+theendlessriver13@users.noreply.github.com>
Co-authored-by: Kyle Spearrin <kyle.spearrin@gmail.com>

* [Auto Logout] Update lock option to be default value (#929)

* Initial commit - make lock action default

* Removed extra whitespace

Co-authored-by: Jonas Kittner <54631600+theendlessriver13@users.noreply.github.com>
Co-authored-by: Kyle Spearrin <kyle.spearrin@gmail.com>
Co-authored-by: Kyle Spearrin <kspearrin@users.noreply.github.com>
2020-05-29 12:26:36 -04:00
Kyle Spearrin
39e10ff01c New Crowdin translations (#931)
* New translations AppResources.resx (Bulgarian)

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (Korean)

* New translations AppResources.resx (Chinese Simplified)
2020-05-29 12:14:35 -04:00
Kyle Spearrin
0b29c6e5a4 Dispost of LiteDatabase instance (#928) 2020-05-28 15:44:27 -04:00
Contribucious
cd3585be58 Early support for future versions of Firefox (+ some forks) (#923)
* Early support for future versions of Firefox

Note: Ideally, a swapping of the two entries of this resource-id value will be done when the time comes; `url_bar_title` becoming Legacy.

* Early support for future versions of some Firefox forks

Note: Ideally, a swapping of the two entries of these resource-id values will be done when the time comes; `url_bar_title` becoming Legacy.
2020-05-26 10:41:27 -04:00
Kyle Spearrin
4e1f91f4d5 New Crowdin translations (#925)
* New translations AppResources.resx (French)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (English, United Kingdom)

* New translations AppResources.resx (Estonian)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Ukrainian)

* New translations AppResources.resx (Swedish)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Polish)

* New translations AppResources.resx (Belarusian)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Italian)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (German)

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (Catalan)

* New translations copy.resx (Belarusian)

* New translations copy.resx (Belarusian)

* New translations AppResources.resx (Norwegian Bokmal)
2020-05-26 10:35:44 -04:00
Kyle Spearrin
272c2e5303 New Crowdin translations (#921)
* New translations AppResources.resx (Romanian)

* New translations AppResources.resx (Vietnamese)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Swedish)

* New translations AppResources.resx (Turkish)

* New translations AppResources.resx (Ukrainian)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Indonesian)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Thai)

* New translations AppResources.resx (Croatian)

* New translations AppResources.resx (Estonian)

* New translations AppResources.resx (Hindi)

* New translations AppResources.resx (English, United Kingdom)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Polish)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (Spanish)

* New translations AppResources.resx (Afrikaans)

* New translations AppResources.resx (Belarusian)

* New translations AppResources.resx (Bulgarian)

* New translations AppResources.resx (Catalan)

* New translations AppResources.resx (Czech)

* New translations AppResources.resx (German)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Greek)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (Hebrew)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (Italian)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Korean)

* New translations AppResources.resx (Norwegian Bokmal)
2020-05-23 09:42:00 -04:00
Contribucious
2a82b09f7b Fix support for Firefox Beta (#918) 2020-05-23 09:38:24 -04:00
Contribucious
fd26492577 Partial revert of "Fix support for Firefox Nightly for Developers" (#919)
My mistake: `org.mozilla.fennec_fdroid` is not an "F-Droid version of Firefox Nightly for Developers". This corrects my error.
2020-05-23 09:38:01 -04:00
Contribucious
528b90b694 Fix support for Firefox Nightly for Developers (#917) 2020-05-23 00:20:51 -04:00
Contribucious
9c7961ff6b Adblock Browser: clarification (#916) 2020-05-22 23:00:27 -04:00
Contribucious
954ed6457c Add support for Privacy Browser (Free/Standard). Closes #407 (#915)
* Add Privacy Browser (Free/Standard) to SupportedBrowsers dict

* Add Privacy Browser (Free/Standard) to CompatBrowsers

* Add Privacy Browser (Free/Standard) compatibility-package entries
2020-05-22 21:06:11 -04:00
Matt Portune
5a8fc2dabc Workaround for pasting into editor within scrollview (#913) 2020-05-20 17:23:59 -04:00
Chad Scharf
ce965ba5e1 Soft delete feature (#890)
* [Soft Delete] Added trash folder to mobile (#856)

* [Soft Delete] Added trash folder to mobile

* [Soft Delete] - Revert send to trash label

Co-authored-by: Chad Scharf <cscharf@users.noreply.github.com>

* [Soft Delete] - Fix for iOS autofill index behavior (#859)

* [Soft Delete] Added trash folder to mobile

* [Soft Delete] - Revert send to trash label

* [Soft Delete] - iOS autofill index behavior fix

Co-authored-by: Chad Scharf <cscharf@users.noreply.github.com>

Co-authored-by: Chad Scharf <cscharf@users.noreply.github.com>
2020-05-20 13:35:20 -04:00
Matt Portune
4b9a036e5e Removal of lifecycle hack for 2FA resume flow (#912) 2020-05-20 09:57:48 -04:00
Matt Portune
4576f378cc lock screen drawing & 2FA entry bugfixes (#910)
* lock screen drawing & 2FA entry bugfixes

* cleanup whitespace
2020-05-20 09:09:28 -04:00
Kyle Spearrin
4c65daa995 New Crowdin translations (#908)
* New translations AppResources.resx (Romanian)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Hebrew)

* New translations copy.resx (Hebrew)

* New translations AppResources.resx (Hungarian)

* New translations copy.resx (Hungarian)

* New translations copy.resx (Hungarian)

* New translations AppResources.resx (Italian)

* New translations copy.resx (Italian)

* New translations copy.resx (Italian)

* New translations AppResources.resx (Korean)

* New translations AppResources.resx (Finnish)

* New translations copy.resx (Korean)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Polish)

* New translations copy.resx (Polish)

* New translations copy.resx (Polish)

* New translations AppResources.resx (Portuguese)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Romanian)

* New translations AppResources.resx (Russian)

* New translations copy.resx (Finnish)

* New translations copy.resx (Portuguese)

* New translations AppResources.resx (Greek)

* New translations AppResources.resx (Bulgarian)

* New translations copy.resx (German)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations copy.resx (Spanish)

* New translations AppResources.resx (Afrikaans)

* New translations copy.resx (Afrikaans)

* New translations copy.resx (Afrikaans)

* New translations AppResources.resx (Belarusian)

* New translations copy.resx (Romanian)

* New translations copy.resx (Bulgarian)

* New translations AppResources.resx (Catalan)

* New translations copy.resx (Catalan)

* New translations copy.resx (Catalan)

* New translations AppResources.resx (Czech)

* New translations copy.resx (Czech)

* New translations AppResources.resx (Danish)

* New translations copy.resx (Danish)

* New translations AppResources.resx (German)

* New translations copy.resx (Bulgarian)

* New translations copy.resx (Persian)

* New translations AppResources.resx (Thai)

* New translations copy.resx (Portuguese, Brazilian)

* New translations copy.resx (Persian)

* New translations AppResources.resx (Persian)

* New translations copy.resx (Indonesian)

* New translations AppResources.resx (Indonesian)

* New translations copy.resx (Thai)

* New translations copy.resx (Estonian)

* New translations copy.resx (Thai)

* New translations AppResources.resx (Croatian)

* New translations copy.resx (Croatian)

* New translations AppResources.resx (Estonian)

* New translations AppResources.resx (Hindi)

* New translations copy.resx (Hindi)

* New translations AppResources.resx (English, United Kingdom)

* New translations copy.resx (English, United Kingdom)

* New translations AppResources.resx (Norwegian Bokmal)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations copy.resx (Portuguese, Brazilian)

* New translations copy.resx (Ukrainian)

* New translations copy.resx (Vietnamese)

* New translations copy.resx (Turkish)

* New translations AppResources.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations copy.resx (Slovak)

* New translations AppResources.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations copy.resx (Swedish)

* New translations AppResources.resx (Turkish)

* New translations copy.resx (Turkish)

* New translations AppResources.resx (Ukrainian)

* New translations copy.resx (Vietnamese)

* New translations copy.resx (Ukrainian)

* New translations AppResources.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Simplified)

* New translations AppResources.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Chinese Traditional)

* New translations AppResources.resx (Vietnamese)

* New translations copy.resx (Norwegian Bokmal)
2020-05-18 15:57:30 -04:00
Jonas Kittner
9159e14dd9 fix typo in PINRequireMasterPasswordRestart (#900) 2020-05-13 13:30:07 -04:00
Matt Portune
da661c229c Lib updates (#889)
* lib updates

* updated libs
2020-05-07 17:03:13 -04:00
Matt Portune
973f09f98a Usability improvements to accessibility settings (#888) 2020-05-07 16:32:42 -04:00
Matt Portune
fef370ad88 Fixed issue with autofill icon image (#887) 2020-05-07 13:54:06 -04:00
Matt Portune
bc46b7172d Updated iOS app icon (#886) 2020-05-07 13:20:07 -04:00
Matt Portune
71f546b467 Updated resources (#884) 2020-05-07 09:03:38 -04:00
Contribucious
a66f66c8ac Add support for AVG Browser (#883)
* Add AVG Browser to SupportedBrowsers dict

* Add AVG Browser to CompatBrowsers

* Add AVG Browser compatibility-package entry
2020-05-06 17:32:53 -04:00
Chad Scharf
c2c6ca22db Fixed spacing on known username field list (#882) 2020-05-06 12:11:58 -04:00
Matt Portune
b29440556a Support for establishing a username field without a password field (#880)
* Support for establishing a username field without a password field

* added aws login
2020-05-06 09:48:34 -04:00
Kyle Spearrin
4104f6f772 Color updates for new branding (#879) 2020-05-05 19:13:18 -04:00
Contribucious
01c56dabdf Add support for LG "Dual Window" mode (Android < 7.0 users) (#878) 2020-05-05 17:38:05 -04:00
Contribucious
fafd8f8ee6 [Browser lists] Global clarification (#876)
* AccessibilityHelpers.cs: global clarification

* AutofillHelpers.cs: global clarification

* autofillservice.xml: global clarification
2020-05-05 10:52:54 -04:00
Contribucious
e2033eee23 Add support for Samsung "Multi Window" mode (Android < 7.0 users) (#877)
... including "Pen Window" support, too.
2020-05-05 10:49:55 -04:00
Contribucious
780761664d Add support for Tor Browser (Alpha) (#872)
* Add Tor Browser (Alpha) to SupportedBrowsers dict

* Add Tor Browser (Alpha) to CompatBrowsers

* Add Tor Browser (Alpha) compatibility-package entry
2020-05-04 09:28:28 -04:00
Contribucious
0cfa737eff Add support for GNU IceCat (#873)
* Add GNU IceCat to SupportedBrowsers dict

* Add GNU IceCat to CompatBrowsers

* Add GNU IceCat compatibility-package entry
2020-05-04 09:27:47 -04:00
Contribucious
6463898c5d Add support for Avast Secure Browser (#874)
* Add Avast Secure Browser to SupportedBrowsers dict

* Add Avast Secure Browser to CompatBrowsers

* Add Avast Secure Browser compatibility-package entry
2020-05-04 09:26:44 -04:00
Contribucious
7d4fffa8b6 Adblock Browser: support for v2+ (stable/beta) + Autofill Framework (#868)
* Adblock Browser (AccessibilityHelpers.SupportedBrowsers): add support for v2+ (stable/beta)

Support for older versions (based on Firefox for Android, instead of Chromium) in the list remains.

* Adblock Browser (AutofillHelpers.CompatBrowsers): add it to the list

* Adblock Browser (autofillservice.xml): add it to the list
2020-05-04 09:25:53 -04:00
Contribucious
bcc415ccb3 Add support for Brave Nightly (also: variants "_dev" and "_default") (#869)
* [Brave] Add all other variants to AccessibilityHelpers.SupportedBrowsers

* [Brave] Add all other variants to AutofillHelpers.CompatBrowsers

* [Brave] Add all other variants to autofillservice.xml
2020-05-04 09:23:09 -04:00
Contribucious
827fead347 AccessibilityHelpers.SupportedBrowsers: add Tor Browser, Firefox Lite and Opera Mini Beta (#866) 2020-05-01 14:00:01 -04:00
Matt Portune
36cdc7dd1c Additional URI parsing fix (#865)
* Additional URI parsing fix

* name cleanup
2020-04-30 16:47:29 -04:00
Contribucious
99dceda8ac [Autofill - Browser lists] Sort the entries alphabetically (#864)
* AccessibilityHelpers.SupportedBrowsers: sort the entries alphabetically

* AutofillHelpers.TrustedBrowsers: sort the entries alphabetically

* AutofillHelpers.CompatBrowsers: sort the entries alphabetically

* autofillservice.xml: sort the entries alphabetically
2020-04-30 11:32:56 -04:00
Matt Portune
9d27f111bf Additional uri parsing intelligence (#861) 2020-04-29 13:09:46 -04:00
Matt Portune
69e0906491 Fixes to uri parsing (#860) 2020-04-29 11:04:50 -04:00
Matt Portune
1d48171fd5 Prevent actionsheet command execution if vault is locked (#857) 2020-04-28 10:25:13 -04:00
Matt Portune
cb0a3e3edf Added additional smarts for establishing permission to draw over other apps (#853) 2020-04-24 14:45:11 -04:00
Chris
2b3915a91f Allow a BiometricPrompt to succeed after an initial failure (#791) (#847)
Previously a call to BiometricPrompt.AuthenticationCallback#OnAuthenticationFailed()
was treated as though an unrecoverable failure had occurred. However this is called on
each failed fingerprint match, for example, and is not a terminal failure. Now these
intermittent failures are ignored and a call to #OnAuthenticationError() is recognised
as an unrecoverable failure instead.
2020-04-23 10:28:43 -04:00
Matt Portune
9a403ba0ed Fixed clipboard not clearing on Android 10 (#851) 2020-04-23 10:01:41 -04:00
Matt Portune
0f35885d1c Set Google publisher upload timeout to 3 minutes (#850) 2020-04-22 13:50:25 -04:00
Matt Portune
e3e07b6bfe Only publish to Google Play if master branch (#848) 2020-04-22 10:54:58 -04:00
Contribucious
84a6d1db71 Add support for Vivaldi Sopranos browser. Closes #673 (#845)
* Add Vivaldi Sopranos to SupportedBrowsers dict

* Add Vivaldi Sopranos to CompatBrowsers

* Add Vivaldi Sopranos compatibility-package entry
2020-04-21 08:49:03 -04:00
Contribucious
8cee50299f Add support for Brave Beta browser (#846)
* Add Brave Beta to SupportedBrowsers dict

* Add Brave Beta to CompatBrowsers

* Add Brave Beta compatibility-package entry
2020-04-21 08:48:29 -04:00
Matt Portune
5a78cbef02 Tracing bundles (#842)
* Output to bundle (play store build only) and enable startup tracing

* Update ci build script to recognize bundle (.aab) for Google Play build

* Added bundle awareness to `appveyor.yml` and `Program.cs`

* Log upload exception

* Restore original apk creation alongside the bundle

* Remove link skip as AOT doesn't support linking
2020-04-17 17:06:37 -04:00
Kyle Spearrin
ae66a781d1 dont use encrypted keystore 2020-04-17 12:55:12 -04:00
Kyle Spearrin
5ae3b66e06 add slash 2020-04-16 17:19:56 -04:00
Kyle Spearrin
c3f1cee5d6 remove salt 2020-04-16 17:12:30 -04:00
Kyle Spearrin
5552c42e37 try secure-file ... again.... 2020-04-16 17:11:25 -04:00
Kyle Spearrin
6f146b888b update secure-file tool 2020-04-16 16:54:32 -04:00
Kyle Spearrin
56c09eae90 add salt to upload key decryption step 2020-04-16 16:43:00 -04:00
Kyle Spearrin
6883864e2d sign with upload keystore 2020-04-16 16:31:52 -04:00
Ash Thompson
15bc395454 Add support for Naver Whale. #600 (#837)
* Add Naver Whale to SupportedBrowsers dict

* Add Naver Whale to CompatBrowsers

* Add Naver Whale compatibility-package
2020-04-16 10:08:16 -04:00
Matt Portune
41997d5fe0 Resync autofill compatibility package list with values from AutofillHelpers (#834) 2020-04-15 13:58:00 -04:00
Jose F. Fernandez
ed259cd130 [FIX] Consider default URI match type on filtering (#830) 2020-04-14 14:56:57 -04:00
Vincent Salucci
1dc027cf49 [Autofill] Apply locked autofill flow to logged out state (#827)
* Initial commit: apply locked auto-fill flow to log out auto-fill

* Alphabetized imports

* Removed unnecessary else conditional

* Fix for talkback slider control (#828)

* Initial commit: apply locked auto-fill flow to log out auto-fill

* Alphabetized imports

* Removed unnecessary else conditional

* Fixed variable init order

Co-authored-by: Matt Portune <59324545+mportune-bw@users.noreply.github.com>
2020-04-13 11:32:23 -05:00
Matt Portune
b2abcda111 Fix for talkback slider control (#828) 2020-04-10 13:30:02 -04:00
Matt Portune
d66eaf8855 Additional accessibility tweaks (#825)
* Additional accessibility tweaks

* Cleanup
2020-04-09 14:57:06 -04:00
Dustin Falgout
78cfd82fdd Remove Unnecessary Toast Message (#808)
Remove unnecessary info message when clicking search during the autofill flow. Fixes #807
2020-04-06 13:19:15 -04:00
Matt Portune
8ad44b405d Add support for Vivaldi snapshot browser (#822)
* Add support for Vivaldi snapshot browser

* Added to autofill helpers CompatBrowsers set as well
2020-04-06 11:43:45 -04:00
Matt Portune
4ce4288f68 Updated dependencies (#820) 2020-04-03 17:17:24 -04:00
Matt Portune
d635555576 Tweaks to autofill tile naming & fixed missing plus icon (#819) 2020-04-03 09:57:04 -04:00
Matt Portune
44999557c0 Tweaks to accessibility autofill overlay (#818) 2020-04-02 19:24:31 -04:00
Matt Portune
5d64bab719 Added handled exception tracking for vault export and bumped CsvHelper to latest (#816)
* Added handled exception tracking for vault export and bumped CsvHelper to latest

* Prevent tracking for FDroid builds

* Remove AppCenter import for FDroid builds
2020-04-02 10:30:21 -04:00
Matt Portune
4d3d8b643a Conversion of HockeyApp to AppCenter for crash reporting (#810)
* Conversion of HockeyApp to AppCenter for crash reporting

* Corrected older-style nuget package definition
2020-04-02 09:02:38 -04:00
Kyle Spearrin
915e8cf072 add litedb to linker skip 2020-03-31 13:23:56 -04:00
Chad Scharf
3c18fd7636 Changed all C# control flow block statements to include space between keyword and open paren (#800) 2020-03-28 09:16:28 -04:00
Matt Portune
6c00ac43fc Added Quick Settings tile for triggering accessibility autofill (#795)
* Added Quick Settings tile for triggering accessibility autofill

* Fix crash when tile attempt to cancel non-visible but non-null overlay

* Persist tile state plus cleanup
2020-03-26 12:15:33 -04:00
Kyle Spearrin
5d9a597d8d switch to stable image 2020-03-26 09:27:24 -04:00
Kyle Spearrin
92930955c3 New Crowdin translations (#794)
* New translations AppResources.resx (Korean)

* New translations copy.resx (Korean)

* New translations copy.resx (Korean)

* New translations AppResources.resx (Polish)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Czech)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (German)

* New translations AppResources.resx (Greek)

* New translations copy.resx (Greek)

* New translations copy.resx (Greek)
2020-03-24 14:49:44 -04:00
Matt Portune
145482ea30 Project lib updates and migration (#789)
* Replace 3rd party FAB lib with our own code

* merged

* merged

* WIP

* WIP

* WIP

* WIP

* Updated LiteDB

* Update ZXing libs to 2.4.1

* Missing semicolon

* rename fab style to btn-fab

* Revert project guid modified by VSmac
2020-03-20 17:54:23 -04:00
Matt Portune
6fdb1e3356 Add support for inverse data matrix QR codes (#787) 2020-03-19 17:07:15 -04:00
Vincent Salucci
55dff81b9f Sanitize Password Length (#783)
* Sanitize Password Length

* Formatting updates

* Else if format

Co-authored-by: Vincent Salucci <vsalucci@bitwarden.com>
2020-03-18 13:43:20 -05:00
Matt Portune
ed37972b99 Updated .gitignore with latest from Xamarin repo and removed Android Resource.designer.cs file (#781)
* Updated .gitignore with latest from Xamarin repo and removed Android Resource.designer.cs file

* didn't mean to leave vscode settings line in
2020-03-18 11:42:20 -04:00
Kyle Spearrin
c6b37307b0 use string.Empty 2020-03-16 21:07:54 -04:00
Kyle Spearrin
5d719ba235 bump version 2020-03-16 21:05:56 -04:00
Vincent Salucci
c19795cce0 Fix bug with policy banner visibility (#777)
Co-authored-by: Vincent Salucci <vsalucci@bitwarden.com>
2020-03-16 11:17:45 -05:00
Vincent Salucci
df8f44d77d Enforce Passphrase Policy (#772)
* Enforce passphrase policy

* Update multi-line conditional formatting

* Updated formatting round 2

Co-authored-by: Vincent Salucci <vsalucci@bitwarden.com>
2020-03-13 23:02:38 -05:00
Matt Portune
a4eff45534 Testing Google publisher fix (#773) 2020-03-13 21:50:00 -04:00
Kyle Spearrin
b2b12be3b0 update google publisher lib 2020-03-13 11:55:03 -04:00
Matt Portune
9c77c53366 Bumped csvhelper to 15.0.1 and added missing error dialog in export failure flow (#771) 2020-03-13 11:02:49 -04:00
Kyle Spearrin
1449f165dd New Crowdin translations (#770)
* New translations AppResources.resx (Belarusian)

* New translations copy.resx (Hebrew)

* New translations copy.resx (Hindi)

* New translations copy.resx (Hungarian)

* New translations copy.resx (Indonesian)

* New translations copy.resx (Italian)

* New translations AppResources.resx (Korean)

* New translations AppResources.resx (Norwegian Bokmal)

* New translations copy.resx (Norwegian Bokmal)

* New translations AppResources.resx (Polish)

* New translations copy.resx (German)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Czech)

* New translations copy.resx (Croatian)

* New translations copy.resx (Danish)

* New translations AppResources.resx (Dutch)

* New translations copy.resx (Finnish)

* New translations copy.resx (Estonian)

* New translations AppResources.resx (Estonian)

* New translations copy.resx (English, United Kingdom)

* New translations AppResources.resx (English, United Kingdom)

* New translations AppResources.resx (Russian)

* New translations copy.resx (Russian)

* New translations AppResources.resx (Spanish)
2020-03-12 21:15:15 -04:00
Matt Portune
94216cf745 Null check policies from SyncResponse before parsing (#767)
* Null check policies from SyncResponse before parsing

* Update src/Core/Services/SyncService.cs

formatting

Co-Authored-By: Kyle Spearrin <kspearrin@users.noreply.github.com>

Co-authored-by: Kyle Spearrin <kspearrin@users.noreply.github.com>
2020-03-12 15:45:34 -04:00
Kyle Spearrin
120e179fb8 Move tab bar colors to styles (#764) 2020-03-11 09:46:48 -04:00
kspearrin
f10114ee17 * iOS.Autofill.csproj: Fix profiles used for builds
* iOS.csproj:
* iOS.Extension.csproj:
2020-03-11 09:33:52 -04:00
Matt Portune
8a059e0fbb Fixed issue where multiple threads were attempting to modify search result list (#761)
* Fixed issue where multiple threads were attempting to modify search result list

* Fixed race condition
2020-03-09 18:43:28 -04:00
Kyle Spearrin
6263788d6a New Crowdin translations (#758)
* New translations AppResources.resx (Estonian)

* New translations AppResources.resx (Greek)

* New translations AppResources.resx (German)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Bulgarian)

* New translations AppResources.resx (Catalan)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Ukrainian)

* New translations AppResources.resx (Turkish)

* New translations AppResources.resx (Swedish)

* New translations AppResources.resx (Spanish)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Norwegian Bokmal)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Polish)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Italian)
2020-03-06 11:30:58 -05:00
Matt Portune
6ffb3136d4 Workaround for older bug in Xamarin.Forms by waiting for app to resume before attempting to set Application.Current.MainPage (#757) 2020-03-05 16:18:04 -05:00
Matt Portune
b65b01fe3d Fixed potential broadcast leak & policy value parsing (#756) 2020-03-05 12:44:01 -05:00
Kyle Spearrin
b9c134654f Allows us to pass in some options to have policies enforced upon. (#755) 2020-03-05 10:11:54 -05:00
Kyle Spearrin
d1a1342587 New Crowdin translations (#754)
* New translations AppResources.resx (Afrikaans)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Italian)

* New translations AppResources.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations copy.resx (Japanese)

* New translations AppResources.resx (Korean)

* New translations AppResources.resx (Norwegian Bokmal)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Polish)

* New translations copy.resx (Polish)

* New translations copy.resx (Polish)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (Romanian)

* New translations AppResources.resx (Russian)

* New translations copy.resx (Russian)

* New translations copy.resx (Russian)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Spanish)

* New translations AppResources.resx (Swedish)

* New translations AppResources.resx (Thai)

* New translations AppResources.resx (Turkish)

* New translations AppResources.resx (Ukrainian)

* New translations AppResources.resx (Indonesian)

* New translations AppResources.resx (Hindi)

* New translations AppResources.resx (Belarusian)

* New translations copy.resx (Dutch)

* New translations copy.resx (Belarusian)

* New translations copy.resx (Belarusian)

* New translations AppResources.resx (Bulgarian)

* New translations AppResources.resx (Catalan)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (Croatian)

* New translations AppResources.resx (Czech)

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (Dutch)

* New translations copy.resx (Dutch)

* New translations AppResources.resx (Hebrew)

* New translations AppResources.resx (English, United Kingdom)

* New translations AppResources.resx (Estonian)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (French)

* New translations copy.resx (French)

* New translations copy.resx (French)

* New translations AppResources.resx (German)

* New translations AppResources.resx (Greek)

* New translations copy.resx (Greek)

* New translations copy.resx (Greek)

* New translations AppResources.resx (Vietnamese)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (German)

* New translations AppResources.resx (Italian)
2020-03-05 09:39:05 -05:00
Kyle Spearrin
1ec2ac472a Update ISSUE_TEMPLATE.md 2020-03-04 09:18:27 -05:00
Clayton
9894322c17 Update ISSUE_TEMPLATE.md (#752)
* Update ISSUE_TEMPLATE.md

Added a uniform template to be used for all issues that are reported.

* Update ISSUE_TEMPLATE.md

Added Beta Version, Build Version, and Videos to the list.
2020-03-04 09:11:38 -05:00
Matt Portune
7edbf4ffc8 Added null check for loading non-existent policies (#753) 2020-03-03 10:53:03 -05:00
Matt Portune
2b1d186611 New Android attachment handling to support saving or opening attachments (#751)
* New Android attachment handling to support saving or opening (when available) attachments

* Simplified options dialog logic & changed error text
2020-03-02 22:14:14 -05:00
Kyle Spearrin
70c49922b0 adjust setting.Value on empty string 2020-02-29 00:44:36 -05:00
Kyle Spearrin
30d6a4d9eb restrictions apparantly cannot have a null default value 2020-02-29 00:42:11 -05:00
Matt Portune
033b2b9ba0 Fixes for html wrapping and encoding (#746) 2020-02-28 15:20:59 -05:00
Matt Portune
25aec80e4c Change switch binding back to using EnforcedPolicyOptions directly (#744) 2020-02-27 23:14:26 -05:00
Matt Portune
cf3d52772d Fixed password color and alignment on iOS password generator (#743) 2020-02-27 21:18:09 -05:00
Matt Portune
f78f303a79 Password generator policy enforcement (#741)
* Password generator policy enforcement

* Formatting

* Changed to simple cast (double unboxing for int64/long)

* Added ui indication of active policy on password generator page and fixed issue with switch enable logic
2020-02-27 19:53:02 -05:00
Kyle Spearrin
02cffa01e2 formatting 2020-02-24 08:58:15 -05:00
Oldřich Jedlička
c2f2a5e52f Make callbacks from UI thread. (#739)
The code expects to be called form UI thread, but it loks like JavaScript
callbacks are not. Switch to UI thread when invoking a callback.

Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
2020-02-24 08:57:36 -05:00
Oldřich Jedlička
d8e19415e3 Fix runtime exception. (#738)
In the callback the processing does not go in the main UI thread, so we
need to switch there. Otherwise on Android this throws
Android.Util.AndroidRuntimeException with detail “Only the original thread
that created a view hierarchy can touch its views”.

Discovered by trying to login with Duo as a two-factor login type.

Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
2020-02-22 21:09:10 -05:00
Matt Portune
387dc2f59c Beginning of policy support (#736)
* Model & service support for policies

* Formatting

* Changes to match existing service and model patterns
2020-02-21 10:23:38 -05:00
Kyle Spearrin
ec3660a86d bitwarden inc 2020-02-18 22:39:35 -05:00
Vincent Salucci
36fb23d467 Add ability to clone personal vault items (#734)
* Add clone ability to personal vault items

* Fixed formatter

* Made requested changes and removed some extra whitespace added by Rider formatter

* Removed formatting on AppResources file

* Fixed casing on UpdateCipherId method

* Update calling method
2020-02-18 15:48:23 -06:00
Matt Portune
33df456cfd In-app vault export support (#729)
* First pass at vault export UI

* Password validation via cryptoService

* Export service framework

* support for constructing json export data

* Support for constructing csv export data

* Cleanup and simplification

* Completion of vault export feature

* Formatting and simplification

* Use dialog instead of toast for invalid master password entry
2020-02-14 16:10:58 -05:00
Kyle Spearrin
7a6fe5ed5f Check blacklist before showing overlay (#730) 2020-02-13 18:33:37 -05:00
Kyle Spearrin
558b10499b var 2020-02-12 09:40:16 -05:00
Kyle Spearrin
1fb3698ba2 support for appconfig settings (#727) 2020-02-10 14:07:06 -05:00
Kyle Spearrin
89f26bbc6b Migrate EnvironmentUrlsKey to pref storage (#725) 2020-02-10 11:32:58 -05:00
Matt Portune
bbd8615cda Align overlay to bottom or top of anchor view depending on available space (bottom by default on initial focus). Establishing visible app height now works much better on Android 5.0+. (#718) 2020-02-05 19:40:44 -05:00
Kyle Spearrin
93132f5d7b version bump 2020-02-03 09:26:29 -05:00
Matt Portune
179514ddf1 Support for multiple browser UriViewIds when extracting a uri (#713)
* Support for multiple browser UriViewIds when extracting a uri

* Simplified
2020-01-29 12:59:17 -05:00
Matt Portune
4b9cff2271 Removal of double-event block for known browsers since it's no longer necessary and was preventing the overlay from working with some browsers (#712) 2020-01-29 09:59:35 -05:00
Kyle Spearrin
c3649a9c80 formatting touchups 2020-01-29 08:46:21 -05:00
Matt Portune
9a66b9003f Made node recycling approach a bit more surgical to appease older versions of Android, and adjusted anchor position offset for older versions of Android (#711) 2020-01-29 07:23:49 -05:00
Matt Portune
34e32403b0 Accessibility fixes (#709)
* Show/hide accessibility overlay on scroll based on several visibility factors

* Improvements to accessibility overlay anchor view tracking

* Increase recursion limit and check for null children when walking the node tree

* Cleanup

* Hide overlay when expanding status (notification) bar

* use .Any() instead of .Count()
2020-01-27 17:36:20 -05:00
Kyle Spearrin
c2e34a8b0e skip linking on OldAndroidSSLSocketFactory 2020-01-27 08:22:46 -05:00
Matt Portune
d0ba4b6702 Accessibility overlay support for username field and scroll tracking (#700)
* Trigger overlay prompt when focusing on username field

* Adjust accessibility overlay position in response to scroll events

* Get username EditText with a single pass of the node tree, plus additional cleanup
2020-01-13 17:14:57 -05:00
Kyle Spearrin
eb16025800 tweaks to accessibility changes 2020-01-10 15:42:50 -05:00
Matt Portune
9f06c9a051 Removal of deprecated Android Accessibility Service options (#698) 2020-01-10 11:34:17 -05:00
Matt Portune
641122b16f UI support in app settings for handling overlay permission requirement in Accessibility Service implementation (#697)
* UI support in app settings for handling overlay permission requirement in Accessibility Service implementation

* Cleaned up shorthand operator with new var
2020-01-10 10:20:19 -05:00
Kyle Spearrin
fbe8708378 cleanup on accessibility service 2020-01-09 13:17:17 -05:00
Matt Portune
21c7b486ff Replaced accessibility service notification with in-line overlay. (#695)
* Replaced accessibility service notification with in-line overlay.  Requires draw-over permission to be enabled (will prompt if not, though this will be enhanced in subsequent commits)

* Updated with requested changes

* Fix for FDroid build
2020-01-09 12:17:16 -05:00
Matt Portune
c33728d418 * AccessibilityHelpers.cs: If (#689)
AccessibilityNodeInfo.FindAccessibilityNodeInfosByViewId(..) returns
  null when the source package is a supported browser, return a null
  uri to prevent overwriting the existing notification's pendingIntent
  uri extra with the brower's packageName.

* AccessibilityService.cs: Added null uri checks as it is now possible
  for AccessibilityHelpers.getUri(..) to return a null uri when the
  Accessibility Service is misbehaving.
2020-01-03 15:19:20 -05:00
ShirokaiLon
a9dacd561c Change password generator to use ColoredPassword (#686)
* Change password generator to use ColoredPassword

* Change ColoredPassword from FormattedString to HTML string for improved performance

* PasswordFormatter fixes

* Correct || to && condition

* Apply password colouring to history pages
2020-01-03 14:56:55 -05:00
Kyle Spearrin
051e15215d bump version 2019-12-26 15:15:15 -05:00
proletarius101
fee8f58c0a Add support for Tor browser (#680) 2019-12-26 07:29:20 -05:00
Kyle Spearrin
cc036cf3c5 Set lock page on resume for android 2019-12-16 09:43:14 -05:00
Kyle Spearrin
4e51517ddb update libs 2019-12-16 09:14:54 -05:00
Kyle Spearrin
3b7454961d New Crowdin translations (#663)
* New translations AppResources.resx (Bulgarian)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (Turkish)

* New translations AppResources.resx (Spanish)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Romanian)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Polish)

* New translations copy.resx (Persian)

* New translations copy.resx (Persian)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Italian)

* New translations AppResources.resx (Hebrew)

* New translations AppResources.resx (Catalan)

* New translations AppResources.resx (German)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (Estonian)

* New translations AppResources.resx (English, United Kingdom)

* New translations copy.resx (Dutch)

* New translations copy.resx (Dutch)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (Czech)

* New translations AppResources.resx (Croatian)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Ukrainian)
2019-12-03 11:35:09 -05:00
Kyle Spearrin
88009e1a63 bump versions 2019-12-03 11:11:30 -05:00
Kyle Spearrin
0afca29b0c still load list if there are any ciphers 2019-11-22 09:51:30 -05:00
Kyle Spearrin
46a75a2944 Revert "try new http client handler for icons"
This reverts commit c099f82752.
2019-11-22 08:24:23 -05:00
Kyle Spearrin
c099f82752 try new http client handler for icons 2019-11-20 17:38:05 -05:00
Kyle Spearrin
1da94bd9c8 completed status on track release 2019-11-20 10:39:15 -05:00
Kyle Spearrin
96ce8165e9 upgrade to v3 publisher apis 2019-11-20 09:57:40 -05:00
Kyle Spearrin
f9b617339d try builds with VS 2019 preview 2019-11-20 08:25:52 -05:00
Kyle Spearrin
58084810f3 don't auto-capitalize password field when viewed 2019-11-20 08:24:08 -05:00
Kyle Spearrin
429e62e6b5 Don't index a "never" uri match. 2019-11-19 07:47:01 -05:00
Kyle Spearrin
b0b7f2afdf Merge branch 'master' of github.com:bitwarden/mobile 2019-11-15 08:55:31 -05:00
Kyle Spearrin
55f160d125 Show exception message from Api errors 2019-11-15 08:55:22 -05:00
Wasim Malik
f6352f5392 Updated Incorrect Check Condition (#648)
Issue is fixed No 361
Now tested on android device extra comma is not appended anymore
2019-11-12 16:28:12 -05:00
Kyle Spearrin
ac7e90c0aa blacklist com.android.settings from autofill 2019-11-08 11:57:07 -05:00
Kyle Spearrin
88fccfd6cd try setting http version 1.0 2019-11-05 09:14:55 -05:00
Kyle Spearrin
5fdf8e6045 set fingerprint lock prop on login 2019-11-05 09:03:48 -05:00
Kyle Spearrin
d9907cdbeb style fixes for xamarin forms 4 2019-10-30 09:30:45 -04:00
Kyle Spearrin
d308f1ca3b update libs and to xamarin forms 4 2019-10-30 08:27:40 -04:00
Kyle Spearrin
0cdc138ba3 dont immediatly prompt biometric when locked 2019-10-30 08:08:15 -04:00
Kyle Spearrin
59d5314164 New Crowdin translations (#639)
* New translations AppResources.resx (Afrikaans)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Italian)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Korean)

* New translations AppResources.resx (Norwegian Bokmal)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Polish)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Romanian)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Spanish)

* New translations AppResources.resx (Swedish)

* New translations AppResources.resx (Thai)

* New translations AppResources.resx (Turkish)

* New translations AppResources.resx (Ukrainian)

* New translations AppResources.resx (Indonesian)

* New translations AppResources.resx (Hindi)

* New translations AppResources.resx (Bulgarian)

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (Catalan)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (Croatian)

* New translations AppResources.resx (Czech)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Hebrew)

* New translations AppResources.resx (English, United Kingdom)

* New translations AppResources.resx (Estonian)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (French)

* New translations AppResources.resx (German)

* New translations AppResources.resx (Vietnamese)
2019-10-23 20:55:51 -04:00
Kyle Spearrin
9c08a37772 UseNativeBiometric only for SDK 29 2019-10-23 11:54:53 -04:00
Kyle Spearrin
b13f5356fe FingerprintManager to detect fingerprints on SDK 28 2019-10-23 11:26:00 -04:00
Kyle Spearrin
5f0c9725ce bump version 2019-10-23 09:27:31 -04:00
Kyle Spearrin
f951fea555 use bio strings for native android bio 2019-10-23 09:24:34 -04:00
Kyle Spearrin
4b989b01e9 use native biomatrics on Android 2019-10-23 09:11:48 -04:00
Kyle Spearrin
aed3ec5474 check authed and unlocked before trying to load 2019-10-22 16:42:05 -04:00
Kyle Spearrin
b354986199 null check apiexception error 2019-10-22 16:37:40 -04:00
Kyle Spearrin
e1983a7d66 fix error when login token expires 2019-10-22 16:30:28 -04:00
Kyle Spearrin
0400d79f43 android 10 and bio permission 2019-10-18 20:41:04 -04:00
Kyle Spearrin
c911484632 upgrade builds to vs 2019 2019-10-18 14:21:07 -04:00
Kyle Spearrin
713e441d2e upgrade to android 10 sdk 2019-10-18 14:19:56 -04:00
Kyle Spearrin
d4b577732b npm audit fix 2019-10-17 08:01:50 -04:00
Kyle Spearrin
440a410d7f skip com.treydev.pns 2019-10-17 08:00:58 -04:00
Kyle Spearrin
37a536b138 catch thrown sync errors from ui 2019-10-15 11:05:56 -04:00
Kyle Spearrin
a0aca3e837 add tf browser 2019-10-11 09:29:33 -04:00
Kyle Spearrin
b58c29111a bump version 2019-10-07 09:42:55 -04:00
Kyle Spearrin
b0f86ea161 focus search bar on appear 2019-10-07 09:23:41 -04:00
Kyle Spearrin
93b59a75a4 remove migration code 2019-10-05 21:36:47 -04:00
Kyle Spearrin
54fcabaea6 shorter delays 2019-10-05 21:31:55 -04:00
Kyle Spearrin
0e966c0304 fix min character assignments for pw gen 2019-10-05 20:39:42 -04:00
Kyle Spearrin
a363712127 use black text for search bar on light theme 2019-10-04 09:32:59 -04:00
Kyle Spearrin
4d8c665917 fix light theme 2019-10-04 09:11:14 -04:00
kspearrin
53bdd92e72 support dark theme default on extensions 2019-09-30 21:40:05 -04:00
Kyle Spearrin
e51aa39ede New Crowdin translations (#614)
* New translations AppResources.resx (Bulgarian)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Korean)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Czech)

* New translations AppResources.resx (Croatian)

* New translations copy.resx (German)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Bulgarian)

* New translations AppResources.resx (Dutch)

* New translations copy.resx (Dutch)

* New translations copy.resx (Dutch)
2019-09-30 21:35:24 -04:00
Kyle Spearrin
33c82129ff bump version 2019-09-30 21:19:20 -04:00
kspearrin
7c5b8c0e9f modal full screen 2019-09-30 21:17:53 -04:00
kspearrin
9dc01bca1c detect dark mode theme. set modal to full screen 2019-09-30 20:38:22 -04:00
Kyle Spearrin
3c7920b84c XF 3.6 update 2019-09-30 20:33:54 -04:00
Kyle Spearrin
b92f3abbaf support dark theme logos 2019-09-30 16:52:20 -04:00
Kyle Spearrin
b6747a63ed stub out support for dark theme by default 2019-09-30 16:41:31 -04:00
Kyle Spearrin
41a44548d2 use TextColor for search bar text color 2019-09-30 16:33:53 -04:00
Kyle Spearrin
a79d3a0d7c uisearchbar tint color 2019-09-30 16:28:07 -04:00
kspearrin
f3a17709e5 get proper hex string from nsdata token 2019-09-30 16:24:35 -04:00
Kyle Spearrin
ced9d33d2e memory stored pinProtectedKey 2019-09-20 16:43:03 -04:00
Kyle Spearrin
23b1373f80 add tag to entitlement 2019-09-20 08:17:37 -04:00
Kyle Spearrin
a80eb1f533 bump version 2019-09-20 07:56:49 -04:00
Kyle Spearrin
f657edf195 add support for vivaldi browser. resolves #599 2019-09-10 17:21:11 -04:00
Kyle Spearrin
d34279dca5 more null checks on add/edit save 2019-09-06 10:03:11 -04:00
Kyle Spearrin
954aa1112a added locale names dictionary 2019-09-06 09:44:25 -04:00
Kyle Spearrin
b35a3339cb device type should be int 2019-09-06 09:44:25 -04:00
Kyle Spearrin
b59433debd New Crowdin translations (#594)
* New translations copy.resx (Bulgarian)

* New translations copy.resx (Bulgarian)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (French)

* New translations AppResources.resx (German)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Norwegian Bokmal)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Romanian)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Turkish)
2019-09-04 13:10:18 -04:00
Kyle Spearrin
fb2db9c652 version bump 2019-09-04 13:02:00 -04:00
Kyle Spearrin
2507f3301b device user agent 2019-09-04 11:52:32 -04:00
Kyle Spearrin
bdad5e4f0a fixes to opening file types on android 2019-08-30 16:43:58 -04:00
Kyle Spearrin
b5dcdc74d7 dark keyboard on dark themes. resolves #588 2019-08-28 20:27:15 -04:00
Kyle Spearrin
e2d1da02d3 more null checking on save 2019-08-27 15:03:33 -04:00
Kyle Spearrin
8253f18312 null check cipher service 2019-08-27 14:55:15 -04:00
kenjirooo
f4a98a2031 Added support for Sleipnir Mobile (https://play.google.com/store/apps/details?id=jp.co.fenrir.android.sleipnir). (#583) 2019-08-22 08:00:03 -04:00
549 changed files with 87054 additions and 90615 deletions

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>ad-hoc</string>
<key>provisioningProfiles</key>
<dict>
<key>com.8bit.bitwarden</key>
<string>Ad hoc: Bitwarden 2020</string>
<key>com.8bit.bitwarden.autofill</key>
<string>Ad hoc: Autofill 2020</string>
<key>com.8bit.bitwarden.find-login-action-extension</key>
<string>Ad hoc: Extension 2020</string>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
<key>provisioningProfiles</key>
<dict>
<key>com.8bit.bitwarden</key>
<string>Dist: Bitwarden 2020</string>
<key>com.8bit.bitwarden.autofill</key>
<string>Dist: Autofill 2020</string>
<key>com.8bit.bitwarden.find-login-action-extension</key>
<string>Dist: Extension 2020</string>
</dict>
</dict>
</plist>

13
.github/scripts/android/build.ps1 vendored Normal file
View File

@@ -0,0 +1,13 @@
param (
[Parameter(Mandatory=$true)]
[string] $configuration
)
$rootPath = $env:GITHUB_WORKSPACE;
$androidPath = $($rootPath + "/src/Android/Android.csproj");
Write-Output "########################################"
Write-Output "##### Build $configuration Configuration"
Write-Output "########################################"
msbuild "$($androidPath)" "/p:Configuration=$configuration"

View File

@@ -0,0 +1,75 @@
$rootPath = $env:GITHUB_WORKSPACE;
$androidPath = $($rootPath + "/src/Android/Android.csproj");
$appPath = $($rootPath + "/src/App/App.csproj");
$androidManifest = $($rootPath + "/src/Android/Properties/AndroidManifest.xml");
Write-Output "########################################"
Write-Output "##### Clean Android and App"
Write-Output "########################################"
msbuild "$($androidPath)" "/t:Clean" "/p:Configuration=FDroid"
msbuild "$($appPath)" "/t:Clean" "/p:Configuration=FDroid"
Write-Output "########################################"
Write-Output "##### Backup project files"
Write-Output "########################################"
Copy-Item $androidManifest $($androidManifest + ".original");
Copy-Item $androidPath $($androidPath + ".original");
Copy-Item $appPath $($appPath + ".original");
Write-Output "########################################"
Write-Output "##### Cleanup Android Manifest"
Write-Output "########################################"
$xml=New-Object XML;
$xml.Load($androidManifest);
$nsAndroid=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
$nsAndroid.AddNamespace("android", "http://schemas.android.com/apk/res/android");
$firebaseReceiver1=$xml.SelectSingleNode(`
"/manifest/application/receiver[@android:name='com.google.firebase.iid.FirebaseInstanceIdInternalReceiver']", `
$nsAndroid);
$firebaseReceiver1.ParentNode.RemoveChild($firebaseReceiver1);
$firebaseReceiver2=$xml.SelectSingleNode(`
"/manifest/application/receiver[@android:name='com.google.firebase.iid.FirebaseInstanceIdReceiver']", `
$nsAndroid);
$firebaseReceiver2.ParentNode.RemoveChild($firebaseReceiver2);
$xml.Save($androidManifest);
Write-Output "########################################"
Write-Output "##### Uninstall from Android.csproj"
Write-Output "########################################"
$xml=New-Object XML;
$xml.Load($androidPath);
$ns=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
$ns.AddNamespace("ns", $xml.DocumentElement.NamespaceURI);
$firebaseNode=$xml.SelectSingleNode(`
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Firebase.Messaging']", $ns);
$firebaseNode.ParentNode.RemoveChild($firebaseNode);
$safetyNetNode=$xml.SelectSingleNode(`
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.GooglePlayServices.SafetyNet']", $ns);
$safetyNetNode.ParentNode.RemoveChild($safetyNetNode);
$xml.Save($androidPath);
Write-Output "########################################"
Write-Output "##### Uninstall from App.csproj"
Write-Output "########################################"
$xml=New-Object XML;
$xml.Load($appPath);
$appCenterNode=$xml.SelectSingleNode("/Project/ItemGroup/PackageReference[@Include='Microsoft.AppCenter.Crashes']");
$appCenterNode.ParentNode.RemoveChild($appCenterNode);
$xml.Save($appPath);

View File

@@ -0,0 +1,24 @@
#!/usr/bin/env bash
cd $GITHUB_WORKSPACE
mkdir dist
cp CNAME ./dist
cd store
chmod 600 fdroid/config.py fdroid/keystore.jks
mkdir -p temp/fdroid
TEMP_DIR="$GITHUB_WORKSPACE/store/temp/fdroid"
cd fdroid
echo "keypass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
echo "keystorepass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
echo "local_copy_dir=\"$TEMP_DIR\"" >>config.py
mkdir -p repo
curl -Lo repo/com.x8bit.bitwarden-fdroid.apk \
https://github.com/bitwarden/mobile/releases/download/$RELEASE_TAG_NAME/com.x8bit.bitwarden-fdroid.apk
fdroid update
fdroid server update
cd ..
rm -rf temp/fdroid/archive
mv -v temp/fdroid ../dist
cd fdroid
cp index.html btn.png qr.png ../../dist/fdroid
cd $GITHUB_WORKSPACE

View File

@@ -0,0 +1,22 @@
$rootPath = $env:GITHUB_WORKSPACE;
$decryptSecretPath = $($rootPath + "/.github/scripts/decrypt-secret.ps1");
$appKeystorePlayFilename = "app_play-keystore.jks";
$appKeystorePlayPath = $($rootPath + "/src/Android/$appKeystorePlayFilename");
$appKeystoreUploadFilename = "app_upload-keystore.jks";
$appKeystoreUploadPath = $($rootPath + "/src/Android/$appKeystoreUploadFilename");
$appKeystoreFdroidFilename = "app_fdroid-keystore.jks";
$appKeystoreFdroidPath = $($rootPath + "/src/Android/$appKeystoreFdroidFilename");
$googleServicesFilename = "google-services.json";
$googleServicesPath = $($rootPath + "/src/Android/$googleServicesFilename");
Invoke-Expression `
"& `"$decryptSecretPath`" -filename $($appKeystorePlayFilename + ".gpg") -output $($appKeystorePlayPath)"
Invoke-Expression `
"& `"$decryptSecretPath`" -filename $($appKeystoreUploadFilename + ".gpg") -output $($appKeystoreUploadPath)"
Invoke-Expression `
"& `"$decryptSecretPath`" -filename $($appKeystoreFdroidFilename + ".gpg") -output $($appKeystoreFdroidPath)"
Invoke-Expression `
"& `"$decryptSecretPath`" -filename $($googleServicesFilename + ".gpg") -output $($googleServicesPath)"
Invoke-Expression "& `"$decryptSecretPath`" -filename play_creds.json.gpg"

View File

@@ -0,0 +1,9 @@
$rootPath = $env:GITHUB_WORKSPACE;
$homePath = Resolve-Path "~" | Select-Object -ExpandProperty Path;
$publisherPath = $($rootPath + "/store/google/Publisher/bin/Release/netcoreapp2.0/Publisher.dll");
$credsPath = $($homePath + "/secrets/play_creds.json");
$aabPath = $($rootPath + "/com.x8bit.bitwarden.aab");
$track = "alpha";
dotnet $publisherPath $credsPath $aabPath $track

View File

@@ -0,0 +1,16 @@
$rootPath = $env:GITHUB_WORKSPACE;
$buildNumber = 3000 + [int]$env:GITHUB_RUN_NUMBER;
Write-Output "########################################"
Write-Output "##### Setting Version Code $buildNumber"
Write-Output "########################################"
$androidManifest = $($rootPath + "/src/Android/Properties/AndroidManifest.xml");
$xml=New-Object XML;
$xml.Load($androidManifest);
$node=$xml.SelectNodes("/manifest");
$node.SetAttribute("android:versionCode", [string]$buildNumber);
$xml.Save($androidManifest);

23
.github/scripts/android/sign-fdroid.ps1 vendored Normal file
View File

@@ -0,0 +1,23 @@
$rootPath = $env:GITHUB_WORKSPACE;
$androidPath = $($rootPath + "/src/Android/Android.csproj");
$appKeystoreFdroidFilename = "app_fdroid-keystore.jks";
Write-Output "########################################"
Write-Output "##### Sign FDroid Configuration"
Write-Output "########################################"
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=FDroid" "/p:AndroidKeyStore=true" `
"/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:FDROID_KEYSTORE_PASSWORD)" `
"/p:AndroidSigningKeyStore=$($appKeystoreFdroidFilename)" `
"/p:AndroidSigningStorePass=$($env:FDROID_KEYSTORE_PASSWORD)" "/v:quiet"
Write-Output "########################################"
Write-Output "##### Copy FDroid apk to project root"
Write-Output "########################################"
$signedApkPath = $($rootPath + "/src/Android/bin/FDroid/com.x8bit.bitwarden-Signed.apk");
$signedApkDestPath = $($rootPath + "/com.x8bit.bitwarden-fdroid.apk");
Copy-Item $signedApkPath $signedApkDestPath

42
.github/scripts/android/sign-play.ps1 vendored Normal file
View File

@@ -0,0 +1,42 @@
$rootPath = $env:GITHUB_WORKSPACE;
$androidPath = $($rootPath + "/src/Android/Android.csproj");
$appKeystorePlayFilename = "app_play-keystore.jks";
$appKeystoreUploadFilename = "app_upload-keystore.jks";
Write-Output "########################################"
Write-Output "##### Sign Google Play Bundle Release Configuration"
Write-Output "########################################"
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" `
"/p:AndroidSigningKeyAlias=upload" "/p:AndroidSigningKeyPass=$($env:UPLOAD_KEYSTORE_PASSWORD)" `
"/p:AndroidSigningKeyStore=$($appKeystoreUploadFilename)" `
"/p:AndroidSigningStorePass=$($env:UPLOAD_KEYSTORE_PASSWORD)" "/p:AndroidPackageFormat=aab" "/v:quiet"
Write-Output "########################################"
Write-Output "##### Copy Google Play Bundle to project root"
Write-Output "########################################"
$signedAabPath = $($rootPath + "/src/Android/bin/Release/com.x8bit.bitwarden-Signed.aab");
$signedAabDestPath = $($rootPath + "/com.x8bit.bitwarden.aab");
Copy-Item $signedAabPath $signedAabDestPath
Write-Output "########################################"
Write-Output "##### Sign APK Release Configuration"
Write-Output "########################################"
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" `
"/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:PLAY_KEYSTORE_PASSWORD)" `
"/p:AndroidSigningKeyStore=$($appKeystorePlayFilename)" `
"/p:AndroidSigningStorePass=$($env:PLAY_KEYSTORE_PASSWORD)" "/v:quiet"
Write-Output "########################################"
Write-Output "##### Copy Release APK to project root"
Write-Output "########################################"
$signedApkPath = $($rootPath + "/src/Android/bin/Release/com.x8bit.bitwarden-Signed.apk");
$signedApkDestPath = $($rootPath + "/com.x8bit.bitwarden.apk");
Copy-Item $signedApkPath $signedApkDestPath

29
.github/scripts/decrypt-secret.ps1 vendored Normal file
View File

@@ -0,0 +1,29 @@
param (
[Parameter(Mandatory=$true)]
[string] $filename,
[string] $output
)
$homePath = Resolve-Path "~" | Select-Object -ExpandProperty Path
$rootPath = $env:GITHUB_WORKSPACE
$secretInputPath = $rootPath + "/.github/secrets"
$input = $secretInputPath + "/" + $filename
$passphrase = $env:DECRYPT_FILE_PASSWORD
$secretOutputPath = $homePath + "/secrets"
if ([string]::IsNullOrEmpty($output)) {
if ($filename.EndsWith(".gpg")) {
$output = $secretOutputPath + "/" + $filename.TrimEnd(".gpg")
} else {
$output = $secretOutputPath + "/" + $filename + ".plaintext"
}
}
if (!(Test-Path -Path $secretOutputPath))
{
New-Item -ItemType Directory -Path $secretOutputPath
}
gpg --quiet --batch --yes --decrypt --passphrase="$passphrase" --output $output $input

29
.github/scripts/ios/build.ps1 vendored Normal file
View File

@@ -0,0 +1,29 @@
param (
[Parameter(Mandatory=$true)]
[string] $configuration,
[string] $platform = "iPhone",
[switch] $archive
)
$rootPath = $env:GITHUB_WORKSPACE;
$iosPath = $($rootPath + "/src/iOS/iOS.csproj");
if ($archive)
{
Write-Output "########################################"
Write-Output "##### Archive $configuration Configuration for $platform Platform"
Write-Output "########################################"
msbuild "$($iosPath)" "/p:Platform=$platform" "/p:Configuration=$configuration" `
"/p:ArchiveOnBuild=true" "/t:`"Build`""
Write-Output "########################################"
Write-Output "##### Done"
Write-Output "########################################"
ls ~/Library/Developer/Xcode/Archives
} else
{
Write-Output "########################################"
Write-Output "##### Build $configuration Configuration for $platform Platform"
Write-Output "########################################"
msbuild "$($iosPath)" "/p:Platform=$platform" "/p:Configuration=$configuration" "/t:`"Build`""
}

View File

@@ -0,0 +1,9 @@
$rootPath = $env:GITHUB_WORKSPACE;
$decryptSecretPath = $($rootPath + "/.github/scripts/decrypt-secret.ps1");
Invoke-Expression "& `"$decryptSecretPath`" -filename bitwarden-mobile-key.p12.gpg"
Invoke-Expression "& `"$decryptSecretPath`" -filename iphone-distribution-cert.p12.gpg"
Invoke-Expression "& `"$decryptSecretPath`" -filename dist_autofill.mobileprovision.gpg"
Invoke-Expression "& `"$decryptSecretPath`" -filename dist_bitwarden.mobileprovision.gpg"
Invoke-Expression "& `"$decryptSecretPath`" -filename dist_extension.mobileprovision.gpg"

View File

@@ -0,0 +1,5 @@
$rootPath = $env:GITHUB_WORKSPACE;
$ipaPath = "$rootPath/bitwarden-export/Bitwarden.ipa"
xcrun altool --upload-app --type ios --file "$ipaPath" `
--username "$env:APPLE_ID_USERNAME" --password "$env:APPLE_ID_PASSWORD"

13
.github/scripts/ios/export-ipa.ps1 vendored Normal file
View File

@@ -0,0 +1,13 @@
param (
[Parameter(Mandatory=$true)]
[string] $method
)
$rootPath = $env:GITHUB_WORKSPACE;
$homePath = Resolve-Path "~" | Select-Object -ExpandProperty Path
$exportOptionsPath = "$rootPath/.github/resources/export-options-$method.plist";
$archivePath = "$homePath/Library/Developer/Xcode/Archives/*/*.xcarchive";
$exportPath = "$rootPath/bitwarden-export";
xcodebuild -exportArchive -archivePath $archivePath -exportPath $exportPath -exportOptionsPlist $exportOptionsPath

View File

@@ -0,0 +1,26 @@
$rootPath = $env:GITHUB_WORKSPACE;
$buildNumber = 100 + [int]$env:GITHUB_RUN_NUMBER;
$bitwardenInfo = $($rootPath + "/src/iOS/Info.plist");
$extensionInfo = $($rootPath + "/src/iOS.Extension/Info.plist");
$autofillInfo = $($rootPath + "/src/iOS.Autofill/Info.plist");
Write-Output "########################################"
Write-Output "##### Setting CFBundleVersion $buildNumber"
Write-Output "########################################"
function Update-Version($file) {
$xml=New-Object XML;
$xml.Load($file);
Select-Xml -xml $xml -XPath "//dict/key[. = 'CFBundleVersion']/following-sibling::string[1]" |
%{
$_.Node.InnerXml = $buildNumber
}
$xml.Save($file);
}
Update-Version $bitwardenInfo
Update-Version $extensionInfo
Update-Version $autofillInfo

13
.github/scripts/ios/setup-keychain.ps1 vendored Normal file
View File

@@ -0,0 +1,13 @@
$homePath = Resolve-Path "~" | Select-Object -ExpandProperty Path;
$secretsPath = $homePath + "/secrets"
$mobileKeyPath = $($secretsPath + "/bitwarden-mobile-key.p12");
$distCertPath = $($secretsPath + "/iphone-distribution-cert.p12");
security create-keychain -p $env:KEYCHAIN_PASSWORD build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p $env:KEYCHAIN_PASSWORD build.keychain
security set-keychain-settings -lut 1200 build.keychain
security import $mobileKeyPath -k build.keychain -P $env:MOBILE_KEY_PASSWORD -T /usr/bin/codesign -T /usr/bin/security
security import $distCertPath -k build.keychain -P $env:DIST_CERT_PASSWORD -T /usr/bin/codesign -T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $env:KEYCHAIN_PASSWORD build.keychain

21
.github/scripts/ios/setup-profiles.ps1 vendored Normal file
View File

@@ -0,0 +1,21 @@
$homePath = Resolve-Path "~" | Select-Object -ExpandProperty Path;
$secretsPath = $homePath + "/secrets"
$autofillProfilePath = $($secretsPath + "/dist_autofill.mobileprovision");
$bitwardenProfilePath = $($secretsPath + "/dist_bitwarden.mobileprovision");
$extensionProfilePath = $($secretsPath + "/dist_extension.mobileprovision");
$profilesDirPath = "~/Library/MobileDevice/Provisioning Profiles"
if (!(Test-Path -Path $profilesDirPath))
{
New-Item -ItemType Directory -Path $profilesDirPath
}
$autofill_uuid = grep UUID -A1 -a $autofillProfilePath | grep -io "[-A-F0-9]\{36\}"
Copy-Item $autofillProfilePath -destination "$profilesDirPath/$autofill_uuid.mobileprovision"
$bitwarden_uuid = grep UUID -A1 -a $bitwardenProfilePath | grep -io "[-A-F0-9]\{36\}"
Copy-Item $bitwardenProfilePath -destination "$profilesDirPath/$bitwarden_uuid.mobileprovision"
$extension_uuid = grep UUID -A1 -a $extensionProfilePath | grep -io "[-A-F0-9]\{36\}"
Copy-Item $extensionProfilePath -destination "$profilesDirPath/$extension_uuid.mobileprovision"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,3 @@
<EFBFBD>
 K<>Y#<23>(<28><><EFBFBD><EFBFBD>EI֐߄T?)l<><6C><EFBFBD><18><><10>"=<3D>|<7C>'e<><0E>m<EFBFBD>/~<7E><>' F<><46>><3E><><EFBFBD><EFBFBD>l<EFBFBD>b<EFBFBD>[<5B>+R<><52>iL<69><4C>"<22><><EFBFBD>~V:<3A><>p<EFBFBD>a<17>ڵel%8t<38><74><EFBFBD>y<<3C>n<EFBFBD><6E><EFBFBD>aU<61>w<16>JD<4A><44><1F><>We<57>9<EFBFBD><39><EFBFBD><EFBFBD><x8d<38>O<EFBFBD>j\<14>ד<EFBFBD><D793><EFBFBD>Vq<56><71>֋
Ǻ<EFBFBD>-<2D>#<23><><11><>]$<24>(<28>l,<2C>Br<42><02><>d<><64><EFBFBD>•a-<2D><><EFBFBD>:<3A><>:<3A><04>9b,!Em<02><19><>Qf<>D<EFBFBD>g<EFBFBD><06><0E>x(P<>ȡ~<7E>͹<EFBFBD><CDB9> <09><>[<06><>!:<3A>;f<><66>

Binary file not shown.

BIN
.github/secrets/play_creds.json.gpg vendored Normal file

Binary file not shown.

Binary file not shown.

299
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,299 @@
name: Build
on:
push:
branches-ignore:
- 'l10n_master'
- 'gh-pages'
release:
types:
- published
jobs:
cloc:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v2
- name: Set up cloc
run: |
sudo apt-get update
sudo apt-get -y install cloc
- name: Print lines of code
run: cloc --vcs git --exclude-dir Resources,store,test,Properties --include-lang C#,XAML
android:
runs-on: windows-latest
steps:
- name: Set up NuGet
uses: nuget/setup-nuget@v1
with:
nuget-version: 'latest'
- name: Set up MSBuild
uses: microsoft/setup-msbuild@v1.0.0
- name: Print environment
run: |
nuget help
msbuild -version
dotnet --info
Write-Output "GitHub ref: $env:GITHUB_REF"
Write-Output "GitHub event: $env:GITHUB_EVENT"
shell: pwsh
env:
GITHUB_REF: ${{ github.ref }}
GITHUB_EVENT: ${{ github.event_name }}
- name: Checkout repo
uses: actions/checkout@v2
- name: Decrypt secrets
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/android/decrypt-secrets.ps1
shell: pwsh
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
- name: Increment version
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/android/increment-version.ps1
shell: pwsh
- name: Restore packages
run: nuget restore
- name: Build Play Store publisher
run: dotnet build ./store/google/Publisher/Publisher.csproj -p:Configuration=Release
- name: Build for Play Store
run: ./.github/scripts/android/build.ps1 -configuration Release
shell: pwsh
- name: Sign for Play Store
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/android/sign-play.ps1
shell: pwsh
env:
PLAY_KEYSTORE_PASSWORD: ${{ secrets.PLAY_KEYSTORE_PASSWORD }}
UPLOAD_KEYSTORE_PASSWORD: ${{ secrets.UPLOAD_KEYSTORE_PASSWORD }}
- name: Upload Play Store .aab artifact
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
uses: actions/upload-artifact@v2
with:
name: com.x8bit.bitwarden.aab
path: ./com.x8bit.bitwarden.aab
- name: Upload Play Store .apk artifact
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
uses: actions/upload-artifact@v2
with:
name: com.x8bit.bitwarden.apk
path: ./com.x8bit.bitwarden.apk
- name: Clean for F-Droid
run: ./.github/scripts/android/clean-fdroid.ps1
shell: pwsh
- name: Restore packages
run: nuget restore
- name: Build for F-Droid
run: ./.github/scripts/android/build.ps1 -configuration FDroid
shell: pwsh
- name: Sign for F-Droid
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/android/sign-fdroid.ps1
shell: pwsh
env:
FDROID_KEYSTORE_PASSWORD: ${{ secrets.FDROID_KEYSTORE_PASSWORD }}
- name: Upload F-Droid .apk artifact
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
uses: actions/upload-artifact@v2
with:
name: com.x8bit.bitwarden-fdroid.apk
path: ./com.x8bit.bitwarden-fdroid.apk
- name: Deploy to Play Store
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/android/deploy-play.ps1
shell: pwsh
- name: Upload release assets
if: github.event_name == 'release'
run: |
hub release edit `
-a ./com.x8bit.bitwarden.aab `
-a ./com.x8bit.bitwarden.apk `
-a ./com.x8bit.bitwarden-fdroid.apk `
-m "Version $($env:RELEASE_TAG_NAME.TrimStart('v'))" `
$env:RELEASE_TAG_NAME
shell: pwsh
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
android-ubuntu:
runs-on: ubuntu-latest
needs: android
steps:
- name: Set up Node
if: github.event_name == 'release'
uses: actions/setup-node@v1
with:
node-version: '10.x'
- name: Set up F-Droid server
if: github.event_name == 'release'
run: |
sudo apt-get -qq update
sudo apt-get -qqy install --no-install-recommends fdroidserver wget
- name: Set up git credentials
if: github.event_name == 'release'
env:
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
run: |
git config --global credential.helper store
echo "https://${ACCESS_TOKEN}:x-oauth-basic@github.com" >> ~/.git-credentials
git config --global user.email "ci@bitwarden.com"
git config --global user.name "Bitwarden CI"
- name: Print environment
if: github.event_name == 'release'
run: |
node --version
npm --version
git --version
Write-Output "GitHub ref: $env:GITHUB_REF"
Write-Output "GitHub event: $env:GITHUB_EVENT"
shell: pwsh
env:
GITHUB_REF: ${{ github.ref }}
GITHUB_EVENT: ${{ github.event_name }}
- name: Checkout repo
if: github.event_name == 'release'
uses: actions/checkout@v2
- name: Install Node dependencies
if: github.event_name == 'release'
run: npm install
- name: Decrypt secrets
if: github.event_name == 'release'
run: |
./.github/scripts/decrypt-secret.ps1 -filename store_fdroid-keystore.jks.gpg `
-output ./store/fdroid/keystore.jks
shell: pwsh
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
- name: Compile for F-Droid Store
if: github.event_name == 'release'
run: |
sudo chmod +x ./.github/scripts/android/compile-fdroid.sh
./.github/scripts/android/compile-fdroid.sh
env:
FDROID_STORE_KEYSTORE_PASSWORD: ${{ secrets.FDROID_STORE_KEYSTORE_PASSWORD }}
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
- name: Deploy to gh-pages
if: github.event_name == 'release'
run: npm run deploy
ios:
runs-on: macos-latest
steps:
- name: Print environment
run: |
nuget help
msbuild -version
dotnet --info
Write-Output "GitHub ref: $env:GITHUB_REF"
Write-Output "GitHub event: $env:GITHUB_EVENT"
shell: pwsh
env:
GITHUB_REF: ${{ github.ref }}
GITHUB_EVENT: ${{ github.event_name }}
- name: Checkout repo
uses: actions/checkout@v2
- name: Decrypt secrets
run: ./.github/scripts/ios/decrypt-secrets.ps1
shell: pwsh
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
- name: Increment version
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/ios/increment-version.ps1
shell: pwsh
- name: Set up keychain
run: ./.github/scripts/ios/setup-keychain.ps1
shell: pwsh
env:
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
MOBILE_KEY_PASSWORD: ${{ secrets.IOS_KEY_PASSWORD }}
DIST_CERT_PASSWORD: ${{ secrets.IOS_DIST_CERT_PASSWORD }}
- name: Set up provisioning profiles
run: ./.github/scripts/ios/setup-profiles.ps1
shell: pwsh
- name: Restore packages
run: nuget restore
- name: Archive Build for App Store
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/ios/build.ps1 -configuration AppStore -platform iPhone -archive
shell: pwsh
- name: Build for App Store
if: github.ref != 'refs/heads/master'
run: ./.github/scripts/ios/build.ps1 -configuration AppStore -platform iPhone
shell: pwsh
- name: Export .ipa for App Store
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/ios/export-ipa.ps1 -method app-store
shell: pwsh
- name: Upload App Store .ipa artifact
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
uses: actions/upload-artifact@v2
with:
name: Bitwarden.ipa
path: ./bitwarden-export/Bitwarden.ipa
- name: Deploy to App Store
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/ios/deploy-app-store.ps1
shell: pwsh
env:
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
- name: Upload release assets
if: github.event_name == 'release'
run: |
hub release edit `
-a ./bitwarden-export/Bitwarden.ipa `
-m "Version $($env:RELEASE_TAG_NAME.TrimStart('v'))" `
$env:RELEASE_TAG_NAME
shell: pwsh
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}

20
.gitignore vendored
View File

@@ -1,15 +1,28 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# Visual Studio (>=2015) project-specific, machine local files
.vs/
# JetBrains Rider project-specific, machine local files
.idea
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# ignore Xamarin.Android Resource.Designer.cs files
**/*.Droid/**/[Rr]esource.[Dd]esigner.cs
**/*.Android/**/[Rr]esource.[Dd]esigner.cs
**/Android/**/[Rr]esource.[Dd]esigner.cs
**/Droid/**/[Rr]esource.[Dd]esigner.cs
# Xamarin Components
Components/
# Build results
[Dd]ebug/
[Dd]ebugPublic/
@@ -22,9 +35,6 @@ bld/
[Bb]in/
[Oo]bj/
# Visual Studo 2015 cache/options directory
.vs/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

View File

@@ -1,5 +1,53 @@
<!--
<!-- Comment:
Please do not submit feature requests. The [Community Forums][1] has a
section for submitting, voting for, and discussing product feature requests.
[1]: https://community.bitwarden.com
-->
## Describe the Bug
<!-- Comment:
A clear and concise description of what the bug is.
-->
## Steps To Reproduce
<!-- Comment:
How can we reproduce the behavior:
-->
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. Click on '...'
## Expected Result
<!-- Comment:
A clear and concise description of what you expected to happen.
-->
## Actual Result
<!-- Comment:
A clear and concise description of what is happening.
-->
## Screenshots or Videos
<!-- Comment:
If applicable, add screenshots and/or a short video to help explain your problem.
-->
## Environment
- Device: [e.g. iPhone6]
- Operating system: [e.g. iOS 8.1]
- Build Version (go to "Settings" → "About" in the app): [e.g. 2.3.0 (2221)]
- Is this a Beta release? [Y/N]
## Additional Context
<!-- Comment:
Add any other context about the problem here.
-->

View File

@@ -1,5 +1,5 @@
image:
- Visual Studio 2017
- Visual Studio 2019
- Ubuntu1804
branches:
@@ -55,7 +55,7 @@ before_build:
- ps: |
if($isWindows) {
nuget restore
if($env:KEYSTORE_DEC_SECRET -or $env:GOOGLE_SERVICES_DEC_SECRET -or $env:PLAY_DEC_SECRET) {
if($env:UPLOAD_KEYSTORE_DEC_SECRET -or$env:KEYSTORE_DEC_SECRET -or $env:GOOGLE_SERVICES_DEC_SECRET -or $env:PLAY_DEC_SECRET) {
nuget install secure-file -ExcludeVersion
}
if($env:GOOGLE_SERVICES_DEC_SECRET) {
@@ -98,6 +98,7 @@ build_script:
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
.\src\Android\ci-build-apks.ps1
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
Push-AppveyorArtifact .\com.x8bit.bitwarden.aab
Push-AppveyorArtifact .\com.x8bit.bitwarden.apk
Push-AppveyorArtifact .\com.x8bit.bitwarden-fdroid.apk
}
@@ -109,12 +110,12 @@ on_success:
npm run deploy
fi
- ps: |
if($isWindows -and $env:PLAY_DEC_SECRET) {
if($isWindows -and $env:PLAY_DEC_SECRET -and $env:APPVEYOR_REPO_BRANCH -eq 'master') {
secure-file\tools\secure-file -decrypt store\google\Publisher\play_creds.json.enc -secret $env:PLAY_DEC_SECRET
cd store\google\Publisher\bin\Release\netcoreapp2.0
dotnet Publisher.dll `
$env:APPVEYOR_BUILD_FOLDER\store\google\Publisher\play_creds.json `
$env:APPVEYOR_BUILD_FOLDER\com.x8bit.bitwarden.apk `
$env:APPVEYOR_BUILD_FOLDER\com.x8bit.bitwarden.aab `
alpha
cd $env:APPVEYOR_BUILD_FOLDER
}

View File

@@ -24,6 +24,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig
.gitignore = .gitignore
appveyor.yml = appveyor.yml
.github\workflows\build.yml = .github\workflows\build.yml
CONTRIBUTING.md = CONTRIBUTING.md
crowdin.yml = crowdin.yml
README.md = README.md
@@ -238,22 +239,22 @@ Global
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|Any CPU.ActiveCfg = AppStore|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|Any CPU.Build.0 = AppStore|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.ActiveCfg = AppStore|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.Build.0 = AppStore|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.Build.0 = AppStore|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.Build.0 = AppStore|iPhone
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.Build.0 = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.ActiveCfg = Debug|iPhone
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.Build.0 = Debug|iPhone
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.Build.0 = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU
@@ -279,6 +280,7 @@ Global
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.ActiveCfg = Debug|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.Build.0 = Debug|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.Deploy.0 = Debug|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|Any CPU.ActiveCfg = FDroid|iPhone
@@ -306,6 +308,7 @@ Global
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.ActiveCfg = Debug|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Build.0 = Debug|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Deploy.0 = Debug|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.ActiveCfg = Release|iPhone
@@ -334,6 +337,7 @@ Global
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.ActiveCfg = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.Build.0 = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.Deploy.0 = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|Any CPU.ActiveCfg = Release|iPhone

8
package-lock.json generated
View File

@@ -1,5 +1,5 @@
{
"name": "bitwarden-fdroid",
"name": "bitwarden-mobile",
"version": "0.0.0",
"lockfileVersion": 1,
"requires": true,
@@ -196,9 +196,9 @@
}
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
"dev": true
},
"minimatch": {

View File

@@ -4,6 +4,8 @@ using Android.OS;
using Android.Runtime;
using Android.Views;
using System;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
namespace Bit.Droid.Accessibility
{
@@ -16,13 +18,13 @@ namespace Bit.Droid.Accessibility
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
LaunchMainActivity(Intent, 932473);
HandleIntent(Intent, 932473);
}
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
LaunchMainActivity(intent, 489729);
HandleIntent(intent, 489729);
}
protected override void OnDestroy()
@@ -33,7 +35,7 @@ namespace Bit.Droid.Accessibility
protected override void OnResume()
{
base.OnResume();
if(!Intent.HasExtra("uri"))
if (!Intent.HasExtra("uri"))
{
Finish();
return;
@@ -44,7 +46,7 @@ namespace Bit.Droid.Accessibility
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if(data == null)
if (data == null)
{
AccessibilityHelpers.LastCredentials = null;
}
@@ -52,7 +54,7 @@ namespace Bit.Droid.Accessibility
{
try
{
if(data.GetStringExtra("canceled") != null)
if (data.GetStringExtra("canceled") != null)
{
AccessibilityHelpers.LastCredentials = null;
}
@@ -78,23 +80,38 @@ namespace Bit.Droid.Accessibility
Finish();
}
private void HandleIntent(Intent callingIntent, int requestCode)
{
if (callingIntent?.GetBooleanExtra("autofillTileClicked", false) ?? false)
{
Intent.RemoveExtra("autofillTileClicked");
var messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
messagingService.Send("OnAutofillTileClick");
Finish();
}
else
{
LaunchMainActivity(callingIntent, requestCode);
}
}
private void LaunchMainActivity(Intent callingIntent, int requestCode)
{
_lastQueriedUri = callingIntent?.GetStringExtra("uri");
if(_lastQueriedUri == null)
if (_lastQueriedUri == null)
{
Finish();
return;
}
var now = DateTime.UtcNow;
if(_lastLaunch.HasValue && (now - _lastLaunch.Value) <= TimeSpan.FromSeconds(2))
if (_lastLaunch.HasValue && (now - _lastLaunch.Value) <= TimeSpan.FromSeconds(2))
{
return;
}
_lastLaunch = now;
var intent = new Intent(this, typeof(MainActivity));
if(!callingIntent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory))
if (!callingIntent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory))
{
intent.PutExtra("uri", _lastQueriedUri);
}

View File

@@ -1,8 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Android.Provider;
using Android.Runtime;
using Android.Views;
using Android.Views.Accessibility;
using Android.Widget;
using Bit.App.Resources;
using Bit.Core;
namespace Bit.Droid.Accessibility
@@ -12,57 +20,93 @@ namespace Bit.Droid.Accessibility
public static Credentials LastCredentials = null;
public static string SystemUiPackage = "com.android.systemui";
public static string BitwardenTag = "bw_access";
public static bool IsAutofillTileAdded = false;
public static bool IsAccessibilityBroadcastReady = false;
// Be sure to keep these two sections sorted alphabetically
public static Dictionary<string, Browser> SupportedBrowsers => new List<Browser>
{
new Browser("com.android.chrome", "url_bar"),
new Browser("com.chrome.beta", "url_bar"),
new Browser("org.chromium.chrome", "url_bar"),
// [Section A] Entries also present in the list of Autofill Framework
//
// So keep them in sync with:
// - AutofillHelpers.{TrustedBrowsers,CompatBrowsers}
// - Resources/xml/autofillservice.xml
new Browser("com.amazon.cloud9", "url"),
new Browser("com.android.browser", "url"),
new Browser("com.android.chrome", "url_bar"),
new Browser("com.avast.android.secure.browser", "editor"),
new Browser("com.avg.android.secure.browser", "editor"),
new Browser("com.brave.browser", "url_bar"),
new Browser("com.brave.browser_beta", "url_bar"),
new Browser("com.brave.browser_default", "url_bar"),
new Browser("com.brave.browser_dev", "url_bar"),
new Browser("com.brave.browser_nightly", "url_bar"),
new Browser("com.chrome.beta", "url_bar"),
new Browser("com.chrome.canary", "url_bar"),
new Browser("com.chrome.dev", "url_bar"),
new Browser("com.duckduckgo.mobile.android", "omnibarTextInput"),
new Browser("com.ecosia.android", "url_bar"),
new Browser("com.google.android.apps.chrome", "url_bar"),
new Browser("com.google.android.apps.chrome_dev", "url_bar"),
new Browser("com.kiwibrowser.browser", "url_bar"),
new Browser("com.microsoft.emmx", "url_bar"),
new Browser("com.naver.whale", "url_bar"),
new Browser("com.opera.browser", "url_field"),
new Browser("com.opera.browser.beta", "url_field"),
new Browser("com.opera.mini.native", "url_field"),
new Browser("com.opera.mini.native.beta", "url_field"),
new Browser("com.opera.touch", "addressbarEdit"),
new Browser("com.chrome.dev", "url_bar"),
new Browser("com.chrome.canary", "url_bar"),
new Browser("com.google.android.apps.chrome", "url_bar"),
new Browser("com.google.android.apps.chrome_dev", "url_bar"),
new Browser("org.codeaurora.swe.browser", "url_bar"),
new Browser("org.iron.srware", "url_bar"),
new Browser("com.qwant.liberty", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
new Browser("com.sec.android.app.sbrowser", "location_bar_edit_text"),
new Browser("com.sec.android.app.sbrowser.beta", "location_bar_edit_text"),
new Browser("com.yandex.browser", "bro_omnibar_address_title_text",
new Browser("com.stoutner.privacybrowser.free", "url_edittext"),
new Browser("com.stoutner.privacybrowser.standard", "url_edittext"),
new Browser("com.vivaldi.browser", "url_bar"),
new Browser("com.vivaldi.browser.snapshot", "url_bar"),
new Browser("com.vivaldi.browser.sopranos", "url_bar"),
new Browser("com.yandex.browser", "bro_omnibar_address_title_text,bro_omnibox_collapsed_title",
(s) => s.Split(new char[]{' ', ' '}).FirstOrDefault()), // 0 = Regular Space, 1 = No-break space (00A0)
new Browser("org.mozilla.firefox", "url_bar_title"),
new Browser("org.mozilla.firefox_beta", "url_bar_title"),
new Browser("org.mozilla.fennec_aurora", "url_bar_title"),
new Browser("org.mozilla.fennec_fdroid", "url_bar_title"),
new Browser("org.mozilla.focus", "display_url"),
new Browser("org.mozilla.klar", "display_url"),
new Browser("mark.via.gp", "aw"),
new Browser("org.adblockplus.browser", "url_bar,url_bar_title"), // 2nd = Legacy (before v2)
new Browser("org.adblockplus.browser.beta", "url_bar,url_bar_title"), // 2nd = Legacy (before v2)
new Browser("org.bromite.bromite", "url_bar"),
new Browser("org.bromite.chromium", "url_bar"),
new Browser("org.chromium.chrome", "url_bar"),
new Browser("org.codeaurora.swe.browser", "url_bar"),
new Browser("org.gnu.icecat", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
new Browser("org.mozilla.fenix", "mozac_browser_toolbar_url_view"),
new Browser("org.mozilla.fenix.nightly", "mozac_browser_toolbar_url_view"),
new Browser("org.mozilla.fennec_aurora", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
new Browser("org.mozilla.fennec_fdroid", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
new Browser("org.mozilla.firefox", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
new Browser("org.mozilla.firefox_beta", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
new Browser("org.mozilla.focus", "display_url"),
new Browser("org.mozilla.klar", "display_url"),
new Browser("org.mozilla.reference.browser", "mozac_browser_toolbar_url_view"),
new Browser("org.mozilla.rocket", "display_url"),
new Browser("org.torproject.torbrowser", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
new Browser("org.torproject.torbrowser_alpha", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
new Browser("org.ungoogled.chromium", "url_bar"),
// [Section B] Entries only present here
//
// FIXME: Test the compatibility of these with Autofill Framework
new Browser("acr.browser.barebones", "search"),
new Browser("acr.browser.lightning", "search"),
new Browser("com.feedback.browser.wjbrowser", "addressbar_url"),
new Browser("com.ghostery.android.ghostery", "search_field"),
new Browser("org.adblockplus.browser", "url_bar_title"),
new Browser("com.htc.sense.browser", "title"),
new Browser("com.amazon.cloud9", "url"),
new Browser("mobi.mgeek.TunnyBrowser", "title"),
new Browser("com.nubelacorp.javelin", "enterUrl"),
new Browser("com.jerky.browser2", "enterUrl"),
new Browser("com.ksmobile.cb", "address_bar_edit_text"),
new Browser("com.linkbubble.playstore", "url_text"),
new Browser("com.mx.browser", "address_editor_with_progress"),
new Browser("com.mx.browser.tablet", "address_editor_with_progress"),
new Browser("com.linkbubble.playstore", "url_text"),
new Browser("com.ksmobile.cb", "address_bar_edit_text"),
new Browser("acr.browser.lightning", "search"),
new Browser("acr.browser.barebones", "search"),
new Browser("com.microsoft.emmx", "url_bar"),
new Browser("com.duckduckgo.mobile.android", "omnibarTextInput"),
new Browser("mark.via.gp", "aw"),
new Browser("org.bromite.bromite", "url_bar"),
new Browser("com.kiwibrowser.browser", "url_bar"),
new Browser("com.ecosia.android", "url_bar"),
new Browser("com.qwant.liberty", "url_bar_title"),
new Browser("com.nubelacorp.javelin", "enterUrl"),
new Browser("jp.co.fenrir.android.sleipnir", "url_text"),
new Browser("jp.co.fenrir.android.sleipnir_black", "url_text"),
new Browser("jp.co.fenrir.android.sleipnir_test", "url_text"),
new Browser("mobi.mgeek.TunnyBrowser", "title"),
new Browser("org.iron.srware", "url_bar"),
}.ToDictionary(n => n.PackageName);
// Known packages to skip
@@ -82,61 +126,88 @@ namespace Bit.Droid.Accessibility
"com.teslacoilsw.launcher.prime",
"is.shortcut",
"me.craftsapp.nlauncher",
"com.ss.squarehome2"
"com.ss.squarehome2",
"com.treydev.pns"
};
// Be sure to keep these entries sorted alphabetically
public static Dictionary<string, KnownUsernameField> KnownUsernameFields => new List<KnownUsernameField>
{
new KnownUsernameField("accounts.google.com", "ServiceLogin", "Email"),
new KnownUsernameField("amazon.com", "signin", "ap_email_login"),
new KnownUsernameField("github.com", "", "user[login]-footer"),
new KnownUsernameField("paypal.com", "signin", "email"),
new KnownUsernameField("signin.aws.amazon.com", "signin", "resolving_input"),
new KnownUsernameField("signin.ebay.com", "eBayISAPI.dll", "userid"),
}.ToDictionary(n => n.UriAuthority);
public static void PrintTestData(AccessibilityNodeInfo root, AccessibilityEvent e)
{
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 });
foreach (var node in testNodesData)
{
System.Diagnostics.Debug.WriteLine("Node: {0} = {1}", node.id, node.text);
}
}
public static string GetUri(AccessibilityNodeInfo root)
{
var uri = string.Concat(Constants.AndroidAppProtocol, root.PackageName);
if(SupportedBrowsers.ContainsKey(root.PackageName))
if (SupportedBrowsers.ContainsKey(root.PackageName))
{
var browser = SupportedBrowsers[root.PackageName];
var addressNode = root.FindAccessibilityNodeInfosByViewId(
$"{root.PackageName}:id/{browser.UriViewId}").FirstOrDefault();
if(addressNode != null)
AccessibilityNodeInfo addressNode = null;
foreach (var uriViewId in browser.UriViewId.Split(","))
{
addressNode = root.FindAccessibilityNodeInfosByViewId(
$"{root.PackageName}:id/{uriViewId}").FirstOrDefault();
if (addressNode != null)
{
break;
}
}
if (addressNode != null)
{
uri = ExtractUri(uri, addressNode, browser);
addressNode.Dispose();
addressNode.Recycle();
}
else
{
// Return null to prevent overwriting notification pendingIntent uri with browser packageName
// (we login to pages, not browsers)
return null;
}
}
return uri;
}
public static string ExtractUri(string uri, AccessibilityNodeInfo addressNode, Browser browser)
private static string ExtractUri(string uri, AccessibilityNodeInfo addressNode, Browser browser)
{
if(addressNode?.Text == null)
if (addressNode?.Text == null)
{
return uri;
}
if(addressNode.Text == null)
if (addressNode.Text == null)
{
return uri;
}
uri = browser.GetUriFunction(addressNode.Text)?.Trim();
if(uri != null && uri.Contains("."))
if (uri != null && uri.Contains("."))
{
if(!uri.Contains("://") && !uri.Contains(" "))
var hasHttpProtocol = uri.StartsWith("http://") || uri.StartsWith("https://");
if (!hasHttpProtocol && uri.Contains("."))
{
uri = string.Concat("http://", uri);
}
else if(Build.VERSION.SdkInt <= BuildVersionCodes.KitkatWatch)
{
var parts = uri.Split(new string[] { ". " }, StringSplitOptions.None);
if(parts.Length > 1)
if (Uri.TryCreate("http://" + uri, UriKind.Absolute, out var uri2))
{
var urlPart = parts.FirstOrDefault(p => p.StartsWith("http"));
if(urlPart != null)
{
uri = urlPart.Trim();
}
return string.Concat("http://", uri);
}
}
if (Uri.TryCreate(uri, UriKind.Absolute, out var uri3))
{
return uri;
}
}
return uri;
}
@@ -146,11 +217,11 @@ namespace Bit.Droid.Accessibility
/// </summary>
public static bool NeedToAutofill(Credentials credentials, string currentUriString)
{
if(credentials == null)
if (credentials == null)
{
return false;
}
if(Uri.TryCreate(credentials.LastUri, UriKind.Absolute, out Uri lastUri) &&
if (Uri.TryCreate(credentials.LastUri, UriKind.Absolute, out Uri lastUri) &&
Uri.TryCreate(currentUriString, UriKind.Absolute, out Uri currentUri))
{
return lastUri.Host == currentUri.Host;
@@ -163,12 +234,11 @@ namespace Bit.Droid.Accessibility
return n?.ClassName?.Contains("EditText") ?? false;
}
public static void FillCredentials(AccessibilityNodeInfo usernameNode,
IEnumerable<AccessibilityNodeInfo> passwordNodes)
{
FillEditText(usernameNode, LastCredentials?.Username);
foreach(var n in passwordNodes)
foreach (var n in passwordNodes)
{
FillEditText(n, LastCredentials?.Password);
}
@@ -176,7 +246,7 @@ namespace Bit.Droid.Accessibility
public static void FillEditText(AccessibilityNodeInfo editTextNode, string value)
{
if(editTextNode == null || value == null)
if (editTextNode == null || value == null)
{
return;
}
@@ -189,31 +259,35 @@ namespace Bit.Droid.Accessibility
Func<AccessibilityNodeInfo, bool> condition, bool disposeIfUnused, NodeList nodes = null,
int recursionDepth = 0)
{
if(nodes == null)
if (nodes == null)
{
nodes = new NodeList();
}
var dispose = disposeIfUnused;
if(n != null && recursionDepth < 50)
if (n != null && recursionDepth < 100)
{
var add = n.WindowId == e.WindowId &&
!(n.ViewIdResourceName?.StartsWith(SystemUiPackage) ?? false) &&
condition(n);
if(add)
if (add)
{
dispose = false;
nodes.Add(n);
}
for(var i = 0; i < n.ChildCount; i++)
for (var i = 0; i < n.ChildCount; i++)
{
var childNode = n.GetChild(i);
if(i > 100)
if (childNode == null)
{
continue;
}
else if (i > 100)
{
Android.Util.Log.Info(BitwardenTag, "Too many child iterations.");
break;
}
else if(childNode.GetHashCode() == n.GetHashCode())
else if (childNode.GetHashCode() == n.GetHashCode())
{
Android.Util.Log.Info(BitwardenTag, "Child node is the same as parent for some reason.");
}
@@ -223,26 +297,352 @@ namespace Bit.Droid.Accessibility
}
}
}
if(dispose)
if (dispose)
{
n?.Recycle();
n?.Dispose();
}
return nodes;
}
public static void GetNodesAndFill(AccessibilityNodeInfo root, AccessibilityEvent e,
IEnumerable<AccessibilityNodeInfo> passwordNodes)
public static AccessibilityNodeInfo GetUsernameEditText(string uriString,
IEnumerable<AccessibilityNodeInfo> allEditTexts)
{
var allEditTexts = GetWindowNodes(root, e, n => EditText(n), false);
var usernameEditText = GetUsernameEditText(allEditTexts);
FillCredentials(usernameEditText, passwordNodes);
allEditTexts.Dispose();
usernameEditText = null;
string uriAuthority = null;
string uriKey = null;
string uriLocalPath = null;
if (Uri.TryCreate(uriString, UriKind.Absolute, out var uri))
{
uriAuthority = uri.Authority;
uriKey = uriAuthority.StartsWith("www.") ? uriAuthority.Substring(4) : uriAuthority;
uriLocalPath = uri.LocalPath;
}
if (!string.IsNullOrEmpty(uriKey))
{
// Uncomment this to log values necessary for username field discovery
// foreach (var editText in allEditTexts)
// {
// System.Diagnostics.Debug.WriteLine(">>> uriKey: {0}, uriLocalPath: {1}, viewId: {2}", uriKey,
// uriLocalPath, editText.ViewIdResourceName);
// }
if (KnownUsernameFields.ContainsKey(uriKey))
{
var usernameField = KnownUsernameFields[uriKey];
if (uriLocalPath.EndsWith(usernameField.UriPathEnd))
{
foreach (var editText in allEditTexts)
{
foreach (var usernameViewId in usernameField.UsernameViewId.Split(","))
{
if (usernameViewId == editText.ViewIdResourceName)
{
return editText;
}
}
}
}
}
}
// no match found, attempt to establish username field based on password field
return GetUsernameEditTextIfPasswordExists(allEditTexts);
}
private static AccessibilityNodeInfo GetUsernameEditTextIfPasswordExists(
IEnumerable<AccessibilityNodeInfo> allEditTexts)
{
AccessibilityNodeInfo previousEditText = null;
foreach (var editText in allEditTexts)
{
if (editText.Password)
{
return previousEditText;
}
previousEditText = editText;
}
return null;
}
public static AccessibilityNodeInfo GetUsernameEditText(IEnumerable<AccessibilityNodeInfo> allEditTexts)
public static bool IsUsernameEditText(AccessibilityNodeInfo root, AccessibilityEvent e)
{
return allEditTexts.TakeWhile(n => !n.Password).LastOrDefault();
var allEditTexts = GetWindowNodes(root, e, n => EditText(n), false);
var uriString = GetUri(root);
var usernameEditText = GetUsernameEditText(uriString, allEditTexts);
var isUsernameEditText = false;
if (usernameEditText != null)
{
isUsernameEditText = IsSameNode(usernameEditText, e.Source);
}
allEditTexts.Dispose();
return isUsernameEditText;
}
public static bool IsSameNode(AccessibilityNodeInfo node1, AccessibilityNodeInfo node2)
{
if (node1 != null && node2 != null)
{
return node1.Equals(node2) || node1.GetHashCode() == node2.GetHashCode();
}
return false;
}
public static bool OverlayPermitted()
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
{
if (Settings.CanDrawOverlays(Application.Context))
{
return true;
}
var appOpsMgr = (AppOpsManager)Application.Context.GetSystemService(Context.AppOpsService);
var mode = appOpsMgr.CheckOpNoThrow("android:system_alert_window", Process.MyUid(),
Application.Context.PackageName);
if (mode == AppOpsManagerMode.Allowed || mode == AppOpsManagerMode.Ignored)
{
return true;
}
try
{
var wm = Application.Context.GetSystemService(Context.WindowService)
.JavaCast<IWindowManager>();
if (wm == null)
{
return false;
}
var testView = new View(Application.Context);
var layoutParams = GetOverlayLayoutParams();
wm.AddView(testView, layoutParams);
wm.RemoveView(testView);
return true;
}
catch { }
return false;
}
// older android versions are always true
return true;
}
public static LinearLayout GetOverlayView(Context context)
{
var inflater = (LayoutInflater)context.GetSystemService(Context.LayoutInflaterService);
var view = (LinearLayout)inflater.Inflate(Resource.Layout.autofill_listitem, null);
var text1 = (TextView)view.FindViewById(Resource.Id.text1);
var text2 = (TextView)view.FindViewById(Resource.Id.text2);
var icon = (ImageView)view.FindViewById(Resource.Id.icon);
text1.Text = AppResources.AutofillWithBitwarden;
text2.Text = AppResources.GoToMyVault;
icon.SetImageResource(Resource.Drawable.icon);
return view;
}
public static WindowManagerLayoutParams GetOverlayLayoutParams()
{
WindowManagerTypes windowManagerType;
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
windowManagerType = WindowManagerTypes.ApplicationOverlay;
}
else
{
windowManagerType = WindowManagerTypes.Phone;
}
var layoutParams = new WindowManagerLayoutParams(
ViewGroup.LayoutParams.WrapContent,
ViewGroup.LayoutParams.WrapContent,
windowManagerType,
WindowManagerFlags.NotFocusable | WindowManagerFlags.NotTouchModal,
Format.Transparent);
layoutParams.Gravity = GravityFlags.Top | GravityFlags.Left;
return layoutParams;
}
public static Point GetOverlayAnchorPosition(AccessibilityService service, AccessibilityNodeInfo anchorView,
int overlayViewHeight, bool isOverlayAboveAnchor)
{
var anchorViewRect = new Rect();
anchorView.GetBoundsInScreen(anchorViewRect);
var anchorViewX = anchorViewRect.Left;
var anchorViewY = isOverlayAboveAnchor ? anchorViewRect.Top : anchorViewRect.Bottom;
anchorViewRect.Dispose();
if (isOverlayAboveAnchor)
{
anchorViewY -= overlayViewHeight;
}
anchorViewY -= GetStatusBarHeight(service);
return new Point(anchorViewX, anchorViewY);
}
public static Point GetOverlayAnchorPosition(AccessibilityService service, AccessibilityNodeInfo anchorNode,
AccessibilityNodeInfo root, IEnumerable<AccessibilityWindowInfo> windows, int overlayViewHeight,
bool isOverlayAboveAnchor)
{
Point point = null;
if (anchorNode != null)
{
// Update node's info since this is still a reference from an older event
anchorNode.Refresh();
if (!anchorNode.VisibleToUser)
{
return new Point(-1, -1);
}
if (!anchorNode.Focused)
{
return null;
}
// node.VisibleToUser doesn't always give us exactly what we want, so attempt to tighten up the range
// of visibility
var inputMethodHeight = 0;
if (windows != null)
{
if (IsStatusBarExpanded(windows))
{
return new Point(-1, -1);
}
inputMethodHeight = GetInputMethodHeight(windows);
}
var minY = 0;
var rootNodeHeight = GetNodeHeight(root);
if (rootNodeHeight == -1)
{
return null;
}
var maxY = rootNodeHeight - GetNavigationBarHeight(service) - GetStatusBarHeight(service) -
inputMethodHeight;
point = GetOverlayAnchorPosition(service, anchorNode, overlayViewHeight, isOverlayAboveAnchor);
if (point.Y < minY)
{
if (isOverlayAboveAnchor)
{
// view nearing bounds, anchor to bottom
point.X = -1;
point.Y = 0;
}
else
{
// view out of bounds, hide overlay
point.X = -1;
point.Y = -1;
}
}
else if (point.Y > (maxY - overlayViewHeight))
{
if (isOverlayAboveAnchor)
{
// view out of bounds, hide overlay
point.X = -1;
point.Y = -1;
}
else
{
// view nearing bounds, anchor to top
point.X = 0;
point.Y = -1;
}
}
else if (isOverlayAboveAnchor && point.Y < (maxY - (overlayViewHeight * 2) - GetNodeHeight(anchorNode)))
{
// This else block forces the overlay to return to bottom alignment as soon as space is available
// below the anchor view. Removing this will change the behavior to wait until there isn't enough
// space above the anchor view before returning to bottom alignment.
point.X = -1;
point.Y = 0;
}
}
return point;
}
public static bool IsStatusBarExpanded(IEnumerable<AccessibilityWindowInfo> windows)
{
if (windows != null && windows.Any())
{
var isSystemWindowsOnly = true;
foreach (var window in windows)
{
if (window.Type != AccessibilityWindowType.System)
{
isSystemWindowsOnly = false;
break;
}
}
return isSystemWindowsOnly;
}
return false;
}
public static int GetInputMethodHeight(IEnumerable<AccessibilityWindowInfo> windows)
{
var inputMethodWindowHeight = 0;
if (windows != null)
{
foreach (var window in windows)
{
if (window.Type == AccessibilityWindowType.InputMethod)
{
var windowRect = new Rect();
window.GetBoundsInScreen(windowRect);
inputMethodWindowHeight = windowRect.Height();
break;
}
}
}
return inputMethodWindowHeight;
}
public static bool IsAutofillServicePromptVisible(IEnumerable<AccessibilityWindowInfo> windows)
{
// Autofill framework not available until API 26
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
return windows?.Any(w => w.Title?.ToLower().Contains("autofill") ?? false) ?? false;
}
return false;
}
public static int GetNodeHeight(AccessibilityNodeInfo node)
{
if (node == null)
{
return -1;
}
var nodeRect = new Rect();
node.GetBoundsInScreen(nodeRect);
var nodeRectHeight = nodeRect.Height();
nodeRect.Dispose();
return nodeRectHeight;
}
private static int GetStatusBarHeight(AccessibilityService service)
{
return GetSystemResourceDimenPx(service, "status_bar_height");
}
private static int GetNavigationBarHeight(AccessibilityService service)
{
return GetSystemResourceDimenPx(service, "navigation_bar_height");
}
private static int GetSystemResourceDimenPx(AccessibilityService service, string resName)
{
var resourceId = service.Resources.GetIdentifier(resName, "dimen", "android");
if (resourceId > 0)
{
return service.Resources.GetDimensionPixelSize(resourceId);
}
return 0;
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -6,7 +6,9 @@ using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Views.Accessibility;
using Android.Widget;
using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions;
@@ -20,149 +22,142 @@ namespace Bit.Droid.Accessibility
[Register("com.x8bit.bitwarden.Accessibility.AccessibilityService")]
public class AccessibilityService : Android.AccessibilityServices.AccessibilityService
{
private NotificationChannel _notificationChannel;
private const int AutoFillNotificationId = 34573;
private const string BitwardenPackage = "com.x8bit.bitwarden";
private const string BitwardenWebsite = "vault.bitwarden.com";
private IStorageService _storageService;
private bool _settingAutofillPasswordField;
private bool _settingAutofillPersistNotification;
private IBroadcasterService _broadcasterService;
private DateTime? _lastSettingsReload = null;
private TimeSpan _settingsReloadSpan = TimeSpan.FromMinutes(1);
private long _lastNotificationTime = 0;
private string _lastNotificationUri = null;
private HashSet<string> _blacklistedUris;
private AccessibilityNodeInfo _anchorNode = null;
private int _lastAnchorX = 0;
private int _lastAnchorY = 0;
private bool _isOverlayAboveAnchor = false;
private static bool _overlayAnchorObserverRunning = false;
private IWindowManager _windowManager = null;
private LinearLayout _overlayView = null;
private int _overlayViewHeight = 0;
private long _lastAutoFillTime = 0;
private Java.Lang.Runnable _overlayAnchorObserverRunnable = null;
private Handler _handler = new Handler(Looper.MainLooper);
private HashSet<string> _launcherPackageNames = null;
private DateTime? _lastLauncherSetBuilt = null;
private TimeSpan _rebuildLauncherSpan = TimeSpan.FromHours(1);
public override void OnCreate()
{
base.OnCreate();
LoadServices();
var settingsTask = LoadSettingsAsync();
_broadcasterService.Subscribe(nameof(AccessibilityService), (message) =>
{
if (message.Command == "OnAutofillTileClick")
{
var runnable = new Java.Lang.Runnable(OnAutofillTileClick);
_handler.PostDelayed(runnable, 250);
}
});
AccessibilityHelpers.IsAccessibilityBroadcastReady = true;
}
public override void OnDestroy()
{
AccessibilityHelpers.IsAccessibilityBroadcastReady = false;
_broadcasterService.Unsubscribe(nameof(AccessibilityService));
}
public override void OnAccessibilityEvent(AccessibilityEvent e)
{
try
{
var powerManager = GetSystemService(PowerService) as PowerManager;
if(Build.VERSION.SdkInt > BuildVersionCodes.KitkatWatch && !powerManager.IsInteractive)
if (Build.VERSION.SdkInt > BuildVersionCodes.KitkatWatch && !powerManager.IsInteractive)
{
return;
}
else if(Build.VERSION.SdkInt < BuildVersionCodes.Lollipop && !powerManager.IsScreenOn)
else if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop && !powerManager.IsScreenOn)
{
return;
}
if(SkipPackage(e?.PackageName))
if (SkipPackage(e?.PackageName))
{
if (e?.PackageName != "com.android.systemui")
{
CancelOverlayPrompt();
}
return;
}
var root = RootInActiveWindow;
if(root == null || root.PackageName != e.PackageName)
{
return;
}
// AccessibilityHelpers.PrintTestData(RootInActiveWindow, e);
// AccessibilityHelpers.PrintTestData(root, e);
LoadServices();
var settingsTask = LoadSettingsAsync();
AccessibilityNodeInfo root = null;
var notificationManager = GetSystemService(NotificationService) as NotificationManager;
var cancelNotification = true;
switch(e.EventType)
switch (e.EventType)
{
case EventTypes.ViewFocused:
if(e.Source == null || !e.Source.Password || !_settingAutofillPasswordField)
case EventTypes.ViewClicked:
if (e.Source == null || e.PackageName == BitwardenPackage)
{
CancelOverlayPrompt();
break;
}
root = RootInActiveWindow;
if (root == null || root.PackageName != e.PackageName)
{
break;
}
if(e.PackageName == BitwardenPackage)
if (!(e.Source?.Password ?? false) && !AccessibilityHelpers.IsUsernameEditText(root, e))
{
CancelNotification(notificationManager);
CancelOverlayPrompt();
break;
}
if(ScanAndAutofill(root, e, notificationManager, cancelNotification))
if (ScanAndAutofill(root, e))
{
CancelNotification(notificationManager);
CancelOverlayPrompt();
}
else
{
OverlayPromptToAutofill(root, e);
}
break;
case EventTypes.WindowContentChanged:
case EventTypes.WindowStateChanged:
if(_settingAutofillPasswordField && e.Source.Password)
if (AccessibilityHelpers.LastCredentials == null)
{
break;
}
else if(_settingAutofillPasswordField && AccessibilityHelpers.LastCredentials == null)
if (e.PackageName == BitwardenPackage)
{
if(string.IsNullOrWhiteSpace(_lastNotificationUri))
{
CancelNotification(notificationManager);
break;
}
var uri = AccessibilityHelpers.GetUri(root);
if(uri != _lastNotificationUri)
{
CancelNotification(notificationManager);
}
else if(uri.StartsWith(Constants.AndroidAppProtocol))
{
CancelNotification(notificationManager, 30000);
}
CancelOverlayPrompt();
break;
}
if(e.PackageName == BitwardenPackage)
root = RootInActiveWindow;
if (root == null || root.PackageName != e.PackageName)
{
CancelNotification(notificationManager);
break;
}
if(_settingAutofillPersistNotification)
if (ScanAndAutofill(root, e))
{
var uri = AccessibilityHelpers.GetUri(root);
if(uri != null && !uri.Contains(BitwardenWebsite))
{
var needToFill = AccessibilityHelpers.NeedToAutofill(
AccessibilityHelpers.LastCredentials, uri);
if(needToFill)
{
var passwordNodes = AccessibilityHelpers.GetWindowNodes(root, e,
n => n.Password, false);
needToFill = passwordNodes.Any();
if(needToFill)
{
AccessibilityHelpers.GetNodesAndFill(root, e, passwordNodes);
}
passwordNodes.Dispose();
}
if(!needToFill)
{
NotifyToAutofill(uri, notificationManager);
cancelNotification = false;
}
}
AccessibilityHelpers.LastCredentials = null;
}
else
{
cancelNotification = ScanAndAutofill(root, e, notificationManager, cancelNotification);
}
if(cancelNotification)
{
CancelNotification(notificationManager);
CancelOverlayPrompt();
}
break;
default:
break;
}
notificationManager?.Dispose();
root.Dispose();
e.Dispose();
}
// Suppress exceptions so that service doesn't crash.
catch { }
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", ex.GetType(), ex.StackTrace);
}
}
public override void OnInterrupt()
@@ -170,28 +165,27 @@ namespace Bit.Droid.Accessibility
// Do nothing.
}
public bool ScanAndAutofill(AccessibilityNodeInfo root, AccessibilityEvent e,
NotificationManager notificationManager, bool cancelNotification)
public bool ScanAndAutofill(AccessibilityNodeInfo root, AccessibilityEvent e)
{
var passwordNodes = AccessibilityHelpers.GetWindowNodes(root, e, n => n.Password, false);
if(passwordNodes.Count > 0)
var filled = false;
var uri = AccessibilityHelpers.GetUri(root);
if (uri != null && !uri.Contains(BitwardenWebsite) &&
AccessibilityHelpers.NeedToAutofill(AccessibilityHelpers.LastCredentials, uri))
{
var uri = AccessibilityHelpers.GetUri(root);
if(uri != null && !uri.Contains(BitwardenWebsite))
var allEditTexts = AccessibilityHelpers.GetWindowNodes(root, e, n => AccessibilityHelpers.EditText(n), false);
var usernameEditText = AccessibilityHelpers.GetUsernameEditText(uri, allEditTexts);
var passwordNodes = AccessibilityHelpers.GetWindowNodes(root, e, n => n.Password, false);
if (usernameEditText != null || passwordNodes.Count > 0)
{
if(AccessibilityHelpers.NeedToAutofill(AccessibilityHelpers.LastCredentials, uri))
{
AccessibilityHelpers.GetNodesAndFill(root, e, passwordNodes);
}
else
{
NotifyToAutofill(uri, notificationManager);
cancelNotification = false;
}
AccessibilityHelpers.FillCredentials(usernameEditText, passwordNodes);
filled = true;
_lastAutoFillTime = Java.Lang.JavaSystem.CurrentTimeMillis();
AccessibilityHelpers.LastCredentials = null;
}
AccessibilityHelpers.LastCredentials = null;
allEditTexts.Dispose();
passwordNodes.Dispose();
}
else if(AccessibilityHelpers.LastCredentials != null)
if (AccessibilityHelpers.LastCredentials != null)
{
Task.Run(async () =>
{
@@ -199,81 +193,243 @@ namespace Bit.Droid.Accessibility
AccessibilityHelpers.LastCredentials = null;
});
}
passwordNodes.Dispose();
return cancelNotification;
return filled;
}
private void OnAutofillTileClick()
{
CancelOverlayPrompt();
var root = RootInActiveWindow;
if (root != null && root.PackageName != BitwardenPackage &&
root.PackageName != AccessibilityHelpers.SystemUiPackage &&
!SkipPackage(root.PackageName))
{
var uri = AccessibilityHelpers.GetUri(root);
if (!string.IsNullOrWhiteSpace(uri))
{
var intent = new Intent(this, typeof(AccessibilityActivity));
intent.PutExtra("uri", uri);
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
StartActivity(intent);
return;
}
}
Toast.MakeText(this, AppResources.AutofillTileUriNotFound, ToastLength.Long).Show();
}
public void CancelNotification(NotificationManager notificationManager, long limit = 250)
private void CancelOverlayPrompt()
{
if(Java.Lang.JavaSystem.CurrentTimeMillis() - _lastNotificationTime < limit)
_overlayAnchorObserverRunning = false;
if (_windowManager != null && _overlayView != null)
{
try
{
_windowManager.RemoveViewImmediate(_overlayView);
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Removed");
}
catch { }
}
_overlayView = null;
_lastAnchorX = 0;
_lastAnchorY = 0;
_isOverlayAboveAnchor = false;
if (_anchorNode != null)
{
_anchorNode.Recycle();
_anchorNode = null;
}
}
private void OverlayPromptToAutofill(AccessibilityNodeInfo root, AccessibilityEvent e)
{
if (Java.Lang.JavaSystem.CurrentTimeMillis() - _lastAutoFillTime < 1000 ||
AccessibilityHelpers.IsAutofillServicePromptVisible(Windows))
{
return;
}
_lastNotificationUri = null;
notificationManager?.Cancel(AutoFillNotificationId);
}
if (!AccessibilityHelpers.OverlayPermitted())
{
if (!AccessibilityHelpers.IsAutofillTileAdded)
{
// The user has the option of only using the autofill tile and leaving the overlay permission
// disabled, so only show this toast if they're using accessibility without overlay permission and
// have _not_ added the autofill tile
System.Diagnostics.Debug.WriteLine(">>> Overlay Permission not granted");
Toast.MakeText(this, AppResources.AccessibilityOverlayPermissionAlert, ToastLength.Long).Show();
}
return;
}
private void NotifyToAutofill(string uri, NotificationManager notificationManager)
{
if(notificationManager == null || string.IsNullOrWhiteSpace(uri))
if (_overlayView != null || _anchorNode != null || _overlayAnchorObserverRunning)
{
CancelOverlayPrompt();
}
var uri = AccessibilityHelpers.GetUri(root);
var fillable = !string.IsNullOrWhiteSpace(uri);
if (fillable)
{
if (_blacklistedUris != null && _blacklistedUris.Any())
{
if (Uri.TryCreate(uri, UriKind.Absolute, out var parsedUri) && parsedUri.Scheme.StartsWith("http"))
{
fillable = !_blacklistedUris.Contains(
string.Format("{0}://{1}", parsedUri.Scheme, parsedUri.Host));
}
else
{
fillable = !_blacklistedUris.Contains(uri);
}
}
}
if (!fillable)
{
return;
}
var now = Java.Lang.JavaSystem.CurrentTimeMillis();
var intent = new Intent(this, typeof(AccessibilityActivity));
intent.PutExtra("uri", uri);
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.UpdateCurrent);
var notificationContent = Build.VERSION.SdkInt > BuildVersionCodes.KitkatWatch ?
AppResources.BitwardenAutofillServiceNotificationContent :
AppResources.BitwardenAutofillServiceNotificationContentOld;
var builder = new Notification.Builder(this);
builder.SetSmallIcon(Resource.Drawable.shield)
.SetContentTitle(AppResources.BitwardenAutofillService)
.SetContentText(notificationContent)
.SetTicker(notificationContent)
.SetWhen(now)
.SetContentIntent(pendingIntent);
if(Build.VERSION.SdkInt > BuildVersionCodes.KitkatWatch)
_overlayView = AccessibilityHelpers.GetOverlayView(this);
_overlayView.Measure(View.MeasureSpec.MakeMeasureSpec(0, 0),
View.MeasureSpec.MakeMeasureSpec(0, 0));
_overlayViewHeight = _overlayView.MeasuredHeight;
_overlayView.Click += (sender, eventArgs) =>
{
builder.SetVisibility(NotificationVisibility.Secret)
.SetColor(Android.Support.V4.Content.ContextCompat.GetColor(ApplicationContext,
Resource.Color.primary));
CancelOverlayPrompt();
StartActivity(intent);
};
var layoutParams = AccessibilityHelpers.GetOverlayLayoutParams();
var anchorPosition = AccessibilityHelpers.GetOverlayAnchorPosition(this, e.Source,
_overlayViewHeight, _isOverlayAboveAnchor);
layoutParams.X = anchorPosition.X;
layoutParams.Y = anchorPosition.Y;
if (_windowManager == null)
{
_windowManager = GetSystemService(WindowService).JavaCast<IWindowManager>();
}
if(Build.VERSION.SdkInt >= BuildVersionCodes.O)
_anchorNode = e.Source;
_lastAnchorX = anchorPosition.X;
_lastAnchorY = anchorPosition.Y;
_windowManager.AddView(_overlayView, layoutParams);
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Added at X:{0} Y:{1}",
layoutParams.X, layoutParams.Y);
StartOverlayAnchorObserver();
}
private void StartOverlayAnchorObserver()
{
if (_overlayAnchorObserverRunning)
{
if(_notificationChannel == null)
return;
}
_overlayAnchorObserverRunning = true;
_overlayAnchorObserverRunnable = new Java.Lang.Runnable(() =>
{
if (_overlayAnchorObserverRunning)
{
_notificationChannel = new NotificationChannel("bitwarden_autofill_service",
AppResources.AutofillService, NotificationImportance.Low);
notificationManager.CreateNotificationChannel(_notificationChannel);
AdjustOverlayForScroll();
_handler.PostDelayed(_overlayAnchorObserverRunnable, 250);
}
builder.SetChannelId(_notificationChannel.Id);
}
if(/*Build.VERSION.SdkInt <= BuildVersionCodes.N && */_settingAutofillPersistNotification)
});
_handler.PostDelayed(_overlayAnchorObserverRunnable, 250);
}
private void AdjustOverlayForScroll()
{
if (_overlayView == null || _anchorNode == null ||
AccessibilityHelpers.IsAutofillServicePromptVisible(Windows))
{
builder.SetPriority(-2);
CancelOverlayPrompt();
return;
}
_lastNotificationTime = now;
_lastNotificationUri = uri;
notificationManager.Notify(AutoFillNotificationId, builder.Build());
builder.Dispose();
var root = RootInActiveWindow;
IEnumerable<AccessibilityWindowInfo> windows = null;
if (Build.VERSION.SdkInt > BuildVersionCodes.Kitkat)
{
windows = Windows;
}
var anchorPosition = AccessibilityHelpers.GetOverlayAnchorPosition(this, _anchorNode, root,
windows, _overlayViewHeight, _isOverlayAboveAnchor);
if (anchorPosition == null)
{
CancelOverlayPrompt();
return;
}
else if (anchorPosition.X == -1 && anchorPosition.Y == -1)
{
if (_overlayView.Visibility != ViewStates.Gone)
{
_overlayView.Visibility = ViewStates.Gone;
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Hidden");
}
return;
}
else if (anchorPosition.X == -1)
{
_isOverlayAboveAnchor = false;
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Below Anchor");
return;
}
else if (anchorPosition.Y == -1)
{
_isOverlayAboveAnchor = true;
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Above Anchor");
return;
}
else if (anchorPosition.X == _lastAnchorX && anchorPosition.Y == _lastAnchorY)
{
if (_overlayView.Visibility != ViewStates.Visible)
{
_overlayView.Visibility = ViewStates.Visible;
}
return;
}
var layoutParams = AccessibilityHelpers.GetOverlayLayoutParams();
layoutParams.X = anchorPosition.X;
layoutParams.Y = anchorPosition.Y;
_lastAnchorX = anchorPosition.X;
_lastAnchorY = anchorPosition.Y;
_windowManager.UpdateViewLayout(_overlayView, layoutParams);
if (_overlayView.Visibility != ViewStates.Visible)
{
_overlayView.Visibility = ViewStates.Visible;
}
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Updated to X:{0} Y:{1}",
layoutParams.X, layoutParams.Y);
}
private bool SkipPackage(string eventPackageName)
{
if(string.IsNullOrWhiteSpace(eventPackageName) ||
if (string.IsNullOrWhiteSpace(eventPackageName) ||
AccessibilityHelpers.FilteredPackageNames.Contains(eventPackageName) ||
eventPackageName.Contains("launcher"))
{
return true;
}
if(_launcherPackageNames == null || _lastLauncherSetBuilt == null ||
if (_launcherPackageNames == null || _lastLauncherSetBuilt == null ||
(DateTime.Now - _lastLauncherSetBuilt.Value) > _rebuildLauncherSpan)
{
// refresh launcher list every now and then
@@ -288,22 +444,29 @@ namespace Bit.Droid.Accessibility
private void LoadServices()
{
if(_storageService == null)
if (_storageService == null)
{
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
}
if (_broadcasterService == null)
{
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
}
}
private async Task LoadSettingsAsync()
{
var now = DateTime.UtcNow;
if(_lastSettingsReload == null || (now - _lastSettingsReload.Value) > _settingsReloadSpan)
if (_lastSettingsReload == null || (now - _lastSettingsReload.Value) > _settingsReloadSpan)
{
_lastSettingsReload = now;
_settingAutofillPasswordField = await _storageService.GetAsync<bool>(
Constants.AccessibilityAutofillPasswordFieldKey);
_settingAutofillPersistNotification = await _storageService.GetAsync<bool>(
Constants.AccessibilityAutofillPersistNotificationKey);
var uris = await _storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
if (uris != null)
{
_blacklistedUris = new HashSet<string>(uris);
}
var isAutoFillTileAdded = await _storageService.GetAsync<bool?>(Constants.AutofillTileAdded);
AccessibilityHelpers.IsAutofillTileAdded = isAutoFillTileAdded.GetValueOrDefault();
}
}
}

View File

@@ -0,0 +1,16 @@
namespace Bit.Droid.Accessibility
{
public class KnownUsernameField
{
public KnownUsernameField(string uriAuthority, string uriPathEnd, string usernameViewId)
{
UriAuthority = uriAuthority;
UriPathEnd = uriPathEnd;
UsernameViewId = usernameViewId;
}
public string UriAuthority { get; set; }
public string UriPathEnd { get; set; }
public string UsernameViewId { get; set; }
}
}

View File

@@ -8,10 +8,11 @@ namespace Bit.Droid.Accessibility
{
public void Dispose()
{
foreach(var item in this)
foreach (var item in this)
{
item.Recycle();
item.Dispose();
}
}
}
}
}

View File

@@ -15,8 +15,7 @@
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v9.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v10.0</TargetFrameworkVersion>
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
@@ -30,10 +29,8 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>3</WarningLevel>
<AndroidLinkMode>None</AndroidLinkMode>
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
<AndroidSupportedAbis />
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
<AndroidLinkSkip>Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;BitwardenCore;Xamarin.Android.Net</AndroidLinkSkip>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>false</DebugSymbols>
@@ -46,10 +43,8 @@
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
<AndroidLinkSkip>Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;BitwardenCore;Xamarin.Android.Net</AndroidLinkSkip>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
<AndroidLinkMode>Full</AndroidLinkMode>
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'FDroid|AnyCPU'">
<DebugSymbols>false</DebugSymbols>
@@ -63,11 +58,9 @@
<DefineConstants>FDROID</DefineConstants>
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
<AndroidLinkSkip>Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;BitwardenCore;Xamarin.Android.Net</AndroidLinkSkip>
<AndroidLinkMode>Full</AndroidLinkMode>
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
@@ -82,21 +75,22 @@
<Version>2.1.0.4</Version>
</PackageReference>
<PackageReference Include="Portable.BouncyCastle">
<Version>1.8.5</Version>
<Version>1.8.6.7</Version>
</PackageReference>
<PackageReference Include="Xamarin.Essentials">
<Version>1.1.0</Version>
<Version>1.5.3.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Firebase.Messaging">
<Version>60.1142.1</Version>
<Version>71.1740.0</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Support.Design" Version="28.0.0.1" />
<PackageReference Include="Xamarin.Android.Support.v7.AppCompat" Version="28.0.0.1" />
<PackageReference Include="Xamarin.Android.Support.v4" Version="28.0.0.1" />
<PackageReference Include="Xamarin.Android.Support.v7.CardView" Version="28.0.0.1" />
<PackageReference Include="Xamarin.Android.Support.v7.MediaRouter" Version="28.0.0.1" />
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.0.0" />
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.1.0" />
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0" />
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0" />
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.1.0" />
<PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.6" />
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
<Version>60.1142.1</Version>
<Version>71.1600.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
@@ -106,6 +100,7 @@
<Compile Include="Accessibility\AccessibilityService.cs" />
<Compile Include="Accessibility\Browser.cs" />
<Compile Include="Accessibility\NodeList.cs" />
<Compile Include="Accessibility\KnownUsernameField.cs" />
<Compile Include="Autofill\AutofillHelpers.cs" />
<Compile Include="Autofill\AutofillService.cs" />
<Compile Include="Autofill\Field.cs" />
@@ -113,13 +108,14 @@
<Compile Include="Autofill\FilledItem.cs" />
<Compile Include="Autofill\Parser.cs" />
<Compile Include="Autofill\SavedItem.cs" />
<Compile Include="Effects\FabShadowEffect.cs" />
<Compile Include="Effects\FixedSizeEffect.cs" />
<Compile Include="Effects\SelectableLabelEffect.cs" />
<Compile Include="Effects\TabBarEffect.cs" />
<Compile Include="Migration\AndroidKeyStoreStorageService.cs" />
<Compile Include="Push\FirebaseInstanceIdService.cs" />
<Compile Include="Push\FirebaseMessagingService.cs" />
<Compile Include="Receivers\ClearClipboardAlarmReceiver.cs" />
<Compile Include="Receivers\RestrictionsChangedReceiver.cs" />
<Compile Include="Receivers\EventUploadReceiver.cs" />
<Compile Include="Receivers\LockAlarmReceiver.cs" />
<Compile Include="Receivers\PackageReplacedReceiver.cs" />
@@ -140,12 +136,11 @@
<Compile Include="Services\CryptoPrimitiveService.cs" />
<Compile Include="Services\DeviceActionService.cs" />
<Compile Include="Services\LocalizeService.cs" />
<Compile Include="Tiles\AutofillTileService.cs" />
<Compile Include="Tiles\GeneratorTileService.cs" />
<Compile Include="Tiles\MyVaultTileService.cs" />
<Compile Include="Utilities\AndroidHelpers.cs" />
<Compile Include="Utilities\CustomFingerprintDialogFragment.cs" />
<Compile Include="Utilities\HockeyAppCrashManagerListener.cs" />
<Compile Include="Utilities\StaticStore.cs" />
<Compile Include="Utilities\AppCenterHelper.cs" />
</ItemGroup>
<ItemGroup>
<AndroidAsset Include="Assets\FontAwesome.ttf" />
@@ -155,11 +150,51 @@
<None Include="ci-build-apks.ps1" />
<GoogleServicesJson Include="google-services.json" />
<GoogleServicesJson Include="google-services.json.enc" />
<None Include="fdroid-keystore.jks.enc" />
<None Include="Properties\AndroidManifest.xml" />
<None Include="upload-keystore.jks.enc" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\accessibility_overlay.png" />
<AndroidResource Include="Resources\drawable-hdpi\accessibility_permission.png" />
<AndroidResource Include="Resources\drawable-hdpi\accessibility_step1.png" />
<AndroidResource Include="Resources\drawable-hdpi\accessibility_step2.png" />
<AndroidResource Include="Resources\drawable-hdpi\autofill_enable.png" />
<AndroidResource Include="Resources\drawable-hdpi\autofill_use.png" />
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_overlay.png" />
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_permission.png" />
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_step1.png" />
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_step2.png" />
<AndroidResource Include="Resources\drawable-xhdpi\autofill_enable.png" />
<AndroidResource Include="Resources\drawable-xhdpi\autofill_use.png" />
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_overlay.png" />
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_permission.png" />
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_step1.png" />
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_step2.png" />
<AndroidResource Include="Resources\drawable-xxhdpi\autofill_enable.png" />
<AndroidResource Include="Resources\drawable-xxhdpi\autofill_use.png" />
<AndroidResource Include="Resources\drawable\accessibility_overlay.png" />
<AndroidResource Include="Resources\drawable\accessibility_permission.png" />
<AndroidResource Include="Resources\drawable\accessibility_step1.png" />
<AndroidResource Include="Resources\drawable\accessibility_step2.png" />
<AndroidResource Include="Resources\drawable\autofill_enable.png" />
<AndroidResource Include="Resources\drawable\autofill_use.png" />
<AndroidResource Include="Resources\drawable\icon.xml" />
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
<AndroidResource Include="Resources\layout\Tabbar.axml" />
<AndroidResource Include="Resources\layout\Toolbar.axml" />
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher_round.xml" />
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher.png" />
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_round.png" />
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher.png" />
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_round.png" />
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher.png" />
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_round.png" />
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher.png" />
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_round.png" />
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher.png" />
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_round.png" />
<AndroidResource Include="Resources\values\styles.xml" />
<AndroidResource Include="Resources\values\colors.xml" />
</ItemGroup>
@@ -194,57 +229,6 @@
<ItemGroup>
<AndroidResource Include="Resources\values\ic_launcher_background.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_foreground.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_round.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher_round.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_foreground.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_round.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_foreground.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_round.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_foreground.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_round.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_foreground.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_round.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\layout\autofill_listitem.xml" />
</ItemGroup>
@@ -287,87 +271,15 @@
<ItemGroup>
<AndroidResource Include="Resources\drawable\shield.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\autofill_use.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\accessibility_notification.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\accessibility_notification_icon.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\accessibility_step1.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\accessibility_step2.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\autofill_enable.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\autofill_use.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\accessibility_notification.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\accessibility_notification_icon.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\accessibility_step1.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\accessibility_step2.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\autofill_enable.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\yubikey.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_notification.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_notification_icon.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_step1.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_step2.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\autofill_enable.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\autofill_use.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\autofill_use.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\logo.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\yubikey.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_notification.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_notification_icon.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_step1.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_step2.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\autofill_enable.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\lock.png" />
</ItemGroup>
@@ -422,21 +334,6 @@
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxxhdpi\id.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\icon.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\icon.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\icon.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\icon.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxxhdpi\icon.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\pencil.png" />
</ItemGroup>
@@ -554,5 +451,11 @@
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\xml\app_restrictions.xml">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project>

View File

@@ -16,73 +16,104 @@ namespace Bit.Droid.Autofill
{
private static int _pendingIntentId = 0;
// These browser work natively with the autofill framework
// These browsers work natively with the Autofill Framework
//
// Be sure:
// - to keep these entries sorted alphabetically and
//
// - ... to keep this list in sync with values in AccessibilityHelpers.SupportedBrowsers [Section A], too.
public static HashSet<string> TrustedBrowsers = new HashSet<string>
{
"com.duckduckgo.mobile.android",
"org.mozilla.focus",
"org.mozilla.klar",
"com.duckduckgo.mobile.android",
};
// These browsers work using the compatibility shim for the autofill framework
// These browsers work using the compatibility shim for the Autofill Framework
//
// Be sure:
// - to keep these entries sorted alphabetically,
// - to keep this list in sync with values in Resources/xml/autofillservice.xml, and
//
// - ... to keep this list in sync with values in AccessibilityHelpers.SupportedBrowsers [Section A], too.
public static HashSet<string> CompatBrowsers = new HashSet<string>
{
"org.mozilla.firefox",
"org.mozilla.firefox_beta",
"com.microsoft.emmx",
"com.android.chrome",
"com.chrome.beta",
"com.amazon.cloud9",
"com.android.browser",
"com.android.chrome",
"com.avast.android.secure.browser",
"com.avg.android.secure.browser",
"com.brave.browser",
"com.brave.browser_beta",
"com.brave.browser_default",
"com.brave.browser_dev",
"com.brave.browser_nightly",
"com.chrome.beta",
"com.chrome.canary",
"com.chrome.dev",
"com.ecosia.android",
"com.google.android.apps.chrome",
"com.google.android.apps.chrome_dev",
"com.kiwibrowser.browser",
"com.microsoft.emmx",
"com.naver.whale",
"com.opera.browser",
"com.opera.browser.beta",
"com.opera.mini.native",
"com.chrome.dev",
"com.chrome.canary",
"com.google.android.apps.chrome",
"com.google.android.apps.chrome_dev",
"com.yandex.browser",
"com.opera.mini.native.beta",
"com.opera.touch",
"com.qwant.liberty",
"com.sec.android.app.sbrowser",
"com.sec.android.app.sbrowser.beta",
"org.codeaurora.swe.browser",
"com.amazon.cloud9",
"com.stoutner.privacybrowser.free",
"com.stoutner.privacybrowser.standard",
"com.vivaldi.browser",
"com.vivaldi.browser.snapshot",
"com.vivaldi.browser.sopranos",
"com.yandex.browser",
"mark.via.gp",
"org.adblockplus.browser",
"org.adblockplus.browser.beta",
"org.bromite.bromite",
"org.bromite.chromium",
"org.chromium.chrome",
"com.kiwibrowser.browser",
"com.ecosia.android",
"com.opera.mini.native.beta",
"org.mozilla.fennec_aurora",
"org.mozilla.fennec_fdroid",
"com.qwant.liberty",
"com.opera.touch",
"org.codeaurora.swe.browser",
"org.gnu.icecat",
"org.mozilla.fenix",
"org.mozilla.fenix.nightly",
"org.mozilla.fennec_aurora",
"org.mozilla.fennec_fdroid",
"org.mozilla.firefox",
"org.mozilla.firefox_beta",
"org.mozilla.reference.browser",
"org.mozilla.rocket",
"org.torproject.torbrowser",
"org.torproject.torbrowser_alpha",
"org.ungoogled.chromium",
};
// The URLs are blacklisted from autofilling
public static HashSet<string> BlacklistedUris = new HashSet<string>
{
"androidapp://android",
"androidapp://com.android.settings",
"androidapp://com.x8bit.bitwarden",
"androidapp://com.oneplus.applocker",
};
public static async Task<List<FilledItem>> GetFillItemsAsync(Parser parser, ICipherService cipherService)
{
if(parser.FieldCollection.FillableForLogin)
if (parser.FieldCollection.FillableForLogin)
{
var ciphers = await cipherService.GetAllDecryptedByUrlAsync(parser.Uri);
if(ciphers.Item1.Any() || ciphers.Item2.Any())
if (ciphers.Item1.Any() || ciphers.Item2.Any())
{
var allCiphers = ciphers.Item1.ToList();
allCiphers.AddRange(ciphers.Item2.ToList());
return allCiphers.Select(c => new FilledItem(c)).ToList();
}
}
else if(parser.FieldCollection.FillableForCard)
else if (parser.FieldCollection.FillableForCard)
{
var ciphers = await cipherService.GetAllDecryptedAsync();
return ciphers.Where(c => c.Type == CipherType.Card).Select(c => new FilledItem(c)).ToList();
@@ -93,12 +124,12 @@ namespace Bit.Droid.Autofill
public static FillResponse BuildFillResponse(Parser parser, List<FilledItem> items, bool locked)
{
var responseBuilder = new FillResponse.Builder();
if(items != null && items.Count > 0)
if (items != null && items.Count > 0)
{
foreach(var item in items)
foreach (var item in items)
{
var dataset = BuildDataset(parser.ApplicationContext, parser.FieldCollection, item);
if(dataset != null)
if (dataset != null)
{
responseBuilder.AddDataset(dataset);
}
@@ -115,7 +146,7 @@ namespace Bit.Droid.Autofill
{
var datasetBuilder = new Dataset.Builder(
BuildListView(filledItem.Name, filledItem.Subtitle, filledItem.Icon, context));
if(filledItem.ApplyToFields(fields, datasetBuilder))
if (filledItem.ApplyToFields(fields, datasetBuilder))
{
return datasetBuilder.Build();
}
@@ -126,15 +157,15 @@ namespace Bit.Droid.Autofill
{
var intent = new Intent(context, typeof(MainActivity));
intent.PutExtra("autofillFramework", true);
if(fields.FillableForLogin)
if (fields.FillableForLogin)
{
intent.PutExtra("autofillFrameworkFillType", (int)CipherType.Login);
}
else if(fields.FillableForCard)
else if (fields.FillableForCard)
{
intent.PutExtra("autofillFrameworkFillType", (int)CipherType.Card);
}
else if(fields.FillableForIdentity)
else if (fields.FillableForIdentity)
{
intent.PutExtra("autofillFrameworkFillType", (int)CipherType.Identity);
}
@@ -156,7 +187,7 @@ namespace Bit.Droid.Autofill
datasetBuilder.SetAuthentication(pendingIntent.IntentSender);
// Dataset must have a value set. We will reset this in the main activity when the real item is chosen.
foreach(var autofillId in fields.AutofillIds)
foreach (var autofillId in fields.AutofillIds)
{
datasetBuilder.SetValue(autofillId, AutofillValue.ForText("PLACEHOLDER"));
}
@@ -178,24 +209,24 @@ namespace Bit.Droid.Autofill
// Docs state that password fields cannot be reliably saved in Compat mode since they will show as
// masked values.
var compatBrowser = CompatBrowsers.Contains(parser.PackageName);
if(compatBrowser && fields.SaveType == SaveDataType.Password)
if (compatBrowser && fields.SaveType == SaveDataType.Password)
{
return;
}
var requiredIds = fields.GetRequiredSaveFields();
if(fields.SaveType == SaveDataType.Generic || requiredIds.Length == 0)
if (fields.SaveType == SaveDataType.Generic || requiredIds.Length == 0)
{
return;
}
var saveBuilder = new SaveInfo.Builder(fields.SaveType, requiredIds);
var optionalIds = fields.GetOptionalSaveIds();
if(optionalIds.Length > 0)
if (optionalIds.Length > 0)
{
saveBuilder.SetOptionalIds(optionalIds);
}
if(compatBrowser)
if (compatBrowser)
{
saveBuilder.SetFlags(SaveFlags.SaveOnAllViewsInvisible);
}

View File

@@ -21,14 +21,14 @@ namespace Bit.Droid.Autofill
public class AutofillService : Android.Service.Autofill.AutofillService
{
private ICipherService _cipherService;
private ILockService _lockService;
private IVaultTimeoutService _vaultTimeoutService;
private IStorageService _storageService;
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
FillCallback callback)
{
var structure = request.FillContexts?.LastOrDefault()?.Structure;
if(structure == null)
if (structure == null)
{
return;
}
@@ -36,27 +36,27 @@ namespace Bit.Droid.Autofill
var parser = new Parser(structure, ApplicationContext);
parser.Parse();
if(_storageService == null)
if (_storageService == null)
{
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
}
var shouldAutofill = await parser.ShouldAutofillAsync(_storageService);
if(!shouldAutofill)
if (!shouldAutofill)
{
return;
}
if(_lockService == null)
if (_vaultTimeoutService == null)
{
_lockService = ServiceContainer.Resolve<ILockService>("lockService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
}
List<FilledItem> items = null;
var locked = await _lockService.IsLockedAsync();
if(!locked)
var locked = await _vaultTimeoutService.IsLockedAsync();
if (!locked)
{
if(_cipherService == null)
if (_cipherService == null)
{
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
}
@@ -71,18 +71,18 @@ namespace Bit.Droid.Autofill
public async override void OnSaveRequest(SaveRequest request, SaveCallback callback)
{
var structure = request.FillContexts?.LastOrDefault()?.Structure;
if(structure == null)
if (structure == null)
{
return;
}
if(_storageService == null)
if (_storageService == null)
{
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
}
var disableSavePrompt = await _storageService.GetAsync<bool?>(Constants.AutofillDisableSavePromptKey);
if(disableSavePrompt.GetValueOrDefault())
if (disableSavePrompt.GetValueOrDefault())
{
return;
}
@@ -91,7 +91,7 @@ namespace Bit.Droid.Autofill
parser.Parse();
var savedItem = parser.FieldCollection.GetSavedItem();
if(savedItem == null)
if (savedItem == null)
{
Toast.MakeText(this, "Unable to save this form.", ToastLength.Short).Show();
return;
@@ -102,7 +102,7 @@ namespace Bit.Droid.Autofill
intent.PutExtra("autofillFramework", true);
intent.PutExtra("autofillFrameworkSave", true);
intent.PutExtra("autofillFrameworkType", (int)savedItem.Type);
switch(savedItem.Type)
switch (savedItem.Type)
{
case CipherType.Login:
intent.PutExtra("autofillFrameworkName", parser.Uri

View File

@@ -31,26 +31,26 @@ namespace Bit.Droid.Autofill
HtmlInfo = node.HtmlInfo;
Node = node;
if(node.AutofillValue != null)
if (node.AutofillValue != null)
{
if(node.AutofillValue.IsList)
if (node.AutofillValue.IsList)
{
var autofillOptions = node.GetAutofillOptions();
if(autofillOptions != null && autofillOptions.Length > 0)
if (autofillOptions != null && autofillOptions.Length > 0)
{
ListValue = node.AutofillValue.ListValue;
TextValue = autofillOptions[node.AutofillValue.ListValue];
}
}
else if(node.AutofillValue.IsDate)
else if (node.AutofillValue.IsDate)
{
DateValue = node.AutofillValue.DateValue;
}
else if(node.AutofillValue.IsText)
else if (node.AutofillValue.IsText)
{
TextValue = node.AutofillValue.TextValue;
}
else if(node.AutofillValue.IsToggle)
else if (node.AutofillValue.IsToggle)
{
ToggleValue = node.AutofillValue.ToggleValue;
}
@@ -93,20 +93,20 @@ namespace Bit.Droid.Autofill
public override bool Equals(object obj)
{
if(this == obj)
if (this == obj)
{
return true;
}
if(obj == null || GetType() != obj.GetType())
if (obj == null || GetType() != obj.GetType())
{
return false;
}
var field = obj as Field;
if(TextValue != null ? !TextValue.Equals(field.TextValue) : field.TextValue != null)
if (TextValue != null ? !TextValue.Equals(field.TextValue) : field.TextValue != null)
{
return false;
}
if(DateValue != null ? !DateValue.Equals(field.DateValue) : field.DateValue != null)
if (DateValue != null ? !DateValue.Equals(field.DateValue) : field.DateValue != null)
{
return false;
}
@@ -128,7 +128,7 @@ namespace Bit.Droid.Autofill
private static bool IsValidHint(string hint)
{
switch(hint)
switch (hint)
{
case View.AutofillHintCreditCardExpirationDate:
case View.AutofillHintCreditCardExpirationDay:
@@ -152,14 +152,14 @@ namespace Bit.Droid.Autofill
private void UpdateSaveTypeFromHints()
{
SaveType = SaveDataType.Generic;
if(_hints == null)
if (_hints == null)
{
return;
}
foreach(var hint in _hints)
foreach (var hint in _hints)
{
switch(hint)
switch (hint)
{
case View.AutofillHintCreditCardExpirationDate:
case View.AutofillHintCreditCardExpirationDay:

View File

@@ -19,11 +19,11 @@ namespace Bit.Droid.Autofill
{
get
{
if(FillableForLogin)
if (FillableForLogin)
{
return SaveDataType.Password;
}
else if(FillableForCard)
else if (FillableForCard)
{
return SaveDataType.CreditCard;
}
@@ -43,14 +43,14 @@ namespace Bit.Droid.Autofill
{
get
{
if(_passwordFields != null)
if (_passwordFields != null)
{
return _passwordFields;
}
if(Hints.Any())
if (Hints.Any())
{
_passwordFields = new List<Field>();
if(HintToFieldsMap.ContainsKey(View.AutofillHintPassword))
if (HintToFieldsMap.ContainsKey(View.AutofillHintPassword))
{
_passwordFields.AddRange(HintToFieldsMap[View.AutofillHintPassword]);
}
@@ -58,7 +58,7 @@ namespace Bit.Droid.Autofill
else
{
_passwordFields = Fields.Where(f => FieldIsPassword(f)).ToList();
if(!_passwordFields.Any())
if (!_passwordFields.Any())
{
_passwordFields = Fields.Where(f => FieldHasPasswordTerms(f)).ToList();
}
@@ -71,29 +71,29 @@ namespace Bit.Droid.Autofill
{
get
{
if(_usernameFields != null)
if (_usernameFields != null)
{
return _usernameFields;
}
_usernameFields = new List<Field>();
if(Hints.Any())
if (Hints.Any())
{
if(HintToFieldsMap.ContainsKey(View.AutofillHintEmailAddress))
if (HintToFieldsMap.ContainsKey(View.AutofillHintEmailAddress))
{
_usernameFields.AddRange(HintToFieldsMap[View.AutofillHintEmailAddress]);
}
if(HintToFieldsMap.ContainsKey(View.AutofillHintUsername))
if (HintToFieldsMap.ContainsKey(View.AutofillHintUsername))
{
_usernameFields.AddRange(HintToFieldsMap[View.AutofillHintUsername]);
}
}
else
{
foreach(var passwordField in PasswordFields)
foreach (var passwordField in PasswordFields)
{
var usernameField = Fields.TakeWhile(f => f.AutofillId != passwordField.AutofillId)
.LastOrDefault();
if(usernameField != null)
if (usernameField != null)
{
_usernameFields.Add(usernameField);
}
@@ -127,7 +127,7 @@ namespace Bit.Droid.Autofill
public void Add(Field field)
{
if(field == null || FieldTrackingIds.Contains(field.TrackingId))
if (field == null || FieldTrackingIds.Contains(field.TrackingId))
{
return;
}
@@ -137,16 +137,16 @@ namespace Bit.Droid.Autofill
Fields.Add(field);
AutofillIds.Add(field.AutofillId);
if(field.Hints != null)
if (field.Hints != null)
{
foreach(var hint in field.Hints)
foreach (var hint in field.Hints)
{
Hints.Add(hint);
if(field.Focused)
if (field.Focused)
{
FocusedHints.Add(hint);
}
if(!HintToFieldsMap.ContainsKey(hint))
if (!HintToFieldsMap.ContainsKey(hint))
{
HintToFieldsMap.Add(hint, new List<Field>());
}
@@ -157,10 +157,10 @@ namespace Bit.Droid.Autofill
public SavedItem GetSavedItem()
{
if(SaveType == SaveDataType.Password)
if (SaveType == SaveDataType.Password)
{
var passwordField = PasswordFields.FirstOrDefault(f => !string.IsNullOrWhiteSpace(f.TextValue));
if(passwordField == null)
if (passwordField == null)
{
return null;
}
@@ -178,7 +178,7 @@ namespace Bit.Droid.Autofill
savedItem.Login.Username = GetFieldValue(usernameField);
return savedItem;
}
else if(SaveType == SaveDataType.CreditCard)
else if (SaveType == SaveDataType.CreditCard)
{
var savedItem = new SavedItem
{
@@ -199,26 +199,26 @@ namespace Bit.Droid.Autofill
public AutofillId[] GetOptionalSaveIds()
{
if(SaveType == SaveDataType.Password)
if (SaveType == SaveDataType.Password)
{
return UsernameFields.Select(f => f.AutofillId).ToArray();
}
else if(SaveType == SaveDataType.CreditCard)
else if (SaveType == SaveDataType.CreditCard)
{
var fieldList = new List<Field>();
if(HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardSecurityCode))
if (HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardSecurityCode))
{
fieldList.AddRange(HintToFieldsMap[View.AutofillHintCreditCardSecurityCode]);
}
if(HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardExpirationYear))
if (HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardExpirationYear))
{
fieldList.AddRange(HintToFieldsMap[View.AutofillHintCreditCardExpirationYear]);
}
if(HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardExpirationMonth))
if (HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardExpirationMonth))
{
fieldList.AddRange(HintToFieldsMap[View.AutofillHintCreditCardExpirationMonth]);
}
if(HintToFieldsMap.ContainsKey(View.AutofillHintName))
if (HintToFieldsMap.ContainsKey(View.AutofillHintName))
{
fieldList.AddRange(HintToFieldsMap[View.AutofillHintName]);
}
@@ -229,11 +229,11 @@ namespace Bit.Droid.Autofill
public AutofillId[] GetRequiredSaveFields()
{
if(SaveType == SaveDataType.Password)
if (SaveType == SaveDataType.Password)
{
return PasswordFields.Select(f => f.AutofillId).ToArray();
}
else if(SaveType == SaveDataType.CreditCard && HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardNumber))
else if (SaveType == SaveDataType.CreditCard && HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardNumber))
{
return HintToFieldsMap[View.AutofillHintCreditCardNumber].Select(f => f.AutofillId).ToArray();
}
@@ -247,12 +247,12 @@ namespace Bit.Droid.Autofill
private string GetFieldValue(string hint, bool monthValue = false)
{
if(HintToFieldsMap.ContainsKey(hint))
if (HintToFieldsMap.ContainsKey(hint))
{
foreach(var field in HintToFieldsMap[hint])
foreach (var field in HintToFieldsMap[hint])
{
var val = GetFieldValue(field, monthValue);
if(!string.IsNullOrWhiteSpace(val))
if (!string.IsNullOrWhiteSpace(val))
{
return val;
}
@@ -263,30 +263,30 @@ namespace Bit.Droid.Autofill
private string GetFieldValue(Field field, bool monthValue = false)
{
if(field == null)
if (field == null)
{
return null;
}
if(!string.IsNullOrWhiteSpace(field.TextValue))
if (!string.IsNullOrWhiteSpace(field.TextValue))
{
if(field.AutofillType == AutofillType.List && field.ListValue.HasValue && monthValue)
if (field.AutofillType == AutofillType.List && field.ListValue.HasValue && monthValue)
{
if(field.AutofillOptions.Count == 13)
if (field.AutofillOptions.Count == 13)
{
return field.ListValue.ToString();
}
else if(field.AutofillOptions.Count == 12)
else if (field.AutofillOptions.Count == 12)
{
return (field.ListValue + 1).ToString();
}
}
return field.TextValue;
}
else if(field.DateValue.HasValue)
else if (field.DateValue.HasValue)
{
return field.DateValue.Value.ToString();
}
else if(field.ToggleValue.HasValue)
else if (field.ToggleValue.HasValue)
{
return field.ToggleValue.Value.ToString();
}
@@ -300,20 +300,20 @@ namespace Bit.Droid.Autofill
f.InputType.HasFlag(InputTypes.TextVariationWebPassword);
// For whatever reason, multi-line input types are coming through with TextVariationPassword flags
if(inputTypePassword && f.InputType.HasFlag(InputTypes.TextVariationPassword) &&
if (inputTypePassword && f.InputType.HasFlag(InputTypes.TextVariationPassword) &&
f.InputType.HasFlag(InputTypes.TextFlagMultiLine))
{
inputTypePassword = false;
}
if(!inputTypePassword && f.HtmlInfo != null && f.HtmlInfo.Tag == "input" &&
if (!inputTypePassword && f.HtmlInfo != null && f.HtmlInfo.Tag == "input" &&
(f.HtmlInfo.Attributes?.Any() ?? false))
{
foreach(var a in f.HtmlInfo.Attributes)
foreach (var a in f.HtmlInfo.Attributes)
{
var key = a.First as Java.Lang.String;
var val = a.Second as Java.Lang.String;
if(key != null && val != null && key.ToString() == "type" && val.ToString() == "password")
if (key != null && val != null && key.ToString() == "type" && val.ToString() == "password")
{
return true;
}
@@ -331,7 +331,7 @@ namespace Bit.Droid.Autofill
private bool ValueContainsAnyTerms(string value, HashSet<string> terms)
{
if(string.IsNullOrWhiteSpace(value))
if (string.IsNullOrWhiteSpace(value))
{
return false;
}

View File

@@ -27,7 +27,7 @@ namespace Bit.Droid.Autofill
Type = cipher.Type;
Subtitle = cipher.SubTitle;
switch(Type)
switch (Type)
{
case CipherType.Login:
Icon = Resource.Drawable.login;
@@ -62,32 +62,32 @@ namespace Bit.Droid.Autofill
public bool ApplyToFields(FieldCollection fieldCollection, Dataset.Builder datasetBuilder)
{
if(!fieldCollection?.Fields.Any() ?? true)
if (!fieldCollection?.Fields.Any() ?? true)
{
return false;
}
var setValues = false;
if(Type == CipherType.Login)
if (Type == CipherType.Login)
{
if(fieldCollection.PasswordFields.Any() && !string.IsNullOrWhiteSpace(_password))
if (fieldCollection.PasswordFields.Any() && !string.IsNullOrWhiteSpace(_password))
{
foreach(var f in fieldCollection.PasswordFields)
foreach (var f in fieldCollection.PasswordFields)
{
var val = ApplyValue(f, _password);
if(val != null)
if (val != null)
{
setValues = true;
datasetBuilder.SetValue(f.AutofillId, val);
}
}
}
if(fieldCollection.UsernameFields.Any() && !string.IsNullOrWhiteSpace(Subtitle))
if (fieldCollection.UsernameFields.Any() && !string.IsNullOrWhiteSpace(Subtitle))
{
foreach(var f in fieldCollection.UsernameFields)
foreach (var f in fieldCollection.UsernameFields)
{
var val = ApplyValue(f, Subtitle);
if(val != null)
if (val != null)
{
setValues = true;
datasetBuilder.SetValue(f.AutofillId, val);
@@ -95,59 +95,59 @@ namespace Bit.Droid.Autofill
}
}
}
else if(Type == CipherType.Card)
else if (Type == CipherType.Card)
{
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintCreditCardNumber,
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintCreditCardNumber,
_cardNumber))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintCreditCardSecurityCode,
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintCreditCardSecurityCode,
_cardCode))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection,
if (ApplyValue(datasetBuilder, fieldCollection,
Android.Views.View.AutofillHintCreditCardExpirationMonth, _cardExpMonth, true))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintCreditCardExpirationYear,
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintCreditCardExpirationYear,
_cardExpYear))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintName, _cardName))
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintName, _cardName))
{
setValues = true;
}
}
else if(Type == CipherType.Identity)
else if (Type == CipherType.Identity)
{
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintPhone, _idPhone))
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintPhone, _idPhone))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintEmailAddress, _idEmail))
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintEmailAddress, _idEmail))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintUsername,
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintUsername,
_idUsername))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintPostalAddress,
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintPostalAddress,
_idAddress))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintPostalCode,
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintPostalCode,
_idPostalCode))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintName, Subtitle))
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintName, Subtitle))
{
setValues = true;
}
@@ -159,12 +159,12 @@ namespace Bit.Droid.Autofill
string hint, string value, bool monthValue = false)
{
bool setValues = false;
if(fieldCollection.HintToFieldsMap.ContainsKey(hint) && !string.IsNullOrWhiteSpace(value))
if (fieldCollection.HintToFieldsMap.ContainsKey(hint) && !string.IsNullOrWhiteSpace(value))
{
foreach(var f in fieldCollection.HintToFieldsMap[hint])
foreach (var f in fieldCollection.HintToFieldsMap[hint])
{
var val = ApplyValue(f, value, monthValue);
if(val != null)
if (val != null)
{
setValues = true;
builder.SetValue(f.AutofillId, val);
@@ -176,31 +176,31 @@ namespace Bit.Droid.Autofill
private static AutofillValue ApplyValue(Field field, string value, bool monthValue = false)
{
switch(field.AutofillType)
switch (field.AutofillType)
{
case AutofillType.Date:
if(long.TryParse(value, out long dateValue))
if (long.TryParse(value, out long dateValue))
{
return AutofillValue.ForDate(dateValue);
}
break;
case AutofillType.List:
if(field.AutofillOptions != null)
if (field.AutofillOptions != null)
{
if(monthValue && int.TryParse(value, out int monthIndex))
if (monthValue && int.TryParse(value, out int monthIndex))
{
if(field.AutofillOptions.Count == 13)
if (field.AutofillOptions.Count == 13)
{
return AutofillValue.ForList(monthIndex);
}
else if(field.AutofillOptions.Count >= monthIndex)
else if (field.AutofillOptions.Count >= monthIndex)
{
return AutofillValue.ForList(monthIndex - 1);
}
}
for(var i = 0; i < field.AutofillOptions.Count; i++)
for (var i = 0; i < field.AutofillOptions.Count; i++)
{
if(field.AutofillOptions[i].Equals(value))
if (field.AutofillOptions[i].Equals(value))
{
return AutofillValue.ForList(i);
}
@@ -210,7 +210,7 @@ namespace Bit.Droid.Autofill
case AutofillType.Text:
return AutofillValue.ForText(value);
case AutofillType.Toggle:
if(bool.TryParse(value, out bool toggleValue))
if (bool.TryParse(value, out bool toggleValue))
{
return AutofillValue.ForToggle(toggleValue);
}

View File

@@ -33,16 +33,16 @@ namespace Bit.Droid.Autofill
{
get
{
if(!string.IsNullOrWhiteSpace(_uri))
if (!string.IsNullOrWhiteSpace(_uri))
{
return _uri;
}
var websiteNull = string.IsNullOrWhiteSpace(Website);
if(websiteNull && string.IsNullOrWhiteSpace(PackageName))
if (websiteNull && string.IsNullOrWhiteSpace(PackageName))
{
_uri = null;
}
else if(!websiteNull)
else if (!websiteNull)
{
_uri = Website;
}
@@ -59,7 +59,7 @@ namespace Bit.Droid.Autofill
get => _packageName;
set
{
if(string.IsNullOrWhiteSpace(value))
if (string.IsNullOrWhiteSpace(value))
{
_packageName = _uri = null;
}
@@ -72,7 +72,7 @@ namespace Bit.Droid.Autofill
get => _website;
set
{
if(string.IsNullOrWhiteSpace(value))
if (string.IsNullOrWhiteSpace(value))
{
_website = _uri = null;
}
@@ -84,10 +84,10 @@ namespace Bit.Droid.Autofill
{
var fillable = !string.IsNullOrWhiteSpace(Uri) && !AutofillHelpers.BlacklistedUris.Contains(Uri) &&
FieldCollection != null && FieldCollection.Fillable;
if(fillable)
if (fillable)
{
var blacklistedUris = await storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
if(blacklistedUris != null && blacklistedUris.Count > 0)
if (blacklistedUris != null && blacklistedUris.Count > 0)
{
fillable = !new HashSet<string>(blacklistedUris).Contains(Uri);
}
@@ -98,20 +98,20 @@ namespace Bit.Droid.Autofill
public void Parse()
{
string titlePackageId = null;
for(var i = 0; i < _structure.WindowNodeCount; i++)
for (var i = 0; i < _structure.WindowNodeCount; i++)
{
var node = _structure.GetWindowNodeAt(i);
if(i == 0)
if (i == 0)
{
titlePackageId = GetTitlePackageId(node);
}
ParseNode(node.RootViewNode);
}
if(string.IsNullOrWhiteSpace(PackageName) && string.IsNullOrWhiteSpace(Website))
if (string.IsNullOrWhiteSpace(PackageName) && string.IsNullOrWhiteSpace(Website))
{
PackageName = titlePackageId;
}
if(!AutofillHelpers.TrustedBrowsers.Contains(PackageName) &&
if (!AutofillHelpers.TrustedBrowsers.Contains(PackageName) &&
!AutofillHelpers.CompatBrowsers.Contains(PackageName))
{
Website = null;
@@ -123,7 +123,7 @@ namespace Bit.Droid.Autofill
SetPackageAndDomain(node);
var hints = node.GetAutofillHints();
var isEditText = node.ClassName == "android.widget.EditText" || node?.HtmlInfo?.Tag == "input";
if(isEditText || (hints?.Length ?? 0) > 0)
if (isEditText || (hints?.Length ?? 0) > 0)
{
FieldCollection.Add(new Field(node));
}
@@ -132,7 +132,7 @@ namespace Bit.Droid.Autofill
FieldCollection.IgnoreAutofillIds.Add(node.AutofillId);
}
for(var i = 0; i < node.ChildCount; i++)
for (var i = 0; i < node.ChildCount; i++)
{
ParseNode(node.GetChildAt(i));
}
@@ -140,15 +140,15 @@ namespace Bit.Droid.Autofill
private void SetPackageAndDomain(ViewNode node)
{
if(string.IsNullOrWhiteSpace(PackageName) && !string.IsNullOrWhiteSpace(node.IdPackage) &&
if (string.IsNullOrWhiteSpace(PackageName) && !string.IsNullOrWhiteSpace(node.IdPackage) &&
!_excludedPackageIds.Contains(node.IdPackage))
{
PackageName = node.IdPackage;
}
if(string.IsNullOrWhiteSpace(Website) && !string.IsNullOrWhiteSpace(node.WebDomain))
if (string.IsNullOrWhiteSpace(Website) && !string.IsNullOrWhiteSpace(node.WebDomain))
{
var scheme = "http";
if((int)Build.VERSION.SdkInt >= 28)
if ((int)Build.VERSION.SdkInt >= 28)
{
scheme = node.WebScheme;
}
@@ -158,13 +158,13 @@ namespace Bit.Droid.Autofill
private string GetTitlePackageId(WindowNode node)
{
if(node != null && !string.IsNullOrWhiteSpace(node.Title))
if (node != null && !string.IsNullOrWhiteSpace(node.Title))
{
var slashPosition = node.Title.IndexOf('/');
if(slashPosition > -1)
if (slashPosition > -1)
{
var packageId = node.Title.Substring(0, slashPosition);
if(packageId.Contains("."))
if (packageId.Contains("."))
{
return packageId;
}

View File

@@ -0,0 +1,30 @@
using Android.Graphics.Drawables;
using Bit.App.Utilities;
using Bit.Droid.Effects;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportEffect(typeof(FabShadowEffect), "FabShadowEffect")]
namespace Bit.Droid.Effects
{
public class FabShadowEffect : PlatformEffect
{
protected override void OnAttached ()
{
if (Control is Android.Widget.Button button)
{
var gd = new GradientDrawable();
gd.SetColor(ThemeManager.GetResourceColor("FabColor").ToAndroid());
gd.SetCornerRadius(100);
button.SetBackground(gd);
button.Elevation = 6;
button.TranslationZ = 20;
}
}
protected override void OnDetached ()
{
}
}
}

View File

@@ -1,7 +1,4 @@
using Android.Support.Design.BottomNavigation;
using Android.Support.Design.Widget;
using Android.Views;
using Android.Widget;
using Android.Widget;
using Bit.Droid.Effects;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
@@ -13,7 +10,7 @@ namespace Bit.Droid.Effects
{
protected override void OnAttached()
{
if(Element is Label label && Control is TextView textView)
if (Element is Label label && Control is TextView textView)
{
textView.SetTextSize(Android.Util.ComplexUnitType.Pt, (float)label.FontSize);
}
@@ -23,4 +20,4 @@ namespace Bit.Droid.Effects
{
}
}
}
}

View File

@@ -1,7 +1,4 @@
using Android.Support.Design.BottomNavigation;
using Android.Support.Design.Widget;
using Android.Views;
using Android.Widget;
using Android.Widget;
using Bit.Droid.Effects;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
@@ -13,7 +10,7 @@ namespace Bit.Droid.Effects
{
protected override void OnAttached()
{
if(Control is TextView textView)
if (Control is TextView textView)
{
textView.SetTextIsSelectable(true);
}
@@ -23,4 +20,4 @@ namespace Bit.Droid.Effects
{
}
}
}
}

View File

@@ -1,7 +1,6 @@
using Android.Support.Design.BottomNavigation;
using Android.Support.Design.Widget;
using Android.Views;
using Android.Views;
using Bit.Droid.Effects;
using Google.Android.Material.BottomNavigation;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
@@ -13,11 +12,11 @@ namespace Bit.Droid.Effects
{
protected override void OnAttached()
{
if(!(Container.GetChildAt(0) is ViewGroup layout))
if (!(Container.GetChildAt(0) is ViewGroup layout))
{
return;
}
if(!(layout.GetChildAt(1) is BottomNavigationView bottomNavigationView))
if (!(layout.GetChildAt(1) is BottomNavigationView bottomNavigationView))
{
return;
}
@@ -28,4 +27,4 @@ namespace Bit.Droid.Effects
{
}
}
}
}

View File

@@ -17,7 +17,7 @@ using Bit.Core.Enums;
using Android.Nfc;
using Bit.App.Utilities;
using System.Threading.Tasks;
using Android.Support.V4.Content;
using AndroidX.Core.Content;
namespace Bit.Droid
{
@@ -37,7 +37,7 @@ namespace Bit.Droid
private IAppIdService _appIdService;
private IStorageService _storageService;
private IEventService _eventService;
private PendingIntent _lockAlarmPendingIntent;
private PendingIntent _vaultTimeoutAlarmPendingIntent;
private PendingIntent _clearClipboardPendingIntent;
private PendingIntent _eventUploadPendingIntent;
private AppOptions _appOptions;
@@ -51,7 +51,7 @@ namespace Bit.Droid
_eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent,
PendingIntentFlags.UpdateCurrent);
var alarmIntent = new Intent(this, typeof(LockAlarmReceiver));
_lockAlarmPendingIntent = PendingIntent.GetBroadcast(this, 0, alarmIntent,
_vaultTimeoutAlarmPendingIntent = PendingIntent.GetBroadcast(this, 0, alarmIntent,
PendingIntentFlags.UpdateCurrent);
var clearClipboardIntent = new Intent(this, typeof(ClearClipboardAlarmReceiver));
_clearClipboardPendingIntent = PendingIntent.GetBroadcast(this, 0, clearClipboardIntent,
@@ -73,14 +73,14 @@ namespace Bit.Droid
UpdateTheme(ThemeManager.GetTheme(true));
base.OnCreate(savedInstanceState);
if(!CoreHelpers.InDebugMode())
if (!CoreHelpers.InDebugMode())
{
Window.AddFlags(Android.Views.WindowManagerFlags.Secure);
}
#if !FDROID
var hockeyAppListener = new HockeyAppCrashManagerListener(_appIdService, _userService);
var hockeyAppTask = hockeyAppListener.InitAsync(this);
var appCenterHelper = new AppCenterHelper(_appIdService, _userService);
var appCenterTask = appCenterHelper.InitAsync();
#endif
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
@@ -90,44 +90,44 @@ namespace Bit.Droid
_broadcasterService.Subscribe(_activityKey, (message) =>
{
if(message.Command == "scheduleLockTimer")
if (message.Command == "scheduleVaultTimeoutTimer")
{
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
var lockOptionMinutes = (int)message.Data;
var lockOptionMs = lockOptionMinutes * 60000;
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + lockOptionMs + 10;
alarmManager.Set(AlarmType.RtcWakeup, triggerMs, _lockAlarmPendingIntent);
var vaultTimeoutMinutes = (int)message.Data;
var vaultTimeoutMs = vaultTimeoutMinutes * 60000;
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + vaultTimeoutMs + 10;
alarmManager.Set(AlarmType.RtcWakeup, triggerMs, _vaultTimeoutAlarmPendingIntent);
}
else if(message.Command == "cancelLockTimer")
else if (message.Command == "cancelVaultTimeoutTimer")
{
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
alarmManager.Cancel(_lockAlarmPendingIntent);
alarmManager.Cancel(_vaultTimeoutAlarmPendingIntent);
}
else if(message.Command == "startEventTimer")
else if (message.Command == "startEventTimer")
{
StartEventAlarm();
}
else if(message.Command == "stopEventTimer")
else if (message.Command == "stopEventTimer")
{
var task = StopEventAlarmAsync();
}
else if(message.Command == "finishMainActivity")
else if (message.Command == "finishMainActivity")
{
Xamarin.Forms.Device.BeginInvokeOnMainThread(() => Finish());
}
else if(message.Command == "listenYubiKeyOTP")
else if (message.Command == "listenYubiKeyOTP")
{
ListenYubiKey((bool)message.Data);
}
else if(message.Command == "updatedTheme")
else if (message.Command == "updatedTheme")
{
RestartApp();
}
else if(message.Command == "exit")
else if (message.Command == "exit")
{
ExitApp();
}
else if(message.Command == "copiedToClipboard")
else if (message.Command == "copiedToClipboard")
{
var task = ClearClipboardAlarmAsync(message.Data as Tuple<string, int?, bool>);
}
@@ -143,7 +143,7 @@ namespace Bit.Droid
protected override void OnResume()
{
base.OnResume();
if(_deviceActionService.SupportsNfc())
if (_deviceActionService.SupportsNfc())
{
try
{
@@ -151,23 +151,24 @@ namespace Bit.Droid
}
catch { }
}
var setRestrictions = AndroidHelpers.SetPreconfiguredRestrictionSettingsAsync(this);
}
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
if(intent.GetBooleanExtra("generatorTile", false))
if (intent.GetBooleanExtra("generatorTile", false))
{
_messagingService.Send("popAllAndGoToTabGenerator");
if(_appOptions != null)
if (_appOptions != null)
{
_appOptions.GeneratorTile = true;
}
}
if(intent.GetBooleanExtra("myVaultTile", false))
if (intent.GetBooleanExtra("myVaultTile", false))
{
_messagingService.Send("popAllAndGoToTabMyVault");
if(_appOptions != null)
if (_appOptions != null)
{
_appOptions.MyVaultTile = true;
}
@@ -181,9 +182,9 @@ namespace Bit.Droid
public async override void OnRequestPermissionsResult(int requestCode, string[] permissions,
[GeneratedEnum] Permission[] grantResults)
{
if(requestCode == Constants.SelectFilePermissionRequestCode)
if (requestCode == Constants.SelectFilePermissionRequestCode)
{
if(grantResults.Any(r => r != Permission.Granted))
if (grantResults.Any(r => r != Permission.Granted))
{
_messagingService.Send("selectFileCameraPermissionDenied");
}
@@ -200,11 +201,12 @@ namespace Bit.Droid
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
if(requestCode == Constants.SelectFileRequestCode && resultCode == Result.Ok)
if (resultCode == Result.Ok &&
(requestCode == Constants.SelectFileRequestCode || requestCode == Constants.SaveFileRequestCode))
{
Android.Net.Uri uri = null;
string fileName = null;
if(data != null && data.Data != null)
if (data != null && data.Data != null)
{
uri = data.Data;
fileName = AndroidHelpers.GetFileName(ApplicationContext, uri);
@@ -217,21 +219,29 @@ namespace Bit.Droid
fileName = $"photo_{DateTime.UtcNow.ToString("yyyyMMddHHmmss")}.jpg";
}
if(uri == null)
if (uri == null)
{
return;
}
if (requestCode == Constants.SaveFileRequestCode)
{
_messagingService.Send("selectSaveFileResult",
new Tuple<string, string>(uri.ToString(), fileName));
return;
}
try
{
using(var stream = ContentResolver.OpenInputStream(uri))
using(var memoryStream = new MemoryStream())
using (var stream = ContentResolver.OpenInputStream(uri))
using (var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
_messagingService.Send("selectFileResult",
new Tuple<byte[], string>(memoryStream.ToArray(), fileName ?? "unknown_file_name"));
}
}
catch(Java.IO.FileNotFoundException)
catch (Java.IO.FileNotFoundException)
{
return;
}
@@ -246,12 +256,12 @@ namespace Bit.Droid
private void ListenYubiKey(bool listen)
{
if(!_deviceActionService.SupportsNfc())
if (!_deviceActionService.SupportsNfc())
{
return;
}
var adapter = NfcAdapter.GetDefaultAdapter(this);
if(listen)
if (listen)
{
var intent = new Intent(this, Class);
intent.AddFlags(ActivityFlags.SingleTop);
@@ -288,11 +298,11 @@ namespace Bit.Droid
FromAutofillFramework = Intent.GetBooleanExtra("autofillFramework", false)
};
var fillType = Intent.GetIntExtra("autofillFrameworkFillType", 0);
if(fillType > 0)
if (fillType > 0)
{
options.FillType = (CipherType)fillType;
}
if(Intent.GetBooleanExtra("autofillFrameworkSave", false))
if (Intent.GetBooleanExtra("autofillFrameworkSave", false))
{
options.SaveType = (CipherType)Intent.GetIntExtra("autofillFrameworkType", 0);
options.SaveName = Intent.GetStringExtra("autofillFrameworkName");
@@ -309,12 +319,12 @@ namespace Bit.Droid
private void ParseYubiKey(string data)
{
if(data == null)
if (data == null)
{
return;
}
var otpMatch = _otpPattern.Matcher(data);
if(otpMatch.Matches())
if (otpMatch.Matches())
{
var otp = otpMatch.Group(1);
_messagingService.Send("gotYubiKeyOTP", otp);
@@ -323,15 +333,15 @@ namespace Bit.Droid
private void UpdateTheme(string theme)
{
if(theme == "dark")
if (theme == "dark")
{
SetTheme(Resource.Style.DarkTheme);
}
else if(theme == "black")
else if (theme == "black")
{
SetTheme(Resource.Style.BlackTheme);
}
else if(theme == "nord")
else if (theme == "nord")
{
SetTheme(Resource.Style.NordTheme);
}
@@ -359,24 +369,23 @@ namespace Bit.Droid
private async Task ClearClipboardAlarmAsync(Tuple<string, int?, bool> data)
{
if(data.Item3)
if (data.Item3)
{
return;
}
var clearMs = data.Item2;
if(clearMs == null)
if (clearMs == null)
{
var clearSeconds = await _storageService.GetAsync<int?>(Constants.ClearClipboardKey);
if(clearSeconds != null)
if (clearSeconds != null)
{
clearMs = clearSeconds.Value * 1000;
}
}
if(clearMs == null)
if (clearMs == null)
{
return;
}
StaticStore.LastClipboardValue = data.Item1;
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + clearMs.Value;
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
alarmManager.Set(AlarmType.Rtc, triggerMs, _clearClipboardPendingIntent);

View File

@@ -37,18 +37,14 @@ namespace Bit.Droid
public MainApplication(IntPtr handle, JniHandleOwnership transer)
: base(handle, transer)
{
if(ServiceContainer.RegisteredServices.Count == 0)
if (ServiceContainer.RegisteredServices.Count == 0)
{
RegisterLocalServices();
ServiceContainer.Init();
if(App.Migration.MigrationHelpers.NeedsMigration())
{
var task = App.Migration.MigrationHelpers.PerformMigrationAsync();
Task.Delay(2000).Wait();
}
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
ServiceContainer.Init(deviceActionService.DeviceUserAgent);
}
#if !FDROID
if(Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
if (Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
{
ProviderInstaller.InstallIfNeededAsync(ApplicationContext, this);
}
@@ -73,14 +69,7 @@ namespace Bit.Droid
private void RegisterLocalServices()
{
ServiceContainer.Register<ILogService>("logService", new AndroidLogService());
ServiceContainer.Register("settingsShim", new App.Migration.SettingsShim());
if(App.Migration.MigrationHelpers.NeedsMigration())
{
ServiceContainer.Register<App.Migration.Abstractions.IOldSecureStorageService>(
"oldSecureStorageService", new Migration.AndroidKeyStoreStorageService());
}
Refractored.FabControl.Droid.FloatingActionButtonViewRenderer.Init();
// Note: This might cause a race condition. Investigate more.
Task.Run(() =>
{
@@ -93,7 +82,6 @@ namespace Bit.Droid
ZXing.Net.Mobile.Forms.Android.Platform.Init();
});
CrossFingerprint.SetCurrentActivityResolver(() => CrossCurrentActivity.Current.Activity);
CrossFingerprint.SetDialogFragmentType<CustomFingerprintDialogFragment>();
var preferencesStorage = new PreferencesStorageService(null);
var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
@@ -155,4 +143,4 @@ namespace Bit.Droid
await ServiceContainer.Resolve<IEnvironmentService>("environmentService").SetUrlsFromStorageAsync();
}
}
}
}

View File

@@ -1,373 +0,0 @@
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 Java.Util;
using Javax.Crypto.Spec;
using Android.Preferences;
using Bit.App.Migration;
using Bit.Core.Utilities;
using Bit.App.Migration.Abstractions;
namespace Bit.Droid.Migration
{
public class AndroidKeyStoreStorageService : IOldSecureStorageService
{
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 SettingsShim _settings;
private readonly KeyStore _keyStore;
public AndroidKeyStoreStorageService()
{
_oldAndroid = Build.VERSION.SdkInt < BuildVersionCodes.M;
_rsaMode = _oldAndroid ? "RSA/ECB/PKCS1Padding" : "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
_settings = ServiceContainer.Resolve<SettingsShim>("settingsShim");
_keyStore = KeyStore.GetInstance(AndroidKeyStore);
_keyStore.Load(null);
/*
try
{
GenerateStoreKey(true);
}
catch
{
GenerateStoreKey(false);
}
GenerateAesKey();
*/
}
public bool Contains(string key)
{
return _settings.Contains(string.Format(SettingsFormat, key)) ||
_settings.Contains(string.Format(SettingsFormatV1, 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(formattedKey, null);
if(string.IsNullOrWhiteSpace(cs))
{
return null;
}
var aesKey = GetAesKey();
if(aesKey == null)
{
return null;
}
try
{
return App.Migration.Crypto.AesCbcDecrypt(new App.Migration.Models.CipherString(cs), aesKey);
}
catch
{
Console.WriteLine("Failed to decrypt from secure storage.");
_settings.Remove(formattedKey);
//Utilities.SendCrashEmail(e);
//Utilities.SaveCrashFile(e);
return null;
}
}
public void Store(string key, byte[] dataBytes)
{
var formattedKey = string.Format(SettingsFormat, key);
CleanupOld(key);
if(dataBytes == null)
{
_settings.Remove(formattedKey);
return;
}
var aesKey = GetAesKey();
if(aesKey == null)
{
return;
}
try
{
var cipherString = App.Migration.Crypto.AesCbcEncrypt(dataBytes, aesKey);
_settings.AddOrUpdateValue(formattedKey, cipherString.EncryptedString);
}
catch
{
Console.WriteLine("Failed to encrypt to secure storage.");
//Utilities.SendCrashEmail(e);
//Utilities.SaveCrashFile(e);
}
}
private void GenerateStoreKey(bool withDate)
{
if(_keyStore.ContainsAlias(KeyAlias))
{
return;
}
ClearSettings();
var end = Calendar.Instance;
end.Add(CalendarField.Year, 99);
if(_oldAndroid)
{
var subject = new X500Principal($"CN={KeyAlias}");
var builder = new KeyPairGeneratorSpec.Builder(Application.Context)
.SetAlias(KeyAlias)
.SetSubject(subject)
.SetSerialNumber(BigInteger.Ten);
if(withDate)
{
builder.SetStartDate(new Date(0)).SetEndDate(end.Time);
}
var spec = builder.Build();
var gen = KeyPairGenerator.GetInstance(KeyProperties.KeyAlgorithmRsa, AndroidKeyStore);
gen.Initialize(spec);
gen.GenerateKeyPair();
}
else
{
var builder = new KeyGenParameterSpec.Builder(KeyAlias, KeyStorePurpose.Decrypt | KeyStorePurpose.Encrypt)
.SetBlockModes(KeyProperties.BlockModeGcm)
.SetEncryptionPaddings(KeyProperties.EncryptionPaddingNone);
if(withDate)
{
builder.SetKeyValidityStart(new Date(0)).SetKeyValidityEnd(end.Time);
}
var spec = builder.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.Migration.Crypto.RandomBytes(512 / 8);
var encKey = _oldAndroid ? RsaEncrypt(key) : AesEncrypt(key);
_settings.AddOrUpdateValue(AesKey, encKey);
}
private App.Migration.Models.SymmetricCryptoKey GetAesKey(bool v1 = false)
{
try
{
var aesKey = v1 ? AesKeyV1 : AesKey;
if(!_settings.Contains(aesKey))
{
return null;
}
var encKey = _settings.GetValueOrDefault(aesKey, null);
if(string.IsNullOrWhiteSpace(encKey))
{
return null;
}
if(_oldAndroid || v1)
{
var encKeyBytes = Convert.FromBase64String(encKey);
var key = RsaDecrypt(encKeyBytes, v1);
return new App.Migration.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.Migration.Models.SymmetricCryptoKey(key);
}
}
catch
{
Console.WriteLine("Cannot get AesKey.");
_keyStore.DeleteEntry(KeyAlias);
_settings.Remove(AesKey);
if(!v1)
{
//Utilities.SendCrashEmail(e);
//Utilities.SaveCrashFile(e);
}
return null;
}
}
private string AesEncrypt(byte[] input)
{
using(var entry = _keyStore.GetKey(KeyAlias, null))
using(var cipher = Cipher.GetInstance(AesMode))
{
cipher.Init(CipherMode.EncryptMode, entry);
var encBytes = cipher.DoFinal(input);
var ivBytes = cipher.GetIV();
return $"{Convert.ToBase64String(ivBytes)}|{Convert.ToBase64String(encBytes)}";
}
}
private byte[] AesDecrypt(byte[] iv, byte[] encData)
{
using(var entry = _keyStore.GetKey(KeyAlias, null))
using(var cipher = Cipher.GetInstance(AesMode))
{
var spec = new GCMParameterSpec(128, iv);
cipher.Init(CipherMode.DecryptMode, entry, spec);
var decBytes = cipher.DoFinal(encData);
return decBytes;
}
}
private string RsaEncrypt(byte[] data)
{
using(var entry = GetRsaKeyEntry(KeyAlias))
using(var cipher = Cipher.GetInstance(_rsaMode))
{
cipher.Init(CipherMode.EncryptMode, entry.Certificate.PublicKey);
var cipherText = cipher.DoFinal(data);
return Convert.ToBase64String(cipherText);
}
}
private byte[] RsaDecrypt(byte[] encData, bool v1)
{
using(var entry = GetRsaKeyEntry(v1 ? KeyAliasV1 : KeyAlias))
using(var cipher = Cipher.GetInstance(_rsaMode))
{
if(_oldAndroid)
{
cipher.Init(CipherMode.DecryptMode, entry.PrivateKey);
}
else
{
cipher.Init(CipherMode.DecryptMode, entry.PrivateKey, OAEPParameterSpec.Default);
}
var plainText = cipher.DoFinal(encData);
return plainText;
}
}
private byte[] TryGetAndMigrate(string key)
{
var formattedKeyV1 = string.Format(SettingsFormatV1, key);
if(_settings.Contains(formattedKeyV1))
{
var aesKeyV1 = GetAesKey(true);
if(aesKeyV1 != null)
{
try
{
var cs = _settings.GetValueOrDefault(formattedKeyV1, null);
var value = App.Migration.Crypto.AesCbcDecrypt(new App.Migration.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)
{
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

@@ -3,17 +3,19 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:versionCode="1"
android:versionName="2.2.1"
android:versionName="2.5.5"
package="com.x8bit.bitwarden">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28" />
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
@@ -28,7 +30,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:networkSecurityConfig="@xml/network_security_config">
<provider
android:name="android.support.v4.content.FileProvider"
android:name="androidx.core.content.FileProvider"
android:authorities="com.x8bit.bitwarden.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
@@ -52,5 +54,13 @@
</receiver>
<meta-data android:name="android.max_aspect" android:value="2.1" />
<meta-data android:name="android.content.APP_RESTRICTIONS" android:resource="@xml/app_restrictions" />
<!-- Support for Samsung "Multi Window" mode (for Android < 7.0 users) -->
<meta-data android:name="com.samsung.android.sdk.multiwindow.enable" android:value="true" />
<meta-data android:name="com.samsung.android.sdk.multiwindow.penwindow.enable" android:value="true" />
<!-- Support for LG "Dual Window" mode (for Android < 7.0 users) -->
<meta-data android:name="com.lge.support.SPLIT_WINDOW" android:value="true" />
</application>
</manifest>

View File

@@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTitle("BitwardenAndroid")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("8bit Solutions LLC")]
[assembly: AssemblyCompany("Bitwarden Inc.")]
[assembly: AssemblyProduct("Bitwarden")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]

View File

@@ -16,12 +16,12 @@ namespace Bit.Droid.Push
{
public async override void OnMessageReceived(RemoteMessage message)
{
if(message?.Data == null)
if (message?.Data == null)
{
return;
}
var data = message.Data.ContainsKey("data") ? message.Data["data"] : null;
if(data == null)
if (data == null)
{
return;
}
@@ -32,7 +32,7 @@ namespace Bit.Droid.Push
"pushNotificationListenerService");
await listener.OnMessageAsync(obj, Device.Android);
}
catch(JsonReaderException ex)
catch (JsonReaderException ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}

View File

@@ -1,8 +1,4 @@
using Android.Content;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Bit.Droid.Utilities;
namespace Bit.Droid.Receivers
{
@@ -12,11 +8,7 @@ namespace Bit.Droid.Receivers
public override void OnReceive(Context context, Intent intent)
{
var clipboardManager = context.GetSystemService(Context.ClipboardService) as ClipboardManager;
if(StaticStore.LastClipboardValue != null && StaticStore.LastClipboardValue == clipboardManager.Text)
{
clipboardManager.Text = string.Empty;
}
StaticStore.LastClipboardValue = null;
clipboardManager.PrimaryClip = ClipData.NewPlainText("bitwarden", string.Empty);
}
}
}

View File

@@ -9,8 +9,8 @@ namespace Bit.Droid.Receivers
{
public async override void OnReceive(Context context, Intent intent)
{
var lockService = ServiceContainer.Resolve<ILockService>("lockService");
await lockService.CheckLockAsync();
var vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
await vaultTimeoutService.CheckVaultTimeoutAsync();
}
}
}

View File

@@ -0,0 +1,23 @@
using System.Collections.Generic;
using Android.App;
using Android.Content;
using Bit.App.Utilities;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Bit.Droid.Utilities;
namespace Bit.Droid.Receivers
{
[BroadcastReceiver(Name = "com.x8bit.bitwarden.RestrictionsChangedReceiver", Exported = false)]
[IntentFilter(new[] { Intent.ActionApplicationRestrictionsChanged })]
public class RestrictionsChangedReceiver : BroadcastReceiver
{
public async override void OnReceive(Context context, Intent intent)
{
if (intent.Action == Intent.ActionApplicationRestrictionsChanged)
{
await AndroidHelpers.SetPreconfiguredRestrictionSettingsAsync(context);
}
}
}
}

View File

@@ -7,6 +7,7 @@ using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
using Bit.App.Controls;
using Bit.App.Utilities;
using Bit.Droid.Renderers;
using FFImageLoading;
using FFImageLoading.Views;
@@ -32,34 +33,30 @@ namespace Bit.Droid.Renderers
protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView,
ViewGroup parent, Context context)
{
if(_faTypeface == null)
if (_faTypeface == null)
{
_faTypeface = Typeface.CreateFromAsset(context.Assets, "FontAwesome.ttf");
}
if(_miTypeface == null)
if (_miTypeface == null)
{
_miTypeface = Typeface.CreateFromAsset(context.Assets, "MaterialIcons_Regular.ttf");
}
if(_textColor == default(Android.Graphics.Color))
if (_textColor == default(Android.Graphics.Color))
{
_textColor = ((Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["TextColor"])
.ToAndroid();
_textColor = ThemeManager.GetResourceColor("TextColor").ToAndroid();
}
if(_mutedColor == default(Android.Graphics.Color))
if (_mutedColor == default(Android.Graphics.Color))
{
_mutedColor = ((Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["MutedColor"])
.ToAndroid();
_mutedColor = ThemeManager.GetResourceColor("MutedColor").ToAndroid();
}
if(_disabledIconColor == default(Android.Graphics.Color))
if (_disabledIconColor == default(Android.Graphics.Color))
{
_disabledIconColor =
((Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["DisabledIconColor"])
.ToAndroid();
_disabledIconColor = ThemeManager.GetResourceColor("DisabledIconColor").ToAndroid();
}
var cipherCell = item as CipherViewCell;
_cell = convertView as AndroidCipherCell;
if(_cell == null)
if (_cell == null)
{
_cell = new AndroidCipherCell(context, cipherCell, _faTypeface, _miTypeface);
}
@@ -77,11 +74,11 @@ namespace Bit.Droid.Renderers
{
var cipherCell = sender as CipherViewCell;
_cell.CipherViewCell = cipherCell;
if(e.PropertyName == CipherViewCell.CipherProperty.PropertyName)
if (e.PropertyName == CipherViewCell.CipherProperty.PropertyName)
{
_cell.UpdateCell(cipherCell);
}
else if(e.PropertyName == CipherViewCell.WebsiteIconsEnabledProperty.PropertyName)
else if (e.PropertyName == CipherViewCell.WebsiteIconsEnabledProperty.PropertyName)
{
_cell.UpdateIconImage(cipherCell);
}
@@ -145,7 +142,7 @@ namespace Bit.Droid.Renderers
var cipher = cipherCell.Cipher;
Name.Text = cipher.Name;
if(!string.IsNullOrWhiteSpace(cipher.SubTitle))
if (!string.IsNullOrWhiteSpace(cipher.SubTitle))
{
SubTitle.Text = cipher.SubTitle;
SubTitle.Visibility = ViewStates.Visible;
@@ -160,7 +157,7 @@ namespace Bit.Droid.Renderers
public void UpdateIconImage(CipherViewCell cipherCell)
{
if(_currentTask != null && !_currentTask.IsCancelled && !_currentTask.IsCompleted)
if (_currentTask != null && !_currentTask.IsCancelled && !_currentTask.IsCompleted)
{
_currentTask.Cancel();
}
@@ -168,7 +165,7 @@ namespace Bit.Droid.Renderers
var cipher = cipherCell.Cipher;
var iconImage = cipherCell.GetIconImage(cipher);
if(iconImage.Item2 != null)
if (iconImage.Item2 != null)
{
IconImage.SetImageResource(Resource.Drawable.login);
IconImage.Visibility = ViewStates.Visible;
@@ -197,7 +194,7 @@ namespace Bit.Droid.Renderers
private void MoreButton_Click(object sender, EventArgs e)
{
if(CipherViewCell.ButtonCommand?.CanExecute(CipherViewCell.Cipher) ?? false)
if (CipherViewCell.ButtonCommand?.CanExecute(CipherViewCell.Cipher) ?? false)
{
CipherViewCell.ButtonCommand.Execute(CipherViewCell.Cipher);
}
@@ -205,7 +202,7 @@ namespace Bit.Droid.Renderers
protected override void Dispose(bool disposing)
{
if(disposing)
if (disposing)
{
MoreButton.Click -= MoreButton_Click;
}

View File

@@ -12,11 +12,20 @@ namespace Bit.Droid.Renderers
public CustomEditorRenderer(Context context)
: base(context)
{ }
// Workaround for issue described here:
// https://github.com/xamarin/Xamarin.Forms/issues/8291#issuecomment-617456651
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
EditText.Enabled = false;
EditText.Enabled = true;
}
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
if(Control != null && e.NewElement != null)
if (Control != null && e.NewElement != null)
{
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
Control.PaddingBottom + 20);

View File

@@ -1,5 +1,9 @@
using Android.Content;
using System.ComponentModel;
using Android.Content;
using Android.Graphics;
using Android.Text;
using Android.Views.InputMethods;
using Android.Widget;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
@@ -16,7 +20,7 @@ namespace Bit.Droid.Renderers
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if(Control != null && e.NewElement != null)
if (Control != null && e.NewElement != null)
{
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
Control.PaddingBottom + 20);
@@ -24,5 +28,42 @@ namespace Bit.Droid.Renderers
(ImeAction)ImeFlags.NoExtractUi;
}
}
// Workaround for failure to disable text prediction on non-password fields
// see https://github.com/xamarin/Xamarin.Forms/issues/10857
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
// Check if changed property is "IsPassword", otherwise ignore
if (e.PropertyName == Entry.IsPasswordProperty.PropertyName)
{
// Check if field type is text, otherwise ignore (numeric passwords, etc.)
EditText.InputType = Element.Keyboard.ToInputType();
if ((EditText.InputType & InputTypes.ClassText) == InputTypes.ClassText)
{
if (Element.IsPassword)
{
// Element is a password field, set inputType to TextVariationPassword which disables
// predictive text by default
EditText.InputType = EditText.InputType | InputTypes.TextVariationPassword;
}
else
{
// Element is not a password field, set inputType to TextVariationVisiblePassword to
// disable predictive text while still displaying the content.
EditText.InputType = EditText.InputType | InputTypes.TextVariationVisiblePassword;
}
// The workaround above forces a reset of the style properties, so we need to re-apply the font.
// see https://xamarin.github.io/bugzilla-archives/33/33666/bug.html
var typeface = Typeface.CreateFromAsset(Context.Assets, "RobotoMono_Regular.ttf");
if (Control is TextView label)
{
label.Typeface = typeface;
}
}
}
}
}
}

View File

@@ -15,7 +15,7 @@ namespace Bit.Droid.Renderers
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if(Control != null && e.NewElement != null)
if (Control != null && e.NewElement != null)
{
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
Control.PaddingBottom + 20);

View File

@@ -16,7 +16,7 @@ namespace Bit.Droid.Renderers
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
if(Control != null && e.NewElement != null)
if (Control != null && e.NewElement != null)
{
try
{

View File

@@ -17,7 +17,7 @@ namespace Bit.Droid.Renderers
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
if(Control != null && e.NewElement != null && e.NewElement is ExtendedListView listView)
if (Control != null && e.NewElement != null && e.NewElement is ExtendedListView listView)
{
// Pad for FAB
Control.SetPadding(0, 0, 0, 170);

View File

@@ -1,6 +1,6 @@
using Android.Content;
using Android.Graphics.Drawables;
using Android.Support.V4.Content.Res;
using AndroidX.Core.Content.Resources;
using Bit.App.Controls;
using Bit.Droid.Renderers;
using Xamarin.Forms;
@@ -18,12 +18,12 @@ namespace Bit.Droid.Renderers
protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
{
base.OnElementChanged(e);
if(Control != null && Element is ExtendedSlider view)
if (Control != null && Element is ExtendedSlider view)
{
var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.slider_thumb, null);
if(t is GradientDrawable thumb)
if (t is GradientDrawable thumb)
{
if(view.ThumbColor == Color.Default)
if (view.ThumbColor == Color.Default)
{
thumb.SetColor(Color.White.ToAndroid());
}

View File

@@ -28,20 +28,20 @@ namespace Bit.Droid.Renderers
{
base.OnElementChanged(e);
if(Control == null)
if (Control == null)
{
var webView = new AWebkit.WebView(_context);
webView.Settings.JavaScriptEnabled = true;
webView.SetWebViewClient(new JSWebViewClient(string.Format("javascript: {0}", JSFunction)));
SetNativeControl(webView);
}
if(e.OldElement != null)
if (e.OldElement != null)
{
Control.RemoveJavascriptInterface("jsBridge");
var hybridWebView = e.OldElement as HybridWebView;
hybridWebView.Cleanup();
}
if(e.NewElement != null)
if (e.NewElement != null)
{
Control.AddJavascriptInterface(new JSBridge(this), "jsBridge");
Control.LoadUrl(Element.Uri);
@@ -51,7 +51,7 @@ namespace Bit.Droid.Renderers
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if(e.PropertyName == HybridWebView.UriProperty.PropertyName)
if (e.PropertyName == HybridWebView.UriProperty.PropertyName)
{
Control.LoadUrl(Element.Uri);
}
@@ -70,7 +70,7 @@ namespace Bit.Droid.Renderers
[Export("invokeAction")]
public void InvokeAction(string data)
{
if(_hybridWebViewRenderer != null &&
if (_hybridWebViewRenderer != null &&
_hybridWebViewRenderer.TryGetTarget(out HybridWebViewRenderer hybridRenderer))
{
hybridRenderer.Element.InvokeAction(data);

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

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