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

Compare commits

...

195 Commits

Author SHA1 Message Date
Kyle Spearrin
b7819838b8 parse fallback package id from first node title 2019-08-01 16:39:01 -04:00
Kyle Spearrin
67c6cf6b8c load previous view/edit page after lock 2019-07-31 16:50:16 -04:00
Kyle Spearrin
d91d71333b LastClipboardValue using static store rather than state 2019-07-31 11:21:07 -04:00
Kyle Spearrin
431804ea80 loop on reset with range instead of clear 2019-07-29 22:35:53 -04:00
Kyle Spearrin
7a7ab7bd0e New Crowdin translations (#570)
* New translations AppResources.resx (French)

* New translations AppResources.resx (German)

* New translations AppResources.resx (Hungarian)

* New translations copy.resx (Hungarian)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Russian)
2019-07-29 15:15:21 -04:00
Kyle Spearrin
b8cdee383b bump version 2019-07-27 12:42:42 -04:00
Kyle Spearrin
580fa02ee1 enable event logging 2019-07-27 12:41:38 -04:00
Kyle Spearrin
421834153d catch InteractionNotAllowed 2019-07-27 12:39:59 -04:00
Kyle Spearrin
011f04e1dc editorconfig 2019-07-26 11:44:43 -04:00
Kyle Spearrin
3d8056704c yubikey token entry is password field 2019-07-25 16:10:46 -04:00
Kyle Spearrin
41263f3419 New Crowdin translations (#569)
* New translations AppResources.resx (Catalan)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Polish)
2019-07-24 10:48:49 -04:00
Kyle Spearrin
9fe9210cb7 null check on id 2019-07-24 10:42:13 -04:00
Kyle Spearrin
2272b10820 null check on cipher when autofilling 2019-07-24 10:40:12 -04:00
Kyle Spearrin
9d6fc73fcc New Crowdin translations (#568)
* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Spanish)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Polish)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Italian)

* New translations AppResources.resx (Hebrew)

* New translations AppResources.resx (German)

* New translations AppResources.resx (French)

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

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (Czech)

* New translations AppResources.resx (Ukrainian)
2019-07-23 09:30:01 -04:00
Kyle Spearrin
73ecd67b20 New Crowdin translations (#567)
* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Polish)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Ukrainian)

* New translations AppResources.resx (Spanish)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Italian)

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (Czech)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Hebrew)

* New translations AppResources.resx (German)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Estonian)
2019-07-23 09:16:10 -04:00
Kyle Spearrin
e5ce3dbd32 visibility typo 2019-07-23 09:11:01 -04:00
Kyle Spearrin
a0a5e30f48 re-work hockeyapp init 2019-07-23 09:08:16 -04:00
Kyle Spearrin
0eddee5816 check $LastExitCode 2019-07-23 00:10:25 -04:00
Kyle Spearrin
6d2dcb73ae event service func 2019-07-23 00:06:34 -04:00
Kyle Spearrin
0d6cc91b67 don't allow device PIN fallback 2019-07-22 23:34:39 -04:00
Kyle Spearrin
ae52922698 version bump 2019-07-22 23:07:00 -04:00
Kyle Spearrin
236496e69f formatting 2019-07-22 21:37:56 -04:00
kspearrin
fe5cdb0004 * CredentialProviderViewController.cs:
* LoadingViewController.cs: reset after using event service
2019-07-22 21:35:05 -04:00
Kyle Spearrin
f9547f158e log autofill events 2019-07-22 15:50:59 -04:00
Kyle Spearrin
0c75374c0f New Crowdin translations (#565)
* New translations AppResources.resx (Afrikaans)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Polish)

* New translations AppResources.resx (Romanian)

* New translations AppResources.resx (Norwegian Bokmal)

* New translations AppResources.resx (Korean)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Thai)

* New translations AppResources.resx (Vietnamese)

* New translations AppResources.resx (Ukrainian)

* New translations AppResources.resx (Turkish)

* New translations AppResources.resx (Swedish)

* New translations AppResources.resx (Spanish)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Italian)

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (Czech)

* New translations AppResources.resx (Croatian)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Catalan)

* New translations AppResources.resx (Bulgarian)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Indonesian)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (Hindi)

* New translations AppResources.resx (Hebrew)

* New translations AppResources.resx (German)

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

* New translations AppResources.resx (French)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (Estonian)
2019-07-22 14:53:12 -04:00
Kyle Spearrin
0b249d4dd4 check yubico token length == 44 2019-07-22 13:59:12 -04:00
Kyle Spearrin
a2bac9d368 a11y labeling 2019-07-22 12:04:17 -04:00
Kyle Spearrin
392e429dfd more options button for ios on folder add/edit 2019-07-22 10:52:19 -04:00
Kyle Spearrin
50623b9b29 fix no folder reference 2019-07-22 10:52:04 -04:00
Kyle Spearrin
e7ce050324 use fingerprintButtonText on extension unlock 2019-07-22 10:24:18 -04:00
Kyle Spearrin
762b574d49 rebuild same fingerprintName string 2019-07-22 10:12:14 -04:00
Kyle Spearrin
d73bf6d225 assign new messageCallback when subbing 2019-07-22 09:21:00 -04:00
Kyle Spearrin
c2108fdda0 null checking 2019-07-22 08:44:55 -04:00
Kyle Spearrin
2062a284e3 fix lock checks on sleep of app 2019-07-22 08:37:06 -04:00
Kyle Spearrin
9164c9b946 InitAppIfNeeded 2019-07-22 08:22:02 -04:00
Kyle Spearrin
13ddd10c40 update autofill identities if needed 2019-07-22 07:09:51 -04:00
Kyle Spearrin
e407acd2a7 bump version 2019-07-13 20:28:20 -04:00
Kyle Spearrin
11cdf52ec8 disable events funcionality for now 2019-07-13 20:27:15 -04:00
Kyle Spearrin
40a3541e8e handle sleep event on ios manually 2019-07-13 20:25:31 -04:00
Kyle Spearrin
7da13e22ad process event uploads on ios 2019-07-12 20:56:54 -04:00
Kyle Spearrin
38d702b6fe log some events 2019-07-12 17:29:40 -04:00
Kyle Spearrin
df2af5459e register event service with container 2019-07-12 15:51:47 -04:00
Kyle Spearrin
40d68b1654 add event service 2019-07-11 09:30:25 -04:00
Kyle Spearrin
a240a4ac66 get autofill web scheme for android p devices 2019-07-11 08:55:40 -04:00
Kyle Spearrin
416ec3812d iphone x screenshots 2019-07-10 14:17:28 -04:00
Kyle Spearrin
ff24891903 update event log types 2019-07-09 10:51:33 -04:00
Kyle Spearrin
ddcbe298ac 6.5 inch screenshots for ios 2019-07-09 09:48:27 -04:00
Kyle Spearrin
6d8f647aee two new screenshots for iphone, update 1 2019-07-09 01:09:53 -04:00
Kyle Spearrin
a654987175 upadte screenshots for 5.5 inch iphone 6 2019-07-08 17:34:04 -04:00
Kyle Spearrin
a5f960d8a1 show proper name for faceid. homepage margin on ios 2019-07-08 13:37:45 -04:00
Kyle Spearrin
1f707cda68 New Crowdin translations (#556)
* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Czech)

* New translations AppResources.resx (Hebrew)

* New translations AppResources.resx (French)
2019-07-08 13:12:09 -04:00
Kyle Spearrin
4e7f195fd2 bump versions 2019-07-08 13:02:48 -04:00
Kyle Spearrin
7728e930be delay focus on entry on ios too 2019-07-08 12:06:37 -04:00
Kyle Spearrin
ab84200347 change default lock on new installs to 15 minutes 2019-07-06 23:04:10 -04:00
Kyle Spearrin
62d8824450 clear cache in main app if change made in extension 2019-07-06 22:49:17 -04:00
Kyle Spearrin
cf35d20adb refresh search on appear if has text 2019-07-06 22:19:29 -04:00
Kyle Spearrin
65725b5a38 yubikey token must be > 40 in length too 2019-07-06 22:09:20 -04:00
Kyle Spearrin
eca4777b99 yubikey fixes for ios 2019-07-06 21:59:13 -04:00
Kyle Spearrin
066b3aba5b wait 5 seconds after migration 2019-07-05 17:37:21 -04:00
Kyle Spearrin
8e485ff26f add back refs for extensions 2019-07-05 17:14:08 -04:00
Kyle Spearrin
341b66f44f settings shim with ios group id 2019-07-05 17:10:37 -04:00
Kyle Spearrin
19c62d3320 Merge branch 'master' of github.com:bitwarden/mobile 2019-07-05 16:37:12 -04:00
Kyle Spearrin
13ffbd7675 add app extension flags to migration 2019-07-05 16:36:56 -04:00
Kyle Spearrin
9af6aae699 fix baseurl check when return web vault url 2019-07-05 13:46:54 -04:00
Kyle Spearrin
2e562e8318 ios migration from old version 2019-07-05 13:35:22 -04:00
Kyle Spearrin
c6db763716 New Crowdin translations (#554)
* New translations AppResources.resx (English, United Kingdom)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Estonian)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Spanish)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Polish)

* New translations AppResources.resx (Catalan)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Spanish)

* New translations AppResources.resx (Italian)

* New translations AppResources.resx (French)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Czech)

* New translations AppResources.resx (Ukrainian)
2019-07-04 08:43:40 -04:00
Kyle Spearrin
a3383af4ae hides shadow on nav bar 2019-07-03 20:39:00 -04:00
Kyle Spearrin
6c56e44b61 prefix keychain key with appid 2019-07-03 20:04:23 -04:00
Kyle Spearrin
64506a7080 UINavigationBar appearance for no bottom line 2019-07-03 17:37:33 -04:00
Kyle Spearrin
fac9ae4b6c only init hockeyapp once 2019-07-03 16:50:12 -04:00
Kyle Spearrin
a2dc73afef New Crowdin translations (#553)
* New translations AppResources.resx (Afrikaans)

* New translations AppResources.resx (Korean)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Norwegian Bokmal)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Italian)

* New translations AppResources.resx (Indonesian)

* New translations AppResources.resx (Polish)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (Swedish)

* New translations AppResources.resx (Spanish)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Romanian)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (Croatian)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Catalan)

* New translations AppResources.resx (Bulgarian)

* New translations AppResources.resx (Czech)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (French)

* New translations AppResources.resx (Hindi)

* New translations AppResources.resx (Hebrew)

* New translations AppResources.resx (German)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (Estonian)

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

* New translations AppResources.resx (Ukrainian)

* New translations AppResources.resx (Vietnamese)

* New translations AppResources.resx (Turkish)

* New translations AppResources.resx (Thai)
2019-07-03 15:15:21 -04:00
Kyle Spearrin
59c5a34cd0 adjust bundle signing config 2019-07-03 15:03:09 -04:00
Kyle Spearrin
3321e6b0e2 set entitlements 2019-07-03 14:54:12 -04:00
Kyle Spearrin
8b7ac179fa in memory storage service 2019-07-03 12:31:18 -04:00
Kyle Spearrin
ea745665c8 remove continue button if no 2fa available 2019-07-03 12:21:06 -04:00
Kyle Spearrin
ca8f6ee10b FFImageLoading only for main app 2019-07-03 10:08:59 -04:00
Kyle Spearrin
3e51ff46f3 forget bg colors :( 2019-07-02 23:31:52 -04:00
Kyle Spearrin
fa2e814559 style headers and footer 2019-07-02 22:27:21 -04:00
Kyle Spearrin
87e337cbeb try color header bg again 2019-07-02 20:52:44 -04:00
Kyle Spearrin
abb39df547 reset service container from extensions 2019-07-02 20:45:54 -04:00
Kyle Spearrin
43e15bf911 more bg color for header 2019-07-02 20:14:23 -04:00
Kyle Spearrin
4d79d0af89 bg for header/footer is tint color 2019-07-02 19:49:50 -04:00
Kyle Spearrin
69100d7db5 search bar bg is same as list header 2019-07-02 19:36:11 -04:00
Kyle Spearrin
a064a6cf9b theme updates to extensions 2019-07-02 19:35:01 -04:00
Kyle Spearrin
7953a9a3ce autofill activated regular color 2019-07-02 17:28:57 -04:00
Kyle Spearrin
be3c6f210d remove see apps from ext page 2019-07-02 17:27:55 -04:00
Kyle Spearrin
f7cbddab4b remove navibar outlet 2019-07-02 17:17:20 -04:00
Kyle Spearrin
d423818764 add hockeyapp to extensions 2019-07-02 16:28:01 -04:00
Kyle Spearrin
519acd43aa provisioning profiles 2019-07-02 16:13:09 -04:00
Kyle Spearrin
2682a0d9e4 --nodevcodeshare on debug 2019-07-02 14:58:18 -04:00
Kyle Spearrin
8629ae048c update build props 2019-07-02 14:43:07 -04:00
Kyle Spearrin
905d01e804 adjust settings 2019-07-02 14:14:59 -04:00
Kyle Spearrin
0588bbc41d remove remaining jsonnet deps 2019-07-02 14:06:21 -04:00
Kyle Spearrin
b308b4c54f move some json dependencies out of extension project 2019-07-02 14:03:59 -04:00
Kyle Spearrin
c2c73d5460 autofill pages for ios 2019-07-02 13:15:00 -04:00
Kyle Spearrin
e01bf57874 re-set state for website icons on login/unlock
resolves #549
2019-07-02 08:05:34 -04:00
Kyle Spearrin
7ced93225b logic on autoPromptFingerprint when locked 2019-07-02 07:50:09 -04:00
Kyle Spearrin
b5e61864af adjust header for autofill page 2019-07-02 00:19:59 -04:00
Kyle Spearrin
1e5aaea8f4 restore lockOptionMs 2019-07-01 23:45:31 -04:00
Kyle Spearrin
ab3bebf06a use background tasks to keep timers alive 2019-07-01 23:44:47 -04:00
Kyle Spearrin
4a294d6a77 save some lines 2019-07-01 21:16:28 -04:00
Kyle Spearrin
e0fda1a0bc fix ui thread issue 2019-07-01 21:15:53 -04:00
Kyle Spearrin
d17da80f19 lock timer 2019-07-01 21:10:24 -04:00
Kyle Spearrin
2e7658f857 clear clipboard timer 2019-07-01 16:56:42 -04:00
Kyle Spearrin
53d0b28c7c fix add matching eq domains
resolves #550
2019-07-01 16:06:52 -04:00
Kyle Spearrin
33ba4d3871 add capitalize and include num to generator 2019-07-01 15:35:26 -04:00
Kyle Spearrin
225db6397d ios app extension theming 2019-07-01 15:12:54 -04:00
Nicholas
73b5d1b3f1 add support for org.mozilla.fenix.nightly (#551)
* Update autofillservice.xml

* Update AutofillHelpers.cs

* Update AccessibilityHelpers.cs
2019-07-01 11:35:58 -04:00
Kyle Spearrin
8da2eac6d0 add support for org.mozilla.fennec_fdroid
resolves #548
2019-06-28 23:23:51 -04:00
Kyle Spearrin
fbd62153ee theme splash for extensions 2019-06-28 12:30:48 -04:00
Kyle Spearrin
9145fa1c48 improvement to lock screen 2019-06-28 11:47:04 -04:00
Kyle Spearrin
caa0af1258 remove old action view controllers 2019-06-28 10:45:42 -04:00
Kyle Spearrin
7a230ee5f5 app extension for autofill ios 2019-06-28 08:57:08 -04:00
Kyle Spearrin
f237fa98d2 ios autofill extension implemented 2019-06-28 08:21:44 -04:00
Kyle Spearrin
be4ae605a9 implement ASHelpers from messages 2019-06-27 16:22:58 -04:00
Kyle Spearrin
9c2cbc0ecb add shared controllers and view to ios core 2019-06-27 15:48:25 -04:00
Kyle Spearrin
fb3009fc66 core utils 2019-06-27 14:07:25 -04:00
Kyle Spearrin
04c32e28cd move device action to ios core 2019-06-27 13:58:08 -04:00
Kyle Spearrin
645576c949 port over models 2019-06-27 13:45:16 -04:00
Kyle Spearrin
775bee3546 fix dependency hell 2019-06-27 13:41:32 -04:00
Kyle Spearrin
88aea96034 add autofill resources 2019-06-26 20:52:17 -04:00
Kyle Spearrin
5f474dfaf5 add some missing resources 2019-06-26 20:43:14 -04:00
Kyle Spearrin
fe7aad0835 autofill extension project 2019-06-26 20:39:45 -04:00
Kyle Spearrin
79746efa2d action extension project 2019-06-26 20:28:23 -04:00
Kyle Spearrin
a158021f46 return selection collection logic 2019-06-26 17:50:57 -04:00
Kyle Spearrin
2d91a893f7 fix cursor color to renderers 2019-06-26 10:20:42 -04:00
Kyle Spearrin
dd4561d985 style cursor color 2019-06-26 10:12:34 -04:00
Kyle Spearrin
92764eeae0 hide status bar on homepage for ios 2019-06-26 10:05:31 -04:00
Kyle Spearrin
b72808ab40 splash screen bg colors and white logo 2019-06-26 09:35:18 -04:00
Kyle Spearrin
14f3f99218 fix attachments selection on ios 2019-06-25 17:46:37 -04:00
Kyle Spearrin
d7130d9b67 no entities state adjustments 2019-06-25 17:16:47 -04:00
Kyle Spearrin
3f94eee4d5 events url 2019-06-25 16:36:21 -04:00
Kyle Spearrin
72cbdcbc8d use internal FilesDir for temp photo 2019-06-25 11:54:31 -04:00
Kyle Spearrin
e33b49e78c externalsFileDir w/ FileProvider for temp store 2019-06-25 10:30:16 -04:00
Kyle Spearrin
8e04945d4e box-row-input-options-platform on share 2019-06-24 17:38:29 -04:00
Kyle Spearrin
3ca5da55cb fix more options for sharing on view/add/edit 2019-06-24 17:34:00 -04:00
Kyle Spearrin
ea30373a09 picker SetUpdateMode for ios 2019-06-24 17:32:24 -04:00
Kyle Spearrin
4b4757d0e5 ios resumed 2019-06-24 17:02:05 -04:00
Kyle Spearrin
4bc837509d fix double key formatting 2019-06-24 16:51:54 -04:00
Kyle Spearrin
c9d1e8dc65 nord theme toast for ios 2019-06-24 16:29:02 -04:00
Kyle Spearrin
88b8a192b5 no listview selection type on non-light theme 2019-06-24 16:16:17 -04:00
Kyle Spearrin
94fbf627ba no options during selection mode 2019-06-24 15:22:46 -04:00
Kyle Spearrin
45fbdb8411 ios theming 2019-06-24 15:13:33 -04:00
Kyle Spearrin
d9c947ccd0 black theme for ios 2019-06-24 14:49:47 -04:00
Kyle Spearrin
2b670a5ae1 ios themeing 2019-06-24 14:29:23 -04:00
Kyle Spearrin
1af0178b50 set theme properly on app launch for ios 2019-06-24 12:23:00 -04:00
Kyle Spearrin
3ec5d894b3 spacing for ios on options page 2019-06-24 12:05:01 -04:00
Kyle Spearrin
d81585ccc3 search bar for ios 2019-06-24 11:53:19 -04:00
Kyle Spearrin
38f91bce1c notes separator for ios 2019-06-24 11:22:34 -04:00
Kyle Spearrin
2d41dd6ae0 switch styling on iOS 2019-06-22 09:51:04 -04:00
Kyle Spearrin
1705a21f68 slider styling 2019-06-22 09:45:54 -04:00
Kyle Spearrin
164d79898a button styling 2019-06-22 09:15:37 -04:00
Kyle Spearrin
50f809d290 undo busy when syncing complete 2019-06-21 16:53:17 -04:00
Kyle Spearrin
39284b475d bottom border on picker and no padding on editor 2019-06-21 16:36:23 -04:00
Kyle Spearrin
d44950d46c bottom border for ios entry 2019-06-21 16:09:20 -04:00
Kyle Spearrin
e9b55bc207 fix tag issue on settings page 2019-06-21 10:01:35 -04:00
Kyle Spearrin
5470f08fee list-row-header-container bg color 2019-06-21 09:59:22 -04:00
Kyle Spearrin
f9a3bbd7fa remove green background 2019-06-21 09:47:10 -04:00
Kyle Spearrin
9d3165dc65 New grid layout structure for cipher view cell 2019-06-21 09:46:46 -04:00
Kyle Spearrin
3475d39f37 use bold colored headers 2019-06-20 17:26:42 -04:00
Kyle Spearrin
44782b1ddf header upper on iOS 2019-06-20 17:05:28 -04:00
Kyle Spearrin
dd8d5fd84c icon sizes for ios 2019-06-20 16:49:27 -04:00
Kyle Spearrin
e8f2d9d0dd list section border colors 2019-06-20 16:40:13 -04:00
Kyle Spearrin
a2de3b5d80 remove binding context from header viewcell 2019-06-20 16:34:17 -04:00
Kyle Spearrin
a2960c45bc accessible font sizes 2019-06-20 16:32:22 -04:00
Kyle Spearrin
dc91624597 some listview styling for iOS 2019-06-20 16:02:39 -04:00
Kyle Spearrin
223ec180fc disable spell check & prediction on certain fields 2019-06-19 16:03:55 -04:00
Kyle Spearrin
0116572fec show nested collections in groupings pages 2019-06-17 10:21:05 -04:00
Kyle Spearrin
5350e5385c version bump 2019-06-16 06:57:25 -04:00
Kyle Spearrin
8f18c4fd45 cleartextTrafficPermitted 2019-06-16 06:54:58 -04:00
Kyle Spearrin
8538fbabe5 dont link core lib 2019-06-15 22:50:54 -04:00
Kyle Spearrin
9367b34bbe more linking 2019-06-15 21:58:43 -04:00
Kyle Spearrin
a766044cb4 bump version 2019-06-15 21:13:12 -04:00
Kyle Spearrin
0eb385e49f revert com.android.settings from blacklist 2019-06-15 21:02:38 -04:00
Kyle Spearrin
e30136dace just check if !HasCiphers 2019-06-15 20:44:36 -04:00
Kyle Spearrin
c50dee479a android http client handler 2019-06-15 18:44:08 -04:00
Kyle Spearrin
58ef292fa7 null checks 2019-06-15 00:14:32 -04:00
Kyle Spearrin
61b728fea7 x86 builds as well 2019-06-14 23:32:58 -04:00
Kyle Spearrin
b782eeb839 fixes to 2fa page 2019-06-14 18:08:08 -04:00
Kyle Spearrin
77314d4b8d cleanup search page for ios 2019-06-14 17:40:21 -04:00
Kyle Spearrin
c79d1d24b3 add and more toolbar buttons for ios 2019-06-14 17:31:06 -04:00
Kyle Spearrin
5dbea8ca09 more options on generator and history page 2019-06-14 17:21:17 -04:00
Kyle Spearrin
09a1c17fb4 null checks 2019-06-14 16:53:01 -04:00
Kyle Spearrin
a0632bcac2 null checks 2019-06-14 16:22:56 -04:00
Kyle Spearrin
07d57ebe8c bump version 2019-06-14 08:46:04 -04:00
Kyle Spearrin
325c88018c more menu for ios 2019-06-14 08:45:28 -04:00
Kyle Spearrin
dcb1102746 crash fixes 2019-06-14 08:05:28 -04:00
Kyle Spearrin
636d3c02c4 catch errors 2019-06-13 20:41:24 -04:00
Kyle Spearrin
5b119ded17 x64 builds as well 2019-06-13 16:25:13 -04:00
Kyle Spearrin
f25ae870c5 hebrew fix 2019-06-13 16:07:08 -04:00
Kyle Spearrin
49673262e4 bump version and fix readme 2019-06-13 15:05:31 -04:00
vargbeaumont
3ed814c1f7 Update styles.xml (#537)
Fix black navigation bar on none Google ROMs.
2019-06-13 15:03:00 -04:00
Kyle Spearrin
8b858e5407 new hi-res icon for store 2019-06-13 14:47:05 -04:00
Kyle Spearrin
30145894b6 bump version 2019-06-13 14:11:12 -04:00
Kyle Spearrin
8df4c27203 handle some loading race conditions 2019-06-13 14:08:21 -04:00
307 changed files with 11654 additions and 1412 deletions

View File

@@ -1,3 +1,112 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Don't use tabs for indentation.
[*]
indent_style = space
# (Please don't specify an indent_size here; that has too many unintended consequences.)
# Code files
[*.{cs,csx,vb,vbx}]
indent_size = 4
insert_final_newline = true
charset = utf-8-bom
# Xml project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
indent_size = 2
# Xml config files
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
indent_size = 2
# JSON files
[*.json]
indent_size = 2
# Dotnet code style settings:
[*.{cs,vb}]
# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true
# Avoid "this." and "Me." if not necessary
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion
# Use language keywords instead of framework type names for type references
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
# Suggest more modern language features when available
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
# Prefix private members with underscore
dotnet_naming_rule.private_members_with_underscore.symbols = private_members
dotnet_naming_rule.private_members_with_underscore.style = underscore_prefix
dotnet_naming_rule.private_members_with_underscore.severity = suggestion
dotnet_naming_symbols.private_members.applicable_kinds = field
dotnet_naming_symbols.private_members.applicable_accessibilities = private
dotnet_naming_symbols.private_members.required_modifiers = readonly
dotnet_naming_style.underscore_prefix.capitalization = camel_case
dotnet_naming_style.underscore_prefix.required_prefix = _
dotnet_naming_style.underscore_prefix.required_suffix =
dotnet_naming_style.underscore_prefix.word_separator =
# Async methods should have "Async" suffix
dotnet_naming_rule.async_methods_end_in_async.symbols = any_async_methods
dotnet_naming_rule.async_methods_end_in_async.style = end_in_async
dotnet_naming_rule.async_methods_end_in_async.severity = suggestion
dotnet_naming_symbols.any_async_methods.applicable_kinds = method
dotnet_naming_symbols.any_async_methods.applicable_accessibilities = *
dotnet_naming_symbols.any_async_methods.required_modifiers = async
dotnet_naming_style.end_in_async.required_prefix =
dotnet_naming_style.end_in_async.required_suffix = Async
dotnet_naming_style.end_in_async.capitalization = pascal_case
dotnet_naming_style.end_in_async.word_separator =
# CSharp code style settings:
[*.cs]
# Prefer "var" everywhere
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true:suggestion
# Prefer method-like constructs to have a expression-body
csharp_style_expression_bodied_methods = true:none
csharp_style_expression_bodied_constructors = true:none
csharp_style_expression_bodied_operators = true:none
# Prefer property-like constructs to have an expression-body
csharp_style_expression_bodied_properties = true:none
csharp_style_expression_bodied_indexers = true:none
csharp_style_expression_bodied_accessors = true:none
# Suggest more modern language features when available
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
# Newline settings
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
# All files
[*]
guidelines = 120
guidelines = 120

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.png" alt="" width="300" height="533" /> <img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-ios.png" alt="" width="300" height="533" />
<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" />
# Build/Run

View File

@@ -95,7 +95,9 @@ build_script:
msbuild bitwarden-mobile.sln `
"/logger:C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" `
"/p:Configuration=Release"
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
.\src\Android\ci-build-apks.ps1
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
Push-AppveyorArtifact .\com.x8bit.bitwarden.apk
Push-AppveyorArtifact .\com.x8bit.bitwarden-fdroid.apk
}

View File

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

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.539
# Visual Studio Version 16
VisualStudioVersion = 16.0.29009.5
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Android", "src\Android\Android.csproj", "{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}"
EndProject
@@ -21,6 +21,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "google", "google", "{2E3996
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{76690DFB-B7F4-4781-83E4-113FDC450AFE}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.gitignore = .gitignore
appveyor.yml = appveyor.yml
CONTRIBUTING.md = CONTRIBUTING.md
@@ -35,6 +36,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Core", "src\iOS.Core\iO
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS", "src\iOS\iOS.csproj", "{599E0201-420A-4C3E-A7BA-5349F72E0B15}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Extension", "src\iOS.Extension\iOS.Extension.csproj", "{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@@ -99,24 +104,24 @@ Global
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.Deploy.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.Deploy.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.Deploy.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.Build.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.Deploy.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.Deploy.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.Deploy.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.Deploy.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.Build.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.Deploy.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.Build.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.Deploy.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.Deploy.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
@@ -141,18 +146,18 @@ Global
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhone.Build.0 = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|Any CPU.Build.0 = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhone.Build.0 = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|iPhone.ActiveCfg = Debug|Any CPU
@@ -231,18 +236,18 @@ Global
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhone.Build.0 = Release|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|Any CPU.ActiveCfg = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|Any CPU.Build.0 = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.ActiveCfg = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.Build.0 = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.Build.0 = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|Any CPU.ActiveCfg = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|Any CPU.Build.0 = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.ActiveCfg = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.Build.0 = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.ActiveCfg = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.Build.0 = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|Any CPU.ActiveCfg = AppStore|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|Any CPU.Build.0 = AppStore|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.ActiveCfg = AppStore|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.Build.0 = AppStore|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.Build.0 = AppStore|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.ActiveCfg = Debug|Any CPU
@@ -286,6 +291,62 @@ Global
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhone.Build.0 = Release|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|Any CPU.Build.0 = AppStore|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.Build.0 = AppStore|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.ActiveCfg = Debug|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Build.0 = Debug|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.ActiveCfg = Release|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.Build.0 = Release|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhone.ActiveCfg = Release|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhone.Build.0 = Release|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|Any CPU.ActiveCfg = Release|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.ActiveCfg = Release|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.Build.0 = Release|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|Any CPU.Build.0 = AppStore|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhone.Build.0 = AppStore|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.ActiveCfg = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.Build.0 = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|Any CPU.ActiveCfg = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|Any CPU.Build.0 = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhone.ActiveCfg = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhone.Build.0 = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|Any CPU.ActiveCfg = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.ActiveCfg = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.Build.0 = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -299,6 +360,8 @@ Global
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D} = {2E399654-26A2-46F6-B9CA-1B496A3F370A}
{E71F3053-056C-4381-9638-048ED73BDFF6} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{599E0201-420A-4C3E-A7BA-5349F72E0B15} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}

View File

@@ -37,9 +37,11 @@ namespace Bit.Droid.Accessibility
new Browser("org.mozilla.firefox", "url_bar_title"),
new Browser("org.mozilla.firefox_beta", "url_bar_title"),
new Browser("org.mozilla.fennec_aurora", "url_bar_title"),
new Browser("org.mozilla.fennec_fdroid", "url_bar_title"),
new Browser("org.mozilla.focus", "display_url"),
new Browser("org.mozilla.klar", "display_url"),
new Browser("org.mozilla.fenix", "mozac_browser_toolbar_url_view"),
new Browser("org.mozilla.fenix.nightly", "mozac_browser_toolbar_url_view"),
new Browser("org.mozilla.reference.browser", "mozac_browser_toolbar_url_view"),
new Browser("com.ghostery.android.ghostery", "search_field"),
new Browser("org.adblockplus.browser", "url_bar_title"),
@@ -243,4 +245,4 @@ namespace Bit.Droid.Accessibility
return allEditTexts.TakeWhile(n => !n.Password).LastOrDefault();
}
}
}
}

