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

Compare commits

...

211 Commits

Author SHA1 Message Date
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
511 changed files with 84026 additions and 90478 deletions

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

@@ -279,6 +279,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 +307,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 +336,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,60 +20,91 @@ 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.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
// [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
@@ -85,61 +124,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;
}
@@ -149,11 +215,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;
@@ -166,12 +232,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);
}
@@ -179,7 +244,7 @@ namespace Bit.Droid.Accessibility
public static void FillEditText(AccessibilityNodeInfo editTextNode, string value)
{
if(editTextNode == null || value == null)
if (editTextNode == null || value == null)
{
return;
}
@@ -192,31 +257,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.");
}
@@ -226,26 +295,345 @@ 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 uriKey = null;
string uriLocalPath = null;
if (Uri.TryCreate(uriString, UriKind.Absolute, out var uri))
{
uriKey = uri.Authority;
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)
{
return windows?.Any(w => w.Title?.ToLower().Contains("autofill") ?? false) ?? 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,102 @@ 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.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",
};
// 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 +122,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 +144,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 +155,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 +185,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 +207,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,19 +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();
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
ServiceContainer.Init(deviceActionService.DeviceUserAgent);
if(App.Migration.MigrationHelpers.NeedsMigration())
{
var task = App.Migration.MigrationHelpers.PerformMigrationAsync();
Task.Delay(2000).Wait();
}
}
#if !FDROID
if(Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
if (Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
{
ProviderInstaller.InstallIfNeededAsync(ApplicationContext, this);
}
@@ -74,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(() =>
{
@@ -94,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);

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.2"
android:versionName="2.4.2"
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

@@ -16,7 +16,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);

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="1024.4897"
android:viewportHeight="1024.4897">
<group android:translateX="300.2449"
android:translateY="261.2449">
<path
android:pathData="M393.4,52.2C389.7,48.5 385.3,46.6 380.3,46.6L43.7,46.6C38.6,46.6 34.3,48.5 30.6,52.2C26.9,55.9 25,60.2 25,65.3L25,289.7C25,306.4 28.3,323.1 34.8,339.5C41.3,356 49.4,370.6 59.1,383.3C68.7,396.1 80.2,408.5 93.6,420.5C106.9,432.6 119.3,442.6 130.6,450.6C141.9,458.6 153.7,466.1 166,473.2C178.3,480.3 187,485.1 192.2,487.7C197.4,490.2 201.5,492.2 204.6,493.5C206.9,494.7 209.5,495.3 212.2,495.3C214.9,495.3 217.5,494.7 219.8,493.5C222.9,492.1 227.1,490.2 232.2,487.7C237.4,485.2 246.1,480.3 258.4,473.2C270.7,466.1 282.5,458.5 293.8,450.6C305.1,442.6 317.4,432.6 330.8,420.5C344.1,408.4 355.6,396 365.3,383.3C374.9,370.5 383,355.9 389.5,339.5C396,323 399.3,306.4 399.3,289.7L399.3,65.3C399,60.2 397.1,55.9 393.4,52.2ZM350,291.8C350,373 212,443 212,443L212,94.6L350,94.6L350,291.8Z"
android:fillColor="#ffffff"
android:fillType="nonZero"/>
</group>
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 941 B

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="1024dp"
android:height="1024dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M1024,864c0,88.4 -71.6,160 -160,160H160C71.6,1024 0,952.4 0,864V160C0,71.6 71.6,0 160,0h704c88.4,0 160,71.6 160,160V864z"
android:fillColor="#175DDC"/>
<path
android:pathData="M829.8,128.6c-6.5,-6.5 -14.2,-9.7 -23,-9.7H217.2c-8.9,0 -16.5,3.2 -23,9.7c-6.5,6.5 -9.7,14.2 -9.7,23v393.1c0,29.3 5.7,58.4 17.1,87.3c11.4,28.8 25.6,54.4 42.5,76.8c16.9,22.3 37,44.1 60.4,65.3c23.4,21.2 45,38.7 64.7,52.7c19.8,14 40.4,27.2 61.9,39.7c21.5,12.5 36.8,20.9 45.8,25.3c9,4.4 16.3,7.9 21.7,10.2c4.1,2 8.5,3.1 13.3,3.1c4.8,0 9.2,-1 13.3,-3.1c5.5,-2.4 12.7,-5.8 21.8,-10.2c9,-4.4 24.3,-12.9 45.8,-25.3c21.5,-12.5 42.1,-25.7 61.9,-39.7c19.8,-14 41.4,-31.6 64.8,-52.7c23.4,-21.2 43.5,-42.9 60.4,-65.3c16.9,-22.4 31,-47.9 42.5,-76.8c11.4,-28.8 17.1,-57.9 17.1,-87.3V151.7C839.6,142.8 836.3,135.1 829.8,128.6zM753.8,548.4c0,142.3 -241.8,264.9 -241.8,264.9V203.1h241.8C753.8,203.1 753.8,406.1 753.8,548.4z"
android:fillColor="#FFFFFF"/>
</vector>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.TabLayout xmlns:android="http://schemas.android.com/apk/res/android"
<com.google.android.material.tabs.TabLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/sliding_tabs"
android:layout_width="match_parent"

View File

@@ -1,4 +1,4 @@
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toolbar"
android:layout_width="match_parent"

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 941 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 896 B

After

Width:  |  Height:  |  Size: 968 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Light theme -->
<color name="colorPrimary">#3c8dbc</color>
<color name="colorPrimaryDark">#222d32</color>
<color name="primary">#3c8dbc</color>
<color name="notificationBar">#3883af</color>
<color name="colorPrimary">#175DDC</color>
<color name="colorPrimaryDark">#1A3B66</color>
<color name="primary">#175DDC</color>
<color name="notificationBar">#1452BC</color>
<color name="border">#dddddd</color>
<!-- Dark theme -->

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#3C8DBC</color>
<color name="ic_launcher_background">#175DDC</color>
</resources>

View File

@@ -16,4 +16,13 @@
<string name="PasswordGenerator">
Password Generator
</string>
<string name="AutoFillTile">
Auto-fill
</string>
<string name="SelfHostedServerUrl">
Self-hosted server URL
</string>
<string name="ServerUrl">
Server URL
</string>
</resources>

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