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

Compare commits

...

141 Commits

Author SHA1 Message Date
Hinton
69cd7d79e7 WIP!!! 2022-02-23 20:40:25 +01:00
Joseph Flinn
dbf94c1b56 Updating gh-pages (#1628) 2021-11-04 09:29:07 -07:00
Matt Portune
4b0fb2840e bump version for testflight (#1629) 2021-11-03 13:04:54 -04:00
stevenlele
629c696c81 [SupportedBrowsers] Add Captive Login and drop Alook (#1625) 2021-11-02 09:17:10 -04:00
Joseph Flinn
bf1aa7c4eb Version bump 2.14.2 (#1622) 2021-10-29 12:50:11 -07:00
Jake Fink
318a3e4de9 fix for bug stopping vault timeout to never (#1618)
- use nullable int on settings page and in vault service
2021-10-29 10:31:38 -04:00
Makoto Kato
0f992d27b3 Turn off autofill compatibility mode on the latest Firefox and Firefox beta. (#1592) 2021-10-28 14:44:24 -04:00
Jake Fink
83fd6736f6 add date and time formatting methods to localize service (#1616)
- uses Apple APIs for formatting on iOS
- uses .Net APIs for formatting Android
- implemented across project
- remove unnecesary calls to DateTimeConverter
2021-10-28 12:52:41 -04:00
Matt Portune
397250368a remove numeric restriction on cc number field (#1617) 2021-10-28 12:52:04 -04:00
Joseph Flinn
5e4365084b Version bump 2.14.1 (#1614) 2021-10-28 06:50:13 -07:00
Joseph Flinn
ea5e4aafa3 adding the missing flag for sed in the fdroid build (#1613) 2021-10-28 06:49:29 -07:00
Joseph Flinn
69d1de47c6 Fixing release template name (#1611) 2021-10-27 13:10:21 -07:00
Joseph Flinn
0d3f819e93 Version Bump 2.14.0 (#1610) 2021-10-27 08:40:08 -07:00
github-actions[bot]
3760e0f9f4 Autosync the updated translations (#1609)
Co-authored-by: github-actions <>
2021-10-27 07:52:31 -07:00
Thomas Rittson
5a13cb53ba Add PR template (#1608) 2021-10-27 18:59:59 +10:00
Jake Fink
0e9cbe4539 add reveal button to password reprompt on iOS (#1607)
* add reveal button to password reprompt on iOS

* format special chars as unicode
2021-10-26 17:46:11 -04:00
Federico Maccaroni
b8c1107c94 Fixed long secure notes edition scrolling when focused issue (#1257) (#1601)
* Fixed long secure notes edition scrolling when focused issue (#1257)

* Improved fix long secure notes edition scrolling when focused issue to not use a new editor custom renderer but an effect (#1257)

* Fixed long editor, on text and notes on send when scrolling when focused issue (#1257)
2021-10-25 16:28:45 -03:00
Federico Maccaroni
a07ef1a1d6 Fix html labels colors issue (#1516) (#1603) 2021-10-25 16:28:14 -03:00
Joseph Flinn
99ccd62bcd Release branch constraint on build (#1599)
* adding in the release branch constraint to the build workflow for mobile

* moving the branch check to a setup job
2021-10-22 13:16:57 -07:00
Joseph Flinn
bfb050a6f9 Change release branch contraint (#1598)
* removing the master branch release ci code execution

* updating some verbiage
2021-10-22 09:24:48 -07:00
Matt Portune
4e0b05571d utilize iOS safe area in UI (#1597) 2021-10-22 10:06:17 -04:00
Matt Portune
d93d70fd66 Autofill support for upcoming changes to google search (#1596) 2021-10-21 10:45:48 -04:00
Matt Portune
41098ff05b fix gap in background color application resulting in flashing during transitions, part 2 (#1595) 2021-10-18 09:56:20 -04:00
Matt Portune
4ed7491116 fix for crash when checking for running accessibility service without activity (#1591) 2021-10-18 09:56:12 -04:00
Matt Portune
1ebad6bca5 fix for crash when terminating app (#1589) 2021-10-16 07:56:17 -04:00
Matt Portune
48e3986264 fix gap in theme application resulting in flashing during transitions (#1588) 2021-10-15 15:46:24 -04:00
Vince Grassia
88a1d8d4e8 Add notify constraint (#1587) 2021-10-15 13:06:53 -04:00
Jake Fink
f3ff991abe check password for null before setting cursor position (#1586) 2021-10-15 10:56:12 -04:00
Kyle Spearrin
17b89dc21c New Crowdin updates (#1583)
* New translations AppResources.resx (Romanian)

* New translations AppResources.resx (Norwegian Nynorsk)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (Vietnamese)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Indonesian)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Tamil)

* New translations AppResources.resx (Bengali)

* New translations AppResources.resx (Thai)

* New translations AppResources.resx (Croatian)

* New translations AppResources.resx (Estonian)

* New translations AppResources.resx (Ukrainian)

* New translations AppResources.resx (Latvian)

* New translations AppResources.resx (Azerbaijani)

* New translations AppResources.resx (Hindi)

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

* New translations AppResources.resx (Filipino)

* New translations AppResources.resx (Malayalam)

* New translations AppResources.resx (Bosnian)

* New translations AppResources.resx (Sinhala)

* New translations AppResources.resx (Kannada)

* New translations AppResources.resx (Norwegian Bokmal)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Turkish)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Hebrew)

* 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 (German)

* New translations AppResources.resx (Greek)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (Swedish)

* New translations AppResources.resx (Italian)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Korean)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Polish)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Slovenian)

* New translations AppResources.resx (Serbian (Cyrillic))

* New translations AppResources.resx (English, India)

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

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (English, India)

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

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (English, India)
2021-10-14 19:22:33 -04:00
github-actions[bot]
ff76a3ec15 Autosync the updated translations (#1582)
Co-authored-by: github-actions <>
2021-10-14 18:04:06 -04:00
Vince Grassia
3a2e012c42 Add Slack alerts for Build workflow failures (#1581) 2021-10-14 14:34:24 -04:00
Matt Portune
a0bb16c35f fix for crash on startup when policies are non-existent (#1579) 2021-10-14 13:53:57 -04:00
Jake Fink
62a8d1c017 fix for Captcha crashing on account creation (#1580) 2021-10-14 13:42:48 -04:00
Matt Portune
ce4e3ed1cd support for new btnReturnText in mobile webauthn connector (#1576)
* support for new btnReturnText in mobile webauthn connector

* added header text for connector localization
2021-10-14 11:53:41 -04:00
Matt Portune
4669275680 Fix for Android 5.x crash caused by new switch styling (#1575) 2021-10-13 12:26:54 -04:00
Jake Fink
fc1000acc1 loop through URIs to find website before showing default icons (#1572)
- create static class for uri logic (keeping converter for future)
- create new property in viewmodel for icon source
- check if icons are enabled and source for icon before showing default glyph
2021-10-12 11:00:33 -04:00
Oscar Hinton
c9ce7256e5 MaximumVaultTimeout policy fixes (#1573) 2021-10-12 15:35:01 +02:00
Vincent Salucci
34aba0e168 [Reset Password] BUG - Update local policies for enforcement (#1565)
* [Reset Password] BUG - Update local policies for enforcement

* Updated with blocking sync

* add the stuff I forgot to tell vsalucci about

* removed the lies I fed vsalucci

* remove unnecessary import

Co-authored-by: Matt Portune <mportune@bitwarden.com>
2021-10-08 16:51:16 -05:00
Matt Portune
9e9e2e12d8 fix padding between sync button and last sync timestamp (#1571) 2021-10-08 15:56:36 -04:00
Jake Fink
3eec349038 Fix crash from SelectableLabelRenderer (#1570) 2021-10-08 13:14:23 -04:00
Jake Fink
69650a1ab5 Hide label if no subtitle for Cipher (#1569) 2021-10-08 12:09:26 -04:00
Jake Fink
faac7ebe5e Set cursor position after toggle password visibility (#1568)
- Lock page
- Login page
- Register page
2021-10-08 12:09:08 -04:00
Jake Fink
d3734c63fc Add SelectableLabel Custom Renderer to allow copy of note text (#1564)
* Add SelectableLabel Custom Renderer to allow copy of note text
- Remove SelectableLabelEffect

* Remove editor changes from text custom field
2021-10-08 08:49:15 -04:00
Matt Portune
4aad34cd75 Dynamic theme switching and visual tweaks (#1556)
* Dynamic theme switching and visual tweaks

* update action runner to use macos-11 for iOS 15 support

* additional tweaks

* refinements

* refinements

* formatting and tweaks
2021-10-08 08:47:40 -04:00
Matt Gibson
73eb3c2c1e Split collections permissions (#1558)
* Split collections permissions

* Remove old permissions

There is no backwards compatibility issue since these permissions are unused.

* Remove unused imports
2021-10-05 11:12:55 -05:00
Jake Fink
6109091ec0 filter Ciphers to exclude deleted items (#1559) 2021-10-04 13:46:45 -04:00
Jake Fink
c0783cd162 Exclude deleted items from folder counts (#1555) 2021-10-04 08:33:23 -04:00
Vince Grassia
a9a4fa56c1 Pipeline fixes (#1549)
* Update path to fix 'Deploy to App Store' step in Build workflow
* Change `sed` to save changes to file by using `-i` flag
2021-09-24 17:02:34 -04:00
Vincent Salucci
271e6b3d92 [Reset Password v1] Update Temp Password (#1492)
* [Reset Password v1] Update Temp Password

* fixed order of operations for reset temp password flow

* Refactored bool with auth result

* Finished removal of temp password flow from set password

* iOS extension support plus extension bugfixes

Co-authored-by: addison <addisonbeck1@gmail.com>
Co-authored-by: Matt Portune <mportune@bitwarden.com>
2021-09-24 13:14:26 -05:00
Vince Grassia
750faf8a83 Update pipeline to new model (#1472)
* Update pipelines to new model
2021-09-24 13:50:54 -04:00
Thomas Rittson
716e52f6ff Move policy checks inside PolicyService (#1533)
* Move policy checks inside PolicyService

* Remove leftover code

* Remove duplicate code

* Reorder code for consistency
2021-09-24 09:51:02 +10:00
Matt Portune
010a4210f4 bump version for testflight (#1547) 2021-09-23 14:24:52 -04:00
Matt Portune
8d23bc89e8 Update XF to remove AndroidX dependency hack (#1544)
* update forms and other libs

* change async pattern
2021-09-23 13:11:51 -04:00
Oscar Hinton
f2857397f0 Disable Private Vault Export Policy (#1546) 2021-09-23 18:30:20 +02:00
Oscar Hinton
6023374fbe Vault Timeout Policy (#1530) 2021-09-23 15:42:38 +02:00
Joseph Flinn
d3c1b58c2a Version bump to 2.13.0 (#1545) 2021-09-22 08:58:18 -07:00
github-actions[bot]
a026af2072 Autosync the updated translations (#1543)
Co-authored-by: github-actions <>
2021-09-21 14:53:36 -07:00
Vincent Salucci
51be6e522b [SSO/Auto Enroll] Fixed response object typo (#1542) 2021-09-16 23:01:12 -05:00
Vincent Salucci
024d9380c9 [SSO Auto Enroll] Auto Enroll status retrieval (#1540)
* [SSO Auto Enroll] Auto Enroll status retrieval

* Updated object property to match server
2021-09-15 12:27:27 -05:00
Matt Portune
14b51b1a7f friendly message for webauthn errors (#1534)
* friendly error message for webauthn error flow

* combine original plus friendly message

* remove redundant phrasing
2021-09-09 10:04:49 -04:00
Oscar Hinton
4667a9d643 Add issue template and template chooser (#1526) 2021-09-09 11:54:43 +02:00
Vincent Salucci
d3f00340fb [SSO] Auto enroll during set password (#1520)
* [SSO] Auto enroll during set password

* Updated with requested changes
2021-09-08 12:43:24 -05:00
Matt Portune
8866fc6322 Bump version to 2.12.1 (#1529) 2021-09-07 13:50:05 -04:00
Matt Portune
68887c5de7 Bugfix typerole (#1528)
* Fix issue with undefined CFBundleTypeRole

* format with tabs for consistency
2021-09-07 12:50:22 -04:00
Vince Grassia
99b67b680c Update workflows with linter suggestions (#1521) 2021-09-02 16:04:41 -04:00
baylorrandolph
9b5bf4306f Update README.md (#1523) 2021-09-02 16:00:25 -04:00
baylorrandolph
be55504b01 Update README.md (#1522) 2021-09-02 15:46:04 -04:00
Matt Portune
307a5a5843 FIDO2 WebAuthn support for mobile (#1519)
* FIDO2 / WebAuthn support for mobile

* fixes
2021-08-30 12:44:12 -04:00
Joseph Flinn
d050215ebc Simplifying Crowdin sync workflow (#1517) 2021-08-27 10:08:40 -07:00
Matt Portune
67e26c778b Revert "bump MinimumOSVersion to 12.0 (#1508)" (#1515)
This reverts commit 745b54bf2a.
2021-08-25 17:03:33 -04:00
Matt Portune
efb10d155d Revert "enable LLVM in iOS builds (#1510)" (#1514)
This reverts commit 244a6a7f41.
2021-08-25 17:03:02 -04:00
Matt Portune
913c673773 Revert "enable LLVM in iOS extensions (#1511)" (#1513)
This reverts commit 380c3583fb.
2021-08-25 17:02:49 -04:00
Joseph Flinn
339decafe4 Reverting changes to Chinese and Portuguese translations (#1512) 2021-08-24 11:57:34 -07:00
Matt Portune
380c3583fb enable LLVM in iOS extensions (#1511) 2021-08-23 11:40:16 -04:00
Matt Portune
244a6a7f41 enable LLVM in iOS builds (#1510) 2021-08-23 10:16:46 -04:00
Matt Portune
745b54bf2a bump MinimumOSVersion to 12.0 (#1508) 2021-08-20 09:54:39 -04:00
Joseph Flinn
4f86bb2043 Reverting English changes from the Auto Crowdin Sync (#1507) 2021-08-19 10:47:15 -07:00
github-actions[bot]
ada8a73849 Autosync Crowdin Translations (#1498)
* Autosync Crowdin translations

* Autosync Crowdin translations

Co-authored-by: github-actions <>
2021-08-18 15:14:15 -07:00
Matt Gibson
1e3204f91d Version bump to 2.12.0 (#1505) 2021-08-17 14:02:00 -05:00
Joseph Flinn
b5c6a57fa0 Crowdin sync workflow (#1499)
* Initial addition of the crowdin sync workflow for testing

* adding logic to handle the case where there are no Crowdin updates
2021-08-13 13:28:07 -07:00
Joseph Flinn
6ca5b66aa7 stubbing out the crowdin sync workflow (#1497) 2021-08-13 07:31:36 -07:00
Matt Gibson
24a0396d0f Fix iphone captcha throws (#1495) 2021-08-12 08:23:02 -05:00
Chad Scharf
7f7b673b0a Fix for EMM system configuration for mobile (#1434) 2021-08-11 10:36:36 -04:00
Matt Gibson
f79ff3fd63 Encode auth email for unicode email support (#1491) 2021-08-10 11:48:51 -05:00
Matt Gibson
2f2fa8a25b Feature/use hcaptcha if bot (#1476)
* Add captcha to login models and methods

* Add captcha web auth to login

* Extract captcha to abstract base class

* Add Captcha to register

* Null out captcha token after each successful challenge

* Cancel > close
2021-08-04 14:47:23 -05:00
Matt Gibson
9042b1009e Revert "Revert "Redefine cipher "share" to "move to organization""" (#1459)
* Revert "Revert "Redefine cipher "share" to "move to organization" (#1433)" (#1440)"

This reverts commit bd4a275558.

* Cancel > close
2021-08-04 14:46:28 -05:00
Vince Grassia
dbc0f490c5 Add Release workflow stub (#1482) 2021-07-27 13:25:12 -04:00
Matt Portune
6d8f627772 disable send test (#1481) 2021-07-27 10:51:47 -04:00
Jonathan Browne
b4c016c9d4 Change Close to Cancel in editing prompts. (#1480) 2021-07-27 09:25:42 -04:00
howyay
ae763ebca8 Add support for Mull browser (#1435) 2021-07-26 09:20:47 -04:00
Matt Portune
880483ac79 bump app version to 2.11.3 for testflight (#1477) 2021-07-23 16:33:15 -04:00
Georges Varouchas
f44e6ab75f bugfix in AuthService.LogInSsoAsync (#1474) (#1475)
add missing parameter in call to LogInHelperAsync
2021-07-23 14:36:49 -04:00
Matt Portune
10a718b0c7 bump version to 2.11.2 (#1473) 2021-07-23 11:33:53 -04:00
Thomas Rittson
9ec4050e4d Move remaining primary buttons in login flow (#1470)
* Fix show/hide logic on Continue button

* Fix bug in 2FA method selection modal

* Move Log In button in SSO flow

* Fix linting
2021-07-22 07:56:39 +10:00
Matt Portune
93e2c0df7c keep app version at 2.11.1 for now (#1471) 2021-07-21 14:03:38 -04:00
Matt Portune
8d07397a59 Revert "lib updates and app version bump (#1468)" (#1469)
This reverts commit 15d44b873b.
2021-07-20 16:21:41 -04:00
Matt Portune
15d44b873b lib updates and app version bump (#1468)
* lib updates and app version bump

* added explicit androidx core dependency
2021-07-20 12:57:01 -04:00
Sang
6a979d0ff5 2FA screen: move continue to a button (#1427)
* Move continue to a button

* Resolve pr comments

* Move use another two step method button

* Resolve code suggestions

* Resolve for iPhone

* Apply suggestions from code review

Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>

Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
2021-07-20 14:50:45 +10:00
Matt Portune
a4db088eda bugfix for incorrect type and nullable for some org vars (#1465) 2021-07-16 13:57:15 -04:00
Matt Portune
96454b7cbf add toggle for removing links to pages containing subscription info (#1463) 2021-07-16 12:02:00 -04:00
Matt Portune
9c1df2179c bump version to 2.11.1 (#1462) 2021-07-14 14:46:18 -04:00
Matt Portune
52c9125404 bump version to 2.11.0 (#1461) 2021-07-13 12:52:14 -04:00
Matt Portune
172a857604 handle intent exceptions to prevent denial of service (#1458) 2021-07-12 12:31:39 -04:00
Matt Portune
d8e68a266c bugfixes for lock block when setting timeout to immediate (#1455) 2021-07-12 10:58:17 -04:00
Matt Portune
1f57ba6c50 workaround for off-screen draw bug in XF4.5+ part 2 (#1454) 2021-07-09 17:29:57 -04:00
Matt Portune
ff19578807 Fix for password unlock for autofill and share-to-send on Android (#1453) 2021-07-09 11:48:03 -04:00
Matt Portune
9298d57f22 fix for resuming autofill when back button was previously used to exit (#1451) 2021-07-06 11:10:17 -04:00
Martin Choutka
8cf5d5728e MaximumAccessCountInfo locale string update (#1441)
String MaximumAccessCountInfo doesn't have "Send" with a capital "s".
2021-07-02 12:20:40 -04:00
Matt Portune
bdf6d764ca update csv lib and skip link (#1449) 2021-07-02 11:21:36 -04:00
Matt Portune
05e8da4bcc Workaround for off-screen draw bug in XF4.5+ (#1447)
* workaround for off-screen draw bug in xf4.5+

* check cols even if orgId is present
2021-06-30 17:46:59 -04:00
Matt Portune
382e547f74 fix for export password handling (#1448) 2021-06-30 16:05:30 -04:00
Kyle Spearrin
4f9985d2b0 New Crowdin updates (#1444)
* New translations AppResources.resx (Romanian)

* New translations copy.resx (Bosnian)

* New translations copy.resx (Kannada)

* New translations copy.resx (Tamil)

* New translations copy.resx (Tamil)

* New translations copy.resx (Bosnian)

* New translations copy.resx (Kannada)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Polish)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Indonesian)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Vietnamese)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Ukrainian)

* New translations AppResources.resx (Turkish)

* New translations AppResources.resx (Swedish)

* New translations AppResources.resx (Serbian (Cyrillic))

* New translations AppResources.resx (Slovenian)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Bengali)

* 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 (Korean)

* 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 (Tamil)

* New translations AppResources.resx (Thai)

* New translations AppResources.resx (Croatian)

* New translations AppResources.resx (Sinhala)

* New translations AppResources.resx (Estonian)

* New translations AppResources.resx (Latvian)

* New translations AppResources.resx (Hindi)

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

* New translations AppResources.resx (Malayalam)

* New translations AppResources.resx (Bosnian)

* New translations AppResources.resx (Kannada)

* New translations AppResources.resx (Norwegian Bokmal)

* New translations AppResources.resx (English, India)

* New translations copy.resx (Bosnian)

* New translations copy.resx (German)

* New translations copy.resx (Danish)

* New translations copy.resx (Czech)

* New translations copy.resx (Catalan)

* New translations copy.resx (Bulgarian)

* New translations copy.resx (Belarusian)

* New translations copy.resx (Afrikaans)

* New translations copy.resx (Spanish)

* New translations copy.resx (French)

* New translations copy.resx (Romanian)

* New translations copy.resx (English, India)

* New translations copy.resx (Norwegian Bokmal)

* New translations copy.resx (Kannada)

* New translations copy.resx (Sinhala)

* New translations copy.resx (Malayalam)

* New translations copy.resx (Finnish)

* New translations copy.resx (Persian)

* New translations copy.resx (Ukrainian)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Vietnamese)

* New translations copy.resx (Portuguese, Brazilian)

* New translations copy.resx (Indonesian)

* New translations copy.resx (Tamil)

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

* New translations copy.resx (Bengali)

* New translations copy.resx (Thai)

* New translations copy.resx (Croatian)

* New translations copy.resx (Estonian)

* New translations copy.resx (Latvian)

* New translations copy.resx (Hindi)

* New translations copy.resx (Greek)

* New translations copy.resx (Hebrew)

* New translations copy.resx (Swedish)

* New translations copy.resx (Latvian)

* New translations copy.resx (Persian)

* New translations copy.resx (Tamil)

* New translations copy.resx (Bengali)

* New translations copy.resx (Thai)

* New translations copy.resx (Croatian)

* New translations copy.resx (Estonian)

* New translations copy.resx (Hindi)

* New translations copy.resx (Portuguese, Brazilian)

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

* New translations copy.resx (Malayalam)

* New translations copy.resx (Bosnian)

* New translations copy.resx (Sinhala)

* New translations copy.resx (Kannada)

* New translations copy.resx (Norwegian Bokmal)

* New translations copy.resx (Indonesian)

* New translations copy.resx (Vietnamese)

* New translations copy.resx (Hungarian)

* New translations copy.resx (Russian)

* New translations copy.resx (Italian)

* New translations copy.resx (Japanese)

* New translations copy.resx (Korean)

* New translations copy.resx (Dutch)

* New translations copy.resx (Polish)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Slovak)

* New translations copy.resx (Chinese Traditional)

* New translations copy.resx (Slovenian)

* New translations copy.resx (Serbian (Cyrillic))

* New translations copy.resx (Swedish)

* New translations copy.resx (Turkish)

* New translations copy.resx (Ukrainian)

* New translations copy.resx (Chinese Simplified)

* New translations copy.resx (Turkish)

* New translations copy.resx (Serbian (Cyrillic))

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Italian)

* New translations copy.resx (Slovenian)

* New translations copy.resx (Italian)

* New translations copy.resx (Danish)

* New translations copy.resx (German)

* New translations copy.resx (Greek)

* New translations copy.resx (Finnish)

* New translations copy.resx (Hebrew)

* New translations copy.resx (Hungarian)

* New translations copy.resx (Japanese)

* New translations copy.resx (Catalan)

* New translations copy.resx (Korean)

* New translations copy.resx (Dutch)

* New translations copy.resx (Polish)

* New translations copy.resx (Portuguese)

* New translations copy.resx (Russian)

* New translations copy.resx (Slovak)

* New translations copy.resx (Czech)

* New translations copy.resx (Bulgarian)

* New translations copy.resx (Belarusian)

* New translations copy.resx (Romanian)

* New translations copy.resx (French)

* New translations copy.resx (Spanish)

* New translations copy.resx (Afrikaans)

* New translations copy.resx (English, India)
2021-06-29 15:41:27 -04:00
Matt Portune
ef97417cd7 disable send tests (#1443) 2021-06-29 15:29:10 -04:00
Chad Scharf
7bdf4d8b18 Fix encoding of & in resx XML (#1445) 2021-06-29 14:32:07 -04:00
Matt Portune
a6c95d06b5 fix for vault timeout locking issue on android (#1442) 2021-06-29 10:23:20 -04:00
Matt Gibson
bd4a275558 Revert "Redefine cipher "share" to "move to organization" (#1433)" (#1440)
This reverts commit 2003ac9d2c.
2021-06-24 14:33:19 -05:00
Oscar Hinton
a2b46ee7cb Add help link to password reprompt (#1439) 2021-06-24 17:26:47 +02:00
Matt Gibson
2003ac9d2c Redefine cipher "share" to "move to organization" (#1433) 2021-06-18 06:52:01 -05:00
Oscar Hinton
2a5667251e Add a 250ms sleep before prompting for password (#1431) 2021-06-15 20:29:11 +02:00
Thomas Rittson
79589b07fc Use 2 iterations for local password hashing (#1423)
* Add HashPurpose parameter to HashPasswordAsync

* Use 2 iterations for local password hashing

* Force logout if user has old keyHash stored

* Revert "Force logout if user has old keyHash stored"

This reverts commit 497d4928fa.

* Add backwards compatability with existing keyHash
2021-06-15 07:39:34 +10:00
Oscar Hinton
0aed13a2cf Fix selecting ciphers in search not working (#1426) 2021-06-11 15:20:42 +02:00
Oscar Hinton
df412e75d1 More Autofill fixes (#1425) 2021-06-11 15:20:21 +02:00
Oscar Hinton
2b8dbde923 Fixes for password reprompt (#1416) 2021-06-10 17:57:18 +02:00
Matt Portune
33791a03ac track failed unlock attempts in storage (#1421) 2021-06-09 10:03:05 -04:00
Matt Gibson
80a33e98a2 Use type to ensure transmitted data is encrypted (#1422) 2021-06-09 08:45:30 -05:00
Trey Greer
afed18908b Asset update (#1417)
* updated screenshots and copy for iOS + Android

* fixed data for copy file

* fixed data for copy file for Google

* fixed more file formatting items for resx

* added Google video
2021-06-01 10:16:52 -04:00
Thomas Rittson
fe58dea3e0 Add encKeyValidation string to encrypted exports (#1412) 2021-05-29 06:16:19 +10:00
Kyle Spearrin
569045fcd5 add auth-email header to auth request (#1414) 2021-05-28 14:06:42 -04:00
Matt Portune
fdda670311 replaced blank taskaffinity with singletask launchmode (#1410) 2021-05-26 14:18:01 -04:00
CookieJarApps
fbb7b05b9c Added support for SmartCookieWeb and SmartCookieWeb Preview (#1411) 2021-05-26 13:41:16 -04:00
Oscar Hinton
976eeab6d7 Password reprompt (#1365)
* Make card number hidden

* Add support for password reprompt

* Rename PasswordPrompt to Reprompt

* Protect autofill

* Use Enums.CipherRepromptType

* Fix iOS not building

* Protect iOS autofill

* Update to match jslib

* Fix failing build
2021-05-21 15:13:54 +02:00
Matt Portune
e61bcd2785 fix for activity exporting and workaround for task affinity (#1408) 2021-05-19 15:55:16 -04:00
Captain Trips
570edb4319 fixes bitwarden/mobile#967 (#1067) 2021-05-17 15:38:21 -04:00
Vince Grassia
8fe8c42765 Pin versions of actions in workflow (#1405) 2021-05-17 15:15:08 -04:00
Matt Gibson
0eebe6b156 Encode exports as UTF8 (#1404) 2021-05-17 11:40:20 -05:00
Matt Portune
946831b37e version bumps (#1399) 2021-05-13 15:21:24 -04:00
Matt Portune
1d4e742d66 Forms update with CollectionView conversion (#1374)
* Forms update with CollectionView conversion

* updates

* removed unnecessary import
2021-05-13 14:36:20 -04:00
Matt Portune
29979f6b04 expand package visibility for Android 11+ (#1384) 2021-05-13 14:16:53 -04:00
Matt Portune
2f6e1ff477 lib updates (#1381)
* lib updates

* included csv and biometric lib
2021-05-13 14:15:26 -04:00
Thomas Rittson
c1030c48fa Explain how to verify email for file Sends (#1395)
* Explain how to verify email for file Sends

* Revert "Explain how to verify email for file Sends"

This reverts commit b72314bc93.

* Explain how to verify email for file Sends
2021-05-11 17:52:18 -04:00
Kyle Spearrin
ef5d08cb75 New Crowdin updates (#1397)
* New translations AppResources.resx (Romanian)

* New translations copy.resx (Tamil)

* 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 (Portuguese, Brazilian)

* New translations AppResources.resx (Indonesian)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Tamil)

* New translations copy.resx (Tamil)

* New translations AppResources.resx (Bengali)

* New translations AppResources.resx (Swedish)

* New translations AppResources.resx (Thai)

* New translations AppResources.resx (Croatian)

* New translations AppResources.resx (Estonian)

* New translations AppResources.resx (Latvian)

* New translations AppResources.resx (Hindi)

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

* New translations AppResources.resx (Malayalam)

* New translations AppResources.resx (Sinhala)

* New translations AppResources.resx (Norwegian Bokmal)

* New translations AppResources.resx (Turkish)

* New translations AppResources.resx (Serbian (Cyrillic))

* New translations AppResources.resx (French)

* New translations AppResources.resx (Greek)

* New translations AppResources.resx (Spanish)

* New translations copy.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 (German)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (Slovenian)

* 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 (Polish)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (English, India)
2021-05-11 14:03:24 -04:00
647 changed files with 421885 additions and 6007 deletions

81
.github/ISSUE_TEMPLATE/bug.yml vendored Normal file
View File

@@ -0,0 +1,81 @@
name: Bug Report
description: File a bug report
labels: [bug]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
Please do not submit feature requests. The [Community Forums](https://community.bitwarden.com) has a section for submitting, voting for, and discussing product feature requests.
- type: textarea
id: reproduce
attributes:
label: Steps To Reproduce
description: How can we reproduce the behavior.
value: |
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. Click on '...'
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Result
description: A clear and concise description of what you expected to happen.
validations:
required: true
- type: textarea
id: actual
attributes:
label: Actual Result
description: A clear and concise description of what is happening.
validations:
required: true
- type: textarea
id: screenshots
attributes:
label: Screenshots or Videos
description: If applicable, add screenshots and/or a short video to help explain your problem.
- type: textarea
id: additional-context
attributes:
label: Additional Context
description: Add any other context about the problem here.
- type: dropdown
id: os
attributes:
label: Operating System
description: What operating system are you seeing the problem on?
multiple: true
options:
- Android
- iOS
validations:
required: true
- type: input
id: os-version
attributes:
label: Operating System Version
description: What version of the operating system(s) are you seeing the problem on?
- type: input
id: device
attributes:
label: Device
description: Which device are you seeing the problem on?
placeholder: iPhone 12, Samsung Galaxy S10
- type: input
id: version
attributes:
label: Build Version
description: What version of our software are you running? (go to "Settings" → "About" in the app)
validations:
required: true
- type: checkboxes
id: beta
attributes:
label: Beta
options:
- label: Using a pre-release version of the application.

17
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
blank_issues_enabled: false
contact_links:
- name: Report mobile autofill failure
url: https://docs.google.com/forms/d/e/1FAIpQLScMopHyN7KGJs8hW562VTzbIGL4KcFnx0wJcsW0GYE1BnPiGA/viewform
about: We are aware of some situations where the Bitwarden mobile app will not autofill information correctly. This is something the Bitwarden team is actively working on but need your help as a community and active Bitwarden users!
- name: Feature Requests
url: https://community.bitwarden.com/c/feature-requests/
about: Request new features using the Community Forums. Please search existing feature requests before making a new one.
- name: Bitwarden Community Forums
url: https://community.bitwarden.com
about: Please visit the community forums for general community discussion, support and the development roadmap.
- name: Customer Support
url: https://bitwarden.com/contact/
about: Please contact our customer support for account issues and general customer support.
- name: Security Issues
url: https://hackerone.com/bitwarden
about: We use HackerOne to manage security disclosures.

32
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,32 @@
## Type of change
- [ ] Bug fix
- [ ] New feature development
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
- [ ] Build/deploy pipeline (DevOps)
- [ ] Other
## Objective
<!--Describe what the purpose of this PR is. For example: what bug you're fixing or what new feature you're adding-->
## Code changes
<!--Explain the changes you've made to each file or major component. This should help the reviewer understand your changes-->
<!--Also refer to any related changes or PRs in other repositories-->
* **file.ext:** Description of what was changed and why
## Screenshots
<!--Required for any UI changes. Delete if not applicable-->
## Testing requirements
<!--What functionality requires testing by QA? This includes testing new behavior and regression testing-->
## Before you submit
- [ ] I have added **unit tests** where it makes sense to do so (encouraged but not required)
- [ ] This change requires a **documentation update** (notify the documentation team)
- [ ] This change has particular **deployment requirements** (notify the DevOps team)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,4 @@
---
name: Build
on:
@@ -5,20 +6,16 @@ on:
branches-ignore:
- 'l10n_master'
- 'gh-pages'
release:
types:
- published
jobs:
cloc:
runs-on: ubuntu-latest
name: CLOC
runs-on: ubuntu-20.04
steps:
- name: Checkout repo
uses: actions/checkout@v2
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
- name: Set up cloc
- name: Set up CLOC
run: |
sudo apt-get update
sudo apt-get -y install cloc
@@ -26,272 +23,515 @@ jobs:
- name: Print lines of code
run: cloc --vcs git --exclude-dir Resources,store,test,Properties --include-lang C#,XAML
android:
runs-on: windows-latest
setup:
name: Setup
runs-on: ubuntu-20.04
outputs:
rc_branch_exists: ${{ steps.branch-check.outputs.rc_branch_exists }}
release_branch_exists: ${{ steps.branch-check.outputs.release_branch_exists }}
steps:
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
- name: Check if special branches exist
id: branch-check
run: |
if [[ $(git ls-remote --heads origin rc) ]]; then
echo "::set-output name=rc_branch_exists::1"
else
echo "::set-output name=rc_branch_exists::0"
fi
if [[ $(git ls-remote --heads origin release) ]]; then
echo "::set-output name=release_branch_exists::1"
else
echo "::set-output name=release_branch_exists::0"
fi
shell: bash
android:
name: Android
runs-on: windows-2019
needs: setup
steps:
- name: Set up MSBuild
uses: microsoft/setup-msbuild@v1
uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d # v1
- name: Print environment
run: |
nuget help
nuget help | grep Version
msbuild -version
dotnet --info
Write-Output "GitHub ref: $env:GITHUB_REF"
Write-Output "GitHub event: $env:GITHUB_EVENT"
shell: pwsh
env:
GITHUB_REF: ${{ github.ref }}
GITHUB_EVENT: ${{ github.event_name }}
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
uses: actions/checkout@v2
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
- name: Decrypt secrets
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/android/decrypt-secrets.ps1
shell: pwsh
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
run: |
mkdir -p ~/secrets
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./src/Android/app_play-keystore.jks ./.github/secrets/app_play-keystore.jks.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./src/Android/app_upload-keystore.jks ./.github/secrets/app_upload-keystore.jks.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./src/Android/google-services.json ./.github/secrets/google-services.json.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/play_creds.json ./.github/secrets/play_creds.json.gpg
shell: bash
- name: Increment version
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/android/increment-version.ps1
shell: pwsh
run: |
BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))
echo "########################################"
echo "##### Setting Version Code $BUILD_NUMBER"
echo "########################################"
sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
./src/Android/Properties/AndroidManifest.xml
shell: bash
- name: Restore packages
run: nuget restore
- name: Run Core Tests
- name: Run Core tests
run: dotnet test test/Core.Test/Core.Test.csproj
- name: Build Play Store publisher
run: dotnet build ./store/google/Publisher/Publisher.csproj -p:Configuration=Release
- name: Build for Play Store
run: ./.github/scripts/android/build.ps1 -configuration Release
run: |
$configuration = "Release";
Write-Output "########################################"
Write-Output "##### Build $configuration Configuration"
Write-Output "########################################"
msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" "/p:Configuration=$configuration"
shell: pwsh
- name: Sign for Play Store
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/android/sign-play.ps1
shell: pwsh
env:
PLAY_KEYSTORE_PASSWORD: ${{ secrets.PLAY_KEYSTORE_PASSWORD }}
UPLOAD_KEYSTORE_PASSWORD: ${{ secrets.UPLOAD_KEYSTORE_PASSWORD }}
run: |
$androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
Write-Output "########################################"
Write-Output "##### Sign Google Play Bundle Release Configuration"
Write-Output "########################################"
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" `
"/p:AndroidSigningKeyAlias=upload" "/p:AndroidSigningKeyPass=$($env:UPLOAD_KEYSTORE_PASSWORD)" `
"/p:AndroidSigningKeyStore=$("app_upload-keystore.jks")" `
"/p:AndroidSigningStorePass=$($env:UPLOAD_KEYSTORE_PASSWORD)" "/p:AndroidPackageFormat=aab" "/v:quiet"
Write-Output "########################################"
Write-Output "##### Copy Google Play Bundle to project root"
Write-Output "########################################"
$signedAabPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/Release/com.x8bit.bitwarden-Signed.aab");
$signedAabDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden.aab");
Copy-Item $signedAabPath $signedAabDestPath
Write-Output "########################################"
Write-Output "##### Sign APK Release Configuration"
Write-Output "########################################"
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" `
"/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:PLAY_KEYSTORE_PASSWORD)" `
"/p:AndroidSigningKeyStore=$("app_play-keystore.jks")" `
"/p:AndroidSigningStorePass=$($env:PLAY_KEYSTORE_PASSWORD)" "/v:quiet"
Write-Output "########################################"
Write-Output "##### Copy Release APK to project root"
Write-Output "########################################"
$signedApkPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/Release/com.x8bit.bitwarden-Signed.apk");
$signedApkDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden.apk");
Copy-Item $signedApkPath $signedApkDestPath
shell: pwsh
- name: Upload Play Store .aab artifact
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
with:
name: com.x8bit.bitwarden.aab
path: ./com.x8bit.bitwarden.aab
if-no-files-found: error
- name: Upload Play Store .apk artifact
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
with:
name: com.x8bit.bitwarden.apk
path: ./com.x8bit.bitwarden.apk
if-no-files-found: error
- name: Deploy to Play Store
if: |
(github.ref == 'refs/heads/master'
&& needs.setup.outputs.rc_branch_exists == 0
&& needs.setup.outputs.release_branch_exists == 0)
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.release_branch_exists == 0)
|| github.ref == 'refs/heads/release'
run: |
PUBLISHER_PATH="$GITHUB_WORKSPACE/store/google/Publisher/bin/Release/netcoreapp2.0/Publisher.dll"
CREDS_PATH="$HOME/secrets/play_creds.json"
AAB_PATH="$GITHUB_WORKSPACE/com.x8bit.bitwarden.aab"
TRACK="internal"
dotnet $PUBLISHER_PATH $CREDS_PATH $AAB_PATH $TRACK
shell: bash
f-droid:
name: F-Droid Build
runs-on: windows-2019
steps:
- name: Set up MSBuild
uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d # v1
- name: Print environment
run: |
nuget help | grep Version
msbuild -version
dotnet --info
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
- name: Decrypt secrets
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
run: |
mkdir -p ~/secrets
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./src/Android/app_fdroid-keystore.jks ./.github/secrets/app_fdroid-keystore.jks.gpg
shell: bash
- name: Increment version
run: |
BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))
echo "########################################"
echo "##### Setting Version Code $BUILD_NUMBER"
echo "########################################"
sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
./src/Android/Properties/AndroidManifest.xml
shell: bash
- name: Clean for F-Droid
run: ./.github/scripts/android/clean-fdroid.ps1
run: |
$androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
$appPath = $($env:GITHUB_WORKSPACE + "/src/App/App.csproj");
$androidManifest = $($env:GITHUB_WORKSPACE + "/src/Android/Properties/AndroidManifest.xml");
Write-Output "########################################"
Write-Output "##### Clean Android and App"
Write-Output "########################################"
msbuild "$($androidPath)" "/t:Clean" "/p:Configuration=FDroid"
msbuild "$($appPath)" "/t:Clean" "/p:Configuration=FDroid"
Write-Output "########################################"
Write-Output "##### Backup project files"
Write-Output "########################################"
Copy-Item $androidManifest $($androidManifest + ".original");
Copy-Item $androidPath $($androidPath + ".original");
Copy-Item $appPath $($appPath + ".original");
Write-Output "########################################"
Write-Output "##### Cleanup Android Manifest"
Write-Output "########################################"
$xml=New-Object XML;
$xml.Load($androidManifest);
$nsAndroid=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
$nsAndroid.AddNamespace("android", "http://schemas.android.com/apk/res/android");
$xml.Save($androidManifest);
Write-Output "########################################"
Write-Output "##### Uninstall from Android.csproj"
Write-Output "########################################"
$xml=New-Object XML;
$xml.Load($androidPath);
$ns=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
$ns.AddNamespace("ns", $xml.DocumentElement.NamespaceURI);
$firebaseNode=$xml.SelectSingleNode(`
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Firebase.Messaging']", $ns);
$firebaseNode.ParentNode.RemoveChild($firebaseNode);
$daggerNode=$xml.SelectSingleNode(`
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Google.Dagger']", $ns);
$daggerNode.ParentNode.RemoveChild($daggerNode);
$safetyNetNode=$xml.SelectSingleNode(`
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.GooglePlayServices.SafetyNet']", $ns);
$safetyNetNode.ParentNode.RemoveChild($safetyNetNode);
$xml.Save($androidPath);
Write-Output "########################################"
Write-Output "##### Uninstall from App.csproj"
Write-Output "########################################"
$xml=New-Object XML;
$xml.Load($appPath);
$appCenterNode=$xml.SelectSingleNode("/Project/ItemGroup/PackageReference[@Include='Microsoft.AppCenter.Crashes']");
$appCenterNode.ParentNode.RemoveChild($appCenterNode);
$xml.Save($appPath);
shell: pwsh
- name: Restore packages
run: nuget restore
- name: Build for F-Droid
run: ./.github/scripts/android/build.ps1 -configuration FDroid
run: |
$configuration = "FDroid";
Write-Output "########################################"
Write-Output "##### Build $configuration Configuration"
Write-Output "########################################"
msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" "/p:Configuration=$configuration"
shell: pwsh
- name: Sign for F-Droid
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/android/sign-fdroid.ps1
shell: pwsh
env:
FDROID_KEYSTORE_PASSWORD: ${{ secrets.FDROID_KEYSTORE_PASSWORD }}
run: |
Write-Output "########################################"
Write-Output "##### Sign FDroid Configuration"
Write-Output "########################################"
msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" `
"/t:SignAndroidPackage" "/p:Configuration=FDroid" "/p:AndroidKeyStore=true" `
"/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:FDROID_KEYSTORE_PASSWORD)" `
"/p:AndroidSigningKeyStore=$("app_fdroid-keystore.jks")" `
"/p:AndroidSigningStorePass=$($env:FDROID_KEYSTORE_PASSWORD)" "/v:quiet"
Write-Output "########################################"
Write-Output "##### Copy FDroid apk to project root"
Write-Output "########################################"
$signedApkPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/FDroid/com.x8bit.bitwarden-Signed.apk");
$signedApkDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden-fdroid.apk");
Copy-Item $signedApkPath $signedApkDestPath
shell: pwsh
- name: Upload F-Droid .apk artifact
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
with:
name: com.x8bit.bitwarden-fdroid.apk
path: ./com.x8bit.bitwarden-fdroid.apk
if-no-files-found: error
- name: Deploy to Play Store
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/android/deploy-play.ps1
shell: pwsh
- name: Upload release assets
if: github.event_name == 'release'
run: |
hub release edit `
-a ./com.x8bit.bitwarden.aab `
-a ./com.x8bit.bitwarden.apk `
-a ./com.x8bit.bitwarden-fdroid.apk `
-m "Version $($env:RELEASE_TAG_NAME.TrimStart('v'))" `
$env:RELEASE_TAG_NAME
shell: pwsh
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
android-ubuntu:
runs-on: ubuntu-latest
needs: android
steps:
- name: Set up Node
if: github.event_name == 'release'
uses: actions/setup-node@v1
with:
node-version: '10.x'
- name: Set up F-Droid server
if: github.event_name == 'release'
run: |
sudo apt-get -qq update
sudo apt-get -qqy install --no-install-recommends fdroidserver wget
- name: Set up git credentials
if: github.event_name == 'release'
env:
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
run: |
git config --global credential.helper store
echo "https://${ACCESS_TOKEN}:x-oauth-basic@github.com" >> ~/.git-credentials
git config --global user.email "ci@bitwarden.com"
git config --global user.name "Bitwarden CI"
- name: Print environment
if: github.event_name == 'release'
run: |
node --version
npm --version
git --version
Write-Output "GitHub ref: $env:GITHUB_REF"
Write-Output "GitHub event: $env:GITHUB_EVENT"
shell: pwsh
env:
GITHUB_REF: ${{ github.ref }}
GITHUB_EVENT: ${{ github.event_name }}
- name: Checkout repo
if: github.event_name == 'release'
uses: actions/checkout@v2
- name: Install Node dependencies
if: github.event_name == 'release'
run: npm install
- name: Decrypt secrets
if: github.event_name == 'release'
run: |
./.github/scripts/decrypt-secret.ps1 -filename store_fdroid-keystore.jks.gpg `
-output ./store/fdroid/keystore.jks
shell: pwsh
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
- name: Compile for F-Droid Store
if: github.event_name == 'release'
run: |
sudo chmod +x ./.github/scripts/android/compile-fdroid.sh
./.github/scripts/android/compile-fdroid.sh
env:
FDROID_STORE_KEYSTORE_PASSWORD: ${{ secrets.FDROID_STORE_KEYSTORE_PASSWORD }}
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
- name: Deploy to gh-pages
if: github.event_name == 'release'
run: npm run deploy
ios:
runs-on: macos-latest
name: Apple iOS
runs-on: macos-11
needs: setup
steps:
- name: Print environment
run: |
nuget help
nuget help | grep Version
msbuild -version
dotnet --info
Write-Output "GitHub ref: $env:GITHUB_REF"
Write-Output "GitHub event: $env:GITHUB_EVENT"
shell: pwsh
env:
GITHUB_REF: ${{ github.ref }}
GITHUB_EVENT: ${{ github.event_name }}
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
uses: actions/checkout@v2
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
- name: Decrypt secrets
run: ./.github/scripts/ios/decrypt-secrets.ps1
shell: pwsh
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
run: |
mkdir -p ~/secrets
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/bitwarden-mobile-key.p12 ./.github/secrets/bitwarden-mobile-key.p12.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/iphone-distribution-cert.p12 ./.github/secrets/iphone-distribution-cert.p12.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/dist_autofill.mobileprovision ./.github/secrets/dist_autofill.mobileprovision.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/dist_bitwarden.mobileprovision ./.github/secrets/dist_bitwarden.mobileprovision.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/dist_extension.mobileprovision ./.github/secrets/dist_extension.mobileprovision.gpg
shell: bash
- name: Increment version
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/ios/increment-version.ps1
shell: pwsh
run: |
BUILD_NUMBER=$((100 + $GITHUB_RUN_NUMBER))
- name: Set up keychain
run: ./.github/scripts/ios/setup-keychain.ps1
shell: pwsh
echo "########################################"
echo "##### Setting CFBundleVersion $BUILD_NUMBER"
echo "########################################"
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Extension/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist
shell: bash
- name: Set up Keychain
env:
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
MOBILE_KEY_PASSWORD: ${{ secrets.IOS_KEY_PASSWORD }}
DIST_CERT_PASSWORD: ${{ secrets.IOS_DIST_CERT_PASSWORD }}
run: |
security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p $KEYCHAIN_PASSWORD build.keychain
security set-keychain-settings -lut 1200 build.keychain
security import ~/secrets/bitwarden-mobile-key.p12 -k build.keychain -P $MOBILE_KEY_PASSWORD \
-T /usr/bin/codesign -T /usr/bin/security
security import ~/secrets/iphone-distribution-cert.p12 -k build.keychain -P $DIST_CERT_PASSWORD \
-T /usr/bin/codesign -T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain
shell: bash
- name: Set up provisioning profiles
run: ./.github/scripts/ios/setup-profiles.ps1
shell: pwsh
run: |
AUTOFILL_PROFILE_PATH=$HOME/secrets/dist_autofill.mobileprovision
BITWARDEN_PROFILE_PATH=$HOME/secrets/dist_bitwarden.mobileprovision
EXTENSION_PROFILE_PATH=$HOME/secrets/dist_extension.mobileprovision
PROFILES_DIR_PATH=$HOME/Library/MobileDevice/Provisioning\ Profiles
mkdir -p "$PROFILES_DIR_PATH"
AUTOFILL_UUID=$(grep UUID -A1 -a $AUTOFILL_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
cp $AUTOFILL_PROFILE_PATH "$PROFILES_DIR_PATH/$AUTOFILL_UUID.mobileprovision"
BITWARDEN_UUID=$(grep UUID -A1 -a $BITWARDEN_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
cp $BITWARDEN_PROFILE_PATH "$PROFILES_DIR_PATH/$BITWARDEN_UUID.mobileprovision"
EXTENSION_UUID=$(grep UUID -A1 -a $EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
cp $EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$EXTENSION_UUID.mobileprovision"
shell: bash
- name: Restore packages
run: nuget restore
- name: Archive Build for App Store
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/ios/build.ps1 -configuration AppStore -platform iPhone -archive
shell: pwsh
run: |
$configuration = "AppStore";
$platform = "iPhone";
- name: Build for App Store
if: github.ref != 'refs/heads/master'
run: ./.github/scripts/ios/build.ps1 -configuration AppStore -platform iPhone
Write-Output "########################################"
Write-Output "##### Archive $configuration Configuration for $platform Platform"
Write-Output "########################################"
msbuild "$($env:GITHUB_WORKSPACE + "/src/iOS/iOS.csproj")" "/p:Platform=$platform" `
"/p:Configuration=$configuration" "/p:ArchiveOnBuild=true" "/t:`"Build`""
Write-Output "########################################"
Write-Output "##### Done"
Write-Output "########################################"
ls ~/Library/Developer/Xcode/Archives
shell: pwsh
- name: Export .ipa for App Store
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/ios/export-ipa.ps1 -method app-store
shell: pwsh
run: |
EXPORT_OPTIONS_PATH="./.github/resources/export-options-app-store.plist"
ARCHIVE_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive"
EXPORT_PATH="./bitwarden-export"
xcodebuild -exportArchive -archivePath $ARCHIVE_PATH -exportPath $EXPORT_PATH \
-exportOptionsPlist $EXPORT_OPTIONS_PATH
shell: bash
- name: Upload App Store .ipa artifact
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
with:
name: Bitwarden.ipa
path: ./bitwarden-export/Bitwarden.ipa
if-no-files-found: error
- name: Deploy to App Store
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
run: ./.github/scripts/ios/deploy-app-store.ps1
shell: pwsh
if: |
(github.ref == 'refs/heads/master'
&& needs.setup.outputs.rc_branch_exists == 0
&& needs.setup.outputs.release_branch_exists == 0)
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.release_branch_exists == 0)
|| github.ref == 'refs/heads/release'
env:
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
- name: Upload release assets
if: github.event_name == 'release'
run: |
hub release edit `
-a ./bitwarden-export/Bitwarden.ipa `
-m "Version $($env:RELEASE_TAG_NAME.TrimStart('v'))" `
$env:RELEASE_TAG_NAME
shell: pwsh
xcrun altool --upload-app --type ios --file "./bitwarden-export/Bitwarden.ipa" \
--username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
shell: bash
check-failures:
name: Check for failures
if: always()
runs-on: ubuntu-20.04
needs:
- cloc
- android
- f-droid
- ios
steps:
- name: Check if any job failed
if: ${{ (github.ref == 'refs/heads/master') || (github.ref == 'refs/heads/rc') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
CLOC_STATUS: ${{ needs.cloc.result }}
ANDROID_STATUS: ${{ needs.android.result }}
F_DROID_STATUS: ${{ needs.f-droid.result }}
IOS_STATUS: ${{ needs.ios.result }}
run: |
if [ "$CLOC_STATUS" = "failure" ]; then
exit 1
elif [ "$ANDROID_STATUS" = "failure" ]; then
exit 1
elif [ "$F_DROID_STATUS" = "failure" ]; then
exit 1
elif [ "$IOS_STATUS" = "failure" ]; then
exit 1
fi
- name: Login to Azure - Prod Subscription
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
if: failure()
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets
id: retrieve-secrets
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
if: failure()
with:
keyvault: "bitwarden-prod-kv"
secrets: "devops-alerts-slack-webhook-url"
- name: Notify Slack on failure
uses: act10ns/slack@e4e71685b9b239384b0f676a63c32367f59c2522 # v1.2.2
if: failure()
env:
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
with:
status: ${{ job.status }}

49
.github/workflows/crowdin-sync.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
---
name: Crowdin Sync
on:
workflow_dispatch:
inputs: {}
# schedule:
# - cron: '0 0 * * *'
jobs:
crowdin-sync:
name: Autosync
runs-on: ubuntu-20.04
env:
_CROWDIN_PROJECT_ID: "269690"
steps:
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
- name: Login to Azure
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets
id: retrieve-secrets
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
with:
keyvault: "bitwarden-prod-kv"
secrets: "crowdin-api-token"
- name: Download translations
uses: crowdin/github-action@e39093fd75daae7859c68eded4b43d42ec78d8ea
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
with:
config: crowdin.yml
crowdin_branch_name: master
upload_sources: false
upload_translations: false
download_translations: true
github_user_name: "github-actions"
github_user_email: "<>"
commit_message: "Autosync the updated translations"
localization_branch_name: crowdin-auto-sync
create_pull_request: true
pull_request_title: "Autosync Crowdin Translations"
pull_request_body: "Autosync the updated translations"

153
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,153 @@
---
name: Release
on:
workflow_dispatch:
jobs:
release:
name: Create Release
runs-on: ubuntu-20.04
steps:
- name: Branch check
run: |
if [[ "$GITHUB_REF" != "refs/heads/release" ]]; then
echo "==================================="
echo "[!] Can only release from the 'release' branch"
echo "==================================="
exit 1
fi
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
with:
ref: release
- name: Retrieve Mobile release version
id: retrieve-mobile-version
run: |
ver=$(sed -n -e '/android:versionName/ s/.*\= *//p' ./src/Android/Properties/AndroidManifest.xml | tr -d '"')
echo "::set-output name=mobile_version::${ver}"
shell: bash
- name: Check to make sure Mobile release version has been bumped
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
latest_ver=$(hub release -L 1 -f '%T')
latest_ver=${latest_ver:1}
echo "Latest version: $latest_ver"
ver=${{ steps.retrieve-mobile-version.outputs.mobile_version }}
echo "Version: $ver"
if [ "$latest_ver" = "$ver" ]; then
echo "Version has not been bumped!"
exit 1
fi
shell: bash
- name: Download all artifacts
uses: dawidd6/action-download-artifact@b9571484721e8187f1fd08147b497129f8972c74 # v2.14.0
with:
workflow: build.yml
workflow_conclusion: success
branch: release
- name: Create release
uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09 # v2.8.5
with:
artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab,
./com.x8bit.bitwarden.apk/com.x8bit.bitwarden.apk,
./com.x8bit.bitwarden-fdroid.apk/com.x8bit.bitwarden-fdroid.apk,
./Bitwarden.ipa/Bitwarden.ipa"
commit: ${{ github.sha }}
tag: v${{ steps.retrieve-mobile-version.outputs.mobile_version }}
name: Version ${{ steps.retrieve-mobile-version.outputs.mobile_version }}
body: "<insert release notes here>"
token: ${{ secrets.GITHUB_TOKEN }}
draft: true
f-droid:
name: F-Droid Release
runs-on: ubuntu-20.04
needs: release
steps:
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
with:
ref: release
- name: Download F-Droid .apk artifact
uses: dawidd6/action-download-artifact@b9571484721e8187f1fd08147b497129f8972c74 # v2.14.0
with:
workflow: build.yml
workflow_conclusion: success
branch: release
name: com.x8bit.bitwarden-fdroid.apk
- name: Set up Node
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea # v2.3.0
with:
node-version: '10.x'
- name: Set up F-Droid server
run: |
sudo apt-get -qq update
sudo apt-get -qqy install --no-install-recommends fdroidserver wget
- name: Set up Git credentials
env:
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
run: |
git config --global credential.helper store
echo "https://${ACCESS_TOKEN}:x-oauth-basic@github.com" >> ~/.git-credentials
git config --global user.email "ci@bitwarden.com"
git config --global user.name "Bitwarden CI"
- name: Print environment
run: |
node --version
npm --version
git --version
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Install Node dependencies
run: npm install
- name: Decrypt secrets
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
run: |
mkdir -p ~/secrets
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./store/fdroid/keystore.jks ./.github/secrets/store_fdroid-keystore.jks.gpg
- name: Compile for F-Droid Store
env:
FDROID_STORE_KEYSTORE_PASSWORD: ${{ secrets.FDROID_STORE_KEYSTORE_PASSWORD }}
run: |
cd $GITHUB_WORKSPACE
mkdir dist
cp CNAME ./dist
cd store
chmod 600 fdroid/config.py fdroid/keystore.jks
mkdir -p temp/fdroid
TEMP_DIR="$GITHUB_WORKSPACE/store/temp/fdroid"
cd fdroid
echo "keypass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
echo "keystorepass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
echo "local_copy_dir=\"$TEMP_DIR\"" >>config.py
mkdir -p repo
mv $GITHUB_WORKSPACE/com.x8bit.bitwarden-fdroid.apk ./repo/
fdroid update
fdroid server update
cd ..
rm -rf temp/fdroid/archive
mv -v temp/fdroid ../dist
cd fdroid
cp index.html btn.png qr.png ../../dist/fdroid
cd $GITHUB_WORKSPACE
- name: Deploy to gh-pages
run: npm run deploy

View File

@@ -1,53 +0,0 @@
<!-- 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

@@ -8,7 +8,7 @@
The Bitwarden mobile application is written in C# with Xamarin Android, Xamarin iOS, and Xamarin Forms.
<img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-android-myvault.png" alt="" width="300" height="533" /> <img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-ios-myvault.png" alt="" width="300" height="533" />
<img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-android-myvault.png" alt="" width="325" height="650" /> <img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-ios-myvault.png" alt="" width="300" height="650" />
# Build/Run

View File

@@ -44,6 +44,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "test\Common\Commo
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Test", "test\Core.Test\Core.Test.csproj", "{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Safari", "src\iOS.Safari\iOS.Safari.csproj", "{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@@ -414,6 +416,36 @@ Global
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhone.Build.0 = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|Any CPU.ActiveCfg = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|Any CPU.Build.0 = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|iPhone.ActiveCfg = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|iPhone.Build.0 = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|Any CPU.ActiveCfg = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|Any CPU.Build.0 = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|iPhone.ActiveCfg = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|iPhone.Build.0 = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|iPhoneSimulator.ActiveCfg = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|iPhoneSimulator.Build.0 = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|Any CPU.Build.0 = Debug|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|iPhone.ActiveCfg = Debug|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|iPhone.Build.0 = Debug|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|Any CPU.ActiveCfg = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|Any CPU.Build.0 = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|iPhone.ActiveCfg = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|iPhone.Build.0 = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|Any CPU.ActiveCfg = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|Any CPU.Build.0 = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|iPhone.ActiveCfg = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|iPhone.Build.0 = Release|iPhone
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -431,6 +463,7 @@ Global
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39} = {8904C536-C67D-420F-9971-51B26574C3AA}
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA}
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7} = {D10CA4A9-F866-40E1-B658-F69051236C71}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}

View File

@@ -1,3 +1,5 @@
project_id_env: _CROWDIN_PROJECT_ID
api_token_env: CROWDIN_API_TOKEN
files:
- source: /src/App/Resources/AppResources.resx
translation: /src/App/Resources/AppResources.%two_letters_code%.resx

710
package-lock.json generated
View File

@@ -1,8 +1,468 @@
{
"name": "bitwarden-mobile",
"version": "0.0.0",
"lockfileVersion": 1,
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "bitwarden-mobile",
"version": "0.0.0",
"devDependencies": {
"gh-pages": "^3.2.3"
}
},
"node_modules/array-union": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
"integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
"dev": true,
"dependencies": {
"array-uniq": "^1.0.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/array-uniq": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
"integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/async": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
"dev": true,
"dependencies": {
"lodash": "^4.17.14"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
"dev": true
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"node_modules/email-addresses": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz",
"integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==",
"dev": true
},
"node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true,
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/filename-reserved-regex": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
"integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/filenamify": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz",
"integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==",
"dev": true,
"dependencies": {
"filename-reserved-regex": "^2.0.0",
"strip-outer": "^1.0.1",
"trim-repeated": "^1.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/find-cache-dir": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
"integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
"dev": true,
"dependencies": {
"commondir": "^1.0.1",
"make-dir": "^3.0.2",
"pkg-dir": "^4.1.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/avajs/find-cache-dir?sponsor=1"
}
},
"node_modules/find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"engines": {
"node": ">=6 <7 || >=8"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"node_modules/gh-pages": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz",
"integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==",
"dev": true,
"dependencies": {
"async": "^2.6.1",
"commander": "^2.18.0",
"email-addresses": "^3.0.1",
"filenamify": "^4.3.0",
"find-cache-dir": "^3.3.1",
"fs-extra": "^8.1.0",
"globby": "^6.1.0"
},
"bin": {
"gh-pages": "bin/gh-pages.js",
"gh-pages-clean": "bin/gh-pages-clean.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/globby": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
"integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
"dev": true,
"dependencies": {
"array-union": "^1.0.1",
"glob": "^7.0.3",
"object-assign": "^4.0.1",
"pify": "^2.0.0",
"pinkie-promise": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/graceful-fs": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
"dev": true
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"node_modules/jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
"dev": true,
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
"dependencies": {
"p-locate": "^4.1.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"node_modules/make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true,
"dependencies": {
"semver": "^6.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"dependencies": {
"wrappy": "1"
}
},
"node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"dependencies": {
"p-try": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dev": true,
"dependencies": {
"p-limit": "^2.2.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/pinkie": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
"integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/pinkie-promise": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
"integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
"dev": true,
"dependencies": {
"pinkie": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
"dev": true,
"dependencies": {
"find-up": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/strip-outer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
"integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==",
"dev": true,
"dependencies": {
"escape-string-regexp": "^1.0.2"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/trim-repeated": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
"integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=",
"dev": true,
"dependencies": {
"escape-string-regexp": "^1.0.2"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true,
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
}
},
"dependencies": {
"array-union": {
"version": "1.0.2",
@@ -20,18 +480,18 @@
"dev": true
},
"async": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
"integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
"dev": true,
"requires": {
"lodash": "^4.17.10"
"lodash": "^4.17.14"
}
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"brace-expansion": {
@@ -45,9 +505,15 @@
}
},
"commander": {
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
"commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
"dev": true
},
"concat-map": {
@@ -56,6 +522,12 @@
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"email-addresses": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz",
"integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==",
"dev": true
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -63,39 +535,50 @@
"dev": true
},
"filename-reserved-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz",
"integrity": "sha1-5hz4BfDeHJhFZ9A4bcXfUO5a9+Q=",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
"integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=",
"dev": true
},
"filenamify": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/filenamify/-/filenamify-1.2.1.tgz",
"integrity": "sha1-qfL/0RxQO+0wABUCknI3jx8TZaU=",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz",
"integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==",
"dev": true,
"requires": {
"filename-reserved-regex": "^1.0.0",
"strip-outer": "^1.0.0",
"filename-reserved-regex": "^2.0.0",
"strip-outer": "^1.0.1",
"trim-repeated": "^1.0.0"
}
},
"filenamify-url": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/filenamify-url/-/filenamify-url-1.0.0.tgz",
"integrity": "sha1-syvYExnvWGO3MHi+1Q9GpPeXX1A=",
"find-cache-dir": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
"integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
"dev": true,
"requires": {
"filenamify": "^1.0.0",
"humanize-url": "^1.0.0"
"commondir": "^1.0.1",
"make-dir": "^3.0.2",
"pkg-dir": "^4.1.0"
}
},
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
"requires": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
}
},
"fs-extra": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
"integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==",
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
}
@@ -107,24 +590,24 @@
"dev": true
},
"gh-pages": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-1.2.0.tgz",
"integrity": "sha512-cGLYAvxtlQ1iTwAS4g7FreZPXoE/g62Fsxln2mmR19mgs4zZI+XJ+wVVUhBFCF/0+Nmvbq+abyTWue1m1BSnmg==",
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz",
"integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==",
"dev": true,
"requires": {
"async": "2.6.1",
"commander": "2.15.1",
"filenamify-url": "^1.0.0",
"fs-extra": "^5.0.0",
"globby": "^6.1.0",
"graceful-fs": "4.1.11",
"rimraf": "^2.6.2"
"async": "^2.6.1",
"commander": "^2.18.0",
"email-addresses": "^3.0.1",
"filenamify": "^4.3.0",
"find-cache-dir": "^3.3.1",
"fs-extra": "^8.1.0",
"globby": "^6.1.0"
}
},
"glob": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
"integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
@@ -149,21 +632,11 @@
}
},
"graceful-fs": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
"dev": true
},
"humanize-url": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/humanize-url/-/humanize-url-1.0.1.tgz",
"integrity": "sha1-9KuZ4NKIF0yk4eUEB8VfuuRk7/8=",
"dev": true,
"requires": {
"normalize-url": "^1.0.0",
"strip-url-auth": "^1.0.0"
}
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -175,15 +648,9 @@
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
},
"is-plain-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
"integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"jsonfile": {
@@ -195,12 +662,30 @@
"graceful-fs": "^4.1.6"
}
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
"requires": {
"p-locate": "^4.1.0"
}
},
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true,
"requires": {
"semver": "^6.0.0"
}
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -210,18 +695,6 @@
"brace-expansion": "^1.1.7"
}
},
"normalize-url": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
"integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=",
"dev": true,
"requires": {
"object-assign": "^4.0.1",
"prepend-http": "^1.0.0",
"query-string": "^4.1.0",
"sort-keys": "^1.0.0"
}
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -237,6 +710,36 @@
"wrappy": "1"
}
},
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dev": true,
"requires": {
"p-limit": "^2.2.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -264,44 +767,19 @@
"pinkie": "^2.0.0"
}
},
"prepend-http": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
"dev": true
},
"query-string": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
"integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
"pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
"dev": true,
"requires": {
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
"find-up": "^4.0.0"
}
},
"rimraf": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"dev": true,
"requires": {
"glob": "^7.1.3"
}
},
"sort-keys": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
"integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=",
"dev": true,
"requires": {
"is-plain-obj": "^1.0.0"
}
},
"strict-uri-encode": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
},
"strip-outer": {
@@ -313,12 +791,6 @@
"escape-string-regexp": "^1.0.2"
}
},
"strip-url-auth": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/strip-url-auth/-/strip-url-auth-1.0.1.tgz",
"integrity": "sha1-IrD6OkE4WzO+PzMVUbu4N/oM164=",
"dev": true
},
"trim-repeated": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",

View File

@@ -6,6 +6,6 @@
"clean:l10n": "git push origin --delete l10n_master"
},
"devDependencies": {
"gh-pages": "^1.2.0"
"gh-pages": "^3.2.3"
}
}

View File

@@ -9,7 +9,7 @@ using Bit.Core.Utilities;
namespace Bit.Droid.Accessibility
{
[Activity(Theme = "@style/LightTheme.Splash", WindowSoftInputMode = SoftInput.StateHidden)]
[Activity(Theme = "@style/BaseTheme", WindowSoftInputMode = SoftInput.StateHidden)]
public class AccessibilityActivity : Activity
{
private DateTime? _lastLaunch = null;

View File

@@ -31,7 +31,6 @@ namespace Bit.Droid.Accessibility
// So keep them in sync with:
// - AutofillHelpers.{TrustedBrowsers,CompatBrowsers}
// - Resources/xml/autofillservice.xml
new Browser("alook.browser", "search_fragment_input_view"),
new Browser("com.amazon.cloud9", "url"),
new Browser("com.android.browser", "url"),
new Browser("com.android.chrome", "url_bar"),
@@ -46,10 +45,13 @@ namespace Bit.Droid.Accessibility
new Browser("com.chrome.beta", "url_bar"),
new Browser("com.chrome.canary", "url_bar"),
new Browser("com.chrome.dev", "url_bar"),
new Browser("com.cookiegames.smartcookie", "search"),
new Browser("com.cookiejarapps.android.smartcookieweb", "mozac_browser_toolbar_url_view"),
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"),
// Rem. for "com.google.android.captiveportallogin": URL displayed in ActionBar subtitle without viewId.
new Browser("com.jamal2367.styx", "search"),
new Browser("com.kiwibrowser.browser", "url_bar"),
new Browser("com.microsoft.emmx", "url_bar"),
@@ -107,6 +109,7 @@ namespace Bit.Droid.Accessibility
new Browser("org.torproject.torbrowser_alpha", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy (before v10.0a8)
new Browser("org.ungoogled.chromium.extensions.stable", "url_bar"),
new Browser("org.ungoogled.chromium.stable", "url_bar"),
new Browser("us.spotco.fennec_dos", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
// [Section B] Entries only present here
//

View File

@@ -28,7 +28,6 @@
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>3</WarningLevel>
<AndroidLinkMode>None</AndroidLinkMode>
<AndroidSupportedAbis />
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
</PropertyGroup>
@@ -75,24 +74,24 @@
<Version>2.1.0.4</Version>
</PackageReference>
<PackageReference Include="Portable.BouncyCastle">
<Version>1.8.6.7</Version>
<Version>1.8.10</Version>
</PackageReference>
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.3-beta01" />
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" />
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.9" />
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0.11" />
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0.10" />
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.2.5.2" />
<PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.8" />
<PackageReference Include="Xamarin.Essentials">
<Version>1.5.3.2</Version>
<Version>1.7.0</Version>
</PackageReference>
<PackageReference Include="Xamarin.Firebase.Messaging">
<Version>121.0.1</Version>
<Version>122.0.0</Version>
</PackageReference>
<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.Google.Dagger" Version="2.27.0" />
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.4.0.4" />
<PackageReference Include="Xamarin.Google.Dagger" Version="2.37.0" />
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
<Version>71.1600.0</Version>
<Version>117.0.1</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
@@ -112,26 +111,25 @@
<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="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" />
<Compile Include="Renderers\CipherViewCellRenderer.cs" />
<Compile Include="Renderers\ExtendedGridRenderer.cs" />
<Compile Include="Renderers\ExtendedDatePickerRenderer.cs" />
<Compile Include="Renderers\CustomTabbedRenderer.cs" />
<Compile Include="Renderers\ExtendedStackLayoutRenderer.cs" />
<Compile Include="Renderers\ExtendedStepperRenderer.cs" />
<Compile Include="Renderers\CustomSwitchRenderer.cs" />
<Compile Include="Renderers\ExtendedTimePickerRenderer.cs" />
<Compile Include="Renderers\ExtendedSliderRenderer.cs" />
<Compile Include="Renderers\CustomEditorRenderer.cs" />
<Compile Include="Renderers\CustomPickerRenderer.cs" />
<Compile Include="Renderers\CustomEntryRenderer.cs" />
<Compile Include="Renderers\CustomSearchBarRenderer.cs" />
<Compile Include="Renderers\ExtendedListViewRenderer.cs" />
<Compile Include="Renderers\HybridWebViewRenderer.cs" />
<Compile Include="Renderers\SendViewCellRenderer.cs" />
<Compile Include="Services\AndroidPushNotificationService.cs" />
<Compile Include="Services\AndroidLogService.cs" />
<Compile Include="MainApplication.cs" />
@@ -147,7 +145,9 @@
<Compile Include="Tiles\MyVaultTileService.cs" />
<Compile Include="Utilities\AndroidHelpers.cs" />
<Compile Include="Utilities\AppCenterHelper.cs" />
<Compile Include="Utilities\ThemeHelpers.cs" />
<Compile Include="WebAuthCallbackActivity.cs" />
<Compile Include="Renderers\SelectableLabelRenderer.cs" />
</ItemGroup>
<ItemGroup>
<AndroidAsset Include="Assets\FontAwesome.ttf" />
@@ -173,6 +173,7 @@
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
<AndroidResource Include="Resources\drawable\id.xml" />
<AndroidResource Include="Resources\drawable\info.xml" />
<AndroidResource Include="Resources\drawable\list_item_bg.xml" />
<AndroidResource Include="Resources\drawable\lock.xml" />
<AndroidResource Include="Resources\drawable\login.xml" />
<AndroidResource Include="Resources\drawable\logo.xml" />
@@ -185,7 +186,7 @@
<AndroidResource Include="Resources\drawable\shield.xml" />
<AndroidResource Include="Resources\drawable-v23\splash_screen.xml" />
<AndroidResource Include="Resources\drawable-v23\splash_screen_dark.xml" />
<AndroidResource Include="Resources\layout\SendViewCell.axml" />
<AndroidResource Include="Resources\drawable\switch_thumb.xml" />
<AndroidResource Include="Resources\layout\Tabbar.axml" />
<AndroidResource Include="Resources\layout\Toolbar.axml" />
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
@@ -262,12 +263,6 @@
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\layout\CipherViewCell.axml">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\xml\app_restrictions.xml">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>

View File

@@ -35,6 +35,7 @@ namespace Bit.Droid.Autofill
public static HashSet<string> TrustedBrowsers = new HashSet<string>
{
"com.duckduckgo.mobile.android",
"com.google.android.googlequicksearchbox",
"org.mozilla.focus",
"org.mozilla.klar",
};
@@ -48,7 +49,6 @@ namespace Bit.Droid.Autofill
// - ... to keep this list in sync with values in AccessibilityHelpers.SupportedBrowsers [Section A], too.
public static HashSet<string> CompatBrowsers = new HashSet<string>
{
"alook.browser",
"com.amazon.cloud9",
"com.android.browser",
"com.android.chrome",
@@ -63,9 +63,12 @@ namespace Bit.Droid.Autofill
"com.chrome.beta",
"com.chrome.canary",
"com.chrome.dev",
"com.cookiegames.smartcookie",
"com.cookiejarapps.android.smartcookieweb",
"com.ecosia.android",
"com.google.android.apps.chrome",
"com.google.android.apps.chrome_dev",
"com.google.android.captiveportallogin",
"com.jamal2367.styx",
"com.kiwibrowser.browser",
"com.microsoft.emmx",
@@ -120,6 +123,7 @@ namespace Bit.Droid.Autofill
"org.torproject.torbrowser_alpha",
"org.ungoogled.chromium.extensions.stable",
"org.ungoogled.chromium.stable",
"us.spotco.fennec_dos",
};
// The URLs are blacklisted from autofilling
@@ -140,13 +144,14 @@ namespace Bit.Droid.Autofill
{
var allCiphers = ciphers.Item1.ToList();
allCiphers.AddRange(ciphers.Item2.ToList());
return allCiphers.Select(c => new FilledItem(c)).ToList();
var nonPromptCiphers = allCiphers.Where(cipher => cipher.Reprompt == CipherRepromptType.None);
return nonPromptCiphers.Select(c => new FilledItem(c)).ToList();
}
}
else if (parser.FieldCollection.FillableForCard)
{
var ciphers = await cipherService.GetAllDecryptedAsync();
return ciphers.Where(c => c.Type == CipherType.Card).Select(c => new FilledItem(c)).ToList();
return ciphers.Where(c => c.Type == CipherType.Card && c.Reprompt == CipherRepromptType.None).Select(c => new FilledItem(c)).ToList();
}
return new List<FilledItem>();
}

View File

@@ -57,6 +57,7 @@ namespace Bit.Droid.Autofill
}
List<FilledItem> items = null;
await _vaultTimeoutService.CheckVaultTimeoutAsync();
var locked = await _vaultTimeoutService.IsLockedAsync();
if (!locked)
{
@@ -93,24 +94,12 @@ namespace Bit.Droid.Autofill
_policyService ??= ServiceContainer.Resolve<IPolicyService>("policyService");
var personalOwnershipPolicies = await _policyService.GetAll(PolicyType.PersonalOwnership);
if (personalOwnershipPolicies != null)
var personalOwnershipPolicyApplies = await _policyService.PolicyAppliesToUser(PolicyType.PersonalOwnership);
if (personalOwnershipPolicyApplies)
{
_userService ??= ServiceContainer.Resolve<IUserService>("userService");
foreach (var policy in personalOwnershipPolicies)
{
if (policy.Enabled)
{
var org = await _userService.GetOrganizationAsync(policy.OrganizationId);
if (org != null && org.Enabled && org.UsePolicies && !org.canManagePolicies
&& org.Status == OrganizationUserStatusType.Confirmed)
{
return;
}
}
}
return;
}
var parser = new Parser(structure, ApplicationContext);
parser.Parse();

View File

@@ -1,6 +1,6 @@
using Android.Graphics.Drawables;
using Bit.App.Utilities;
using Bit.Droid.Effects;
using Bit.Droid.Utilities;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
@@ -14,7 +14,7 @@ namespace Bit.Droid.Effects
if (Control is Android.Widget.Button button)
{
var gd = new GradientDrawable();
gd.SetColor(ThemeManager.GetResourceColor("FabColor").ToAndroid());
gd.SetColor(ThemeHelpers.FabColor);
gd.SetCornerRadius(100);
button.SetBackground(gd);

View File

@@ -1,23 +0,0 @@
using Android.Widget;
using Bit.Droid.Effects;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportEffect(typeof(SelectableLabelEffect), "SelectableLabelEffect")]
namespace Bit.Droid.Effects
{
public class SelectableLabelEffect : PlatformEffect
{
protected override void OnAttached()
{
if (Control is TextView textView)
{
textView.SetTextIsSelectable(true);
}
}
protected override void OnDetached()
{
}
}
}

View File

@@ -15,9 +15,9 @@ using Bit.Droid.Receivers;
using Bit.App.Models;
using Bit.Core.Enums;
using Android.Nfc;
using Bit.App.Utilities;
using System.Threading.Tasks;
using AndroidX.Core.Content;
using Bit.App.Utilities;
using ZXing.Net.Mobile.Android;
namespace Bit.Droid
@@ -27,9 +27,10 @@ namespace Bit.Droid
Icon = "@mipmap/ic_launcher",
Theme = "@style/LaunchTheme",
MainLauncher = true,
LaunchMode = LaunchMode.SingleTask,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation |
ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden |
ConfigChanges.Navigation)]
ConfigChanges.Navigation | ConfigChanges.UiMode)]
[IntentFilter(
new[] { Intent.ActionSend },
Categories = new[] { Intent.CategoryDefault },
@@ -50,7 +51,6 @@ namespace Bit.Droid
private IAppIdService _appIdService;
private IStorageService _storageService;
private IEventService _eventService;
private PendingIntent _vaultTimeoutAlarmPendingIntent;
private PendingIntent _clearClipboardPendingIntent;
private PendingIntent _eventUploadPendingIntent;
private AppOptions _appOptions;
@@ -63,9 +63,6 @@ namespace Bit.Droid
var eventUploadIntent = new Intent(this, typeof(EventUploadReceiver));
_eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent,
PendingIntentFlags.UpdateCurrent);
var alarmIntent = new Intent(this, typeof(LockAlarmReceiver));
_vaultTimeoutAlarmPendingIntent = PendingIntent.GetBroadcast(this, 0, alarmIntent,
PendingIntentFlags.UpdateCurrent);
var clearClipboardIntent = new Intent(this, typeof(ClearClipboardAlarmReceiver));
_clearClipboardPendingIntent = PendingIntent.GetBroadcast(this, 0, clearClipboardIntent,
PendingIntentFlags.UpdateCurrent);
@@ -84,7 +81,6 @@ namespace Bit.Droid
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
UpdateTheme(ThemeManager.GetTheme(true));
base.OnCreate(savedInstanceState);
if (!CoreHelpers.InDebugMode())
{
@@ -103,20 +99,7 @@ namespace Bit.Droid
_broadcasterService.Subscribe(_activityKey, (message) =>
{
if (message.Command == "scheduleVaultTimeoutTimer")
{
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
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 == "cancelVaultTimeoutTimer")
{
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
alarmManager.Cancel(_vaultTimeoutAlarmPendingIntent);
}
else if (message.Command == "startEventTimer")
if (message.Command == "startEventTimer")
{
StartEventAlarm();
}
@@ -134,7 +117,7 @@ namespace Bit.Droid
}
else if (message.Command == "updatedTheme")
{
RestartApp();
Xamarin.Forms.Device.BeginInvokeOnMainThread(() => AppearanceAdjustments());
}
else if (message.Command == "exit")
{
@@ -157,6 +140,7 @@ namespace Bit.Droid
{
base.OnResume();
Xamarin.Essentials.Platform.OnResume();
AppearanceAdjustments();
if (_deviceActionService.SupportsNfc())
{
try
@@ -165,39 +149,48 @@ namespace Bit.Droid
}
catch { }
}
var setRestrictions = AndroidHelpers.SetPreconfiguredRestrictionSettingsAsync(this);
AndroidHelpers.SetPreconfiguredRestrictionSettingsAsync(this)
.GetAwaiter()
.GetResult();
}
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
if (intent.GetBooleanExtra("generatorTile", false))
try
{
_messagingService.Send("popAllAndGoToTabGenerator");
if (_appOptions != null)
if (intent.GetBooleanExtra("generatorTile", false))
{
_appOptions.GeneratorTile = true;
_messagingService.Send("popAllAndGoToTabGenerator");
if (_appOptions != null)
{
_appOptions.GeneratorTile = true;
}
}
else if (intent.GetBooleanExtra("myVaultTile", false))
{
_messagingService.Send("popAllAndGoToTabMyVault");
if (_appOptions != null)
{
_appOptions.MyVaultTile = true;
}
}
else if (intent.Action == Intent.ActionSend && intent.Type != null)
{
if (_appOptions != null)
{
_appOptions.CreateSend = GetCreateSendRequest(intent);
}
_messagingService.Send("popAllAndGoToTabSend");
}
else
{
ParseYubiKey(intent.DataString);
}
}
else if (intent.GetBooleanExtra("myVaultTile", false))
catch (Exception e)
{
_messagingService.Send("popAllAndGoToTabMyVault");
if (_appOptions != null)
{
_appOptions.MyVaultTile = true;
}
}
else if (intent.Action == Intent.ActionSend && intent.Type != null)
{
if (_appOptions != null)
{
_appOptions.CreateSend = GetCreateSendRequest(intent);
}
_messagingService.Send("popAllAndGoToTabSend");
}
else
{
ParseYubiKey(intent.DataString);
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", e.GetType(), e.StackTrace);
}
}
@@ -343,6 +336,11 @@ namespace Bit.Droid
{
if (intent.Action == Intent.ActionSend && intent.Type != null)
{
if ((intent.Flags & ActivityFlags.LaunchedFromHistory) == ActivityFlags.LaunchedFromHistory)
{
// don't re-deliver intent if resuming from app switcher
return null;
}
var type = intent.Type;
if (type.Contains("text/"))
{
@@ -384,45 +382,11 @@ namespace Bit.Droid
}
}
private void UpdateTheme(string theme)
private void AppearanceAdjustments()
{
if (theme == "light")
{
SetTheme(Resource.Style.LightTheme);
}
else if (theme == "dark")
{
SetTheme(Resource.Style.DarkTheme);
}
else if (theme == "black")
{
SetTheme(Resource.Style.BlackTheme);
}
else if (theme == "nord")
{
SetTheme(Resource.Style.NordTheme);
}
else
{
if (_deviceActionService.UsingDarkTheme())
{
SetTheme(Resource.Style.DarkTheme);
}
else
{
SetTheme(Resource.Style.LightTheme);
}
}
}
private void RestartApp()
{
var intent = new Intent(this, typeof(MainActivity));
var pendingIntent = PendingIntent.GetActivity(this, 5923650, intent, PendingIntentFlags.CancelCurrent);
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + 500;
alarmManager.Set(AlarmType.Rtc, triggerMs, pendingIntent);
Java.Lang.JavaSystem.Exit(0);
Window?.SetStatusBarColor(ThemeHelpers.NavBarBackgroundColor);
Window?.DecorView.SetBackgroundColor(ThemeHelpers.BackgroundColor);
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(true), ThemeManager.OsDarkModeEnabled());
}
private void ExitApp()

View File

@@ -99,6 +99,9 @@ namespace Bit.Droid
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
broadcasterService);
var biometricService = new BiometricService();
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
var cryptoService = new CryptoService(mobileStorageService, secureStorageService, cryptoFunctionService);
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService);
ServiceContainer.Register<IMessagingService>("messagingService", messagingService);
@@ -110,6 +113,9 @@ namespace Bit.Droid
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
ServiceContainer.Register<ICryptoFunctionService>("cryptoFunctionService", cryptoFunctionService);
ServiceContainer.Register<ICryptoService>("cryptoService", cryptoService);
ServiceContainer.Register<IPasswordRepromptService>("passwordRepromptService", passwordRepromptService);
// Push
#if FDROID

View File

@@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:versionCode="1"
android:versionName="2.10.0"
android:versionName="2.14.3"
android:installLocation="internalOnly"
package="com.x8bit.bitwarden">
@@ -54,12 +54,7 @@
<!-- Package visibility (for Android 11+) -->
<queries>
<intent>
<action android:name="android.intent.action.VIEW"/>
<data android:scheme="http"/>
</intent>
<intent>
<action android:name="android.intent.action.VIEW"/>
<data android:scheme="https"/>
<action android:name="*"/>
</intent>
</queries>

View File

@@ -1,16 +0,0 @@
using Android.Content;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
namespace Bit.Droid.Receivers
{
[BroadcastReceiver(Name = "com.x8bit.bitwarden.LockAlarmReceiver", Exported = false)]
public class LockAlarmReceiver : BroadcastReceiver
{
public async override void OnReceive(Context context, Intent intent)
{
var vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
await vaultTimeoutService.CheckVaultTimeoutAsync();
}
}
}

View File

@@ -1,242 +0,0 @@
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.Runtime;
using Android.Util;
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;
using FFImageLoading.Work;
using System;
using System.ComponentModel;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(CipherViewCell), typeof(CipherViewCellRenderer))]
namespace Bit.Droid.Renderers
{
public class CipherViewCellRenderer : ViewCellRenderer
{
private static Typeface _faTypeface;
private static Typeface _miTypeface;
private static Android.Graphics.Color _textColor;
private static Android.Graphics.Color _mutedColor;
private static Android.Graphics.Color _disabledIconColor;
private static bool _usingLightTheme;
private AndroidCipherCell _cell;
protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView,
ViewGroup parent, Context context)
{
// TODO expand beyond light/dark detection once we support custom theme switching without app restart
var themeChanged = _usingLightTheme != ThemeManager.UsingLightTheme;
if (_faTypeface == null)
{
_faTypeface = Typeface.CreateFromAsset(context.Assets, "FontAwesome.ttf");
}
if (_miTypeface == null)
{
_miTypeface = Typeface.CreateFromAsset(context.Assets, "MaterialIcons_Regular.ttf");
}
if (_textColor == default(Android.Graphics.Color) || themeChanged)
{
_textColor = ThemeManager.GetResourceColor("TextColor").ToAndroid();
}
if (_mutedColor == default(Android.Graphics.Color) || themeChanged)
{
_mutedColor = ThemeManager.GetResourceColor("MutedColor").ToAndroid();
}
if (_disabledIconColor == default(Android.Graphics.Color) || themeChanged)
{
_disabledIconColor = ThemeManager.GetResourceColor("DisabledIconColor").ToAndroid();
}
_usingLightTheme = ThemeManager.UsingLightTheme;
var cipherCell = item as CipherViewCell;
_cell = convertView as AndroidCipherCell;
if (_cell == null)
{
_cell = new AndroidCipherCell(context, cipherCell, _faTypeface, _miTypeface);
}
else
{
_cell.CipherViewCell.PropertyChanged -= CellPropertyChanged;
}
cipherCell.PropertyChanged += CellPropertyChanged;
_cell.UpdateCell(cipherCell);
_cell.UpdateColors(_textColor, _mutedColor, _disabledIconColor);
return _cell;
}
public void CellPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var cipherCell = sender as CipherViewCell;
_cell.CipherViewCell = cipherCell;
if (e.PropertyName == CipherViewCell.CipherProperty.PropertyName)
{
_cell.UpdateCell(cipherCell);
}
else if (e.PropertyName == CipherViewCell.WebsiteIconsEnabledProperty.PropertyName)
{
_cell.UpdateIconImage(cipherCell);
}
}
}
public class AndroidCipherCell : LinearLayout, INativeElementView
{
private readonly Typeface _faTypeface;
private readonly Typeface _miTypeface;
private IScheduledWork _currentTask;
public AndroidCipherCell(Context context, CipherViewCell cipherView, Typeface faTypeface, Typeface miTypeface)
: base(context)
{
CipherViewCell = cipherView;
_faTypeface = faTypeface;
_miTypeface = miTypeface;
var view = (context as Activity).LayoutInflater.Inflate(Resource.Layout.CipherViewCell, null);
IconImage = view.FindViewById<IconImageView>(Resource.Id.CipherCellIconImage);
Icon = view.FindViewById<TextView>(Resource.Id.CipherCellIcon);
Name = view.FindViewById<TextView>(Resource.Id.CipherCellName);
SubTitle = view.FindViewById<TextView>(Resource.Id.CipherCellSubTitle);
SharedIcon = view.FindViewById<TextView>(Resource.Id.CipherCellSharedIcon);
AttachmentsIcon = view.FindViewById<TextView>(Resource.Id.CipherCellAttachmentsIcon);
MoreButton = view.FindViewById<Android.Widget.Button>(Resource.Id.CipherCellButton);
MoreButton.Click += MoreButton_Click;
Icon.Typeface = _faTypeface;
SharedIcon.Typeface = _faTypeface;
AttachmentsIcon.Typeface = _faTypeface;
MoreButton.Typeface = _miTypeface;
var small = (float)Device.GetNamedSize(NamedSize.Small, typeof(Label));
Icon.SetTextSize(ComplexUnitType.Pt, 10);
Name.SetTextSize(ComplexUnitType.Sp, (float)Device.GetNamedSize(NamedSize.Medium, typeof(Label)));
SubTitle.SetTextSize(ComplexUnitType.Sp, small);
SharedIcon.SetTextSize(ComplexUnitType.Sp, small);
AttachmentsIcon.SetTextSize(ComplexUnitType.Sp, small);
MoreButton.SetTextSize(ComplexUnitType.Sp, 25);
AddView(view);
}
public CipherViewCell CipherViewCell { get; set; }
public Element Element => CipherViewCell;
public IconImageView IconImage { get; set; }
public TextView Icon { get; set; }
public TextView Name { get; set; }
public TextView SubTitle { get; set; }
public TextView SharedIcon { get; set; }
public TextView AttachmentsIcon { get; set; }
public Android.Widget.Button MoreButton { get; set; }
public void UpdateCell(CipherViewCell cipherCell)
{
UpdateIconImage(cipherCell);
var cipher = cipherCell.Cipher;
Name.Text = cipher.Name;
if (!string.IsNullOrWhiteSpace(cipher.SubTitle))
{
SubTitle.Text = cipher.SubTitle;
SubTitle.Visibility = ViewStates.Visible;
}
else
{
SubTitle.Visibility = ViewStates.Invisible;
}
SharedIcon.Visibility = cipher.Shared ? ViewStates.Visible : ViewStates.Gone;
AttachmentsIcon.Visibility = cipher.HasAttachments ? ViewStates.Visible : ViewStates.Gone;
}
public void UpdateIconImage(CipherViewCell cipherCell)
{
if (_currentTask != null && !_currentTask.IsCancelled && !_currentTask.IsCompleted)
{
_currentTask.Cancel();
}
var cipher = cipherCell.Cipher;
var iconImage = cipherCell.GetIconImage(cipher);
if (iconImage.Item2 != null)
{
IconImage.SetImageResource(Resource.Drawable.login);
IconImage.Visibility = ViewStates.Visible;
Icon.Visibility = ViewStates.Gone;
_currentTask = ImageService.Instance.LoadUrl(iconImage.Item2).DownSample(64).Into(IconImage);
IconImage.Key = iconImage.Item2;
}
else
{
IconImage.Visibility = ViewStates.Gone;
Icon.Visibility = ViewStates.Visible;
Icon.Text = iconImage.Item1;
}
}
public void UpdateColors(Android.Graphics.Color textColor, Android.Graphics.Color mutedColor,
Android.Graphics.Color iconDisabledColor)
{
Name.SetTextColor(textColor);
SubTitle.SetTextColor(mutedColor);
Icon.SetTextColor(mutedColor);
SharedIcon.SetTextColor(mutedColor);
AttachmentsIcon.SetTextColor(mutedColor);
MoreButton.SetTextColor(iconDisabledColor);
}
private void MoreButton_Click(object sender, EventArgs e)
{
if (CipherViewCell.ButtonCommand?.CanExecute(CipherViewCell.Cipher) ?? false)
{
CipherViewCell.ButtonCommand.Execute(CipherViewCell.Cipher);
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
MoreButton.Click -= MoreButton_Click;
}
base.Dispose(disposing);
}
}
[Android.Runtime.Preserve(AllMembers = true)]
[Register("bit.droid.renderers.IconImageView")]
public class IconImageView : ImageViewAsync
{
public IconImageView(Context context) : base(context)
{ }
public IconImageView(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{ }
public IconImageView(Context context, IAttributeSet attrs)
: base(context, attrs)
{ }
public string Key { get; set; }
protected override void JavaFinalize()
{
SetImageDrawable(null);
SetImageBitmap(null);
ImageService.Instance.InvalidateCacheEntryAsync(Key, FFImageLoading.Cache.CacheType.Memory);
base.JavaFinalize();
}
}
}

View File

@@ -1,6 +1,9 @@
using Android.Content;
using System.ComponentModel;
using Android.Content;
using Android.Content.Res;
using Android.Views.InputMethods;
using Bit.Droid.Renderers;
using Bit.Droid.Utilities;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
@@ -25,6 +28,7 @@ namespace Bit.Droid.Renderers
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
UpdateBorderColor();
if (Control != null && e.NewElement != null)
{
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
@@ -33,5 +37,33 @@ namespace Bit.Droid.Renderers
(ImeAction)ImeFlags.NoExtractUi;
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Entry.TextColorProperty.PropertyName)
{
UpdateBorderColor();
}
}
private void UpdateBorderColor()
{
if (Control != null)
{
var states = new[]
{
new[] { Android.Resource.Attribute.StateFocused }, // focused
new[] { -Android.Resource.Attribute.StateFocused }, // unfocused
};
var colors = new int[]
{
ThemeHelpers.PrimaryColor,
ThemeHelpers.MutedColor
};
Control.BackgroundTintList = new ColorStateList(states, colors);
}
}
}
}

View File

@@ -1,10 +1,12 @@
using System.ComponentModel;
using Android.Content;
using Android.Content.Res;
using Android.Graphics;
using Android.Text;
using Android.Views.InputMethods;
using Android.Widget;
using Bit.Droid.Renderers;
using Bit.Droid.Utilities;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
@@ -20,6 +22,7 @@ namespace Bit.Droid.Renderers
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
UpdateBorderColor();
if (Control != null && e.NewElement != null)
{
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
@@ -68,6 +71,28 @@ namespace Bit.Droid.Renderers
}
}
}
else if (e.PropertyName == Entry.TextColorProperty.PropertyName)
{
UpdateBorderColor();
}
}
private void UpdateBorderColor()
{
if (Control != null)
{
var states = new[]
{
new[] { Android.Resource.Attribute.StateFocused }, // focused
new[] { -Android.Resource.Attribute.StateFocused }, // unfocused
};
var colors = new int[]
{
ThemeHelpers.PrimaryColor,
ThemeHelpers.MutedColor
};
Control.BackgroundTintList = new ColorStateList(states, colors);
}
}
}
}

View File

@@ -1,5 +1,8 @@
using Android.Content;
using System.ComponentModel;
using Android.Content;
using Android.Content.Res;
using Bit.Droid.Renderers;
using Bit.Droid.Utilities;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
@@ -15,11 +18,40 @@ namespace Bit.Droid.Renderers
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
UpdateBorderColor();
if (Control != null && e.NewElement != null)
{
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
Control.PaddingBottom + 20);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Picker.TextColorProperty.PropertyName)
{
UpdateBorderColor();
}
}
private void UpdateBorderColor()
{
if (Control != null)
{
var states = new[]
{
new[] { Android.Resource.Attribute.StateFocused }, // focused
new[] { -Android.Resource.Attribute.StateFocused }, // unfocused
};
var colors = new int[]
{
ThemeHelpers.PrimaryColor,
ThemeHelpers.MutedColor
};
Control.BackgroundTintList = new ColorStateList(states, colors);
}
}
}
}

View File

@@ -0,0 +1,66 @@
using System.ComponentModel;
using Android.Content;
using Android.Content.Res;
using Android.Graphics.Drawables;
using Android.OS;
using AndroidX.Core.Content.Resources;
using Bit.Droid.Renderers;
using Bit.Droid.Utilities;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(Switch), typeof(CustomSwitchRenderer))]
namespace Bit.Droid.Renderers
{
public class CustomSwitchRenderer : SwitchRenderer
{
public CustomSwitchRenderer(Context context)
: base(context)
{}
protected override void OnElementChanged(ElementChangedEventArgs<Switch> e)
{
base.OnElementChanged(e);
UpdateColors();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Switch.OnColorProperty.PropertyName)
{
UpdateColors();
}
}
private void UpdateColors()
{
if (Build.VERSION.SdkInt <= BuildVersionCodes.LollipopMr1)
{
// Android 5.x doesn't support ThumbTintList, and using SwitchCompat on every version after 5.x
// doesn't apply tinting the way we want. Let 5.x to do its own thing here.
return;
}
if (Control != null)
{
var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.switch_thumb, null);
if (t is GradientDrawable thumb)
{
Control.ThumbDrawable = thumb;
}
var thumbStates = new[]
{
new[] { Android.Resource.Attribute.StateChecked }, // checked
new[] { -Android.Resource.Attribute.StateChecked }, // unchecked
};
var thumbColors = new int[]
{
ThemeHelpers.SwitchOnColor,
ThemeHelpers.SwitchThumbColor
};
Control.ThumbTintList = new ColorStateList(thumbStates, thumbColors);
}
}
}
}

View File

@@ -49,11 +49,11 @@ namespace Bit.Droid.Renderers
return null;
}
async void BottomNavigationView.IOnNavigationItemReselectedListener.OnNavigationItemReselected(IMenuItem item)
public void OnNavigationItemReselected(IMenuItem item)
{
if (_page?.CurrentPage?.Navigation != null && _page.CurrentPage.Navigation.NavigationStack.Count > 0)
{
await _page.CurrentPage.Navigation.PopToRootAsync();
Device.BeginInvokeOnMainThread(async () => await _page.CurrentPage.Navigation.PopToRootAsync());
}
}
}

View File

@@ -0,0 +1,23 @@
using Android.Content;
using Bit.App.Controls;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ExtendedGrid), typeof(ExtendedGridRenderer))]
namespace Bit.Droid.Renderers
{
public class ExtendedGridRenderer : ViewRenderer
{
public ExtendedGridRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<View> elementChangedEvent)
{
base.OnElementChanged(elementChangedEvent);
if (elementChangedEvent.NewElement != null)
{
SetBackgroundResource(Resource.Drawable.list_item_bg);
}
}
}
}

View File

@@ -1,29 +0,0 @@
using Android.Content;
using Android.Views;
using Bit.App.Controls;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ExtendedListView), typeof(ExtendedListViewRenderer))]
namespace Bit.Droid.Renderers
{
public class ExtendedListViewRenderer : ListViewRenderer
{
public ExtendedListViewRenderer(Context context)
: base(context)
{ }
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
if (Control != null && e.NewElement != null && e.NewElement is ExtendedListView listView)
{
// Pad for FAB
Control.SetPadding(0, 0, 0, 170);
Control.SetClipToPadding(false);
Control.ScrollBarStyle = ScrollbarStyles.OutsideOverlay;
}
}
}
}

View File

@@ -1,4 +1,5 @@
using Android.Content;
using System.ComponentModel;
using Android.Content;
using Android.Graphics.Drawables;
using AndroidX.Core.Content.Resources;
using Bit.App.Controls;
@@ -18,6 +19,21 @@ namespace Bit.Droid.Renderers
protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
{
base.OnElementChanged(e);
UpdateColor();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == ExtendedSlider.ThumbBorderColorProperty.PropertyName)
{
UpdateColor();
}
}
private void UpdateColor()
{
if (Control != null && Element is ExtendedSlider view)
{
var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.slider_thumb, null);

View File

@@ -0,0 +1,23 @@
using Android.Content;
using Bit.App.Controls;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ExtendedStackLayout), typeof(ExtendedStackLayoutRenderer))]
namespace Bit.Droid.Renderers
{
public class ExtendedStackLayoutRenderer : ViewRenderer
{
public ExtendedStackLayoutRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<View> elementChangedEvent)
{
base.OnElementChanged(elementChangedEvent);
if (elementChangedEvent.NewElement != null)
{
SetBackgroundResource(Resource.Drawable.list_item_bg);
}
}
}
}

View File

@@ -0,0 +1,72 @@
using System.ComponentModel;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Bit.App.Controls;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ExtendedStepper), typeof(ExtendedStepperRenderer))]
namespace Bit.Droid.Renderers
{
public class ExtendedStepperRenderer : StepperRenderer
{
public ExtendedStepperRenderer(Context context)
: base(context)
{}
protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
{
base.OnElementChanged(e);
UpdateBgColor();
UpdateFgColor();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == ExtendedStepper.StepperBackgroundColorProperty.PropertyName)
{
UpdateBgColor();
}
else if (e.PropertyName == ExtendedStepper.StepperForegroundColorProperty.PropertyName)
{
UpdateFgColor();
}
}
private void UpdateBgColor()
{
if (Control != null && Element is ExtendedStepper view)
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
{
Control.GetChildAt(0)?.Background?.SetColorFilter(
new BlendModeColorFilter(view.StepperBackgroundColor.ToAndroid(), BlendMode.Multiply));
Control.GetChildAt(1)?.Background?.SetColorFilter(
new BlendModeColorFilter(view.StepperBackgroundColor.ToAndroid(), BlendMode.Multiply));
}
else
{
Control.GetChildAt(0)?.Background?.SetColorFilter(
view.StepperBackgroundColor.ToAndroid(), PorterDuff.Mode.Multiply);
Control.GetChildAt(1)?.Background?.SetColorFilter(
view.StepperBackgroundColor.ToAndroid(), PorterDuff.Mode.Multiply);
}
}
}
private void UpdateFgColor()
{
if (Control != null && Element is ExtendedStepper view)
{
var btn0 = Control.GetChildAt(0) as Android.Widget.Button;
btn0?.SetTextColor(view.StepperForegroundColor.ToAndroid());
var btn1 = Control.GetChildAt(1) as Android.Widget.Button;
btn1?.SetTextColor(view.StepperForegroundColor.ToAndroid());
}
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
using Android.Content;
using Bit.App.Controls;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(SelectableLabel), typeof(SelectableLabelRenderer))]
namespace Bit.Droid.Renderers
{
public class SelectableLabelRenderer : LabelRenderer
{
public SelectableLabelRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.SetTextIsSelectable(true);
}
}
}
}

View File

@@ -1,210 +0,0 @@
using System;
using System.ComponentModel;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.Util;
using Android.Views;
using Android.Widget;
using Bit.App.Controls;
using Bit.App.Utilities;
using Bit.Droid.Renderers;
using FFImageLoading.Work;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Button = Android.Widget.Button;
using Color = Android.Graphics.Color;
using View = Android.Views.View;
[assembly: ExportRenderer(typeof(SendViewCell), typeof(SendViewCellRenderer))]
namespace Bit.Droid.Renderers
{
public class SendViewCellRenderer : ViewCellRenderer
{
private static Typeface _faTypeface;
private static Typeface _miTypeface;
private static Color _textColor;
private static Color _mutedColor;
private static Color _disabledIconColor;
private static bool _usingLightTheme;
private AndroidSendCell _cell;
protected override View GetCellCore(Cell item, View convertView,
ViewGroup parent, Context context)
{
// TODO expand beyond light/dark detection once we support custom theme switching without app restart
var themeChanged = _usingLightTheme != ThemeManager.UsingLightTheme;
if (_faTypeface == null)
{
_faTypeface = Typeface.CreateFromAsset(context.Assets, "FontAwesome.ttf");
}
if (_miTypeface == null)
{
_miTypeface = Typeface.CreateFromAsset(context.Assets, "MaterialIcons_Regular.ttf");
}
if (_textColor == default(Color) || themeChanged)
{
_textColor = ThemeManager.GetResourceColor("TextColor").ToAndroid();
}
if (_mutedColor == default(Color) || themeChanged)
{
_mutedColor = ThemeManager.GetResourceColor("MutedColor").ToAndroid();
}
if (_disabledIconColor == default(Color) || themeChanged)
{
_disabledIconColor = ThemeManager.GetResourceColor("DisabledIconColor").ToAndroid();
}
_usingLightTheme = ThemeManager.UsingLightTheme;
var sendCell = item as SendViewCell;
_cell = convertView as AndroidSendCell;
if (_cell == null)
{
_cell = new AndroidSendCell(context, sendCell, _faTypeface, _miTypeface);
}
else
{
_cell.SendViewCell.PropertyChanged -= CellPropertyChanged;
}
sendCell.PropertyChanged += CellPropertyChanged;
_cell.UpdateCell(sendCell);
_cell.UpdateColors(_textColor, _mutedColor, _disabledIconColor);
return _cell;
}
public void CellPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var sendCell = sender as SendViewCell;
_cell.SendViewCell = sendCell;
if (e.PropertyName == SendViewCell.SendProperty.PropertyName)
{
_cell.UpdateCell(sendCell);
}
}
}
public class AndroidSendCell : LinearLayout, INativeElementView
{
private readonly Typeface _faTypeface;
private readonly Typeface _miTypeface;
private IScheduledWork _currentTask;
public AndroidSendCell(Context context, SendViewCell sendView, Typeface faTypeface, Typeface miTypeface)
: base(context)
{
SendViewCell = sendView;
_faTypeface = faTypeface;
_miTypeface = miTypeface;
var view = (context as Activity).LayoutInflater.Inflate(Resource.Layout.SendViewCell, null);
Icon = view.FindViewById<TextView>(Resource.Id.SendCellIcon);
Name = view.FindViewById<TextView>(Resource.Id.SendCellName);
SubTitle = view.FindViewById<TextView>(Resource.Id.SendCellSubTitle);
DisabledIcon = view.FindViewById<TextView>(Resource.Id.SendCellDisabledIcon);
HasPasswordIcon = view.FindViewById<TextView>(Resource.Id.SendCellHasPasswordIcon);
MaxAccessCountReachedIcon = view.FindViewById<TextView>(Resource.Id.SendCellMaxAccessCountReachedIcon);
ExpiredIcon = view.FindViewById<TextView>(Resource.Id.SendCellExpiredIcon);
PendingDeleteIcon = view.FindViewById<TextView>(Resource.Id.SendCellPendingDeleteIcon);
MoreButton = view.FindViewById<Button>(Resource.Id.SendCellButton);
MoreButton.Click += MoreButton_Click;
Icon.Typeface = _faTypeface;
DisabledIcon.Typeface = _faTypeface;
HasPasswordIcon.Typeface = _faTypeface;
MaxAccessCountReachedIcon.Typeface = _faTypeface;
ExpiredIcon.Typeface = _faTypeface;
PendingDeleteIcon.Typeface = _faTypeface;
MoreButton.Typeface = _miTypeface;
var small = (float)Device.GetNamedSize(NamedSize.Small, typeof(Label));
Icon.SetTextSize(ComplexUnitType.Pt, 10);
Name.SetTextSize(ComplexUnitType.Sp, (float)Device.GetNamedSize(NamedSize.Medium, typeof(Label)));
SubTitle.SetTextSize(ComplexUnitType.Sp, small);
DisabledIcon.SetTextSize(ComplexUnitType.Sp, small);
HasPasswordIcon.SetTextSize(ComplexUnitType.Sp, small);
MaxAccessCountReachedIcon.SetTextSize(ComplexUnitType.Sp, small);
ExpiredIcon.SetTextSize(ComplexUnitType.Sp, small);
PendingDeleteIcon.SetTextSize(ComplexUnitType.Sp, small);
MoreButton.SetTextSize(ComplexUnitType.Sp, 25);
if (!SendViewCell.ShowOptions)
{
MoreButton.Visibility = ViewStates.Gone;
}
AddView(view);
}
public SendViewCell SendViewCell { get; set; }
public Element Element => SendViewCell;
public TextView Icon { get; set; }
public TextView Name { get; set; }
public TextView SubTitle { get; set; }
public TextView DisabledIcon { get; set; }
public TextView HasPasswordIcon { get; set; }
public TextView MaxAccessCountReachedIcon { get; set; }
public TextView ExpiredIcon { get; set; }
public TextView PendingDeleteIcon { get; set; }
public Button MoreButton { get; set; }
public void UpdateCell(SendViewCell sendCell)
{
UpdateIconImage(sendCell);
var send = sendCell.Send;
Name.Text = send.Name;
SubTitle.Text = send.DisplayDate;
DisabledIcon.Visibility = send.Disabled ? ViewStates.Visible : ViewStates.Gone;
HasPasswordIcon.Visibility = send.HasPassword ? ViewStates.Visible : ViewStates.Gone;
MaxAccessCountReachedIcon.Visibility = send.MaxAccessCountReached ? ViewStates.Visible : ViewStates.Gone;
ExpiredIcon.Visibility = send.Expired ? ViewStates.Visible : ViewStates.Gone;
PendingDeleteIcon.Visibility = send.PendingDelete ? ViewStates.Visible : ViewStates.Gone;
}
public void UpdateIconImage(SendViewCell sendCell)
{
if (_currentTask != null && !_currentTask.IsCancelled && !_currentTask.IsCompleted)
{
_currentTask.Cancel();
}
var send = sendCell.Send;
var iconImage = sendCell.GetIconImage(send);
Icon.Text = iconImage;
}
public void UpdateColors(Color textColor, Color mutedColor,
Color iconDisabledColor)
{
Name.SetTextColor(textColor);
SubTitle.SetTextColor(mutedColor);
Icon.SetTextColor(mutedColor);
DisabledIcon.SetTextColor(mutedColor);
HasPasswordIcon.SetTextColor(mutedColor);
MaxAccessCountReachedIcon.SetTextColor(mutedColor);
ExpiredIcon.SetTextColor(mutedColor);
PendingDeleteIcon.SetTextColor(mutedColor);
MoreButton.SetTextColor(iconDisabledColor);
}
private void MoreButton_Click(object sender, EventArgs e)
{
if (SendViewCell.ButtonCommand?.CanExecute(SendViewCell.Send) ?? false)
{
SendViewCell.ButtonCommand.Execute(SendViewCell.Send);
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
MoreButton.Click -= MoreButton_Click;
}
base.Dispose(disposing);
}
}
}

View File

@@ -6,7 +6,7 @@ TODO: When API 23 becomes our new minimum, replace 'splash_screen.xml' in 'drawa
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<color android:color="@color/lightgray"/>
<color android:color="@color/white"/>
</item>
<item
android:drawable="@drawable/logo"

View File

@@ -0,0 +1,7 @@
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#8E8E93">
<item
android:id="@android:id/mask"
android:drawable="@android:color/white" />
</ripple>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<color android:color="@color/lightgray"/>
<color android:color="@color/white"/>
</item>
<item>
<bitmap android:src="@drawable/logo_legacy" android:tileMode="disabled" android:gravity="center"/>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="@color/white"/>
<size android:width="20dp" android:height="20dp"/>
</shape>

View File

@@ -1,86 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="44dp"
android:gravity="center_vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="2.2dp">
<LinearLayout
android:orientation="horizontal"
android:layout_width="39.8dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:gravity="center">
<bit.droid.renderers.IconImageView
android:id="@+id/CipherCellIconImage"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_gravity="center"
android:gravity="center" />
<TextView
android:id="@+id/CipherCellIcon"
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_gravity="center"
android:gravity="center"
android:text="[X]" />
</LinearLayout>
<LinearLayout
android:id="@+id/CipherCellContent"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:gravity="center"
android:paddingVertical="7.65dp">
<LinearLayout
android:id="@+id/CipherCellContentTop"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/CipherCellName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:text="Name" />
<TextView
android:id="@+id/CipherCellSharedIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="5dp"
android:singleLine="true"
android:text="&#xf1e0;" />
<TextView
android:id="@+id/CipherCellAttachmentsIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="5dp"
android:singleLine="true"
android:text="&#xf0c6;" />
</LinearLayout>
<TextView
android:id="@+id/CipherCellSubTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:text="SubTitle" />
</LinearLayout>
<Button
android:id="@+id/CipherCellButton"
android:layout_width="37dp"
android:layout_height="match_parent"
android:text="&#xe5d4;"
android:gravity="center"
android:padding="0dp"
android:background="@android:color/transparent" />
</LinearLayout>
</RelativeLayout>

View File

@@ -1,104 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="44dp"
android:gravity="center_vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="2.2dp">
<LinearLayout
android:orientation="horizontal"
android:layout_width="39.8dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:gravity="center">
<TextView
android:id="@+id/SendCellIcon"
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_gravity="center"
android:gravity="center"
android:text="[X]" />
</LinearLayout>
<LinearLayout
android:id="@+id/SendCellContent"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:gravity="center"
android:paddingVertical="7.65dp">
<LinearLayout
android:id="@+id/SendCellContentTop"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/SendCellName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:text="Name" />
<TextView
android:id="@+id/SendCellDisabledIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="5dp"
android:singleLine="true"
android:text="&#xf071;" />
<TextView
android:id="@+id/SendCellHasPasswordIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="5dp"
android:singleLine="true"
android:text="&#xf084;" />
<TextView
android:id="@+id/SendCellMaxAccessCountReachedIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="5dp"
android:singleLine="true"
android:text="&#xf05e;" />
<TextView
android:id="@+id/SendCellExpiredIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="5dp"
android:singleLine="true"
android:text="&#xf017;" />
<TextView
android:id="@+id/SendCellPendingDeleteIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="5dp"
android:singleLine="true"
android:text="&#xf1f8;" />
</LinearLayout>
<TextView
android:id="@+id/SendCellSubTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:text="SubTitle" />
</LinearLayout>
<Button
android:id="@+id/SendCellButton"
android:layout_width="37dp"
android:layout_height="match_parent"
android:text="&#xe5d4;"
android:gravity="center"
android:padding="0dp"
android:background="@android:color/transparent" />
</LinearLayout>
</RelativeLayout>

View File

@@ -1,8 +1,26 @@
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<!-- Launch theme (for auto dark/light based on system) -->
<style name="LaunchTheme" parent="DarkTheme.Base">
<style name="LaunchTheme" parent="BaseTheme">
<item name="android:windowBackground">@drawable/splash_screen_dark</item>
<item name="android:windowNoTitle">true</item>
</style>
<style name="BaseTheme" parent="Theme.AppCompat">
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="colorPrimaryDark">@color/dark_notificationBar</item>
<item name="colorAccent">@color/dark_primary</item>
<item name="colorControlNormal">@color/dark_border</item>
<item name="android:navigationBarColor">@color/dark_navigationBarBackground</item>
<item name="windowActionModeOverlay">true</item>
<item name="android:colorActivatedHighlight">@android:color/transparent</item>
<item name="android:textCursorDrawable">@null</item>
<item name="popupTheme">@style/ThemeOverlay.AppCompat</item>
<item name="buttonStyle">@style/ButtonStyle</item>
</style>
<style name="ButtonStyle" parent="Widget.AppCompat.Button">
<item name="android:textAllCaps">false</item>
</style>
</resources>

View File

@@ -1,37 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Light theme -->
<!-- Light System -->
<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 -->
<!-- Dark System -->
<color name="dark_primary">#52bdfb</color>
<color name="dark_notificationBar">#191919</color>
<color name="dark_border">#191919</color>
<color name="dark_border">#666666</color>
<color name="dark_navigationBarBackground">#191919</color>
<!-- Black theme -->
<color name="black_border">#282828</color>
<!-- Nord theme -->
<color name="nord_background">#3b4252</color>
<color name="nord_text">#e5e9f0</color>
<color name="nord_primary">#81a1c1</color>
<color name="nord_actionBar">#2e3440</color>
<color name="nord_actionBarText">#e5e9f0</color>
<color name="nord_notificationBar">#20242D</color>
<color name="nord_dialogBackground">#3b4252</color>
<color name="nord_border">#2e3440</color>
<color name="nord_popupBackground">#4c566a</color>
<color name="nord_popupText">#e5e9f0</color>
<color name="nord_buttonBackground">#4c566a</color>
<color name="nord_navigationBarBackground">#20242D</color>
<!-- Other -->
<color name="launcher_background">#FFFFFF</color>
<color name="white">#FFFFFF</color>
<color name="black">#000000</color>
<color name="darkgray">#333333</color>

View File

@@ -1,21 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<resources xmlns:tools="http://schemas.android.com/tools">
<resources>
<!-- Launch theme (for auto dark/light based on system) -->
<style name="LaunchTheme" parent="LightTheme.Base">
<style name="LaunchTheme" parent="BaseTheme">
<item name="android:windowBackground">@drawable/splash_screen</item>
<item name="android:windowNoTitle">true</item>
</style>
<!-- Light theme -->
<style name="LightTheme" parent="LightTheme.Base">
</style>
<style name="LightTheme.Splash" parent="LightTheme.Base">
<item name="android:windowBackground">@drawable/splash_screen</item>
<item name="android:windowNoTitle">true</item>
</style>
<style name="LightTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
<style name="BaseTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="colorPrimary">@color/primary</item>
@@ -27,70 +18,10 @@
<item name="android:colorActivatedHighlight">@android:color/transparent</item>
<item name="android:textCursorDrawable">@null</item>
<item name="popupTheme">@style/ThemeOverlay.AppCompat.Light</item>
<item name="buttonStyle">@style/ButtonStyle</item>
</style>
<!-- Dark theme -->
<style name="DarkTheme" parent="DarkTheme.Base">
</style>
<style name="DarkTheme.Splash" parent="DarkTheme.Base">
<item name="android:windowBackground">@drawable/splash_screen_dark</item>
<item name="android:windowNoTitle">true</item>
</style>
<style name="DarkTheme.Base" parent="Theme.AppCompat">
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="colorPrimaryDark">@color/dark_notificationBar</item>
<item name="colorAccent">@color/dark_primary</item>
<item name="colorControlNormal">@color/dark_border</item>
<item name="android:navigationBarColor">@color/dark_navigationBarBackground</item>
<item name="windowActionModeOverlay">true</item>
<item name="android:colorActivatedHighlight">@android:color/transparent</item>
<item name="android:textCursorDrawable">@null</item>
<item name="popupTheme">@style/ThemeOverlay.AppCompat</item>
</style>
<!-- Black theme -->
<style name="BlackTheme" parent="BlackTheme.Base">
</style>
<style name="BlackTheme.Splash" parent="DarkTheme.Splash">
</style>
<style name="BlackTheme.Base" parent="DarkTheme.Base">
<item name="android:windowBackground">@android:color/black</item>
<item name="colorPrimary">@android:color/black</item>
<item name="colorPrimaryDark">@android:color/black</item>
<item name="colorControlNormal">@color/black_border</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<!-- Nord theme -->
<style name="NordTheme" parent="NordTheme.Base">
</style>
<style name="NordTheme.Splash" parent="DarkTheme.Splash">
</style>
<style name="NordTheme.Base" parent="DarkTheme.Base">
<item name="android:windowBackground">@color/nord_background</item>
<item name="android:actionMenuTextColor">@color/nord_actionBarText</item>
<item name="android:textColor">@color/nord_text</item>
<item name="colorAccent">@color/nord_primary</item>
<item name="colorPrimary">@color/nord_actionBar</item>
<item name="colorPrimaryDark">@color/nord_notificationBar</item>
<item name="colorControlNormal">@color/nord_border</item>
<item name="android:navigationBarColor">@color/nord_navigationBarBackground</item>
<item name="colorBackgroundFloating">@color/nord_dialogBackground</item>
<item name="android:colorBackgroundFloating" tools:targetApi="23">@color/nord_dialogBackground</item>
<item name="popupTheme">@style/NordTheme.Popup</item>
<item name="colorButtonNormal">@color/nord_buttonBackground</item>
<item name="android:colorButtonNormal">@color/nord_buttonBackground</item>
</style>
<style name="NordTheme.Popup" parent="ThemeOverlay.AppCompat">
<item name="android:colorBackground">@color/nord_popupBackground</item>
<item name="android:textColor">@color/nord_popupText</item>
</style>
<style name="ButtonStyle" parent="Widget.AppCompat.Button">
<item name="android:textAllCaps">false</item>
</style>
</resources>

View File

@@ -11,9 +11,6 @@
-->
<autofill-service xmlns:android="http://schemas.android.com/apk/res/android"
android:supportsInlineSuggestions="true">
<compatibility-package
android:name="alook.browser"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.amazon.cloud9"
android:maxLongVersionCode="10000000000"/>
@@ -56,6 +53,12 @@
<compatibility-package
android:name="com.chrome.dev"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.cookiegames.smartcookie"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.cookiejarapps.android.smartcookieweb"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.ecosia.android"
android:maxLongVersionCode="10000000000"/>
@@ -65,6 +68,9 @@
<compatibility-package
android:name="com.google.android.apps.chrome_dev"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.google.android.captiveportallogin"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.jamal2367.styx"
android:maxLongVersionCode="10000000000"/>
@@ -205,10 +211,10 @@
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="org.mozilla.firefox"
android:maxLongVersionCode="10000000000"/>
android:maxLongVersionCode="2015836711"/>
<compatibility-package
android:name="org.mozilla.firefox_beta"
android:maxLongVersionCode="10000000000"/>
android:maxLongVersionCode="2015849447"/>
<compatibility-package
android:name="org.mozilla.reference.browser"
android:maxLongVersionCode="10000000000"/>
@@ -227,4 +233,7 @@
<compatibility-package
android:name="org.ungoogled.chromium.stable"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="us.spotco.fennec_dos"
android:maxLongVersionCode="10000000000"/>
</autofill-service>

View File

@@ -8,7 +8,6 @@ using Android.App;
using Android.App.Assist;
using Android.Content;
using Android.Content.PM;
using Android.Content.Res;
using Android.Nfc;
using Android.OS;
using Android.Provider;
@@ -307,7 +306,7 @@ namespace Bit.Droid.Services
public Task<string> DisplayPromptAync(string title = null, string description = null,
string text = null, string okButtonText = null, string cancelButtonText = null,
bool numericKeyboard = false, bool autofocus = true)
bool numericKeyboard = false, bool autofocus = true, bool password = false)
{
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
if (activity == null)
@@ -333,6 +332,10 @@ namespace Bit.Droid.Services
input.KeyListener = DigitsKeyListener.GetInstance(false, false);
#pragma warning restore CS0618 // Type or member is obsolete
}
if (password)
{
input.InputType = InputTypes.TextVariationPassword | InputTypes.ClassText;
}
input.ImeOptions = input.ImeOptions | (ImeAction)ImeFlags.NoPersonalizedLearning |
(ImeAction)ImeFlags.NoExtractUi;
@@ -637,18 +640,10 @@ namespace Bit.Droid.Services
public bool AutofillAccessibilityServiceRunning()
{
try
{
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
var manager = activity.GetSystemService(Context.ActivityService) as ActivityManager;
var services = manager.GetRunningServices(int.MaxValue);
return services.Any(s => s.Process.ToLowerInvariant().Contains("bitwarden") &&
s.Service.ClassName.ToLowerInvariant().Contains("accessibilityservice"));
}
catch
{
return false;
}
var enabledServices = Settings.Secure.GetString(Application.Context.ContentResolver,
Settings.Secure.EnabledAccessibilityServices);
return Application.Context.PackageName != null &&
(enabledServices?.Contains(Application.Context.PackageName) ?? false);
}
public bool AutofillAccessibilityOverlayPermitted()
@@ -737,21 +732,6 @@ namespace Bit.Droid.Services
}
}
public bool UsingDarkTheme()
{
try
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
{
var app = CrossCurrentActivity.Current.AppContext;
var uiModeFlags = app.Resources.Configuration.UiMode & UiMode.NightMask;
return uiModeFlags == UiMode.NightYes;
}
}
catch { }
return false;
}
public long GetActiveTime()
{
// Returns milliseconds since the system was booted, and includes deep sleep. This clock is guaranteed to
@@ -772,6 +752,11 @@ namespace Bit.Droid.Services
_messagingService.Send("finishMainActivity");
}
public bool SupportsFido2()
{
return true;
}
private bool DeleteDir(Java.IO.File dir)
{
if (dir != null && dir.IsDirectory)

View File

@@ -98,5 +98,15 @@ namespace Bit.Droid.Services
Console.WriteLine(".NET Fallback Language/Locale:" + netLanguage + " (application-specific)");
return netLanguage;
}
public string GetLocaleShortDate(DateTime? date)
{
return date?.ToShortDateString() ?? string.Empty;
}
public string GetLocaleShortTime(DateTime? time)
{
return time?.ToShortTimeString() ?? string.Empty;
}
}
}

View File

@@ -0,0 +1,71 @@
using Android.Graphics;
using Bit.App.Utilities;
using Xamarin.Forms.Platform.Android;
namespace Bit.Droid.Utilities
{
public class ThemeHelpers
{
public static bool LightTheme = true;
public static Color PrimaryColor
{
get => ThemeManager.GetResourceColor("PrimaryColor").ToAndroid();
}
public static Color MutedColor
{
get => ThemeManager.GetResourceColor("MutedColor").ToAndroid();
}
public static Color BackgroundColor
{
get => ThemeManager.GetResourceColor("BackgroundColor").ToAndroid();
}
public static Color NavBarBackgroundColor
{
get => ThemeManager.GetResourceColor("NavigationBarBackgroundColor").ToAndroid();
}
public static Color FabColor
{
get => ThemeManager.GetResourceColor("FabColor").ToAndroid();
}
public static Color SwitchOnColor
{
get => ThemeManager.GetResourceColor("SwitchOnColor").ToAndroid();
}
public static Color SwitchThumbColor
{
get => ThemeManager.GetResourceColor("SwitchThumbColor").ToAndroid();
}
public static void SetAppearance(string theme, bool osDarkModeEnabled)
{
SetThemeVariables(theme, osDarkModeEnabled);
}
public static int GetDialogTheme()
{
if (LightTheme)
{
return Android.Resource.Style.ThemeMaterialLightDialog;
}
return Android.Resource.Style.ThemeMaterialDialog;
}
private static void SetThemeVariables(string theme, bool osDarkModeEnabled)
{
if (string.IsNullOrWhiteSpace(theme) && osDarkModeEnabled)
{
theme = "dark";
}
if (theme == "dark" || theme == "black" || theme == "nord")
{
LightTheme = false;
}
else
{
LightTheme = true;
}
}
}
}

View File

@@ -3,7 +3,9 @@ using Android.Content.PM;
namespace Bit.Droid
{
[Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop)]
[Activity(
NoHistory = true,
LaunchMode = LaunchMode.SingleTop)]
[IntentFilter(new[] { Android.Content.Intent.ActionView },
Categories = new[] { Android.Content.Intent.CategoryDefault, Android.Content.Intent.CategoryBrowsable },
DataScheme = "bitwarden")]

View File

@@ -19,7 +19,7 @@ namespace Bit.App.Abstractions
Task SelectFileAsync();
Task<string> DisplayPromptAync(string title = null, string description = null, string text = null,
string okButtonText = null, string cancelButtonText = null, bool numericKeyboard = false,
bool autofocus = true);
bool autofocus = true, bool password = false);
void RateApp();
bool SupportsFaceBiometric();
Task<bool> SupportsFaceBiometricAsync();
@@ -42,8 +42,8 @@ namespace Bit.App.Abstractions
void OpenAccessibilitySettings();
void OpenAccessibilityOverlayPermissionSettings();
void OpenAutofillSettings();
bool UsingDarkTheme();
long GetActiveTime();
void CloseMainApp();
bool SupportsFido2();
}
}

View File

@@ -1,9 +1,22 @@
using System.Globalization;
using System;
using System.Globalization;
namespace Bit.App.Abstractions
{
public interface ILocalizeService
{
CultureInfo GetCurrentCultureInfo();
/// <summary>
/// Format date using device locale.
/// Needed for iOS as it provides locales unsupported in .Net
/// </summary>
string GetLocaleShortDate(DateTime? date);
/// <summary>
/// Format time using device locale.
/// Needed for iOS as it provides locales unsupported in .Net
/// </summary>
string GetLocaleShortTime(DateTime? time);
}
}

View File

@@ -0,0 +1,11 @@
using System.Threading.Tasks;
namespace Bit.App.Abstractions
{
public interface IPasswordRepromptService
{
string[] ProtectedFields { get; }
Task<bool> ShowPasswordPromptAsync();
}
}

View File

@@ -13,11 +13,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="3.2.1" />
<PackageReference Include="Plugin.Fingerprint" Version="2.1.2" />
<PackageReference Include="Xamarin.Essentials" Version="1.5.3.2" />
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.4.0" />
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
<PackageReference Include="Xamarin.Forms" Version="4.5.0.725" />
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2125" />
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
</ItemGroup>
@@ -124,6 +124,7 @@
<ItemGroup>
<Folder Include="Resources\" />
<Folder Include="Behaviors\" />
</ItemGroup>
<ItemGroup>
@@ -411,4 +412,7 @@
</EmbeddedResource>
</ItemGroup>
</Project>
<ItemGroup>
<None Remove="Behaviors\" />
</ItemGroup>
</Project>

View File

@@ -17,21 +17,12 @@ namespace Bit.App
{
public partial class App : Application
{
private readonly MobileI18nService _i18nService;
private readonly IUserService _userService;
private readonly IBroadcasterService _broadcasterService;
private readonly IMessagingService _messagingService;
private readonly IStateService _stateService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly ISyncService _syncService;
private readonly ITokenService _tokenService;
private readonly ICryptoService _cryptoService;
private readonly ICipherService _cipherService;
private readonly IFolderService _folderService;
private readonly ICollectionService _collectionService;
private readonly ISettingsService _settingsService;
private readonly IPasswordGenerationService _passwordGenerationService;
private readonly ISearchService _searchService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IAuthService _authService;
private readonly IStorageService _storageService;
@@ -54,20 +45,10 @@ namespace Bit.App
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_tokenService = ServiceContainer.Resolve<ITokenService>("tokenService");
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
_settingsService = ServiceContainer.Resolve<ISettingsService>("settingsService");
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
_searchService = ServiceContainer.Resolve<ISearchService>("searchService");
_authService = ServiceContainer.Resolve<IAuthService>("authService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
"passwordGenerationService");
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
Bootstrap();
@@ -178,6 +159,10 @@ namespace Bit.App
SyncIfNeeded();
}
}
if (Device.RuntimePlatform == Device.Android)
{
await _vaultTimeoutService.CheckVaultTimeoutAsync();
}
_messagingService.Send("startEventTimer");
}
@@ -215,8 +200,8 @@ namespace Bit.App
private async void ResumedAsync()
{
UpdateTheme();
await _vaultTimeoutService.CheckVaultTimeoutAsync();
_messagingService.Send("cancelVaultTimeoutTimer");
_messagingService.Send("startEventTimer");
await ClearCacheIfNeededAsync();
Prime();
@@ -237,22 +222,7 @@ namespace Bit.App
private async Task LogOutAsync(bool expired)
{
var userId = await _userService.GetUserIdAsync();
await Task.WhenAll(
_syncService.SetLastSyncAsync(DateTime.MinValue),
_tokenService.ClearTokenAsync(),
_cryptoService.ClearKeysAsync(),
_userService.ClearAsync(),
_settingsService.ClearAsync(userId),
_cipherService.ClearAsync(userId),
_folderService.ClearAsync(userId),
_collectionService.ClearAsync(userId),
_passwordGenerationService.ClearAsync(),
_vaultTimeoutService.ClearAsync(),
_stateService.PurgeAsync(),
_deviceActionService.ClearCacheAsync());
_vaultTimeoutService.BiometricLocked = true;
_searchService.ClearIndex();
await AppHelpers.LogOutAsync();
_authService.LogOut(() =>
{
Current.MainPage = new HomePage();
@@ -306,18 +276,9 @@ namespace Bit.App
{
return;
}
// Will only ever be null - look to remove this in the future
var vaultTimeout = _platformUtilsService.LockTimeout();
if (vaultTimeout == null)
{
vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
}
var vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
vaultTimeout = vaultTimeout.GetValueOrDefault(-1);
if (vaultTimeout > 0)
{
_messagingService.Send("scheduleVaultTimeoutTimer", vaultTimeout.Value);
}
else if (vaultTimeout == 0)
if (vaultTimeout == 0)
{
var action = await _storageService.GetAsync<string>(Constants.VaultTimeoutActionKey);
if (action == "logOut")
@@ -377,6 +338,10 @@ namespace Bit.App
InitializeComponent();
SetCulture();
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
Current.RequestedThemeChanged += (s, a) =>
{
UpdateTheme();
};
Current.MainPage = new HomePage();
var mainPageTask = SetMainPageAsync();
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
@@ -399,6 +364,15 @@ namespace Bit.App
});
}
private void UpdateTheme()
{
Device.BeginInvokeOnMainThread(() =>
{
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
_messagingService.Send("updatedTheme");
});
}
private async Task LockedAsync(bool autoPromptBiometric)
{
await _stateService.PurgeAsync();

View File

@@ -0,0 +1,43 @@
using Xamarin.Essentials;
using Xamarin.Forms;
namespace Bit.App.Behaviors
{
/// <summary>
/// This behavior prevents the Editor to be automatically scrolled to the bottom on focus.
/// This is needed due to this Xamarin Forms issue: https://github.com/xamarin/Xamarin.Forms/issues/2233
/// </summary>
public class EditorPreventAutoBottomScrollingOnFocusedBehavior : Behavior<Editor>
{
public static readonly BindableProperty ParentScrollViewProperty
= BindableProperty.Create(nameof(ParentScrollView), typeof(ScrollView), typeof(EditorPreventAutoBottomScrollingOnFocusedBehavior));
public ScrollView ParentScrollView
{
get => (ScrollView)GetValue(ParentScrollViewProperty);
set => SetValue(ParentScrollViewProperty, value);
}
protected override void OnAttachedTo(Editor bindable)
{
base.OnAttachedTo(bindable);
bindable.Focused += OnFocused;
}
private void OnFocused(object sender, FocusEventArgs e)
{
if (DeviceInfo.Platform.Equals(DevicePlatform.iOS) && ParentScrollView != null)
{
ParentScrollView.ScrollToAsync(ParentScrollView.ScrollX, ParentScrollView.ScrollY, true);
}
}
protected override void OnDetachingFrom(Editor bindable)
{
bindable.Focused -= OnFocused;
base.OnDetachingFrom(bindable);
}
}
}

View File

@@ -1,114 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Controls.CipherViewCell"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities"
xmlns:ff="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms">
<controls:ExtendedGrid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Controls.CipherViewCell"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities"
xmlns:ff="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
StyleClass="list-row, list-row-platform"
RowSpacing="0"
ColumnSpacing="0"
x:DataType="controls:CipherViewCellViewModel">
<Grid
x:Name="_grid"
StyleClass="list-row, list-row-platform"
RowSpacing="0"
ColumnSpacing="0"
x:DataType="controls:CipherViewCellViewModel">
<Grid.Resources>
<u:IconGlyphConverter x:Key="iconGlyphConverter"/>
<u:IconImageConverter x:Key="iconImageConverter"/>
<u:InverseBoolConverter x:Key="inverseBool" />
<u:StringHasValueConverter x:Key="stringHasValueConverter" />
</Grid.Resources>
<Grid.BindingContext>
<controls:CipherViewCellViewModel />
</Grid.BindingContext>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<controls:FaLabel
Grid.Column="0"
HorizontalOptions="Center"
VerticalOptions="Center"
StyleClass="list-icon, list-icon-platform"
IsVisible="{Binding ShowIconImage, Converter={StaticResource inverseBool}}"
Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}"
AutomationProperties.IsInAccessibleTree="False" />
<ff:CachedImage
Grid.Column="0"
BitmapOptimizations="True"
ErrorPlaceholder="login.png"
LoadingPlaceholder="login.png"
HorizontalOptions="Center"
VerticalOptions="Center"
WidthRequest="22"
HeightRequest="22"
IsVisible="{Binding ShowIconImage}"
Source="{Binding IconImageSource, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="False" />
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center" Padding="0, 7">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<Label
LineBreakMode="TailTruncation"
Grid.Column="0"
Grid.Row="0"
StyleClass="list-title, list-title-platform"
Text="{Binding Cipher.Name}" />
<Label
LineBreakMode="TailTruncation"
Grid.Column="0"
Grid.Row="1"
Grid.ColumnSpan="3"
StyleClass="list-subtitle, list-subtitle-platform"
Text="{Binding Cipher.SubTitle}"
IsVisible="{Binding Source={RelativeSource Self}, Path=Text,
Converter={StaticResource stringHasValueConverter}}"/>
<controls:FaLabel
x:Name="_icon"
Grid.Column="1"
Grid.Row="0"
Grid.Column="0"
HorizontalOptions="Center"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-icon, list-icon-platform"
AutomationProperties.IsInAccessibleTree="False" />
<ff:CachedImage
x:Name="_image"
Grid.Row="0"
Grid.Column="0"
BitmapOptimizations="True"
ErrorPlaceholder="login.png"
HorizontalOptions="Center"
VerticalOptions="Center"
WidthRequest="22"
HeightRequest="22"
IsVisible="False"
AutomationProperties.IsInAccessibleTree="False" />
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
LineBreakMode="TailTruncation"
Grid.Column="0"
Grid.Row="0"
StyleClass="list-title, list-title-platform"
Text="{Binding Cipher.Name, Mode=OneWay}" />
<Label
LineBreakMode="TailTruncation"
Grid.Column="0"
Grid.Row="1"
Grid.ColumnSpan="3"
StyleClass="list-subtitle, list-subtitle-platform"
Text="{Binding Cipher.SubTitle, Mode=OneWay}" />
<controls:FaLabel
Grid.Column="1"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf1e0;"
IsVisible="{Binding Cipher.Shared, Mode=OneWay}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Shared}" />
<controls:FaLabel
Grid.Column="2"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf0c6;"
IsVisible="{Binding Cipher.HasAttachments, Mode=OneWay}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Attachments}" />
</Grid>
<controls:MiButton
Grid.Row="0"
Grid.Column="2"
Text="&#xe5d3;"
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
Clicked="MoreButton_Clicked"
VerticalOptions="CenterAndExpand"
HorizontalOptions="EndAndExpand"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf1b2;"
IsVisible="{Binding Cipher.Shared, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" />
AutomationProperties.Name="{u:I18n Shared}" />
<controls:FaLabel
Grid.Column="2"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf0c6;"
IsVisible="{Binding Cipher.HasAttachments, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Attachments}" />
</Grid>
</ViewCell>
<controls:MiButton
Grid.Row="0"
Grid.Column="2"
Text="&#xe5d3;"
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
Clicked="MoreButton_Clicked"
VerticalOptions="CenterAndExpand"
HorizontalOptions="EndAndExpand"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" />
</controls:ExtendedGrid>

View File

@@ -1,45 +1,26 @@
using Bit.App.Pages;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using System;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using System;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public partial class CipherViewCell : ViewCell
public partial class CipherViewCell : ExtendedGrid
{
public static readonly BindableProperty CipherProperty = BindableProperty.Create(
nameof(Cipher), typeof(CipherView), typeof(CipherViewCell), default(CipherView), BindingMode.OneWay);
public static readonly BindableProperty WebsiteIconsEnabledProperty = BindableProperty.Create(
nameof(WebsiteIconsEnabled), typeof(bool), typeof(CipherViewCell), true, BindingMode.OneWay);
nameof(WebsiteIconsEnabled), typeof(bool?), typeof(CipherViewCell));
public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create(
nameof(ButtonCommand), typeof(Command<CipherView>), typeof(CipherViewCell));
private readonly IEnvironmentService _environmentService;
private CipherViewCellViewModel _viewModel;
private bool _usingNativeCell;
public CipherViewCell()
{
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
if (Device.RuntimePlatform == Device.iOS)
{
InitializeComponent();
_viewModel = _grid.BindingContext as CipherViewCellViewModel;
}
else
{
_usingNativeCell = true;
}
InitializeComponent();
}
public bool WebsiteIconsEnabled
public bool? WebsiteIconsEnabled
{
get => (bool)GetValue(WebsiteIconsEnabledProperty);
set => SetValue(WebsiteIconsEnabledProperty, value);
@@ -60,130 +41,31 @@ namespace Bit.App.Controls
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (_usingNativeCell)
{
return;
}
if (propertyName == CipherProperty.PropertyName)
{
_viewModel.Cipher = Cipher;
if (Cipher == null)
{
return;
}
BindingContext = new CipherViewCellViewModel(Cipher, WebsiteIconsEnabled ?? false);
}
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (_usingNativeCell)
else if (propertyName == WebsiteIconsEnabledProperty.PropertyName)
{
return;
if (Cipher == null)
{
return;
}
((CipherViewCellViewModel)BindingContext).WebsiteIconsEnabled = WebsiteIconsEnabled ?? false;
}
_image.Source = null;
CipherView cipher = null;
if (BindingContext is GroupingsPageListItem groupingsPageListItem)
{
cipher = groupingsPageListItem.Cipher;
}
else if (BindingContext is CipherView cv)
{
cipher = cv;
}
if (cipher != null)
{
var iconImage = GetIconImage(cipher);
if (iconImage.Item2 != null)
{
_image.IsVisible = true;
_icon.IsVisible = false;
_image.Source = iconImage.Item2;
_image.LoadingPlaceholder = "login.png";
}
else
{
_image.IsVisible = false;
_icon.IsVisible = true;
_icon.Text = iconImage.Item1;
}
}
}
public Tuple<string, string> GetIconImage(CipherView cipher)
{
string icon = null;
string image = null;
switch (cipher.Type)
{
case CipherType.Login:
var loginIconImage = GetLoginIconImage(cipher);
icon = loginIconImage.Item1;
image = loginIconImage.Item2;
break;
case CipherType.SecureNote:
icon = "";
break;
case CipherType.Card:
icon = "";
break;
case CipherType.Identity:
icon = "";
break;
default:
break;
}
return new Tuple<string, string>(icon, image);
}
private Tuple<string, string> GetLoginIconImage(CipherView cipher)
{
string icon = "";
string image = null;
if (cipher.Login.Uri != null)
{
var hostnameUri = cipher.Login.Uri;
var isWebsite = false;
if (hostnameUri.StartsWith(Constants.AndroidAppProtocol))
{
icon = "";
}
else if (hostnameUri.StartsWith(Constants.iOSAppProtocol))
{
icon = "";
}
else if (WebsiteIconsEnabled && !hostnameUri.Contains("://") && hostnameUri.Contains("."))
{
hostnameUri = string.Concat("http://", hostnameUri);
isWebsite = true;
}
else if (WebsiteIconsEnabled)
{
isWebsite = hostnameUri.StartsWith("http") && hostnameUri.Contains(".");
}
if (WebsiteIconsEnabled && isWebsite)
{
var hostname = CoreHelpers.GetHostname(hostnameUri);
var iconsUrl = _environmentService.IconsUrl;
if (string.IsNullOrWhiteSpace(iconsUrl))
{
if (!string.IsNullOrWhiteSpace(_environmentService.BaseUrl))
{
iconsUrl = string.Format("{0}/icons", _environmentService.BaseUrl);
}
else
{
iconsUrl = "https://icons.bitwarden.net";
}
}
image = string.Format("{0}/{1}/icon.png", iconsUrl, hostname);
}
}
return new Tuple<string, string>(icon, image);
}
private void MoreButton_Clicked(object sender, EventArgs e)
{
ButtonCommand?.Execute(Cipher);
var cipher = ((sender as MiButton)?.BindingContext as CipherViewCellViewModel)?.Cipher;
if (cipher != null)
{
ButtonCommand?.Execute(cipher);
}
}
}
}

View File

@@ -1,4 +1,5 @@
using Bit.Core.Models.View;
using Bit.App.Utilities;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
namespace Bit.App.Controls
@@ -6,11 +7,45 @@ namespace Bit.App.Controls
public class CipherViewCellViewModel : ExtendedViewModel
{
private CipherView _cipher;
private bool _websiteIconsEnabled;
private string _iconImageSource = string.Empty;
public CipherViewCellViewModel(CipherView cipherView, bool websiteIconsEnabled)
{
Cipher = cipherView;
WebsiteIconsEnabled = websiteIconsEnabled;
}
public CipherView Cipher
{
get => _cipher;
set => SetProperty(ref _cipher, value);
}
public bool WebsiteIconsEnabled
{
get => _websiteIconsEnabled;
set => SetProperty(ref _websiteIconsEnabled, value);
}
public bool ShowIconImage
{
get => WebsiteIconsEnabled
&& !string.IsNullOrWhiteSpace(Cipher.Login?.Uri)
&& IconImageSource != null;
}
public string IconImageSource
{
get
{
if (_iconImageSource == string.Empty) // default value since icon source can return null
{
_iconImageSource = IconImageHelper.GetLoginIconImage(Cipher);
}
return _iconImageSource;
}
}
}
}

View File

@@ -0,0 +1,8 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedCollectionView : CollectionView
{
}
}

View File

@@ -0,0 +1,8 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedGrid : Grid
{
}
}

View File

@@ -1,12 +0,0 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedListView : ListView
{
public ExtendedListView() { }
public ExtendedListView(ListViewCachingStrategy cachingStrategy)
: base(cachingStrategy) { }
}
}

View File

@@ -5,7 +5,7 @@ namespace Bit.App.Controls
public class ExtendedSlider : Slider
{
public static readonly BindableProperty ThumbBorderColorProperty = BindableProperty.Create(
nameof(ThumbBorderColor), typeof(Color), typeof(ExtendedSlider), Color.Gray);
nameof(ThumbBorderColor), typeof(Color), typeof(ExtendedSlider), Color.Default);
public Color ThumbBorderColor
{

View File

@@ -0,0 +1,8 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedStackLayout : StackLayout
{
}
}

View File

@@ -0,0 +1,25 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedStepper : Stepper
{
public static readonly BindableProperty StepperBackgroundColorProperty = BindableProperty.Create(
nameof(StepperBackgroundColor), typeof(Color), typeof(ExtendedStepper), Color.Default);
public static readonly BindableProperty StepperForegroundColorProperty = BindableProperty.Create(
nameof(StepperForegroundColor), typeof(Color), typeof(ExtendedStepper), Color.Default);
public Color StepperBackgroundColor
{
get => (Color)GetValue(StepperBackgroundColorProperty);
set => SetValue(StepperBackgroundColorProperty, value);
}
public Color StepperForegroundColor
{
get => (Color)GetValue(StepperForegroundColorProperty);
set => SetValue(StepperForegroundColorProperty, value);
}
}
}

View File

@@ -0,0 +1,10 @@
using System;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class SelectableLabel : Label
{
}
}

View File

@@ -1,137 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Controls.SendViewCell"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities">
<controls:ExtendedGrid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Controls.SendViewCell"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities"
StyleClass="list-row, list-row-platform"
RowSpacing="0"
ColumnSpacing="0"
x:DataType="controls:SendViewCellViewModel">
<Grid
x:Name="_grid"
StyleClass="list-row, list-row-platform"
RowSpacing="0"
ColumnSpacing="0"
x:DataType="controls:SendViewCellViewModel">
<Grid.Resources>
<u:SendIconGlyphConverter x:Key="sendIconGlyphConverter"/>
</Grid.Resources>
<Grid.BindingContext>
<controls:SendViewCellViewModel />
</Grid.BindingContext>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<controls:FaLabel
Grid.Row="0"
Grid.Column="0"
HorizontalOptions="Center"
VerticalOptions="Center"
StyleClass="list-icon, list-icon-platform"
Text="{Binding Send, Converter={StaticResource sendIconGlyphConverter}}"
AutomationProperties.IsInAccessibleTree="False" />
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center" Padding="0, 7">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<controls:FaLabel
x:Name="_icon"
Grid.Row="0"
<Label
LineBreakMode="TailTruncation"
Grid.Column="0"
HorizontalOptions="Center"
VerticalOptions="Center"
StyleClass="list-icon, list-icon-platform"
AutomationProperties.IsInAccessibleTree="False" />
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
LineBreakMode="TailTruncation"
Grid.Column="0"
Grid.Row="0"
StyleClass="list-title, list-title-platform"
Text="{Binding Send.Name, Mode=OneWay}" />
<Label
LineBreakMode="TailTruncation"
Grid.Column="0"
Grid.Row="1"
Grid.ColumnSpan="6"
StyleClass="list-subtitle, list-subtitle-platform"
Text="{Binding Send.DisplayDate, Mode=OneWay}" />
<controls:FaLabel
Grid.Column="1"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf071;"
IsVisible="{Binding Send.Disabled, Mode=OneWay}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Disabled}" />
<controls:FaLabel
Grid.Column="2"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf084;"
IsVisible="{Binding Send.HasPassword, Mode=OneWay}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Password}" />
<controls:FaLabel
Grid.Column="3"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf05e;"
IsVisible="{Binding Send.MaxAccessCountReached, Mode=OneWay}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n MaxAccessCountReached}" />
<controls:FaLabel
Grid.Column="4"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf017;"
IsVisible="{Binding Send.Expired, Mode=OneWay}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Expired}" />
<controls:FaLabel
Grid.Column="5"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf1f8;"
IsVisible="{Binding Send.PendingDelete, Mode=OneWay}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n PendingDelete}" />
</Grid>
<controls:MiButton
Grid.Row="0"
Grid.Column="2"
Text="&#xe5d3;"
IsVisible="{Binding ShowOptions, Mode=OneWay}"
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
Clicked="MoreButton_Clicked"
VerticalOptions="CenterAndExpand"
HorizontalOptions="EndAndExpand"
StyleClass="list-title, list-title-platform"
Text="{Binding Send.Name}" />
<Label
LineBreakMode="TailTruncation"
Grid.Column="0"
Grid.Row="1"
Grid.ColumnSpan="6"
StyleClass="list-subtitle, list-subtitle-platform"
Text="{Binding Send.DisplayDate}" />
<controls:FaLabel
Grid.Column="1"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf071;"
IsVisible="{Binding Send.Disabled, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" />
AutomationProperties.Name="{u:I18n Disabled}" />
<controls:FaLabel
Grid.Column="2"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf084;"
IsVisible="{Binding Send.HasPassword, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Password}" />
<controls:FaLabel
Grid.Column="3"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf05e;"
IsVisible="{Binding Send.MaxAccessCountReached, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n MaxAccessCountReached}" />
<controls:FaLabel
Grid.Column="4"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf017;"
IsVisible="{Binding Send.Expired, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Expired}" />
<controls:FaLabel
Grid.Column="5"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf1f8;"
IsVisible="{Binding Send.PendingDelete, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n PendingDelete}" />
</Grid>
</ViewCell>
<controls:MiButton
Grid.Row="0"
Grid.Column="2"
Text="&#xe5d3;"
IsVisible="{Binding ShowOptions, Mode=OneWay}"
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
Clicked="MoreButton_Clicked"
VerticalOptions="CenterAndExpand"
HorizontalOptions="EndAndExpand"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" />
</controls:ExtendedGrid>

View File

@@ -1,14 +1,10 @@
using System;
using Bit.App.Pages;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public partial class SendViewCell : ViewCell
public partial class SendViewCell : ExtendedGrid
{
public static readonly BindableProperty SendProperty = BindableProperty.Create(
nameof(Send), typeof(SendView), typeof(SendViewCell), default(SendView), BindingMode.OneWay);
@@ -17,25 +13,11 @@ namespace Bit.App.Controls
nameof(ButtonCommand), typeof(Command<SendView>), typeof(SendViewCell));
public static readonly BindableProperty ShowOptionsProperty = BindableProperty.Create(
nameof(ShowOptions), typeof(bool), typeof(SendViewCell));
private readonly IEnvironmentService _environmentService;
private SendViewCellViewModel _viewModel;
private bool _usingNativeCell;
nameof(ShowOptions), typeof(bool), typeof(SendViewCell), true, BindingMode.OneWay);
public SendViewCell()
{
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
if (Device.RuntimePlatform == Device.iOS)
{
InitializeComponent();
_viewModel = _grid.BindingContext as SendViewCellViewModel;
}
else
{
_usingNativeCell = true;
}
InitializeComponent();
}
public SendView Send
@@ -52,72 +34,30 @@ namespace Bit.App.Controls
public bool ShowOptions
{
get => GetValue(ShowOptionsProperty) is bool && (bool)GetValue(ShowOptionsProperty);
get => (bool)GetValue(ShowOptionsProperty);
set => SetValue(ShowOptionsProperty, value);
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (_usingNativeCell)
{
return;
}
if (propertyName == SendProperty.PropertyName)
{
_viewModel.Send = Send;
if (Send == null)
{
return;
}
BindingContext = new SendViewCellViewModel(Send, ShowOptions);
}
else if (propertyName == ShowOptionsProperty.PropertyName)
{
_viewModel.ShowOptions = ShowOptions;
}
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (_usingNativeCell)
{
return;
}
SendView send = null;
if (BindingContext is SendGroupingsPageListItem sendGroupingsPageListItem)
{
send = sendGroupingsPageListItem.Send;
}
else if (BindingContext is SendView sv)
{
send = sv;
}
if (send != null)
{
var iconImage = GetIconImage(send);
_icon.IsVisible = true;
_icon.Text = iconImage;
}
}
public string GetIconImage(SendView send)
{
string icon = null;
switch (send.Type)
{
case SendType.Text:
icon = "\uf0f6"; // fa-file-text-o
break;
case SendType.File:
icon = "\uf016"; // fa-file-o
break;
default:
break;
}
return icon;
}
private void MoreButton_Clicked(object sender, EventArgs e)
{
ButtonCommand?.Execute(Send);
var send = ((sender as MiButton)?.BindingContext as SendViewCellViewModel)?.Send;
if (send != null)
{
ButtonCommand?.Execute(send);
}
}
}
}

View File

@@ -8,6 +8,12 @@ namespace Bit.App.Controls
private SendView _send;
private bool _showOptions;
public SendViewCellViewModel(SendView sendView, bool showOptions)
{
Send = sendView;
ShowOptions = showOptions;
}
public SendView Send
{
get => _send;

View File

@@ -0,0 +1,25 @@
using Xamarin.Forms;
namespace Bit.App.Effects
{
public class ScrollEnabledEffect : RoutingEffect
{
public static readonly BindableProperty IsScrollEnabledProperty =
BindableProperty.CreateAttached("IsScrollEnabled", typeof(bool), typeof(ScrollEnabledEffect), true);
public static bool GetIsScrollEnabled(BindableObject view)
{
return (bool)view.GetValue(IsScrollEnabledProperty);
}
public static void SetIsScrollEnabled(BindableObject view, bool value)
{
view.SetValue(IsScrollEnabledProperty, value);
}
public ScrollEnabledEffect()
: base("Bitwarden.ScrollEnabledEffect")
{
}
}
}

View File

@@ -1,11 +0,0 @@
using Xamarin.Forms;
namespace Bit.App.Effects
{
public class SelectableLabelEffect : RoutingEffect
{
public SelectableLabelEffect()
: base("Bitwarden.SelectableLabelEffect")
{ }
}
}

View File

@@ -0,0 +1,187 @@
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.Core.Models.Domain;
using Xamarin.Essentials;
namespace Bit.App.Pages
{
public class BaseChangePasswordViewModel : BaseViewModel
{
protected readonly IPlatformUtilsService _platformUtilsService;
protected readonly IUserService _userService;
protected readonly IPolicyService _policyService;
protected readonly IPasswordGenerationService _passwordGenerationService;
protected readonly II18nService _i18nService;
protected readonly ICryptoService _cryptoService;
protected readonly IDeviceActionService _deviceActionService;
protected readonly IApiService _apiService;
protected readonly ISyncService _syncService;
private bool _showPassword;
private bool _isPolicyInEffect;
private string _policySummary;
private MasterPasswordPolicyOptions _policy;
protected BaseChangePasswordViewModel()
{
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_userService = ServiceContainer.Resolve<IUserService>("userService");
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
_passwordGenerationService =
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
}
public bool ShowPassword
{
get => _showPassword;
set => SetProperty(ref _showPassword, value,
additionalPropertyNames: new[] { nameof(ShowPasswordIcon) });
}
public bool IsPolicyInEffect
{
get => _isPolicyInEffect;
set => SetProperty(ref _isPolicyInEffect, value);
}
public string PolicySummary
{
get => _policySummary;
set => SetProperty(ref _policySummary, value);
}
public MasterPasswordPolicyOptions Policy
{
get => _policy;
set => SetProperty(ref _policy, value);
}
public string ShowPasswordIcon => ShowPassword ? "" : "";
public string MasterPassword { get; set; }
public string ConfirmMasterPassword { get; set; }
public string Hint { get; set; }
public async Task InitAsync(bool forceSync = false)
{
if (forceSync)
{
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
await task.ContinueWith(async (t) => await CheckPasswordPolicy());
}
else
{
await CheckPasswordPolicy();
}
}
private async Task CheckPasswordPolicy()
{
Policy = await _policyService.GetMasterPasswordPolicyOptions();
IsPolicyInEffect = Policy?.InEffect() ?? false;
if (!IsPolicyInEffect)
{
return;
}
var bullet = "\n" + "".PadLeft(4) + "\u2022 ";
var sb = new StringBuilder();
sb.Append(_i18nService.T("MasterPasswordPolicyInEffect"));
if (Policy.MinComplexity > 0)
{
sb.Append(bullet)
.Append(string.Format(_i18nService.T("PolicyInEffectMinComplexity"), Policy.MinComplexity));
}
if (Policy.MinLength > 0)
{
sb.Append(bullet).Append(string.Format(_i18nService.T("PolicyInEffectMinLength"), Policy.MinLength));
}
if (Policy.RequireUpper)
{
sb.Append(bullet).Append(_i18nService.T("PolicyInEffectUppercase"));
}
if (Policy.RequireLower)
{
sb.Append(bullet).Append(_i18nService.T("PolicyInEffectLowercase"));
}
if (Policy.RequireNumbers)
{
sb.Append(bullet).Append(_i18nService.T("PolicyInEffectNumbers"));
}
if (Policy.RequireSpecial)
{
sb.Append(bullet).Append(string.Format(_i18nService.T("PolicyInEffectSpecial"), "!@#$%^&*"));
}
PolicySummary = sb.ToString();
}
protected async Task<bool> ValidateMasterPasswordAsync()
{
if (Connectivity.NetworkAccess == NetworkAccess.None)
{
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
AppResources.InternetConnectionRequiredTitle, AppResources.Ok);
return false;
}
if (string.IsNullOrWhiteSpace(MasterPassword))
{
await _platformUtilsService.ShowDialogAsync(
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
AppResources.AnErrorHasOccurred, AppResources.Ok);
return false;
}
if (IsPolicyInEffect)
{
var userInput = await GetPasswordStrengthUserInput();
var passwordStrength = _passwordGenerationService.PasswordStrength(MasterPassword, userInput);
if (!await _policyService.EvaluateMasterPassword(passwordStrength.Score, MasterPassword, Policy))
{
await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordPolicyValidationMessage,
AppResources.MasterPasswordPolicyValidationTitle, AppResources.Ok);
return false;
}
}
else
{
if (MasterPassword.Length < 8)
{
await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordLengthValMessage,
AppResources.MasterPasswordPolicyValidationTitle, AppResources.Ok);
return false;
}
}
if (MasterPassword != ConfirmMasterPassword)
{
await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordConfirmationValMessage,
AppResources.AnErrorHasOccurred, AppResources.Ok);
return false;
}
return true;
}
private async Task<List<string>> GetPasswordStrengthUserInput()
{
var email = await _userService.GetEmailAsync();
List<string> userInput = null;
var atPosition = email.IndexOf('@');
if (atPosition > -1)
{
var rx = new Regex("/[^A-Za-z0-9]/", RegexOptions.Compiled);
var data = rx.Split(email.Substring(0, atPosition).Trim().ToLower());
userInput = new List<string>(data);
}
return userInput;
}
}
}

View File

@@ -13,7 +13,7 @@
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
<ToolbarItem Text="{u:I18n Save}" Clicked="Submit_Clicked" />
</ContentPage.ToolbarItems>

View File

@@ -13,7 +13,7 @@
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
<ToolbarItem Text="{u:I18n Submit}" Clicked="Submit_Clicked" />
</ContentPage.ToolbarItems>

View File

@@ -40,11 +40,12 @@
HorizontalTextAlignment="Center"></Label>
<StackLayout Spacing="5">
<Button Text="{u:I18n LogIn}"
Clicked="LogIn_Clicked"></Button>
StyleClass="btn-primary"
Clicked="LogIn_Clicked" />
<Button Text="{u:I18n CreateAccount}"
Clicked="Register_Clicked"></Button>
Clicked="Register_Clicked" />
<Button Text="{u:I18n LogInSso}"
Clicked="LogInSso_Clicked"></Button>
Clicked="LogInSso_Clicked" />
</StackLayout>
</StackLayout>
</StackLayout>

View File

@@ -13,11 +13,13 @@ namespace Bit.App.Pages
private readonly HomeViewModel _vm;
private readonly AppOptions _appOptions;
private IMessagingService _messagingService;
private IBroadcasterService _broadcasterService;
public HomePage(AppOptions appOptions = null)
{
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_messagingService.Send("showStatusBar", false);
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_appOptions = appOptions;
InitializeComponent();
_vm = BindingContext as HomeViewModel;
@@ -26,7 +28,7 @@ namespace Bit.App.Pages
_vm.StartRegisterAction = () => Device.BeginInvokeOnMainThread(async () => await StartRegisterAsync());
_vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync());
_vm.StartEnvironmentAction = () => Device.BeginInvokeOnMainThread(async () => await StartEnvironmentAsync());
_logo.Source = !ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png";
UpdateLogo();
}
public async Task DismissRegisterPageAndLogInAsync(string email)
@@ -39,6 +41,27 @@ namespace Bit.App.Pages
{
base.OnAppearing();
_messagingService.Send("showStatusBar", false);
_broadcasterService.Subscribe(nameof(HomePage), async (message) =>
{
if (message.Command == "updatedTheme")
{
Device.BeginInvokeOnMainThread(() =>
{
UpdateLogo();
});
}
});
}
protected override void OnDisappearing()
{
base.OnDisappearing();
_broadcasterService.Unsubscribe(nameof(HomePage));
}
private void UpdateLogo()
{
_logo.Source = !ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png";
}
private void Close_Clicked(object sender, EventArgs e)

View File

@@ -120,7 +120,9 @@
IsVisible="{Binding BiometricIntegrityValid, Converter={StaticResource inverseBool}}" />
<Button Text="{Binding BiometricButtonText}" Clicked="Biometric_Clicked"
IsVisible="{Binding BiometricButtonVisible}"></Button>
<Button Text="{u:I18n Unlock}" Clicked="Unlock_Clicked"></Button>
<Button Text="{u:I18n Unlock}"
StyleClass="btn-primary"
Clicked="Unlock_Clicked" />
</StackLayout>
</StackLayout>
</ScrollView>

View File

@@ -8,6 +8,7 @@ using Bit.Core.Models.Domain;
using Bit.Core.Utilities;
using System;
using System.Threading.Tasks;
using Bit.App.Utilities;
using Bit.Core.Models.Request;
using Xamarin.Forms;
@@ -37,7 +38,6 @@ namespace Bit.App.Pages
private string _biometricButtonText;
private string _loggedInAsText;
private string _lockedVerifyText;
private int _invalidPinAttempts = 0;
private Tuple<bool, bool> _pinSet;
public LockPageViewModel()
@@ -208,6 +208,7 @@ namespace Bit.App.Pages
if (!failed)
{
Pin = string.Empty;
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
await SetKeyAndContinueAsync(key);
}
}
@@ -217,6 +218,7 @@ namespace Bit.App.Pages
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
failed = false;
Pin = string.Empty;
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
await SetKeyAndContinueAsync(key);
}
}
@@ -226,8 +228,8 @@ namespace Bit.App.Pages
}
if (failed)
{
_invalidPinAttempts++;
if (_invalidPinAttempts >= 5)
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
if (invalidUnlockAttempts >= 5)
{
_messagingService.Send("logout");
return;
@@ -239,32 +241,31 @@ namespace Bit.App.Pages
else
{
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdf, kdfIterations);
var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key);
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
var passwordValid = false;
if (keyHash != null)
if (storedKeyHash != null)
{
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
if (storedKeyHash != null)
passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(MasterPassword, key);
}
else
{
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
var request = new PasswordVerificationRequest();
request.MasterPasswordHash = keyHash;
try
{
passwordValid = storedKeyHash == keyHash;
await _apiService.PostAccountVerifyPasswordAsync(request);
passwordValid = true;
var localKeyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
await _cryptoService.SetKeyHashAsync(localKeyHash);
}
else
catch (Exception e)
{
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
var request = new PasswordVerificationRequest();
request.MasterPasswordHash = keyHash;
try
{
await _apiService.PostAccountVerifyPasswordAsync(request);
passwordValid = true;
await _cryptoService.SetKeyHashAsync(keyHash);
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", e.GetType(), e.StackTrace);
}
await _deviceActionService.HideLoadingAsync();
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", e.GetType(), e.StackTrace);
}
await _deviceActionService.HideLoadingAsync();
}
if (passwordValid)
{
@@ -278,6 +279,7 @@ namespace Bit.App.Pages
_vaultTimeoutService.PinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
}
MasterPassword = string.Empty;
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
await SetKeyAndContinueAsync(key);
// Re-enable biometrics
@@ -288,6 +290,12 @@ namespace Bit.App.Pages
}
else
{
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
if (invalidUnlockAttempts >= 5)
{
_messagingService.Send("logout");
return;
}
await _platformUtilsService.ShowDialogAsync(AppResources.InvalidMasterPassword,
AppResources.AnErrorHasOccurred);
}
@@ -309,7 +317,9 @@ namespace Bit.App.Pages
ShowPassword = !ShowPassword;
var page = (Page as LockPage);
var entry = PinLock ? page.PinEntry : page.MasterPasswordEntry;
var str = PinLock ? Pin : MasterPassword;
entry.Focus();
entry.CursorPosition = String.IsNullOrEmpty(str) ? 0 : str.Length;
}
public async Task PromptBiometricAsync()

View File

@@ -82,7 +82,9 @@
</Grid>
</StackLayout>
<StackLayout Padding="10, 0">
<Button Text="{u:I18n LogIn}" Clicked="LogIn_Clicked" />
<Button Text="{u:I18n LogIn}"
StyleClass="btn-primary"
Clicked="LogIn_Clicked" />
</StackLayout>
</StackLayout>
</ScrollView>

View File

@@ -16,6 +16,8 @@ namespace Bit.App.Pages
private readonly LoginPageViewModel _vm;
private readonly AppOptions _appOptions;
private bool _inputFocused;
public LoginPage(string email = null, AppOptions appOptions = null)
{
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
@@ -27,6 +29,8 @@ namespace Bit.App.Pages
_vm.Page = this;
_vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
_vm.LogInSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await LogInSuccessAsync());
_vm.UpdateTempPasswordAction =
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
_vm.CloseAction = async () =>
{
_messagingService.Send("showStatusBar", false);
@@ -58,13 +62,10 @@ namespace Bit.App.Pages
{
base.OnAppearing();
await _vm.InitAsync();
if (string.IsNullOrWhiteSpace(_vm.Email))
if (!_inputFocused)
{
RequestFocus(_email);
}
else
{
RequestFocus(_masterPassword);
RequestFocus(string.IsNullOrWhiteSpace(_vm.Email) ? _email : _masterPassword);
_inputFocused = true;
}
}
@@ -123,5 +124,11 @@ namespace Bit.App.Pages
var previousPage = await AppHelpers.ClearPreviousPage();
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
}
private async Task UpdateTempPasswordAsync()
{
var page = new UpdateTempPasswordPage();
await Navigation.PushModalAsync(new NavigationPage(page));
}
}
}

View File

@@ -6,11 +6,12 @@ using Bit.Core.Exceptions;
using Bit.Core.Utilities;
using System;
using System.Threading.Tasks;
using Bit.App.Utilities;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class LoginPageViewModel : BaseViewModel
public class LoginPageViewModel : CaptchaProtectedViewModel
{
private const string Keys_RememberedEmail = "rememberedEmail";
private const string Keys_RememberEmail = "rememberEmail";
@@ -21,6 +22,8 @@ namespace Bit.App.Pages
private readonly IStorageService _storageService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IStateService _stateService;
private readonly IEnvironmentService _environmentService;
private readonly II18nService _i18nService;
private bool _showPassword;
private string _email;
@@ -34,6 +37,8 @@ namespace Bit.App.Pages
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
PageTitle = AppResources.Bitwarden;
TogglePasswordCommand = new Command(TogglePassword);
@@ -68,8 +73,14 @@ namespace Bit.App.Pages
public bool RememberEmail { get; set; }
public Action StartTwoFactorAction { get; set; }
public Action LogInSuccessAction { get; set; }
public Action UpdateTempPasswordAction { get; set; }
public Action CloseAction { get; set; }
protected override II18nService i18nService => _i18nService;
protected override IEnvironmentService environmentService => _environmentService;
protected override IDeviceActionService deviceActionService => _deviceActionService;
protected override IPlatformUtilsService platformUtilsService => _platformUtilsService;
public async Task InitAsync()
{
if (string.IsNullOrWhiteSpace(Email))
@@ -80,20 +91,19 @@ namespace Bit.App.Pages
RememberEmail = rememberEmail.GetValueOrDefault(true);
}
public async Task LogInAsync()
public async Task LogInAsync(bool showLoading = true)
{
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
{
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
AppResources.InternetConnectionRequiredTitle);
AppResources.InternetConnectionRequiredTitle, AppResources.Ok);
return;
}
if (string.IsNullOrWhiteSpace(Email))
{
await _platformUtilsService.ShowDialogAsync(
string.Format(AppResources.ValidationFieldRequired, AppResources.EmailAddress),
AppResources.AnErrorHasOccurred,
AppResources.Ok);
AppResources.AnErrorHasOccurred, AppResources.Ok);
return;
}
if (!Email.Contains("@"))
@@ -106,17 +116,19 @@ namespace Bit.App.Pages
{
await _platformUtilsService.ShowDialogAsync(
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
AppResources.AnErrorHasOccurred,
AppResources.Ok);
AppResources.AnErrorHasOccurred, AppResources.Ok);
return;
}
ShowPassword = false;
try
{
await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn);
var response = await _authService.LogInAsync(Email, MasterPassword);
MasterPassword = string.Empty;
if (showLoading)
{
await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn);
}
var response = await _authService.LogInAsync(Email, MasterPassword, _captchaToken);
if (RememberEmail)
{
await _storageService.SaveAsync(Keys_RememberedEmail, Email);
@@ -125,11 +137,30 @@ namespace Bit.App.Pages
{
await _storageService.RemoveAsync(Keys_RememberedEmail);
}
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
if (response.CaptchaNeeded)
{
if (await HandleCaptchaAsync(response.CaptchaSiteKey))
{
await LogInAsync(false);
_captchaToken = null;
}
return;
}
MasterPassword = string.Empty;
_captchaToken = null;
await _deviceActionService.HideLoadingAsync();
if (response.TwoFactor)
{
StartTwoFactorAction?.Invoke();
}
else if (response.ForcePasswordReset)
{
UpdateTempPasswordAction?.Invoke();
}
else
{
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
@@ -140,11 +171,13 @@ namespace Bit.App.Pages
}
catch (ApiException e)
{
_captchaToken = null;
MasterPassword = string.Empty;
await _deviceActionService.HideLoadingAsync();
if (e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
AppResources.AnErrorHasOccurred, AppResources.Ok);
}
}
}
@@ -152,7 +185,9 @@ namespace Bit.App.Pages
public void TogglePassword()
{
ShowPassword = !ShowPassword;
(Page as LoginPage).MasterPasswordEntry.Focus();
var entry = (Page as LoginPage).MasterPasswordEntry;
entry.Focus();
entry.CursorPosition = String.IsNullOrEmpty(MasterPassword) ? 0 : MasterPassword.Length;
}
}
}

View File

@@ -13,8 +13,7 @@
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
<ToolbarItem Text="{u:I18n LogIn}" Clicked="LogIn_Clicked" />
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
</ContentPage.ToolbarItems>
<ScrollView>
@@ -36,6 +35,11 @@
ReturnCommand="{Binding LogInCommand}" />
</StackLayout>
</StackLayout>
<StackLayout Padding="10, 0">
<Button Text="{u:I18n LogIn}"
StyleClass="btn-primary"
Clicked="LogIn_Clicked"></Button>
</StackLayout>
</StackLayout>
</ScrollView>

View File

@@ -32,6 +32,8 @@ namespace Bit.App.Pages
_vm.StartSetPasswordAction = () =>
Device.BeginInvokeOnMainThread(async () => await StartSetPasswordAsync());
_vm.SsoAuthSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SsoAuthSuccessAsync());
_vm.UpdateTempPasswordAction =
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
_vm.CloseAction = async () =>
{
_messagingService.Send("showStatusBar", false);
@@ -103,6 +105,12 @@ namespace Bit.App.Pages
var page = new SetPasswordPage(_appOptions, _vm.OrgIdentifier);
await Navigation.PushModalAsync(new NavigationPage(page));
}
private async Task UpdateTempPasswordAsync()
{
var page = new UpdateTempPasswordPage();
await Navigation.PushModalAsync(new NavigationPage(page));
}
private async Task SsoAuthSuccessAsync()
{

View File

@@ -5,6 +5,7 @@ using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System;
using System.Threading.Tasks;
using Bit.App.Utilities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Domain;
@@ -59,6 +60,7 @@ namespace Bit.App.Pages
public Action StartSetPasswordAction { get; set; }
public Action SsoAuthSuccessAction { get; set; }
public Action CloseAction { get; set; }
public Action UpdateTempPasswordAction { get; set; }
public async Task InitAsync()
{
@@ -122,43 +124,28 @@ namespace Bit.App.Pages
"domain_hint=" + Uri.EscapeDataString(OrgIdentifier);
WebAuthenticatorResult authResult = null;
bool cancelled = false;
try
{
authResult = await WebAuthenticator.AuthenticateAsync(new Uri(url),
new Uri(redirectUri));
}
catch (TaskCanceledException taskCanceledException)
catch (TaskCanceledException)
{
// user canceled
await _deviceActionService.HideLoadingAsync();
return;
}
var code = GetResultCode(authResult, state);
if (!string.IsNullOrEmpty(code))
{
await LogIn(code, codeVerifier, redirectUri);
}
else
{
await _deviceActionService.HideLoadingAsync();
cancelled = true;
}
catch (Exception e)
{
// WebAuthenticator throws NSErrorException if iOS flow is cancelled - by setting cancelled to true
// here we maintain the appearance of a clean cancellation (we don't want to do this across the board
// because we still want to present legitimate errors). If/when this is fixed, we can remove this
// particular catch block (catching taskCanceledException above must remain)
// https://github.com/xamarin/Essentials/issues/1240
if (Device.RuntimePlatform == Device.iOS)
{
await _deviceActionService.HideLoadingAsync();
cancelled = true;
}
}
if (!cancelled)
{
var code = GetResultCode(authResult, state);
if (!string.IsNullOrEmpty(code))
{
await LogIn(code, codeVerifier, redirectUri);
}
else
{
await _deviceActionService.HideLoadingAsync();
await _platformUtilsService.ShowDialogAsync(AppResources.LoginSsoError,
AppResources.AnErrorHasOccurred);
}
await _platformUtilsService.ShowDialogAsync(AppResources.LoginSsoError,
AppResources.AnErrorHasOccurred);
}
}
@@ -182,6 +169,7 @@ namespace Bit.App.Pages
try
{
var response = await _authService.LogInSsoAsync(code, codeVerifier, redirectUri);
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
if (RememberOrgIdentifier)
{
await _storageService.SaveAsync(Keys_RememberedOrgIdentifier, OrgIdentifier);
@@ -198,6 +186,10 @@ namespace Bit.App.Pages
else if (response.ResetMasterPassword)
{
StartSetPasswordAction?.Invoke();
}
else if (response.ForcePasswordReset)
{
UpdateTempPasswordAction?.Invoke();
}
else
{

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