View File

@@ -33,10 +33,10 @@
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
<AndroidSupportedAbis />
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
<AndroidLinkSkip>Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;Xamarin.Android.Net</AndroidLinkSkip>
<AndroidLinkSkip>Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;BitwardenCore;Xamarin.Android.Net</AndroidLinkSkip>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugSymbols>false</DebugSymbols>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
@@ -44,13 +44,15 @@
<WarningLevel>4</WarningLevel>
<AndroidManagedSymbols>true</AndroidManagedSymbols>
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
<AndroidSupportedAbis />
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
<AndroidLinkSkip>Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;Xamarin.Android.Net</AndroidLinkSkip>
<AndroidLinkSkip>Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;BitwardenCore;Xamarin.Android.Net</AndroidLinkSkip>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
<AndroidLinkMode>Full</AndroidLinkMode>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'FDroid|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<DebugSymbols>false</DebugSymbols>
<OutputPath>bin\FDroid\</OutputPath>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
@@ -59,12 +61,13 @@
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<DefineConstants>FDROID</DefineConstants>
<AndroidSupportedAbis />
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
<AndroidLinkSkip>Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;Xamarin.Android.Net</AndroidLinkSkip>
<AndroidLinkSkip>Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;BitwardenCore;Xamarin.Android.Net</AndroidLinkSkip>
<AndroidLinkMode>Full</AndroidLinkMode>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
@@ -117,6 +120,7 @@
<Compile Include="Push\FirebaseInstanceIdService.cs" />
<Compile Include="Push\FirebaseMessagingService.cs" />
<Compile Include="Receivers\ClearClipboardAlarmReceiver.cs" />
<Compile Include="Receivers\EventUploadReceiver.cs" />
<Compile Include="Receivers\LockAlarmReceiver.cs" />
<Compile Include="Receivers\PackageReplacedReceiver.cs" />
<Compile Include="Renderers\CipherViewCellRenderer.cs" />
@@ -141,6 +145,7 @@
<Compile Include="Utilities\AndroidHelpers.cs" />
<Compile Include="Utilities\CustomFingerprintDialogFragment.cs" />
<Compile Include="Utilities\HockeyAppCrashManagerListener.cs" />
<Compile Include="Utilities\StaticStore.cs" />
</ItemGroup>
<ItemGroup>
<AndroidAsset Include="Assets\FontAwesome.ttf" />

View File

@@ -53,9 +53,11 @@ namespace Bit.Droid.Autofill
"com.ecosia.android",
"com.opera.mini.native.beta",
"org.mozilla.fennec_aurora",
"org.mozilla.fennec_fdroid",
"com.qwant.liberty",
"com.opera.touch",
"org.mozilla.fenix",
"org.mozilla.fenix.nightly",
"org.mozilla.reference.browser",
"org.mozilla.rocket",
};
@@ -66,7 +68,6 @@ namespace Bit.Droid.Autofill
"androidapp://android",
"androidapp://com.x8bit.bitwarden",
"androidapp://com.oneplus.applocker",
"androidapp://com.android.settings",
};
public static async Task<List<FilledItem>> GetFillItemsAsync(Parser parser, ICipherService cipherService)

View File

@@ -5,6 +5,7 @@ using Bit.Core;
using Android.Content;
using Bit.Core.Abstractions;
using System.Threading.Tasks;
using Android.OS;
namespace Bit.Droid.Autofill
{
@@ -17,7 +18,7 @@ namespace Bit.Droid.Autofill
private readonly AssistStructure _structure;
private string _uri;
private string _packageName;
private string _webDomain;
private string _website;
public Parser(AssistStructure structure, Context applicationContext)
{
@@ -36,14 +37,14 @@ namespace Bit.Droid.Autofill
{
return _uri;
}
var webDomainNull = string.IsNullOrWhiteSpace(WebDomain);
if(webDomainNull && string.IsNullOrWhiteSpace(PackageName))
var websiteNull = string.IsNullOrWhiteSpace(Website);
if(websiteNull && string.IsNullOrWhiteSpace(PackageName))
{
_uri = null;
}
else if(!webDomainNull)
else if(!websiteNull)
{
_uri = string.Concat("http://", WebDomain);
_uri = Website;
}
else
{
@@ -66,16 +67,16 @@ namespace Bit.Droid.Autofill
}
}
public string WebDomain
public string Website
{
get => _webDomain;
get => _website;
set
{
if(string.IsNullOrWhiteSpace(value))
{
_webDomain = _uri = null;
_website = _uri = null;
}
_webDomain = value;
_website = value;
}
}
@@ -96,15 +97,24 @@ namespace Bit.Droid.Autofill
public void Parse()
{
string titlePackageId = null;
for(var i = 0; i < _structure.WindowNodeCount; i++)
{
var node = _structure.GetWindowNodeAt(i);
if(i == 0)
{
titlePackageId = GetTitlePackageId(node);
}
ParseNode(node.RootViewNode);
}
if(string.IsNullOrWhiteSpace(PackageName) && string.IsNullOrWhiteSpace(Website))
{
PackageName = titlePackageId;
}
if(!AutofillHelpers.TrustedBrowsers.Contains(PackageName) &&
!AutofillHelpers.CompatBrowsers.Contains(PackageName))
{
WebDomain = null;
Website = null;
}
}
@@ -135,10 +145,32 @@ namespace Bit.Droid.Autofill
{
PackageName = node.IdPackage;
}
if(string.IsNullOrWhiteSpace(WebDomain) && !string.IsNullOrWhiteSpace(node.WebDomain))
if(string.IsNullOrWhiteSpace(Website) && !string.IsNullOrWhiteSpace(node.WebDomain))
{
WebDomain = node.WebDomain;
var scheme = "http";
if((int)Build.VERSION.SdkInt >= 28)
{
scheme = node.WebScheme;
}
Website = string.Format("{0}://{1}", scheme, node.WebDomain);
}
}
private string GetTitlePackageId(WindowNode node)
{
if(node != null && !string.IsNullOrWhiteSpace(node.Title))
{
var slashPosition = node.Title.IndexOf('/');
if(slashPosition > -1)
{
var packageId = node.Title.Substring(0, slashPosition);
if(packageId.Contains("."))
{
return packageId;
}
}
}
return null;
}
}
}

View File

@@ -17,6 +17,7 @@ using Bit.Core.Enums;
using Android.Nfc;
using Bit.App.Utilities;
using System.Threading.Tasks;
using Android.Support.V4.Content;
namespace Bit.Droid
{
@@ -29,17 +30,16 @@ namespace Bit.Droid
[Register("com.x8bit.bitwarden.MainActivity")]
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
private const string HockeyAppId = "d3834185b4a643479047b86c65293d42";
private IDeviceActionService _deviceActionService;
private IMessagingService _messagingService;
private IBroadcasterService _broadcasterService;
private IUserService _userService;
private IAppIdService _appIdService;
private IStorageService _storageService;
private IStateService _stateService;
private IEventService _eventService;
private PendingIntent _lockAlarmPendingIntent;
private PendingIntent _clearClipboardPendingIntent;
private PendingIntent _eventUploadPendingIntent;
private AppOptions _appOptions;
private string _activityKey = $"{nameof(MainActivity)}_{Java.Lang.JavaSystem.CurrentTimeMillis().ToString()}";
private Java.Util.Regex.Pattern _otpPattern =
@@ -47,6 +47,9 @@ namespace Bit.Droid
protected override void OnCreate(Bundle savedInstanceState)
{
var eventUploadIntent = new Intent(this, typeof(EventUploadReceiver));
_eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent,
PendingIntentFlags.UpdateCurrent);
var alarmIntent = new Intent(this, typeof(LockAlarmReceiver));
_lockAlarmPendingIntent = PendingIntent.GetBroadcast(this, 0, alarmIntent,
PendingIntentFlags.UpdateCurrent);
@@ -63,12 +66,12 @@ namespace Bit.Droid
_userService = ServiceContainer.Resolve<IUserService>("userService");
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
UpdateTheme(ThemeManager.GetTheme());
UpdateTheme(ThemeManager.GetTheme(true));
base.OnCreate(savedInstanceState);
if(!CoreHelpers.InDebugMode())
{
@@ -77,8 +80,7 @@ namespace Bit.Droid
#if !FDROID
var hockeyAppListener = new HockeyAppCrashManagerListener(_appIdService, _userService);
var hockeyAppTask = hockeyAppListener.InitAsync();
HockeyApp.Android.CrashManager.Register(this, HockeyAppId, hockeyAppListener);
var hockeyAppTask = hockeyAppListener.InitAsync(this);
#endif
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
@@ -90,10 +92,10 @@ namespace Bit.Droid
{
if(message.Command == "scheduleLockTimer")
{
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
var lockOptionMinutes = (int)message.Data;
var lockOptionMs = lockOptionMinutes * 60000;
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + lockOptionMs + 10;
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
alarmManager.Set(AlarmType.RtcWakeup, triggerMs, _lockAlarmPendingIntent);
}
else if(message.Command == "cancelLockTimer")
@@ -101,6 +103,14 @@ namespace Bit.Droid
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
alarmManager.Cancel(_lockAlarmPendingIntent);
}
else if(message.Command == "startEventTimer")
{
StartEventAlarm();
}
else if(message.Command == "stopEventTimer")
{
var task = StopEventAlarmAsync();
}
else if(message.Command == "finishMainActivity")
{
Xamarin.Forms.Device.BeginInvokeOnMainThread(() => Finish());
@@ -202,9 +212,8 @@ namespace Bit.Droid
else
{
// camera
var root = new Java.IO.File(Android.OS.Environment.ExternalStorageDirectory, "bitwarden");
var file = new Java.IO.File(root, "temp_camera_photo.jpg");
uri = Android.Net.Uri.FromFile(file);
var file = new Java.IO.File(FilesDir, "temp_camera_photo.jpg");
uri = FileProvider.GetUriForFile(this, "com.x8bit.bitwarden.fileprovider", file);
fileName = $"photo_{DateTime.UtcNow.ToString("yyyyMMddHHmmss")}.jpg";
}
@@ -367,10 +376,23 @@ namespace Bit.Droid
{
return;
}
await _stateService.SaveAsync(Constants.LastClipboardValueKey, data.Item1);
StaticStore.LastClipboardValue = data.Item1;
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + clearMs.Value;
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
alarmManager.Set(AlarmType.Rtc, triggerMs, _clearClipboardPendingIntent);
}
private void StartEventAlarm()
{
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
alarmManager.SetInexactRepeating(AlarmType.ElapsedRealtime, 120000, 300000, _eventUploadPendingIntent);
}
private async Task StopEventAlarmAsync()
{
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
alarmManager.Cancel(_eventUploadPendingIntent);
await _eventService.UploadEventsAsync();
}
}
}

View File

@@ -15,6 +15,7 @@ using Bit.Droid.Services;
using Bit.Droid.Utilities;
using Plugin.CurrentActivity;
using Plugin.Fingerprint;
using Xamarin.Android.Net;
#if !FDROID
using Android.Gms.Security;
#endif
@@ -106,7 +107,7 @@ namespace Bit.Droid
var cryptoPrimitiveService = new CryptoPrimitiveService();
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
var deviceActionService = new DeviceActionService(mobileStorageService, messagingService,
broadcasterService);
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
broadcasterService);
@@ -147,10 +148,10 @@ namespace Bit.Droid
private async Task BootstrapAsync()
{
var disableFavicon = await ServiceContainer.Resolve<IStorageService>("storageService").GetAsync<bool?>(
Constants.DisableFaviconKey);
await ServiceContainer.Resolve<IStateService>("stateService").SaveAsync(Constants.DisableFaviconKey,
disableFavicon);
var disableFavicon = await ServiceContainer.Resolve<IStorageService>("storageService")
.GetAsync<bool?>(Constants.DisableFaviconKey);
await ServiceContainer.Resolve<IStateService>("stateService").SaveAsync(
Constants.DisableFaviconKey, disableFavicon);
await ServiceContainer.Resolve<IEnvironmentService>("environmentService").SetUrlsFromStorageAsync();
}
}

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.0.1"
android:versionName="2.2.0"
package="com.x8bit.bitwarden">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28" />

View File

@@ -2,22 +2,21 @@
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Bit.Droid.Utilities;
namespace Bit.Droid.Receivers
{
[BroadcastReceiver(Name = "com.x8bit.bitwarden.ClearClipboardAlarmReceiver", Exported = false)]
public class ClearClipboardAlarmReceiver : BroadcastReceiver
{
public async override void OnReceive(Context context, Intent intent)
public override void OnReceive(Context context, Intent intent)
{
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
var clipboardManager = context.GetSystemService(Context.ClipboardService) as ClipboardManager;
var lastClipboardValue = await stateService.GetAsync<string>(Constants.LastClipboardValueKey);
await stateService.RemoveAsync(Constants.LastClipboardValueKey);
if(lastClipboardValue == clipboardManager.Text)
if(StaticStore.LastClipboardValue != null && StaticStore.LastClipboardValue == clipboardManager.Text)
{
clipboardManager.Text = string.Empty;
}
StaticStore.LastClipboardValue = null;
}
}
}

View File

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

View File

@@ -23,7 +23,14 @@ namespace Bit.Droid.Renderers
var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.slider_thumb, null);
if(t is GradientDrawable thumb)
{
thumb.SetColor(view.ThumbColor.ToAndroid());
if(view.ThumbColor == Color.Default)
{
thumb.SetColor(Color.White.ToAndroid());
}
else
{
thumb.SetColor(view.ThumbColor.ToAndroid());
}
thumb.SetStroke(3, view.ThumbBorderColor.ToAndroid());
Control.SetThumb(thumb);
}

View File

@@ -59,7 +59,7 @@
<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/black</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<!-- Nord theme -->

View File

@@ -48,6 +48,9 @@
<compatibility-package
android:name="org.mozilla.fenix"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="org.mozilla.fenix.nightly"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="org.mozilla.reference.browser"
android:maxLongVersionCode="10000000000"/>

View File

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

View File

@@ -1,5 +1,5 @@
<network-security-config>
<base-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<!-- Trust pre-installed CAs -->
<certificates src="system" />

View File

@@ -36,6 +36,7 @@ namespace Bit.Droid.Services
private readonly IStorageService _storageService;
private readonly IMessagingService _messagingService;
private readonly IBroadcasterService _broadcasterService;
private readonly Func<IEventService> _eventServiceFunc;
private ProgressDialog _progressDialog;
private bool _cameraPermissionsDenied;
private Toast _toast;
@@ -43,11 +44,13 @@ namespace Bit.Droid.Services
public DeviceActionService(
IStorageService storageService,
IMessagingService messagingService,
IBroadcasterService broadcasterService)
IBroadcasterService broadcasterService,
Func<IEventService> eventServiceFunc)
{
_storageService = storageService;
_messagingService = messagingService;
_broadcasterService = broadcasterService;
_eventServiceFunc = eventServiceFunc;
_broadcasterService.Subscribe(nameof(DeviceActionService), (message) =>
{
@@ -201,14 +204,14 @@ namespace Bit.Droid.Services
{
try
{
var root = new Java.IO.File(Android.OS.Environment.ExternalStorageDirectory, "bitwarden");
var file = new Java.IO.File(root, "temp_camera_photo.jpg");
var file = new Java.IO.File(activity.FilesDir, "temp_camera_photo.jpg");
if(!file.Exists())
{
file.ParentFile.Mkdirs();
file.CreateNewFile();
}
var outputFileUri = Android.Net.Uri.FromFile(file);
var outputFileUri = FileProvider.GetUriForFile(activity,
"com.x8bit.bitwarden.fileprovider", file);
additionalIntents.AddRange(GetCameraIntents(outputFileUri));
}
catch(Java.IO.IOException) { }
@@ -463,6 +466,7 @@ namespace Bit.Droid.Services
replyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, dataset);
activity.SetResult(Result.Ok, replyIntent);
activity.Finish();
var eventTask = _eventServiceFunc().CollectAsync(EventType.Cipher_ClientAutofilled, cipher.Id);
}
else
{
@@ -488,6 +492,10 @@ namespace Bit.Droid.Services
}
activity.Finish();
_messagingService.Send("finishMainActivity");
if(cipher != null)
{
var eventTask = _eventServiceFunc().CollectAsync(EventType.Cipher_ClientAutofilled, cipher.Id);
}
}
}

