mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
Compare commits
195 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7819838b8 | ||
|
|
67c6cf6b8c | ||
|
|
d91d71333b | ||
|
|
431804ea80 | ||
|
|
7a7ab7bd0e | ||
|
|
b8cdee383b | ||
|
|
580fa02ee1 | ||
|
|
421834153d | ||
|
|
011f04e1dc | ||
|
|
3d8056704c | ||
|
|
41263f3419 | ||
|
|
9fe9210cb7 | ||
|
|
2272b10820 | ||
|
|
9d6fc73fcc | ||
|
|
73ecd67b20 | ||
|
|
e5ce3dbd32 | ||
|
|
a0a5e30f48 | ||
|
|
0eddee5816 | ||
|
|
6d2dcb73ae | ||
|
|
0d6cc91b67 | ||
|
|
ae52922698 | ||
|
|
236496e69f | ||
|
|
fe5cdb0004 | ||
|
|
f9547f158e | ||
|
|
0c75374c0f | ||
|
|
0b249d4dd4 | ||
|
|
a2bac9d368 | ||
|
|
392e429dfd | ||
|
|
50623b9b29 | ||
|
|
e7ce050324 | ||
|
|
762b574d49 | ||
|
|
d73bf6d225 | ||
|
|
c2108fdda0 | ||
|
|
2062a284e3 | ||
|
|
9164c9b946 | ||
|
|
13ddd10c40 | ||
|
|
e407acd2a7 | ||
|
|
11cdf52ec8 | ||
|
|
40a3541e8e | ||
|
|
7da13e22ad | ||
|
|
38d702b6fe | ||
|
|
df2af5459e | ||
|
|
40d68b1654 | ||
|
|
a240a4ac66 | ||
|
|
416ec3812d | ||
|
|
ff24891903 | ||
|
|
ddcbe298ac | ||
|
|
6d8f647aee | ||
|
|
a654987175 | ||
|
|
a5f960d8a1 | ||
|
|
1f707cda68 | ||
|
|
4e7f195fd2 | ||
|
|
7728e930be | ||
|
|
ab84200347 | ||
|
|
62d8824450 | ||
|
|
cf35d20adb | ||
|
|
65725b5a38 | ||
|
|
eca4777b99 | ||
|
|
066b3aba5b | ||
|
|
8e485ff26f | ||
|
|
341b66f44f | ||
|
|
19c62d3320 | ||
|
|
13ffbd7675 | ||
|
|
9af6aae699 | ||
|
|
2e562e8318 | ||
|
|
c6db763716 | ||
|
|
a3383af4ae | ||
|
|
6c56e44b61 | ||
|
|
64506a7080 | ||
|
|
fac9ae4b6c | ||
|
|
a2dc73afef | ||
|
|
59c5a34cd0 | ||
|
|
3321e6b0e2 | ||
|
|
8b7ac179fa | ||
|
|
ea745665c8 | ||
|
|
ca8f6ee10b | ||
|
|
3e51ff46f3 | ||
|
|
fa2e814559 | ||
|
|
87e337cbeb | ||
|
|
abb39df547 | ||
|
|
43e15bf911 | ||
|
|
4d79d0af89 | ||
|
|
69100d7db5 | ||
|
|
a064a6cf9b | ||
|
|
7953a9a3ce | ||
|
|
be3c6f210d | ||
|
|
f7cbddab4b | ||
|
|
d423818764 | ||
|
|
519acd43aa | ||
|
|
2682a0d9e4 | ||
|
|
8629ae048c | ||
|
|
905d01e804 | ||
|
|
0588bbc41d | ||
|
|
b308b4c54f | ||
|
|
c2c73d5460 | ||
|
|
e01bf57874 | ||
|
|
7ced93225b | ||
|
|
b5e61864af | ||
|
|
1e5aaea8f4 | ||
|
|
ab3bebf06a | ||
|
|
4a294d6a77 | ||
|
|
e0fda1a0bc | ||
|
|
d17da80f19 | ||
|
|
2e7658f857 | ||
|
|
53d0b28c7c | ||
|
|
33ba4d3871 | ||
|
|
225db6397d | ||
|
|
73b5d1b3f1 | ||
|
|
8da2eac6d0 | ||
|
|
fbd62153ee | ||
|
|
9145fa1c48 | ||
|
|
caa0af1258 | ||
|
|
7a230ee5f5 | ||
|
|
f237fa98d2 | ||
|
|
be4ae605a9 | ||
|
|
9c2cbc0ecb | ||
|
|
fb3009fc66 | ||
|
|
04c32e28cd | ||
|
|
645576c949 | ||
|
|
775bee3546 | ||
|
|
88aea96034 | ||
|
|
5f474dfaf5 | ||
|
|
fe7aad0835 | ||
|
|
79746efa2d | ||
|
|
a158021f46 | ||
|
|
2d91a893f7 | ||
|
|
dd4561d985 | ||
|
|
92764eeae0 | ||
|
|
b72808ab40 | ||
|
|
14f3f99218 | ||
|
|
d7130d9b67 | ||
|
|
3f94eee4d5 | ||
|
|
72cbdcbc8d | ||
|
|
e33b49e78c | ||
|
|
8e04945d4e | ||
|
|
3ca5da55cb | ||
|
|
ea30373a09 | ||
|
|
4b4757d0e5 | ||
|
|
4bc837509d | ||
|
|
c9d1e8dc65 | ||
|
|
88b8a192b5 | ||
|
|
94fbf627ba | ||
|
|
45fbdb8411 | ||
|
|
d9c947ccd0 | ||
|
|
2b670a5ae1 | ||
|
|
1af0178b50 | ||
|
|
3ec5d894b3 | ||
|
|
d81585ccc3 | ||
|
|
38f91bce1c | ||
|
|
2d41dd6ae0 | ||
|
|
1705a21f68 | ||
|
|
164d79898a | ||
|
|
50f809d290 | ||
|
|
39284b475d | ||
|
|
d44950d46c | ||
|
|
e9b55bc207 | ||
|
|
5470f08fee | ||
|
|
f9a3bbd7fa | ||
|
|
9d3165dc65 | ||
|
|
3475d39f37 | ||
|
|
44782b1ddf | ||
|
|
dd8d5fd84c | ||
|
|
e8f2d9d0dd | ||
|
|
a2de3b5d80 | ||
|
|
a2960c45bc | ||
|
|
dc91624597 | ||
|
|
223ec180fc | ||
|
|
0116572fec | ||
|
|
5350e5385c | ||
|
|
8f18c4fd45 | ||
|
|
8538fbabe5 | ||
|
|
9367b34bbe | ||
|
|
a766044cb4 | ||
|
|
0eb385e49f | ||
|
|
e30136dace | ||
|
|
c50dee479a | ||
|
|
58ef292fa7 | ||
|
|
61b728fea7 | ||
|
|
b782eeb839 | ||
|
|
77314d4b8d | ||
|
|
c79d1d24b3 | ||
|
|
5dbea8ca09 | ||
|
|
09a1c17fb4 | ||
|
|
a0632bcac2 | ||
|
|
07d57ebe8c | ||
|
|
325c88018c | ||
|
|
dcb1102746 | ||
|
|
636d3c02c4 | ||
|
|
5b119ded17 | ||
|
|
f25ae870c5 | ||
|
|
49673262e4 | ||
|
|
3ed814c1f7 | ||
|
|
8b858e5407 | ||
|
|
30145894b6 | ||
|
|
8df4c27203 |
111
.editorconfig
111
.editorconfig
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
16
src/Android/Receivers/EventUploadReceiver.cs
Normal file
16
src/Android/Receivers/EventUploadReceiver.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<network-security-config>
|
||||
<base-config>
|
||||
<base-config cleartextTrafficPermitted="true">
|
||||
<trust-anchors>
|
||||
<!-- Trust pre-installed CAs -->
|
||||
<certificates src="system" />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
19
src/Android/Utilities/StaticStore.cs
Normal file
19
src/Android/Utilities/StaticStore.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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=""
|
||||
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=""
|
||||
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=""
|
||||
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=""
|
||||
IsVisible="{Binding Cipher.HasAttachments, Mode=OneWay}"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n Attachments}" />
|
||||
</Grid>
|
||||
|
||||
<controls:MiButton
|
||||
Text=""
|
||||
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=""
|
||||
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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
9
src/App/Models/PreviousPageInfo.cs
Normal file
9
src/App/Models/PreviousPageInfo.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,16 @@
|
||||
<controls:FaButton Text=""
|
||||
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"
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
48
src/App/Pages/Settings/AutofillPage.xaml
Normal file
48
src/App/Pages/Settings/AutofillPage.xaml
Normal 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>
|
||||
20
src/App/Pages/Settings/AutofillPage.xaml.cs
Normal file
20
src/App/Pages/Settings/AutofillPage.xaml.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
95
src/App/Pages/Settings/ExtensionPage.xaml
Normal file
95
src/App/Pages/Settings/ExtensionPage.xaml
Normal 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>
|
||||
38
src/App/Pages/Settings/ExtensionPage.xaml.cs
Normal file
38
src/App/Pages/Settings/ExtensionPage.xaml.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
75
src/App/Pages/Settings/ExtensionPageViewModel.cs
Normal file
75
src/App/Pages/Settings/ExtensionPageViewModel.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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=""
|
||||
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=""
|
||||
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=""
|
||||
@@ -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"
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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=""
|
||||
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">
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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=""
|
||||
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=""
|
||||
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=""
|
||||
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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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=""
|
||||
@@ -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=""
|
||||
@@ -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=""
|
||||
@@ -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=""
|
||||
@@ -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=""
|
||||
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>
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
58
src/App/Resources/AppResources.Designer.cs
generated
58
src/App/Resources/AppResources.Designer.cs
generated
@@ -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'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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>L’adreç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 l’aplicació?</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 l’emplenament 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 d’emplenament 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 l’aplicació.</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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user