Compare commits
393 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fcf006484 | ||
|
|
48e5b1686f | ||
|
|
608d879c80 | ||
|
|
66055f1d7c | ||
|
|
1120bff34d | ||
|
|
24547e67bf | ||
|
|
f8c7285f56 | ||
|
|
82eb6a4568 | ||
|
|
a3f1f7c78d | ||
|
|
c377c4a52b | ||
|
|
e5a74cf43c | ||
|
|
ccf2bf84da | ||
|
|
fbf3d97d57 | ||
|
|
6da0f82ddd | ||
|
|
4c3df2e1e1 | ||
|
|
39e10ff01c | ||
|
|
0b29c6e5a4 | ||
|
|
cd3585be58 | ||
|
|
4e1f91f4d5 | ||
|
|
272c2e5303 | ||
|
|
2a82b09f7b | ||
|
|
fd26492577 | ||
|
|
528b90b694 | ||
|
|
9c7961ff6b | ||
|
|
954ed6457c | ||
|
|
5a8fc2dabc | ||
|
|
ce965ba5e1 | ||
|
|
4b9a036e5e | ||
|
|
4576f378cc | ||
|
|
4c65daa995 | ||
|
|
9159e14dd9 | ||
|
|
da661c229c | ||
|
|
973f09f98a | ||
|
|
fef370ad88 | ||
|
|
bc46b7172d | ||
|
|
71f546b467 | ||
|
|
a66f66c8ac | ||
|
|
c2c6ca22db | ||
|
|
b29440556a | ||
|
|
4104f6f772 | ||
|
|
01c56dabdf | ||
|
|
fafd8f8ee6 | ||
|
|
e2033eee23 | ||
|
|
780761664d | ||
|
|
0cfa737eff | ||
|
|
6463898c5d | ||
|
|
7d4fffa8b6 | ||
|
|
bcc415ccb3 | ||
|
|
827fead347 | ||
|
|
36cdc7dd1c | ||
|
|
99dceda8ac | ||
|
|
9d27f111bf | ||
|
|
69e0906491 | ||
|
|
1d48171fd5 | ||
|
|
cb0a3e3edf | ||
|
|
2b3915a91f | ||
|
|
9a403ba0ed | ||
|
|
0f35885d1c | ||
|
|
e3e07b6bfe | ||
|
|
84a6d1db71 | ||
|
|
8cee50299f | ||
|
|
5a78cbef02 | ||
|
|
ae66a781d1 | ||
|
|
5ae3b66e06 | ||
|
|
c3f1cee5d6 | ||
|
|
5552c42e37 | ||
|
|
6f146b888b | ||
|
|
56c09eae90 | ||
|
|
6883864e2d | ||
|
|
15bc395454 | ||
|
|
41997d5fe0 | ||
|
|
ed259cd130 | ||
|
|
1dc027cf49 | ||
|
|
b2abcda111 | ||
|
|
d66eaf8855 | ||
|
|
78cfd82fdd | ||
|
|
8ad44b405d | ||
|
|
4ce4288f68 | ||
|
|
d635555576 | ||
|
|
44999557c0 | ||
|
|
5d64bab719 | ||
|
|
4d3d8b643a | ||
|
|
915e8cf072 | ||
|
|
3c18fd7636 | ||
|
|
6c00ac43fc | ||
|
|
5d9a597d8d | ||
|
|
92930955c3 | ||
|
|
145482ea30 | ||
|
|
6fdb1e3356 | ||
|
|
55dff81b9f | ||
|
|
ed37972b99 | ||
|
|
c6b37307b0 | ||
|
|
5d719ba235 | ||
|
|
c19795cce0 | ||
|
|
df8f44d77d | ||
|
|
a4eff45534 | ||
|
|
b2b12be3b0 | ||
|
|
9c77c53366 | ||
|
|
1449f165dd | ||
|
|
94216cf745 | ||
|
|
120e179fb8 | ||
|
|
f10114ee17 | ||
|
|
8a059e0fbb | ||
|
|
6263788d6a | ||
|
|
6ffb3136d4 | ||
|
|
b65b01fe3d | ||
|
|
b9c134654f | ||
|
|
d1a1342587 | ||
|
|
1ec2ac472a | ||
|
|
9894322c17 | ||
|
|
7edbf4ffc8 | ||
|
|
2b1d186611 | ||
|
|
70c49922b0 | ||
|
|
30d6a4d9eb | ||
|
|
033b2b9ba0 | ||
|
|
25aec80e4c | ||
|
|
cf3d52772d | ||
|
|
f78f303a79 | ||
|
|
02cffa01e2 | ||
|
|
c2f2a5e52f | ||
|
|
d8e19415e3 | ||
|
|
387dc2f59c | ||
|
|
ec3660a86d | ||
|
|
36fb23d467 | ||
|
|
33df456cfd | ||
|
|
7a6fe5ed5f | ||
|
|
558b10499b | ||
|
|
1fb3698ba2 | ||
|
|
89f26bbc6b | ||
|
|
bbd8615cda | ||
|
|
93132f5d7b | ||
|
|
179514ddf1 | ||
|
|
4b9cff2271 | ||
|
|
c3649a9c80 | ||
|
|
9a66b9003f | ||
|
|
34e32403b0 | ||
|
|
c2e34a8b0e | ||
|
|
d0ba4b6702 | ||
|
|
eb16025800 | ||
|
|
9f06c9a051 | ||
|
|
641122b16f | ||
|
|
fbe8708378 | ||
|
|
21c7b486ff | ||
|
|
c33728d418 | ||
|
|
a9dacd561c | ||
|
|
051e15215d | ||
|
|
fee8f58c0a | ||
|
|
cc036cf3c5 | ||
|
|
4e51517ddb | ||
|
|
3b7454961d | ||
|
|
88009e1a63 | ||
|
|
0afca29b0c | ||
|
|
46a75a2944 | ||
|
|
c099f82752 | ||
|
|
1da94bd9c8 | ||
|
|
96ce8165e9 | ||
|
|
f9b617339d | ||
|
|
58084810f3 | ||
|
|
429e62e6b5 | ||
|
|
b0b7f2afdf | ||
|
|
55f160d125 | ||
|
|
f6352f5392 | ||
|
|
ac7e90c0aa | ||
|
|
88fccfd6cd | ||
|
|
5fdf8e6045 | ||
|
|
d9907cdbeb | ||
|
|
d308f1ca3b | ||
|
|
0cdc138ba3 | ||
|
|
59d5314164 | ||
|
|
9c08a37772 | ||
|
|
b13f5356fe | ||
|
|
5f0c9725ce | ||
|
|
f951fea555 | ||
|
|
4b989b01e9 | ||
|
|
aed3ec5474 | ||
|
|
b354986199 | ||
|
|
e1983a7d66 | ||
|
|
0400d79f43 | ||
|
|
c911484632 | ||
|
|
713e441d2e | ||
|
|
d4b577732b | ||
|
|
440a410d7f | ||
|
|
37a536b138 | ||
|
|
a0aca3e837 | ||
|
|
b58c29111a | ||
|
|
b0f86ea161 | ||
|
|
93b59a75a4 | ||
|
|
54fcabaea6 | ||
|
|
0e966c0304 | ||
|
|
a363712127 | ||
|
|
4d8c665917 | ||
|
|
53bdd92e72 | ||
|
|
e51aa39ede | ||
|
|
33c82129ff | ||
|
|
7c5b8c0e9f | ||
|
|
9dc01bca1c | ||
|
|
3c7920b84c | ||
|
|
b92f3abbaf | ||
|
|
b6747a63ed | ||
|
|
41a44548d2 | ||
|
|
a79d3a0d7c | ||
|
|
f3a17709e5 | ||
|
|
ced9d33d2e | ||
|
|
23b1373f80 | ||
|
|
a80eb1f533 | ||
|
|
f657edf195 | ||
|
|
d34279dca5 | ||
|
|
954aa1112a | ||
|
|
b35a3339cb | ||
|
|
b59433debd | ||
|
|
fb2db9c652 | ||
|
|
2507f3301b | ||
|
|
bdad5e4f0a | ||
|
|
b5dcdc74d7 | ||
|
|
e2d1da02d3 | ||
|
|
8253f18312 | ||
|
|
f4a98a2031 | ||
|
|
224845cfd3 | ||
|
|
fc8c2ad67a | ||
|
|
c9d6f58563 | ||
|
|
325b557506 | ||
|
|
ce751cfc87 | ||
|
|
0f451fd4b9 | ||
|
|
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 |
109
.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
|
# All files
|
||||||
[*]
|
[*]
|
||||||
guidelines = 120
|
guidelines = 120
|
||||||
20
.gitignore
vendored
@@ -1,15 +1,28 @@
|
|||||||
## Ignore Visual Studio temporary files, build results, and
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
## files generated by popular Visual Studio add-ons.
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
# Visual Studio (>=2015) project-specific, machine local files
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# JetBrains Rider project-specific, machine local files
|
||||||
|
.idea
|
||||||
|
|
||||||
# User-specific files
|
# User-specific files
|
||||||
*.suo
|
*.suo
|
||||||
*.user
|
*.user
|
||||||
*.userosscache
|
*.userosscache
|
||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
|
||||||
*.userprefs
|
*.userprefs
|
||||||
|
|
||||||
|
# ignore Xamarin.Android Resource.Designer.cs files
|
||||||
|
**/*.Droid/**/[Rr]esource.[Dd]esigner.cs
|
||||||
|
**/*.Android/**/[Rr]esource.[Dd]esigner.cs
|
||||||
|
**/Android/**/[Rr]esource.[Dd]esigner.cs
|
||||||
|
**/Droid/**/[Rr]esource.[Dd]esigner.cs
|
||||||
|
|
||||||
|
# Xamarin Components
|
||||||
|
Components/
|
||||||
|
|
||||||
# Build results
|
# Build results
|
||||||
[Dd]ebug/
|
[Dd]ebug/
|
||||||
[Dd]ebugPublic/
|
[Dd]ebugPublic/
|
||||||
@@ -22,9 +35,6 @@ bld/
|
|||||||
[Bb]in/
|
[Bb]in/
|
||||||
[Oo]bj/
|
[Oo]bj/
|
||||||
|
|
||||||
# Visual Studo 2015 cache/options directory
|
|
||||||
.vs/
|
|
||||||
|
|
||||||
# MSTest test Results
|
# MSTest test Results
|
||||||
[Tt]est[Rr]esult*/
|
[Tt]est[Rr]esult*/
|
||||||
[Bb]uild[Ll]og.*
|
[Bb]uild[Ll]og.*
|
||||||
|
|||||||
@@ -1,5 +1,53 @@
|
|||||||
<!--
|
<!-- Comment:
|
||||||
Please do not submit feature requests. The [Community Forums][1] has a
|
Please do not submit feature requests. The [Community Forums][1] has a
|
||||||
section for submitting, voting for, and discussing product feature requests.
|
section for submitting, voting for, and discussing product feature requests.
|
||||||
[1]: https://community.bitwarden.com
|
[1]: https://community.bitwarden.com
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## Describe the Bug
|
||||||
|
|
||||||
|
<!-- Comment:
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Steps To Reproduce
|
||||||
|
|
||||||
|
<!-- Comment:
|
||||||
|
How can we reproduce the behavior:
|
||||||
|
-->
|
||||||
|
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. Click on '...'
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
<!-- Comment:
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
<!-- Comment:
|
||||||
|
A clear and concise description of what is happening.
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Screenshots or Videos
|
||||||
|
|
||||||
|
<!-- Comment:
|
||||||
|
If applicable, add screenshots and/or a short video to help explain your problem.
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
- Device: [e.g. iPhone6]
|
||||||
|
- Operating system: [e.g. iOS 8.1]
|
||||||
|
- Build Version (go to "Settings" → "About" in the app): [e.g. 2.3.0 (2221)]
|
||||||
|
- Is this a Beta release? [Y/N]
|
||||||
|
|
||||||
|
## Additional Context
|
||||||
|
|
||||||
|
<!-- Comment:
|
||||||
|
Add any other context about the problem here.
|
||||||
|
-->
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
The Bitwarden mobile application is written in C# with Xamarin Android, Xamarin iOS, and Xamarin Forms.
|
The Bitwarden mobile application is written in C# with Xamarin Android, Xamarin iOS, and Xamarin Forms.
|
||||||
|
|
||||||
<img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-android-myvault.png" alt="" width="300" height="533" /> <img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-ios.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
|
# Build/Run
|
||||||
|
|
||||||
|
|||||||
11
appveyor.yml
@@ -1,5 +1,5 @@
|
|||||||
image:
|
image:
|
||||||
- Visual Studio 2017
|
- Visual Studio 2019
|
||||||
- Ubuntu1804
|
- Ubuntu1804
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
@@ -55,7 +55,7 @@ before_build:
|
|||||||
- ps: |
|
- ps: |
|
||||||
if($isWindows) {
|
if($isWindows) {
|
||||||
nuget restore
|
nuget restore
|
||||||
if($env:KEYSTORE_DEC_SECRET -or $env:GOOGLE_SERVICES_DEC_SECRET -or $env:PLAY_DEC_SECRET) {
|
if($env:UPLOAD_KEYSTORE_DEC_SECRET -or$env:KEYSTORE_DEC_SECRET -or $env:GOOGLE_SERVICES_DEC_SECRET -or $env:PLAY_DEC_SECRET) {
|
||||||
nuget install secure-file -ExcludeVersion
|
nuget install secure-file -ExcludeVersion
|
||||||
}
|
}
|
||||||
if($env:GOOGLE_SERVICES_DEC_SECRET) {
|
if($env:GOOGLE_SERVICES_DEC_SECRET) {
|
||||||
@@ -95,7 +95,10 @@ build_script:
|
|||||||
msbuild bitwarden-mobile.sln `
|
msbuild bitwarden-mobile.sln `
|
||||||
"/logger:C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" `
|
"/logger:C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" `
|
||||||
"/p:Configuration=Release"
|
"/p:Configuration=Release"
|
||||||
|
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
|
||||||
.\src\Android\ci-build-apks.ps1
|
.\src\Android\ci-build-apks.ps1
|
||||||
|
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
|
||||||
|
Push-AppveyorArtifact .\com.x8bit.bitwarden.aab
|
||||||
Push-AppveyorArtifact .\com.x8bit.bitwarden.apk
|
Push-AppveyorArtifact .\com.x8bit.bitwarden.apk
|
||||||
Push-AppveyorArtifact .\com.x8bit.bitwarden-fdroid.apk
|
Push-AppveyorArtifact .\com.x8bit.bitwarden-fdroid.apk
|
||||||
}
|
}
|
||||||
@@ -107,12 +110,12 @@ on_success:
|
|||||||
npm run deploy
|
npm run deploy
|
||||||
fi
|
fi
|
||||||
- ps: |
|
- ps: |
|
||||||
if($isWindows -and $env:PLAY_DEC_SECRET) {
|
if($isWindows -and $env:PLAY_DEC_SECRET -and $env:APPVEYOR_REPO_BRANCH -eq 'master') {
|
||||||
secure-file\tools\secure-file -decrypt store\google\Publisher\play_creds.json.enc -secret $env:PLAY_DEC_SECRET
|
secure-file\tools\secure-file -decrypt store\google\Publisher\play_creds.json.enc -secret $env:PLAY_DEC_SECRET
|
||||||
cd store\google\Publisher\bin\Release\netcoreapp2.0
|
cd store\google\Publisher\bin\Release\netcoreapp2.0
|
||||||
dotnet Publisher.dll `
|
dotnet Publisher.dll `
|
||||||
$env:APPVEYOR_BUILD_FOLDER\store\google\Publisher\play_creds.json `
|
$env:APPVEYOR_BUILD_FOLDER\store\google\Publisher\play_creds.json `
|
||||||
$env:APPVEYOR_BUILD_FOLDER\com.x8bit.bitwarden.apk `
|
$env:APPVEYOR_BUILD_FOLDER\com.x8bit.bitwarden.aab `
|
||||||
alpha
|
alpha
|
||||||
cd $env:APPVEYOR_BUILD_FOLDER
|
cd $env:APPVEYOR_BUILD_FOLDER
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio Version 16
|
||||||
VisualStudioVersion = 15.0.28307.539
|
VisualStudioVersion = 16.0.29009.5
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Android", "src\Android\Android.csproj", "{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Android", "src\Android\Android.csproj", "{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}"
|
||||||
EndProject
|
EndProject
|
||||||
@@ -21,6 +21,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "google", "google", "{2E3996
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{76690DFB-B7F4-4781-83E4-113FDC450AFE}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{76690DFB-B7F4-4781-83E4-113FDC450AFE}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.editorconfig = .editorconfig
|
||||||
.gitignore = .gitignore
|
.gitignore = .gitignore
|
||||||
appveyor.yml = appveyor.yml
|
appveyor.yml = appveyor.yml
|
||||||
CONTRIBUTING.md = CONTRIBUTING.md
|
CONTRIBUTING.md = CONTRIBUTING.md
|
||||||
@@ -35,6 +36,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Core", "src\iOS.Core\iO
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS", "src\iOS\iOS.csproj", "{599E0201-420A-4C3E-A7BA-5349F72E0B15}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS", "src\iOS\iOS.csproj", "{599E0201-420A-4C3E-A7BA-5349F72E0B15}"
|
||||||
EndProject
|
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
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
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.ActiveCfg = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhoneSimulator.Build.0 = 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
|
{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.ActiveCfg = Release|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.Build.0 = Release|Any CPU
|
||||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.Deploy.0 = Debug|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 = Debug|Any CPU
|
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.Build.0 = Debug|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 = Debug|Any CPU
|
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.Deploy.0 = Release|Any CPU
|
||||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|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 = Debug|Any CPU
|
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
||||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.Build.0 = Debug|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 = Debug|Any CPU
|
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.Deploy.0 = Release|Any CPU
|
||||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.Deploy.0 = Debug|Any CPU
|
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.Deploy.0 = Release|Any CPU
|
||||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.Deploy.0 = Debug|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.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.Build.0 = Debug|Any CPU
|
||||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|Any CPU.Deploy.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.ActiveCfg = Release|Any CPU
|
||||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhoneSimulator.Build.0 = 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
|
{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.ActiveCfg = Release|Any CPU
|
||||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|Any CPU.Build.0 = Debug|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 = Debug|Any CPU
|
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
||||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhoneSimulator.Build.0 = Debug|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.ActiveCfg = Debug|Any CPU
|
||||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|Any CPU.Build.0 = 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
|
{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|iPhone.Build.0 = Release|Any CPU
|
||||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhoneSimulator.ActiveCfg = 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
|
{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.ActiveCfg = Ad-Hoc|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|Any CPU.Build.0 = FDroid|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 = FDroid|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 = FDroid|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 = FDroid|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 = FDroid|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 = FDroid|Any CPU
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|Any CPU.ActiveCfg = AppStore|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|Any CPU.Build.0 = FDroid|Any CPU
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|Any CPU.Build.0 = AppStore|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.ActiveCfg = FDroid|Any CPU
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.ActiveCfg = AppStore|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.Build.0 = FDroid|Any CPU
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.Build.0 = AppStore|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.ActiveCfg = FDroid|Any CPU
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.Build.0 = FDroid|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.ActiveCfg = Debug|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
@@ -274,6 +279,7 @@ Global
|
|||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|Any CPU.ActiveCfg = Debug|iPhone
|
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|Any CPU.ActiveCfg = Debug|iPhone
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.Build.0 = Debug|iPhone
|
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.Build.0 = Debug|iPhone
|
||||||
|
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.Deploy.0 = Debug|iPhone
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|Any CPU.ActiveCfg = FDroid|iPhone
|
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|Any CPU.ActiveCfg = FDroid|iPhone
|
||||||
@@ -286,6 +292,64 @@ Global
|
|||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhone.Build.0 = Release|iPhone
|
{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.ActiveCfg = Release|iPhoneSimulator
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhoneSimulator.Build.0 = 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|iPhone.Deploy.0 = Debug|iPhone
|
||||||
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||||
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||||
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.ActiveCfg = Release|iPhone
|
||||||
|
{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|iPhone.Deploy.0 = Debug|iPhone
|
||||||
|
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||||
|
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||||
|
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|Any CPU.ActiveCfg = Release|iPhone
|
||||||
|
{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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -299,6 +363,8 @@ Global
|
|||||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D} = {2E399654-26A2-46F6-B9CA-1B496A3F370A}
|
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D} = {2E399654-26A2-46F6-B9CA-1B496A3F370A}
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
{E71F3053-056C-4381-9638-048ED73BDFF6} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15} = {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
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}
|
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}
|
||||||
|
|||||||
8
package-lock.json
generated
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "bitwarden-fdroid",
|
"name": "bitwarden-mobile",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
@@ -196,9 +196,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.11",
|
"version": "4.17.15",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||||
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
|
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ using Android.OS;
|
|||||||
using Android.Runtime;
|
using Android.Runtime;
|
||||||
using Android.Views;
|
using Android.Views;
|
||||||
using System;
|
using System;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Droid.Accessibility
|
namespace Bit.Droid.Accessibility
|
||||||
{
|
{
|
||||||
@@ -16,13 +18,13 @@ namespace Bit.Droid.Accessibility
|
|||||||
protected override void OnCreate(Bundle bundle)
|
protected override void OnCreate(Bundle bundle)
|
||||||
{
|
{
|
||||||
base.OnCreate(bundle);
|
base.OnCreate(bundle);
|
||||||
LaunchMainActivity(Intent, 932473);
|
HandleIntent(Intent, 932473);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnNewIntent(Intent intent)
|
protected override void OnNewIntent(Intent intent)
|
||||||
{
|
{
|
||||||
base.OnNewIntent(intent);
|
base.OnNewIntent(intent);
|
||||||
LaunchMainActivity(intent, 489729);
|
HandleIntent(intent, 489729);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
protected override void OnDestroy()
|
||||||
@@ -78,6 +80,21 @@ namespace Bit.Droid.Accessibility
|
|||||||
Finish();
|
Finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandleIntent(Intent callingIntent, int requestCode)
|
||||||
|
{
|
||||||
|
if (callingIntent?.GetBooleanExtra("autofillTileClicked", false) ?? false)
|
||||||
|
{
|
||||||
|
Intent.RemoveExtra("autofillTileClicked");
|
||||||
|
var messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
|
messagingService.Send("OnAutofillTileClick");
|
||||||
|
Finish();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LaunchMainActivity(callingIntent, requestCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void LaunchMainActivity(Intent callingIntent, int requestCode)
|
private void LaunchMainActivity(Intent callingIntent, int requestCode)
|
||||||
{
|
{
|
||||||
_lastQueriedUri = callingIntent?.GetStringExtra("uri");
|
_lastQueriedUri = callingIntent?.GetStringExtra("uri");
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Graphics;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
|
using Android.Provider;
|
||||||
|
using Android.Runtime;
|
||||||
|
using Android.Views;
|
||||||
using Android.Views.Accessibility;
|
using Android.Views.Accessibility;
|
||||||
|
using Android.Widget;
|
||||||
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
|
|
||||||
namespace Bit.Droid.Accessibility
|
namespace Bit.Droid.Accessibility
|
||||||
@@ -12,55 +20,91 @@ namespace Bit.Droid.Accessibility
|
|||||||
public static Credentials LastCredentials = null;
|
public static Credentials LastCredentials = null;
|
||||||
public static string SystemUiPackage = "com.android.systemui";
|
public static string SystemUiPackage = "com.android.systemui";
|
||||||
public static string BitwardenTag = "bw_access";
|
public static string BitwardenTag = "bw_access";
|
||||||
|
public static bool IsAutofillTileAdded = false;
|
||||||
|
public static bool IsAccessibilityBroadcastReady = false;
|
||||||
|
|
||||||
|
// Be sure to keep these two sections sorted alphabetically
|
||||||
public static Dictionary<string, Browser> SupportedBrowsers => new List<Browser>
|
public static Dictionary<string, Browser> SupportedBrowsers => new List<Browser>
|
||||||
{
|
{
|
||||||
new Browser("com.android.chrome", "url_bar"),
|
// [Section A] Entries also present in the list of Autofill Framework
|
||||||
new Browser("com.chrome.beta", "url_bar"),
|
//
|
||||||
new Browser("org.chromium.chrome", "url_bar"),
|
// So keep them in sync with:
|
||||||
|
// - AutofillHelpers.{TrustedBrowsers,CompatBrowsers}
|
||||||
|
// - Resources/xml/autofillservice.xml
|
||||||
|
new Browser("com.amazon.cloud9", "url"),
|
||||||
new Browser("com.android.browser", "url"),
|
new Browser("com.android.browser", "url"),
|
||||||
|
new Browser("com.android.chrome", "url_bar"),
|
||||||
|
new Browser("com.avast.android.secure.browser", "editor"),
|
||||||
|
new Browser("com.avg.android.secure.browser", "editor"),
|
||||||
new Browser("com.brave.browser", "url_bar"),
|
new Browser("com.brave.browser", "url_bar"),
|
||||||
|
new Browser("com.brave.browser_beta", "url_bar"),
|
||||||
|
new Browser("com.brave.browser_default", "url_bar"),
|
||||||
|
new Browser("com.brave.browser_dev", "url_bar"),
|
||||||
|
new Browser("com.brave.browser_nightly", "url_bar"),
|
||||||
|
new Browser("com.chrome.beta", "url_bar"),
|
||||||
|
new Browser("com.chrome.canary", "url_bar"),
|
||||||
|
new Browser("com.chrome.dev", "url_bar"),
|
||||||
|
new Browser("com.duckduckgo.mobile.android", "omnibarTextInput"),
|
||||||
|
new Browser("com.ecosia.android", "url_bar"),
|
||||||
|
new Browser("com.google.android.apps.chrome", "url_bar"),
|
||||||
|
new Browser("com.google.android.apps.chrome_dev", "url_bar"),
|
||||||
|
new Browser("com.kiwibrowser.browser", "url_bar"),
|
||||||
|
new Browser("com.microsoft.emmx", "url_bar"),
|
||||||
|
new Browser("com.naver.whale", "url_bar"),
|
||||||
new Browser("com.opera.browser", "url_field"),
|
new Browser("com.opera.browser", "url_field"),
|
||||||
new Browser("com.opera.browser.beta", "url_field"),
|
new Browser("com.opera.browser.beta", "url_field"),
|
||||||
new Browser("com.opera.mini.native", "url_field"),
|
new Browser("com.opera.mini.native", "url_field"),
|
||||||
|
new Browser("com.opera.mini.native.beta", "url_field"),
|
||||||
new Browser("com.opera.touch", "addressbarEdit"),
|
new Browser("com.opera.touch", "addressbarEdit"),
|
||||||
new Browser("com.chrome.dev", "url_bar"),
|
new Browser("com.qwant.liberty", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
||||||
new Browser("com.chrome.canary", "url_bar"),
|
|
||||||
new Browser("com.google.android.apps.chrome", "url_bar"),
|
|
||||||
new Browser("com.google.android.apps.chrome_dev", "url_bar"),
|
|
||||||
new Browser("org.codeaurora.swe.browser", "url_bar"),
|
|
||||||
new Browser("org.iron.srware", "url_bar"),
|
|
||||||
new Browser("com.sec.android.app.sbrowser", "location_bar_edit_text"),
|
new Browser("com.sec.android.app.sbrowser", "location_bar_edit_text"),
|
||||||
new Browser("com.sec.android.app.sbrowser.beta", "location_bar_edit_text"),
|
new Browser("com.sec.android.app.sbrowser.beta", "location_bar_edit_text"),
|
||||||
new Browser("com.yandex.browser", "bro_omnibar_address_title_text",
|
new Browser("com.stoutner.privacybrowser.free", "url_edittext"),
|
||||||
|
new Browser("com.stoutner.privacybrowser.standard", "url_edittext"),
|
||||||
|
new Browser("com.vivaldi.browser", "url_bar"),
|
||||||
|
new Browser("com.vivaldi.browser.snapshot", "url_bar"),
|
||||||
|
new Browser("com.vivaldi.browser.sopranos", "url_bar"),
|
||||||
|
new Browser("com.yandex.browser", "bro_omnibar_address_title_text,bro_omnibox_collapsed_title",
|
||||||
(s) => s.Split(new char[]{' ', ' '}).FirstOrDefault()), // 0 = Regular Space, 1 = No-break space (00A0)
|
(s) => s.Split(new char[]{' ', ' '}).FirstOrDefault()), // 0 = Regular Space, 1 = No-break space (00A0)
|
||||||
new Browser("org.mozilla.firefox", "url_bar_title"),
|
new Browser("mark.via.gp", "aw"),
|
||||||
new Browser("org.mozilla.firefox_beta", "url_bar_title"),
|
new Browser("org.adblockplus.browser", "url_bar,url_bar_title"), // 2nd = Legacy (before v2)
|
||||||
new Browser("org.mozilla.fennec_aurora", "url_bar_title"),
|
new Browser("org.adblockplus.browser.beta", "url_bar,url_bar_title"), // 2nd = Legacy (before v2)
|
||||||
|
new Browser("org.bromite.bromite", "url_bar"),
|
||||||
|
new Browser("org.chromium.chrome", "url_bar"),
|
||||||
|
new Browser("org.codeaurora.swe.browser", "url_bar"),
|
||||||
|
new Browser("org.gnu.icecat", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
||||||
|
new Browser("org.mozilla.fenix", "mozac_browser_toolbar_url_view"),
|
||||||
|
new Browser("org.mozilla.fenix.nightly", "mozac_browser_toolbar_url_view"),
|
||||||
|
new Browser("org.mozilla.fennec_aurora", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
||||||
|
new Browser("org.mozilla.fennec_fdroid", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
||||||
|
new Browser("org.mozilla.firefox", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
||||||
|
new Browser("org.mozilla.firefox_beta", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
||||||
new Browser("org.mozilla.focus", "display_url"),
|
new Browser("org.mozilla.focus", "display_url"),
|
||||||
new Browser("org.mozilla.klar", "display_url"),
|
new Browser("org.mozilla.klar", "display_url"),
|
||||||
new Browser("org.mozilla.fenix", "mozac_browser_toolbar_url_view"),
|
|
||||||
new Browser("org.mozilla.reference.browser", "mozac_browser_toolbar_url_view"),
|
new Browser("org.mozilla.reference.browser", "mozac_browser_toolbar_url_view"),
|
||||||
|
new Browser("org.mozilla.rocket", "display_url"),
|
||||||
|
new Browser("org.torproject.torbrowser", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
||||||
|
new Browser("org.torproject.torbrowser_alpha", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
||||||
|
|
||||||
|
// [Section B] Entries only present here
|
||||||
|
//
|
||||||
|
// FIXME: Test the compatibility of these with Autofill Framework
|
||||||
|
new Browser("acr.browser.barebones", "search"),
|
||||||
|
new Browser("acr.browser.lightning", "search"),
|
||||||
|
new Browser("com.feedback.browser.wjbrowser", "addressbar_url"),
|
||||||
new Browser("com.ghostery.android.ghostery", "search_field"),
|
new Browser("com.ghostery.android.ghostery", "search_field"),
|
||||||
new Browser("org.adblockplus.browser", "url_bar_title"),
|
|
||||||
new Browser("com.htc.sense.browser", "title"),
|
new Browser("com.htc.sense.browser", "title"),
|
||||||
new Browser("com.amazon.cloud9", "url"),
|
|
||||||
new Browser("mobi.mgeek.TunnyBrowser", "title"),
|
|
||||||
new Browser("com.nubelacorp.javelin", "enterUrl"),
|
|
||||||
new Browser("com.jerky.browser2", "enterUrl"),
|
new Browser("com.jerky.browser2", "enterUrl"),
|
||||||
|
new Browser("com.ksmobile.cb", "address_bar_edit_text"),
|
||||||
|
new Browser("com.linkbubble.playstore", "url_text"),
|
||||||
new Browser("com.mx.browser", "address_editor_with_progress"),
|
new Browser("com.mx.browser", "address_editor_with_progress"),
|
||||||
new Browser("com.mx.browser.tablet", "address_editor_with_progress"),
|
new Browser("com.mx.browser.tablet", "address_editor_with_progress"),
|
||||||
new Browser("com.linkbubble.playstore", "url_text"),
|
new Browser("com.nubelacorp.javelin", "enterUrl"),
|
||||||
new Browser("com.ksmobile.cb", "address_bar_edit_text"),
|
new Browser("jp.co.fenrir.android.sleipnir", "url_text"),
|
||||||
new Browser("acr.browser.lightning", "search"),
|
new Browser("jp.co.fenrir.android.sleipnir_black", "url_text"),
|
||||||
new Browser("acr.browser.barebones", "search"),
|
new Browser("jp.co.fenrir.android.sleipnir_test", "url_text"),
|
||||||
new Browser("com.microsoft.emmx", "url_bar"),
|
new Browser("mobi.mgeek.TunnyBrowser", "title"),
|
||||||
new Browser("com.duckduckgo.mobile.android", "omnibarTextInput"),
|
new Browser("org.iron.srware", "url_bar"),
|
||||||
new Browser("mark.via.gp", "aw"),
|
|
||||||
new Browser("org.bromite.bromite", "url_bar"),
|
|
||||||
new Browser("com.kiwibrowser.browser", "url_bar"),
|
|
||||||
new Browser("com.ecosia.android", "url_bar"),
|
|
||||||
new Browser("com.qwant.liberty", "url_bar_title"),
|
|
||||||
}.ToDictionary(n => n.PackageName);
|
}.ToDictionary(n => n.PackageName);
|
||||||
|
|
||||||
// Known packages to skip
|
// Known packages to skip
|
||||||
@@ -80,13 +124,29 @@ namespace Bit.Droid.Accessibility
|
|||||||
"com.teslacoilsw.launcher.prime",
|
"com.teslacoilsw.launcher.prime",
|
||||||
"is.shortcut",
|
"is.shortcut",
|
||||||
"me.craftsapp.nlauncher",
|
"me.craftsapp.nlauncher",
|
||||||
"com.ss.squarehome2"
|
"com.ss.squarehome2",
|
||||||
|
"com.treydev.pns"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Be sure to keep these entries sorted alphabetically
|
||||||
|
public static Dictionary<string, KnownUsernameField> KnownUsernameFields => new List<KnownUsernameField>
|
||||||
|
{
|
||||||
|
new KnownUsernameField("accounts.google.com", "ServiceLogin", "Email"),
|
||||||
|
new KnownUsernameField("amazon.com", "signin", "ap_email_login"),
|
||||||
|
new KnownUsernameField("github.com", "", "user[login]-footer"),
|
||||||
|
new KnownUsernameField("paypal.com", "signin", "email"),
|
||||||
|
new KnownUsernameField("signin.aws.amazon.com", "signin", "resolving_input"),
|
||||||
|
new KnownUsernameField("signin.ebay.com", "eBayISAPI.dll", "userid"),
|
||||||
|
}.ToDictionary(n => n.UriAuthority);
|
||||||
|
|
||||||
public static void PrintTestData(AccessibilityNodeInfo root, AccessibilityEvent e)
|
public static void PrintTestData(AccessibilityNodeInfo root, AccessibilityEvent e)
|
||||||
{
|
{
|
||||||
var testNodes = GetWindowNodes(root, e, n => n.ViewIdResourceName != null && n.Text != null, false);
|
var testNodes = GetWindowNodes(root, e, n => n.ViewIdResourceName != null && n.Text != null, false);
|
||||||
var testNodesData = testNodes.Select(n => new { id = n.ViewIdResourceName, text = n.Text });
|
var testNodesData = testNodes.Select(n => new { id = n.ViewIdResourceName, text = n.Text });
|
||||||
|
foreach (var node in testNodesData)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine("Node: {0} = {1}", node.id, node.text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetUri(AccessibilityNodeInfo root)
|
public static string GetUri(AccessibilityNodeInfo root)
|
||||||
@@ -95,18 +155,33 @@ namespace Bit.Droid.Accessibility
|
|||||||
if (SupportedBrowsers.ContainsKey(root.PackageName))
|
if (SupportedBrowsers.ContainsKey(root.PackageName))
|
||||||
{
|
{
|
||||||
var browser = SupportedBrowsers[root.PackageName];
|
var browser = SupportedBrowsers[root.PackageName];
|
||||||
var addressNode = root.FindAccessibilityNodeInfosByViewId(
|
AccessibilityNodeInfo addressNode = null;
|
||||||
$"{root.PackageName}:id/{browser.UriViewId}").FirstOrDefault();
|
foreach (var uriViewId in browser.UriViewId.Split(","))
|
||||||
|
{
|
||||||
|
addressNode = root.FindAccessibilityNodeInfosByViewId(
|
||||||
|
$"{root.PackageName}:id/{uriViewId}").FirstOrDefault();
|
||||||
|
if (addressNode != null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (addressNode != null)
|
if (addressNode != null)
|
||||||
{
|
{
|
||||||
uri = ExtractUri(uri, addressNode, browser);
|
uri = ExtractUri(uri, addressNode, browser);
|
||||||
addressNode.Dispose();
|
addressNode.Recycle();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Return null to prevent overwriting notification pendingIntent uri with browser packageName
|
||||||
|
// (we login to pages, not browsers)
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ExtractUri(string uri, AccessibilityNodeInfo addressNode, Browser browser)
|
private static string ExtractUri(string uri, AccessibilityNodeInfo addressNode, Browser browser)
|
||||||
{
|
{
|
||||||
if (addressNode?.Text == null)
|
if (addressNode?.Text == null)
|
||||||
{
|
{
|
||||||
@@ -119,21 +194,17 @@ namespace Bit.Droid.Accessibility
|
|||||||
uri = browser.GetUriFunction(addressNode.Text)?.Trim();
|
uri = browser.GetUriFunction(addressNode.Text)?.Trim();
|
||||||
if (uri != null && uri.Contains("."))
|
if (uri != null && uri.Contains("."))
|
||||||
{
|
{
|
||||||
if(!uri.Contains("://") && !uri.Contains(" "))
|
var hasHttpProtocol = uri.StartsWith("http://") || uri.StartsWith("https://");
|
||||||
|
if (!hasHttpProtocol && uri.Contains("."))
|
||||||
{
|
{
|
||||||
uri = string.Concat("http://", uri);
|
if (Uri.TryCreate("http://" + uri, UriKind.Absolute, out var uri2))
|
||||||
}
|
|
||||||
else if(Build.VERSION.SdkInt <= BuildVersionCodes.KitkatWatch)
|
|
||||||
{
|
{
|
||||||
var parts = uri.Split(new string[] { ". " }, StringSplitOptions.None);
|
return string.Concat("http://", uri);
|
||||||
if(parts.Length > 1)
|
|
||||||
{
|
|
||||||
var urlPart = parts.FirstOrDefault(p => p.StartsWith("http"));
|
|
||||||
if(urlPart != null)
|
|
||||||
{
|
|
||||||
uri = urlPart.Trim();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (Uri.TryCreate(uri, UriKind.Absolute, out var uri3))
|
||||||
|
{
|
||||||
|
return uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return uri;
|
return uri;
|
||||||
@@ -161,7 +232,6 @@ namespace Bit.Droid.Accessibility
|
|||||||
return n?.ClassName?.Contains("EditText") ?? false;
|
return n?.ClassName?.Contains("EditText") ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void FillCredentials(AccessibilityNodeInfo usernameNode,
|
public static void FillCredentials(AccessibilityNodeInfo usernameNode,
|
||||||
IEnumerable<AccessibilityNodeInfo> passwordNodes)
|
IEnumerable<AccessibilityNodeInfo> passwordNodes)
|
||||||
{
|
{
|
||||||
@@ -192,7 +262,7 @@ namespace Bit.Droid.Accessibility
|
|||||||
nodes = new NodeList();
|
nodes = new NodeList();
|
||||||
}
|
}
|
||||||
var dispose = disposeIfUnused;
|
var dispose = disposeIfUnused;
|
||||||
if(n != null && recursionDepth < 50)
|
if (n != null && recursionDepth < 100)
|
||||||
{
|
{
|
||||||
var add = n.WindowId == e.WindowId &&
|
var add = n.WindowId == e.WindowId &&
|
||||||
!(n.ViewIdResourceName?.StartsWith(SystemUiPackage) ?? false) &&
|
!(n.ViewIdResourceName?.StartsWith(SystemUiPackage) ?? false) &&
|
||||||
@@ -206,7 +276,11 @@ namespace Bit.Droid.Accessibility
|
|||||||
for (var i = 0; i < n.ChildCount; i++)
|
for (var i = 0; i < n.ChildCount; i++)
|
||||||
{
|
{
|
||||||
var childNode = n.GetChild(i);
|
var childNode = n.GetChild(i);
|
||||||
if(i > 100)
|
if (childNode == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (i > 100)
|
||||||
{
|
{
|
||||||
Android.Util.Log.Info(BitwardenTag, "Too many child iterations.");
|
Android.Util.Log.Info(BitwardenTag, "Too many child iterations.");
|
||||||
break;
|
break;
|
||||||
@@ -223,24 +297,343 @@ namespace Bit.Droid.Accessibility
|
|||||||
}
|
}
|
||||||
if (dispose)
|
if (dispose)
|
||||||
{
|
{
|
||||||
|
n?.Recycle();
|
||||||
n?.Dispose();
|
n?.Dispose();
|
||||||
}
|
}
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void GetNodesAndFill(AccessibilityNodeInfo root, AccessibilityEvent e,
|
public static AccessibilityNodeInfo GetUsernameEditText(string uriString,
|
||||||
IEnumerable<AccessibilityNodeInfo> passwordNodes)
|
IEnumerable<AccessibilityNodeInfo> allEditTexts)
|
||||||
{
|
{
|
||||||
var allEditTexts = GetWindowNodes(root, e, n => EditText(n), false);
|
string uriKey = null;
|
||||||
var usernameEditText = GetUsernameEditText(allEditTexts);
|
string uriLocalPath = null;
|
||||||
FillCredentials(usernameEditText, passwordNodes);
|
if (Uri.TryCreate(uriString, UriKind.Absolute, out var uri))
|
||||||
allEditTexts.Dispose();
|
{
|
||||||
usernameEditText = null;
|
uriKey = uri.Authority;
|
||||||
|
uriLocalPath = uri.LocalPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AccessibilityNodeInfo GetUsernameEditText(IEnumerable<AccessibilityNodeInfo> allEditTexts)
|
if (!string.IsNullOrEmpty(uriKey))
|
||||||
{
|
{
|
||||||
return allEditTexts.TakeWhile(n => !n.Password).LastOrDefault();
|
// Uncomment this to log values necessary for username field discovery
|
||||||
|
// foreach (var editText in allEditTexts)
|
||||||
|
// {
|
||||||
|
// System.Diagnostics.Debug.WriteLine(">>> uriKey: {0}, uriLocalPath: {1}, viewId: {2}", uriKey,
|
||||||
|
// uriLocalPath, editText.ViewIdResourceName);
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (KnownUsernameFields.ContainsKey(uriKey))
|
||||||
|
{
|
||||||
|
var usernameField = KnownUsernameFields[uriKey];
|
||||||
|
if (uriLocalPath.EndsWith(usernameField.UriPathEnd))
|
||||||
|
{
|
||||||
|
foreach (var editText in allEditTexts)
|
||||||
|
{
|
||||||
|
foreach (var usernameViewId in usernameField.UsernameViewId.Split(","))
|
||||||
|
{
|
||||||
|
if (usernameViewId == editText.ViewIdResourceName)
|
||||||
|
{
|
||||||
|
return editText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no match found, attempt to establish username field based on password field
|
||||||
|
return GetUsernameEditTextIfPasswordExists(allEditTexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AccessibilityNodeInfo GetUsernameEditTextIfPasswordExists(
|
||||||
|
IEnumerable<AccessibilityNodeInfo> allEditTexts)
|
||||||
|
{
|
||||||
|
AccessibilityNodeInfo previousEditText = null;
|
||||||
|
foreach (var editText in allEditTexts)
|
||||||
|
{
|
||||||
|
if (editText.Password)
|
||||||
|
{
|
||||||
|
return previousEditText;
|
||||||
|
}
|
||||||
|
previousEditText = editText;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsUsernameEditText(AccessibilityNodeInfo root, AccessibilityEvent e)
|
||||||
|
{
|
||||||
|
var allEditTexts = GetWindowNodes(root, e, n => EditText(n), false);
|
||||||
|
var uriString = GetUri(root);
|
||||||
|
var usernameEditText = GetUsernameEditText(uriString, allEditTexts);
|
||||||
|
|
||||||
|
var isUsernameEditText = false;
|
||||||
|
if (usernameEditText != null)
|
||||||
|
{
|
||||||
|
isUsernameEditText = IsSameNode(usernameEditText, e.Source);
|
||||||
|
}
|
||||||
|
allEditTexts.Dispose();
|
||||||
|
|
||||||
|
return isUsernameEditText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsSameNode(AccessibilityNodeInfo node1, AccessibilityNodeInfo node2)
|
||||||
|
{
|
||||||
|
if (node1 != null && node2 != null)
|
||||||
|
{
|
||||||
|
return node1.Equals(node2) || node1.GetHashCode() == node2.GetHashCode();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool OverlayPermitted()
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
|
||||||
|
{
|
||||||
|
if (Settings.CanDrawOverlays(Application.Context))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var appOpsMgr = (AppOpsManager)Application.Context.GetSystemService(Context.AppOpsService);
|
||||||
|
var mode = appOpsMgr.CheckOpNoThrow("android:system_alert_window", Process.MyUid(),
|
||||||
|
Application.Context.PackageName);
|
||||||
|
if (mode == AppOpsManagerMode.Allowed || mode == AppOpsManagerMode.Ignored)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var wm = Application.Context.GetSystemService(Context.WindowService)
|
||||||
|
.JavaCast<IWindowManager>();
|
||||||
|
if (wm == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var testView = new View(Application.Context);
|
||||||
|
var layoutParams = GetOverlayLayoutParams();
|
||||||
|
wm.AddView(testView, layoutParams);
|
||||||
|
wm.RemoveView(testView);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// older android versions are always true
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LinearLayout GetOverlayView(Context context)
|
||||||
|
{
|
||||||
|
var inflater = (LayoutInflater)context.GetSystemService(Context.LayoutInflaterService);
|
||||||
|
var view = (LinearLayout)inflater.Inflate(Resource.Layout.autofill_listitem, null);
|
||||||
|
var text1 = (TextView)view.FindViewById(Resource.Id.text1);
|
||||||
|
var text2 = (TextView)view.FindViewById(Resource.Id.text2);
|
||||||
|
var icon = (ImageView)view.FindViewById(Resource.Id.icon);
|
||||||
|
text1.Text = AppResources.AutofillWithBitwarden;
|
||||||
|
text2.Text = AppResources.GoToMyVault;
|
||||||
|
icon.SetImageResource(Resource.Drawable.icon);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WindowManagerLayoutParams GetOverlayLayoutParams()
|
||||||
|
{
|
||||||
|
WindowManagerTypes windowManagerType;
|
||||||
|
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
|
||||||
|
{
|
||||||
|
windowManagerType = WindowManagerTypes.ApplicationOverlay;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
windowManagerType = WindowManagerTypes.Phone;
|
||||||
|
}
|
||||||
|
|
||||||
|
var layoutParams = new WindowManagerLayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WrapContent,
|
||||||
|
ViewGroup.LayoutParams.WrapContent,
|
||||||
|
windowManagerType,
|
||||||
|
WindowManagerFlags.NotFocusable | WindowManagerFlags.NotTouchModal,
|
||||||
|
Format.Transparent);
|
||||||
|
layoutParams.Gravity = GravityFlags.Top | GravityFlags.Left;
|
||||||
|
|
||||||
|
return layoutParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Point GetOverlayAnchorPosition(AccessibilityService service, AccessibilityNodeInfo anchorView,
|
||||||
|
int overlayViewHeight, bool isOverlayAboveAnchor)
|
||||||
|
{
|
||||||
|
var anchorViewRect = new Rect();
|
||||||
|
anchorView.GetBoundsInScreen(anchorViewRect);
|
||||||
|
var anchorViewX = anchorViewRect.Left;
|
||||||
|
var anchorViewY = isOverlayAboveAnchor ? anchorViewRect.Top : anchorViewRect.Bottom;
|
||||||
|
anchorViewRect.Dispose();
|
||||||
|
|
||||||
|
if (isOverlayAboveAnchor)
|
||||||
|
{
|
||||||
|
anchorViewY -= overlayViewHeight;
|
||||||
|
}
|
||||||
|
anchorViewY -= GetStatusBarHeight(service);
|
||||||
|
|
||||||
|
return new Point(anchorViewX, anchorViewY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Point GetOverlayAnchorPosition(AccessibilityService service, AccessibilityNodeInfo anchorNode,
|
||||||
|
AccessibilityNodeInfo root, IEnumerable<AccessibilityWindowInfo> windows, int overlayViewHeight,
|
||||||
|
bool isOverlayAboveAnchor)
|
||||||
|
{
|
||||||
|
Point point = null;
|
||||||
|
if (anchorNode != null)
|
||||||
|
{
|
||||||
|
// Update node's info since this is still a reference from an older event
|
||||||
|
anchorNode.Refresh();
|
||||||
|
if (!anchorNode.VisibleToUser)
|
||||||
|
{
|
||||||
|
return new Point(-1, -1);
|
||||||
|
}
|
||||||
|
if (!anchorNode.Focused)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// node.VisibleToUser doesn't always give us exactly what we want, so attempt to tighten up the range
|
||||||
|
// of visibility
|
||||||
|
var inputMethodHeight = 0;
|
||||||
|
if (windows != null)
|
||||||
|
{
|
||||||
|
if (IsStatusBarExpanded(windows))
|
||||||
|
{
|
||||||
|
return new Point(-1, -1);
|
||||||
|
}
|
||||||
|
inputMethodHeight = GetInputMethodHeight(windows);
|
||||||
|
}
|
||||||
|
var minY = 0;
|
||||||
|
var rootNodeHeight = GetNodeHeight(root);
|
||||||
|
if (rootNodeHeight == -1)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var maxY = rootNodeHeight - GetNavigationBarHeight(service) - GetStatusBarHeight(service) -
|
||||||
|
inputMethodHeight;
|
||||||
|
|
||||||
|
point = GetOverlayAnchorPosition(service, anchorNode, overlayViewHeight, isOverlayAboveAnchor);
|
||||||
|
if (point.Y < minY)
|
||||||
|
{
|
||||||
|
if (isOverlayAboveAnchor)
|
||||||
|
{
|
||||||
|
// view nearing bounds, anchor to bottom
|
||||||
|
point.X = -1;
|
||||||
|
point.Y = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// view out of bounds, hide overlay
|
||||||
|
point.X = -1;
|
||||||
|
point.Y = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (point.Y > (maxY - overlayViewHeight))
|
||||||
|
{
|
||||||
|
if (isOverlayAboveAnchor)
|
||||||
|
{
|
||||||
|
// view out of bounds, hide overlay
|
||||||
|
point.X = -1;
|
||||||
|
point.Y = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// view nearing bounds, anchor to top
|
||||||
|
point.X = 0;
|
||||||
|
point.Y = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isOverlayAboveAnchor && point.Y < (maxY - (overlayViewHeight * 2) - GetNodeHeight(anchorNode)))
|
||||||
|
{
|
||||||
|
// This else block forces the overlay to return to bottom alignment as soon as space is available
|
||||||
|
// below the anchor view. Removing this will change the behavior to wait until there isn't enough
|
||||||
|
// space above the anchor view before returning to bottom alignment.
|
||||||
|
point.X = -1;
|
||||||
|
point.Y = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsStatusBarExpanded(IEnumerable<AccessibilityWindowInfo> windows)
|
||||||
|
{
|
||||||
|
if (windows != null && windows.Any())
|
||||||
|
{
|
||||||
|
var isSystemWindowsOnly = true;
|
||||||
|
foreach (var window in windows)
|
||||||
|
{
|
||||||
|
if (window.Type != AccessibilityWindowType.System)
|
||||||
|
{
|
||||||
|
isSystemWindowsOnly = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isSystemWindowsOnly;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetInputMethodHeight(IEnumerable<AccessibilityWindowInfo> windows)
|
||||||
|
{
|
||||||
|
var inputMethodWindowHeight = 0;
|
||||||
|
if (windows != null)
|
||||||
|
{
|
||||||
|
foreach (var window in windows)
|
||||||
|
{
|
||||||
|
if (window.Type == AccessibilityWindowType.InputMethod)
|
||||||
|
{
|
||||||
|
var windowRect = new Rect();
|
||||||
|
window.GetBoundsInScreen(windowRect);
|
||||||
|
inputMethodWindowHeight = windowRect.Height();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return inputMethodWindowHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsAutofillServicePromptVisible(IEnumerable<AccessibilityWindowInfo> windows)
|
||||||
|
{
|
||||||
|
return windows?.Any(w => w.Title?.ToLower().Contains("autofill") ?? false) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetNodeHeight(AccessibilityNodeInfo node)
|
||||||
|
{
|
||||||
|
if (node == null)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
var nodeRect = new Rect();
|
||||||
|
node.GetBoundsInScreen(nodeRect);
|
||||||
|
var nodeRectHeight = nodeRect.Height();
|
||||||
|
nodeRect.Dispose();
|
||||||
|
return nodeRectHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetStatusBarHeight(AccessibilityService service)
|
||||||
|
{
|
||||||
|
return GetSystemResourceDimenPx(service, "status_bar_height");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetNavigationBarHeight(AccessibilityService service)
|
||||||
|
{
|
||||||
|
return GetSystemResourceDimenPx(service, "navigation_bar_height");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetSystemResourceDimenPx(AccessibilityService service, string resName)
|
||||||
|
{
|
||||||
|
var resourceId = service.Resources.GetIdentifier(resName, "dimen", "android");
|
||||||
|
if (resourceId > 0)
|
||||||
|
{
|
||||||
|
return service.Resources.GetDimensionPixelSize(resourceId);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -6,7 +6,9 @@ using Android.App;
|
|||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Android.Runtime;
|
using Android.Runtime;
|
||||||
|
using Android.Views;
|
||||||
using Android.Views.Accessibility;
|
using Android.Views.Accessibility;
|
||||||
|
using Android.Widget;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
@@ -20,23 +22,52 @@ namespace Bit.Droid.Accessibility
|
|||||||
[Register("com.x8bit.bitwarden.Accessibility.AccessibilityService")]
|
[Register("com.x8bit.bitwarden.Accessibility.AccessibilityService")]
|
||||||
public class AccessibilityService : Android.AccessibilityServices.AccessibilityService
|
public class AccessibilityService : Android.AccessibilityServices.AccessibilityService
|
||||||
{
|
{
|
||||||
private NotificationChannel _notificationChannel;
|
|
||||||
|
|
||||||
private const int AutoFillNotificationId = 34573;
|
|
||||||
private const string BitwardenPackage = "com.x8bit.bitwarden";
|
private const string BitwardenPackage = "com.x8bit.bitwarden";
|
||||||
private const string BitwardenWebsite = "vault.bitwarden.com";
|
private const string BitwardenWebsite = "vault.bitwarden.com";
|
||||||
|
|
||||||
private IStorageService _storageService;
|
private IStorageService _storageService;
|
||||||
private bool _settingAutofillPasswordField;
|
private IBroadcasterService _broadcasterService;
|
||||||
private bool _settingAutofillPersistNotification;
|
|
||||||
private DateTime? _lastSettingsReload = null;
|
private DateTime? _lastSettingsReload = null;
|
||||||
private TimeSpan _settingsReloadSpan = TimeSpan.FromMinutes(1);
|
private TimeSpan _settingsReloadSpan = TimeSpan.FromMinutes(1);
|
||||||
private long _lastNotificationTime = 0;
|
private HashSet<string> _blacklistedUris;
|
||||||
private string _lastNotificationUri = null;
|
private AccessibilityNodeInfo _anchorNode = null;
|
||||||
|
private int _lastAnchorX = 0;
|
||||||
|
private int _lastAnchorY = 0;
|
||||||
|
private bool _isOverlayAboveAnchor = false;
|
||||||
|
private static bool _overlayAnchorObserverRunning = false;
|
||||||
|
private IWindowManager _windowManager = null;
|
||||||
|
private LinearLayout _overlayView = null;
|
||||||
|
private int _overlayViewHeight = 0;
|
||||||
|
private long _lastAutoFillTime = 0;
|
||||||
|
private Java.Lang.Runnable _overlayAnchorObserverRunnable = null;
|
||||||
|
private Handler _handler = new Handler(Looper.MainLooper);
|
||||||
|
|
||||||
private HashSet<string> _launcherPackageNames = null;
|
private HashSet<string> _launcherPackageNames = null;
|
||||||
private DateTime? _lastLauncherSetBuilt = null;
|
private DateTime? _lastLauncherSetBuilt = null;
|
||||||
private TimeSpan _rebuildLauncherSpan = TimeSpan.FromHours(1);
|
private TimeSpan _rebuildLauncherSpan = TimeSpan.FromHours(1);
|
||||||
|
|
||||||
|
public override void OnCreate()
|
||||||
|
{
|
||||||
|
base.OnCreate();
|
||||||
|
LoadServices();
|
||||||
|
var settingsTask = LoadSettingsAsync();
|
||||||
|
_broadcasterService.Subscribe(nameof(AccessibilityService), (message) =>
|
||||||
|
{
|
||||||
|
if (message.Command == "OnAutofillTileClick")
|
||||||
|
{
|
||||||
|
var runnable = new Java.Lang.Runnable(OnAutofillTileClick);
|
||||||
|
_handler.PostDelayed(runnable, 250);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AccessibilityHelpers.IsAccessibilityBroadcastReady = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnDestroy()
|
||||||
|
{
|
||||||
|
AccessibilityHelpers.IsAccessibilityBroadcastReady = false;
|
||||||
|
_broadcasterService.Unsubscribe(nameof(AccessibilityService));
|
||||||
|
}
|
||||||
|
|
||||||
public override void OnAccessibilityEvent(AccessibilityEvent e)
|
public override void OnAccessibilityEvent(AccessibilityEvent e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -53,116 +84,80 @@ namespace Bit.Droid.Accessibility
|
|||||||
|
|
||||||
if (SkipPackage(e?.PackageName))
|
if (SkipPackage(e?.PackageName))
|
||||||
{
|
{
|
||||||
return;
|
if (e?.PackageName != "com.android.systemui")
|
||||||
}
|
|
||||||
|
|
||||||
var root = RootInActiveWindow;
|
|
||||||
if(root == null || root.PackageName != e.PackageName)
|
|
||||||
{
|
{
|
||||||
|
CancelOverlayPrompt();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccessibilityHelpers.PrintTestData(root, e);
|
// AccessibilityHelpers.PrintTestData(RootInActiveWindow, e);
|
||||||
|
|
||||||
LoadServices();
|
LoadServices();
|
||||||
var settingsTask = LoadSettingsAsync();
|
var settingsTask = LoadSettingsAsync();
|
||||||
|
AccessibilityNodeInfo root = null;
|
||||||
var notificationManager = GetSystemService(NotificationService) as NotificationManager;
|
|
||||||
var cancelNotification = true;
|
|
||||||
|
|
||||||
switch (e.EventType)
|
switch (e.EventType)
|
||||||
{
|
{
|
||||||
case EventTypes.ViewFocused:
|
case EventTypes.ViewFocused:
|
||||||
if(e.Source == null || !e.Source.Password || !_settingAutofillPasswordField)
|
case EventTypes.ViewClicked:
|
||||||
|
if (e.Source == null || e.PackageName == BitwardenPackage)
|
||||||
|
{
|
||||||
|
CancelOverlayPrompt();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
root = RootInActiveWindow;
|
||||||
|
if (root == null || root.PackageName != e.PackageName)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(e.PackageName == BitwardenPackage)
|
|
||||||
|
if (!(e.Source?.Password ?? false) && !AccessibilityHelpers.IsUsernameEditText(root, e))
|
||||||
{
|
{
|
||||||
CancelNotification(notificationManager);
|
CancelOverlayPrompt();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(ScanAndAutofill(root, e, notificationManager, cancelNotification))
|
if (ScanAndAutofill(root, e))
|
||||||
{
|
{
|
||||||
CancelNotification(notificationManager);
|
CancelOverlayPrompt();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OverlayPromptToAutofill(root, e);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EventTypes.WindowContentChanged:
|
case EventTypes.WindowContentChanged:
|
||||||
case EventTypes.WindowStateChanged:
|
case EventTypes.WindowStateChanged:
|
||||||
if(_settingAutofillPasswordField && e.Source.Password)
|
if (AccessibilityHelpers.LastCredentials == null)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if(_settingAutofillPasswordField && AccessibilityHelpers.LastCredentials == null)
|
|
||||||
{
|
|
||||||
if(string.IsNullOrWhiteSpace(_lastNotificationUri))
|
|
||||||
{
|
|
||||||
CancelNotification(notificationManager);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
var uri = AccessibilityHelpers.GetUri(root);
|
|
||||||
if(uri != _lastNotificationUri)
|
|
||||||
{
|
|
||||||
CancelNotification(notificationManager);
|
|
||||||
}
|
|
||||||
else if(uri.StartsWith(Constants.AndroidAppProtocol))
|
|
||||||
{
|
|
||||||
CancelNotification(notificationManager, 30000);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.PackageName == BitwardenPackage)
|
if (e.PackageName == BitwardenPackage)
|
||||||
{
|
{
|
||||||
CancelNotification(notificationManager);
|
CancelOverlayPrompt();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_settingAutofillPersistNotification)
|
root = RootInActiveWindow;
|
||||||
|
if (root == null || root.PackageName != e.PackageName)
|
||||||
{
|
{
|
||||||
var uri = AccessibilityHelpers.GetUri(root);
|
break;
|
||||||
if(uri != null && !uri.Contains(BitwardenWebsite))
|
|
||||||
{
|
|
||||||
var needToFill = AccessibilityHelpers.NeedToAutofill(
|
|
||||||
AccessibilityHelpers.LastCredentials, uri);
|
|
||||||
if(needToFill)
|
|
||||||
{
|
|
||||||
var passwordNodes = AccessibilityHelpers.GetWindowNodes(root, e,
|
|
||||||
n => n.Password, false);
|
|
||||||
needToFill = passwordNodes.Any();
|
|
||||||
if(needToFill)
|
|
||||||
{
|
|
||||||
AccessibilityHelpers.GetNodesAndFill(root, e, passwordNodes);
|
|
||||||
}
|
}
|
||||||
passwordNodes.Dispose();
|
if (ScanAndAutofill(root, e))
|
||||||
}
|
|
||||||
if(!needToFill)
|
|
||||||
{
|
{
|
||||||
NotifyToAutofill(uri, notificationManager);
|
CancelOverlayPrompt();
|
||||||
cancelNotification = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AccessibilityHelpers.LastCredentials = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cancelNotification = ScanAndAutofill(root, e, notificationManager, cancelNotification);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cancelNotification)
|
|
||||||
{
|
|
||||||
CancelNotification(notificationManager);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
notificationManager?.Dispose();
|
|
||||||
root.Dispose();
|
|
||||||
e.Dispose();
|
|
||||||
}
|
}
|
||||||
// Suppress exceptions so that service doesn't crash.
|
// Suppress exceptions so that service doesn't crash.
|
||||||
catch { }
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", ex.GetType(), ex.StackTrace);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnInterrupt()
|
public override void OnInterrupt()
|
||||||
@@ -170,28 +165,27 @@ namespace Bit.Droid.Accessibility
|
|||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ScanAndAutofill(AccessibilityNodeInfo root, AccessibilityEvent e,
|
public bool ScanAndAutofill(AccessibilityNodeInfo root, AccessibilityEvent e)
|
||||||
NotificationManager notificationManager, bool cancelNotification)
|
|
||||||
{
|
|
||||||
var passwordNodes = AccessibilityHelpers.GetWindowNodes(root, e, n => n.Password, false);
|
|
||||||
if(passwordNodes.Count > 0)
|
|
||||||
{
|
{
|
||||||
|
var filled = false;
|
||||||
var uri = AccessibilityHelpers.GetUri(root);
|
var uri = AccessibilityHelpers.GetUri(root);
|
||||||
if(uri != null && !uri.Contains(BitwardenWebsite))
|
if (uri != null && !uri.Contains(BitwardenWebsite) &&
|
||||||
|
AccessibilityHelpers.NeedToAutofill(AccessibilityHelpers.LastCredentials, uri))
|
||||||
{
|
{
|
||||||
if(AccessibilityHelpers.NeedToAutofill(AccessibilityHelpers.LastCredentials, uri))
|
var allEditTexts = AccessibilityHelpers.GetWindowNodes(root, e, n => AccessibilityHelpers.EditText(n), false);
|
||||||
|
var usernameEditText = AccessibilityHelpers.GetUsernameEditText(uri, allEditTexts);
|
||||||
|
var passwordNodes = AccessibilityHelpers.GetWindowNodes(root, e, n => n.Password, false);
|
||||||
|
if (usernameEditText != null || passwordNodes.Count > 0)
|
||||||
{
|
{
|
||||||
AccessibilityHelpers.GetNodesAndFill(root, e, passwordNodes);
|
AccessibilityHelpers.FillCredentials(usernameEditText, passwordNodes);
|
||||||
}
|
filled = true;
|
||||||
else
|
_lastAutoFillTime = Java.Lang.JavaSystem.CurrentTimeMillis();
|
||||||
{
|
|
||||||
NotifyToAutofill(uri, notificationManager);
|
|
||||||
cancelNotification = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AccessibilityHelpers.LastCredentials = null;
|
AccessibilityHelpers.LastCredentials = null;
|
||||||
}
|
}
|
||||||
else if(AccessibilityHelpers.LastCredentials != null)
|
allEditTexts.Dispose();
|
||||||
|
passwordNodes.Dispose();
|
||||||
|
}
|
||||||
|
if (AccessibilityHelpers.LastCredentials != null)
|
||||||
{
|
{
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
@@ -199,70 +193,232 @@ namespace Bit.Droid.Accessibility
|
|||||||
AccessibilityHelpers.LastCredentials = null;
|
AccessibilityHelpers.LastCredentials = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
passwordNodes.Dispose();
|
return filled;
|
||||||
return cancelNotification;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CancelNotification(NotificationManager notificationManager, long limit = 250)
|
private void OnAutofillTileClick()
|
||||||
{
|
{
|
||||||
if(Java.Lang.JavaSystem.CurrentTimeMillis() - _lastNotificationTime < limit)
|
CancelOverlayPrompt();
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_lastNotificationUri = null;
|
|
||||||
notificationManager?.Cancel(AutoFillNotificationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void NotifyToAutofill(string uri, NotificationManager notificationManager)
|
var root = RootInActiveWindow;
|
||||||
|
if (root != null && root.PackageName != BitwardenPackage &&
|
||||||
|
root.PackageName != AccessibilityHelpers.SystemUiPackage &&
|
||||||
|
!SkipPackage(root.PackageName))
|
||||||
{
|
{
|
||||||
if(notificationManager == null || string.IsNullOrWhiteSpace(uri))
|
var uri = AccessibilityHelpers.GetUri(root);
|
||||||
|
if (!string.IsNullOrWhiteSpace(uri))
|
||||||
{
|
{
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var now = Java.Lang.JavaSystem.CurrentTimeMillis();
|
|
||||||
var intent = new Intent(this, typeof(AccessibilityActivity));
|
var intent = new Intent(this, typeof(AccessibilityActivity));
|
||||||
intent.PutExtra("uri", uri);
|
intent.PutExtra("uri", uri);
|
||||||
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
|
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
|
||||||
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.UpdateCurrent);
|
StartActivity(intent);
|
||||||
|
return;
|
||||||
var notificationContent = Build.VERSION.SdkInt > BuildVersionCodes.KitkatWatch ?
|
|
||||||
AppResources.BitwardenAutofillServiceNotificationContent :
|
|
||||||
AppResources.BitwardenAutofillServiceNotificationContentOld;
|
|
||||||
|
|
||||||
var builder = new Notification.Builder(this);
|
|
||||||
builder.SetSmallIcon(Resource.Drawable.shield)
|
|
||||||
.SetContentTitle(AppResources.BitwardenAutofillService)
|
|
||||||
.SetContentText(notificationContent)
|
|
||||||
.SetTicker(notificationContent)
|
|
||||||
.SetWhen(now)
|
|
||||||
.SetContentIntent(pendingIntent);
|
|
||||||
|
|
||||||
if(Build.VERSION.SdkInt > BuildVersionCodes.KitkatWatch)
|
|
||||||
{
|
|
||||||
builder.SetVisibility(NotificationVisibility.Secret)
|
|
||||||
.SetColor(Android.Support.V4.Content.ContextCompat.GetColor(ApplicationContext,
|
|
||||||
Resource.Color.primary));
|
|
||||||
}
|
}
|
||||||
if(Build.VERSION.SdkInt >= BuildVersionCodes.O)
|
|
||||||
{
|
|
||||||
if(_notificationChannel == null)
|
|
||||||
{
|
|
||||||
_notificationChannel = new NotificationChannel("bitwarden_autofill_service",
|
|
||||||
AppResources.AutofillService, NotificationImportance.Low);
|
|
||||||
notificationManager.CreateNotificationChannel(_notificationChannel);
|
|
||||||
}
|
|
||||||
builder.SetChannelId(_notificationChannel.Id);
|
|
||||||
}
|
|
||||||
if(/*Build.VERSION.SdkInt <= BuildVersionCodes.N && */_settingAutofillPersistNotification)
|
|
||||||
{
|
|
||||||
builder.SetPriority(-2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_lastNotificationTime = now;
|
Toast.MakeText(this, AppResources.AutofillTileUriNotFound, ToastLength.Long).Show();
|
||||||
_lastNotificationUri = uri;
|
}
|
||||||
notificationManager.Notify(AutoFillNotificationId, builder.Build());
|
|
||||||
builder.Dispose();
|
private void CancelOverlayPrompt()
|
||||||
|
{
|
||||||
|
_overlayAnchorObserverRunning = false;
|
||||||
|
|
||||||
|
if (_windowManager != null && _overlayView != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_windowManager.RemoveViewImmediate(_overlayView);
|
||||||
|
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Removed");
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
_overlayView = null;
|
||||||
|
_lastAnchorX = 0;
|
||||||
|
_lastAnchorY = 0;
|
||||||
|
_isOverlayAboveAnchor = false;
|
||||||
|
|
||||||
|
if (_anchorNode != null)
|
||||||
|
{
|
||||||
|
_anchorNode.Recycle();
|
||||||
|
_anchorNode = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OverlayPromptToAutofill(AccessibilityNodeInfo root, AccessibilityEvent e)
|
||||||
|
{
|
||||||
|
if (Java.Lang.JavaSystem.CurrentTimeMillis() - _lastAutoFillTime < 1000 ||
|
||||||
|
AccessibilityHelpers.IsAutofillServicePromptVisible(Windows))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AccessibilityHelpers.OverlayPermitted())
|
||||||
|
{
|
||||||
|
if (!AccessibilityHelpers.IsAutofillTileAdded)
|
||||||
|
{
|
||||||
|
// The user has the option of only using the autofill tile and leaving the overlay permission
|
||||||
|
// disabled, so only show this toast if they're using accessibility without overlay permission and
|
||||||
|
// have _not_ added the autofill tile
|
||||||
|
System.Diagnostics.Debug.WriteLine(">>> Overlay Permission not granted");
|
||||||
|
Toast.MakeText(this, AppResources.AccessibilityOverlayPermissionAlert, ToastLength.Long).Show();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_overlayView != null || _anchorNode != null || _overlayAnchorObserverRunning)
|
||||||
|
{
|
||||||
|
CancelOverlayPrompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
var uri = AccessibilityHelpers.GetUri(root);
|
||||||
|
var fillable = !string.IsNullOrWhiteSpace(uri);
|
||||||
|
if (fillable)
|
||||||
|
{
|
||||||
|
if (_blacklistedUris != null && _blacklistedUris.Any())
|
||||||
|
{
|
||||||
|
if (Uri.TryCreate(uri, UriKind.Absolute, out var parsedUri) && parsedUri.Scheme.StartsWith("http"))
|
||||||
|
{
|
||||||
|
fillable = !_blacklistedUris.Contains(
|
||||||
|
string.Format("{0}://{1}", parsedUri.Scheme, parsedUri.Host));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fillable = !_blacklistedUris.Contains(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!fillable)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var intent = new Intent(this, typeof(AccessibilityActivity));
|
||||||
|
intent.PutExtra("uri", uri);
|
||||||
|
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
|
||||||
|
|
||||||
|
_overlayView = AccessibilityHelpers.GetOverlayView(this);
|
||||||
|
_overlayView.Measure(View.MeasureSpec.MakeMeasureSpec(0, 0),
|
||||||
|
View.MeasureSpec.MakeMeasureSpec(0, 0));
|
||||||
|
_overlayViewHeight = _overlayView.MeasuredHeight;
|
||||||
|
_overlayView.Click += (sender, eventArgs) =>
|
||||||
|
{
|
||||||
|
CancelOverlayPrompt();
|
||||||
|
StartActivity(intent);
|
||||||
|
};
|
||||||
|
|
||||||
|
var layoutParams = AccessibilityHelpers.GetOverlayLayoutParams();
|
||||||
|
var anchorPosition = AccessibilityHelpers.GetOverlayAnchorPosition(this, e.Source,
|
||||||
|
_overlayViewHeight, _isOverlayAboveAnchor);
|
||||||
|
layoutParams.X = anchorPosition.X;
|
||||||
|
layoutParams.Y = anchorPosition.Y;
|
||||||
|
|
||||||
|
if (_windowManager == null)
|
||||||
|
{
|
||||||
|
_windowManager = GetSystemService(WindowService).JavaCast<IWindowManager>();
|
||||||
|
}
|
||||||
|
|
||||||
|
_anchorNode = e.Source;
|
||||||
|
_lastAnchorX = anchorPosition.X;
|
||||||
|
_lastAnchorY = anchorPosition.Y;
|
||||||
|
|
||||||
|
_windowManager.AddView(_overlayView, layoutParams);
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Added at X:{0} Y:{1}",
|
||||||
|
layoutParams.X, layoutParams.Y);
|
||||||
|
|
||||||
|
StartOverlayAnchorObserver();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartOverlayAnchorObserver()
|
||||||
|
{
|
||||||
|
if (_overlayAnchorObserverRunning)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_overlayAnchorObserverRunning = true;
|
||||||
|
_overlayAnchorObserverRunnable = new Java.Lang.Runnable(() =>
|
||||||
|
{
|
||||||
|
if (_overlayAnchorObserverRunning)
|
||||||
|
{
|
||||||
|
AdjustOverlayForScroll();
|
||||||
|
_handler.PostDelayed(_overlayAnchorObserverRunnable, 250);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_handler.PostDelayed(_overlayAnchorObserverRunnable, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AdjustOverlayForScroll()
|
||||||
|
{
|
||||||
|
if (_overlayView == null || _anchorNode == null ||
|
||||||
|
AccessibilityHelpers.IsAutofillServicePromptVisible(Windows))
|
||||||
|
{
|
||||||
|
CancelOverlayPrompt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var root = RootInActiveWindow;
|
||||||
|
IEnumerable<AccessibilityWindowInfo> windows = null;
|
||||||
|
if (Build.VERSION.SdkInt > BuildVersionCodes.Kitkat)
|
||||||
|
{
|
||||||
|
windows = Windows;
|
||||||
|
}
|
||||||
|
|
||||||
|
var anchorPosition = AccessibilityHelpers.GetOverlayAnchorPosition(this, _anchorNode, root,
|
||||||
|
windows, _overlayViewHeight, _isOverlayAboveAnchor);
|
||||||
|
if (anchorPosition == null)
|
||||||
|
{
|
||||||
|
CancelOverlayPrompt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (anchorPosition.X == -1 && anchorPosition.Y == -1)
|
||||||
|
{
|
||||||
|
if (_overlayView.Visibility != ViewStates.Gone)
|
||||||
|
{
|
||||||
|
_overlayView.Visibility = ViewStates.Gone;
|
||||||
|
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Hidden");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (anchorPosition.X == -1)
|
||||||
|
{
|
||||||
|
_isOverlayAboveAnchor = false;
|
||||||
|
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Below Anchor");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (anchorPosition.Y == -1)
|
||||||
|
{
|
||||||
|
_isOverlayAboveAnchor = true;
|
||||||
|
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Above Anchor");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (anchorPosition.X == _lastAnchorX && anchorPosition.Y == _lastAnchorY)
|
||||||
|
{
|
||||||
|
if (_overlayView.Visibility != ViewStates.Visible)
|
||||||
|
{
|
||||||
|
_overlayView.Visibility = ViewStates.Visible;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var layoutParams = AccessibilityHelpers.GetOverlayLayoutParams();
|
||||||
|
layoutParams.X = anchorPosition.X;
|
||||||
|
layoutParams.Y = anchorPosition.Y;
|
||||||
|
|
||||||
|
_lastAnchorX = anchorPosition.X;
|
||||||
|
_lastAnchorY = anchorPosition.Y;
|
||||||
|
|
||||||
|
_windowManager.UpdateViewLayout(_overlayView, layoutParams);
|
||||||
|
|
||||||
|
if (_overlayView.Visibility != ViewStates.Visible)
|
||||||
|
{
|
||||||
|
_overlayView.Visibility = ViewStates.Visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Updated to X:{0} Y:{1}",
|
||||||
|
layoutParams.X, layoutParams.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool SkipPackage(string eventPackageName)
|
private bool SkipPackage(string eventPackageName)
|
||||||
@@ -292,6 +448,10 @@ namespace Bit.Droid.Accessibility
|
|||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
}
|
}
|
||||||
|
if (_broadcasterService == null)
|
||||||
|
{
|
||||||
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadSettingsAsync()
|
private async Task LoadSettingsAsync()
|
||||||
@@ -300,10 +460,13 @@ namespace Bit.Droid.Accessibility
|
|||||||
if (_lastSettingsReload == null || (now - _lastSettingsReload.Value) > _settingsReloadSpan)
|
if (_lastSettingsReload == null || (now - _lastSettingsReload.Value) > _settingsReloadSpan)
|
||||||
{
|
{
|
||||||
_lastSettingsReload = now;
|
_lastSettingsReload = now;
|
||||||
_settingAutofillPasswordField = await _storageService.GetAsync<bool>(
|
var uris = await _storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
|
||||||
Constants.AccessibilityAutofillPasswordFieldKey);
|
if (uris != null)
|
||||||
_settingAutofillPersistNotification = await _storageService.GetAsync<bool>(
|
{
|
||||||
Constants.AccessibilityAutofillPersistNotificationKey);
|
_blacklistedUris = new HashSet<string>(uris);
|
||||||
|
}
|
||||||
|
var isAutoFillTileAdded = await _storageService.GetAsync<bool?>(Constants.AutofillTileAdded);
|
||||||
|
AccessibilityHelpers.IsAutofillTileAdded = isAutoFillTileAdded.GetValueOrDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
src/Android/Accessibility/KnownUsernameField.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
namespace Bit.Droid.Accessibility
|
||||||
|
{
|
||||||
|
public class KnownUsernameField
|
||||||
|
{
|
||||||
|
public KnownUsernameField(string uriAuthority, string uriPathEnd, string usernameViewId)
|
||||||
|
{
|
||||||
|
UriAuthority = uriAuthority;
|
||||||
|
UriPathEnd = uriPathEnd;
|
||||||
|
UsernameViewId = usernameViewId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string UriAuthority { get; set; }
|
||||||
|
public string UriPathEnd { get; set; }
|
||||||
|
public string UsernameViewId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ namespace Bit.Droid.Accessibility
|
|||||||
{
|
{
|
||||||
foreach (var item in this)
|
foreach (var item in this)
|
||||||
{
|
{
|
||||||
|
item.Recycle();
|
||||||
item.Dispose();
|
item.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,7 @@
|
|||||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||||
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
|
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
|
||||||
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
|
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
|
||||||
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
|
<TargetFrameworkVersion>v10.0</TargetFrameworkVersion>
|
||||||
<TargetFrameworkVersion>v9.0</TargetFrameworkVersion>
|
|
||||||
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
|
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
|
||||||
<NuGetPackageImportStamp>
|
<NuGetPackageImportStamp>
|
||||||
</NuGetPackageImportStamp>
|
</NuGetPackageImportStamp>
|
||||||
@@ -30,10 +29,8 @@
|
|||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>3</WarningLevel>
|
<WarningLevel>3</WarningLevel>
|
||||||
<AndroidLinkMode>None</AndroidLinkMode>
|
<AndroidLinkMode>None</AndroidLinkMode>
|
||||||
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
|
|
||||||
<AndroidSupportedAbis />
|
<AndroidSupportedAbis />
|
||||||
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
||||||
<AndroidLinkSkip>Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;BitwardenCore;Xamarin.Android.Net</AndroidLinkSkip>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<DebugSymbols>false</DebugSymbols>
|
<DebugSymbols>false</DebugSymbols>
|
||||||
@@ -46,10 +43,8 @@
|
|||||||
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
|
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
|
||||||
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
||||||
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
|
|
||||||
<AndroidLinkSkip>Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;BitwardenCore;Xamarin.Android.Net</AndroidLinkSkip>
|
|
||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||||
<AndroidLinkMode>Full</AndroidLinkMode>
|
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'FDroid|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'FDroid|AnyCPU'">
|
||||||
<DebugSymbols>false</DebugSymbols>
|
<DebugSymbols>false</DebugSymbols>
|
||||||
@@ -63,18 +58,15 @@
|
|||||||
<DefineConstants>FDROID</DefineConstants>
|
<DefineConstants>FDROID</DefineConstants>
|
||||||
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
|
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
|
||||||
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
||||||
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
|
|
||||||
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||||
<AndroidLinkSkip>Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;BitwardenCore;Xamarin.Android.Net</AndroidLinkSkip>
|
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
|
||||||
<AndroidLinkMode>Full</AndroidLinkMode>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Mono.Android" />
|
<Reference Include="Mono.Android" />
|
||||||
<Reference Include="Mono.Android.Export" />
|
<Reference Include="Mono.Android.Export" />
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Net.Http" />
|
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -83,21 +75,22 @@
|
|||||||
<Version>2.1.0.4</Version>
|
<Version>2.1.0.4</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Portable.BouncyCastle">
|
<PackageReference Include="Portable.BouncyCastle">
|
||||||
<Version>1.8.5</Version>
|
<Version>1.8.6.7</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.Essentials">
|
<PackageReference Include="Xamarin.Essentials">
|
||||||
<Version>1.1.0</Version>
|
<Version>1.5.3.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.Firebase.Messaging">
|
<PackageReference Include="Xamarin.Firebase.Messaging">
|
||||||
<Version>60.1142.1</Version>
|
<Version>71.1740.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.Android.Support.Design" Version="28.0.0.1" />
|
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.0.0" />
|
||||||
<PackageReference Include="Xamarin.Android.Support.v7.AppCompat" Version="28.0.0.1" />
|
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.1.0" />
|
||||||
<PackageReference Include="Xamarin.Android.Support.v4" Version="28.0.0.1" />
|
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0" />
|
||||||
<PackageReference Include="Xamarin.Android.Support.v7.CardView" Version="28.0.0.1" />
|
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0" />
|
||||||
<PackageReference Include="Xamarin.Android.Support.v7.MediaRouter" Version="28.0.0.1" />
|
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.1.0" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.6" />
|
||||||
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
|
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
|
||||||
<Version>60.1142.1</Version>
|
<Version>71.1600.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -107,6 +100,7 @@
|
|||||||
<Compile Include="Accessibility\AccessibilityService.cs" />
|
<Compile Include="Accessibility\AccessibilityService.cs" />
|
||||||
<Compile Include="Accessibility\Browser.cs" />
|
<Compile Include="Accessibility\Browser.cs" />
|
||||||
<Compile Include="Accessibility\NodeList.cs" />
|
<Compile Include="Accessibility\NodeList.cs" />
|
||||||
|
<Compile Include="Accessibility\KnownUsernameField.cs" />
|
||||||
<Compile Include="Autofill\AutofillHelpers.cs" />
|
<Compile Include="Autofill\AutofillHelpers.cs" />
|
||||||
<Compile Include="Autofill\AutofillService.cs" />
|
<Compile Include="Autofill\AutofillService.cs" />
|
||||||
<Compile Include="Autofill\Field.cs" />
|
<Compile Include="Autofill\Field.cs" />
|
||||||
@@ -114,13 +108,15 @@
|
|||||||
<Compile Include="Autofill\FilledItem.cs" />
|
<Compile Include="Autofill\FilledItem.cs" />
|
||||||
<Compile Include="Autofill\Parser.cs" />
|
<Compile Include="Autofill\Parser.cs" />
|
||||||
<Compile Include="Autofill\SavedItem.cs" />
|
<Compile Include="Autofill\SavedItem.cs" />
|
||||||
|
<Compile Include="Effects\FabShadowEffect.cs" />
|
||||||
<Compile Include="Effects\FixedSizeEffect.cs" />
|
<Compile Include="Effects\FixedSizeEffect.cs" />
|
||||||
<Compile Include="Effects\SelectableLabelEffect.cs" />
|
<Compile Include="Effects\SelectableLabelEffect.cs" />
|
||||||
<Compile Include="Effects\TabBarEffect.cs" />
|
<Compile Include="Effects\TabBarEffect.cs" />
|
||||||
<Compile Include="Migration\AndroidKeyStoreStorageService.cs" />
|
|
||||||
<Compile Include="Push\FirebaseInstanceIdService.cs" />
|
<Compile Include="Push\FirebaseInstanceIdService.cs" />
|
||||||
<Compile Include="Push\FirebaseMessagingService.cs" />
|
<Compile Include="Push\FirebaseMessagingService.cs" />
|
||||||
<Compile Include="Receivers\ClearClipboardAlarmReceiver.cs" />
|
<Compile Include="Receivers\ClearClipboardAlarmReceiver.cs" />
|
||||||
|
<Compile Include="Receivers\RestrictionsChangedReceiver.cs" />
|
||||||
|
<Compile Include="Receivers\EventUploadReceiver.cs" />
|
||||||
<Compile Include="Receivers\LockAlarmReceiver.cs" />
|
<Compile Include="Receivers\LockAlarmReceiver.cs" />
|
||||||
<Compile Include="Receivers\PackageReplacedReceiver.cs" />
|
<Compile Include="Receivers\PackageReplacedReceiver.cs" />
|
||||||
<Compile Include="Renderers\CipherViewCellRenderer.cs" />
|
<Compile Include="Renderers\CipherViewCellRenderer.cs" />
|
||||||
@@ -140,11 +136,11 @@
|
|||||||
<Compile Include="Services\CryptoPrimitiveService.cs" />
|
<Compile Include="Services\CryptoPrimitiveService.cs" />
|
||||||
<Compile Include="Services\DeviceActionService.cs" />
|
<Compile Include="Services\DeviceActionService.cs" />
|
||||||
<Compile Include="Services\LocalizeService.cs" />
|
<Compile Include="Services\LocalizeService.cs" />
|
||||||
|
<Compile Include="Tiles\AutofillTileService.cs" />
|
||||||
<Compile Include="Tiles\GeneratorTileService.cs" />
|
<Compile Include="Tiles\GeneratorTileService.cs" />
|
||||||
<Compile Include="Tiles\MyVaultTileService.cs" />
|
<Compile Include="Tiles\MyVaultTileService.cs" />
|
||||||
<Compile Include="Utilities\AndroidHelpers.cs" />
|
<Compile Include="Utilities\AndroidHelpers.cs" />
|
||||||
<Compile Include="Utilities\CustomFingerprintDialogFragment.cs" />
|
<Compile Include="Utilities\AppCenterHelper.cs" />
|
||||||
<Compile Include="Utilities\HockeyAppCrashManagerListener.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidAsset Include="Assets\FontAwesome.ttf" />
|
<AndroidAsset Include="Assets\FontAwesome.ttf" />
|
||||||
@@ -155,10 +151,49 @@
|
|||||||
<GoogleServicesJson Include="google-services.json" />
|
<GoogleServicesJson Include="google-services.json" />
|
||||||
<GoogleServicesJson Include="google-services.json.enc" />
|
<GoogleServicesJson Include="google-services.json.enc" />
|
||||||
<None Include="Properties\AndroidManifest.xml" />
|
<None Include="Properties\AndroidManifest.xml" />
|
||||||
|
<None Include="upload-keystore.jks.enc" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-hdpi\accessibility_overlay.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-hdpi\accessibility_permission.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-hdpi\accessibility_step1.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-hdpi\accessibility_step2.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-hdpi\autofill_enable.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-hdpi\autofill_use.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_overlay.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_permission.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_step1.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_step2.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-xhdpi\autofill_enable.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-xhdpi\autofill_use.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_overlay.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_permission.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_step1.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_step2.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-xxhdpi\autofill_enable.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable-xxhdpi\autofill_use.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable\accessibility_overlay.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable\accessibility_permission.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable\accessibility_step1.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable\accessibility_step2.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable\autofill_enable.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable\autofill_use.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable\icon.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
|
||||||
<AndroidResource Include="Resources\layout\Tabbar.axml" />
|
<AndroidResource Include="Resources\layout\Tabbar.axml" />
|
||||||
<AndroidResource Include="Resources\layout\Toolbar.axml" />
|
<AndroidResource Include="Resources\layout\Toolbar.axml" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher_round.xml" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher.png" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_round.png" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher.png" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_round.png" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher.png" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_round.png" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher.png" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_round.png" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher.png" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_round.png" />
|
||||||
<AndroidResource Include="Resources\values\styles.xml" />
|
<AndroidResource Include="Resources\values\styles.xml" />
|
||||||
<AndroidResource Include="Resources\values\colors.xml" />
|
<AndroidResource Include="Resources\values\colors.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -193,57 +228,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\values\ic_launcher_background.xml" />
|
<AndroidResource Include="Resources\values\ic_launcher_background.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_foreground.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_round.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher_round.xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_foreground.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_round.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_foreground.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_round.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_foreground.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_round.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_foreground.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_round.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\layout\autofill_listitem.xml" />
|
<AndroidResource Include="Resources\layout\autofill_listitem.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -286,87 +270,15 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable\shield.png" />
|
<AndroidResource Include="Resources\drawable\shield.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\autofill_use.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\accessibility_notification.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\accessibility_notification_icon.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\accessibility_step1.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\accessibility_step2.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\autofill_enable.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\autofill_use.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\accessibility_notification.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\accessibility_notification_icon.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\accessibility_step1.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\accessibility_step2.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\autofill_enable.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\yubikey.png" />
|
<AndroidResource Include="Resources\drawable-xhdpi\yubikey.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_notification.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_notification_icon.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_step1.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_step2.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\autofill_enable.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\autofill_use.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\autofill_use.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\logo.png" />
|
<AndroidResource Include="Resources\drawable-xxhdpi\logo.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\yubikey.png" />
|
<AndroidResource Include="Resources\drawable-xxhdpi\yubikey.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_notification.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_notification_icon.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_step1.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_step2.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\autofill_enable.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\lock.png" />
|
<AndroidResource Include="Resources\drawable-hdpi\lock.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -421,21 +333,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\id.png" />
|
<AndroidResource Include="Resources\drawable-xxxhdpi\id.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\icon.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\icon.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\icon.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\icon.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\icon.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable\pencil.png" />
|
<AndroidResource Include="Resources\drawable\pencil.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -553,5 +450,11 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</AndroidResource>
|
</AndroidResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\xml\app_restrictions.xml">
|
||||||
|
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</AndroidResource>
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
||||||
@@ -16,54 +16,85 @@ namespace Bit.Droid.Autofill
|
|||||||
{
|
{
|
||||||
private static int _pendingIntentId = 0;
|
private static int _pendingIntentId = 0;
|
||||||
|
|
||||||
// These browser work natively with the autofill framework
|
// These browsers work natively with the Autofill Framework
|
||||||
|
//
|
||||||
|
// Be sure:
|
||||||
|
// - to keep these entries sorted alphabetically and
|
||||||
|
//
|
||||||
|
// - ... to keep this list in sync with values in AccessibilityHelpers.SupportedBrowsers [Section A], too.
|
||||||
public static HashSet<string> TrustedBrowsers = new HashSet<string>
|
public static HashSet<string> TrustedBrowsers = new HashSet<string>
|
||||||
{
|
{
|
||||||
|
"com.duckduckgo.mobile.android",
|
||||||
"org.mozilla.focus",
|
"org.mozilla.focus",
|
||||||
"org.mozilla.klar",
|
"org.mozilla.klar",
|
||||||
"com.duckduckgo.mobile.android",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// These browsers work using the compatibility shim for the autofill framework
|
// These browsers work using the compatibility shim for the Autofill Framework
|
||||||
|
//
|
||||||
|
// Be sure:
|
||||||
|
// - to keep these entries sorted alphabetically,
|
||||||
|
// - to keep this list in sync with values in Resources/xml/autofillservice.xml, and
|
||||||
|
//
|
||||||
|
// - ... to keep this list in sync with values in AccessibilityHelpers.SupportedBrowsers [Section A], too.
|
||||||
public static HashSet<string> CompatBrowsers = new HashSet<string>
|
public static HashSet<string> CompatBrowsers = new HashSet<string>
|
||||||
{
|
{
|
||||||
"org.mozilla.firefox",
|
"com.amazon.cloud9",
|
||||||
"org.mozilla.firefox_beta",
|
|
||||||
"com.microsoft.emmx",
|
|
||||||
"com.android.chrome",
|
|
||||||
"com.chrome.beta",
|
|
||||||
"com.android.browser",
|
"com.android.browser",
|
||||||
|
"com.android.chrome",
|
||||||
|
"com.avast.android.secure.browser",
|
||||||
|
"com.avg.android.secure.browser",
|
||||||
"com.brave.browser",
|
"com.brave.browser",
|
||||||
|
"com.brave.browser_beta",
|
||||||
|
"com.brave.browser_default",
|
||||||
|
"com.brave.browser_dev",
|
||||||
|
"com.brave.browser_nightly",
|
||||||
|
"com.chrome.beta",
|
||||||
|
"com.chrome.canary",
|
||||||
|
"com.chrome.dev",
|
||||||
|
"com.ecosia.android",
|
||||||
|
"com.google.android.apps.chrome",
|
||||||
|
"com.google.android.apps.chrome_dev",
|
||||||
|
"com.kiwibrowser.browser",
|
||||||
|
"com.microsoft.emmx",
|
||||||
|
"com.naver.whale",
|
||||||
"com.opera.browser",
|
"com.opera.browser",
|
||||||
"com.opera.browser.beta",
|
"com.opera.browser.beta",
|
||||||
"com.opera.mini.native",
|
"com.opera.mini.native",
|
||||||
"com.chrome.dev",
|
"com.opera.mini.native.beta",
|
||||||
"com.chrome.canary",
|
"com.opera.touch",
|
||||||
"com.google.android.apps.chrome",
|
"com.qwant.liberty",
|
||||||
"com.google.android.apps.chrome_dev",
|
|
||||||
"com.yandex.browser",
|
|
||||||
"com.sec.android.app.sbrowser",
|
"com.sec.android.app.sbrowser",
|
||||||
"com.sec.android.app.sbrowser.beta",
|
"com.sec.android.app.sbrowser.beta",
|
||||||
"org.codeaurora.swe.browser",
|
"com.stoutner.privacybrowser.free",
|
||||||
"com.amazon.cloud9",
|
"com.stoutner.privacybrowser.standard",
|
||||||
|
"com.vivaldi.browser",
|
||||||
|
"com.vivaldi.browser.snapshot",
|
||||||
|
"com.vivaldi.browser.sopranos",
|
||||||
|
"com.yandex.browser",
|
||||||
"mark.via.gp",
|
"mark.via.gp",
|
||||||
|
"org.adblockplus.browser",
|
||||||
|
"org.adblockplus.browser.beta",
|
||||||
"org.bromite.bromite",
|
"org.bromite.bromite",
|
||||||
"org.chromium.chrome",
|
"org.chromium.chrome",
|
||||||
"com.kiwibrowser.browser",
|
"org.codeaurora.swe.browser",
|
||||||
"com.ecosia.android",
|
"org.gnu.icecat",
|
||||||
"com.opera.mini.native.beta",
|
|
||||||
"org.mozilla.fennec_aurora",
|
|
||||||
"com.qwant.liberty",
|
|
||||||
"com.opera.touch",
|
|
||||||
"org.mozilla.fenix",
|
"org.mozilla.fenix",
|
||||||
|
"org.mozilla.fenix.nightly",
|
||||||
|
"org.mozilla.fennec_aurora",
|
||||||
|
"org.mozilla.fennec_fdroid",
|
||||||
|
"org.mozilla.firefox",
|
||||||
|
"org.mozilla.firefox_beta",
|
||||||
"org.mozilla.reference.browser",
|
"org.mozilla.reference.browser",
|
||||||
"org.mozilla.rocket",
|
"org.mozilla.rocket",
|
||||||
|
"org.torproject.torbrowser",
|
||||||
|
"org.torproject.torbrowser_alpha",
|
||||||
};
|
};
|
||||||
|
|
||||||
// The URLs are blacklisted from autofilling
|
// The URLs are blacklisted from autofilling
|
||||||
public static HashSet<string> BlacklistedUris = new HashSet<string>
|
public static HashSet<string> BlacklistedUris = new HashSet<string>
|
||||||
{
|
{
|
||||||
"androidapp://android",
|
"androidapp://android",
|
||||||
|
"androidapp://com.android.settings",
|
||||||
"androidapp://com.x8bit.bitwarden",
|
"androidapp://com.x8bit.bitwarden",
|
||||||
"androidapp://com.oneplus.applocker",
|
"androidapp://com.oneplus.applocker",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace Bit.Droid.Autofill
|
|||||||
public class AutofillService : Android.Service.Autofill.AutofillService
|
public class AutofillService : Android.Service.Autofill.AutofillService
|
||||||
{
|
{
|
||||||
private ICipherService _cipherService;
|
private ICipherService _cipherService;
|
||||||
private ILockService _lockService;
|
private IVaultTimeoutService _vaultTimeoutService;
|
||||||
private IStorageService _storageService;
|
private IStorageService _storageService;
|
||||||
|
|
||||||
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
|
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
|
||||||
@@ -47,13 +47,13 @@ namespace Bit.Droid.Autofill
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_lockService == null)
|
if (_vaultTimeoutService == null)
|
||||||
{
|
{
|
||||||
_lockService = ServiceContainer.Resolve<ILockService>("lockService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<FilledItem> items = null;
|
List<FilledItem> items = null;
|
||||||
var locked = await _lockService.IsLockedAsync();
|
var locked = await _vaultTimeoutService.IsLockedAsync();
|
||||||
if (!locked)
|
if (!locked)
|
||||||
{
|
{
|
||||||
if (_cipherService == null)
|
if (_cipherService == null)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Bit.Core;
|
|||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Android.OS;
|
||||||
|
|
||||||
namespace Bit.Droid.Autofill
|
namespace Bit.Droid.Autofill
|
||||||
{
|
{
|
||||||
@@ -17,7 +18,7 @@ namespace Bit.Droid.Autofill
|
|||||||
private readonly AssistStructure _structure;
|
private readonly AssistStructure _structure;
|
||||||
private string _uri;
|
private string _uri;
|
||||||
private string _packageName;
|
private string _packageName;
|
||||||
private string _webDomain;
|
private string _website;
|
||||||
|
|
||||||
public Parser(AssistStructure structure, Context applicationContext)
|
public Parser(AssistStructure structure, Context applicationContext)
|
||||||
{
|
{
|
||||||
@@ -36,14 +37,14 @@ namespace Bit.Droid.Autofill
|
|||||||
{
|
{
|
||||||
return _uri;
|
return _uri;
|
||||||
}
|
}
|
||||||
var webDomainNull = string.IsNullOrWhiteSpace(WebDomain);
|
var websiteNull = string.IsNullOrWhiteSpace(Website);
|
||||||
if(webDomainNull && string.IsNullOrWhiteSpace(PackageName))
|
if (websiteNull && string.IsNullOrWhiteSpace(PackageName))
|
||||||
{
|
{
|
||||||
_uri = null;
|
_uri = null;
|
||||||
}
|
}
|
||||||
else if(!webDomainNull)
|
else if (!websiteNull)
|
||||||
{
|
{
|
||||||
_uri = string.Concat("http://", WebDomain);
|
_uri = Website;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -66,16 +67,16 @@ namespace Bit.Droid.Autofill
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string WebDomain
|
public string Website
|
||||||
{
|
{
|
||||||
get => _webDomain;
|
get => _website;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
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()
|
public void Parse()
|
||||||
{
|
{
|
||||||
|
string titlePackageId = null;
|
||||||
for (var i = 0; i < _structure.WindowNodeCount; i++)
|
for (var i = 0; i < _structure.WindowNodeCount; i++)
|
||||||
{
|
{
|
||||||
var node = _structure.GetWindowNodeAt(i);
|
var node = _structure.GetWindowNodeAt(i);
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
titlePackageId = GetTitlePackageId(node);
|
||||||
|
}
|
||||||
ParseNode(node.RootViewNode);
|
ParseNode(node.RootViewNode);
|
||||||
}
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace(PackageName) && string.IsNullOrWhiteSpace(Website))
|
||||||
|
{
|
||||||
|
PackageName = titlePackageId;
|
||||||
|
}
|
||||||
if (!AutofillHelpers.TrustedBrowsers.Contains(PackageName) &&
|
if (!AutofillHelpers.TrustedBrowsers.Contains(PackageName) &&
|
||||||
!AutofillHelpers.CompatBrowsers.Contains(PackageName))
|
!AutofillHelpers.CompatBrowsers.Contains(PackageName))
|
||||||
{
|
{
|
||||||
WebDomain = null;
|
Website = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,10 +145,32 @@ namespace Bit.Droid.Autofill
|
|||||||
{
|
{
|
||||||
PackageName = node.IdPackage;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
src/Android/Effects/FabShadowEffect.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using Android.Graphics.Drawables;
|
||||||
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Droid.Effects;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
[assembly: ExportEffect(typeof(FabShadowEffect), "FabShadowEffect")]
|
||||||
|
namespace Bit.Droid.Effects
|
||||||
|
{
|
||||||
|
public class FabShadowEffect : PlatformEffect
|
||||||
|
{
|
||||||
|
protected override void OnAttached ()
|
||||||
|
{
|
||||||
|
if (Control is Android.Widget.Button button)
|
||||||
|
{
|
||||||
|
var gd = new GradientDrawable();
|
||||||
|
gd.SetColor(ThemeManager.GetResourceColor("FabColor").ToAndroid());
|
||||||
|
gd.SetCornerRadius(100);
|
||||||
|
|
||||||
|
button.SetBackground(gd);
|
||||||
|
button.Elevation = 6;
|
||||||
|
button.TranslationZ = 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDetached ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
using Android.Support.Design.BottomNavigation;
|
using Android.Widget;
|
||||||
using Android.Support.Design.Widget;
|
|
||||||
using Android.Views;
|
|
||||||
using Android.Widget;
|
|
||||||
using Bit.Droid.Effects;
|
using Bit.Droid.Effects;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
using Android.Support.Design.BottomNavigation;
|
using Android.Widget;
|
||||||
using Android.Support.Design.Widget;
|
|
||||||
using Android.Views;
|
|
||||||
using Android.Widget;
|
|
||||||
using Bit.Droid.Effects;
|
using Bit.Droid.Effects;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Android.Support.Design.BottomNavigation;
|
using Android.Views;
|
||||||
using Android.Support.Design.Widget;
|
|
||||||
using Android.Views;
|
|
||||||
using Bit.Droid.Effects;
|
using Bit.Droid.Effects;
|
||||||
|
using Google.Android.Material.BottomNavigation;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ using Bit.Core.Enums;
|
|||||||
using Android.Nfc;
|
using Android.Nfc;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using AndroidX.Core.Content;
|
||||||
|
|
||||||
namespace Bit.Droid
|
namespace Bit.Droid
|
||||||
{
|
{
|
||||||
@@ -29,17 +30,16 @@ namespace Bit.Droid
|
|||||||
[Register("com.x8bit.bitwarden.MainActivity")]
|
[Register("com.x8bit.bitwarden.MainActivity")]
|
||||||
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
|
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
|
||||||
{
|
{
|
||||||
private const string HockeyAppId = "d3834185b4a643479047b86c65293d42";
|
|
||||||
|
|
||||||
private IDeviceActionService _deviceActionService;
|
private IDeviceActionService _deviceActionService;
|
||||||
private IMessagingService _messagingService;
|
private IMessagingService _messagingService;
|
||||||
private IBroadcasterService _broadcasterService;
|
private IBroadcasterService _broadcasterService;
|
||||||
private IUserService _userService;
|
private IUserService _userService;
|
||||||
private IAppIdService _appIdService;
|
private IAppIdService _appIdService;
|
||||||
private IStorageService _storageService;
|
private IStorageService _storageService;
|
||||||
private IStateService _stateService;
|
private IEventService _eventService;
|
||||||
private PendingIntent _lockAlarmPendingIntent;
|
private PendingIntent _vaultTimeoutAlarmPendingIntent;
|
||||||
private PendingIntent _clearClipboardPendingIntent;
|
private PendingIntent _clearClipboardPendingIntent;
|
||||||
|
private PendingIntent _eventUploadPendingIntent;
|
||||||
private AppOptions _appOptions;
|
private AppOptions _appOptions;
|
||||||
private string _activityKey = $"{nameof(MainActivity)}_{Java.Lang.JavaSystem.CurrentTimeMillis().ToString()}";
|
private string _activityKey = $"{nameof(MainActivity)}_{Java.Lang.JavaSystem.CurrentTimeMillis().ToString()}";
|
||||||
private Java.Util.Regex.Pattern _otpPattern =
|
private Java.Util.Regex.Pattern _otpPattern =
|
||||||
@@ -47,8 +47,11 @@ namespace Bit.Droid
|
|||||||
|
|
||||||
protected override void OnCreate(Bundle savedInstanceState)
|
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));
|
var alarmIntent = new Intent(this, typeof(LockAlarmReceiver));
|
||||||
_lockAlarmPendingIntent = PendingIntent.GetBroadcast(this, 0, alarmIntent,
|
_vaultTimeoutAlarmPendingIntent = PendingIntent.GetBroadcast(this, 0, alarmIntent,
|
||||||
PendingIntentFlags.UpdateCurrent);
|
PendingIntentFlags.UpdateCurrent);
|
||||||
var clearClipboardIntent = new Intent(this, typeof(ClearClipboardAlarmReceiver));
|
var clearClipboardIntent = new Intent(this, typeof(ClearClipboardAlarmReceiver));
|
||||||
_clearClipboardPendingIntent = PendingIntent.GetBroadcast(this, 0, clearClipboardIntent,
|
_clearClipboardPendingIntent = PendingIntent.GetBroadcast(this, 0, clearClipboardIntent,
|
||||||
@@ -63,12 +66,12 @@ namespace Bit.Droid
|
|||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||||
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||||
|
|
||||||
TabLayoutResource = Resource.Layout.Tabbar;
|
TabLayoutResource = Resource.Layout.Tabbar;
|
||||||
ToolbarResource = Resource.Layout.Toolbar;
|
ToolbarResource = Resource.Layout.Toolbar;
|
||||||
|
|
||||||
UpdateTheme(ThemeManager.GetTheme());
|
UpdateTheme(ThemeManager.GetTheme(true));
|
||||||
base.OnCreate(savedInstanceState);
|
base.OnCreate(savedInstanceState);
|
||||||
if (!CoreHelpers.InDebugMode())
|
if (!CoreHelpers.InDebugMode())
|
||||||
{
|
{
|
||||||
@@ -76,9 +79,8 @@ namespace Bit.Droid
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if !FDROID
|
#if !FDROID
|
||||||
var hockeyAppListener = new HockeyAppCrashManagerListener(_appIdService, _userService);
|
var appCenterHelper = new AppCenterHelper(_appIdService, _userService);
|
||||||
var hockeyAppTask = hockeyAppListener.InitAsync();
|
var appCenterTask = appCenterHelper.InitAsync();
|
||||||
HockeyApp.Android.CrashManager.Register(this, HockeyAppId, hockeyAppListener);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
|
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
|
||||||
@@ -88,18 +90,26 @@ namespace Bit.Droid
|
|||||||
|
|
||||||
_broadcasterService.Subscribe(_activityKey, (message) =>
|
_broadcasterService.Subscribe(_activityKey, (message) =>
|
||||||
{
|
{
|
||||||
if(message.Command == "scheduleLockTimer")
|
if (message.Command == "scheduleVaultTimeoutTimer")
|
||||||
{
|
{
|
||||||
var lockOptionMinutes = (int)message.Data;
|
|
||||||
var lockOptionMs = lockOptionMinutes * 60000;
|
|
||||||
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + lockOptionMs + 10;
|
|
||||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
||||||
alarmManager.Set(AlarmType.RtcWakeup, triggerMs, _lockAlarmPendingIntent);
|
var vaultTimeoutMinutes = (int)message.Data;
|
||||||
|
var vaultTimeoutMs = vaultTimeoutMinutes * 60000;
|
||||||
|
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + vaultTimeoutMs + 10;
|
||||||
|
alarmManager.Set(AlarmType.RtcWakeup, triggerMs, _vaultTimeoutAlarmPendingIntent);
|
||||||
}
|
}
|
||||||
else if(message.Command == "cancelLockTimer")
|
else if (message.Command == "cancelVaultTimeoutTimer")
|
||||||
{
|
{
|
||||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
||||||
alarmManager.Cancel(_lockAlarmPendingIntent);
|
alarmManager.Cancel(_vaultTimeoutAlarmPendingIntent);
|
||||||
|
}
|
||||||
|
else if (message.Command == "startEventTimer")
|
||||||
|
{
|
||||||
|
StartEventAlarm();
|
||||||
|
}
|
||||||
|
else if (message.Command == "stopEventTimer")
|
||||||
|
{
|
||||||
|
var task = StopEventAlarmAsync();
|
||||||
}
|
}
|
||||||
else if (message.Command == "finishMainActivity")
|
else if (message.Command == "finishMainActivity")
|
||||||
{
|
{
|
||||||
@@ -141,6 +151,7 @@ namespace Bit.Droid
|
|||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
|
var setRestrictions = AndroidHelpers.SetPreconfiguredRestrictionSettingsAsync(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnNewIntent(Intent intent)
|
protected override void OnNewIntent(Intent intent)
|
||||||
@@ -190,7 +201,8 @@ namespace Bit.Droid
|
|||||||
|
|
||||||
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
|
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
|
||||||
{
|
{
|
||||||
if(requestCode == Constants.SelectFileRequestCode && resultCode == Result.Ok)
|
if (resultCode == Result.Ok &&
|
||||||
|
(requestCode == Constants.SelectFileRequestCode || requestCode == Constants.SaveFileRequestCode))
|
||||||
{
|
{
|
||||||
Android.Net.Uri uri = null;
|
Android.Net.Uri uri = null;
|
||||||
string fileName = null;
|
string fileName = null;
|
||||||
@@ -202,9 +214,8 @@ namespace Bit.Droid
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// camera
|
// camera
|
||||||
var root = new Java.IO.File(Android.OS.Environment.ExternalStorageDirectory, "bitwarden");
|
var file = new Java.IO.File(FilesDir, "temp_camera_photo.jpg");
|
||||||
var file = new Java.IO.File(root, "temp_camera_photo.jpg");
|
uri = FileProvider.GetUriForFile(this, "com.x8bit.bitwarden.fileprovider", file);
|
||||||
uri = Android.Net.Uri.FromFile(file);
|
|
||||||
fileName = $"photo_{DateTime.UtcNow.ToString("yyyyMMddHHmmss")}.jpg";
|
fileName = $"photo_{DateTime.UtcNow.ToString("yyyyMMddHHmmss")}.jpg";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +223,14 @@ namespace Bit.Droid
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (requestCode == Constants.SaveFileRequestCode)
|
||||||
|
{
|
||||||
|
_messagingService.Send("selectSaveFileResult",
|
||||||
|
new Tuple<string, string>(uri.ToString(), fileName));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var stream = ContentResolver.OpenInputStream(uri))
|
using (var stream = ContentResolver.OpenInputStream(uri))
|
||||||
@@ -367,10 +386,22 @@ namespace Bit.Droid
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await _stateService.SaveAsync(Constants.LastClipboardValueKey, data.Item1);
|
|
||||||
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + clearMs.Value;
|
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + clearMs.Value;
|
||||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
||||||
alarmManager.Set(AlarmType.Rtc, triggerMs, _clearClipboardPendingIntent);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,12 +40,8 @@ namespace Bit.Droid
|
|||||||
if (ServiceContainer.RegisteredServices.Count == 0)
|
if (ServiceContainer.RegisteredServices.Count == 0)
|
||||||
{
|
{
|
||||||
RegisterLocalServices();
|
RegisterLocalServices();
|
||||||
ServiceContainer.Init(new AndroidClientHandler());
|
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
if(App.Migration.MigrationHelpers.NeedsMigration())
|
ServiceContainer.Init(deviceActionService.DeviceUserAgent);
|
||||||
{
|
|
||||||
var task = App.Migration.MigrationHelpers.PerformMigrationAsync();
|
|
||||||
Task.Delay(2000).Wait();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#if !FDROID
|
#if !FDROID
|
||||||
if (Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
|
if (Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
|
||||||
@@ -73,14 +69,7 @@ namespace Bit.Droid
|
|||||||
private void RegisterLocalServices()
|
private void RegisterLocalServices()
|
||||||
{
|
{
|
||||||
ServiceContainer.Register<ILogService>("logService", new AndroidLogService());
|
ServiceContainer.Register<ILogService>("logService", new AndroidLogService());
|
||||||
ServiceContainer.Register("settingsShim", new App.Migration.SettingsShim());
|
|
||||||
if(App.Migration.MigrationHelpers.NeedsMigration())
|
|
||||||
{
|
|
||||||
ServiceContainer.Register<App.Migration.Abstractions.IOldSecureStorageService>(
|
|
||||||
"oldSecureStorageService", new Migration.AndroidKeyStoreStorageService());
|
|
||||||
}
|
|
||||||
|
|
||||||
Refractored.FabControl.Droid.FloatingActionButtonViewRenderer.Init();
|
|
||||||
// Note: This might cause a race condition. Investigate more.
|
// Note: This might cause a race condition. Investigate more.
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
@@ -93,7 +82,6 @@ namespace Bit.Droid
|
|||||||
ZXing.Net.Mobile.Forms.Android.Platform.Init();
|
ZXing.Net.Mobile.Forms.Android.Platform.Init();
|
||||||
});
|
});
|
||||||
CrossFingerprint.SetCurrentActivityResolver(() => CrossCurrentActivity.Current.Activity);
|
CrossFingerprint.SetCurrentActivityResolver(() => CrossCurrentActivity.Current.Activity);
|
||||||
CrossFingerprint.SetDialogFragmentType<CustomFingerprintDialogFragment>();
|
|
||||||
|
|
||||||
var preferencesStorage = new PreferencesStorageService(null);
|
var preferencesStorage = new PreferencesStorageService(null);
|
||||||
var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
|
var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
|
||||||
@@ -107,7 +95,7 @@ namespace Bit.Droid
|
|||||||
var cryptoPrimitiveService = new CryptoPrimitiveService();
|
var cryptoPrimitiveService = new CryptoPrimitiveService();
|
||||||
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
|
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
|
||||||
var deviceActionService = new DeviceActionService(mobileStorageService, messagingService,
|
var deviceActionService = new DeviceActionService(mobileStorageService, messagingService,
|
||||||
broadcasterService);
|
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
|
||||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
||||||
broadcasterService);
|
broadcasterService);
|
||||||
|
|
||||||
@@ -148,10 +136,10 @@ namespace Bit.Droid
|
|||||||
|
|
||||||
private async Task BootstrapAsync()
|
private async Task BootstrapAsync()
|
||||||
{
|
{
|
||||||
var disableFavicon = await ServiceContainer.Resolve<IStorageService>("storageService").GetAsync<bool?>(
|
var disableFavicon = await ServiceContainer.Resolve<IStorageService>("storageService")
|
||||||
Constants.DisableFaviconKey);
|
.GetAsync<bool?>(Constants.DisableFaviconKey);
|
||||||
await ServiceContainer.Resolve<IStateService>("stateService").SaveAsync(Constants.DisableFaviconKey,
|
await ServiceContainer.Resolve<IStateService>("stateService").SaveAsync(
|
||||||
disableFavicon);
|
Constants.DisableFaviconKey, disableFavicon);
|
||||||
await ServiceContainer.Resolve<IEnvironmentService>("environmentService").SetUrlsFromStorageAsync();
|
await ServiceContainer.Resolve<IEnvironmentService>("environmentService").SetUrlsFromStorageAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,373 +0,0 @@
|
|||||||
using Java.Security;
|
|
||||||
using Javax.Crypto;
|
|
||||||
using Android.OS;
|
|
||||||
using Bit.App.Abstractions;
|
|
||||||
using System;
|
|
||||||
using Android.Security;
|
|
||||||
using Javax.Security.Auth.X500;
|
|
||||||
using Java.Math;
|
|
||||||
using Android.Security.Keystore;
|
|
||||||
using Android.App;
|
|
||||||
using Java.Util;
|
|
||||||
using Javax.Crypto.Spec;
|
|
||||||
using Android.Preferences;
|
|
||||||
using Bit.App.Migration;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using Bit.App.Migration.Abstractions;
|
|
||||||
|
|
||||||
namespace Bit.Droid.Migration
|
|
||||||
{
|
|
||||||
public class AndroidKeyStoreStorageService : IOldSecureStorageService
|
|
||||||
{
|
|
||||||
private const string AndroidKeyStore = "AndroidKeyStore";
|
|
||||||
private const string AesMode = "AES/GCM/NoPadding";
|
|
||||||
|
|
||||||
private const string KeyAlias = "bitwardenKey2";
|
|
||||||
private const string KeyAliasV1 = "bitwardenKey";
|
|
||||||
|
|
||||||
private const string SettingsFormat = "ksSecured2:{0}";
|
|
||||||
private const string SettingsFormatV1 = "ksSecured:{0}";
|
|
||||||
|
|
||||||
private const string AesKey = "ksSecured2:aesKeyForService";
|
|
||||||
private const string AesKeyV1 = "ksSecured:aesKeyForService";
|
|
||||||
|
|
||||||
private readonly string _rsaMode;
|
|
||||||
private readonly bool _oldAndroid;
|
|
||||||
private readonly SettingsShim _settings;
|
|
||||||
private readonly KeyStore _keyStore;
|
|
||||||
|
|
||||||
public AndroidKeyStoreStorageService()
|
|
||||||
{
|
|
||||||
_oldAndroid = Build.VERSION.SdkInt < BuildVersionCodes.M;
|
|
||||||
_rsaMode = _oldAndroid ? "RSA/ECB/PKCS1Padding" : "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
|
|
||||||
|
|
||||||
_settings = ServiceContainer.Resolve<SettingsShim>("settingsShim");
|
|
||||||
|
|
||||||
_keyStore = KeyStore.GetInstance(AndroidKeyStore);
|
|
||||||
_keyStore.Load(null);
|
|
||||||
|
|
||||||
/*
|
|
||||||
try
|
|
||||||
{
|
|
||||||
GenerateStoreKey(true);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
GenerateStoreKey(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
GenerateAesKey();
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Contains(string key)
|
|
||||||
{
|
|
||||||
return _settings.Contains(string.Format(SettingsFormat, key)) ||
|
|
||||||
_settings.Contains(string.Format(SettingsFormatV1, key));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Delete(string key)
|
|
||||||
{
|
|
||||||
CleanupOld(key);
|
|
||||||
|
|
||||||
var formattedKey = string.Format(SettingsFormat, key);
|
|
||||||
if(_settings.Contains(formattedKey))
|
|
||||||
{
|
|
||||||
_settings.Remove(formattedKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] Retrieve(string key)
|
|
||||||
{
|
|
||||||
var formattedKey = string.Format(SettingsFormat, key);
|
|
||||||
if(!_settings.Contains(formattedKey))
|
|
||||||
{
|
|
||||||
return TryGetAndMigrate(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
var cs = _settings.GetValueOrDefault(formattedKey, null);
|
|
||||||
if(string.IsNullOrWhiteSpace(cs))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var aesKey = GetAesKey();
|
|
||||||
if(aesKey == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return App.Migration.Crypto.AesCbcDecrypt(new App.Migration.Models.CipherString(cs), aesKey);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Console.WriteLine("Failed to decrypt from secure storage.");
|
|
||||||
_settings.Remove(formattedKey);
|
|
||||||
//Utilities.SendCrashEmail(e);
|
|
||||||
//Utilities.SaveCrashFile(e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Store(string key, byte[] dataBytes)
|
|
||||||
{
|
|
||||||
var formattedKey = string.Format(SettingsFormat, key);
|
|
||||||
CleanupOld(key);
|
|
||||||
if(dataBytes == null)
|
|
||||||
{
|
|
||||||
_settings.Remove(formattedKey);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var aesKey = GetAesKey();
|
|
||||||
if(aesKey == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var cipherString = App.Migration.Crypto.AesCbcEncrypt(dataBytes, aesKey);
|
|
||||||
_settings.AddOrUpdateValue(formattedKey, cipherString.EncryptedString);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Console.WriteLine("Failed to encrypt to secure storage.");
|
|
||||||
//Utilities.SendCrashEmail(e);
|
|
||||||
//Utilities.SaveCrashFile(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GenerateStoreKey(bool withDate)
|
|
||||||
{
|
|
||||||
if(_keyStore.ContainsAlias(KeyAlias))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClearSettings();
|
|
||||||
|
|
||||||
var end = Calendar.Instance;
|
|
||||||
end.Add(CalendarField.Year, 99);
|
|
||||||
|
|
||||||
if(_oldAndroid)
|
|
||||||
{
|
|
||||||
var subject = new X500Principal($"CN={KeyAlias}");
|
|
||||||
|
|
||||||
var builder = new KeyPairGeneratorSpec.Builder(Application.Context)
|
|
||||||
.SetAlias(KeyAlias)
|
|
||||||
.SetSubject(subject)
|
|
||||||
.SetSerialNumber(BigInteger.Ten);
|
|
||||||
|
|
||||||
if(withDate)
|
|
||||||
{
|
|
||||||
builder.SetStartDate(new Date(0)).SetEndDate(end.Time);
|
|
||||||
}
|
|
||||||
|
|
||||||
var spec = builder.Build();
|
|
||||||
var gen = KeyPairGenerator.GetInstance(KeyProperties.KeyAlgorithmRsa, AndroidKeyStore);
|
|
||||||
gen.Initialize(spec);
|
|
||||||
gen.GenerateKeyPair();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var builder = new KeyGenParameterSpec.Builder(KeyAlias, KeyStorePurpose.Decrypt | KeyStorePurpose.Encrypt)
|
|
||||||
.SetBlockModes(KeyProperties.BlockModeGcm)
|
|
||||||
.SetEncryptionPaddings(KeyProperties.EncryptionPaddingNone);
|
|
||||||
|
|
||||||
if(withDate)
|
|
||||||
{
|
|
||||||
builder.SetKeyValidityStart(new Date(0)).SetKeyValidityEnd(end.Time);
|
|
||||||
}
|
|
||||||
|
|
||||||
var spec = builder.Build();
|
|
||||||
var gen = KeyGenerator.GetInstance(KeyProperties.KeyAlgorithmAes, AndroidKeyStore);
|
|
||||||
gen.Init(spec);
|
|
||||||
gen.GenerateKey();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private KeyStore.PrivateKeyEntry GetRsaKeyEntry(string alias)
|
|
||||||
{
|
|
||||||
return _keyStore.GetEntry(alias, null) as KeyStore.PrivateKeyEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GenerateAesKey()
|
|
||||||
{
|
|
||||||
if(_settings.Contains(AesKey))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var key = App.Migration.Crypto.RandomBytes(512 / 8);
|
|
||||||
var encKey = _oldAndroid ? RsaEncrypt(key) : AesEncrypt(key);
|
|
||||||
_settings.AddOrUpdateValue(AesKey, encKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
private App.Migration.Models.SymmetricCryptoKey GetAesKey(bool v1 = false)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var aesKey = v1 ? AesKeyV1 : AesKey;
|
|
||||||
if(!_settings.Contains(aesKey))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var encKey = _settings.GetValueOrDefault(aesKey, null);
|
|
||||||
if(string.IsNullOrWhiteSpace(encKey))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_oldAndroid || v1)
|
|
||||||
{
|
|
||||||
var encKeyBytes = Convert.FromBase64String(encKey);
|
|
||||||
var key = RsaDecrypt(encKeyBytes, v1);
|
|
||||||
return new App.Migration.Models.SymmetricCryptoKey(key);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var parts = encKey.Split('|');
|
|
||||||
if(parts.Length < 2)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ivBytes = Convert.FromBase64String(parts[0]);
|
|
||||||
var encKeyBytes = Convert.FromBase64String(parts[1]);
|
|
||||||
var key = AesDecrypt(ivBytes, encKeyBytes);
|
|
||||||
return new App.Migration.Models.SymmetricCryptoKey(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Console.WriteLine("Cannot get AesKey.");
|
|
||||||
_keyStore.DeleteEntry(KeyAlias);
|
|
||||||
_settings.Remove(AesKey);
|
|
||||||
if(!v1)
|
|
||||||
{
|
|
||||||
//Utilities.SendCrashEmail(e);
|
|
||||||
//Utilities.SaveCrashFile(e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string AesEncrypt(byte[] input)
|
|
||||||
{
|
|
||||||
using(var entry = _keyStore.GetKey(KeyAlias, null))
|
|
||||||
using(var cipher = Cipher.GetInstance(AesMode))
|
|
||||||
{
|
|
||||||
cipher.Init(CipherMode.EncryptMode, entry);
|
|
||||||
var encBytes = cipher.DoFinal(input);
|
|
||||||
var ivBytes = cipher.GetIV();
|
|
||||||
return $"{Convert.ToBase64String(ivBytes)}|{Convert.ToBase64String(encBytes)}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] AesDecrypt(byte[] iv, byte[] encData)
|
|
||||||
{
|
|
||||||
using(var entry = _keyStore.GetKey(KeyAlias, null))
|
|
||||||
using(var cipher = Cipher.GetInstance(AesMode))
|
|
||||||
{
|
|
||||||
var spec = new GCMParameterSpec(128, iv);
|
|
||||||
cipher.Init(CipherMode.DecryptMode, entry, spec);
|
|
||||||
var decBytes = cipher.DoFinal(encData);
|
|
||||||
return decBytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string RsaEncrypt(byte[] data)
|
|
||||||
{
|
|
||||||
using(var entry = GetRsaKeyEntry(KeyAlias))
|
|
||||||
using(var cipher = Cipher.GetInstance(_rsaMode))
|
|
||||||
{
|
|
||||||
cipher.Init(CipherMode.EncryptMode, entry.Certificate.PublicKey);
|
|
||||||
var cipherText = cipher.DoFinal(data);
|
|
||||||
return Convert.ToBase64String(cipherText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] RsaDecrypt(byte[] encData, bool v1)
|
|
||||||
{
|
|
||||||
using(var entry = GetRsaKeyEntry(v1 ? KeyAliasV1 : KeyAlias))
|
|
||||||
using(var cipher = Cipher.GetInstance(_rsaMode))
|
|
||||||
{
|
|
||||||
if(_oldAndroid)
|
|
||||||
{
|
|
||||||
cipher.Init(CipherMode.DecryptMode, entry.PrivateKey);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cipher.Init(CipherMode.DecryptMode, entry.PrivateKey, OAEPParameterSpec.Default);
|
|
||||||
}
|
|
||||||
|
|
||||||
var plainText = cipher.DoFinal(encData);
|
|
||||||
return plainText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] TryGetAndMigrate(string key)
|
|
||||||
{
|
|
||||||
var formattedKeyV1 = string.Format(SettingsFormatV1, key);
|
|
||||||
if(_settings.Contains(formattedKeyV1))
|
|
||||||
{
|
|
||||||
var aesKeyV1 = GetAesKey(true);
|
|
||||||
if(aesKeyV1 != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var cs = _settings.GetValueOrDefault(formattedKeyV1, null);
|
|
||||||
var value = App.Migration.Crypto.AesCbcDecrypt(new App.Migration.Models.CipherString(cs), aesKeyV1);
|
|
||||||
Store(key, value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Console.WriteLine("Failed to decrypt v1 from secure storage.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_settings.Remove(formattedKeyV1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CleanupOld(string key)
|
|
||||||
{
|
|
||||||
var formattedKeyV1 = string.Format(SettingsFormatV1, key);
|
|
||||||
if(_settings.Contains(formattedKeyV1))
|
|
||||||
{
|
|
||||||
_settings.Remove(formattedKeyV1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearSettings(string format = SettingsFormat)
|
|
||||||
{
|
|
||||||
var prefix = string.Format(format, string.Empty);
|
|
||||||
|
|
||||||
using(var sharedPreferences = PreferenceManager.GetDefaultSharedPreferences(Application.Context))
|
|
||||||
using(var sharedPreferencesEditor = sharedPreferences.Edit())
|
|
||||||
{
|
|
||||||
var removed = false;
|
|
||||||
foreach(var pref in sharedPreferences.All)
|
|
||||||
{
|
|
||||||
if(pref.Key.StartsWith(prefix))
|
|
||||||
{
|
|
||||||
removed = true;
|
|
||||||
sharedPreferencesEditor.Remove(pref.Key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(removed)
|
|
||||||
{
|
|
||||||
sharedPreferencesEditor.Commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,17 +3,19 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:versionCode="1"
|
android:versionCode="1"
|
||||||
android:versionName="2.0.5"
|
android:versionName="2.4.1"
|
||||||
package="com.x8bit.bitwarden">
|
package="com.x8bit.bitwarden">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.NFC" />
|
<uses-permission android:name="android.permission.NFC" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||||
|
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||||
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
|
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||||
@@ -28,7 +30,7 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:networkSecurityConfig="@xml/network_security_config">
|
android:networkSecurityConfig="@xml/network_security_config">
|
||||||
<provider
|
<provider
|
||||||
android:name="android.support.v4.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="com.x8bit.bitwarden.fileprovider"
|
android:authorities="com.x8bit.bitwarden.fileprovider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
@@ -52,5 +54,13 @@
|
|||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<meta-data android:name="android.max_aspect" android:value="2.1" />
|
<meta-data android:name="android.max_aspect" android:value="2.1" />
|
||||||
|
<meta-data android:name="android.content.APP_RESTRICTIONS" android:resource="@xml/app_restrictions" />
|
||||||
|
|
||||||
|
<!-- Support for Samsung "Multi Window" mode (for Android < 7.0 users) -->
|
||||||
|
<meta-data android:name="com.samsung.android.sdk.multiwindow.enable" android:value="true" />
|
||||||
|
<meta-data android:name="com.samsung.android.sdk.multiwindow.penwindow.enable" android:value="true" />
|
||||||
|
|
||||||
|
<!-- Support for LG "Dual Window" mode (for Android < 7.0 users) -->
|
||||||
|
<meta-data android:name="com.lge.support.SPLIT_WINDOW" android:value="true" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyTitle("BitwardenAndroid")]
|
[assembly: AssemblyTitle("BitwardenAndroid")]
|
||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("8bit Solutions LLC")]
|
[assembly: AssemblyCompany("Bitwarden Inc.")]
|
||||||
[assembly: AssemblyProduct("Bitwarden")]
|
[assembly: AssemblyProduct("Bitwarden")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
|
|||||||
@@ -1,23 +1,14 @@
|
|||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
|
|
||||||
namespace Bit.Droid.Receivers
|
namespace Bit.Droid.Receivers
|
||||||
{
|
{
|
||||||
[BroadcastReceiver(Name = "com.x8bit.bitwarden.ClearClipboardAlarmReceiver", Exported = false)]
|
[BroadcastReceiver(Name = "com.x8bit.bitwarden.ClearClipboardAlarmReceiver", Exported = false)]
|
||||||
public class ClearClipboardAlarmReceiver : BroadcastReceiver
|
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 clipboardManager = context.GetSystemService(Context.ClipboardService) as ClipboardManager;
|
||||||
var lastClipboardValue = await stateService.GetAsync<string>(Constants.LastClipboardValueKey);
|
clipboardManager.PrimaryClip = ClipData.NewPlainText("bitwarden", string.Empty);
|
||||||
await stateService.RemoveAsync(Constants.LastClipboardValueKey);
|
|
||||||
if(lastClipboardValue == clipboardManager.Text)
|
|
||||||
{
|
|
||||||
clipboardManager.Text = string.Empty;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,8 +9,8 @@ namespace Bit.Droid.Receivers
|
|||||||
{
|
{
|
||||||
public async override void OnReceive(Context context, Intent intent)
|
public async override void OnReceive(Context context, Intent intent)
|
||||||
{
|
{
|
||||||
var lockService = ServiceContainer.Resolve<ILockService>("lockService");
|
var vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
await lockService.CheckLockAsync();
|
await vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/Android/Receivers/RestrictionsChangedReceiver.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using Bit.Droid.Utilities;
|
||||||
|
|
||||||
|
namespace Bit.Droid.Receivers
|
||||||
|
{
|
||||||
|
[BroadcastReceiver(Name = "com.x8bit.bitwarden.RestrictionsChangedReceiver", Exported = false)]
|
||||||
|
[IntentFilter(new[] { Intent.ActionApplicationRestrictionsChanged })]
|
||||||
|
public class RestrictionsChangedReceiver : BroadcastReceiver
|
||||||
|
{
|
||||||
|
public async override void OnReceive(Context context, Intent intent)
|
||||||
|
{
|
||||||
|
if (intent.Action == Intent.ActionApplicationRestrictionsChanged)
|
||||||
|
{
|
||||||
|
await AndroidHelpers.SetPreconfiguredRestrictionSettingsAsync(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ using Android.Views;
|
|||||||
using Android.Views.InputMethods;
|
using Android.Views.InputMethods;
|
||||||
using Android.Widget;
|
using Android.Widget;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using Bit.Droid.Renderers;
|
using Bit.Droid.Renderers;
|
||||||
using FFImageLoading;
|
using FFImageLoading;
|
||||||
using FFImageLoading.Views;
|
using FFImageLoading.Views;
|
||||||
@@ -42,19 +43,15 @@ namespace Bit.Droid.Renderers
|
|||||||
}
|
}
|
||||||
if (_textColor == default(Android.Graphics.Color))
|
if (_textColor == default(Android.Graphics.Color))
|
||||||
{
|
{
|
||||||
_textColor = ((Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["TextColor"])
|
_textColor = ThemeManager.GetResourceColor("TextColor").ToAndroid();
|
||||||
.ToAndroid();
|
|
||||||
}
|
}
|
||||||
if (_mutedColor == default(Android.Graphics.Color))
|
if (_mutedColor == default(Android.Graphics.Color))
|
||||||
{
|
{
|
||||||
_mutedColor = ((Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["MutedColor"])
|
_mutedColor = ThemeManager.GetResourceColor("MutedColor").ToAndroid();
|
||||||
.ToAndroid();
|
|
||||||
}
|
}
|
||||||
if (_disabledIconColor == default(Android.Graphics.Color))
|
if (_disabledIconColor == default(Android.Graphics.Color))
|
||||||
{
|
{
|
||||||
_disabledIconColor =
|
_disabledIconColor = ThemeManager.GetResourceColor("DisabledIconColor").ToAndroid();
|
||||||
((Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["DisabledIconColor"])
|
|
||||||
.ToAndroid();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var cipherCell = item as CipherViewCell;
|
var cipherCell = item as CipherViewCell;
|
||||||
|
|||||||
@@ -13,6 +13,15 @@ namespace Bit.Droid.Renderers
|
|||||||
: base(context)
|
: base(context)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
// Workaround for issue described here:
|
||||||
|
// https://github.com/xamarin/Xamarin.Forms/issues/8291#issuecomment-617456651
|
||||||
|
protected override void OnAttachedToWindow()
|
||||||
|
{
|
||||||
|
base.OnAttachedToWindow();
|
||||||
|
EditText.Enabled = false;
|
||||||
|
EditText.Enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
|
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.Graphics.Drawables;
|
using Android.Graphics.Drawables;
|
||||||
using Android.Support.V4.Content.Res;
|
using AndroidX.Core.Content.Resources;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
using Bit.Droid.Renderers;
|
using Bit.Droid.Renderers;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
@@ -22,8 +22,15 @@ namespace Bit.Droid.Renderers
|
|||||||
{
|
{
|
||||||
var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.slider_thumb, null);
|
var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.slider_thumb, null);
|
||||||
if (t is GradientDrawable thumb)
|
if (t is GradientDrawable thumb)
|
||||||
|
{
|
||||||
|
if (view.ThumbColor == Color.Default)
|
||||||
|
{
|
||||||
|
thumb.SetColor(Color.White.ToAndroid());
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
thumb.SetColor(view.ThumbColor.ToAndroid());
|
thumb.SetColor(view.ThumbColor.ToAndroid());
|
||||||
|
}
|
||||||
thumb.SetStroke(3, view.ThumbBorderColor.ToAndroid());
|
thumb.SetStroke(3, view.ThumbBorderColor.ToAndroid());
|
||||||
Control.SetThumb(thumb);
|
Control.SetThumb(thumb);
|
||||||
}
|
}
|
||||||
|
|||||||
15621
src/Android/Resources/Resource.designer.cs
generated
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 7.1 KiB |
BIN
src/Android/Resources/drawable-hdpi/accessibility_overlay.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
src/Android/Resources/drawable-hdpi/accessibility_permission.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
BIN
src/Android/Resources/drawable-xhdpi/accessibility_overlay.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 11 KiB |
BIN
src/Android/Resources/drawable-xxhdpi/accessibility_overlay.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
BIN
src/Android/Resources/drawable/accessibility_overlay.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/Android/Resources/drawable/accessibility_permission.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 13 KiB |
13
src/Android/Resources/drawable/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="1024.4897"
|
||||||
|
android:viewportHeight="1024.4897">
|
||||||
|
<group android:translateX="300.2449"
|
||||||
|
android:translateY="261.2449">
|
||||||
|
<path
|
||||||
|
android:pathData="M393.4,52.2C389.7,48.5 385.3,46.6 380.3,46.6L43.7,46.6C38.6,46.6 34.3,48.5 30.6,52.2C26.9,55.9 25,60.2 25,65.3L25,289.7C25,306.4 28.3,323.1 34.8,339.5C41.3,356 49.4,370.6 59.1,383.3C68.7,396.1 80.2,408.5 93.6,420.5C106.9,432.6 119.3,442.6 130.6,450.6C141.9,458.6 153.7,466.1 166,473.2C178.3,480.3 187,485.1 192.2,487.7C197.4,490.2 201.5,492.2 204.6,493.5C206.9,494.7 209.5,495.3 212.2,495.3C214.9,495.3 217.5,494.7 219.8,493.5C222.9,492.1 227.1,490.2 232.2,487.7C237.4,485.2 246.1,480.3 258.4,473.2C270.7,466.1 282.5,458.5 293.8,450.6C305.1,442.6 317.4,432.6 330.8,420.5C344.1,408.4 355.6,396 365.3,383.3C374.9,370.5 383,355.9 389.5,339.5C396,323 399.3,306.4 399.3,289.7L399.3,65.3C399,60.2 397.1,55.9 393.4,52.2ZM350,291.8C350,373 212,443 212,443L212,94.6L350,94.6L350,291.8Z"
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:fillType="nonZero"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
|
Before Width: | Height: | Size: 941 B |
12
src/Android/Resources/drawable/icon.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="1024dp"
|
||||||
|
android:height="1024dp"
|
||||||
|
android:viewportWidth="1024"
|
||||||
|
android:viewportHeight="1024">
|
||||||
|
<path
|
||||||
|
android:pathData="M1024,864c0,88.4 -71.6,160 -160,160H160C71.6,1024 0,952.4 0,864V160C0,71.6 71.6,0 160,0h704c88.4,0 160,71.6 160,160V864z"
|
||||||
|
android:fillColor="#175DDC"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M829.8,128.6c-6.5,-6.5 -14.2,-9.7 -23,-9.7H217.2c-8.9,0 -16.5,3.2 -23,9.7c-6.5,6.5 -9.7,14.2 -9.7,23v393.1c0,29.3 5.7,58.4 17.1,87.3c11.4,28.8 25.6,54.4 42.5,76.8c16.9,22.3 37,44.1 60.4,65.3c23.4,21.2 45,38.7 64.7,52.7c19.8,14 40.4,27.2 61.9,39.7c21.5,12.5 36.8,20.9 45.8,25.3c9,4.4 16.3,7.9 21.7,10.2c4.1,2 8.5,3.1 13.3,3.1c4.8,0 9.2,-1 13.3,-3.1c5.5,-2.4 12.7,-5.8 21.8,-10.2c9,-4.4 24.3,-12.9 45.8,-25.3c21.5,-12.5 42.1,-25.7 61.9,-39.7c19.8,-14 41.4,-31.6 64.8,-52.7c23.4,-21.2 43.5,-42.9 60.4,-65.3c16.9,-22.4 31,-47.9 42.5,-76.8c11.4,-28.8 17.1,-57.9 17.1,-87.3V151.7C839.6,142.8 836.3,135.1 829.8,128.6zM753.8,548.4c0,142.3 -241.8,264.9 -241.8,264.9V203.1h241.8C753.8,203.1 753.8,406.1 753.8,548.4z"
|
||||||
|
android:fillColor="#FFFFFF"/>
|
||||||
|
</vector>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<android.support.design.widget.TabLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<com.google.android.material.tabs.TabLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/sliding_tabs"
|
android:id="@+id/sliding_tabs"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<android.support.v7.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 941 B |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 896 B After Width: | Height: | Size: 968 B |
|
Before Width: | Height: | Size: 608 B |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.8 KiB |
@@ -1,10 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<!-- Light theme -->
|
<!-- Light theme -->
|
||||||
<color name="colorPrimary">#3c8dbc</color>
|
<color name="colorPrimary">#175DDC</color>
|
||||||
<color name="colorPrimaryDark">#222d32</color>
|
<color name="colorPrimaryDark">#1A3B66</color>
|
||||||
<color name="primary">#3c8dbc</color>
|
<color name="primary">#175DDC</color>
|
||||||
<color name="notificationBar">#3883af</color>
|
<color name="notificationBar">#1452BC</color>
|
||||||
<color name="border">#dddddd</color>
|
<color name="border">#dddddd</color>
|
||||||
|
|
||||||
<!-- Dark theme -->
|
<!-- Dark theme -->
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="ic_launcher_background">#3C8DBC</color>
|
<color name="ic_launcher_background">#175DDC</color>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -16,4 +16,13 @@
|
|||||||
<string name="PasswordGenerator">
|
<string name="PasswordGenerator">
|
||||||
Password Generator
|
Password Generator
|
||||||
</string>
|
</string>
|
||||||
|
<string name="AutoFillTile">
|
||||||
|
Auto-fill
|
||||||
|
</string>
|
||||||
|
<string name="SelfHostedServerUrl">
|
||||||
|
Self-hosted server URL
|
||||||
|
</string>
|
||||||
|
<string name="ServerUrl">
|
||||||
|
Server URL
|
||||||
|
</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
|
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:summary="@string/AutoFillServiceSummary"
|
android:summary="@string/AutoFillServiceSummary"
|
||||||
android:description="@string/AutoFillServiceDescription"
|
android:description="@string/AutoFillServiceDescription"
|
||||||
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged|typeViewFocused"
|
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged|typeViewFocused|typeViewClicked"
|
||||||
android:accessibilityFeedbackType="feedbackGeneric"
|
android:accessibilityFeedbackType="feedbackGeneric"
|
||||||
android:accessibilityFlags="flagReportViewIds"
|
android:accessibilityFlags="flagReportViewIds|flagRetrieveInteractiveWindows"
|
||||||
android:notificationTimeout="100"
|
android:notificationTimeout="100"
|
||||||
android:canRetrieveWindowContent="true"/>
|
android:canRetrieveWindowContent="true"/>
|
||||||
9
src/Android/Resources/xml/app_restrictions.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<restriction
|
||||||
|
android:key="baseEnvironmentUrl"
|
||||||
|
android:title="@string/SelfHostedServerUrl"
|
||||||
|
android:restrictionType="string"
|
||||||
|
android:description="@string/ServerUrl"
|
||||||
|
android:defaultValue="" />
|
||||||
|
</restrictions>
|
||||||
@@ -1,20 +1,72 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<!--
|
||||||
|
These browsers work using the compatibility shim for the Autofill Framework
|
||||||
|
(and need to be listed here as well)
|
||||||
|
|
||||||
|
Be sure:
|
||||||
|
- to keep these entries sorted alphabetically,
|
||||||
|
- to keep this list in sync with values in AutofillHelpers.CompatBrowsers, and
|
||||||
|
|
||||||
|
- ... to keep this list in sync with values in AccessibilityHelpers.SupportedBrowsers [Section A], too.
|
||||||
|
-->
|
||||||
<autofill-service xmlns:android="http://schemas.android.com/apk/res/android">
|
<autofill-service xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.amazon.cloud9"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.android.browser"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.android.chrome"
|
android:name="com.android.chrome"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.avast.android.secure.browser"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.avg.android.secure.browser"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.brave.browser"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.brave.browser_beta"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.brave.browser_default"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.brave.browser_dev"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.brave.browser_nightly"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.chrome.beta"
|
android:name="com.chrome.beta"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
|
||||||
android:name="com.chrome.dev"
|
|
||||||
android:maxLongVersionCode="10000000000"/>
|
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.chrome.canary"
|
android:name="com.chrome.canary"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.chrome.dev"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.ecosia.android"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.google.android.apps.chrome"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.google.android.apps.chrome_dev"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.kiwibrowser.browser"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.microsoft.emmx"
|
android:name="com.microsoft.emmx"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.naver.whale"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.opera.browser"
|
android:name="com.opera.browser"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
@@ -27,12 +79,63 @@
|
|||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.opera.mini.native.beta"
|
android:name="com.opera.mini.native.beta"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.opera.touch"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.qwant.liberty"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.sec.android.app.sbrowser"
|
android:name="com.sec.android.app.sbrowser"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.sec.android.app.sbrowser.beta"
|
android:name="com.sec.android.app.sbrowser.beta"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.stoutner.privacybrowser.free"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.stoutner.privacybrowser.standard"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.vivaldi.browser"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.vivaldi.browser.snapshot"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.vivaldi.browser.sopranos"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.yandex.browser"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="mark.via.gp"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="org.adblockplus.browser"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="org.adblockplus.browser.beta"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="org.bromite.bromite"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="org.chromium.chrome"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="org.codeaurora.swe.browser"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="org.gnu.icecat"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="org.mozilla.fenix"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="org.mozilla.fenix.nightly"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="org.mozilla.fennec_aurora"
|
android:name="org.mozilla.fennec_aurora"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
@@ -45,9 +148,6 @@
|
|||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="org.mozilla.firefox_beta"
|
android:name="org.mozilla.firefox_beta"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
|
||||||
android:name="org.mozilla.fenix"
|
|
||||||
android:maxLongVersionCode="10000000000"/>
|
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="org.mozilla.reference.browser"
|
android:name="org.mozilla.reference.browser"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
@@ -55,36 +155,9 @@
|
|||||||
android:name="org.mozilla.rocket"
|
android:name="org.mozilla.rocket"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.brave.browser"
|
android:name="org.torproject.torbrowser"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.google.android.apps.chrome"
|
android:name="org.torproject.torbrowser_alpha"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
|
||||||
<compatibility-package
|
|
||||||
android:name="com.google.android.apps.chrome_dev"
|
|
||||||
android:maxLongVersionCode="10000000000"/>
|
|
||||||
<compatibility-package
|
|
||||||
android:name="com.yandex.browser"
|
|
||||||
android:maxLongVersionCode="10000000000"/>
|
|
||||||
<compatibility-package
|
|
||||||
android:name="org.codeaurora.swe.browser"
|
|
||||||
android:maxLongVersionCode="10000000000"/>
|
|
||||||
<compatibility-package
|
|
||||||
android:name="com.amazon.cloud9"
|
|
||||||
android:maxLongVersionCode="10000000000"/>
|
|
||||||
<compatibility-package
|
|
||||||
android:name="mark.via.gp"
|
|
||||||
android:maxLongVersionCode="10000000000"/>
|
|
||||||
<compatibility-package
|
|
||||||
android:name="org.bromite.bromite"
|
|
||||||
android:maxLongVersionCode="10000000000"/>
|
|
||||||
<compatibility-package
|
|
||||||
android:name="org.chromium.chrome"
|
|
||||||
android:maxLongVersionCode="10000000000"/>
|
|
||||||
<compatibility-package
|
|
||||||
android:name="com.kiwibrowser.browser"
|
|
||||||
android:maxLongVersionCode="10000000000"/>
|
|
||||||
<compatibility-package
|
|
||||||
android:name="com.ecosia.android"
|
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
</autofill-service>
|
</autofill-service>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<cache-path name="cache" path="." />
|
<cache-path name="cache" path="." />
|
||||||
|
<files-path name="internal" path="." />
|
||||||
</paths>
|
</paths>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<network-security-config>
|
<network-security-config>
|
||||||
<base-config>
|
<base-config cleartextTrafficPermitted="true">
|
||||||
<trust-anchors>
|
<trust-anchors>
|
||||||
<!-- Trust pre-installed CAs -->
|
<!-- Trust pre-installed CAs -->
|
||||||
<certificates src="system" />
|
<certificates src="system" />
|
||||||
|
|||||||
@@ -8,17 +8,20 @@ using Android.App;
|
|||||||
using Android.App.Assist;
|
using Android.App.Assist;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.Content.PM;
|
using Android.Content.PM;
|
||||||
|
using Android.Hardware.Biometrics;
|
||||||
|
using Android.Hardware.Fingerprints;
|
||||||
using Android.Nfc;
|
using Android.Nfc;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Android.Provider;
|
using Android.Provider;
|
||||||
using Android.Support.V4.App;
|
using Android.Runtime;
|
||||||
using Android.Support.V4.Content;
|
|
||||||
using Android.Text;
|
using Android.Text;
|
||||||
using Android.Text.Method;
|
using Android.Text.Method;
|
||||||
using Android.Views.Autofill;
|
using Android.Views.Autofill;
|
||||||
using Android.Views.InputMethods;
|
using Android.Views.InputMethods;
|
||||||
using Android.Webkit;
|
using Android.Webkit;
|
||||||
using Android.Widget;
|
using Android.Widget;
|
||||||
|
using AndroidX.Core.App;
|
||||||
|
using AndroidX.Core.Content;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
@@ -28,6 +31,7 @@ using Bit.Core.Models.View;
|
|||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.Droid.Autofill;
|
using Bit.Droid.Autofill;
|
||||||
using Plugin.CurrentActivity;
|
using Plugin.CurrentActivity;
|
||||||
|
using Plugin.Fingerprint;
|
||||||
|
|
||||||
namespace Bit.Droid.Services
|
namespace Bit.Droid.Services
|
||||||
{
|
{
|
||||||
@@ -36,18 +40,22 @@ namespace Bit.Droid.Services
|
|||||||
private readonly IStorageService _storageService;
|
private readonly IStorageService _storageService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
|
private readonly Func<IEventService> _eventServiceFunc;
|
||||||
private ProgressDialog _progressDialog;
|
private ProgressDialog _progressDialog;
|
||||||
private bool _cameraPermissionsDenied;
|
private bool _cameraPermissionsDenied;
|
||||||
private Toast _toast;
|
private Toast _toast;
|
||||||
|
private string _userAgent;
|
||||||
|
|
||||||
public DeviceActionService(
|
public DeviceActionService(
|
||||||
IStorageService storageService,
|
IStorageService storageService,
|
||||||
IMessagingService messagingService,
|
IMessagingService messagingService,
|
||||||
IBroadcasterService broadcasterService)
|
IBroadcasterService broadcasterService,
|
||||||
|
Func<IEventService> eventServiceFunc)
|
||||||
{
|
{
|
||||||
_storageService = storageService;
|
_storageService = storageService;
|
||||||
_messagingService = messagingService;
|
_messagingService = messagingService;
|
||||||
_broadcasterService = broadcasterService;
|
_broadcasterService = broadcasterService;
|
||||||
|
_eventServiceFunc = eventServiceFunc;
|
||||||
|
|
||||||
_broadcasterService.Subscribe(nameof(DeviceActionService), (message) =>
|
_broadcasterService.Subscribe(nameof(DeviceActionService), (message) =>
|
||||||
{
|
{
|
||||||
@@ -58,6 +66,19 @@ namespace Bit.Droid.Services
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string DeviceUserAgent
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_userAgent))
|
||||||
|
{
|
||||||
|
_userAgent = $"Bitwarden_Mobile/{Xamarin.Essentials.AppInfo.VersionString} " +
|
||||||
|
$"(Android {Build.VERSION.Release}; SDK {Build.VERSION.Sdk}; Model {Build.Model})";
|
||||||
|
}
|
||||||
|
return _userAgent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public DeviceType DeviceType => DeviceType.Android;
|
public DeviceType DeviceType => DeviceType.Android;
|
||||||
|
|
||||||
public void Toast(string text, bool longDuration = false)
|
public void Toast(string text, bool longDuration = false)
|
||||||
@@ -111,38 +132,14 @@ namespace Bit.Droid.Services
|
|||||||
|
|
||||||
public bool OpenFile(byte[] fileData, string id, string fileName)
|
public bool OpenFile(byte[] fileData, string id, string fileName)
|
||||||
{
|
{
|
||||||
if(!CanOpenFile(fileName))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName.Replace(' ', '_').ToLower());
|
|
||||||
if(extension == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
|
|
||||||
if(mimeType == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
||||||
var cachePath = activity.CacheDir;
|
|
||||||
var filePath = Path.Combine(cachePath.Path, fileName);
|
|
||||||
File.WriteAllBytes(filePath, fileData);
|
|
||||||
var file = new Java.IO.File(cachePath, fileName);
|
|
||||||
if(!file.IsFile)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var intent = new Intent(Intent.ActionView);
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
var uri = FileProvider.GetUriForFile(activity.ApplicationContext,
|
var intent = BuildOpenFileIntent(fileData, fileName);
|
||||||
"com.x8bit.bitwarden.fileprovider", file);
|
if (intent == null)
|
||||||
intent.SetDataAndType(uri, mimeType);
|
{
|
||||||
intent.SetFlags(ActivityFlags.GrantReadUriPermission);
|
return false;
|
||||||
|
}
|
||||||
activity.StartActivity(intent);
|
activity.StartActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -152,21 +149,104 @@ namespace Bit.Droid.Services
|
|||||||
|
|
||||||
public bool CanOpenFile(string fileName)
|
public bool CanOpenFile(string fileName)
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
|
var intent = BuildOpenFileIntent(new byte[0], string.Concat("opentest_", fileName));
|
||||||
|
if (intent == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var activities = activity.PackageManager.QueryIntentActivities(intent,
|
||||||
|
PackageInfoFlags.MatchDefaultOnly);
|
||||||
|
return (activities?.Count ?? 0) > 0;
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent BuildOpenFileIntent(byte[] fileData, string fileName)
|
||||||
|
{
|
||||||
|
var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName.Replace(' ', '_').ToLower());
|
||||||
|
if (extension == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
|
||||||
|
if (mimeType == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
|
var cachePath = activity.CacheDir;
|
||||||
|
var filePath = Path.Combine(cachePath.Path, fileName);
|
||||||
|
File.WriteAllBytes(filePath, fileData);
|
||||||
|
var file = new Java.IO.File(cachePath, fileName);
|
||||||
|
if (!file.IsFile)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var intent = new Intent(Intent.ActionView);
|
||||||
|
var uri = FileProvider.GetUriForFile(activity.ApplicationContext,
|
||||||
|
"com.x8bit.bitwarden.fileprovider", file);
|
||||||
|
intent.SetDataAndType(uri, mimeType);
|
||||||
|
intent.SetFlags(ActivityFlags.GrantReadUriPermission);
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SaveFile(byte[] fileData, string id, string fileName, string contentUri)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
|
|
||||||
|
if (contentUri != null)
|
||||||
|
{
|
||||||
|
var uri = Android.Net.Uri.Parse(contentUri);
|
||||||
|
var stream = activity.ContentResolver.OpenOutputStream(uri);
|
||||||
|
// Using java bufferedOutputStream due to this issue:
|
||||||
|
// https://github.com/xamarin/xamarin-android/issues/3498
|
||||||
|
var javaStream = new Java.IO.BufferedOutputStream(stream);
|
||||||
|
javaStream.Write(fileData);
|
||||||
|
javaStream.Flush();
|
||||||
|
javaStream.Close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prompt for location to save file
|
||||||
var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName.Replace(' ', '_').ToLower());
|
var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName.Replace(' ', '_').ToLower());
|
||||||
if (extension == null)
|
if (extension == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
|
|
||||||
|
string mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
|
||||||
if (mimeType == null)
|
if (mimeType == null)
|
||||||
{
|
{
|
||||||
return false;
|
// Unable to identify so fall back to generic "any" type
|
||||||
|
mimeType = "*/*";
|
||||||
}
|
}
|
||||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
||||||
var intent = new Intent(Intent.ActionView);
|
var intent = new Intent(Intent.ActionCreateDocument);
|
||||||
intent.SetType(mimeType);
|
intent.SetType(mimeType);
|
||||||
var activities = activity.PackageManager.QueryIntentActivities(intent, PackageInfoFlags.MatchDefaultOnly);
|
intent.AddCategory(Intent.CategoryOpenable);
|
||||||
return (activities?.Count ?? 0) > 0;
|
intent.PutExtra(Intent.ExtraTitle, fileName);
|
||||||
|
|
||||||
|
activity.StartActivityForResult(intent, Constants.SaveFileRequestCode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", ex.GetType(), ex.StackTrace);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ClearCacheAsync()
|
public async Task ClearCacheAsync()
|
||||||
@@ -182,7 +262,8 @@ namespace Bit.Droid.Services
|
|||||||
public Task SelectFileAsync()
|
public Task SelectFileAsync()
|
||||||
{
|
{
|
||||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
var hasStorageWritePermission = !_cameraPermissionsDenied && HasPermission(Manifest.Permission.WriteExternalStorage);
|
var hasStorageWritePermission = !_cameraPermissionsDenied &&
|
||||||
|
HasPermission(Manifest.Permission.WriteExternalStorage);
|
||||||
var additionalIntents = new List<IParcelable>();
|
var additionalIntents = new List<IParcelable>();
|
||||||
if (activity.PackageManager.HasSystemFeature(PackageManager.FeatureCamera))
|
if (activity.PackageManager.HasSystemFeature(PackageManager.FeatureCamera))
|
||||||
{
|
{
|
||||||
@@ -201,14 +282,14 @@ namespace Bit.Droid.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var root = new Java.IO.File(Android.OS.Environment.ExternalStorageDirectory, "bitwarden");
|
var file = new Java.IO.File(activity.FilesDir, "temp_camera_photo.jpg");
|
||||||
var file = new Java.IO.File(root, "temp_camera_photo.jpg");
|
|
||||||
if (!file.Exists())
|
if (!file.Exists())
|
||||||
{
|
{
|
||||||
file.ParentFile.Mkdirs();
|
file.ParentFile.Mkdirs();
|
||||||
file.CreateNewFile();
|
file.CreateNewFile();
|
||||||
}
|
}
|
||||||
var outputFileUri = Android.Net.Uri.FromFile(file);
|
var outputFileUri = FileProvider.GetUriForFile(activity,
|
||||||
|
"com.x8bit.bitwarden.fileprovider", file);
|
||||||
additionalIntents.AddRange(GetCameraIntents(outputFileUri));
|
additionalIntents.AddRange(GetCameraIntents(outputFileUri));
|
||||||
}
|
}
|
||||||
catch (Java.IO.IOException) { }
|
catch (Java.IO.IOException) { }
|
||||||
@@ -306,11 +387,73 @@ namespace Bit.Droid.Services
|
|||||||
Application.Context.PackageName, 0).VersionCode.ToString();
|
Application.Context.PackageName, 0).VersionCode.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SupportsFaceId()
|
public bool SupportsFaceBiometric()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<bool> SupportsFaceBiometricAsync()
|
||||||
|
{
|
||||||
|
return Task.FromResult(SupportsFaceBiometric());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> BiometricAvailableAsync()
|
||||||
|
{
|
||||||
|
if (UseNativeBiometric())
|
||||||
|
{
|
||||||
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
|
var manager = activity.GetSystemService(Context.BiometricService) as BiometricManager;
|
||||||
|
return manager.CanAuthenticate() == BiometricCode.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await CrossFingerprint.Current.IsAvailableAsync();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UseNativeBiometric()
|
||||||
|
{
|
||||||
|
return (int)Build.VERSION.SdkInt >= 29;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<bool> AuthenticateBiometricAsync(string text = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(text))
|
||||||
|
{
|
||||||
|
text = AppResources.BiometricsDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
|
using (var builder = new BiometricPrompt.Builder(activity))
|
||||||
|
{
|
||||||
|
builder.SetTitle(text);
|
||||||
|
builder.SetConfirmationRequired(false);
|
||||||
|
builder.SetNegativeButton(AppResources.Cancel, activity.MainExecutor,
|
||||||
|
new DialogInterfaceOnClickListener
|
||||||
|
{
|
||||||
|
Clicked = () => { }
|
||||||
|
});
|
||||||
|
var prompt = builder.Build();
|
||||||
|
var result = new TaskCompletionSource<bool>();
|
||||||
|
prompt.Authenticate(new CancellationSignal(), activity.MainExecutor,
|
||||||
|
new BiometricAuthenticationCallback
|
||||||
|
{
|
||||||
|
Success = authResult => result.TrySetResult(true),
|
||||||
|
Error = () => result.TrySetResult(false),
|
||||||
|
Failed = () => { },
|
||||||
|
Help = (helpCode, helpString) => { }
|
||||||
|
});
|
||||||
|
return result.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool SupportsNfc()
|
public bool SupportsNfc()
|
||||||
{
|
{
|
||||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
@@ -426,6 +569,13 @@ namespace Bit.Droid.Services
|
|||||||
return result.Task;
|
return result.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<string> DisplayActionSheetAsync(string title, string cancel, string destruction,
|
||||||
|
params string[] buttons)
|
||||||
|
{
|
||||||
|
return await Xamarin.Forms.Application.Current.MainPage.DisplayActionSheet(
|
||||||
|
title, cancel, destruction, buttons);
|
||||||
|
}
|
||||||
|
|
||||||
public void Autofill(CipherView cipher)
|
public void Autofill(CipherView cipher)
|
||||||
{
|
{
|
||||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
@@ -463,6 +613,7 @@ namespace Bit.Droid.Services
|
|||||||
replyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, dataset);
|
replyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, dataset);
|
||||||
activity.SetResult(Result.Ok, replyIntent);
|
activity.SetResult(Result.Ok, replyIntent);
|
||||||
activity.Finish();
|
activity.Finish();
|
||||||
|
var eventTask = _eventServiceFunc().CollectAsync(EventType.Cipher_ClientAutofilled, cipher.Id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -488,6 +639,10 @@ namespace Bit.Droid.Services
|
|||||||
}
|
}
|
||||||
activity.Finish();
|
activity.Finish();
|
||||||
_messagingService.Send("finishMainActivity");
|
_messagingService.Send("finishMainActivity");
|
||||||
|
if (cipher != null)
|
||||||
|
{
|
||||||
|
var eventTask = _eventServiceFunc().CollectAsync(EventType.Cipher_ClientAutofilled, cipher.Id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,6 +681,40 @@ namespace Bit.Droid.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool AutofillAccessibilityOverlayPermitted()
|
||||||
|
{
|
||||||
|
return Accessibility.AccessibilityHelpers.OverlayPermitted();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OpenAccessibilityOverlayPermissionSettings()
|
||||||
|
{
|
||||||
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var intent = new Intent(Settings.ActionManageOverlayPermission);
|
||||||
|
intent.SetData(Android.Net.Uri.Parse("package:com.x8bit.bitwarden"));
|
||||||
|
activity.StartActivity(intent);
|
||||||
|
}
|
||||||
|
catch (ActivityNotFoundException)
|
||||||
|
{
|
||||||
|
// can't open overlay permission management, fall back to app settings
|
||||||
|
var intent = new Intent(Settings.ActionApplicationDetailsSettings);
|
||||||
|
intent.SetData(Android.Net.Uri.Parse("package:com.x8bit.bitwarden"));
|
||||||
|
activity.StartActivity(intent);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
var alertBuilder = new AlertDialog.Builder(activity);
|
||||||
|
alertBuilder.SetMessage(AppResources.BitwardenAutofillGoToSettings);
|
||||||
|
alertBuilder.SetCancelable(true);
|
||||||
|
alertBuilder.SetPositiveButton(AppResources.Ok, (sender, args) =>
|
||||||
|
{
|
||||||
|
(sender as AlertDialog)?.Cancel();
|
||||||
|
});
|
||||||
|
alertBuilder.Create().Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool AutofillServiceEnabled()
|
public bool AutofillServiceEnabled()
|
||||||
{
|
{
|
||||||
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
|
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
|
||||||
@@ -535,7 +724,8 @@ namespace Bit.Droid.Services
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
var afm = (AutofillManager)activity.GetSystemService(Java.Lang.Class.FromType(typeof(AutofillManager)));
|
var afm = (AutofillManager)activity.GetSystemService(
|
||||||
|
Java.Lang.Class.FromType(typeof(AutofillManager)));
|
||||||
return afm.IsEnabled && afm.HasEnabledAutofillServices;
|
return afm.IsEnabled && afm.HasEnabledAutofillServices;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -577,6 +767,11 @@ namespace Bit.Droid.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool UsingDarkTheme()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private bool DeleteDir(Java.IO.File dir)
|
private bool DeleteDir(Java.IO.File dir)
|
||||||
{
|
{
|
||||||
if (dir != null && dir.IsDirectory)
|
if (dir != null && dir.IsDirectory)
|
||||||
@@ -673,7 +868,50 @@ namespace Bit.Droid.Services
|
|||||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
var clipboardManager = activity.GetSystemService(
|
var clipboardManager = activity.GetSystemService(
|
||||||
Context.ClipboardService) as Android.Content.ClipboardManager;
|
Context.ClipboardService) as Android.Content.ClipboardManager;
|
||||||
clipboardManager.Text = text;
|
clipboardManager.PrimaryClip = ClipData.NewPlainText("bitwarden", text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BiometricAuthenticationCallback : BiometricPrompt.AuthenticationCallback
|
||||||
|
{
|
||||||
|
public Action<BiometricPrompt.AuthenticationResult> Success { get; set; }
|
||||||
|
public Action Error { get; set; }
|
||||||
|
public Action Failed { get; set; }
|
||||||
|
public Action<BiometricAcquiredStatus, Java.Lang.ICharSequence> Help { get; set; }
|
||||||
|
|
||||||
|
public override void OnAuthenticationSucceeded(BiometricPrompt.AuthenticationResult authResult)
|
||||||
|
{
|
||||||
|
base.OnAuthenticationSucceeded(authResult);
|
||||||
|
Success?.Invoke(authResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnAuthenticationError([GeneratedEnum] BiometricErrorCode errorCode, Java.Lang.ICharSequence errString)
|
||||||
|
{
|
||||||
|
base.OnAuthenticationError(errorCode, errString);
|
||||||
|
Error?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnAuthenticationFailed()
|
||||||
|
{
|
||||||
|
base.OnAuthenticationFailed();
|
||||||
|
Failed?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnAuthenticationHelp([GeneratedEnum] BiometricAcquiredStatus helpCode,
|
||||||
|
Java.Lang.ICharSequence helpString)
|
||||||
|
{
|
||||||
|
base.OnAuthenticationHelp(helpCode, helpString);
|
||||||
|
Help?.Invoke(helpCode, helpString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DialogInterfaceOnClickListener : Java.Lang.Object, IDialogInterfaceOnClickListener
|
||||||
|
{
|
||||||
|
public Action Clicked { get; set; }
|
||||||
|
|
||||||
|
public void OnClick(IDialogInterface dialog, int which)
|
||||||
|
{
|
||||||
|
Clicked?.Invoke();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||