View File

@@ -54,6 +54,11 @@ namespace Bit.Droid.Services
netLanguage = "zh-Hans";
}
}
else if(androidLanguage.StartsWith("iw"))
{
// Uncomment when we support RTL
// netLanguage = "he";
}
else
{
// Certain languages need to be converted to CultureInfo equivalent

View File

@@ -5,11 +5,14 @@ using Newtonsoft.Json;
using Android.Runtime;
using Bit.Core.Abstractions;
using System.Threading.Tasks;
using Android.Content;
namespace Bit.Droid.Utilities
{
public class HockeyAppCrashManagerListener : CrashManagerListener
{
private const string HockeyAppId = "d3834185b4a643479047b86c65293d42";
private readonly IAppIdService _appIdService;
private readonly IUserService _userService;
@@ -31,10 +34,11 @@ namespace Bit.Droid.Utilities
_userService = userService;
}
public async Task InitAsync()
public async Task InitAsync(Context context)
{
_userId = await _userService.GetUserIdAsync();
_appId = await _appIdService.GetAppIdAsync();
CrashManager.Register(context, HockeyAppId, this);
}
public override string Description

View File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
namespace Bit.Droid.Utilities
{
public static class StaticStore
{
public static string LastClipboardValue { get; set; }
}
}

View File

@@ -51,6 +51,12 @@
<Compile Update="Pages\Generator\GeneratorHistoryPage.xaml.cs">
<DependentUpon>GeneratorHistoryPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Settings\AutofillPage.xaml.cs">
<DependentUpon>AutofillPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Settings\ExtensionPage.xaml.cs">
<DependentUpon>ExtensionPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Settings\AutofillServicePage.xaml.cs">
<DependentUpon>AutofillServicePage.xaml</DependentUpon>
</Compile>

View File

@@ -89,9 +89,7 @@ namespace Bit.App
}
else if(message.Command == "locked")
{
await _stateService.PurgeAsync();
var lockPage = new LockPage(_appOptions, !(message.Data as bool?).GetValueOrDefault());
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
await LockedAsync(!(message.Data as bool?).GetValueOrDefault());
}
else if(message.Command == "lockVault")
{
@@ -114,7 +112,14 @@ namespace Bit.App
{
if(Device.RuntimePlatform == Device.iOS)
{
SyncIfNeeded();
ResumedAsync();
}
}
else if(message.Command == "slept")
{
if(Device.RuntimePlatform == Device.iOS)
{
await SleptAsync();
}
}
else if(message.Command == "migrated")
@@ -153,6 +158,7 @@ namespace Bit.App
{
System.Diagnostics.Debug.WriteLine("XF App: OnStart");
await ClearCacheIfNeededAsync();
await TryClearCiphersCacheAsync();
Prime();
if(string.IsNullOrWhiteSpace(_appOptions.Uri))
{
@@ -163,6 +169,7 @@ namespace Bit.App
SyncIfNeeded();
}
}
_messagingService.Send("startEventTimer");
}
protected async override void OnSleep()
@@ -170,22 +177,39 @@ namespace Bit.App
System.Diagnostics.Debug.WriteLine("XF App: OnSleep");
if(Device.RuntimePlatform == Device.Android)
{
await _storageService.SaveAsync(Constants.LastActiveKey, DateTime.UtcNow);
var isLocked = await _lockService.IsLockedAsync();
if(!isLocked)
{
await _storageService.SaveAsync(Constants.LastActiveKey, DateTime.UtcNow);
}
SetTabsPageFromAutofill(isLocked);
await SleptAsync();
}
SetTabsPageFromAutofill();
await HandleLockingAsync();
}
protected async override void OnResume()
protected override void OnResume()
{
System.Diagnostics.Debug.WriteLine("XF App: OnResume");
_messagingService.Send("cancelLockTimer");
await ClearCacheIfNeededAsync();
Prime();
if(Device.RuntimePlatform == Device.Android)
{
SyncIfNeeded();
ResumedAsync();
}
}
private async Task SleptAsync()
{
await HandleLockingAsync();
_messagingService.Send("stopEventTimer");
}
private async void ResumedAsync()
{
_messagingService.Send("cancelLockTimer");
_messagingService.Send("startEventTimer");
await ClearCacheIfNeededAsync();
await TryClearCiphersCacheAsync();
Prime();
SyncIfNeeded();
if(Current.MainPage is NavigationPage navPage && navPage.CurrentPage is LockPage lockPage)
{
await lockPage.PromptFingerprintAfterResumeAsync();
@@ -213,7 +237,8 @@ namespace Bit.App
_folderService.ClearAsync(userId),
_collectionService.ClearAsync(userId),
_passwordGenerationService.ClearAsync(),
_lockService.ClearAsync());
_lockService.ClearAsync(),
_stateService.PurgeAsync());
_lockService.PinLocked = false;
_lockService.FingerprintLocked = true;
_searchService.ClearIndex();
@@ -287,7 +312,7 @@ namespace Bit.App
}
}
private void SetTabsPageFromAutofill()
private void SetTabsPageFromAutofill(bool isLocked)
{
if(Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(_appOptions.Uri) &&
!_appOptions.FromAutofillFramework)
@@ -296,8 +321,15 @@ namespace Bit.App
{
Device.BeginInvokeOnMainThread(() =>
{
Current.MainPage = new TabsPage();
_appOptions.Uri = null;
if(isLocked)
{
Current.MainPage = new NavigationPage(new LockPage());
}
else
{
Current.MainPage = new TabsPage();
}
});
});
}
@@ -316,7 +348,7 @@ namespace Bit.App
{
InitializeComponent();
SetCulture();
ThemeManager.SetTheme();
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android);
Current.MainPage = new HomePage();
var mainPageTask = SetMainPageAsync();
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
@@ -341,5 +373,59 @@ namespace Bit.App
}
});
}
private async Task TryClearCiphersCacheAsync()
{
if(Device.RuntimePlatform != Device.iOS)
{
return;
}
var clearCache = await _storageService.GetAsync<bool?>(Constants.ClearCiphersCacheKey);
if(clearCache.GetValueOrDefault())
{
_cipherService.ClearCache();
await _storageService.RemoveAsync(Constants.ClearCiphersCacheKey);
}
}
private async Task LockedAsync(bool autoPromptFingerprint)
{
await _stateService.PurgeAsync();
if(autoPromptFingerprint && Device.RuntimePlatform == Device.iOS)
{
var lockOptions = await _storageService.GetAsync<int?>(Constants.LockOptionKey);
if(lockOptions == 0)
{
autoPromptFingerprint = false;
}
}
PreviousPageInfo lastPageBeforeLock = null;
if(Current.MainPage is TabbedPage tabbedPage && tabbedPage.Navigation.ModalStack.Count > 0)
{
var topPage = tabbedPage.Navigation.ModalStack[tabbedPage.Navigation.ModalStack.Count - 1];
if(topPage is NavigationPage navPage)
{
if(navPage.CurrentPage is ViewPage viewPage)
{
lastPageBeforeLock = new PreviousPageInfo
{
Page = "view",
CipherId = viewPage.ViewModel.CipherId
};
}
else if(navPage.CurrentPage is AddEditPage addEditPage && addEditPage.ViewModel.EditMode)
{
lastPageBeforeLock = new PreviousPageInfo
{
Page = "edit",
CipherId = addEditPage.ViewModel.CipherId
};
}
}
}
await _storageService.SaveAsync(Constants.PreviousPageKey, lastPageBeforeLock);
var lockPage = new LockPage(_appOptions, autoPromptFingerprint);
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
}
}
}

View File

@@ -3,84 +3,112 @@
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">
<Grid x:Name="_grid"
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.BindingContext>
<controls:CipherViewCellViewModel />
</Grid.BindingContext>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<controls:FaLabel x:Name="_icon"
Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="2"
HorizontalOptions="Center"
VerticalOptions="Center"
StyleClass="list-icon, list-icon-platform" />
<ff:CachedImage x:Name="_image"
Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="2"
BitmapOptimizations="True"
ErrorPlaceholder="login.png"
HorizontalOptions="Center"
VerticalOptions="Center"
WidthRequest="22"
HeightRequest="22"
IsVisible="False"/>
<Label LineBreakMode="TailTruncation"
Grid.Column="1"
<controls:FaLabel
x:Name="_icon"
Grid.Row="0"
Grid.Column="0"
HorizontalOptions="Center"
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="1"
<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="2"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf1e0;"
IsVisible="{Binding Cipher.Shared, Mode=OneWay}" />
<controls:FaLabel
Grid.Column="3"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf0c6;"
IsVisible="{Binding Cipher.HasAttachments, 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
Text="&#xe5d4;"
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
Clicked="ImageButton_Clicked"
Grid.Column="4"
Grid.Row="0"
Grid.RowSpan="2" />
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}" />
</Grid>
</ViewCell>

View File

@@ -181,7 +181,7 @@ namespace Bit.App.Controls
return new Tuple<string, string>(icon, image);
}
private void ImageButton_Clicked(object sender, EventArgs e)
private void MoreButton_Clicked(object sender, EventArgs e)
{
ButtonCommand?.Execute(Cipher);
}

View File

@@ -107,6 +107,10 @@ namespace Bit.App.Migration
// await storageService.SaveAsync(Constants.PushLastRegistrationDateKey, lastReg);
await storageService.SaveAsync("rememberedEmail",
settingsShim.GetValueOrDefault("other:lastLoginEmail", null));
await storageService.SaveAsync("appExtensionStarted",
settingsShim.GetValueOrDefault("extension:started", false));
await storageService.SaveAsync("appExtensionActivated",
settingsShim.GetValueOrDefault("extension:activated", false));
await environmentService.SetUrlsAsync(new Core.Models.Data.EnvironmentUrlData
{

View File

@@ -4,64 +4,119 @@ namespace Bit.App.Migration
{
public class SettingsShim
{
private readonly string _sharedName;
public SettingsShim(string sharedName = null)
{
_sharedName = sharedName;
}
public bool Contains(string key)
{
return Xamarin.Essentials.Preferences.ContainsKey(key);
return _sharedName != null ? Xamarin.Essentials.Preferences.ContainsKey(key, _sharedName) :
Xamarin.Essentials.Preferences.ContainsKey(key);
}
public string GetValueOrDefault(string key, string defaultValue)
{
return Xamarin.Essentials.Preferences.Get(key, defaultValue);
return _sharedName != null ? Xamarin.Essentials.Preferences.Get(key, defaultValue, _sharedName) :
Xamarin.Essentials.Preferences.Get(key, defaultValue);
}
public DateTime GetValueOrDefault(string key, DateTime defaultValue)
{
return Xamarin.Essentials.Preferences.Get(key, defaultValue);
return _sharedName != null ? Xamarin.Essentials.Preferences.Get(key, defaultValue, _sharedName) :
Xamarin.Essentials.Preferences.Get(key, defaultValue);
}
public bool GetValueOrDefault(string key, bool defaultValue)
{
return Xamarin.Essentials.Preferences.Get(key, defaultValue);
return _sharedName != null ? Xamarin.Essentials.Preferences.Get(key, defaultValue, _sharedName) :
Xamarin.Essentials.Preferences.Get(key, defaultValue);
}
public int GetValueOrDefault(string key, int defaultValue)
{
return Xamarin.Essentials.Preferences.Get(key, defaultValue);
return _sharedName != null ? Xamarin.Essentials.Preferences.Get(key, defaultValue, _sharedName) :
Xamarin.Essentials.Preferences.Get(key, defaultValue);
}
public long GetValueOrDefault(string key, long defaultValue)
{
return Xamarin.Essentials.Preferences.Get(key, defaultValue);
return _sharedName != null ? Xamarin.Essentials.Preferences.Get(key, defaultValue, _sharedName) :
Xamarin.Essentials.Preferences.Get(key, defaultValue);
}
public void AddOrUpdateValue(string key, string value)
{
Xamarin.Essentials.Preferences.Set(key, value);
if(_sharedName != null)
{
Xamarin.Essentials.Preferences.Set(key, value, _sharedName);
}
else
{
Xamarin.Essentials.Preferences.Set(key, value);
}
}
public void AddOrUpdateValue(string key, DateTime value)
{
Xamarin.Essentials.Preferences.Set(key, value);
if(_sharedName != null)
{
Xamarin.Essentials.Preferences.Set(key, value, _sharedName);
}
else
{
Xamarin.Essentials.Preferences.Set(key, value);
}
}
public void AddOrUpdateValue(string key, bool value)
{
Xamarin.Essentials.Preferences.Set(key, value);
if(_sharedName != null)
{
Xamarin.Essentials.Preferences.Set(key, value, _sharedName);
}
else
{
Xamarin.Essentials.Preferences.Set(key, value);
}
}
public void AddOrUpdateValue(string key, long value)
{
Xamarin.Essentials.Preferences.Set(key, value);
if(_sharedName != null)
{
Xamarin.Essentials.Preferences.Set(key, value, _sharedName);
}
else
{
Xamarin.Essentials.Preferences.Set(key, value);
}
}
public void AddOrUpdateValue(string key, int value)
{
Xamarin.Essentials.Preferences.Set(key, value);
if(_sharedName != null)
{
Xamarin.Essentials.Preferences.Set(key, value, _sharedName);
}
else
{
Xamarin.Essentials.Preferences.Set(key, value);
}
}
public void Remove(string key)
{
Xamarin.Essentials.Preferences.Remove(key);
if(_sharedName != null)
{
Xamarin.Essentials.Preferences.Remove(key, _sharedName);
}
else
{
Xamarin.Essentials.Preferences.Remove(key);
}
}
}
}

View File

@@ -0,0 +1,9 @@
namespace Bit.App.Models
{
public class PreviousPageInfo
{
public string Page { get; set; }
public string CipherId { get; set; }
public string SearchText { get; set; }
}
}

View File

@@ -21,7 +21,7 @@
<StackLayout Spacing="20">
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n SelfHostedEnvironment}"
<Label Text="{u:I18n SelfHostedEnvironment, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<StackLayout StyleClass="box-row">
@@ -42,7 +42,7 @@
</StackLayout>
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n CustomEnvironment}"
<Label Text="{u:I18n CustomEnvironment, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<StackLayout StyleClass="box-row">

View File

@@ -1,14 +1,19 @@
using System;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public partial class EnvironmentPage : BaseContentPage
{
private EnvironmentPageViewModel _vm;
private readonly IMessagingService _messagingService;
private readonly EnvironmentPageViewModel _vm;
public EnvironmentPage()
{
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_messagingService.Send("showStatusBar", true);
InitializeComponent();
_vm = BindingContext as EnvironmentPageViewModel;
_vm.Page = this;
@@ -33,10 +38,11 @@ namespace Bit.App.Pages
}
}
private async void Close_Clicked(object sender, System.EventArgs e)
private async void Close_Clicked(object sender, EventArgs e)
{
if(DoOnce())
{
_messagingService.Send("showStatusBar", false);
await Navigation.PopModalAsync();
}
}

View File

@@ -16,7 +16,16 @@
<controls:FaButton Text="&#xf013;"
StyleClass="btn-muted, btn-icon, btn-icon-platform"
HorizontalOptions="Start"
Clicked="Settings_Clicked" />
Clicked="Settings_Clicked"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}">
<controls:FaButton.Margin>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 10, 0, 0" />
<On Platform="Android" Value="0" />
</OnPlatform>
</controls:FaButton.Margin>
</controls:FaButton>
<StackLayout VerticalOptions="CenterAndExpand" Spacing="20">
<Image
x:Name="_logo"

View File

@@ -1,4 +1,6 @@
using Bit.App.Utilities;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
@@ -7,10 +9,14 @@ namespace Bit.App.Pages
{
public partial class HomePage : BaseContentPage
{
private IMessagingService _messagingService;
public HomePage()
{
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_messagingService.Send("showStatusBar", false);
InitializeComponent();
var theme = ThemeManager.GetTheme();
var theme = ThemeManager.GetTheme(Device.RuntimePlatform == Device.Android);
var darkbasedTheme = theme == "dark" || theme == "black" || theme == "nord";
_logo.Source = darkbasedTheme ? "logo_white.png" : "logo.png";
}
@@ -21,6 +27,12 @@ namespace Bit.App.Pages
await Navigation.PushModalAsync(new NavigationPage(new LoginPage(email)));
}
protected override void OnAppearing()
{
base.OnAppearing();
_messagingService.Send("showStatusBar", false);
}
private void LogIn_Clicked(object sender, EventArgs e)
{
if(DoOnce())

View File

@@ -45,6 +45,8 @@
Text="{Binding Pin}"
StyleClass="box-value"
Keyboard="Numeric"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
@@ -56,7 +58,9 @@
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
<Grid StyleClass="box-row" IsVisible="{Binding PinLock, Converter={StaticResource inverseBool}}">
<Grid.RowDefinitions>
@@ -76,6 +80,8 @@
x:Name="_masterPassword"
Text="{Binding MasterPassword}"
StyleClass="box-value"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
@@ -87,7 +93,9 @@
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
<Label
Text="{Binding LockedVerifyText}"

View File

@@ -1,4 +1,7 @@
using Bit.App.Models;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
@@ -7,6 +10,7 @@ namespace Bit.App.Pages
{
public partial class LockPage : BaseContentPage
{
private readonly IStorageService _storageService;
private readonly AppOptions _appOptions;
private readonly bool _autoPromptFingerprint;
private readonly LockPageViewModel _vm;
@@ -16,28 +20,13 @@ namespace Bit.App.Pages
public LockPage(AppOptions appOptions = null, bool autoPromptFingerprint = true)
{
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_appOptions = appOptions;
_autoPromptFingerprint = autoPromptFingerprint;
InitializeComponent();
_vm = BindingContext as LockPageViewModel;
_vm.Page = this;
_vm.UnlockedAction = () =>
{
if(_appOptions != null)
{
if(_appOptions.FromAutofillFramework && _appOptions.SaveType.HasValue)
{
Application.Current.MainPage = new NavigationPage(new AddEditPage(appOptions: _appOptions));
return;
}
else if(_appOptions.Uri != null)
{
Application.Current.MainPage = new NavigationPage(new AutofillCiphersPage(_appOptions));
return;
}
}
Application.Current.MainPage = new TabsPage(_appOptions);
};
_vm.UnlockedAction = () => Device.BeginInvokeOnMainThread(async () => await UnlockedAsync());
MasterPasswordEntry = _masterPassword;
PinEntry = _pin;
}
@@ -87,10 +76,7 @@ namespace Bit.App.Pages
var tasks = Task.Run(async () =>
{
await Task.Delay(50);
Device.BeginInvokeOnMainThread(async () =>
{
await _vm.SubmitAsync();
});
Device.BeginInvokeOnMainThread(async () => await _vm.SubmitAsync());
});
}
}
@@ -110,5 +96,28 @@ namespace Bit.App.Pages
await _vm.PromptFingerprintAsync();
}
}
private async Task UnlockedAsync()
{
if(_appOptions != null)
{
if(_appOptions.FromAutofillFramework && _appOptions.SaveType.HasValue)
{
Application.Current.MainPage = new NavigationPage(new AddEditPage(appOptions: _appOptions));
return;
}
else if(_appOptions.Uri != null)
{
Application.Current.MainPage = new NavigationPage(new AutofillCiphersPage(_appOptions));
return;
}
}
var previousPage = await _storageService.GetAsync<PreviousPageInfo>(Constants.PreviousPageKey);
if(previousPage != null)
{
await _storageService.RemoveAsync(Constants.PreviousPageKey);
}
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
}
}
}

View File

@@ -23,6 +23,7 @@ namespace Bit.App.Pages
private readonly IMessagingService _messagingService;
private readonly IStorageService _secureStorageService;
private readonly IEnvironmentService _environmentService;
private readonly IStateService _stateService;
private bool _hasKey;
private string _email;
@@ -46,6 +47,7 @@ namespace Bit.App.Pages
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
PageTitle = AppResources.VerifyMasterPassword;
TogglePasswordCommand = new Command(TogglePassword);
@@ -174,7 +176,7 @@ namespace Bit.App.Pages
if(!failed)
{
Pin = string.Empty;
DoContinue();
await DoContinueAsync();
}
}
else
@@ -270,7 +272,7 @@ namespace Bit.App.Pages
_lockService.FingerprintLocked = !success;
if(success)
{
DoContinue();
await DoContinueAsync();
}
}
@@ -280,11 +282,13 @@ namespace Bit.App.Pages
{
await _cryptoService.SetKeyAsync(key);
}
DoContinue();
await DoContinueAsync();
}
private void DoContinue()
private async Task DoContinueAsync()
{
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
_messagingService.Send("unlocked");
UnlockedAction?.Invoke();
}

View File

@@ -55,6 +55,8 @@
x:Name="_masterPassword"
Text="{Binding MasterPassword}"
StyleClass="box-value"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
@@ -66,7 +68,9 @@
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
</StackLayout>
<StackLayout Padding="10, 0">

View File

@@ -1,14 +1,19 @@
using System;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public partial class LoginPage : BaseContentPage
{
private LoginPageViewModel _vm;
private readonly IMessagingService _messagingService;
private readonly LoginPageViewModel _vm;
public LoginPage(string email = null)
{
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_messagingService.Send("showStatusBar", true);
InitializeComponent();
_vm = BindingContext as LoginPageViewModel;
_vm.Page = this;
@@ -55,10 +60,11 @@ namespace Bit.App.Pages
}
}
private async void Close_Clicked(object sender, System.EventArgs e)
private async void Close_Clicked(object sender, EventArgs e)
{
if(DoOnce())
{
_messagingService.Send("showStatusBar", false);
await Navigation.PopModalAsync();
}
}

View File

@@ -1,5 +1,6 @@
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Exceptions;
using Bit.Core.Utilities;
@@ -18,6 +19,7 @@ namespace Bit.App.Pages
private readonly ISyncService _syncService;
private readonly IStorageService _storageService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IStateService _stateService;
private bool _showPassword;
private string _email;
@@ -30,6 +32,7 @@ namespace Bit.App.Pages
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
PageTitle = AppResources.Bitwarden;
TogglePasswordCommand = new Command(TogglePassword);
@@ -123,6 +126,8 @@ namespace Bit.App.Pages
}
else
{
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
Application.Current.MainPage = new TabsPage();
}

View File

@@ -55,6 +55,8 @@
x:Name="_masterPassword"
Text="{Binding MasterPassword}"
StyleClass="box-value"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0" />
@@ -64,7 +66,9 @@
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
<Label
Text="{u:I18n MasterPasswordDescription}"
@@ -89,6 +93,8 @@
x:Name="_confirmMasterPassword"
Text="{Binding ConfirmMasterPassword}"
StyleClass="box-value"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0" />
@@ -98,7 +104,9 @@
Command="{Binding ToggleConfirmPasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
<StackLayout StyleClass="box-row">
<Label

View File

@@ -1,14 +1,19 @@
using System;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public partial class RegisterPage : BaseContentPage
{
private RegisterPageViewModel _vm;
private readonly IMessagingService _messagingService;
private readonly RegisterPageViewModel _vm;
public RegisterPage(HomePage homePage)
{
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_messagingService.Send("showStatusBar", true);
InitializeComponent();
_vm = BindingContext as RegisterPageViewModel;
_vm.Page = this;
@@ -51,10 +56,11 @@ namespace Bit.App.Pages
}
}
private async void Close_Clicked(object sender, System.EventArgs e)
private async void Close_Clicked(object sender, EventArgs e)
{
if(DoOnce())
{
_messagingService.Send("showStatusBar", false);
await Navigation.PopModalAsync();
}
}

View File

@@ -13,19 +13,20 @@
<pages:TwoFactorPageViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
x:Name="_cancelItem" />
<ToolbarItem Text="{u:I18n Continue}" Clicked="Continue_Clicked" Order="Primary"
x:Name="_continueItem" />
</ContentPage.ToolbarItems>
<ContentPage.Resources>
<ResourceDictionary>
<u:InverseBoolConverter x:Key="inverseBool" />
<u:IsNullConverter x:Key="isNull" />
<ToolbarItem Text="{u:I18n Continue}" Clicked="Continue_Clicked"
x:Name="_continueItem" x:Key="continueItem" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
</ContentPage.ToolbarItems>
<ScrollView x:Name="_scrollView">
<StackLayout Spacing="10" Padding="0, 0, 0, 10" VerticalOptions="FillAndExpand">
<StackLayout Spacing="20" Padding="0" IsVisible="{Binding TotpMethod, Mode=OneWay}">
@@ -75,6 +76,9 @@
x:Name="_yubikeyTokenEntry"
Text="{Binding Token}"
StyleClass="box-value"
IsPassword="True"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
ReturnType="Go"
ReturnCommand="{Binding SubmitCommand}" />
</StackLayout>
@@ -123,6 +127,10 @@
IsVisible="{Binding EmailMethod}"
Clicked="ResendEmail_Clicked"
Margin="10, 0"></Button>
<Button Text="{u:I18n TryAgain}"
IsVisible="{Binding ShowTryAgain}"
Clicked="TryAgain_Clicked"
Margin="10, 0"></Button>
<Button Text="{u:I18n UseAnotherTwoStepMethod}"
Clicked="Methods_Clicked"
Margin="10, 0"></Button>

View File

@@ -26,7 +26,7 @@ namespace Bit.App.Pages
DuoWebView = _duoWebView;
if(Device.RuntimePlatform == Device.Android)
{
ToolbarItems.RemoveAt(0);
ToolbarItems.Remove(_cancelItem);
}
}
@@ -34,7 +34,7 @@ namespace Bit.App.Pages
public void AddContinueButton()
{
if(ToolbarItems.Count == 0)
if(!ToolbarItems.Contains(_continueItem))
{
ToolbarItems.Add(_continueItem);
}
@@ -42,7 +42,7 @@ namespace Bit.App.Pages
public void RemoveContinueButton()
{
if(ToolbarItems.Count > 0)
if(ToolbarItems.Contains(_continueItem))
{
ToolbarItems.Remove(_continueItem);
}
@@ -55,11 +55,13 @@ namespace Bit.App.Pages
{
if(message.Command == "gotYubiKeyOTP")
{
if(_vm.YubikeyMethod)
var token = (string)message.Data;
if(_vm.YubikeyMethod && !string.IsNullOrWhiteSpace(token) &&
token.Length == 44 && !token.Contains(" "))
{
Device.BeginInvokeOnMainThread(async () =>
{
_vm.Token = (string)message.Data;
_vm.Token = token;
await _vm.SubmitAsync();
});
}
@@ -138,5 +140,16 @@ namespace Bit.App.Pages
await Navigation.PopModalAsync();
}
}
private void TryAgain_Clicked(object sender, EventArgs e)
{
if(DoOnce())
{
if(_vm.YubikeyMethod)
{
_messagingService.Send("listenYubiKeyOTP", true);
}
}
}
}
}

View File

@@ -1,5 +1,6 @@
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
@@ -23,6 +24,7 @@ namespace Bit.App.Pages
private readonly IEnvironmentService _environmentService;
private readonly IMessagingService _messagingService;
private readonly IBroadcasterService _broadcasterService;
private readonly IStateService _stateService;
private bool _u2fSupported = false;
private TwoFactorProviderType? _selectedProviderType;
@@ -40,6 +42,7 @@ namespace Bit.App.Pages
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
PageTitle = AppResources.TwoStepLogin;
SubmitCommand = new Command(async () => await SubmitAsync());
@@ -66,6 +69,8 @@ namespace Bit.App.Pages
public bool TotpMethod => AuthenticatorMethod || EmailMethod;
public bool ShowTryAgain => YubikeyMethod && Device.RuntimePlatform == Device.iOS;
public string YubikeyInstruction => Device.RuntimePlatform == Device.iOS ? AppResources.YubiKeyInstructionIos :
AppResources.YubiKeyInstruction;
@@ -79,6 +84,7 @@ namespace Bit.App.Pages
nameof(YubikeyMethod),
nameof(AuthenticatorMethod),
nameof(TotpMethod),
nameof(ShowTryAgain),
});
}
public Command SubmitCommand { get; }
@@ -157,7 +163,7 @@ namespace Bit.App.Pages
{
_messagingService.Send("listenYubiKeyOTP", false);
}
if(DuoMethod)
if(SelectedProviderType == null || DuoMethod)
{
page.RemoveContinueButton();
}
@@ -200,6 +206,8 @@ namespace Bit.App.Pages
var task = Task.Run(() => _syncService.FullSyncAsync(true));
_messagingService.Send("listenYubiKeyOTP", false);
_broadcasterService.Unsubscribe(nameof(TwoFactorPage));
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
Application.Current.MainPage = new TabsPage();
}
catch(ApiException e)

View File

@@ -11,8 +11,8 @@ namespace Bit.App.Pages
{
private IStorageService _storageService;
protected int AndroidShowModalAnimationDelay = 400;
protected int AndroidShowPageAnimationDelay = 100;
protected int ShowModalAnimationDelay = 400;
protected int ShowPageAnimationDelay = 100;
public DateTime? LastPageAction { get; set; }
@@ -77,21 +77,16 @@ namespace Bit.App.Pages
}
await Task.Run(async () =>
{
await Task.Delay(fromModal ? AndroidShowModalAnimationDelay : AndroidShowPageAnimationDelay);
await Task.Delay(fromModal ? ShowModalAnimationDelay : ShowPageAnimationDelay);
Device.BeginInvokeOnMainThread(async () => await DoWorkAsync());
});
}
protected void RequestFocus(InputView input)
{
if(Device.RuntimePlatform == Device.iOS)
{
input.Focus();
return;
}
Task.Run(async () =>
{
await Task.Delay(AndroidShowModalAnimationDelay);
await Task.Delay(ShowModalAnimationDelay);
Device.BeginInvokeOnMainThread(() => input.Focus());
});
}

View File

@@ -19,17 +19,22 @@
<ResourceDictionary>
<u:InverseBoolConverter x:Key="inverseBool" />
<u:DateTimeConverter x:Key="dateTime" />
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
x:Name="_closeItem" x:Key="closeItem" />
<ToolbarItem Text="{u:I18n Clear}"
Clicked="Clear_Clicked"
Order="Secondary"
x:Name="_clearItem"
x:Key="clearItem" />
<ToolbarItem Icon="more_vert.png"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}"
Clicked="More_Clicked"
x:Name="_moreItem"
x:Key="moreItem" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
<ToolbarItem Text="{u:I18n Clear}"
Clicked="Clear_Clicked"
Order="Secondary"
x:Name="_clearItem" />
</ContentPage.ToolbarItems>
<StackLayout x:Name="_mainLayout">
<Label IsVisible="{Binding ShowNoData}"
Text="{u:I18n NoPasswordsToList}"
@@ -79,7 +84,9 @@
CommandParameter="{Binding .}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyPassword}" />
</Grid>
</ViewCell>
</DataTemplate>

View File

@@ -1,4 +1,6 @@
using System;
using Bit.App.Resources;
using System;
using Xamarin.Forms;
namespace Bit.App.Pages
{
@@ -12,6 +14,15 @@ namespace Bit.App.Pages
SetActivityIndicator();
_vm = BindingContext as GeneratorHistoryPageViewModel;
_vm.Page = this;
if(Device.RuntimePlatform == Device.iOS)
{
ToolbarItems.Add(_closeItem);
ToolbarItems.Add(_moreItem);
}
else
{
ToolbarItems.Add(_clearItem);
}
}
protected override async void OnAppearing()
@@ -34,5 +45,19 @@ namespace Bit.App.Pages
await Navigation.PopModalAsync();
}
}
private async void More_Clicked(object sender, EventArgs e)
{
if(!DoOnce())
{
return;
}
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel,
null, AppResources.Clear);
if(selection == AppResources.Clear)
{
await _vm.ClearAsync();
}
}
}
}

View File

@@ -12,19 +12,27 @@
<pages:GeneratorPageViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Select}"
Clicked="Select_Clicked"
Order="Primary"
x:Name="_selectItem" />
<ToolbarItem Text="{u:I18n PasswordHistory}"
Clicked="History_Clicked"
Order="Secondary" />
</ContentPage.ToolbarItems>
<ContentPage.Resources>
<ResourceDictionary>
<u:InverseBoolConverter x:Key="inverseBool" />
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
x:Name="_closeItem" x:Key="closeItem" />
<ToolbarItem Text="{u:I18n Select}"
Clicked="Select_Clicked"
Order="Primary"
x:Name="_selectItem"
x:Key="selectItem" />
<ToolbarItem Text="{u:I18n PasswordHistory}"
Clicked="History_Clicked"
Order="Secondary"
x:Name="_historyItem"
x:Key="historyItem" />
<ToolbarItem Icon="more_vert.png"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}"
Clicked="More_Clicked"
x:Name="_moreItem"
x:Key="moreItem" />
</ResourceDictionary>
</ContentPage.Resources>
@@ -46,7 +54,7 @@
</StackLayout>
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n Options}"
<Label Text="{u:I18n Options, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<StackLayout StyleClass="box-row, box-row-input">
@@ -54,6 +62,7 @@
Text="{u:I18n Type}"
StyleClass="box-label" />
<Picker
x:Name="_typePicker"
ItemsSource="{Binding TypeOptions, Mode=OneTime}"
SelectedIndex="{Binding TypeSelectedIndex}"
StyleClass="box-value" />
@@ -87,8 +96,31 @@
StyleClass="box-label" />
<Entry
Text="{Binding WordSeparator}"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
StyleClass="box-value" />
</StackLayout>
<StackLayout StyleClass="box-row, box-row-switch">
<Label
Text="{u:I18n Capitalize}"
StyleClass="box-label, box-label-regular"
HorizontalOptions="StartAndExpand" />
<Switch
IsToggled="{Binding Capitalize}"
StyleClass="box-value"
HorizontalOptions="End" />
</StackLayout>
<BoxView StyleClass="box-row-separator" />
<StackLayout StyleClass="box-row, box-row-switch">
<Label
Text="{u:I18n IncludeNumber}"
StyleClass="box-label, box-label-regular"
HorizontalOptions="StartAndExpand" />
<Switch
IsToggled="{Binding IncludeNumber}"
StyleClass="box-value"
HorizontalOptions="End" />
</StackLayout>
</StackLayout>
<StackLayout Spacing="0" Padding="0" IsVisible="{Binding IsPassword}">
<StackLayout StyleClass="box-row, box-row-slider">

View File

@@ -1,6 +1,9 @@
using System;
using Bit.App.Resources;
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
namespace Bit.App.Pages
{
@@ -19,9 +22,29 @@ namespace Bit.App.Pages
_vm.Page = this;
_fromTabPage = fromTabPage;
_selectAction = selectAction;
if(selectAction == null)
var isIos = Device.RuntimePlatform == Device.iOS;
if(selectAction != null)
{
ToolbarItems.Remove(_selectItem);
if(isIos)
{
ToolbarItems.Add(_closeItem);
}
ToolbarItems.Add(_selectItem);
}
else
{
if(isIos)
{
ToolbarItems.Add(_moreItem);
}
else
{
ToolbarItems.Add(_historyItem);
}
}
if(isIos)
{
_typePicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
}
}
@@ -59,6 +82,21 @@ namespace Bit.App.Pages
await _vm.CopyAsync();
}
private async void More_Clicked(object sender, EventArgs e)
{
if(!DoOnce())
{
return;
}
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel,
null, AppResources.PasswordHistory);
if(selection == AppResources.PasswordHistory)
{
var page = new GeneratorHistoryPage();
await Navigation.PushModalAsync(new Xamarin.Forms.NavigationPage(page));
}
}
private void Select_Clicked(object sender, EventArgs e)
{
_selectAction?.Invoke(_vm.Password);
@@ -67,12 +105,20 @@ namespace Bit.App.Pages
private async void History_Clicked(object sender, EventArgs e)
{
var page = new GeneratorHistoryPage();
await Navigation.PushModalAsync(new NavigationPage(page));
await Navigation.PushModalAsync(new Xamarin.Forms.NavigationPage(page));
}
private async void LengthSlider_DragCompleted(object sender, EventArgs e)
{
await _vm.SliderChangedAsync();
}
private async void Close_Clicked(object sender, EventArgs e)
{
if(DoOnce())
{
await Navigation.PopModalAsync();
}
}
}
}

View File

@@ -3,9 +3,7 @@ using Bit.App.Utilities;
using Bit.Core.Abstractions;
using Bit.Core.Models.Domain;
using Bit.Core.Utilities;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Xamarin.Forms;
@@ -29,6 +27,8 @@ namespace Bit.App.Pages
private int _length = 5;
private int _numWords = 3;
private string _wordSeparator;
private bool _capitalize;
private bool _includeNumber;
private int _typeSelectedIndex;
private bool _doneIniting;
@@ -196,6 +196,32 @@ namespace Bit.App.Pages
}
}
public bool Capitalize
{
get => _capitalize;
set
{
if(SetProperty(ref _capitalize, value))
{
_options.Capitalize = value;
var task = SaveOptionsAsync();
}
}
}
public bool IncludeNumber
{
get => _includeNumber;
set
{
if(SetProperty(ref _includeNumber, value))
{
_options.Number = value;
var task = SaveOptionsAsync();
}
}
}
public int TypeSelectedIndex
{
get => _typeSelectedIndex;
@@ -273,6 +299,8 @@ namespace Bit.App.Pages
Uppercase = _options.Uppercase.GetValueOrDefault();
Lowercase = _options.Lowercase.GetValueOrDefault();
Length = _options.Length.GetValueOrDefault(5);
Capitalize = _options.Capitalize.GetValueOrDefault();
IncludeNumber = _options.IncludeNumber.GetValueOrDefault();
}
private void SetOptions()
@@ -288,6 +316,8 @@ namespace Bit.App.Pages
_options.Uppercase = Uppercase;
_options.Lowercase = Lowercase;
_options.Length = Length;
_options.Capitalize = Capitalize;
_options.IncludeNumber = IncludeNumber;
}
}
}

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.AutofillPage"
xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:u="clr-namespace:Bit.App.Utilities"
Title="{u:I18n PasswordAutofill}">
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
</ContentPage.ToolbarItems>
<ScrollView>
<StackLayout Spacing="5"
Padding="20, 20, 20, 30"
VerticalOptions="FillAndExpand">
<Label Text="{u:I18n ExtensionInstantAccess}"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"
LineBreakMode="WordWrap"
StyleClass="text-lg"
Margin="0, 0, 0, 15" />
<Label Text="{u:I18n AutofillTurnOn}"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"
LineBreakMode="WordWrap"
Margin="0, 0, 0, 15" />
<Label Text="{u:I18n AutofillTurnOn1}"
LineBreakMode="WordWrap" />
<Label Text="{u:I18n AutofillTurnOn2}"
LineBreakMode="WordWrap" />
<Label Text="{u:I18n AutofillTurnOn3}"
LineBreakMode="WordWrap" />
<Label Text="{u:I18n AutofillTurnOn4}"
LineBreakMode="WordWrap" />
<Label Text="{u:I18n AutofillTurnOn5}"
LineBreakMode="WordWrap" />
<Image Source="autofill-kb.png"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
Margin="0, 10, 0, 0"
WidthRequest="290"
HeightRequest="252" />
</StackLayout>
</ScrollView>
</pages:BaseContentPage>

View File

@@ -0,0 +1,20 @@
using System;
namespace Bit.App.Pages
{
public partial class AutofillPage : BaseContentPage
{
public AutofillPage()
{
InitializeComponent();
}
private void Close_Clicked(object sender, EventArgs e)
{
if(DoOnce())
{
Navigation.PopModalAsync();
}
}
}
}

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.ExtensionPage"
xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="pages:ExtensionPageViewModel"
Title="{Binding PageTitle}">
<ContentPage.BindingContext>
<pages:ExtensionPageViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
</ContentPage.ToolbarItems>
<ScrollView>
<StackLayout Padding="0" Spacing="0" VerticalOptions="FillAndExpand">
<StackLayout Spacing="20"
Padding="20, 20, 20, 30"
VerticalOptions="FillAndExpand"
IsVisible="{Binding NotStarted}">
<Label Text="{u:I18n ExtensionInstantAccess}"
StyleClass="text-lg"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"
LineBreakMode="WordWrap" />
<Label Text="{u:I18n ExtensionTurnOn}"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"
LineBreakMode="WordWrap" />
<Image Source="ext-more.png"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
Margin="0, -10, 0, 0"
WidthRequest="290"
HeightRequest="252" />
<Button Text="{u:I18n ExtensionEnable}"
Clicked="Show_Clicked"
VerticalOptions="End"
HorizontalOptions="Fill" />
</StackLayout>
<StackLayout Spacing="20"
Padding="20, 20, 20, 30"
VerticalOptions="FillAndExpand"
IsVisible="{Binding StartedAndNotActivated}">
<Label Text="{u:I18n ExtensionAlmostDone}"
StyleClass="text-lg"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"
LineBreakMode="WordWrap" />
<Label Text="{u:I18n ExtensionTapIcon}"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"
LineBreakMode="WordWrap" />
<Image Source="ext-act.png"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
Margin="0, -10, 0, 0"
WidthRequest="290"
HeightRequest="252" />
<Button Text="{u:I18n ExtensionEnable}"
Clicked="Show_Clicked"
VerticalOptions="End"
HorizontalOptions="Fill" />
</StackLayout>
<StackLayout Spacing="20"
Padding="20, 20, 20, 30"
VerticalOptions="FillAndExpand"
IsVisible="{Binding StartedAndActivated}">
<Label Text="{u:I18n ExtensionReady}"
StyleClass="text-lg"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"
LineBreakMode="WordWrap" />
<Label Text="{u:I18n ExtensionInSafari}"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"
LineBreakMode="WordWrap" />
<Image Source="ext-use.png"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
Margin="0, -10, 0, 0"
WidthRequest="290"
HeightRequest="252" />
<Button Text="{u:I18n ExntesionReenable}"
Clicked="Show_Clicked"
VerticalOptions="End"
HorizontalOptions="Fill" />
</StackLayout>
</StackLayout>
</ScrollView>
</pages:BaseContentPage>

View File

@@ -0,0 +1,38 @@
using System;
namespace Bit.App.Pages
{
public partial class ExtensionPage : BaseContentPage
{
private readonly ExtensionPageViewModel _vm;
public ExtensionPage()
{
InitializeComponent();
_vm = BindingContext as ExtensionPageViewModel;
_vm.Page = this;
}
protected async override void OnAppearing()
{
base.OnAppearing();
await _vm.InitAsync();
}
private void Show_Clicked(object sender, EventArgs e)
{
if(DoOnce())
{
_vm.ShowExtension();
}
}
private void Close_Clicked(object sender, EventArgs e)
{
if(DoOnce())
{
Navigation.PopModalAsync();
}
}
}
}

View File

@@ -0,0 +1,75 @@
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System.Threading.Tasks;
namespace Bit.App.Pages
{
public class ExtensionPageViewModel : BaseViewModel
{
private const string StartedKey = "appExtensionStarted";
private const string ActivatedKey = "appExtensionActivated";
private readonly IMessagingService _messagingService;
private readonly IStorageService _storageService;
private readonly IPlatformUtilsService _platformUtilsService;
private bool _started;
private bool _activated;
public ExtensionPageViewModel()
{
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
PageTitle = AppResources.AppExtension;
}
public bool Started
{
get => _started;
set => SetProperty(ref _started, value, additionalPropertyNames: new string[]
{
nameof(NotStarted),
nameof(StartedAndNotActivated),
nameof(StartedAndActivated)
});
}
public bool Activated
{
get => _activated;
set => SetProperty(ref _activated, value, additionalPropertyNames: new string[]
{
nameof(StartedAndNotActivated),
nameof(StartedAndActivated)
});
}
public bool NotStarted => !Started;
public bool StartedAndNotActivated => Started && !Activated;
public bool StartedAndActivated => Started && Activated;
public async Task InitAsync()
{
var started = await _storageService.GetAsync<bool?>(StartedKey);
var activated = await _storageService.GetAsync<bool?>(ActivatedKey);
Started = started.GetValueOrDefault();
Activated = activated.GetValueOrDefault();
}
public void ShowExtension()
{
_messagingService.Send("showAppExtension", this);
}
public void EnabledExtension(bool enabled)
{
Started = true;
if(!Activated && enabled)
{
Activated = enabled;
}
}
}
}

View File

@@ -21,6 +21,15 @@
IsDestructive="True"
x:Name="_deleteItem" />
</ContentPage.ToolbarItems>
<ContentPage.Resources>
<ResourceDictionary>
<ToolbarItem Icon="more_vert.png" Clicked="More_Clicked" Order="Primary" x:Name="_moreItem"
x:Key="moreItem"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" />
</ResourceDictionary>
</ContentPage.Resources>
<ScrollView x:Name="_scrollView">
<StackLayout Spacing="20">

View File

@@ -1,4 +1,6 @@
using Xamarin.Forms;
using Bit.App.Resources;
using System.Collections.Generic;
using Xamarin.Forms;
namespace Bit.App.Pages
{
@@ -19,6 +21,10 @@ namespace Bit.App.Pages
{
ToolbarItems.Remove(_deleteItem);
}
if(_vm.EditMode && Device.RuntimePlatform == Device.iOS)
{
ToolbarItems.Add(_moreItem);
}
if(Device.RuntimePlatform == Device.Android)
{
ToolbarItems.RemoveAt(0);
@@ -61,5 +67,20 @@ namespace Bit.App.Pages
await Navigation.PopModalAsync();
}
}
private async void More_Clicked(object sender, System.EventArgs e)
{
if(!DoOnce())
{
return;
}
var options = new List<string> { };
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel,
_vm.EditMode ? AppResources.Delete : null, options.ToArray());
if(selection == AppResources.Delete)
{
await _vm.DeleteAsync();
}
}
}
}

View File

@@ -46,7 +46,10 @@ namespace Bit.App.Pages
if(EditMode)
{
var folder = await _folderService.GetAsync(FolderId);
Folder = await folder.DecryptAsync();
if(folder != null)
{
Folder = await folder.DecryptAsync();
}
}
else
{
@@ -57,6 +60,10 @@ namespace Bit.App.Pages
public async Task<bool> SubmitAsync()
{
if(Folder == null)
{
return false;
}
if(Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
{
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
@@ -93,6 +100,10 @@ namespace Bit.App.Pages
public async Task<bool> DeleteAsync()
{
if(Folder == null)
{
return false;
}
if(Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
{
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,

View File

@@ -16,13 +16,15 @@
<pages:FoldersPageViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
</ContentPage.ToolbarItems>
<ContentPage.Resources>
<ResourceDictionary>
<u:InverseBoolConverter x:Key="inverseBool" />
<ToolbarItem x:Name="_closeItem" x:Key="closeItem" Text="{u:I18n Close}"
Clicked="Close_Clicked" Order="Primary" Priority="-1" />
<ToolbarItem x:Name="_addItem" x:Key="addItem" Icon="plus.png"
Clicked="AddButton_Clicked" Order="Primary"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n AddItem}" />
<StackLayout x:Name="_mainLayout" x:Key="mainLayout">
<Label IsVisible="{Binding ShowNoData}"
Text="{u:I18n NoFoldersToList}"
@@ -68,7 +70,9 @@
x:Name="_fab"
ImageName="plus.png"
AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="1, 1, AutoSize, AutoSize">
AbsoluteLayout.LayoutBounds="1, 1, AutoSize, AutoSize"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n AddFolder}">
</fab:FloatingActionButtonView>
</AbsoluteLayout>

View File

@@ -18,10 +18,11 @@ namespace Bit.App.Pages
if(Device.RuntimePlatform == Device.iOS)
{
_absLayout.Children.Remove(_fab);
ToolbarItems.Add(_closeItem);
ToolbarItems.Add(_addItem);
}
else
{
ToolbarItems.RemoveAt(0);
_fab.Clicked = AddButton_Clicked;
}
}

View File

@@ -10,14 +10,12 @@ namespace Bit.App.Pages
{
public class FoldersPageViewModel : BaseViewModel
{
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IFolderService _folderService;
private bool _showNoData;
public FoldersPageViewModel()
{
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
PageTitle = AppResources.Folders;

View File

@@ -19,7 +19,7 @@
<ScrollView Padding="0, 0, 0, 20">
<StackLayout Padding="0" Spacing="20">
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row, box-row-input">
<StackLayout StyleClass="box-row, box-row-input, box-row-input-options-platform">
<Label
Text="{u:I18n Theme}"
StyleClass="box-label" />
@@ -35,7 +35,7 @@
x:Name="_themeDescriptionLabel" />
</StackLayout>
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row, box-row-input">
<StackLayout StyleClass="box-row, box-row-input, box-row-input-options-platform">
<Label
Text="{u:I18n DefaultUriMatchDetection}"
StyleClass="box-label" />
@@ -50,7 +50,7 @@
StyleClass="box-footer-label" />
</StackLayout>
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row, box-row-input">
<StackLayout StyleClass="box-row, box-row-input, box-row-input-options-platform">
<Label
Text="{u:I18n ClearClipboard}"
StyleClass="box-label" />
@@ -96,7 +96,7 @@
</StackLayout>
<StackLayout StyleClass="box" IsVisible="{Binding ShowAndroidAutofillSettings}">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n AutofillService}"
<Label Text="{u:I18n AutofillService, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<StackLayout StyleClass="box-row, box-row-switch">
@@ -134,7 +134,7 @@
</StackLayout>
<StackLayout StyleClass="box" IsVisible="{Binding ShowAndroidAccessibilitySettings}">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n AutofillAccessibilityService}"
<Label Text="{u:I18n AutofillAccessibilityService, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<StackLayout StyleClass="box-row, box-row-switch">

View File

@@ -2,6 +2,8 @@
using Bit.App.Resources;
using Bit.Core.Utilities;
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
namespace Bit.App.Pages
{
@@ -27,6 +29,12 @@ namespace Bit.App.Pages
_themeDescriptionLabel.Text = string.Concat(_themeDescriptionLabel.Text, " ",
AppResources.RestartIsRequired);
}
else
{
_themePicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
_uriMatchPicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
_clearClipboardPicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
}
}
protected async override void OnAppearing()

View File

@@ -46,6 +46,7 @@ namespace Bit.App.Pages
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
PageTitle = AppResources.Options;
var iosIos = Device.RuntimePlatform == Device.iOS;
ClearClipboardOptions = new List<KeyValuePair<int?, string>>
{
@@ -53,21 +54,21 @@ namespace Bit.App.Pages
new KeyValuePair<int?, string>(10, AppResources.TenSeconds),
new KeyValuePair<int?, string>(20, AppResources.TwentySeconds),
new KeyValuePair<int?, string>(30, AppResources.ThirtySeconds),
new KeyValuePair<int?, string>(60, AppResources.OneMinute),
new KeyValuePair<int?, string>(120, AppResources.TwoMinutes),
new KeyValuePair<int?, string>(300, AppResources.FiveMinutes),
new KeyValuePair<int?, string>(60, AppResources.OneMinute)
};
if(!iosIos)
{
ClearClipboardOptions.Add(new KeyValuePair<int?, string>(120, AppResources.TwoMinutes));
ClearClipboardOptions.Add(new KeyValuePair<int?, string>(300, AppResources.FiveMinutes));
}
ThemeOptions = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>(null, AppResources.Default),
new KeyValuePair<string, string>("light", AppResources.Light),
new KeyValuePair<string, string>("dark", AppResources.Dark),
new KeyValuePair<string, string>("black", AppResources.Black),
new KeyValuePair<string, string>("nord", "Nord"),
};
if(Device.RuntimePlatform == Device.Android)
{
ThemeOptions.Add(new KeyValuePair<string, string>("black", AppResources.Black));
}
ThemeOptions.Add(new KeyValuePair<string, string>("nord", "Nord"));
UriMatchOptions = new List<KeyValuePair<UriMatchType?, string>>
{
new KeyValuePair<UriMatchType?, string>(UriMatchType.Domain, AppResources.BaseDomain),
@@ -300,6 +301,11 @@ namespace Bit.App.Pages
await Task.Delay(1000);
}
_messagingService.Send("updatedTheme", theme);
if(Device.RuntimePlatform == Device.iOS)
{
await Task.Delay(500);
await _platformUtilsService.ShowDialogAsync(AppResources.ThemeAppliedOnRestart);
}
}
}

View File

@@ -47,7 +47,8 @@
<ListView
ItemsSource="{Binding GroupedItems}"
VerticalOptions="FillAndExpand"
HasUnevenRows="true"
HasUnevenRows="True"
RowHeight="-1"
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
IsGroupingEnabled="True"
ItemSelected="RowSelected"
@@ -56,14 +57,18 @@
<ListView.GroupHeaderTemplate>
<DataTemplate x:DataType="pages:SettingsPageListGroup">
<ViewCell>
<StackLayout Padding="0" Spacing="0">
<BoxView StyleClass="list-section-separator"
IsVisible="{Binding First, Converter={StaticResource inverseBool}}" />
<StackLayout StyleClass="list-row-header">
<StackLayout
Padding="0" Spacing="0" VerticalOptions="FillAndExpand"
StyleClass="list-row-header-container, list-row-header-container-platform">
<BoxView
StyleClass="list-section-separator-top, list-section-separator-top-platform"
IsVisible="{Binding First, Converter={StaticResource inverseBool}}" />
<StackLayout StyleClass="list-row-header, list-row-header-platform">
<Label
Text="{Binding Name}"
StyleClass="list-header, list-header-platform" />
</StackLayout>
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
</StackLayout>
</ViewCell>
</DataTemplate>

View File

@@ -67,11 +67,11 @@ namespace Bit.App.Pages
}
else if(item.Name == AppResources.PasswordAutofill)
{
// await Navigation.PushModalAsync(new NavigationPage(new OptionsPage()));
await Navigation.PushModalAsync(new NavigationPage(new AutofillPage()));
}
else if(item.Name == AppResources.AppExtension)
{
// await Navigation.PushModalAsync(new NavigationPage(new OptionsPage()));
await Navigation.PushModalAsync(new NavigationPage(new ExtensionPage()));
}
else if(item.Name == AppResources.Options)
{
@@ -137,10 +137,18 @@ namespace Bit.App.Pages
{
await _vm.UpdatePinAsync();
}
else if(item.Name.Contains(AppResources.Fingerprint) || item.Name.Contains(AppResources.TouchID) ||
item.Name.Contains(AppResources.FaceID))
else
{
await _vm.UpdateFingerprintAsync();
var fingerprintName = AppResources.Fingerprint;
if(Device.RuntimePlatform == Device.iOS)
{
fingerprintName = _deviceActionService.SupportsFaceId() ?
AppResources.FaceID : AppResources.TouchID;
}
if(item.Name == string.Format(AppResources.UnlockWith, fingerprintName))
{
await _vm.UpdateFingerprintAsync();
}
}
}
}

View File

@@ -23,7 +23,6 @@ namespace Bit.App.Pages
private readonly IStorageService _storageService;
private readonly ISyncService _syncService;
private string _fingerprintName;
private bool _supportsFingerprint;
private bool _pin;
private bool _fingerprint;
@@ -57,12 +56,6 @@ namespace Bit.App.Pages
GroupedItems = new ExtendedObservableCollection<SettingsPageListGroup>();
PageTitle = AppResources.Settings;
_fingerprintName = AppResources.Fingerprint;
if(Device.RuntimePlatform == Device.iOS)
{
_fingerprintName = _deviceActionService.SupportsFaceId() ? AppResources.FaceID : AppResources.TouchID;
}
}
public ExtendedObservableCollection<SettingsPageListGroup> GroupedItems { get; set; }
@@ -327,11 +320,17 @@ namespace Bit.App.Pages
new SettingsPageListItem { Name = AppResources.LockNow },
new SettingsPageListItem { Name = AppResources.TwoStepLogin }
};
if(_supportsFingerprint)
if(_supportsFingerprint || _fingerprint)
{
var fingerprintName = AppResources.Fingerprint;
if(Device.RuntimePlatform == Device.iOS)
{
fingerprintName = _deviceActionService.SupportsFaceId() ?
AppResources.FaceID : AppResources.TouchID;
}
var item = new SettingsPageListItem
{
Name = string.Format(AppResources.UnlockWith, _fingerprintName),
Name = string.Format(AppResources.UnlockWith, fingerprintName),
SubLabel = _fingerprint ? AppResources.Enabled : AppResources.Disabled
};
securityItems.Insert(1, item);

View File

@@ -10,9 +10,9 @@ namespace Bit.App.Pages
private NavigationPage _groupingsPage;
private NavigationPage _generatorPage;
public TabsPage(AppOptions appOptions = null)
public TabsPage(AppOptions appOptions = null, PreviousPageInfo previousPage = null)
{
_groupingsPage = new NavigationPage(new GroupingsPage(true))
_groupingsPage = new NavigationPage(new GroupingsPage(true, previousPage: previousPage))
{
Title = AppResources.MyVault,
Icon = "lock.png"

View File

@@ -15,17 +15,7 @@
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
<ToolbarItem Text="{u:I18n Save}" Clicked="Save_Clicked" Order="Primary" />
<ToolbarItem Text="{u:I18n Attachments}"
Clicked="Attachments_Clicked"
Order="Secondary"
x:Name="_attachmentsItem" />
<ToolbarItem Text="{u:I18n Delete}"
Clicked="Delete_Clicked"
Order="Secondary"
IsDestructive="True"
x:Name="_deleteItem" />
</ContentPage.ToolbarItems>
<ContentPage.Resources>
@@ -33,16 +23,33 @@
<u:InverseBoolConverter x:Key="inverseBool" />
<u:StringHasValueConverter x:Key="stringHasValue" />
<u:IsNotNullConverter x:Key="notNull" />
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
x:Key="closeItem" x:Name="_closeItem" />
<ToolbarItem Text="{u:I18n Collections}"
x:Key="collectionsItem"
x:Name="_collectionsItem"
Clicked="Collections_Clicked"
Order="Secondary" />
x:Key="collectionsItem"
x:Name="_collectionsItem"
Clicked="Collections_Clicked"
Order="Secondary" />
<ToolbarItem Text="{u:I18n Share}"
x:Key="shareItem"
x:Name="_shareItem"
Clicked="Share_Clicked"
Order="Secondary" />
x:Key="shareItem"
x:Name="_shareItem"
Clicked="Share_Clicked"
Order="Secondary" />
<ToolbarItem Icon="more_vert.png" Clicked="More_Clicked" Order="Primary" x:Name="_moreItem"
x:Key="moreItem"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" />
<ToolbarItem Text="{u:I18n Attachments}"
Clicked="Attachments_Clicked"
Order="Secondary"
x:Name="_attachmentsItem"
x:Key="attachmentsItem" />
<ToolbarItem Text="{u:I18n Delete}"
Clicked="Delete_Clicked"
Order="Secondary"
IsDestructive="True"
x:Name="_deleteItem"
x:Key="deleteItem" />
</ResourceDictionary>
</ContentPage.Resources>
@@ -50,7 +57,7 @@
<StackLayout Spacing="20">
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n ItemInformation}"
<Label Text="{u:I18n ItemInformation, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<StackLayout StyleClass="box-row, box-row-input"
@@ -105,28 +112,36 @@
StyleClass="box-value"
Grid.Row="1"
Grid.Column="0"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}" />
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="&#xf058;"
Command="{Binding CheckPasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CheckPassword}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="2"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="&#xf021;"
Command="{Binding GeneratePasswordCommand}"
Grid.Row="0"
Grid.Column="3"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n GeneratePassword}" />
</Grid>
<Grid StyleClass="box-row, box-row-input">
@@ -146,6 +161,8 @@
<controls:MonoEntry
x:Name="_loginTotpEntry"
Text="{Binding Cipher.Login.Totp}"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
StyleClass="box-value"
Grid.Row="1"
Grid.Column="0" />
@@ -155,7 +172,9 @@
Clicked="ScanTotp_Clicked"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ScanQrTitle}" />
</Grid>
</StackLayout>
<StackLayout IsVisible="{Binding IsCard}" Spacing="0" Padding="0">
@@ -229,14 +248,18 @@
Grid.Row="1"
Grid.Column="0"
Keyboard="Numeric"
IsPassword="{Binding ShowCardCode, Converter={StaticResource inverseBool}}" />
IsPassword="{Binding ShowCardCode, Converter={StaticResource inverseBool}}"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowCardCodeIcon}"
Command="{Binding ToggleCardCodeCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
</StackLayout>
<StackLayout IsVisible="{Binding IsIdentity}" Spacing="0" Padding="0">
@@ -328,6 +351,7 @@
StyleClass="box-label" />
<Entry
x:Name="_identityEmailEntry"
Keyboard="Email"
Text="{Binding Cipher.Identity.Email}"
StyleClass="box-value" />
</StackLayout>
@@ -407,7 +431,7 @@
</StackLayout>
<StackLayout StyleClass="box" IsVisible="{Binding IsLogin}">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n URIs}"
<Label Text="{u:I18n URIs, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<controls:RepeaterView ItemsSource="{Binding Uris}">
@@ -440,7 +464,9 @@
CommandParameter="{Binding .}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" />
</Grid>
</DataTemplate>
</controls:RepeaterView.ItemTemplate>
@@ -450,7 +476,7 @@
</StackLayout>
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n Miscellaneous}"
<Label Text="{u:I18n Miscellaneous, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<StackLayout StyleClass="box-row, box-row-input">
@@ -477,7 +503,7 @@
</StackLayout>
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n Notes}"
<Label Text="{u:I18n Notes, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<StackLayout StyleClass="box-row, box-row-input">
@@ -487,10 +513,11 @@
Text="{Binding Cipher.Notes}"
StyleClass="box-value" />
</StackLayout>
<BoxView StyleClass="box-row-separator" IsVisible="{Binding ShowNotesSeparator}" />
</StackLayout>
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n CustomFields}"
<Label Text="{u:I18n CustomFields, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<controls:RepeaterView ItemsSource="{Binding Fields}">
@@ -534,7 +561,9 @@
Grid.Row="1"
Grid.Column="0"
IsVisible="{Binding IsHiddenType}"
IsPassword="{Binding ShowHiddenValue, Converter={StaticResource inverseBool}}" />
IsPassword="{Binding ShowHiddenValue, Converter={StaticResource inverseBool}}"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False" />
<Switch
IsToggled="{Binding BooleanValue}"
Grid.Row="0"
@@ -548,7 +577,9 @@
IsVisible="{Binding IsHiddenType}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="&#xf013;"
@@ -556,7 +587,9 @@
CommandParameter="{Binding .}"
Grid.Row="0"
Grid.Column="2"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" />
</Grid>
<BoxView StyleClass="box-row-separator" IsVisible="{Binding IsBooleanType}" />
</StackLayout>
@@ -568,7 +601,7 @@
</StackLayout>
<StackLayout StyleClass="box" IsVisible="{Binding EditMode, Converter={StaticResource inverseBool}}">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n Ownership}"
<Label Text="{u:I18n Ownership, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<StackLayout StyleClass="box-row, box-row-input">
@@ -584,7 +617,7 @@
</StackLayout>
<StackLayout StyleClass="box" IsVisible="{Binding ShowCollections}">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n Collections}"
<Label Text="{u:I18n Collections, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<StackLayout Spacing="0" Padding="0"

View File

@@ -8,6 +8,8 @@ using Bit.Core.Utilities;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
namespace Bit.App.Pages
{
@@ -39,21 +41,29 @@ namespace Bit.App.Pages
_vm = BindingContext as AddEditPageViewModel;
_vm.Page = this;
_vm.CipherId = cipherId;
_vm.FolderId = folderId;
_vm.FolderId = folderId == "none" ? null : folderId;
_vm.CollectionIds = collectionId != null ? new HashSet<string>(new List<string> { collectionId }) : null;
_vm.Type = type;
_vm.DefaultName = name ?? appOptions?.SaveName;
_vm.DefaultUri = uri ?? appOptions?.Uri;
_vm.Init();
SetActivityIndicator();
if(!_vm.EditMode || Device.RuntimePlatform == Device.iOS)
if(_vm.EditMode && Device.RuntimePlatform == Device.Android)
{
ToolbarItems.Remove(_attachmentsItem);
ToolbarItems.Remove(_deleteItem);
ToolbarItems.Add(_attachmentsItem);
ToolbarItems.Add(_deleteItem);
}
if(Device.RuntimePlatform == Device.Android)
if(Device.RuntimePlatform == Device.iOS)
{
ToolbarItems.RemoveAt(0);
ToolbarItems.Add(_closeItem);
if(_vm.EditMode)
{
ToolbarItems.Add(_moreItem);
}
_vm.ShowNotesSeparator = true;
_typePicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
_ownershipPicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
}
_typePicker.ItemDisplayBinding = new Binding("Key");
@@ -121,6 +131,7 @@ namespace Bit.App.Pages
}
public bool FromAutofillFramework { get; set; }
public AddEditPageViewModel ViewModel => _vm;
protected override async void OnAppearing()
{
@@ -151,7 +162,7 @@ namespace Bit.App.Pages
{
if(FromAutofillFramework)
{
Application.Current.MainPage = new TabsPage();
Xamarin.Forms.Application.Current.MainPage = new TabsPage();
return true;
}
return base.OnBackButtonPressed();
@@ -161,7 +172,8 @@ namespace Bit.App.Pages
{
if(DoOnce())
{
await Navigation.PushModalAsync(new NavigationPage(new PasswordHistoryPage(_vm.CipherId)));
await Navigation.PushModalAsync(
new Xamarin.Forms.NavigationPage(new PasswordHistoryPage(_vm.CipherId)));
}
}
@@ -188,7 +200,7 @@ namespace Bit.App.Pages
if(DoOnce())
{
var page = new AttachmentsPage(_vm.CipherId);
await Navigation.PushModalAsync(new NavigationPage(page));
await Navigation.PushModalAsync(new Xamarin.Forms.NavigationPage(page));
}
}
@@ -197,7 +209,7 @@ namespace Bit.App.Pages
if(DoOnce())
{
var page = new SharePage(_vm.CipherId);
await Navigation.PushModalAsync(new NavigationPage(page));
await Navigation.PushModalAsync(new Xamarin.Forms.NavigationPage(page));
}
}
@@ -217,7 +229,7 @@ namespace Bit.App.Pages
if(DoOnce())
{
var page = new CollectionsPage(_vm.CipherId);
await Navigation.PushModalAsync(new NavigationPage(page));
await Navigation.PushModalAsync(new Xamarin.Forms.NavigationPage(page));
}
}
@@ -233,7 +245,44 @@ namespace Bit.App.Pages
await _vm.UpdateTotpKeyAsync(key);
});
});
await Navigation.PushModalAsync(new NavigationPage(page));
await Navigation.PushModalAsync(new Xamarin.Forms.NavigationPage(page));
}
}
private async void More_Clicked(object sender, System.EventArgs e)
{
if(!DoOnce())
{
return;
}
var options = new List<string> { AppResources.Attachments };
if(_vm.EditMode)
{
options.Add(_vm.Cipher.OrganizationId == null ? AppResources.Share : AppResources.Collections);
}
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel,
_vm.EditMode ? AppResources.Delete : null, options.ToArray());
if(selection == AppResources.Delete)
{
if(await _vm.DeleteAsync())
{
await Navigation.PopModalAsync();
}
}
else if(selection == AppResources.Attachments)
{
var page = new AttachmentsPage(_vm.CipherId);
await Navigation.PushModalAsync(new Xamarin.Forms.NavigationPage(page));
}
else if(selection == AppResources.Collections)
{
var page = new CollectionsPage(_vm.CipherId);
await Navigation.PushModalAsync(new Xamarin.Forms.NavigationPage(page));
}
else if(selection == AppResources.Share)
{
var page = new SharePage(_vm.CipherId);
await Navigation.PushModalAsync(new Xamarin.Forms.NavigationPage(page));
}
}

View File

@@ -23,7 +23,9 @@ namespace Bit.App.Pages
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IAuditService _auditService;
private readonly IMessagingService _messagingService;
private readonly IEventService _eventService;
private CipherView _cipher;
private bool _showNotesSeparator;
private bool _showPassword;
private bool _showCardCode;
private int _typeSelectedIndex;
@@ -33,6 +35,7 @@ namespace Bit.App.Pages
private int _folderSelectedIndex;
private int _ownershipSelectedIndex;
private bool _hasCollections;
private string _previousCipherId;
private List<Core.Models.View.CollectionView> _writeableCollections;
private string[] _additionalCipherProperties = new string[]
{
@@ -73,6 +76,7 @@ namespace Bit.App.Pages
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
GeneratePasswordCommand = new Command(GeneratePassword);
TogglePasswordCommand = new Command(TogglePassword);
ToggleCardCodeCommand = new Command(ToggleCardCode);
@@ -224,6 +228,11 @@ namespace Bit.App.Pages
get => _cipher;
set => SetProperty(ref _cipher, value, additionalPropertyNames: _additionalCipherProperties);
}
public bool ShowNotesSeparator
{
get => _showNotesSeparator;
set => SetProperty(ref _showNotesSeparator, value);
}
public bool ShowPassword
{
get => _showPassword;
@@ -359,14 +368,25 @@ namespace Bit.App.Pages
}
if(Cipher.Fields != null)
{
Fields.ResetWithRange(Cipher.Fields?.Select(f => new AddEditPageFieldViewModel(f)));
Fields.ResetWithRange(Cipher.Fields?.Select(f => new AddEditPageFieldViewModel(Cipher, f)));
}
}
if(EditMode && _previousCipherId != CipherId)
{
var task = _eventService.CollectAsync(EventType.Cipher_ClientViewed, CipherId);
}
_previousCipherId = CipherId;
return true;
}
public async Task<bool> SubmitAsync()
{
if(Cipher == null)
{
return false;
}
if(Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
{
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
@@ -381,10 +401,10 @@ namespace Bit.App.Pages
return false;
}
Cipher.Fields = Fields.Any() ? Fields.Select(f => f.Field).ToList() : null;
Cipher.Fields = Fields != null && Fields.Any() ? Fields.Select(f => f.Field).ToList() : null;
if(Cipher.Login != null)
{
Cipher.Login.Uris = Uris.ToList();
Cipher.Login.Uris = Uris?.ToList();
if(!EditMode && Cipher.Type == CipherType.Login && (Cipher.Login.Uris?.Count ?? 0) == 1 &&
string.IsNullOrWhiteSpace(Cipher.Login.Uris.First().Uri))
{
@@ -406,6 +426,10 @@ namespace Bit.App.Pages
}
var cipher = await _cipherService.EncryptAsync(Cipher);
if(cipher == null)
{
return false;
}
try
{
await _deviceActionService.ShowLoadingAsync(AppResources.Saving);
@@ -414,9 +438,9 @@ namespace Bit.App.Pages
await _deviceActionService.HideLoadingAsync();
_platformUtilsService.ShowToast("success", null,
EditMode ? AppResources.ItemUpdated : AppResources.NewItemCreated);
_messagingService.Send(EditMode ? "editedCipher" : "addedCipher");
_messagingService.Send(EditMode ? "editedCipher" : "addedCipher", Cipher.Id);
if((Page as AddEditPage).FromAutofillFramework)
if(Page is AddEditPage page && page.FromAutofillFramework)
{
// Close and go back to app
_deviceActionService.CloseAutofill();
@@ -455,7 +479,7 @@ namespace Bit.App.Pages
await _cipherService.DeleteWithServerAsync(Cipher.Id);
await _deviceActionService.HideLoadingAsync();
_platformUtilsService.ShowToast("success", null, AppResources.ItemDeleted);
_messagingService.Send("deletedCipher");
_messagingService.Send("deletedCipher", Cipher);
return true;
}
catch(ApiException e)
@@ -577,7 +601,7 @@ namespace Bit.App.Pages
Fields = new ExtendedObservableCollection<AddEditPageFieldViewModel>();
}
var type = _fieldTypeOptions.FirstOrDefault(f => f.Value == typeSelection).Key;
Fields.Add(new AddEditPageFieldViewModel(new FieldView
Fields.Add(new AddEditPageFieldViewModel(Cipher, new FieldView
{
Type = type,
Name = string.IsNullOrWhiteSpace(name) ? null : name
@@ -588,11 +612,19 @@ namespace Bit.App.Pages
public void TogglePassword()
{
ShowPassword = !ShowPassword;
if(EditMode && ShowPassword)
{
var task = _eventService.CollectAsync(EventType.Cipher_ClientToggledPasswordVisible, CipherId);
}
}
public void ToggleCardCode()
{
ShowCardCode = !ShowCardCode;
if(EditMode && ShowCardCode)
{
var task = _eventService.CollectAsync(EventType.Cipher_ClientToggledCardCodeVisible, CipherId);
}
}
public async Task UpdateTotpKeyAsync(string key)
@@ -706,6 +738,7 @@ namespace Bit.App.Pages
public class AddEditPageFieldViewModel : ExtendedViewModel
{
private FieldView _field;
private CipherView _cipher;
private bool _showHiddenValue;
private bool _booleanValue;
private string[] _additionalFieldProperties = new string[]
@@ -715,8 +748,9 @@ namespace Bit.App.Pages
nameof(IsTextType),
};
public AddEditPageFieldViewModel(FieldView field)
public AddEditPageFieldViewModel(CipherView cipher, FieldView field)
{
_cipher = cipher;
Field = field;
ToggleHiddenValueCommand = new Command(ToggleHiddenValue);
BooleanValue = IsBooleanType && field.Value == "true";
@@ -761,6 +795,11 @@ namespace Bit.App.Pages
public void ToggleHiddenValue()
{
ShowHiddenValue = !ShowHiddenValue;
if(ShowHiddenValue && _cipher?.Id != null)
{
var eventService = ServiceContainer.Resolve<IEventService>("eventService");
var task = eventService.CollectAsync(EventType.Cipher_ClientToggledHiddenFieldVisible, _cipher.Id);
}
}
public void TriggerFieldChanged()

View File

@@ -30,9 +30,9 @@
<ScrollView x:Name="_scrollView">
<StackLayout Spacing="20">
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row"
<StackLayout StyleClass="box-row" Padding="10, 20"
IsVisible="{Binding HasAttachments, Converter={StaticResource inverseBool}}">
<Label Text="{u:I18n NoAttachments}" />
<Label Text="{u:I18n NoAttachments}" HorizontalTextAlignment="Center" />
</StackLayout>
<controls:RepeaterView ItemsSource="{Binding Attachments}" IsVisible="{Binding HasAttachments}">
<controls:RepeaterView.ItemTemplate>
@@ -54,7 +54,9 @@
Text="&#xf014;"
Command="{Binding BindingContext.DeleteAttachmentCommand, Source={x:Reference _page}}"
CommandParameter="{Binding .}"
VerticalOptions="Center" />
VerticalOptions="Center"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Delete}" />
</StackLayout>
<BoxView StyleClass="box-row-separator" />
</StackLayout>
@@ -64,7 +66,7 @@
</StackLayout>
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n AddNewAttachment}"
<Label Text="{u:I18n AddNewAttachment, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<StackLayout StyleClass="box-row">

View File

@@ -45,7 +45,10 @@ namespace Bit.App.Pages
protected override void OnDisappearing()
{
base.OnDisappearing();
_broadcasterService.Unsubscribe(nameof(AttachmentsPage));
if(Device.RuntimePlatform != Device.iOS)
{
_broadcasterService.Unsubscribe(nameof(AttachmentsPage));
}
}
private async void Save_Clicked(object sender, EventArgs e)

View File

@@ -21,7 +21,7 @@
<ContentPage.Resources>
<ResourceDictionary>
<u:InverseBoolConverter x:Key="inverseBool" />
<DataTemplate x:Key="cipherTemplate"
x:DataType="pages:GroupingsPageListItem">
<controls:CipherViewCell
@@ -63,13 +63,19 @@
<ListView.GroupHeaderTemplate>
<DataTemplate x:DataType="pages:GroupingsPageListGroup">
<ViewCell>
<StackLayout StyleClass="list-row-header">
<Label
Text="{Binding Name}"
StyleClass="list-header, list-header-platform" />
<Label
Text="{Binding ItemCount}"
StyleClass="list-header-sub" />
<StackLayout
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
StyleClass="list-row-header-container, list-row-header-container-platform">
<BoxView
StyleClass="list-section-separator-top, list-section-separator-top-platform" />
<StackLayout StyleClass="list-row-header, list-row-header-platform">
<Label
Text="{Binding Name}"
StyleClass="list-header, list-header-platform" />
<Label
Text="{Binding ItemCount}"
StyleClass="list-header-sub" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>

View File

@@ -3,6 +3,8 @@ using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Utilities;
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace Bit.App.Pages
@@ -31,7 +33,15 @@ namespace Bit.App.Pages
base.OnAppearing();
await LoadOnAppearedAsync(_mainLayout, false, async () =>
{
await _vm.LoadAsync();
try
{
await _vm.LoadAsync();
}
catch(Exception e) when(e.Message.Contains("No key."))
{
await Task.Delay(5000);
await _vm.LoadAsync();
}
}, _mainContent);
}

View File

@@ -112,6 +112,10 @@ namespace Bit.App.Pages
public async Task SelectCipherAsync(CipherView cipher, bool fuzzy)
{
if(cipher == null)
{
return;
}
if(_deviceActionService.SystemMajorVersion() < 21)
{
await AppHelpers.CipherListOptions(Page, cipher);

View File

@@ -15,39 +15,38 @@
<pages:CiphersPageViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
</ContentPage.ToolbarItems>
<ContentPage.Resources>
<ResourceDictionary>
<u:DateTimeConverter x:Key="dateTime" />
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
x:Name="_closeItem" x:Key="closeItem" />
<StackLayout
Orientation="Horizontal"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
Spacing="0"
Padding="0"
x:Name="_titleLayout"
x:Key="titleLayout">
<controls:MiButton
StyleClass="btn-title, btn-title-platform"
Text="&#xe5c4;"
VerticalOptions="CenterAndExpand"
Clicked="BackButton_Clicked"
x:Name="_backButton" />
<SearchBar
x:Name="_searchBar"
HorizontalOptions="FillAndExpand"
TextChanged="SearchBar_TextChanged"
SearchButtonPressed="SearchBar_SearchButtonPressed"
Placeholder="{Binding PageTitle}" />
</StackLayout>
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform"
x:Name="_separator" x:Key="separator" />
</ResourceDictionary>
</ContentPage.Resources>
<NavigationPage.TitleView>
<StackLayout
Orientation="Horizontal"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
Spacing="0"
Padding="0">
<controls:MiButton
StyleClass="btn-title, btn-title-platform"
Text="&#xe5c4;"
VerticalOptions="CenterAndExpand"
Clicked="BackButton_Clicked" />
<SearchBar
x:Name="_searchBar"
HorizontalOptions="FillAndExpand"
BackgroundColor="Transparent"
TextChanged="SearchBar_TextChanged"
SearchButtonPressed="SearchBar_SearchButtonPressed"
Placeholder="{Binding PageTitle}" />
</StackLayout>
</NavigationPage.TitleView>
<StackLayout x:Name="_mainLayout">
<StackLayout x:Name="_mainLayout" Spacing="0" Padding="0">
<controls:FaLabel IsVisible="{Binding ShowSearchDirection}"
Text="&#xf002;"
StyleClass="text-muted"
@@ -62,13 +61,13 @@
HorizontalOptions="CenterAndExpand"
HorizontalTextAlignment="Center" />
<ListView x:Name="_listView"
IsVisible="{Binding ShowList}"
ItemsSource="{Binding Ciphers}"
VerticalOptions="FillAndExpand"
HasUnevenRows="true"
CachingStrategy="RecycleElement"
ItemSelected="RowSelected"
StyleClass="list, list-platform">
IsVisible="{Binding ShowList}"
ItemsSource="{Binding Ciphers}"
VerticalOptions="FillAndExpand"
HasUnevenRows="true"
CachingStrategy="RecycleElement"
ItemSelected="RowSelected"
StyleClass="list, list-platform">
<ListView.ItemTemplate>
<DataTemplate x:DataType="views:CipherView">
<controls:CipherViewCell

View File

@@ -40,9 +40,16 @@ namespace Bit.App.Pages
_vm.PageTitle = AppResources.SearchVault;
}
if(Device.RuntimePlatform == Device.Android)
if(Device.RuntimePlatform == Device.iOS)
{
ToolbarItems.RemoveAt(0);
ToolbarItems.Add(_closeItem);
_searchBar.Placeholder = AppResources.Search;
_mainLayout.Children.Insert(0, _searchBar);
_mainLayout.Children.Insert(1, _separator);
}
else
{
NavigationPage.SetTitleView(this, _titleLayout);
}
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
}
@@ -121,12 +128,9 @@ namespace Bit.App.Pages
}
}
private async void Close_Clicked(object sender, System.EventArgs e)
private void Close_Clicked(object sender, EventArgs e)
{
if(DoOnce())
{
await Navigation.PopModalAsync();
}
GoBack();
}
}
}

View File

@@ -75,6 +75,10 @@ namespace Bit.App.Pages
{
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey))
.GetValueOrDefault();
if(!string.IsNullOrWhiteSpace((Page as CiphersPage).SearchBar.Text))
{
Search((Page as CiphersPage).SearchBar.Text, 500);
}
}
public void Search(string searchText, int? timeout = null)
@@ -126,7 +130,7 @@ namespace Bit.App.Pages
if(!string.IsNullOrWhiteSpace(AutofillUrl))
{
var options = new List<string> { AppResources.Autofill };
if(cipher.Type == CipherType.Login &&
if(cipher.Type == CipherType.Login &&
Xamarin.Essentials.Connectivity.NetworkAccess != Xamarin.Essentials.NetworkAccess.None)
{
options.Add(AppResources.AutofillAndSave);

View File

@@ -28,9 +28,9 @@
<ScrollView x:Name="_scrollView">
<StackLayout Spacing="20">
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row"
<StackLayout StyleClass="box-row" Padding="10, 20"
IsVisible="{Binding HasCollections, Converter={StaticResource inverseBool}}">
<Label Text="{u:I18n NoCollectionsToList}" />
<Label Text="{u:I18n NoCollectionsToList}" HorizontalTextAlignment="Center" />
</StackLayout>
<controls:RepeaterView ItemsSource="{Binding Collections}" IsVisible="{Binding HasCollections}">
<controls:RepeaterView.ItemTemplate>

View File

@@ -58,7 +58,8 @@ namespace Bit.App.Pages
public async Task<bool> SubmitAsync()
{
if(!Collections.Any(c => c.Checked))
var selectedCollectionIds = Collections?.Where(c => c.Checked).Select(c => c.Collection.Id);
if(!selectedCollectionIds?.Any() ?? true)
{
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, AppResources.SelectOneCollection,
AppResources.Ok);
@@ -71,8 +72,7 @@ namespace Bit.App.Pages
return false;
}
_cipherDomain.CollectionIds = new HashSet<string>(
Collections.Where(c => c.Checked).Select(c => c.Collection.Id));
_cipherDomain.CollectionIds = new HashSet<string>(selectedCollectionIds);
try
{
await _deviceActionService.ShowLoadingAsync(AppResources.Saving);

View File

@@ -16,7 +16,9 @@
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Icon="search.png" Clicked="Search_Clicked" />
<ToolbarItem Icon="search.png" Clicked="Search_Clicked"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Search}" />
</ContentPage.ToolbarItems>
<ContentPage.Resources>
@@ -29,6 +31,10 @@
Clicked="Lock_Clicked" Order="Secondary" />
<ToolbarItem x:Name="_exitItem" x:Key="exitItem" Text="{u:I18n Exit}"
Clicked="Exit_Clicked" Order="Secondary" />
<ToolbarItem x:Name="_addItem" x:Key="addItem" Icon="plus.png"
Clicked="AddButton_Clicked" Order="Primary"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n AddItem}" />
<DataTemplate x:Key="cipherTemplate"
x:DataType="pages:GroupingsPageListItem">
@@ -89,9 +95,10 @@
IsVisible="{Binding ShowList}"
ItemsSource="{Binding GroupedItems}"
VerticalOptions="FillAndExpand"
HasUnevenRows="true"
HasUnevenRows="True"
RowHeight="-1"
RefreshCommand="{Binding RefreshCommand}"
IsPullToRefreshEnabled="true"
IsPullToRefreshEnabled="True"
IsRefreshing="{Binding Refreshing}"
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
IsGroupingEnabled="True"
@@ -104,10 +111,13 @@
<ListView.GroupHeaderTemplate>
<DataTemplate x:DataType="pages:GroupingsPageListGroup">
<ViewCell>
<StackLayout Spacing="0" Padding="0">
<BoxView StyleClass="list-section-separator"
IsVisible="{Binding First, Converter={StaticResource inverseBool}}" />
<StackLayout StyleClass="list-row-header">
<StackLayout
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
StyleClass="list-row-header-container, list-row-header-container-platform">
<BoxView
StyleClass="list-section-separator-top, list-section-separator-top-platform"
IsVisible="{Binding First, Converter={StaticResource inverseBool}}" />
<StackLayout StyleClass="list-row-header, list-row-header-platform">
<Label
Text="{Binding Name}"
StyleClass="list-header, list-header-platform" />
@@ -115,6 +125,7 @@
Text="{Binding ItemCount}"
StyleClass="list-header-sub" />
</StackLayout>
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
</StackLayout>
</ViewCell>
</DataTemplate>
@@ -137,7 +148,9 @@
x:Name="_fab"
ImageName="plus.png"
AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="1, 1, AutoSize, AutoSize">
AbsoluteLayout.LayoutBounds="1, 1, AutoSize, AutoSize"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n AddItem}">
</fab:FloatingActionButtonView>
</AbsoluteLayout>

View File

@@ -1,12 +1,12 @@
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.App.Models;
using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Utilities;
using System;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
@@ -23,8 +23,10 @@ namespace Bit.App.Pages
private readonly GroupingsPageViewModel _vm;
private readonly string _pageName;
private PreviousPageInfo _previousPage;
public GroupingsPage(bool mainPage, CipherType? type = null, string folderId = null,
string collectionId = null, string pageTitle = null)
string collectionId = null, string pageTitle = null, PreviousPageInfo previousPage = null)
{
_pageName = string.Concat(nameof(GroupingsPage), "_", DateTime.UtcNow.Ticks);
InitializeComponent();
@@ -42,6 +44,7 @@ namespace Bit.App.Pages
_vm.Type = type;
_vm.FolderId = folderId;
_vm.CollectionId = collectionId;
_previousPage = previousPage;
if(pageTitle != null)
{
_vm.PageTitle = pageTitle;
@@ -50,6 +53,7 @@ namespace Bit.App.Pages
if(Device.RuntimePlatform == Device.iOS)
{
_absLayout.Children.Remove(_fab);
ToolbarItems.Add(_addItem);
}
else
{
@@ -82,7 +86,10 @@ namespace Bit.App.Pages
Device.BeginInvokeOnMainThread(() =>
{
IsBusy = false;
var task = _vm.LoadAsync();
if(_vm.LoadedOnce)
{
var task = _vm.LoadAsync();
}
});
}
});
@@ -92,8 +99,15 @@ namespace Bit.App.Pages
{
if(!_syncService.SyncInProgress)
{
await Task.Delay(500);
await _vm.LoadAsync();
try
{
await _vm.LoadAsync();
}
catch(Exception e) when(e.Message.Contains("No key."))
{
await Task.Delay(5000);
await _vm.LoadAsync();
}
}
else
{
@@ -105,7 +119,7 @@ namespace Bit.App.Pages
}
// Forced sync if for some reason we have no data after a v1 migration
if(_vm.MainPage && !_syncService.SyncInProgress && migratedFromV1.GetValueOrDefault() &&
!_vm.HasCiphers && !_vm.HasFolders &&
!_vm.HasCiphers &&
Xamarin.Essentials.Connectivity.NetworkAccess != Xamarin.Essentials.NetworkAccess.None)
{
var triedV1ReSync = await _storageService.GetAsync<bool?>(Constants.TriedV1Resync);
@@ -115,6 +129,7 @@ namespace Bit.App.Pages
await _syncService.FullSyncAsync(true);
}
}
await ShowPreviousPageAsync();
}, _mainContent);
if(!_vm.MainPage)
@@ -234,5 +249,22 @@ namespace Bit.App.Pages
await Navigation.PushModalAsync(new NavigationPage(page));
}
}
private async Task ShowPreviousPageAsync()
{
if(_previousPage == null)
{
return;
}
if(_previousPage.Page == "view" && !string.IsNullOrWhiteSpace(_previousPage.CipherId))
{
await Navigation.PushModalAsync(new NavigationPage(new ViewPage(_previousPage.CipherId)));
}
else if(_previousPage.Page == "edit" && !string.IsNullOrWhiteSpace(_previousPage.CipherId))
{
await Navigation.PushModalAsync(new NavigationPage(new AddEditPage(_previousPage.CipherId)));
}
_previousPage = null;
}
}
}

View File

@@ -126,6 +126,7 @@ namespace Bit.App.Pages
public ExtendedObservableCollection<GroupingsPageListGroup> GroupedItems { get; set; }
public Command RefreshCommand { get; set; }
public Command<CipherView> CipherOptionsCommand { get; set; }
public bool LoadedOnce { get; set; }
public async Task LoadAsync()
{
@@ -134,6 +135,7 @@ namespace Bit.App.Pages
return;
}
_doingLoad = true;
LoadedOnce = true;
ShowNoData = false;
Loading = true;
ShowList = false;
@@ -326,7 +328,7 @@ namespace Bit.App.Pages
{
Folders = await _folderService.GetAllDecryptedAsync();
NestedFolders = await _folderService.GetAllNestedAsync();
HasFolders = NestedFolders.Any();
HasFolders = NestedFolders.Any(f => f.Node?.Id != null);
Collections = await _collectionService.GetAllDecryptedAsync();
NestedCollections = await _collectionService.GetAllNestedAsync(Collections);
HasCollections = NestedCollections.Any();
@@ -364,6 +366,7 @@ namespace Bit.App.Pages
if(collectionNode?.Node != null)
{
PageTitle = collectionNode.Node.Name;
NestedCollections = (collectionNode.Children?.Count ?? 0) > 0 ? collectionNode.Children : null;
}
Filter = c => c.CollectionIds?.Contains(CollectionId) ?? false;
}

View File

@@ -75,7 +75,9 @@
CommandParameter="{Binding .}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyPassword}" />
</Grid>
</ViewCell>
</DataTemplate>

View File

@@ -27,12 +27,15 @@
<ScrollView x:Name="_scrollView">
<StackLayout Spacing="20">
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row"
IsVisible="{Binding HasOrganizations, Converter={StaticResource inverseBool}}">
<Label Text="{u:I18n NoOrgsToList}" />
<StackLayout StyleClass="box"
IsVisible="{Binding HasOrganizations, Converter={StaticResource inverseBool}}">
<StackLayout StyleClass="box-row" Padding="10, 20">
<Label Text="{u:I18n NoOrgsToList}" HorizontalTextAlignment="Center" />
</StackLayout>
<StackLayout StyleClass="box-row, box-row-input">
</StackLayout>
<StackLayout StyleClass="box"
IsVisible="{Binding HasOrganizations}">
<StackLayout StyleClass="box-row, box-row-input, box-row-input-options-platform">
<Label
Text="{u:I18n Organization}"
StyleClass="box-label" />
@@ -49,7 +52,7 @@
<StackLayout StyleClass="box"
IsVisible="{Binding OrganizationId, Converter={StaticResource notNull}}">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n Collections}"
<Label Text="{u:I18n Collections, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<StackLayout StyleClass="box-row"

View File

@@ -1,4 +1,6 @@
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
namespace Bit.App.Pages
{
@@ -17,6 +19,10 @@ namespace Bit.App.Pages
{
ToolbarItems.RemoveAt(0);
}
else
{
_organizationPicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
}
_organizationPicker.ItemDisplayBinding = new Binding("Key");
}

View File

@@ -86,7 +86,8 @@ namespace Bit.App.Pages
public async Task<bool> SubmitAsync()
{
if(!Collections?.Any(c => c.Checked) ?? true)
var selectedCollectionIds = Collections?.Where(c => c.Checked).Select(c => c.Collection.Id);
if(!selectedCollectionIds?.Any() ?? true)
{
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, AppResources.SelectOneCollection,
AppResources.Ok);
@@ -102,8 +103,7 @@ namespace Bit.App.Pages
var cipherDomain = await _cipherService.GetAsync(CipherId);
var cipherView = await cipherDomain.DecryptAsync();
var checkedCollectionIds = new HashSet<string>(
Collections.Where(c => c.Checked).Select(c => c.Collection.Id));
var checkedCollectionIds = new HashSet<string>(selectedCollectionIds);
try
{
await _deviceActionService.ShowLoadingAsync(AppResources.Saving);

View File

@@ -16,13 +16,6 @@
<pages:ViewPageViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
<ToolbarItem Text="{u:I18n Edit}" Clicked="EditToolbarItem_Clicked" Order="Primary" />
<ToolbarItem Text="{u:I18n Attachments}" Clicked="Attachments_Clicked" Order="Secondary" />
<ToolbarItem Text="{u:I18n Delete}" Clicked="Delete_Clicked" Order="Secondary" IsDestructive="True" />
</ContentPage.ToolbarItems>
<ContentPage.Resources>
<ResourceDictionary>
<u:InverseBoolConverter x:Key="inverseBool" />
@@ -38,12 +31,24 @@
x:Name="_shareItem"
Clicked="Share_Clicked"
Order="Secondary" />
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
x:Name="_closeItem" x:Key="closeItem" />
<ToolbarItem Text="{u:I18n Edit}" Clicked="EditToolbarItem_Clicked" Order="Primary"
x:Name="_editItem" x:Key="editItem" />
<ToolbarItem Icon="more_vert.png" Clicked="More_Clicked" Order="Primary"
x:Name="_moreItem" x:Key="moreItem"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" />
<ToolbarItem Text="{u:I18n Attachments}" Clicked="Attachments_Clicked" Order="Secondary"
x:Name="_attachmentsItem" x:Key="attachmentsItem" />
<ToolbarItem Text="{u:I18n Delete}" Clicked="Delete_Clicked" Order="Secondary" IsDestructive="True"
x:Name="_deleteItem" x:Key="deleteItem" />
<ScrollView x:Key="scrollView" x:Name="_scrollView">
<StackLayout Spacing="20" x:Name="_mainLayout">
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n ItemInformation}"
<Label Text="{u:I18n ItemInformation, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<StackLayout StyleClass="box-row">
@@ -83,7 +88,9 @@
CommandParameter="LoginUsername"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyUsername}" />
</Grid>
<BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Login.Username, Converter={StaticResource stringHasValue}}" />
@@ -115,6 +122,7 @@
StyleClass="box-value"
Grid.Row="1"
Grid.Column="0"
LineBreakMode="CharacterWrap"
IsVisible="{Binding ShowPassword}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
@@ -122,14 +130,18 @@
Command="{Binding CheckPasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CheckPassword}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="2"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="&#xf0ea;"
@@ -137,7 +149,9 @@
CommandParameter="LoginPassword"
Grid.Row="0"
Grid.Column="3"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyPassword}" />
</Grid>
<BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Login.Password, Converter={StaticResource stringHasValue}}" />
@@ -178,7 +192,9 @@
CommandParameter="LoginTotp"
Grid.Row="0"
Grid.Column="2"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyTotp}" />
</Grid>
<BoxView StyleClass="box-row-separator" IsVisible="{Binding ShowTotp}" />
</StackLayout>
@@ -221,7 +237,9 @@
CommandParameter="CardNumber"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyNumber}" />
</Grid>
<BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Card.Number, Converter={StaticResource stringHasValue}}" />
@@ -281,7 +299,9 @@
Command="{Binding ToggleCardCodeCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="&#xf0ea;"
@@ -289,7 +309,9 @@
CommandParameter="CardCode"
Grid.Row="0"
Grid.Column="2"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopySecurityCode}" />
</Grid>
<BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Card.Code, Converter={StaticResource stringHasValue}}" />
@@ -413,7 +435,7 @@
</StackLayout>
<StackLayout StyleClass="box" IsVisible="{Binding ShowUris}">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n URIs}"
<Label Text="{u:I18n URIs, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<controls:RepeaterView ItemsSource="{Binding Cipher.Login.Uris}">
@@ -455,7 +477,9 @@
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
IsVisible="{Binding CanLaunch, Mode=OneWay}" />
IsVisible="{Binding CanLaunch, Mode=OneWay}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Launch}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="&#xf0ea;"
@@ -463,7 +487,9 @@
CommandParameter="{Binding .}"
Grid.Row="0"
Grid.Column="2"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Copy}" />
</Grid>
<BoxView StyleClass="box-row-separator" />
</StackLayout>
@@ -474,7 +500,7 @@
<StackLayout StyleClass="box"
IsVisible="{Binding Cipher.Notes, Converter={StaticResource stringHasValue}}">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n Notes}"
<Label Text="{u:I18n Notes, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<StackLayout StyleClass="box-row">
@@ -491,7 +517,7 @@
</StackLayout>
<StackLayout StyleClass="box" IsVisible="{Binding Cipher.HasFields}">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n CustomFields}"
<Label Text="{u:I18n CustomFields, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<controls:RepeaterView ItemsSource="{Binding Fields}">
@@ -545,7 +571,9 @@
IsVisible="{Binding IsHiddenType}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="&#xf0ea;"
@@ -554,7 +582,9 @@
IsVisible="{Binding ShowCopyButton}"
Grid.Row="0"
Grid.Column="2"
Grid.RowSpan="2" />
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Copy}" />
</Grid>
<BoxView StyleClass="box-row-separator" />
</StackLayout>
@@ -564,7 +594,7 @@
</StackLayout>
<StackLayout StyleClass="box" IsVisible="{Binding ShowAttachments}">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n Attachments}"
<Label Text="{u:I18n Attachments, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<controls:RepeaterView ItemsSource="{Binding Cipher.Attachments}">
@@ -587,7 +617,9 @@
Text="&#xf019;"
Command="{Binding BindingContext.DownloadAttachmentCommand, Source={x:Reference _page}}"
CommandParameter="{Binding .}"
VerticalOptions="Center" />
VerticalOptions="Center"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Download}" />
</StackLayout>
<BoxView StyleClass="box-row-separator" />
</StackLayout>
@@ -632,7 +664,9 @@
x:Name="_fab"
ImageName="pencil.png"
AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="1, 1, AutoSize, AutoSize">
AbsoluteLayout.LayoutBounds="1, 1, AutoSize, AutoSize"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n EditItem}">
</fab:FloatingActionButtonView>
</AbsoluteLayout>

View File

@@ -1,4 +1,5 @@
using Bit.Core.Abstractions;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System.Collections.Generic;
using System.Threading.Tasks;
@@ -25,18 +26,21 @@ namespace Bit.App.Pages
if(Device.RuntimePlatform == Device.iOS)
{
_absLayout.Children.Remove(_fab);
ToolbarItems.RemoveAt(2);
ToolbarItems.RemoveAt(2);
ToolbarItems.Add(_closeItem);
ToolbarItems.Add(_editItem);
ToolbarItems.Add(_moreItem);
}
else
{
ToolbarItems.RemoveAt(0);
ToolbarItems.RemoveAt(0);
_fab.Clicked = EditButton_Clicked;
_mainLayout.Padding = new Thickness(0, 0, 0, 75);
ToolbarItems.Add(_attachmentsItem);
ToolbarItems.Add(_deleteItem);
}
}
public ViewPageViewModel ViewModel => _vm;
protected override async void OnAppearing()
{
base.OnAppearing();
@@ -145,6 +149,40 @@ namespace Bit.App.Pages
}
}
private async void More_Clicked(object sender, System.EventArgs e)
{
if(!DoOnce())
{
return;
}
var options = new List<string> { AppResources.Attachments };
options.Add(_vm.Cipher.OrganizationId == null ? AppResources.Share : AppResources.Collections);
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel,
AppResources.Delete, options.ToArray());
if(selection == AppResources.Delete)
{
if(await _vm.DeleteAsync())
{
await Navigation.PopModalAsync();
}
}
else if(selection == AppResources.Attachments)
{
var page = new AttachmentsPage(_vm.CipherId);
await Navigation.PushModalAsync(new NavigationPage(page));
}
else if(selection == AppResources.Collections)
{
var page = new CollectionsPage(_vm.CipherId);
await Navigation.PushModalAsync(new NavigationPage(page));
}
else if(selection == AppResources.Share)
{
var page = new SharePage(_vm.CipherId);
await Navigation.PushModalAsync(new NavigationPage(page));
}
}
private async void Close_Clicked(object sender, System.EventArgs e)
{
if(DoOnce())

View File

@@ -22,6 +22,7 @@ namespace Bit.App.Pages
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IAuditService _auditService;
private readonly IMessagingService _messagingService;
private readonly IEventService _eventService;
private CipherView _cipher;
private List<ViewPageFieldViewModel> _fields;
private bool _canAccessPremium;
@@ -32,6 +33,7 @@ namespace Bit.App.Pages
private string _totpSec;
private bool _totpLow;
private DateTime? _totpInterval = null;
private string _previousCipherId;
public ViewPageViewModel()
{
@@ -42,6 +44,7 @@ namespace Bit.App.Pages
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
CopyCommand = new Command<string>((id) => CopyAsync(id, null));
CopyUriCommand = new Command<LoginUriView>(CopyUri);
CopyFieldCommand = new Command<FieldView>(CopyField);
@@ -217,7 +220,7 @@ namespace Bit.App.Pages
}
Cipher = await cipher.DecryptAsync();
CanAccessPremium = await _userService.CanAccessPremiumAsync();
Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(f)).ToList();
Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(Cipher, f)).ToList();
if(Cipher.Type == Core.Enums.CipherType.Login && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) &&
(Cipher.OrganizationUseTotp || CanAccessPremium))
@@ -236,6 +239,11 @@ namespace Bit.App.Pages
return true;
});
}
if(_previousCipherId != CipherId)
{
var task = _eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientViewed, CipherId);
}
_previousCipherId = CipherId;
finishedLoadingAction?.Invoke();
return true;
}
@@ -248,11 +256,20 @@ namespace Bit.App.Pages
public void TogglePassword()
{
ShowPassword = !ShowPassword;
if(ShowPassword)
{
var task = _eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientToggledPasswordVisible, CipherId);
}
}
public void ToggleCardCode()
{
ShowCardCode = !ShowCardCode;
if(ShowCardCode)
{
var task = _eventService.CollectAsync(
Core.Enums.EventType.Cipher_ClientToggledCardCodeVisible, CipherId);
}
}
public async Task<bool> DeleteAsync()
@@ -275,7 +292,7 @@ namespace Bit.App.Pages
await _cipherService.DeleteWithServerAsync(Cipher.Id);
await _deviceActionService.HideLoadingAsync();
_platformUtilsService.ShowToast("success", null, AppResources.ItemDeleted);
_messagingService.Send("deletedCipher");
_messagingService.Send("deletedCipher", Cipher);
return true;
}
catch(ApiException e)
@@ -434,7 +451,7 @@ namespace Bit.App.Pages
{
name = AppResources.URI;
}
else if(id == "FieldValue")
else if(id == "FieldValue" || id == "H_FieldValue")
{
name = AppResources.Value;
}
@@ -456,6 +473,18 @@ namespace Bit.App.Pages
{
_platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, name));
}
if(id == "LoginPassword")
{
await _eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedPassword, CipherId);
}
else if(id == "CardCode")
{
await _eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedCardCode, CipherId);
}
else if(id == "H_FieldValue")
{
await _eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedHiddenField, CipherId);
}
}
}
@@ -466,7 +495,7 @@ namespace Bit.App.Pages
private void CopyField(FieldView field)
{
CopyAsync("FieldValue", field.Value);
CopyAsync(field.Type == Core.Enums.FieldType.Hidden ? "H_FieldValue" : "FieldValue", field.Value);
}
private void LaunchUri(LoginUriView uri)
@@ -481,10 +510,12 @@ namespace Bit.App.Pages
public class ViewPageFieldViewModel : ExtendedViewModel
{
private FieldView _field;
private CipherView _cipher;
private bool _showHiddenValue;
public ViewPageFieldViewModel(FieldView field)
public ViewPageFieldViewModel(CipherView cipher, FieldView field)
{
_cipher = cipher;
Field = field;
ToggleHiddenValueCommand = new Command(ToggleHiddenValue);
}
@@ -526,6 +557,12 @@ namespace Bit.App.Pages
public void ToggleHiddenValue()
{
ShowHiddenValue = !ShowHiddenValue;
if(ShowHiddenValue)
{
var eventService = ServiceContainer.Resolve<IEventService>("eventService");
var task = eventService.CollectAsync(
Core.Enums.EventType.Cipher_ClientToggledHiddenFieldVisible, _cipher.Id);
}
}
}
}

View File

@@ -19,7 +19,7 @@ namespace Bit.App.Resources {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class AppResources {
@@ -807,6 +807,15 @@ namespace Bit.App.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Capitalize.
/// </summary>
public static string Capitalize {
get {
return ResourceManager.GetString("Capitalize", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cardholder Name.
/// </summary>
@@ -1239,6 +1248,15 @@ namespace Bit.App.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Download.
/// </summary>
public static string Download {
get {
return ResourceManager.GetString("Download", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Downloading....
/// </summary>
@@ -1932,6 +1950,15 @@ namespace Bit.App.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Include Number.
/// </summary>
public static string IncludeNumber {
get {
return ResourceManager.GetString("IncludeNumber", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Please connect to the internet before continuing..
/// </summary>
@@ -3327,6 +3354,15 @@ namespace Bit.App.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Shared.
/// </summary>
public static string Shared {
get {
return ResourceManager.GetString("Shared", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Choose an organization that you wish to share this item with. Sharing transfers ownership of the item to the organization. You will no longer be the direct owner of this item once it has been shared..
/// </summary>
@@ -3498,6 +3534,15 @@ namespace Bit.App.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Your theme changes will apply when the app is restarted..
/// </summary>
public static string ThemeAppliedOnRestart {
get {
return ResourceManager.GetString("ThemeAppliedOnRestart", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Change the application&apos;s color theme..
/// </summary>
@@ -3525,6 +3570,15 @@ namespace Bit.App.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Toggle Visibility.
/// </summary>
public static string ToggleVisibility {
get {
return ResourceManager.GetString("ToggleVisibility", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Tools.
/// </summary>
@@ -3553,7 +3607,7 @@ namespace Bit.App.Resources {
}
/// <summary>
/// Looks up a localized string similar to Try Again.
/// Looks up a localized string similar to Try again.
/// </summary>
public static string TryAgain {
get {

View File

@@ -1556,4 +1556,23 @@
<data name="AutofillServiceNotEnabled" xml:space="preserve">
<value>Auto-fill makes it easy to securely access your Bitwarden vault from other websites and apps. It looks like you have not enabled an auto-fill service for Bitwarden. Enable auto-fill for Bitwarden from the "Settings" screen.</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Your theme changes will apply when the app is restarted.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Capitalize</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>Include Number</value>
</data>
<data name="Download" xml:space="preserve">
<value>Download</value>
</data>
<data name="Shared" xml:space="preserve">
<value>Shared</value>
</data>
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
</data>
</root>

View File

@@ -1556,4 +1556,23 @@
<data name="AutofillServiceNotEnabled" xml:space="preserve">
<value>Auto-fill makes it easy to securely access your Bitwarden vault from other websites and apps. It looks like you have not enabled an auto-fill service for Bitwarden. Enable auto-fill for Bitwarden from the "Settings" screen.</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Your theme changes will apply when the app is restarted.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Capitalize</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>Include Number</value>
</data>
<data name="Download" xml:space="preserve">
<value>Download</value>
</data>
<data name="Shared" xml:space="preserve">
<value>Shared</value>
</data>
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
</data>
</root>

View File

@@ -412,7 +412,7 @@
<value>Extensió de l'aplicació Bitwarden</value>
</data>
<data name="BitwardenAppExtensionAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is from the Bitwarden App Extension. Learn more about using the Bitwarden App Extension by navigating to the "Settings" screen.</value>
<value>La forma més senzilla d'afegir nous inicis de sessió a la vostra caixa forta és amb l'extensió d'aplicació Bitwarden. Obteniu més informació sobre el seu ús en la pantalla "Eines".</value>
</data>
<data name="BitwardenAppExtensionDescription" xml:space="preserve">
<value>Utilitzeu Bitwarden en Safari i altres aplicacions per omplir automàticament els vostres inicis de sessió.</value>
@@ -753,7 +753,7 @@
<value>Estat</value>
</data>
<data name="BitwardenAutofillServiceAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is from the Bitwarden Auto-fill Service. Learn more about using the Bitwarden Auto-fill Service by navigating to the "Settings" screen.</value>
<value>La forma més senzilla d'afegir nous inicis de sessió a la vostra caixa forta és amb el servei d'emplenament automàtic de Bitwarden. Obteniu més informació sobre com utilitzar el servei en la pantalla "Eines".</value>
</data>
<data name="Autofill" xml:space="preserve">
<value>Emplenament automàtic</value>
@@ -1302,7 +1302,7 @@
<value>Emplenament automàtic de contrasenya</value>
</data>
<data name="BitwardenAutofillAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is by using the Bitwarden Password AutoFill extension. Learn more about using the Bitwarden Password AutoFill extension by navigating to the "Settings" screen.</value>
<value>La forma més senzilla d'afegir nous inicis de sessió a la vostra caixa forta és mitjançant l'extensió Emplenament automàtic de contrasenya de Bitwarden. Obteniu més informació sobre l'ús de l'extensió de Bitwarden a la pantalla "Eines".</value>
</data>
<data name="InvalidEmail" xml:space="preserve">
<value>Ladreça electrònica no és vàlida.</value>
@@ -1323,21 +1323,21 @@
<value>Tots els elements</value>
</data>
<data name="URIs" xml:space="preserve">
<value>URIs</value>
<value>URI</value>
<comment>Plural form of a URI</comment>
</data>
<data name="CheckingPassword" xml:space="preserve">
<value>Comprovant la contrasenya...</value>
<value>S'està comprovant la contrasenya...</value>
<comment>A loading message when doing an exposed password check.</comment>
</data>
<data name="CheckPassword" xml:space="preserve">
<value>Comprova si la contrasenya ha estat exposada.</value>
</data>
<data name="PasswordExposed" xml:space="preserve">
<value>This password has been exposed {0} time(s) in data breaches. You should change it.</value>
<value>Aquesta contrasenya ha estat exposada {0} vegades en filtracions de seguretat de dades. Heu de canviar-la.</value>
</data>
<data name="PasswordSafe" xml:space="preserve">
<value>This password was not found in any known data breaches. It should be safe to use.</value>
<value>Aquesta contrasenya no s'ha trobat en cap filtració de dades coneguda. Hauries de poder utilitzar-la de manera segura.</value>
</data>
<data name="IdentityName" xml:space="preserve">
<value>Nom d'identitat</value>
@@ -1349,13 +1349,13 @@
<value>Historial de les contrasenyes</value>
</data>
<data name="Types" xml:space="preserve">
<value>Tipologies</value>
<value>Tipus</value>
</data>
<data name="NoPasswordsToList" xml:space="preserve">
<value>Sense contrasenyes per mostrar.</value>
<value>No hi ha cap contrasenya per llistar.</value>
</data>
<data name="NoItemsToList" xml:space="preserve">
<value>Sense elements per mostrar.</value>
<value>No hi ha cap element per llistar.</value>
</data>
<data name="SearchCollection" xml:space="preserve">
<value>Cerca a la col·lecció</value>
@@ -1364,19 +1364,19 @@
<value>Cerca a la carpeta</value>
</data>
<data name="SearchType" xml:space="preserve">
<value>Tipologia de cerca</value>
<value>Tipus de cerca</value>
</data>
<data name="Type" xml:space="preserve">
<value>Tipologia</value>
<value>Tipus</value>
</data>
<data name="MoveDown" xml:space="preserve">
<value>Mou avall</value>
<value>Desplaça cap avall</value>
</data>
<data name="MoveUp" xml:space="preserve">
<value>Mou amunt</value>
<value>Desplaça cap amunt</value>
</data>
<data name="Miscellaneous" xml:space="preserve">
<value>Miscel·lània</value>
<value>Diversos</value>
</data>
<data name="Ownership" xml:space="preserve">
<value>Propietat</value>
@@ -1385,10 +1385,10 @@
<value>Qui és el propietari d'aquest element?</value>
</data>
<data name="NoCollectionsToList" xml:space="preserve">
<value>Sense col·leccions per mostrar.</value>
<value>No hi ha cap col·lecció per llistar.</value>
</data>
<data name="ItemShared" xml:space="preserve">
<value>S'ha compartit l'element.</value>
<value>L'element s'ha compartit.</value>
</data>
<data name="SelectOneCollection" xml:space="preserve">
<value>Heu d'escollir com a mínim una col·lecció.</value>
@@ -1400,13 +1400,13 @@
<value>Comparteix l'element</value>
</data>
<data name="NoOrgsToList" xml:space="preserve">
<value>Sense organitzacions per mostrar.</value>
<value>No hi ha cap organització per llistar.</value>
</data>
<data name="ShareDesc" xml:space="preserve">
<value>Choose an organization that you wish to share this item with. Sharing transfers ownership of the item to the organization. You will no longer be the direct owner of this item once it has been shared.</value>
<value>Trieu una organització amb la qual vulgueu compartir aquest element. En compartir transferiu la propietat de l'element a l'organització. Ja no sereu el propietari directe d'aquest element una vegada que s'haja compartit.</value>
</data>
<data name="NumberOfWords" xml:space="preserve">
<value>Nombre de mots</value>
<value>Nombre de paraules</value>
</data>
<data name="Passphrase" xml:space="preserve">
<value>Frase de pas</value>
@@ -1415,7 +1415,7 @@
<value>Separador de paraules</value>
</data>
<data name="Clear" xml:space="preserve">
<value>Neteja</value>
<value>Esborra</value>
<comment>To clear something out. example: To clear browser history.</comment>
</data>
<data name="Generator" xml:space="preserve">
@@ -1423,18 +1423,18 @@
<comment>Short for "Password Generator"</comment>
</data>
<data name="NoFoldersToList" xml:space="preserve">
<value>Sense carpetes per mostrar.</value>
<value>No hi ha carpetes per llistar.</value>
</data>
<data name="FingerprintPhrase" xml:space="preserve">
<value>Frase d'empremta digital</value>
<comment>A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing.</comment>
</data>
<data name="YourAccountsFingerprint" xml:space="preserve">
<value>Your account's fingerprint phrase</value>
<value>Frase d'empremta digital del vostre compte</value>
<comment>A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing.</comment>
</data>
<data name="ShareVaultConfirmation" xml:space="preserve">
<value>Bitwarden allows you to share your vault with others by using an organization account. Would you like to visit the bitwarden.com website to learn more?</value>
<value>Bitwarden us permet compartir la vostra caixa forta amb altres usuaris mitjançant un compte d'organització. Voleu visitar el lloc web de bitwarden.com per obtenir més informació?</value>
</data>
<data name="ExportVault" xml:space="preserve">
<value>Exporta caixa forta</value>
@@ -1458,14 +1458,14 @@
<value>Estableix el teu codi PIN per desbloquejar Bitwarden. La configuració PIN es restablirà si tanqueu sessió de manera completa de l'aplicació.</value>
</data>
<data name="LoggedInAsOn" xml:space="preserve">
<value>Logged in as {0} on {1}.</value>
<value>Heu iniciat la sessió com a {0} en {1}.</value>
<comment>ex: Logged in as user@example.com on bitwarden.com.</comment>
</data>
<data name="VaultLockedMasterPassword" xml:space="preserve">
<value>Your vault is locked. Verify your master password to continue.</value>
<value>La caixa forta està bloquejada. Verifiqueu la contrasenya mestra per continuar.</value>
</data>
<data name="VaultLockedPIN" xml:space="preserve">
<value>Your vault is locked. Verify your PIN code to continue.</value>
<value>La caixa forta està bloquejada. Verifiqueu El codi PIN per continuar.</value>
</data>
<data name="Dark" xml:space="preserve">
<value>Fosc</value>
@@ -1498,15 +1498,15 @@
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data>
<data name="ClearClipboardDescription" xml:space="preserve">
<value>Automatically clear copied values from your clipboard.</value>
<value>Esborra automàticament els valors copiats del porta-retalls.</value>
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data>
<data name="DefaultUriMatchDetection" xml:space="preserve">
<value>Default URI Match Detection</value>
<value>Detecció de coincidències URI per defecte</value>
<comment>Default URI match detection for auto-fill.</comment>
</data>
<data name="DefaultUriMatchDetectionDescription" xml:space="preserve">
<value>Choose the default way that URI match detection is handled for logins when performing actions such as auto-fill.</value>
<value>Trieu la manera predeterminada en que es gestiona la detecció de coincidència d'URI per als inicis de sessió en realitzar accions com l'emplenament automàtic.</value>
</data>
<data name="Theme" xml:space="preserve">
<value>Tema</value>
@@ -1520,40 +1520,59 @@
<comment>Referring to restarting the application.</comment>
</data>
<data name="Restarting" xml:space="preserve">
<value>Reiniciant...</value>
<value>S'està reiniciant...</value>
</data>
<data name="CopyNotes" xml:space="preserve">
<value>Copia notes</value>
</data>
<data name="Exit" xml:space="preserve">
<value>Surt</value>
<value>Tanca</value>
</data>
<data name="ExitConfirmation" xml:space="preserve">
<value>Are you sure you want to exit Bitwarden?</value>
<value>Esteu segur que voleu tancar Bitwarden?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Voleu sol·licitar el desbloqueig amb la vostra contrasenya mestra quan es reinicie laplicació?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Negre</value>
<comment>The color black</comment>
</data>
<data name="BlacklistedUris" xml:space="preserve">
<value>URls de la llista negra</value>
<value>URl a la llista negra</value>
</data>
<data name="BlacklistedUrisDescription" xml:space="preserve">
<value>URIs that are blacklisted will not offer auto-fill. The list of apps should be comma separated. Ex: "https://twitter.com, androidapp://com.twitter.android".</value>
<value>Els URI que es mostren a la llista negra no oferiran lemplenament automàtic. La llista ha de estar separada per comes. Ex: "https://twitter.com, androidapp: //com.twitter.android".</value>
</data>
<data name="DisableSavePrompt" xml:space="preserve">
<value>Disable Save Prompt</value>
<value>Desactiva Guarda elements nous</value>
</data>
<data name="DisableSavePromptDescription" xml:space="preserve">
<value>The "Save Prompt" automatically prompts you to save new items to your vault whenever you enter them for the first time.</value>
<value>"Guarda elements nous" demanarà automàticament que guardeu elements nous al magatzem cada volta que els introduïu per primera vegada.</value>
</data>
<data name="LockOptionOnRestart" xml:space="preserve">
<value>On App Restart</value>
<value>En reiniciar aplicació</value>
</data>
<data name="AutofillServiceNotEnabled" xml:space="preserve">
<value>Auto-fill makes it easy to securely access your Bitwarden vault from other websites and apps. It looks like you have not enabled an auto-fill service for Bitwarden. Enable auto-fill for Bitwarden from the "Settings" screen.</value>
<value>L'emplenament automàtic facilita l'accés de forma segura a la vostra caixa forta de Bitwarden des d'altres llocs web i aplicacions. Sembla que no heu habilitat un servei demplenament automàtic per a Bitwarden. Habiliteu-lo a la pantalla "Configuració".</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Els canvis de tema s'aplicaran quan es reinicie laplicació.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Majúscules inicials</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>Inclou número</value>
</data>
<data name="Download" xml:space="preserve">
<value>Baixa</value>
</data>
<data name="Shared" xml:space="preserve">
<value>Compartit</value>
</data>
<data name="ToggleVisibility" xml:space="preserve">
<value>Commuta la visibilitat</value>
</data>
</root>

View File

@@ -412,7 +412,7 @@
<value>Rozšíření aplikace Bitwarden</value>
</data>
<data name="BitwardenAppExtensionAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is from the Bitwarden App Extension. Learn more about using the Bitwarden App Extension by navigating to the "Settings" screen.</value>
<value>Nejjednodušší způsob přidání nových přihlášení do trezoru je z rozšíření Bitwarden. Další informace o používání rozšíření Bitwarden získáte klepnutím na obrazovku Nastavení.</value>
</data>
<data name="BitwardenAppExtensionDescription" xml:space="preserve">
<value>Použijte Bitwarden v Safari a ostatních prohlížečích pro automatické vyplnění přihlašovacích údajů.</value>
@@ -753,7 +753,7 @@
<value>Stav</value>
</data>
<data name="BitwardenAutofillServiceAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is from the Bitwarden Auto-fill Service. Learn more about using the Bitwarden Auto-fill Service by navigating to the "Settings" screen.</value>
<value>Nejjednodušší způsob, jak přidat nové přihlašovací údaje do trezoru, je pomocí služby automatického vyplňování Bitwarden. Další informace o používání služby automatického vyplňování Bitwarden naleznete na obrazovce Nastavení.</value>
</data>
<data name="Autofill" xml:space="preserve">
<value>Automatické vyplnění</value>
@@ -1302,258 +1302,277 @@
<value>Automatické vyplnění hesel</value>
</data>
<data name="BitwardenAutofillAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is by using the Bitwarden Password AutoFill extension. Learn more about using the Bitwarden Password AutoFill extension by navigating to the "Settings" screen.</value>
<value>Nejjednodušší způsob, jak přidat nové přihlašovací údaje do trezoru, je pomocí rozšíření automatického vyplňování Bitwarden. Další informace o používání rozšíření automatického vyplňování Bitwarden naleznete na obrazovce Nastavení.</value>
</data>
<data name="InvalidEmail" xml:space="preserve">
<value>Invalid email address.</value>
<value>Neplatná e-mailová adresa.</value>
</data>
<data name="Cards" xml:space="preserve">
<value>Cards</value>
<value>Karty</value>
</data>
<data name="Identities" xml:space="preserve">
<value>Identities</value>
<value>Identity</value>
</data>
<data name="Logins" xml:space="preserve">
<value>Logins</value>
<value>Přihlašovací údaje</value>
</data>
<data name="SecureNotes" xml:space="preserve">
<value>Secure Notes</value>
<value>Poznámky</value>
</data>
<data name="AllItems" xml:space="preserve">
<value>All Items</value>
<value>Všechny položky</value>
</data>
<data name="URIs" xml:space="preserve">
<value>URIs</value>
<value>URI</value>
<comment>Plural form of a URI</comment>
</data>
<data name="CheckingPassword" xml:space="preserve">
<value>Checking password...</value>
<value>Prověřování hesla…</value>
<comment>A loading message when doing an exposed password check.</comment>
</data>
<data name="CheckPassword" xml:space="preserve">
<value>Check if password has been exposed.</value>
<value>Zkontrolujte, zda nedošlo k úniku hesla.</value>
</data>
<data name="PasswordExposed" xml:space="preserve">
<value>This password has been exposed {0} time(s) in data breaches. You should change it.</value>
<value>K úniku tohoto hesla došlo celkem {0} krát. Měli byste jej změnit.</value>
</data>
<data name="PasswordSafe" xml:space="preserve">
<value>This password was not found in any known data breaches. It should be safe to use.</value>
<value>Toto heslo nebylo nalezeno v žádném známém úniku dat. Mělo by být bezpečné ho použít.</value>
</data>
<data name="IdentityName" xml:space="preserve">
<value>Identity Name</value>
<value>Název identity</value>
</data>
<data name="Value" xml:space="preserve">
<value>Value</value>
<value>Hodnota</value>
</data>
<data name="PasswordHistory" xml:space="preserve">
<value>Password History</value>
<value>Historie hesel</value>
</data>
<data name="Types" xml:space="preserve">
<value>Types</value>
<value>Typy</value>
</data>
<data name="NoPasswordsToList" xml:space="preserve">
<value>No passwords to list.</value>
<value>Žádná hesla k zobrazení.</value>
</data>
<data name="NoItemsToList" xml:space="preserve">
<value>There are no items to list.</value>
<value>Žádné položky k zobrazení.</value>
</data>
<data name="SearchCollection" xml:space="preserve">
<value>Search collection</value>
<value>Vyledat v kolekci</value>
</data>
<data name="SearchFolder" xml:space="preserve">
<value>Search folder</value>
<value>Vyhledat ve složce</value>
</data>
<data name="SearchType" xml:space="preserve">
<value>Search type</value>
<value>Typ hledání</value>
</data>
<data name="Type" xml:space="preserve">
<value>Type</value>
<value>Typ</value>
</data>
<data name="MoveDown" xml:space="preserve">
<value>Move Down</value>
<value>Přesunout dolů</value>
</data>
<data name="MoveUp" xml:space="preserve">
<value>Move Up</value>
<value>Posunout nahoru</value>
</data>
<data name="Miscellaneous" xml:space="preserve">
<value>Miscellaneous</value>
<value>Různé</value>
</data>
<data name="Ownership" xml:space="preserve">
<value>Ownership</value>
<value>Vlastnictví</value>
</data>
<data name="WhoOwnsThisItem" xml:space="preserve">
<value>Who owns this item?</value>
<value>Kdo vlastní tuto položku?</value>
</data>
<data name="NoCollectionsToList" xml:space="preserve">
<value>There are no collections to list.</value>
<value>Žádné kolekce k zobrazení.</value>
</data>
<data name="ItemShared" xml:space="preserve">
<value>Item has been shared.</value>
<value>Položka byla sdílena.</value>
</data>
<data name="SelectOneCollection" xml:space="preserve">
<value>You must select at least one collection.</value>
<value>Musíte vybrat alespoň jednu kolekci.</value>
</data>
<data name="Share" xml:space="preserve">
<value>Share</value>
<value>Sdílet</value>
</data>
<data name="ShareItem" xml:space="preserve">
<value>Share Item</value>
<value>Sdílet položku</value>
</data>
<data name="NoOrgsToList" xml:space="preserve">
<value>No organizations to list.</value>
<value>Žádné organizace k zobrazení.</value>
</data>
<data name="ShareDesc" xml:space="preserve">
<value>Choose an organization that you wish to share this item with. Sharing transfers ownership of the item to the organization. You will no longer be the direct owner of this item once it has been shared.</value>
<value>Vyberte organizaci se kterou chcete tuto položku sdílet. Sdílení přenese vlastnictví položky na onu organizaci a již nadále nebudete přímým vlastníkem.</value>
</data>
<data name="NumberOfWords" xml:space="preserve">
<value>Number of Words</value>
<value>Počet slov</value>
</data>
<data name="Passphrase" xml:space="preserve">
<value>Passphrase</value>
<value>Heslová fráze</value>
</data>
<data name="WordSeparator" xml:space="preserve">
<value>Word Separator</value>
<value>Oddělovač slov</value>
</data>
<data name="Clear" xml:space="preserve">
<value>Clear</value>
<value>Vymazat</value>
<comment>To clear something out. example: To clear browser history.</comment>
</data>
<data name="Generator" xml:space="preserve">
<value>Generator</value>
<value>Generátor</value>
<comment>Short for "Password Generator"</comment>
</data>
<data name="NoFoldersToList" xml:space="preserve">
<value>There are no folders to list.</value>
<value>Nejsou k dispozici žádné složky k zobrazení.</value>
</data>
<data name="FingerprintPhrase" xml:space="preserve">
<value>Fingerprint Phrase</value>
<value>Fráze otisku prstu</value>
<comment>A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing.</comment>
</data>
<data name="YourAccountsFingerprint" xml:space="preserve">
<value>Your account's fingerprint phrase</value>
<value>Fráze otisku prstu vašeho účtu</value>
<comment>A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing.</comment>
</data>
<data name="ShareVaultConfirmation" xml:space="preserve">
<value>Bitwarden allows you to share your vault with others by using an organization account. Would you like to visit the bitwarden.com website to learn more?</value>
<value>Bitwarden umožňuje sdílet váš trezor s ostatními prostřednictvím účtu organizace. Chcete přejít na bitwarden.com a dozvědět se víc?</value>
</data>
<data name="ExportVault" xml:space="preserve">
<value>Export Vault</value>
<value>Exportovat přihlašovací údaje</value>
</data>
<data name="LockNow" xml:space="preserve">
<value>Lock Now</value>
<value>Zamknout nyní</value>
</data>
<data name="PIN" xml:space="preserve">
<value>PIN</value>
</data>
<data name="Unlock" xml:space="preserve">
<value>Unlock</value>
<value>Odemknout</value>
</data>
<data name="LockOption30Minutes" xml:space="preserve">
<value>30 minutes</value>
<value>Po 30 minutách</value>
</data>
<data name="LockOption5Minutes" xml:space="preserve">
<value>5 minutes</value>
<value>Po 5 minutách</value>
</data>
<data name="SetPINDescription" xml:space="preserve">
<value>Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application.</value>
<value>Nastavte svůj PIN kód pro odemknutí trezoru. Pokud se zcela odhlásíte z aplikace bude váš současný PIN bude resetován.</value>
</data>
<data name="LoggedInAsOn" xml:space="preserve">
<value>Logged in as {0} on {1}.</value>
<value>Přihlášen jako {0} na {1}.</value>
<comment>ex: Logged in as user@example.com on bitwarden.com.</comment>
</data>
<data name="VaultLockedMasterPassword" xml:space="preserve">
<value>Your vault is locked. Verify your master password to continue.</value>
<value>Váš trezor je uzamčen. Pro pokračování musíte zadat hlavní heslo.</value>
</data>
<data name="VaultLockedPIN" xml:space="preserve">
<value>Your vault is locked. Verify your PIN code to continue.</value>
<value>Váš trezor je uzamčen. Pro pokračování musíte zadat PIN.</value>
</data>
<data name="Dark" xml:space="preserve">
<value>Dark</value>
<value>Tmavý</value>
<comment>A dark color</comment>
</data>
<data name="Light" xml:space="preserve">
<value>Light</value>
<value>Světlý</value>
<comment>A light color</comment>
</data>
<data name="FiveMinutes" xml:space="preserve">
<value>5 minutes</value>
<value>5 minut</value>
</data>
<data name="OneMinute" xml:space="preserve">
<value>1 minute</value>
<value>1 minuta</value>
</data>
<data name="TenSeconds" xml:space="preserve">
<value>10 seconds</value>
<value>10 sekund</value>
</data>
<data name="ThirtySeconds" xml:space="preserve">
<value>30 seconds</value>
<value>30 sekund</value>
</data>
<data name="TwentySeconds" xml:space="preserve">
<value>20 seconds</value>
<value>20 sekund</value>
</data>
<data name="TwoMinutes" xml:space="preserve">
<value>2 minutes</value>
<value>2 minuty</value>
</data>
<data name="ClearClipboard" xml:space="preserve">
<value>Clear Clipboard</value>
<value>Vymazat schránku</value>
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data>
<data name="ClearClipboardDescription" xml:space="preserve">
<value>Automatically clear copied values from your clipboard.</value>
<value>Automaticky smazat zkopírované hodnoty z vaší schránky.</value>
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data>
<data name="DefaultUriMatchDetection" xml:space="preserve">
<value>Default URI Match Detection</value>
<value>Výchozí detekce shody URI</value>
<comment>Default URI match detection for auto-fill.</comment>
</data>
<data name="DefaultUriMatchDetectionDescription" xml:space="preserve">
<value>Choose the default way that URI match detection is handled for logins when performing actions such as auto-fill.</value>
<value>Vyberte výchozí způsob, jakým se detekuje shoda URI přihlašovacích údajů. Používá se například pro automatické vyplňování.</value>
</data>
<data name="Theme" xml:space="preserve">
<value>Theme</value>
<value>Motiv</value>
<comment>Color theme</comment>
</data>
<data name="ThemeDescription" xml:space="preserve">
<value>Change the application's color theme.</value>
<value>Změna barevného motivu aplikace.</value>
</data>
<data name="RestartIsRequired" xml:space="preserve">
<value>Restart is required.</value>
<value>Je vyžadován restart.</value>
<comment>Referring to restarting the application.</comment>
</data>
<data name="Restarting" xml:space="preserve">
<value>Restarting...</value>
<value>Restartování…</value>
</data>
<data name="CopyNotes" xml:space="preserve">
<value>Copy Notes</value>
<value>Kopírovat poznámky</value>
</data>
<data name="Exit" xml:space="preserve">
<value>Exit</value>
<value>Ukončit</value>
</data>
<data name="ExitConfirmation" xml:space="preserve">
<value>Are you sure you want to exit Bitwarden?</value>
<value>Opravdu chcete ukončit Bitwarden?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Chcete po restartování aplikace vyžadovat hlavní heslo pro odemknutí trezoru?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Black</value>
<value>Černá</value>
<comment>The color black</comment>
</data>
<data name="BlacklistedUris" xml:space="preserve">
<value>Blacklisted URIs</value>
<value>URI na černé listině</value>
</data>
<data name="BlacklistedUrisDescription" xml:space="preserve">
<value>URIs that are blacklisted will not offer auto-fill. The list of apps should be comma separated. Ex: "https://twitter.com, androidapp://com.twitter.android".</value>
</data>
<data name="DisableSavePrompt" xml:space="preserve">
<value>Disable Save Prompt</value>
<value>Zakázat výzvu o uložení</value>
</data>
<data name="DisableSavePromptDescription" xml:space="preserve">
<value>The "Save Prompt" automatically prompts you to save new items to your vault whenever you enter them for the first time.</value>
<value>"Výzva k uložení" vás automaticky vyzve k uložení nových položek do trezoru, kdykoli je zadáte poprvé.</value>
</data>
<data name="LockOptionOnRestart" xml:space="preserve">
<value>On App Restart</value>
<value>Při restartu aplikace</value>
</data>
<data name="AutofillServiceNotEnabled" xml:space="preserve">
<value>Auto-fill makes it easy to securely access your Bitwarden vault from other websites and apps. It looks like you have not enabled an auto-fill service for Bitwarden. Enable auto-fill for Bitwarden from the "Settings" screen.</value>
<value>Automatické vyplňování usnadňuje bezpečný přístup k trezoru Bitwarden z jiných webových stránek a aplikací. Vypadá to, že jste službu Bitwarden nepovolili. Povolte automatické vyplnění pro Bitwarden z obrazovky "Nastavení".</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Změna motivu se projeví po restartování aplikace.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Velká písmena na začátku slova</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>Zahrnout číslo</value>
</data>
<data name="Download" xml:space="preserve">
<value>Stáhnout</value>
</data>
<data name="Shared" xml:space="preserve">
<value>Sdílené</value>
</data>
<data name="ToggleVisibility" xml:space="preserve">
<value>Přepnout viditelnost</value>
</data>
</root>

View File

@@ -345,7 +345,7 @@
<comment>Label for a uri/url.</comment>
</data>
<data name="UseFingerprintToUnlock" xml:space="preserve">
<value>Benyt fingeraftryk til oplåsning</value>
<value>Lås op med dit fingeraftryk</value>
</data>
<data name="Username" xml:space="preserve">
<value>Brugernavn</value>
@@ -1542,7 +1542,7 @@
<value>Sortlistede URI'er</value>
</data>
<data name="BlacklistedUrisDescription" xml:space="preserve">
<value>Sortlistede URI'er tilbyder ikke autoudfyldning. Listen over apps skal være kommasepareret. F.eks: "https://twitter.com, androidapp: //com.twitter.android".</value>
<value>Sortlistede URI'er tilbyder ikke autoudfyldning. Listen skal være kommasepareret. F.eks: "https://twitter.com, androidapp: //com.twitter.android".</value>
</data>
<data name="DisableSavePrompt" xml:space="preserve">
<value>Deaktivér gem-forespørgsel</value>
@@ -1556,4 +1556,23 @@
<data name="AutofillServiceNotEnabled" xml:space="preserve">
<value>Autoudfyldning gør det nemt at få en sikret adgang til din Bitwarden boks fra andre websteder og apps. Det ser ud til, at du ikke har aktiveret en autoudfyldningstjeneste for Bitwarden. Aktivér autoudfyldning for Bitwarden fra skærmen "Indstillinger".</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Dine temaændringer effektueres efter appen genstartes.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Stort begyndelsesbogstav</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>Inkludér ciffer</value>
</data>
<data name="Download" xml:space="preserve">
<value>Download</value>
</data>
<data name="Shared" xml:space="preserve">
<value>Delt</value>
</data>
<data name="ToggleVisibility" xml:space="preserve">
<value>Slå synlighed til/fra</value>
</data>
</root>

View File

@@ -1379,7 +1379,7 @@
<value>Verschiedenes</value>
</data>
<data name="Ownership" xml:space="preserve">
<value>Besitz</value>
<value>Besitzer</value>
</data>
<data name="WhoOwnsThisItem" xml:space="preserve">
<value>Wer besitzt diesen Eintrag?</value>
@@ -1556,4 +1556,23 @@
<data name="AutofillServiceNotEnabled" xml:space="preserve">
<value>Durch automatisches Füllen ist es leicht, auf Ihren Bitwarden Tresor von anderen Webseiten und Apps sicher zuzugreifen. Es sieht aus, als ob Sie den Automatisches Fülldienst für Bitwarden nicht aktiviert haben. Aktivieren Sie automatisches Füllen im "Einstellungen"-Bildschirm.</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Ihre Änderungen am Aussehen der App werden beim nächsten Neustart der App angewendet.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Großschreiben</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>Ziffer hinzufügen</value>
</data>
<data name="Download" xml:space="preserve">
<value>Herunterladen</value>
</data>
<data name="Shared" xml:space="preserve">
<value>Geteilt</value>
</data>
<data name="ToggleVisibility" xml:space="preserve">
<value>Sichtbarkeit umschalten</value>
</data>
</root>

View File

@@ -1556,4 +1556,23 @@
<data name="AutofillServiceNotEnabled" xml:space="preserve">
<value>Auto-fill makes it easy to securely access your Bitwarden vault from other websites and apps. It looks like you have not enabled an auto-fill service for Bitwarden. Enable auto-fill for Bitwarden from the "Settings" screen.</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Your theme changes will apply when the app is restarted.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Capitalise</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>Include number</value>
</data>
<data name="Download" xml:space="preserve">
<value>Download</value>
</data>
<data name="Shared" xml:space="preserve">
<value>Shared</value>
</data>
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle visibility</value>
</data>
</root>

View File

@@ -1302,7 +1302,7 @@
<value>Autorellenado de contraseña</value>
</data>
<data name="BitwardenAutofillAlert2" xml:space="preserve">
<value>La forma más facil de añadir nuevas entradas a tu caja fuerte es usando la extensión de Autorellenado de contraseñas Bitwarden. Aprende más sobre como utilizar la extensión de Autorellenado de contraseñas Bitwarden yendo a la pantalla de Herramientas.</value>
<value>La forma más fácil de añadir nuevas entradas a tu caja fuerte es usando la extensión de Autorellenado de contraseñas Bitwarden. Aprende más sobre como utilizar la extensión de Autorellenado de contraseñas Bitwarden yendo a la pantalla de Herramientas.</value>
</data>
<data name="InvalidEmail" xml:space="preserve">
<value>Dirección de correo electrónico inválida.</value>
@@ -1434,10 +1434,10 @@
<comment>A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing.</comment>
</data>
<data name="ShareVaultConfirmation" xml:space="preserve">
<value>Bitwarden te permite compartir tu bóveda con otras personas utilizando una cuenta de organización. ¿Quieres visitar la web bitwarden.com para conocer más?</value>
<value>Bitwarden te permite compartir tu caja fuerte con otras personas utilizando una cuenta de organización. ¿Quieres visitar la web bitwarden.com para conocer más?</value>
</data>
<data name="ExportVault" xml:space="preserve">
<value>Exportar bóveda</value>
<value>Exportar caja fuerte</value>
</data>
<data name="LockNow" xml:space="preserve">
<value>Bloquear ahora</value>
@@ -1462,10 +1462,10 @@
<comment>ex: Logged in as user@example.com on bitwarden.com.</comment>
</data>
<data name="VaultLockedMasterPassword" xml:space="preserve">
<value>Tu bóveda está bloqueada. Verifica tu contraseña maestra para continuar.</value>
<value>Tu caja fuerte está bloqueada. Verifica tu contraseña maestra para continuar.</value>
</data>
<data name="VaultLockedPIN" xml:space="preserve">
<value>Tu bóveda está bloqueada. Verifica tu código PIN para continuar.</value>
<value>Tu caja fuerte está bloqueada. Verifica tu código PIN para continuar.</value>
</data>
<data name="Dark" xml:space="preserve">
<value>Oscuro</value>
@@ -1554,6 +1554,25 @@
<value>Al reiniciar la App</value>
</data>
<data name="AutofillServiceNotEnabled" xml:space="preserve">
<value>El auto-relleno facilita el acceso seguro a la caja fuerte de Bitwarden desde otros sitios web y aplicaciones. Parece que no has habilitado un servicio de autocompletado para Bitwarden. Habilita el autocompletado para Bitwarden desde la pantalla "Ajustes".</value>
<value>El autocompletado facilita el acceso seguro a la caja fuerte de Bitwarden desde otros sitios web y aplicaciones. Parece que no has habilitado un servicio de autocompletado para Bitwarden. Habilita el autocompletado para Bitwarden desde la pantalla "Ajustes".</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Los cambios de tu tema se aplicarán al reiniciar la aplicación.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Mayúsculas iniciales</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>Incluir número</value>
</data>
<data name="Download" xml:space="preserve">
<value>Descargar</value>
</data>
<data name="Shared" xml:space="preserve">
<value>Compartido</value>
</data>
<data name="ToggleVisibility" xml:space="preserve">
<value>Alternar visibilidad</value>
</data>
</root>

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