Compare commits
241 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c242117230 | ||
|
|
d7c1b23fa2 | ||
|
|
25e9919bb3 | ||
|
|
912c01457a | ||
|
|
8083390eab | ||
|
|
44baa4cc1e | ||
|
|
f0662bb878 | ||
|
|
fbe1a6d4c5 | ||
|
|
2235f1f7af | ||
|
|
3f46f83ec8 | ||
|
|
d537d4a27e | ||
|
|
c67250da2d | ||
|
|
6027406eef | ||
|
|
fdc51f33ad | ||
|
|
ea7290afab | ||
|
|
be65597d57 | ||
|
|
253ed75800 | ||
|
|
e4f3671ae0 | ||
|
|
c60cefd188 | ||
|
|
175a41f275 | ||
|
|
bd5fd72459 | ||
|
|
98b70a647b | ||
|
|
3eee5e696d | ||
|
|
d92c6cc6c6 | ||
|
|
488485da54 | ||
|
|
a3f0254fb2 | ||
|
|
ab5f1385c5 | ||
|
|
cde5b09943 | ||
|
|
db42b6a3a5 | ||
|
|
ece35b96db | ||
|
|
c4c24ee240 | ||
|
|
c7ba465970 | ||
|
|
937ad444da | ||
|
|
0c0a928e87 | ||
|
|
2823a86b4e | ||
|
|
1a06683611 | ||
|
|
50fa74adfe | ||
|
|
4ebd249356 | ||
|
|
4dc388015c | ||
|
|
0270cf6e45 | ||
|
|
7a19c50ec0 | ||
|
|
e2fc5fff23 | ||
|
|
839df123ff | ||
|
|
f897193f79 | ||
|
|
9f23f4ead7 | ||
|
|
45ab6d47de | ||
|
|
35bc94f4bd | ||
|
|
94a4a38798 | ||
|
|
7f431dbd01 | ||
|
|
d0257df134 | ||
|
|
be3ed16d3c | ||
|
|
b651becf66 | ||
|
|
bcf49ab396 | ||
|
|
fb76ecf198 | ||
|
|
1bed49b4c6 | ||
|
|
c34376820a | ||
|
|
582e6ee322 | ||
|
|
2b4ffaa357 | ||
|
|
379a82972a | ||
|
|
713796a4f7 | ||
|
|
54161aaf39 | ||
|
|
4054519f38 | ||
|
|
e5d5d8b434 | ||
|
|
519fd212d9 | ||
|
|
4b21660fd6 | ||
|
|
ac5c9e7242 | ||
|
|
4c8431bd5b | ||
|
|
a4a93f0999 | ||
|
|
b6a4efa7ba | ||
|
|
a4fbd521e3 | ||
|
|
6fe5e89ecc | ||
|
|
bc40c95f20 | ||
|
|
acd35ac8a2 | ||
|
|
c9d9ec1c77 | ||
|
|
08c4e2d465 | ||
|
|
f300d1bafd | ||
|
|
d395115cc9 | ||
|
|
8571755daa | ||
|
|
9f3368ba1f | ||
|
|
919df1edd5 | ||
|
|
c180422e8b | ||
|
|
e90501a986 | ||
|
|
545af007b4 | ||
|
|
e189ece487 | ||
|
|
cebc2b5bdb | ||
|
|
444d48a259 | ||
|
|
4fd70ad252 | ||
|
|
293326b647 | ||
|
|
b071238eda | ||
|
|
09ef1b66cc | ||
|
|
c06df3889b | ||
|
|
280fc78f7e | ||
|
|
7b9fc04704 | ||
|
|
00e60f2592 | ||
|
|
45e9c762a7 | ||
|
|
77dcb91741 | ||
|
|
383c683716 | ||
|
|
ca3c380493 | ||
|
|
57ec5cb036 | ||
|
|
177b48ac90 | ||
|
|
b4e7fd6fa8 | ||
|
|
baf785d9f1 | ||
|
|
25b75fd6e4 | ||
|
|
0c4c8534b4 | ||
|
|
a559dbfe06 | ||
|
|
de20bb22d9 | ||
|
|
45c0ec9035 | ||
|
|
b16da90e42 | ||
|
|
ce7bcfa666 | ||
|
|
f6833699a6 | ||
|
|
040dc72877 | ||
|
|
056bce3dd9 | ||
|
|
5d6575e97b | ||
|
|
f092d4ffc3 | ||
|
|
5bae15831b | ||
|
|
cf19bd88f0 | ||
|
|
38ac6a1082 | ||
|
|
b88e2bd3ce | ||
|
|
fad24c4308 | ||
|
|
018fd83dba | ||
|
|
aa95da167f | ||
|
|
24e6a0be68 | ||
|
|
a2c962c2f6 | ||
|
|
aa61331181 | ||
|
|
00f0a7589c | ||
|
|
d39609351a | ||
|
|
6985ccf076 | ||
|
|
b448cad4de | ||
|
|
14540b4cc0 | ||
|
|
898b76a549 | ||
|
|
5cf6e382d8 | ||
|
|
e2ba56a227 | ||
|
|
ec9960e28e | ||
|
|
dc59283160 | ||
|
|
d255d44be5 | ||
|
|
b2f68a5a7e | ||
|
|
ec32679ab1 | ||
|
|
8b2471c128 | ||
|
|
022eba2c05 | ||
|
|
029c6fcfe3 | ||
|
|
faaa0b2488 | ||
|
|
daa2ca876b | ||
|
|
81700cfb44 | ||
|
|
6e58db95ed | ||
|
|
9b54862450 | ||
|
|
f79efadd82 | ||
|
|
615a7670bd | ||
|
|
155b8b472f | ||
|
|
b35e3454f0 | ||
|
|
51b4716d45 | ||
|
|
b62803a03a | ||
|
|
616893955f | ||
|
|
0f387a139b | ||
|
|
219c81aac5 | ||
|
|
083003d34f | ||
|
|
699f76c29e | ||
|
|
b670280688 | ||
|
|
37ea84ffe9 | ||
|
|
40b861acbe | ||
|
|
783c4d104c | ||
|
|
9bbddd6aeb | ||
|
|
e753acbc3f | ||
|
|
92b7b1d603 | ||
|
|
b07dc8443e | ||
|
|
3f99c513f3 | ||
|
|
793241523d | ||
|
|
7cff22fb9e | ||
|
|
214f308027 | ||
|
|
c1ce971adb | ||
|
|
f5896be699 | ||
|
|
186f839569 | ||
|
|
4879d906d9 | ||
|
|
09412f0b78 | ||
|
|
2f2d85576f | ||
|
|
362ddd0339 | ||
|
|
9499b7f562 | ||
|
|
d8bb12b5f1 | ||
|
|
5d464f4477 | ||
|
|
aaea0b2659 | ||
|
|
c9ceb09906 | ||
|
|
3b44ede67e | ||
|
|
b48e8eeb0e | ||
|
|
1fafc29ec3 | ||
|
|
1a9d0576c8 | ||
|
|
bc04211b79 | ||
|
|
cfe34355bd | ||
|
|
e3e833d8c0 | ||
|
|
5606a0a968 | ||
|
|
f0358f1da8 | ||
|
|
a3129e9e17 | ||
|
|
7435ede254 | ||
|
|
84e79e92b4 | ||
|
|
6268130998 | ||
|
|
7ad639599a | ||
|
|
caff67b77d | ||
|
|
c45a77d538 | ||
|
|
4b24fe1bf4 | ||
|
|
73e5fb6314 | ||
|
|
84ea28adfa | ||
|
|
955fc97cb2 | ||
|
|
e4012e4f87 | ||
|
|
2c662c428c | ||
|
|
da199deed1 | ||
|
|
abf75cffd9 | ||
|
|
184f13b148 | ||
|
|
d1c7309b29 | ||
|
|
62db6552d2 | ||
|
|
a019b9e1d3 | ||
|
|
cb22572f2b | ||
|
|
b52134e9ee | ||
|
|
44ef82219b | ||
|
|
8c89b0e587 | ||
|
|
322b251def | ||
|
|
0a6767209d | ||
|
|
1694b5d6fd | ||
|
|
0dd9ad43e8 | ||
|
|
c1ae3f1fb2 | ||
|
|
d84627aa2c | ||
|
|
0e020924ff | ||
|
|
4f5e238685 | ||
|
|
72ff680114 | ||
|
|
849ec6fa8f | ||
|
|
36ee3aaec6 | ||
|
|
497d4f50dd | ||
|
|
74a40b2274 | ||
|
|
75e85541a6 | ||
|
|
1d8fbac796 | ||
|
|
daf6d1936f | ||
|
|
1768e8cb62 | ||
|
|
d2d6bfc065 | ||
|
|
d4f6e9c587 | ||
|
|
6d06f2212e | ||
|
|
73b310d59e | ||
|
|
3dc705a7a9 | ||
|
|
8fc8d03cc4 | ||
|
|
df77f42145 | ||
|
|
0dea5bdbea | ||
|
|
5c7f939440 | ||
|
|
09bef28362 | ||
|
|
1f0f94746b | ||
|
|
c057be17d0 |
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
# bitwarden mobile
|
# bitwarden mobile
|
||||||
|
|
||||||
<a href="https://play.google.com/store/apps/details?id=com.x8bit.bitwarden" target="_blank"><img alt="Get it on Google Play" src="https://imgur.com/YQzmZi9.png" width="153" height="46"></a> <a href="https://itunes.apple.com/us/app/bitwarden-free-password-manager/id1137397744?mt=8" target="_blank"><img src="https://imgur.com/GdGqPMY.png" width="135" height="40"></a> <a href="https://www.amazon.com/dp/B06XMYGPMV" target="_blank"><img src="https://imgur.com/f75uYeM.png" width="132" height="45"></a>
|
<a href="https://play.google.com/store/apps/details?id=com.x8bit.bitwarden" target="_blank"><img alt="Get it on Google Play" src="https://imgur.com/YQzmZi9.png" width="153" height="46"></a> <a href="https://itunes.apple.com/us/app/bitwarden-free-password-manager/id1137397744?mt=8" target="_blank"><img src="https://imgur.com/GdGqPMY.png" width="135" height="40"></a>
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
skip_tags: true
|
install:
|
||||||
|
- appveyor DownloadFile https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
|
||||||
before_build:
|
before_build:
|
||||||
- nuget restore
|
- nuget restore
|
||||||
- IF DEFINED keystore_dec_secret nuget install secure-file -ExcludeVersion
|
- IF DEFINED keystore_dec_secret nuget install secure-file -ExcludeVersion
|
||||||
|
- IF DEFINED google_services_dec_secret secure-file\tools\secure-file -decrypt src\Android\google-services.json.enc -secret %google_services_dec_secret%
|
||||||
after_build:
|
after_build:
|
||||||
- ps: IF($env:keystore_dec_secret) { .\src\Android\increment-version.ps1 $($env:APPVEYOR_BUILD_FOLDER) $($env:APPVEYOR_BUILD_NUMBER) }
|
- ps: IF($env:keystore_dec_secret) { .\src\Android\increment-version.ps1 $($env:APPVEYOR_BUILD_FOLDER) $($env:APPVEYOR_BUILD_NUMBER) }
|
||||||
- IF DEFINED keystore_dec_secret secure-file\tools\secure-file -decrypt src\Android\8bit.keystore.enc -secret %keystore_dec_secret%
|
- IF DEFINED keystore_dec_secret secure-file\tools\secure-file -decrypt src\Android\8bit.keystore.enc -secret %keystore_dec_secret%
|
||||||
@@ -9,10 +11,11 @@ after_build:
|
|||||||
- ps: IF($env:keystore_dec_secret) { copy-item src\Android\bin\Release\com.x8bit.bitwarden-Signed.apk .\com.x8bit.bitwarden-$($env:APPVEYOR_BUILD_NUMBER).apk }
|
- ps: IF($env:keystore_dec_secret) { copy-item src\Android\bin\Release\com.x8bit.bitwarden-Signed.apk .\com.x8bit.bitwarden-$($env:APPVEYOR_BUILD_NUMBER).apk }
|
||||||
on_success:
|
on_success:
|
||||||
- IF DEFINED play_dec_secret secure-file\tools\secure-file -decrypt store\google\Publisher\play_creds.json.enc -secret %play_dec_secret%
|
- IF DEFINED play_dec_secret secure-file\tools\secure-file -decrypt store\google\Publisher\play_creds.json.enc -secret %play_dec_secret%
|
||||||
- IF DEFINED play_dec_secret store\google\Publisher\bin\Debug\Publisher.exe %APPVEYOR_BUILD_FOLDER%\store\google\Publisher\play_creds.json %APPVEYOR_BUILD_FOLDER%\src\Android\bin\Release\com.x8bit.bitwarden-Signed.apk alpha
|
- IF DEFINED play_dec_secret dotnet store\google\Publisher\bin\Debug\netcoreapp2.0\Publisher.dll %APPVEYOR_BUILD_FOLDER%\store\google\Publisher\play_creds.json %APPVEYOR_BUILD_FOLDER%\src\Android\bin\Release\com.x8bit.bitwarden-Signed.apk alpha
|
||||||
artifacts:
|
artifacts:
|
||||||
- path: com.x8bit.bitwarden-%APPVEYOR_BUILD_NUMBER%.apk
|
- path: com.x8bit.bitwarden-%APPVEYOR_BUILD_NUMBER%.apk
|
||||||
branches:
|
branches:
|
||||||
except:
|
except:
|
||||||
- l10n_master
|
- l10n_master
|
||||||
|
skip_tags: true
|
||||||
image: Visual Studio 2017
|
image: Visual Studio 2017
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio 15
|
||||||
VisualStudioVersion = 15.0.26730.16
|
VisualStudioVersion = 15.0.27130.2003
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Android", "src\Android\Android.csproj", "{04B18ED2-B76D-4947-8474-191F8FD2B5E0}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Android", "src\Android\Android.csproj", "{04B18ED2-B76D-4947-8474-191F8FD2B5E0}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS", "src\iOS\iOS.csproj", "{1F78403F-9A28-405B-9289-B9DBEB55F074}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS", "src\iOS\iOS.csproj", "{1F78403F-9A28-405B-9289-B9DBEB55F074}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "src\App\App.csproj", "{B490C5DA-639E-4994-ABD2-54222B8A348E}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0D790714-ECF8-4A83-BE4A-E9C84DD1BB5D}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0D790714-ECF8-4A83-BE4A-E9C84DD1BB5D}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EC730FD9-F623-4B6C-B503-95CDCFBCF277}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EC730FD9-F623-4B6C-B503-95CDCFBCF277}"
|
||||||
@@ -19,20 +17,27 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Extension", "src\iOS.Ex
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Core", "src\iOS.Core\iOS.Core.csproj", "{B2538ADA-B605-4D6F-ACD2-62A409680F84}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Core", "src\iOS.Core\iOS.Core.csproj", "{B2538ADA-B605-4D6F-ACD2-62A409680F84}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Test", "test\iOS.Test\iOS.Test.csproj", "{6702027A-F726-4149-863E-7CB924674B9A}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Android.Test", "test\Android.Test\Android.Test.csproj", "{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "store", "store", "{92470CBD-9047-4C3C-8EA3-D972D6622D84}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "store", "store", "{92470CBD-9047-4C3C-8EA3-D972D6622D84}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "google", "google", "{2E399654-26A2-46F6-B9CA-1B496A3F370A}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "google", "google", "{2E399654-26A2-46F6-B9CA-1B496A3F370A}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Publisher", "store\google\Publisher\Publisher.csproj", "{428CACAB-CC26-4F41-9062-1E4A9BC82640}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UWP", "src\UWP\UWP.csproj", "{3A2D5669-ED71-4F2B-BA85-2D36BAA05141}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UWP", "src\UWP\UWP.csproj", "{3A2D5669-ED71-4F2B-BA85-2D36BAA05141}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "UWP.Images", "src\UWP.Images\UWP.Images.shproj", "{0BE54BBB-7772-4289-BD51-1FDBB0CC2446}"
|
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "UWP.Images", "src\UWP.Images\UWP.Images.shproj", "{0BE54BBB-7772-4289-BD51-1FDBB0CC2446}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App", "src\App\App.csproj", "{8A279EE4-4537-4656-9C93-44945E594556}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Publisher", "store\google\Publisher\Publisher.csproj", "{D5D91152-CB01-4F24-A503-304D3A94408B}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F0E2E596-C3DB-474A-9C88-7824662894FA}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.gitignore = .gitignore
|
||||||
|
appveyor.yml = appveyor.yml
|
||||||
|
crowdin.yml = crowdin.yml
|
||||||
|
README.md = README.md
|
||||||
|
SECURITY.md = SECURITY.md
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||||
src\UWP.Images\UWP.Images.projitems*{0be54bbb-7772-4289-bd51-1fdbb0cc2446}*SharedItemsImports = 13
|
src\UWP.Images\UWP.Images.projitems*{0be54bbb-7772-4289-bd51-1fdbb0cc2446}*SharedItemsImports = 13
|
||||||
@@ -152,54 +157,6 @@ Global
|
|||||||
{1F78403F-9A28-405B-9289-B9DBEB55F074}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
{1F78403F-9A28-405B-9289-B9DBEB55F074}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||||
{1F78403F-9A28-405B-9289-B9DBEB55F074}.Release|x64.ActiveCfg = Release|iPhone
|
{1F78403F-9A28-405B-9289-B9DBEB55F074}.Release|x64.ActiveCfg = Release|iPhone
|
||||||
{1F78403F-9A28-405B-9289-B9DBEB55F074}.Release|x86.ActiveCfg = Release|iPhone
|
{1F78403F-9A28-405B-9289-B9DBEB55F074}.Release|x86.ActiveCfg = Release|iPhone
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Ad-Hoc|ARM.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Ad-Hoc|x64.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Ad-Hoc|x86.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.AppStore|ARM.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.AppStore|ARM.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.AppStore|iPhone.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.AppStore|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.AppStore|x64.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.AppStore|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.AppStore|x86.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Debug|ARM.Build.0 = Debug|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Debug|iPhone.Build.0 = Debug|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Release|ARM.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Release|ARM.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Release|iPhone.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Release|iPhone.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{A300DCE1-8D10-4267-B96A-CB01AEB7C220}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
{A300DCE1-8D10-4267-B96A-CB01AEB7C220}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{A300DCE1-8D10-4267-B96A-CB01AEB7C220}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
{A300DCE1-8D10-4267-B96A-CB01AEB7C220}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
||||||
{A300DCE1-8D10-4267-B96A-CB01AEB7C220}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU
|
{A300DCE1-8D10-4267-B96A-CB01AEB7C220}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU
|
||||||
@@ -315,155 +272,6 @@ Global
|
|||||||
{B2538ADA-B605-4D6F-ACD2-62A409680F84}.Release|x64.Build.0 = Release|Any CPU
|
{B2538ADA-B605-4D6F-ACD2-62A409680F84}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{B2538ADA-B605-4D6F-ACD2-62A409680F84}.Release|x86.ActiveCfg = Release|Any CPU
|
{B2538ADA-B605-4D6F-ACD2-62A409680F84}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{B2538ADA-B605-4D6F-ACD2-62A409680F84}.Release|x86.Build.0 = Release|Any CPU
|
{B2538ADA-B605-4D6F-ACD2-62A409680F84}.Release|x86.Build.0 = Release|Any CPU
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Ad-Hoc|ARM.ActiveCfg = Ad-Hoc|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Ad-Hoc|x64.ActiveCfg = Ad-Hoc|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Ad-Hoc|x86.ActiveCfg = Ad-Hoc|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.AppStore|ARM.ActiveCfg = AppStore|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.AppStore|iPhone.Build.0 = AppStore|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.AppStore|x64.ActiveCfg = AppStore|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.AppStore|x86.ActiveCfg = AppStore|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Debug|Any CPU.ActiveCfg = Debug|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Debug|ARM.ActiveCfg = Debug|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Debug|iPhone.Build.0 = Debug|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Debug|x64.ActiveCfg = Debug|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Debug|x86.ActiveCfg = Debug|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Release|Any CPU.ActiveCfg = Release|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Release|ARM.ActiveCfg = Release|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Release|iPhone.ActiveCfg = Release|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Release|iPhone.Build.0 = Release|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Release|x64.ActiveCfg = Release|iPhone
|
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A}.Release|x86.ActiveCfg = Release|iPhone
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|Any CPU.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|ARM.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|ARM.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|iPhone.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|x64.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|x64.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|x86.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Ad-Hoc|x86.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|Any CPU.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|ARM.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|ARM.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|ARM.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|iPhone.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|iPhone.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|x64.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|x64.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|x86.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.AppStore|x86.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Debug|ARM.Build.0 = Debug|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Debug|ARM.Deploy.0 = Debug|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Debug|iPhone.Build.0 = Debug|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Debug|iPhone.Deploy.0 = Debug|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Debug|x86.Deploy.0 = Debug|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|ARM.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|ARM.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|ARM.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|iPhone.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|iPhone.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|iPhone.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|x64.Deploy.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE}.Release|x86.Deploy.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Ad-Hoc|ARM.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Ad-Hoc|x64.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Ad-Hoc|x86.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.AppStore|ARM.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.AppStore|ARM.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.AppStore|iPhone.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.AppStore|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.AppStore|x64.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.AppStore|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.AppStore|x86.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Debug|ARM.Build.0 = Debug|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Debug|iPhone.Build.0 = Debug|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Release|ARM.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Release|ARM.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Release|iPhone.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Release|iPhone.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{3A2D5669-ED71-4F2B-BA85-2D36BAA05141}.Ad-Hoc|Any CPU.ActiveCfg = Release|x64
|
{3A2D5669-ED71-4F2B-BA85-2D36BAA05141}.Ad-Hoc|Any CPU.ActiveCfg = Release|x64
|
||||||
{3A2D5669-ED71-4F2B-BA85-2D36BAA05141}.Ad-Hoc|Any CPU.Build.0 = Release|x64
|
{3A2D5669-ED71-4F2B-BA85-2D36BAA05141}.Ad-Hoc|Any CPU.Build.0 = Release|x64
|
||||||
{3A2D5669-ED71-4F2B-BA85-2D36BAA05141}.Ad-Hoc|Any CPU.Deploy.0 = Release|x64
|
{3A2D5669-ED71-4F2B-BA85-2D36BAA05141}.Ad-Hoc|Any CPU.Deploy.0 = Release|x64
|
||||||
@@ -524,6 +332,102 @@ Global
|
|||||||
{3A2D5669-ED71-4F2B-BA85-2D36BAA05141}.Release|x86.ActiveCfg = Release|x86
|
{3A2D5669-ED71-4F2B-BA85-2D36BAA05141}.Release|x86.ActiveCfg = Release|x86
|
||||||
{3A2D5669-ED71-4F2B-BA85-2D36BAA05141}.Release|x86.Build.0 = Release|x86
|
{3A2D5669-ED71-4F2B-BA85-2D36BAA05141}.Release|x86.Build.0 = Release|x86
|
||||||
{3A2D5669-ED71-4F2B-BA85-2D36BAA05141}.Release|x86.Deploy.0 = Release|x86
|
{3A2D5669-ED71-4F2B-BA85-2D36BAA05141}.Release|x86.Deploy.0 = Release|x86
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.AppStore|ARM.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.AppStore|ARM.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.AppStore|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.AppStore|x64.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.AppStore|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.AppStore|x86.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Release|ARM.Build.0 = Release|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.AppStore|ARM.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.AppStore|ARM.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.AppStore|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.AppStore|x64.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.AppStore|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.AppStore|x86.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Release|ARM.Build.0 = Release|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -531,16 +435,14 @@ Global
|
|||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{04B18ED2-B76D-4947-8474-191F8FD2B5E0} = {EC730FD9-F623-4B6C-B503-95CDCFBCF277}
|
{04B18ED2-B76D-4947-8474-191F8FD2B5E0} = {EC730FD9-F623-4B6C-B503-95CDCFBCF277}
|
||||||
{1F78403F-9A28-405B-9289-B9DBEB55F074} = {EC730FD9-F623-4B6C-B503-95CDCFBCF277}
|
{1F78403F-9A28-405B-9289-B9DBEB55F074} = {EC730FD9-F623-4B6C-B503-95CDCFBCF277}
|
||||||
{B490C5DA-639E-4994-ABD2-54222B8A348E} = {EC730FD9-F623-4B6C-B503-95CDCFBCF277}
|
|
||||||
{A300DCE1-8D10-4267-B96A-CB01AEB7C220} = {0D790714-ECF8-4A83-BE4A-E9C84DD1BB5D}
|
{A300DCE1-8D10-4267-B96A-CB01AEB7C220} = {0D790714-ECF8-4A83-BE4A-E9C84DD1BB5D}
|
||||||
{32F5A2D6-F54D-4DA1-AE26-0A980D48F422} = {EC730FD9-F623-4B6C-B503-95CDCFBCF277}
|
{32F5A2D6-F54D-4DA1-AE26-0A980D48F422} = {EC730FD9-F623-4B6C-B503-95CDCFBCF277}
|
||||||
{B2538ADA-B605-4D6F-ACD2-62A409680F84} = {EC730FD9-F623-4B6C-B503-95CDCFBCF277}
|
{B2538ADA-B605-4D6F-ACD2-62A409680F84} = {EC730FD9-F623-4B6C-B503-95CDCFBCF277}
|
||||||
{6702027A-F726-4149-863E-7CB924674B9A} = {0D790714-ECF8-4A83-BE4A-E9C84DD1BB5D}
|
|
||||||
{FA507A17-D4E3-46DF-ACD8-D7E6D7D4E3AE} = {0D790714-ECF8-4A83-BE4A-E9C84DD1BB5D}
|
|
||||||
{2E399654-26A2-46F6-B9CA-1B496A3F370A} = {92470CBD-9047-4C3C-8EA3-D972D6622D84}
|
{2E399654-26A2-46F6-B9CA-1B496A3F370A} = {92470CBD-9047-4C3C-8EA3-D972D6622D84}
|
||||||
{428CACAB-CC26-4F41-9062-1E4A9BC82640} = {2E399654-26A2-46F6-B9CA-1B496A3F370A}
|
|
||||||
{3A2D5669-ED71-4F2B-BA85-2D36BAA05141} = {EC730FD9-F623-4B6C-B503-95CDCFBCF277}
|
{3A2D5669-ED71-4F2B-BA85-2D36BAA05141} = {EC730FD9-F623-4B6C-B503-95CDCFBCF277}
|
||||||
{0BE54BBB-7772-4289-BD51-1FDBB0CC2446} = {EC730FD9-F623-4B6C-B503-95CDCFBCF277}
|
{0BE54BBB-7772-4289-BD51-1FDBB0CC2446} = {EC730FD9-F623-4B6C-B503-95CDCFBCF277}
|
||||||
|
{8A279EE4-4537-4656-9C93-44945E594556} = {EC730FD9-F623-4B6C-B503-95CDCFBCF277}
|
||||||
|
{D5D91152-CB01-4F24-A503-304D3A94408B} = {2E399654-26A2-46F6-B9CA-1B496A3F370A}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {318CB2DF-0118-43A3-AC83-56BADCF71CCD}
|
SolutionGuid = {318CB2DF-0118-43A3-AC83-56BADCF71CCD}
|
||||||
|
|||||||
@@ -7,14 +7,6 @@ files:
|
|||||||
zh-TW: zh-Hant
|
zh-TW: zh-Hant
|
||||||
pt-PT: pt-PT
|
pt-PT: pt-PT
|
||||||
pt-BR: pt-BR
|
pt-BR: pt-BR
|
||||||
- source: /store/amazon/en/copy.resx
|
|
||||||
translation: /store/amazon/%two_letters_code%/copy.resx
|
|
||||||
languages_mapping:
|
|
||||||
two_letters_code:
|
|
||||||
zh-CN: zh-Hans
|
|
||||||
zh-TW: zh-Hant
|
|
||||||
pt-PT: pt-PT
|
|
||||||
pt-BR: pt-BR
|
|
||||||
- source: /store/apple/en/copy.resx
|
- source: /store/apple/en/copy.resx
|
||||||
translation: /store/apple/%two_letters_code%/copy.resx
|
translation: /store/apple/%two_letters_code%/copy.resx
|
||||||
languages_mapping:
|
languages_mapping:
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProductVersion>8.0.30703</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>{04B18ED2-B76D-4947-8474-191F8FD2B5E0}</ProjectGuid>
|
<ProjectGuid>{04B18ED2-B76D-4947-8474-191F8FD2B5E0}</ProjectGuid>
|
||||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
@@ -17,7 +15,7 @@
|
|||||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||||
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
|
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
|
||||||
<TargetFrameworkVersion>v7.1</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
|
||||||
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis>
|
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis>
|
||||||
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
|
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
|
||||||
<AndroidStoreUncompressedFileExtensions />
|
<AndroidStoreUncompressedFileExtensions />
|
||||||
@@ -73,220 +71,31 @@
|
|||||||
<AndroidSupportedAbis>armeabi;armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
|
<AndroidSupportedAbis>armeabi;armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Acr.Support.Android, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Acr.Support.2.1.0\lib\MonoAndroid10\Acr.Support.Android.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Acr.UserDialogs, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Acr.UserDialogs.6.3.10\lib\MonoAndroid10\Acr.UserDialogs.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Acr.UserDialogs.Interface, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Acr.UserDialogs.6.3.10\lib\MonoAndroid10\Acr.UserDialogs.Interface.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="AndHUD, Version=1.2.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\AndHUD.1.2.0\lib\MonoAndroid\AndHUD.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="BouncyCastle.Crypto, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
|
|
||||||
<HintPath>..\..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="FFImageLoading, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.FFImageLoading.2.2.9\lib\MonoAndroid10\FFImageLoading.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="FFImageLoading.Forms, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.FFImageLoading.Forms.2.2.9\lib\MonoAndroid10\FFImageLoading.Forms.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="FFImageLoading.Forms.Droid, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.FFImageLoading.Forms.2.2.9\lib\MonoAndroid10\FFImageLoading.Forms.Droid.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="FFImageLoading.Platform, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.FFImageLoading.2.2.9\lib\MonoAndroid10\FFImageLoading.Platform.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="FormsViewGroup, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.Forms.2.3.4.267\lib\MonoAndroid10\FormsViewGroup.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="HockeySDK, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\HockeySDK.Xamarin.4.1.5\lib\MonoAndroid403\HockeySDK.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="HockeySDK.AndroidBindings, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\HockeySDK.Xamarin.4.1.5\lib\MonoAndroid403\HockeySDK.AndroidBindings.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Microsoft.Practices.ServiceLocation, Version=1.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Mono.Android" />
|
<Reference Include="Mono.Android" />
|
||||||
<Reference Include="Mono.Android.Export" />
|
<Reference Include="Mono.Android.Export" />
|
||||||
<Reference Include="mscorlib" />
|
<Reference Include="Naxam.Ittianyu.BottomNavExtension, Version=1.2.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll</HintPath>
|
<HintPath>.\Naxam.Ittianyu.BottomNavExtension.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="PCLCrypto, Version=2.0.0.0, Culture=neutral, PublicKeyToken=d4421c8a4786956c, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\PCLCrypto.2.0.147\lib\MonoAndroid23\PCLCrypto.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="PInvoke.BCrypt, Version=0.5.0.0, Culture=neutral, PublicKeyToken=9e300f9f87f04a7a, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\PInvoke.BCrypt.0.5.97\lib\portable-net45+win8+wpa81\PInvoke.BCrypt.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="PInvoke.Kernel32, Version=0.5.0.0, Culture=neutral, PublicKeyToken=9e300f9f87f04a7a, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\PInvoke.Kernel32.0.5.97\lib\portable-net45+win8+wpa81\PInvoke.Kernel32.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="PInvoke.NCrypt, Version=0.5.0.0, Culture=neutral, PublicKeyToken=9e300f9f87f04a7a, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\PInvoke.NCrypt.0.5.97\lib\portable-net45+win8+wpa81\PInvoke.NCrypt.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="PInvoke.Windows.Core, Version=0.5.0.0, Culture=neutral, PublicKeyToken=9e300f9f87f04a7a, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\PInvoke.Windows.Core.0.5.97\lib\portable-net45+win8+wpa81\PInvoke.Windows.Core.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Connectivity, Version=3.0.2.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xam.Plugin.Connectivity.3.0.2\lib\MonoAndroid10\Plugin.Connectivity.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Connectivity.Abstractions, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xam.Plugin.Connectivity.3.0.2\lib\MonoAndroid10\Plugin.Connectivity.Abstractions.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.CurrentActivity, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Plugin.CurrentActivity.1.0.1\lib\MonoAndroid10\Plugin.CurrentActivity.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Fingerprint, Version=1.4.5.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Plugin.Fingerprint.1.4.5\lib\MonoAndroid\Plugin.Fingerprint.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Fingerprint.Abstractions, Version=1.4.5.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Plugin.Fingerprint.1.4.5\lib\MonoAndroid\Plugin.Fingerprint.Abstractions.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Fingerprint.Android.Samsung, Version=1.4.5.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Plugin.Fingerprint.1.4.5\lib\MonoAndroid\Plugin.Fingerprint.Android.Samsung.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Settings, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xam.Plugins.Settings.3.0.1\lib\MonoAndroid10\Plugin.Settings.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Settings.Abstractions, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xam.Plugins.Settings.3.0.1\lib\MonoAndroid10\Plugin.Settings.Abstractions.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="SimpleInjector, Version=4.0.8.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\SimpleInjector.4.0.8\lib\netstandard1.3\SimpleInjector.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Splat, Version=1.6.2.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Splat.1.6.2\lib\monoandroid\Splat.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="SQLite-net, Version=1.5.166.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\sqlite-net-pcl.1.5.166-beta\lib\netstandard1.1\SQLite-net.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="SQLitePCLRaw.batteries_green, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a84b7dcfb1391f7f, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\MonoAndroid\SQLitePCLRaw.batteries_green.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="SQLitePCLRaw.batteries_v2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\MonoAndroid\SQLitePCLRaw.batteries_v2.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\SQLitePCLRaw.core.1.1.8\lib\MonoAndroid\SQLitePCLRaw.core.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="SQLitePCLRaw.lib.e_sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e4ad490600e2234c, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\SQLitePCLRaw.lib.e_sqlite3.android.1.1.8\lib\MonoAndroid\SQLitePCLRaw.lib.e_sqlite3.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="SQLitePCLRaw.provider.e_sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9c301db686d0bd12, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\SQLitePCLRaw.provider.e_sqlite3.android.1.1.8\lib\MonoAndroid\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.IO.Compression" />
|
<Reference Include="System.IO.Compression" />
|
||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
<Reference Include="Validation, Version=2.3.0.0, Culture=neutral, PublicKeyToken=2fc06f0d701809a7, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Validation.2.3.7\lib\dotnet\Validation.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.Android.Support.Animated.Vector.Drawable, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.Animated.Vector.Drawable.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.Android.Support.Design, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.Android.Support.Design.23.3.0\lib\MonoAndroid43\Xamarin.Android.Support.Design.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.Android.Support.v4, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.Android.Support.v4.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.v4.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.Android.Support.v7.AppCompat, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.Android.Support.v7.AppCompat.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.v7.AppCompat.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.Android.Support.v7.CardView, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.Android.Support.v7.CardView.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.v7.CardView.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.Android.Support.v7.MediaRouter, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.Android.Support.v7.MediaRouter.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.v7.MediaRouter.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.Android.Support.v7.RecyclerView, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.Android.Support.v7.RecyclerView.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.v7.RecyclerView.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.Android.Support.Vector.Drawable, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.Android.Support.Vector.Drawable.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.Vector.Drawable.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.Forms.Core, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.Forms.2.3.4.267\lib\MonoAndroid10\Xamarin.Forms.Core.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.Forms.Platform, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.Forms.2.3.4.267\lib\MonoAndroid10\Xamarin.Forms.Platform.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.Forms.Platform.Android, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.Forms.2.3.4.267\lib\MonoAndroid10\Xamarin.Forms.Platform.Android.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.Forms.Xaml, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.Forms.2.3.4.267\lib\MonoAndroid10\Xamarin.Forms.Xaml.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.GooglePlayServices.Analytics, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.GooglePlayServices.Analytics.29.0.0.2\lib\MonoAndroid41\Xamarin.GooglePlayServices.Analytics.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.GooglePlayServices.Base, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.GooglePlayServices.Base.29.0.0.2\lib\MonoAndroid41\Xamarin.GooglePlayServices.Base.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.GooglePlayServices.Basement, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.GooglePlayServices.Basement.29.0.0.2\lib\MonoAndroid41\Xamarin.GooglePlayServices.Basement.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.GooglePlayServices.Gcm, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.GooglePlayServices.Gcm.29.0.0.2\lib\MonoAndroid41\Xamarin.GooglePlayServices.Gcm.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.GooglePlayServices.Measurement, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\Xamarin.GooglePlayServices.Measurement.29.0.0.2\lib\MonoAndroid41\Xamarin.GooglePlayServices.Measurement.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="XLabs.Ioc, Version=2.0.5782.12218, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\XLabs.IoC.2.0.5782\lib\portable-net45+netcore45+wp8+MonoAndroid1+MonoTouch1\XLabs.Ioc.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="XLabs.Ioc.SimpleInjector, Version=2.0.5782.12229, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\XLabs.IoC.SimpleInjector.2.0.5782\lib\portable-net45+netcore45+wp8+MonoAndroid1+MonoTouch1\XLabs.Ioc.SimpleInjector.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="ZXing.Net.Mobile.Core, Version=2.1.47.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\ZXing.Net.Mobile.2.1.47\lib\MonoAndroid403\ZXing.Net.Mobile.Core.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="ZXing.Net.Mobile.Forms, Version=2.1.47.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\ZXing.Net.Mobile.Forms.2.1.47\lib\MonoAndroid403\ZXing.Net.Mobile.Forms.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="ZXing.Net.Mobile.Forms.Android, Version=2.1.47.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\ZXing.Net.Mobile.Forms.2.1.47\lib\MonoAndroid403\ZXing.Net.Mobile.Forms.Android.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="zxing.portable, Version=2.1.47.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\ZXing.Net.Mobile.2.1.47\lib\MonoAndroid403\zxing.portable.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="ZXingNetMobile, Version=2.1.47.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\packages\ZXing.Net.Mobile.2.1.47\lib\MonoAndroid403\ZXingNetMobile.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="AutofillActivity.cs" />
|
<Compile Include="AutofillActivity.cs" />
|
||||||
<Compile Include="AutofillCredentials.cs" />
|
<Compile Include="AutofillCredentials.cs" />
|
||||||
|
<Compile Include="Controls\CustomSliderRenderer.cs" />
|
||||||
|
<Compile Include="Controls\ExtendedListViewRenderer.cs" />
|
||||||
|
<Compile Include="FirebaseInstanceIdService.cs" />
|
||||||
|
<Compile Include="FirebaseMessagingService.cs" />
|
||||||
|
<Compile Include="Autofill\Field.cs" />
|
||||||
|
<Compile Include="Autofill\FieldCollection.cs" />
|
||||||
|
<Compile Include="Autofill\AutofillService.cs" />
|
||||||
|
<Compile Include="Autofill\AutofillHelpers.cs" />
|
||||||
|
<Compile Include="Autofill\FilledItem.cs" />
|
||||||
|
<Compile Include="Autofill\SavedItem.cs" />
|
||||||
|
<Compile Include="Controls\CustomLabelRenderer.cs" />
|
||||||
<Compile Include="Controls\CustomSearchBarRenderer.cs" />
|
<Compile Include="Controls\CustomSearchBarRenderer.cs" />
|
||||||
<Compile Include="Controls\CustomButtonRenderer.cs" />
|
<Compile Include="Controls\CustomButtonRenderer.cs" />
|
||||||
<Compile Include="Controls\HybridWebViewRenderer.cs" />
|
<Compile Include="Controls\HybridWebViewRenderer.cs" />
|
||||||
@@ -302,55 +111,51 @@
|
|||||||
<Compile Include="Controls\ExtendedPickerRenderer.cs" />
|
<Compile Include="Controls\ExtendedPickerRenderer.cs" />
|
||||||
<Compile Include="Controls\ExtendedEntryRenderer.cs" />
|
<Compile Include="Controls\ExtendedEntryRenderer.cs" />
|
||||||
<Compile Include="MyVaultTileService.cs" />
|
<Compile Include="MyVaultTileService.cs" />
|
||||||
|
<Compile Include="Services\AndroidPushNotificationService.cs" />
|
||||||
|
<Compile Include="Services\GoogleAnalyticsService.cs" />
|
||||||
<Compile Include="Services\HttpService.cs" />
|
<Compile Include="Services\HttpService.cs" />
|
||||||
<Compile Include="Services\AndroidKeyStoreStorageService.cs" />
|
<Compile Include="Services\AndroidKeyStoreStorageService.cs" />
|
||||||
<Compile Include="Services\LocalizeService.cs" />
|
<Compile Include="Services\LocalizeService.cs" />
|
||||||
<Compile Include="MainApplication.cs" />
|
<Compile Include="MainApplication.cs" />
|
||||||
<Compile Include="Resources\Resource.Designer.cs" />
|
<Compile Include="Resources\Resource.Designer.cs" />
|
||||||
<Compile Include="Services\DeviceInfoService.cs" />
|
<Compile Include="Services\DeviceInfoService.cs" />
|
||||||
<Compile Include="Services\GoogleAnalyticsService.cs" />
|
|
||||||
<Compile Include="Services\AppInfoService.cs" />
|
<Compile Include="Services\AppInfoService.cs" />
|
||||||
<Compile Include="Services\DeviceActionService.cs" />
|
<Compile Include="Services\DeviceActionService.cs" />
|
||||||
<Compile Include="Services\BouncyCastleKeyDerivationService.cs" />
|
<Compile Include="Services\BouncyCastleKeyDerivationService.cs" />
|
||||||
<Compile Include="Services\KeyStoreStorageService.cs" />
|
|
||||||
<Compile Include="MainActivity.cs" />
|
<Compile Include="MainActivity.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Services\LogService.cs" />
|
<Compile Include="Services\LogService.cs" />
|
||||||
<Compile Include="Services\MemoryService.cs" />
|
<Compile Include="Services\MemoryService.cs" />
|
||||||
<Compile Include="Services\AndroidPushNotificationService.cs" />
|
|
||||||
<Compile Include="Services\ReflectionService.cs" />
|
<Compile Include="Services\ReflectionService.cs" />
|
||||||
<Compile Include="Services\SqlService.cs" />
|
<Compile Include="Services\SqlService.cs" />
|
||||||
<Compile Include="SplashActivity.cs" />
|
<Compile Include="SplashActivity.cs" />
|
||||||
<Compile Include="PackageReplacedReceiver.cs" />
|
<Compile Include="PackageReplacedReceiver.cs" />
|
||||||
|
<Compile Include="Autofill\Parser.cs" />
|
||||||
<Compile Include="Utilities.cs" />
|
<Compile Include="Utilities.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="8bit.keystore.enc" />
|
<None Include="8bit.keystore.enc" />
|
||||||
<None Include="app.config">
|
<GoogleServicesJson Include="google-services.json" />
|
||||||
<SubType>Designer</SubType>
|
<None Include="google-services.json.enc" />
|
||||||
</None>
|
|
||||||
<None Include="increment-version.ps1" />
|
<None Include="increment-version.ps1" />
|
||||||
<None Include="packages.config">
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
</None>
|
|
||||||
<None Include="Resources\AboutResources.txt" />
|
<None Include="Resources\AboutResources.txt" />
|
||||||
<None Include="Assets\AboutAssets.txt" />
|
<None Include="Assets\AboutAssets.txt" />
|
||||||
|
<AndroidResource Include="Resources\drawable\slider_thumb.xml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</AndroidResource>
|
||||||
<AndroidResource Include="Resources\layout\tabs.axml">
|
<AndroidResource Include="Resources\layout\tabs.axml">
|
||||||
<SubType>AndroidResource</SubType>
|
<SubType>AndroidResource</SubType>
|
||||||
</AndroidResource>
|
</AndroidResource>
|
||||||
<AndroidResource Include="Resources\layout\toolbar.axml">
|
<AndroidResource Include="Resources\layout\toolbar.axml">
|
||||||
<SubType>AndroidResource</SubType>
|
<SubType>AndroidResource</SubType>
|
||||||
</AndroidResource>
|
</AndroidResource>
|
||||||
|
<AndroidResource Include="Resources\layout\autofill_listitem.axml">
|
||||||
|
<SubType>AndroidResource</SubType>
|
||||||
|
</AndroidResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="Properties\AndroidManifest.xml" />
|
<None Include="Properties\AndroidManifest.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\App\App.csproj">
|
|
||||||
<Project>{b490c5da-639e-4994-abd2-54222b8a348e}</Project>
|
|
||||||
<Name>App</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\values-v21\styles.xml" />
|
<AndroidResource Include="Resources\values-v21\styles.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -600,66 +405,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable\splash_screen.xml" />
|
<AndroidResource Include="Resources\drawable\splash_screen.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\tools_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\cogs_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\fa_lock_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\star_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\fa_lock_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\star_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\cogs_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\cogs_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\fa_lock_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\star_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\cogs_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\fa_lock_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\star_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\fa_lock_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\star_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\cogs_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\tools_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\tools_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\tools_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\tools_selected.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable\cogs.png" />
|
<AndroidResource Include="Resources\drawable\cogs.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -675,36 +420,21 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\cogs.png" />
|
<AndroidResource Include="Resources\drawable-xxxhdpi\cogs.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\star.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable\fa_lock.png" />
|
<AndroidResource Include="Resources\drawable\fa_lock.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\star.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\fa_lock.png" />
|
<AndroidResource Include="Resources\drawable-hdpi\fa_lock.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\star.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\fa_lock.png" />
|
<AndroidResource Include="Resources\drawable-xhdpi\fa_lock.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\star.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\fa_lock.png" />
|
<AndroidResource Include="Resources\drawable-xxhdpi\fa_lock.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\fa_lock.png" />
|
<AndroidResource Include="Resources\drawable-xxxhdpi\fa_lock.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\star.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\tools.png" />
|
<AndroidResource Include="Resources\drawable-xxxhdpi\tools.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -909,9 +639,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\paperclip.png" />
|
<AndroidResource Include="Resources\drawable-xxxhdpi\paperclip.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\trash.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\trash.png" />
|
<AndroidResource Include="Resources\drawable-hdpi\trash.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -1062,23 +789,100 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable\apple.png" />
|
<AndroidResource Include="Resources\drawable\apple.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\xml\autofillservice.xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\cube.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-hdpi\cube.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-xhdpi\cube.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-xxhdpi\cube.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-xxxhdpi\cube.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\folder_o.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-hdpi\folder_o.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-xhdpi\folder_o.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-xxhdpi\folder_o.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-xxxhdpi\folder_o.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\autofill_enable.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-hdpi\autofill_enable.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-xhdpi\autofill_enable.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-xxhdpi\autofill_enable.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\autofill_use.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-hdpi\autofill_use.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-xhdpi\autofill_use.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-xxhdpi\autofill_use.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\App\App.csproj">
|
||||||
|
<Project>{8a279ee4-4537-4656-9c93-44945e594556}</Project>
|
||||||
|
<Name>App</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="SimpleInjector">
|
||||||
|
<Version>4.0.12</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Xamarin.Firebase.Messaging">
|
||||||
|
<Version>42.1021.1</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Xamarin.GooglePlayServices.Analytics">
|
||||||
|
<Version>42.1021.1</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="XLabs.IoC.SimpleInjector" Version="2.0.5782" />
|
||||||
|
<PackageReference Include="Portable.BouncyCastle" Version="1.8.1.3" />
|
||||||
|
<PackageReference Include="Plugin.CurrentActivity" Version="1.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\bottom_nav_bg.xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-hdpi\pencil.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\pencil.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-xhdpi\pencil.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-xxhdpi\pencil.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-xxxhdpi\pencil.png" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||||
<Import Project="..\..\packages\Xamarin.Android.Support.Vector.Drawable.23.3.0\build\Xamarin.Android.Support.Vector.Drawable.targets" Condition="Exists('..\..\packages\Xamarin.Android.Support.Vector.Drawable.23.3.0\build\Xamarin.Android.Support.Vector.Drawable.targets')" />
|
|
||||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
|
||||||
<PropertyGroup>
|
|
||||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Error Condition="!Exists('..\..\packages\Xamarin.Android.Support.Vector.Drawable.23.3.0\build\Xamarin.Android.Support.Vector.Drawable.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Xamarin.Android.Support.Vector.Drawable.23.3.0\build\Xamarin.Android.Support.Vector.Drawable.targets'))" />
|
|
||||||
<Error Condition="!Exists('..\..\packages\Xamarin.GooglePlayServices.Basement.29.0.0.2\build\Xamarin.GooglePlayServices.Basement.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Xamarin.GooglePlayServices.Basement.29.0.0.2\build\Xamarin.GooglePlayServices.Basement.targets'))" />
|
|
||||||
<Error Condition="!Exists('..\..\packages\Xamarin.Forms.2.3.4.267\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Xamarin.Forms.2.3.4.267\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets'))" />
|
|
||||||
</Target>
|
|
||||||
<Import Project="..\..\packages\Xamarin.GooglePlayServices.Basement.29.0.0.2\build\Xamarin.GooglePlayServices.Basement.targets" Condition="Exists('..\..\packages\Xamarin.GooglePlayServices.Basement.29.0.0.2\build\Xamarin.GooglePlayServices.Basement.targets')" />
|
|
||||||
<Import Project="..\..\packages\Xamarin.Forms.2.3.4.267\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets" Condition="Exists('..\..\packages\Xamarin.Forms.2.3.4.267\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets')" />
|
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
|
||||||
<Target Name="BeforeBuild">
|
|
||||||
</Target>
|
|
||||||
<Target Name="AfterBuild">
|
|
||||||
</Target>
|
|
||||||
-->
|
|
||||||
</Project>
|
</Project>
|
||||||
146
src/Android/Autofill/AutofillHelpers.cs
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Service.Autofill;
|
||||||
|
using Android.Widget;
|
||||||
|
using System.Linq;
|
||||||
|
using Android.App;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Bit.App.Enums;
|
||||||
|
using Android.Views.Autofill;
|
||||||
|
|
||||||
|
namespace Bit.Android.Autofill
|
||||||
|
{
|
||||||
|
public static class AutofillHelpers
|
||||||
|
{
|
||||||
|
private static int _pendingIntentId = 0;
|
||||||
|
|
||||||
|
public static async Task<List<FilledItem>> GetFillItemsAsync(Parser parser, ICipherService service)
|
||||||
|
{
|
||||||
|
var items = new List<FilledItem>();
|
||||||
|
|
||||||
|
if(parser.FieldCollection.FillableForLogin)
|
||||||
|
{
|
||||||
|
var ciphers = await service.GetAllAsync(parser.Uri);
|
||||||
|
if(ciphers.Item1.Any() || ciphers.Item2.Any())
|
||||||
|
{
|
||||||
|
var allCiphers = ciphers.Item1.ToList();
|
||||||
|
allCiphers.AddRange(ciphers.Item2.ToList());
|
||||||
|
foreach(var cipher in allCiphers)
|
||||||
|
{
|
||||||
|
items.Add(new FilledItem(cipher));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(parser.FieldCollection.FillableForCard)
|
||||||
|
{
|
||||||
|
var ciphers = await service.GetAllAsync();
|
||||||
|
foreach(var cipher in ciphers.Where(c => c.Type == CipherType.Card))
|
||||||
|
{
|
||||||
|
items.Add(new FilledItem(cipher));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FillResponse BuildFillResponse(Context context, Parser parser, List<FilledItem> items, bool locked)
|
||||||
|
{
|
||||||
|
var responseBuilder = new FillResponse.Builder();
|
||||||
|
if(items != null && items.Count > 0)
|
||||||
|
{
|
||||||
|
foreach(var item in items)
|
||||||
|
{
|
||||||
|
var dataset = BuildDataset(context, parser.FieldCollection, item);
|
||||||
|
if(dataset != null)
|
||||||
|
{
|
||||||
|
responseBuilder.AddDataset(dataset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
responseBuilder.AddDataset(BuildVaultDataset(context, parser.FieldCollection, parser.Uri, locked));
|
||||||
|
AddSaveInfo(responseBuilder, parser.FieldCollection);
|
||||||
|
responseBuilder.SetIgnoredIds(parser.FieldCollection.IgnoreAutofillIds.ToArray());
|
||||||
|
return responseBuilder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dataset BuildDataset(Context context, FieldCollection fields, FilledItem filledItem)
|
||||||
|
{
|
||||||
|
var datasetBuilder = new Dataset.Builder(
|
||||||
|
BuildListView(context.PackageName, filledItem.Name, filledItem.Subtitle, filledItem.Icon));
|
||||||
|
if(filledItem.ApplyToFields(fields, datasetBuilder))
|
||||||
|
{
|
||||||
|
return datasetBuilder.Build();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dataset BuildVaultDataset(Context context, FieldCollection fields, string uri, bool locked)
|
||||||
|
{
|
||||||
|
var intent = new Intent(context, typeof(MainActivity));
|
||||||
|
intent.PutExtra("autofillFramework", true);
|
||||||
|
if(fields.FillableForLogin)
|
||||||
|
{
|
||||||
|
intent.PutExtra("autofillFrameworkFillType", (int)CipherType.Login);
|
||||||
|
}
|
||||||
|
else if(fields.FillableForCard)
|
||||||
|
{
|
||||||
|
intent.PutExtra("autofillFrameworkFillType", (int)CipherType.Card);
|
||||||
|
}
|
||||||
|
else if(fields.FillableForIdentity)
|
||||||
|
{
|
||||||
|
intent.PutExtra("autofillFrameworkFillType", (int)CipherType.Identity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
intent.PutExtra("autofillFrameworkUri", uri);
|
||||||
|
var pendingIntent = PendingIntent.GetActivity(context, ++_pendingIntentId, intent,
|
||||||
|
PendingIntentFlags.CancelCurrent);
|
||||||
|
|
||||||
|
var view = BuildListView(context.PackageName, AppResources.AutofillWithBitwarden,
|
||||||
|
locked ? AppResources.VaultIsLocked : AppResources.GoToMyVault, Resource.Drawable.icon);
|
||||||
|
|
||||||
|
var datasetBuilder = new Dataset.Builder(view);
|
||||||
|
datasetBuilder.SetAuthentication(pendingIntent.IntentSender);
|
||||||
|
|
||||||
|
// Dataset must have a value set. We will reset this in the main activity when the real item is chosen.
|
||||||
|
foreach(var autofillId in fields.AutofillIds)
|
||||||
|
{
|
||||||
|
datasetBuilder.SetValue(autofillId, AutofillValue.ForText("PLACEHOLDER"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return datasetBuilder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RemoteViews BuildListView(string packageName, string text, string subtext, int iconId)
|
||||||
|
{
|
||||||
|
var view = new RemoteViews(packageName, Resource.Layout.autofill_listitem);
|
||||||
|
view.SetTextViewText(Resource.Id.text, text);
|
||||||
|
view.SetTextViewText(Resource.Id.text2, subtext);
|
||||||
|
view.SetImageViewResource(Resource.Id.icon, iconId);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddSaveInfo(FillResponse.Builder responseBuilder, FieldCollection fields)
|
||||||
|
{
|
||||||
|
var requiredIds = fields.GetRequiredSaveFields();
|
||||||
|
if(fields.SaveType == SaveDataType.Generic || requiredIds.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var saveBuilder = new SaveInfo.Builder(fields.SaveType, requiredIds);
|
||||||
|
var optionalIds = fields.GetOptionalSaveIds();
|
||||||
|
if(optionalIds.Length > 0)
|
||||||
|
{
|
||||||
|
saveBuilder.SetOptionalIds(optionalIds);
|
||||||
|
}
|
||||||
|
responseBuilder.SetSaveInfo(saveBuilder.Build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
113
src/Android/Autofill/AutofillService.cs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
using Android;
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.OS;
|
||||||
|
using Android.Runtime;
|
||||||
|
using Android.Service.Autofill;
|
||||||
|
using Android.Widget;
|
||||||
|
using Bit.App;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Enums;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using XLabs.Ioc;
|
||||||
|
|
||||||
|
namespace Bit.Android.Autofill
|
||||||
|
{
|
||||||
|
[Service(Permission = Manifest.Permission.BindAutofillService, Label = "bitwarden")]
|
||||||
|
[IntentFilter(new string[] { "android.service.autofill.AutofillService" })]
|
||||||
|
[MetaData("android.autofill", Resource = "@xml/autofillservice")]
|
||||||
|
[Register("com.x8bit.bitwarden.Autofill.AutofillService")]
|
||||||
|
public class AutofillService : global::Android.Service.Autofill.AutofillService
|
||||||
|
{
|
||||||
|
private ICipherService _cipherService;
|
||||||
|
private ILockService _lockService;
|
||||||
|
|
||||||
|
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback)
|
||||||
|
{
|
||||||
|
var structure = request.FillContexts?.LastOrDefault()?.Structure;
|
||||||
|
if(structure == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parser = new Parser(structure);
|
||||||
|
parser.Parse();
|
||||||
|
|
||||||
|
if(string.IsNullOrWhiteSpace(parser.Uri) || parser.Uri == "androidapp://com.x8bit.bitwarden" ||
|
||||||
|
parser.Uri == "androidapp://android" || !parser.FieldCollection.Fillable)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_lockService == null)
|
||||||
|
{
|
||||||
|
_lockService = Resolver.Resolve<ILockService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<FilledItem> items = null;
|
||||||
|
var locked = (await _lockService.GetLockTypeAsync(false)) != LockType.None;
|
||||||
|
if(!locked)
|
||||||
|
{
|
||||||
|
if(_cipherService == null)
|
||||||
|
{
|
||||||
|
_cipherService = Resolver.Resolve<ICipherService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
items = await AutofillHelpers.GetFillItemsAsync(parser, _cipherService);
|
||||||
|
}
|
||||||
|
|
||||||
|
// build response
|
||||||
|
var response = AutofillHelpers.BuildFillResponse(this, parser, items, locked);
|
||||||
|
callback.OnSuccess(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnSaveRequest(SaveRequest request, SaveCallback callback)
|
||||||
|
{
|
||||||
|
var structure = request.FillContexts?.LastOrDefault()?.Structure;
|
||||||
|
if(structure == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parser = new Parser(structure);
|
||||||
|
parser.Parse();
|
||||||
|
|
||||||
|
var savedItem = parser.FieldCollection.GetSavedItem();
|
||||||
|
if(savedItem == null)
|
||||||
|
{
|
||||||
|
Toast.MakeText(this, "Unable to save this form.", ToastLength.Short).Show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var intent = new Intent(this, typeof(MainActivity));
|
||||||
|
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTop);
|
||||||
|
intent.PutExtra("autofillFramework", true);
|
||||||
|
intent.PutExtra("autofillFrameworkSave", true);
|
||||||
|
intent.PutExtra("autofillFrameworkType", (int)savedItem.Type);
|
||||||
|
switch(savedItem.Type)
|
||||||
|
{
|
||||||
|
case CipherType.Login:
|
||||||
|
intent.PutExtra("autofillFrameworkName", parser.Uri
|
||||||
|
.Replace(Constants.AndroidAppProtocol, string.Empty)
|
||||||
|
.Replace("https://", string.Empty)
|
||||||
|
.Replace("http://", string.Empty));
|
||||||
|
intent.PutExtra("autofillFrameworkUri", parser.Uri);
|
||||||
|
intent.PutExtra("autofillFrameworkUsername", savedItem.Login.Username);
|
||||||
|
intent.PutExtra("autofillFrameworkPassword", savedItem.Login.Password);
|
||||||
|
break;
|
||||||
|
case CipherType.Card:
|
||||||
|
intent.PutExtra("autofillFrameworkCardName", savedItem.Card.Name);
|
||||||
|
intent.PutExtra("autofillFrameworkCardNumber", savedItem.Card.Number);
|
||||||
|
intent.PutExtra("autofillFrameworkCardExpMonth", savedItem.Card.ExpMonth);
|
||||||
|
intent.PutExtra("autofillFrameworkCardExpYear", savedItem.Card.ExpYear);
|
||||||
|
intent.PutExtra("autofillFrameworkCardCode", savedItem.Card.Code);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Toast.MakeText(this, "Unable to save this type of form.", ToastLength.Short).Show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StartActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
197
src/Android/Autofill/Field.cs
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Android.Service.Autofill;
|
||||||
|
using Android.Views;
|
||||||
|
using Android.Views.Autofill;
|
||||||
|
using static Android.App.Assist.AssistStructure;
|
||||||
|
using Android.Text;
|
||||||
|
using static Android.Views.ViewStructure;
|
||||||
|
|
||||||
|
namespace Bit.Android.Autofill
|
||||||
|
{
|
||||||
|
public class Field
|
||||||
|
{
|
||||||
|
private List<string> _hints;
|
||||||
|
|
||||||
|
public Field(ViewNode node)
|
||||||
|
{
|
||||||
|
Id = node.Id;
|
||||||
|
IdEntry = node.IdEntry;
|
||||||
|
AutofillId = node.AutofillId;
|
||||||
|
AutofillType = node.AutofillType;
|
||||||
|
InputType = node.InputType;
|
||||||
|
Focused = node.IsFocused;
|
||||||
|
Selected = node.IsSelected;
|
||||||
|
Clickable = node.IsClickable;
|
||||||
|
Visible = node.Visibility == ViewStates.Visible;
|
||||||
|
Hints = FilterForSupportedHints(node.GetAutofillHints());
|
||||||
|
Hint = node.Hint;
|
||||||
|
AutofillOptions = node.GetAutofillOptions()?.ToList();
|
||||||
|
HtmlInfo = node.HtmlInfo;
|
||||||
|
Node = node;
|
||||||
|
|
||||||
|
if(node.AutofillValue != null)
|
||||||
|
{
|
||||||
|
if(node.AutofillValue.IsList)
|
||||||
|
{
|
||||||
|
var autofillOptions = node.GetAutofillOptions();
|
||||||
|
if(autofillOptions != null && autofillOptions.Length > 0)
|
||||||
|
{
|
||||||
|
ListValue = node.AutofillValue.ListValue;
|
||||||
|
TextValue = autofillOptions[node.AutofillValue.ListValue];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(node.AutofillValue.IsDate)
|
||||||
|
{
|
||||||
|
DateValue = node.AutofillValue.DateValue;
|
||||||
|
}
|
||||||
|
else if(node.AutofillValue.IsText)
|
||||||
|
{
|
||||||
|
TextValue = node.AutofillValue.TextValue;
|
||||||
|
}
|
||||||
|
else if(node.AutofillValue.IsToggle)
|
||||||
|
{
|
||||||
|
ToggleValue = node.AutofillValue.ToggleValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SaveDataType SaveType { get; set; } = SaveDataType.Generic;
|
||||||
|
public List<string> Hints
|
||||||
|
{
|
||||||
|
get => _hints;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_hints = value;
|
||||||
|
UpdateSaveTypeFromHints();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string Hint { get; set; }
|
||||||
|
public int Id { get; private set; }
|
||||||
|
public string IdEntry { get; set; }
|
||||||
|
public AutofillId AutofillId { get; private set; }
|
||||||
|
public AutofillType AutofillType { get; private set; }
|
||||||
|
public InputTypes InputType { get; private set; }
|
||||||
|
public bool Focused { get; private set; }
|
||||||
|
public bool Selected { get; private set; }
|
||||||
|
public bool Clickable { get; private set; }
|
||||||
|
public bool Visible { get; private set; }
|
||||||
|
public List<string> AutofillOptions { get; set; }
|
||||||
|
public string TextValue { get; set; }
|
||||||
|
public long? DateValue { get; set; }
|
||||||
|
public int? ListValue { get; set; }
|
||||||
|
public bool? ToggleValue { get; set; }
|
||||||
|
public HtmlInfo HtmlInfo { get; private set; }
|
||||||
|
public ViewNode Node { get; private set; }
|
||||||
|
|
||||||
|
private void UpdateSaveTypeFromHints()
|
||||||
|
{
|
||||||
|
SaveType = SaveDataType.Generic;
|
||||||
|
if(_hints == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(var hint in _hints)
|
||||||
|
{
|
||||||
|
switch(hint)
|
||||||
|
{
|
||||||
|
case View.AutofillHintCreditCardExpirationDate:
|
||||||
|
case View.AutofillHintCreditCardExpirationDay:
|
||||||
|
case View.AutofillHintCreditCardExpirationMonth:
|
||||||
|
case View.AutofillHintCreditCardExpirationYear:
|
||||||
|
case View.AutofillHintCreditCardNumber:
|
||||||
|
case View.AutofillHintCreditCardSecurityCode:
|
||||||
|
SaveType |= SaveDataType.CreditCard;
|
||||||
|
break;
|
||||||
|
case View.AutofillHintEmailAddress:
|
||||||
|
SaveType |= SaveDataType.EmailAddress;
|
||||||
|
break;
|
||||||
|
case View.AutofillHintPhone:
|
||||||
|
case View.AutofillHintName:
|
||||||
|
SaveType |= SaveDataType.Generic;
|
||||||
|
break;
|
||||||
|
case View.AutofillHintPassword:
|
||||||
|
SaveType |= SaveDataType.Password;
|
||||||
|
SaveType &= ~SaveDataType.EmailAddress;
|
||||||
|
SaveType &= ~SaveDataType.Username;
|
||||||
|
break;
|
||||||
|
case View.AutofillHintPostalAddress:
|
||||||
|
case View.AutofillHintPostalCode:
|
||||||
|
SaveType |= SaveDataType.Address;
|
||||||
|
break;
|
||||||
|
case View.AutofillHintUsername:
|
||||||
|
SaveType |= SaveDataType.Username;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ValueIsNull()
|
||||||
|
{
|
||||||
|
return TextValue == null && DateValue == null && ToggleValue == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if(this == obj)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(obj == null || GetType() != obj.GetType())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var field = obj as Field;
|
||||||
|
if(TextValue != null ? !TextValue.Equals(field.TextValue) : field.TextValue != null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(DateValue != null ? !DateValue.Equals(field.DateValue) : field.DateValue != null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ToggleValue != null ? ToggleValue.Equals(field.ToggleValue) : field.ToggleValue == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
var result = TextValue != null ? TextValue.GetHashCode() : 0;
|
||||||
|
result = 31 * result + (DateValue != null ? DateValue.GetHashCode() : 0);
|
||||||
|
result = 31 * result + (ToggleValue != null ? ToggleValue.GetHashCode() : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> FilterForSupportedHints(string[] hints)
|
||||||
|
{
|
||||||
|
return hints?.Where(h => IsValidHint(h)).ToList() ?? new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsValidHint(string hint)
|
||||||
|
{
|
||||||
|
switch(hint)
|
||||||
|
{
|
||||||
|
case View.AutofillHintCreditCardExpirationDate:
|
||||||
|
case View.AutofillHintCreditCardExpirationDay:
|
||||||
|
case View.AutofillHintCreditCardExpirationMonth:
|
||||||
|
case View.AutofillHintCreditCardExpirationYear:
|
||||||
|
case View.AutofillHintCreditCardNumber:
|
||||||
|
case View.AutofillHintCreditCardSecurityCode:
|
||||||
|
case View.AutofillHintEmailAddress:
|
||||||
|
case View.AutofillHintPhone:
|
||||||
|
case View.AutofillHintName:
|
||||||
|
case View.AutofillHintPassword:
|
||||||
|
case View.AutofillHintPostalAddress:
|
||||||
|
case View.AutofillHintPostalCode:
|
||||||
|
case View.AutofillHintUsername:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
316
src/Android/Autofill/FieldCollection.cs
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Android.Service.Autofill;
|
||||||
|
using Android.Views.Autofill;
|
||||||
|
using System.Linq;
|
||||||
|
using Android.Text;
|
||||||
|
using Android.Views;
|
||||||
|
|
||||||
|
namespace Bit.Android.Autofill
|
||||||
|
{
|
||||||
|
public class FieldCollection
|
||||||
|
{
|
||||||
|
private List<Field> _passwordFields = null;
|
||||||
|
private List<Field> _usernameFields = null;
|
||||||
|
|
||||||
|
public HashSet<int> Ids { get; private set; } = new HashSet<int>();
|
||||||
|
public List<AutofillId> AutofillIds { get; private set; } = new List<AutofillId>();
|
||||||
|
public SaveDataType SaveType
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if(FillableForLogin)
|
||||||
|
{
|
||||||
|
return SaveDataType.Password;
|
||||||
|
}
|
||||||
|
else if(FillableForCard)
|
||||||
|
{
|
||||||
|
return SaveDataType.CreditCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SaveDataType.Generic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public HashSet<string> Hints { get; private set; } = new HashSet<string>();
|
||||||
|
public HashSet<string> FocusedHints { get; private set; } = new HashSet<string>();
|
||||||
|
public List<Field> Fields { get; private set; } = new List<Field>();
|
||||||
|
public IDictionary<int, Field> IdToFieldMap { get; private set; } =
|
||||||
|
new Dictionary<int, Field>();
|
||||||
|
public IDictionary<string, List<Field>> HintToFieldsMap { get; private set; } =
|
||||||
|
new Dictionary<string, List<Field>>();
|
||||||
|
public List<AutofillId> IgnoreAutofillIds { get; private set; } = new List<AutofillId>();
|
||||||
|
|
||||||
|
public List<Field> PasswordFields
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if(_passwordFields != null)
|
||||||
|
{
|
||||||
|
return _passwordFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Hints.Any())
|
||||||
|
{
|
||||||
|
_passwordFields = new List<Field>();
|
||||||
|
if(HintToFieldsMap.ContainsKey(View.AutofillHintPassword))
|
||||||
|
{
|
||||||
|
_passwordFields.AddRange(HintToFieldsMap[View.AutofillHintPassword]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordFields = Fields
|
||||||
|
.Where(f =>
|
||||||
|
(!f.IdEntry?.ToLowerInvariant().Contains("search") ?? true) &&
|
||||||
|
(!f.Hint?.ToLowerInvariant().Contains("search") ?? true) &&
|
||||||
|
(
|
||||||
|
f.InputType.HasFlag(InputTypes.TextVariationPassword) ||
|
||||||
|
f.InputType.HasFlag(InputTypes.TextVariationVisiblePassword) ||
|
||||||
|
f.InputType.HasFlag(InputTypes.TextVariationWebPassword)
|
||||||
|
)
|
||||||
|
).ToList();
|
||||||
|
if(!_passwordFields.Any())
|
||||||
|
{
|
||||||
|
_passwordFields = Fields.Where(f =>
|
||||||
|
(f.IdEntry?.ToLowerInvariant().Contains("password") ?? false)
|
||||||
|
|| (f.Hint?.ToLowerInvariant().Contains("password") ?? false)).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _passwordFields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Field> UsernameFields
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if(_usernameFields != null)
|
||||||
|
{
|
||||||
|
return _usernameFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
_usernameFields = new List<Field>();
|
||||||
|
if(Hints.Any())
|
||||||
|
{
|
||||||
|
if(HintToFieldsMap.ContainsKey(View.AutofillHintEmailAddress))
|
||||||
|
{
|
||||||
|
_usernameFields.AddRange(HintToFieldsMap[View.AutofillHintEmailAddress]);
|
||||||
|
}
|
||||||
|
if(HintToFieldsMap.ContainsKey(View.AutofillHintUsername))
|
||||||
|
{
|
||||||
|
_usernameFields.AddRange(HintToFieldsMap[View.AutofillHintUsername]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach(var passwordField in PasswordFields)
|
||||||
|
{
|
||||||
|
var usernameField = Fields.TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault();
|
||||||
|
if(usernameField != null)
|
||||||
|
{
|
||||||
|
_usernameFields.Add(usernameField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _usernameFields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FillableForLogin => FocusedHintsContain(
|
||||||
|
new string[] { View.AutofillHintUsername, View.AutofillHintEmailAddress, View.AutofillHintPassword }) ||
|
||||||
|
UsernameFields.Any(f => f.Focused) || PasswordFields.Any(f => f.Focused);
|
||||||
|
public bool FillableForCard => FocusedHintsContain(
|
||||||
|
new string[] { View.AutofillHintCreditCardNumber, View.AutofillHintCreditCardExpirationMonth,
|
||||||
|
View.AutofillHintCreditCardExpirationYear, View.AutofillHintCreditCardSecurityCode});
|
||||||
|
public bool FillableForIdentity => FocusedHintsContain(
|
||||||
|
new string[] { View.AutofillHintName, View.AutofillHintPhone, View.AutofillHintPostalAddress,
|
||||||
|
View.AutofillHintPostalCode });
|
||||||
|
|
||||||
|
public bool Fillable => FillableForLogin || FillableForCard || FillableForIdentity;
|
||||||
|
|
||||||
|
public void Add(Field field)
|
||||||
|
{
|
||||||
|
if(Ids.Contains(field.Id))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_passwordFields = _usernameFields = null;
|
||||||
|
|
||||||
|
if(field.Id > -1)
|
||||||
|
{
|
||||||
|
Ids.Add(field.Id);
|
||||||
|
IdToFieldMap.Add(field.Id, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
Fields.Add(field);
|
||||||
|
AutofillIds.Add(field.AutofillId);
|
||||||
|
|
||||||
|
if(field.Hints != null)
|
||||||
|
{
|
||||||
|
foreach(var hint in field.Hints)
|
||||||
|
{
|
||||||
|
Hints.Add(hint);
|
||||||
|
if(field.Focused)
|
||||||
|
{
|
||||||
|
FocusedHints.Add(hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!HintToFieldsMap.ContainsKey(hint))
|
||||||
|
{
|
||||||
|
HintToFieldsMap.Add(hint, new List<Field>());
|
||||||
|
}
|
||||||
|
|
||||||
|
HintToFieldsMap[hint].Add(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SavedItem GetSavedItem()
|
||||||
|
{
|
||||||
|
if(SaveType == SaveDataType.Password)
|
||||||
|
{
|
||||||
|
var passwordField = PasswordFields.FirstOrDefault(f => !string.IsNullOrWhiteSpace(f.TextValue));
|
||||||
|
if(passwordField == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var savedItem = new SavedItem
|
||||||
|
{
|
||||||
|
Type = App.Enums.CipherType.Login,
|
||||||
|
Login = new SavedItem.LoginItem
|
||||||
|
{
|
||||||
|
Password = GetFieldValue(passwordField)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var usernameField = Fields.TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault();
|
||||||
|
savedItem.Login.Username = GetFieldValue(usernameField);
|
||||||
|
|
||||||
|
return savedItem;
|
||||||
|
}
|
||||||
|
else if(SaveType == SaveDataType.CreditCard)
|
||||||
|
{
|
||||||
|
var savedItem = new SavedItem
|
||||||
|
{
|
||||||
|
Type = App.Enums.CipherType.Card,
|
||||||
|
Card = new SavedItem.CardItem
|
||||||
|
{
|
||||||
|
Number = GetFieldValue(View.AutofillHintCreditCardNumber),
|
||||||
|
Name = GetFieldValue(View.AutofillHintName),
|
||||||
|
ExpMonth = GetFieldValue(View.AutofillHintCreditCardExpirationMonth, true),
|
||||||
|
ExpYear = GetFieldValue(View.AutofillHintCreditCardExpirationYear),
|
||||||
|
Code = GetFieldValue(View.AutofillHintCreditCardSecurityCode)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return savedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AutofillId[] GetOptionalSaveIds()
|
||||||
|
{
|
||||||
|
if(SaveType == SaveDataType.Password)
|
||||||
|
{
|
||||||
|
return UsernameFields.Select(f => f.AutofillId).ToArray();
|
||||||
|
}
|
||||||
|
else if(SaveType == SaveDataType.CreditCard)
|
||||||
|
{
|
||||||
|
var fieldList = new List<Field>();
|
||||||
|
if(HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardSecurityCode))
|
||||||
|
{
|
||||||
|
fieldList.AddRange(HintToFieldsMap[View.AutofillHintCreditCardSecurityCode]);
|
||||||
|
}
|
||||||
|
if(HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardExpirationYear))
|
||||||
|
{
|
||||||
|
fieldList.AddRange(HintToFieldsMap[View.AutofillHintCreditCardExpirationYear]);
|
||||||
|
}
|
||||||
|
if(HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardExpirationMonth))
|
||||||
|
{
|
||||||
|
fieldList.AddRange(HintToFieldsMap[View.AutofillHintCreditCardExpirationMonth]);
|
||||||
|
}
|
||||||
|
if(HintToFieldsMap.ContainsKey(View.AutofillHintName))
|
||||||
|
{
|
||||||
|
fieldList.AddRange(HintToFieldsMap[View.AutofillHintName]);
|
||||||
|
}
|
||||||
|
return fieldList.Select(f => f.AutofillId).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AutofillId[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public AutofillId[] GetRequiredSaveFields()
|
||||||
|
{
|
||||||
|
if(SaveType == SaveDataType.Password)
|
||||||
|
{
|
||||||
|
return PasswordFields.Select(f => f.AutofillId).ToArray();
|
||||||
|
}
|
||||||
|
else if(SaveType == SaveDataType.CreditCard && HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardNumber))
|
||||||
|
{
|
||||||
|
return HintToFieldsMap[View.AutofillHintCreditCardNumber].Select(f => f.AutofillId).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AutofillId[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool FocusedHintsContain(IEnumerable<string> hints)
|
||||||
|
{
|
||||||
|
return hints.Any(h => FocusedHints.Contains(h));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetFieldValue(string hint, bool monthValue = false)
|
||||||
|
{
|
||||||
|
if(HintToFieldsMap.ContainsKey(hint))
|
||||||
|
{
|
||||||
|
foreach(var field in HintToFieldsMap[hint])
|
||||||
|
{
|
||||||
|
var val = GetFieldValue(field, monthValue);
|
||||||
|
if(!string.IsNullOrWhiteSpace(val))
|
||||||
|
{
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetFieldValue(Field field, bool monthValue = false)
|
||||||
|
{
|
||||||
|
if(field == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!string.IsNullOrWhiteSpace(field.TextValue))
|
||||||
|
{
|
||||||
|
if(field.AutofillType == AutofillType.List && field.ListValue.HasValue && monthValue)
|
||||||
|
{
|
||||||
|
if(field.AutofillOptions.Count == 13)
|
||||||
|
{
|
||||||
|
return field.ListValue.ToString();
|
||||||
|
}
|
||||||
|
else if(field.AutofillOptions.Count == 12)
|
||||||
|
{
|
||||||
|
return (field.ListValue + 1).ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return field.TextValue;
|
||||||
|
}
|
||||||
|
else if(field.DateValue.HasValue)
|
||||||
|
{
|
||||||
|
return field.DateValue.Value.ToString();
|
||||||
|
}
|
||||||
|
else if(field.ToggleValue.HasValue)
|
||||||
|
{
|
||||||
|
return field.ToggleValue.Value.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
273
src/Android/Autofill/FilledItem.cs
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
using System;
|
||||||
|
using Android.Service.Autofill;
|
||||||
|
using Android.Views.Autofill;
|
||||||
|
using System.Linq;
|
||||||
|
using Bit.App.Models;
|
||||||
|
using Bit.App.Enums;
|
||||||
|
using Android.Views;
|
||||||
|
|
||||||
|
namespace Bit.Android.Autofill
|
||||||
|
{
|
||||||
|
public class FilledItem
|
||||||
|
{
|
||||||
|
private Lazy<string> _password;
|
||||||
|
private Lazy<string> _cardName;
|
||||||
|
private string _cardNumber;
|
||||||
|
private Lazy<string> _cardExpMonth;
|
||||||
|
private Lazy<string> _cardExpYear;
|
||||||
|
private Lazy<string> _cardCode;
|
||||||
|
private Lazy<string> _idPhone;
|
||||||
|
private Lazy<string> _idEmail;
|
||||||
|
private Lazy<string> _idUsername;
|
||||||
|
private Lazy<string> _idAddress;
|
||||||
|
private Lazy<string> _idPostalCode;
|
||||||
|
|
||||||
|
public FilledItem(Cipher cipher)
|
||||||
|
{
|
||||||
|
Name = cipher.Name?.Decrypt(cipher.OrganizationId) ?? "--";
|
||||||
|
Type = cipher.Type;
|
||||||
|
|
||||||
|
switch(Type)
|
||||||
|
{
|
||||||
|
case CipherType.Login:
|
||||||
|
Subtitle = cipher.Login.Username?.Decrypt(cipher.OrganizationId) ?? string.Empty;
|
||||||
|
Icon = Resource.Drawable.login;
|
||||||
|
_password = new Lazy<string>(() => cipher.Login.Password?.Decrypt(cipher.OrganizationId));
|
||||||
|
break;
|
||||||
|
case CipherType.Card:
|
||||||
|
Subtitle = cipher.Card.Brand?.Decrypt(cipher.OrganizationId);
|
||||||
|
_cardNumber = cipher.Card.Number?.Decrypt(cipher.OrganizationId);
|
||||||
|
if(!string.IsNullOrWhiteSpace(_cardNumber) && _cardNumber.Length >= 4)
|
||||||
|
{
|
||||||
|
if(!string.IsNullOrWhiteSpace(_cardNumber))
|
||||||
|
{
|
||||||
|
Subtitle += ", ";
|
||||||
|
}
|
||||||
|
Subtitle += ("*" + _cardNumber.Substring(_cardNumber.Length - 4));
|
||||||
|
}
|
||||||
|
Icon = Resource.Drawable.card;
|
||||||
|
_cardName = new Lazy<string>(() => cipher.Card.CardholderName?.Decrypt(cipher.OrganizationId));
|
||||||
|
_cardCode = new Lazy<string>(() => cipher.Card.Code?.Decrypt(cipher.OrganizationId));
|
||||||
|
_cardExpMonth = new Lazy<string>(() => cipher.Card.ExpMonth?.Decrypt(cipher.OrganizationId));
|
||||||
|
_cardExpYear = new Lazy<string>(() => cipher.Card.ExpYear?.Decrypt(cipher.OrganizationId));
|
||||||
|
break;
|
||||||
|
case CipherType.Identity:
|
||||||
|
var firstName = cipher.Identity?.FirstName?.Decrypt(cipher.OrganizationId) ?? " ";
|
||||||
|
var lastName = cipher.Identity?.LastName?.Decrypt(cipher.OrganizationId) ?? " ";
|
||||||
|
Subtitle = " ";
|
||||||
|
if(!string.IsNullOrWhiteSpace(firstName))
|
||||||
|
{
|
||||||
|
Subtitle = firstName;
|
||||||
|
}
|
||||||
|
if(!string.IsNullOrWhiteSpace(lastName))
|
||||||
|
{
|
||||||
|
if(!string.IsNullOrWhiteSpace(Subtitle))
|
||||||
|
{
|
||||||
|
Subtitle += " ";
|
||||||
|
}
|
||||||
|
Subtitle += lastName;
|
||||||
|
}
|
||||||
|
Icon = Resource.Drawable.id;
|
||||||
|
_idPhone = new Lazy<string>(() => cipher.Identity.Phone?.Decrypt(cipher.OrganizationId));
|
||||||
|
_idEmail = new Lazy<string>(() => cipher.Identity.Email?.Decrypt(cipher.OrganizationId));
|
||||||
|
_idUsername = new Lazy<string>(() => cipher.Identity.Username?.Decrypt(cipher.OrganizationId));
|
||||||
|
_idAddress = new Lazy<string>(() =>
|
||||||
|
{
|
||||||
|
var address = cipher.Identity.Address1?.Decrypt(cipher.OrganizationId);
|
||||||
|
|
||||||
|
var address2 = cipher.Identity.Address2?.Decrypt(cipher.OrganizationId);
|
||||||
|
if(!string.IsNullOrWhiteSpace(address2))
|
||||||
|
{
|
||||||
|
if(!string.IsNullOrWhiteSpace(address))
|
||||||
|
{
|
||||||
|
address += ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
address += address2;
|
||||||
|
}
|
||||||
|
|
||||||
|
var address3 = cipher.Identity.Address3?.Decrypt(cipher.OrganizationId);
|
||||||
|
if(!string.IsNullOrWhiteSpace(address3))
|
||||||
|
{
|
||||||
|
if(!string.IsNullOrWhiteSpace(address))
|
||||||
|
{
|
||||||
|
address += ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
address += address3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return address;
|
||||||
|
});
|
||||||
|
_idPostalCode = new Lazy<string>(() => cipher.Identity.PostalCode?.Decrypt(cipher.OrganizationId));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Subtitle { get; set; } = string.Empty;
|
||||||
|
public int Icon { get; set; } = Resource.Drawable.login;
|
||||||
|
public CipherType Type { get; set; }
|
||||||
|
|
||||||
|
public bool ApplyToFields(FieldCollection fieldCollection, Dataset.Builder datasetBuilder)
|
||||||
|
{
|
||||||
|
if(!fieldCollection?.Fields.Any() ?? true)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var setValues = false;
|
||||||
|
if(Type == CipherType.Login)
|
||||||
|
{
|
||||||
|
if(fieldCollection.PasswordFields.Any() && !string.IsNullOrWhiteSpace(_password.Value))
|
||||||
|
{
|
||||||
|
foreach(var f in fieldCollection.PasswordFields)
|
||||||
|
{
|
||||||
|
var val = ApplyValue(f, _password.Value);
|
||||||
|
if(val != null)
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
datasetBuilder.SetValue(f.AutofillId, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fieldCollection.UsernameFields.Any() && !string.IsNullOrWhiteSpace(Subtitle))
|
||||||
|
{
|
||||||
|
foreach(var f in fieldCollection.UsernameFields)
|
||||||
|
{
|
||||||
|
var val = ApplyValue(f, Subtitle);
|
||||||
|
if(val != null)
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
datasetBuilder.SetValue(f.AutofillId, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(Type == CipherType.Card)
|
||||||
|
{
|
||||||
|
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintCreditCardNumber,
|
||||||
|
new Lazy<string>(() => _cardNumber)))
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
}
|
||||||
|
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintCreditCardSecurityCode, _cardCode))
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
}
|
||||||
|
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintCreditCardExpirationMonth, _cardExpMonth, true))
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
}
|
||||||
|
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintCreditCardExpirationYear, _cardExpYear))
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
}
|
||||||
|
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintName, _cardName))
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(Type == CipherType.Identity)
|
||||||
|
{
|
||||||
|
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintPhone, _idPhone))
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
}
|
||||||
|
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintEmailAddress, _idEmail))
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
}
|
||||||
|
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintUsername, _idUsername))
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
}
|
||||||
|
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintPostalAddress, _idAddress))
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
}
|
||||||
|
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintPostalCode, _idPostalCode))
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
}
|
||||||
|
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintName, new Lazy<string>(() => Subtitle)))
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return setValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ApplyValue(Dataset.Builder builder, FieldCollection fieldCollection,
|
||||||
|
string hint, Lazy<string> value, bool monthValue = false)
|
||||||
|
{
|
||||||
|
bool setValues = false;
|
||||||
|
if(fieldCollection.HintToFieldsMap.ContainsKey(hint) && !string.IsNullOrWhiteSpace(value.Value))
|
||||||
|
{
|
||||||
|
foreach(var f in fieldCollection.HintToFieldsMap[hint])
|
||||||
|
{
|
||||||
|
var val = ApplyValue(f, value.Value, monthValue);
|
||||||
|
if(val != null)
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
builder.SetValue(f.AutofillId, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return setValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AutofillValue ApplyValue(Field field, string value, bool monthValue = false)
|
||||||
|
{
|
||||||
|
switch(field.AutofillType)
|
||||||
|
{
|
||||||
|
case AutofillType.Date:
|
||||||
|
if(long.TryParse(value, out long dateValue))
|
||||||
|
{
|
||||||
|
return AutofillValue.ForDate(dateValue);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AutofillType.List:
|
||||||
|
if(field.AutofillOptions != null)
|
||||||
|
{
|
||||||
|
if(monthValue && int.TryParse(value, out int monthIndex))
|
||||||
|
{
|
||||||
|
if(field.AutofillOptions.Count == 13)
|
||||||
|
{
|
||||||
|
return AutofillValue.ForList(monthIndex);
|
||||||
|
}
|
||||||
|
else if(field.AutofillOptions.Count >= monthIndex)
|
||||||
|
{
|
||||||
|
return AutofillValue.ForList(monthIndex - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i = 0; i < field.AutofillOptions.Count; i++)
|
||||||
|
{
|
||||||
|
if(field.AutofillOptions[i].Equals(value))
|
||||||
|
{
|
||||||
|
return AutofillValue.ForList(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AutofillType.Text:
|
||||||
|
return AutofillValue.ForText(value);
|
||||||
|
case AutofillType.Toggle:
|
||||||
|
if(bool.TryParse(value, out bool toggleValue))
|
||||||
|
{
|
||||||
|
return AutofillValue.ForToggle(toggleValue);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
125
src/Android/Autofill/Parser.cs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
using static Android.App.Assist.AssistStructure;
|
||||||
|
using Android.App.Assist;
|
||||||
|
using Bit.App;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Bit.Android.Autofill
|
||||||
|
{
|
||||||
|
public class Parser
|
||||||
|
{
|
||||||
|
public static HashSet<string> TrustedBrowsers = new HashSet<string>
|
||||||
|
{
|
||||||
|
"org.mozilla.focus","org.mozilla.firefox","org.mozilla.firefox_beta","com.microsoft.emmx",
|
||||||
|
"com.android.chrome","com.chrome.beta","com.android.browser","com.brave.browser","com.opera.browser",
|
||||||
|
"com.opera.browser.beta","com.opera.mini.native","com.chrome.dev","com.chrome.canary",
|
||||||
|
"com.google.android.apps.chrome","com.google.android.apps.chrome_dev","com.yandex.browser",
|
||||||
|
"com.sec.android.app.sbrowser","com.sec.android.app.sbrowser.beta","org.codeaurora.swe.browser",
|
||||||
|
"com.amazon.cloud9"
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly AssistStructure _structure;
|
||||||
|
private string _uri;
|
||||||
|
private string _packageName;
|
||||||
|
private string _webDomain;
|
||||||
|
|
||||||
|
public Parser(AssistStructure structure)
|
||||||
|
{
|
||||||
|
_structure = structure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldCollection FieldCollection { get; private set; } = new FieldCollection();
|
||||||
|
public string Uri
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if(!string.IsNullOrWhiteSpace(_uri))
|
||||||
|
{
|
||||||
|
return _uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(string.IsNullOrWhiteSpace(WebDomain) && string.IsNullOrWhiteSpace(PackageName))
|
||||||
|
{
|
||||||
|
_uri = null;
|
||||||
|
}
|
||||||
|
else if(!string.IsNullOrWhiteSpace(WebDomain))
|
||||||
|
{
|
||||||
|
_uri = string.Concat("http://", WebDomain);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_uri = string.Concat(Constants.AndroidAppProtocol, PackageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string PackageName
|
||||||
|
{
|
||||||
|
get => _packageName;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
_packageName = _uri = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_packageName = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string WebDomain
|
||||||
|
{
|
||||||
|
get => _webDomain;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
_webDomain = _uri = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_webDomain = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Parse()
|
||||||
|
{
|
||||||
|
for(var i = 0; i < _structure.WindowNodeCount; i++)
|
||||||
|
{
|
||||||
|
var node = _structure.GetWindowNodeAt(i);
|
||||||
|
ParseNode(node.RootViewNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseNode(ViewNode node)
|
||||||
|
{
|
||||||
|
var hints = node.GetAutofillHints();
|
||||||
|
var isEditText = node.ClassName == "android.widget.EditText" || node?.HtmlInfo?.Tag == "input";
|
||||||
|
if(isEditText || (hints?.Length ?? 0) > 0)
|
||||||
|
{
|
||||||
|
if(PackageName == null)
|
||||||
|
{
|
||||||
|
PackageName = node.IdPackage;
|
||||||
|
}
|
||||||
|
if(WebDomain == null && TrustedBrowsers.Contains(node.IdPackage))
|
||||||
|
{
|
||||||
|
WebDomain = node.WebDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldCollection.Add(new Field(node));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(WebDomain == null && TrustedBrowsers.Contains(node.IdPackage))
|
||||||
|
{
|
||||||
|
WebDomain = node.WebDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldCollection.IgnoreAutofillIds.Add(node.AutofillId);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i = 0; i < node.ChildCount; i++)
|
||||||
|
{
|
||||||
|
ParseNode(node.GetChildAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/Android/Autofill/SavedItem.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using Bit.App.Enums;
|
||||||
|
|
||||||
|
namespace Bit.Android.Autofill
|
||||||
|
{
|
||||||
|
public class SavedItem
|
||||||
|
{
|
||||||
|
public CipherType Type { get; set; }
|
||||||
|
public LoginItem Login { get; set; }
|
||||||
|
public CardItem Card { get; set; }
|
||||||
|
|
||||||
|
public class LoginItem
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CardItem
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Number { get; set; }
|
||||||
|
public string ExpMonth { get; set; }
|
||||||
|
public string ExpYear { get; set; }
|
||||||
|
public string Code { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ using Android.OS;
|
|||||||
using Android.Views.Accessibility;
|
using Android.Views.Accessibility;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using XLabs.Ioc;
|
using XLabs.Ioc;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
|
||||||
namespace Bit.Android
|
namespace Bit.Android
|
||||||
{
|
{
|
||||||
@@ -16,6 +17,8 @@ namespace Bit.Android
|
|||||||
[MetaData("android.accessibilityservice", Resource = "@xml/accessibilityservice")]
|
[MetaData("android.accessibilityservice", Resource = "@xml/accessibilityservice")]
|
||||||
public class AutofillService : AccessibilityService
|
public class AutofillService : AccessibilityService
|
||||||
{
|
{
|
||||||
|
private NotificationChannel _notificationChannel;
|
||||||
|
|
||||||
private const int AutoFillNotificationId = 34573;
|
private const int AutoFillNotificationId = 34573;
|
||||||
private const string SystemUiPackage = "com.android.systemui";
|
private const string SystemUiPackage = "com.android.systemui";
|
||||||
private const string BitwardenPackage = "com.x8bit.bitwarden";
|
private const string BitwardenPackage = "com.x8bit.bitwarden";
|
||||||
@@ -344,12 +347,12 @@ namespace Bit.Android
|
|||||||
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.UpdateCurrent);
|
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.UpdateCurrent);
|
||||||
|
|
||||||
var notificationContent = Build.VERSION.SdkInt > BuildVersionCodes.KitkatWatch ?
|
var notificationContent = Build.VERSION.SdkInt > BuildVersionCodes.KitkatWatch ?
|
||||||
App.Resources.AppResources.BitwardenAutofillServiceNotificationContent :
|
AppResources.BitwardenAutofillServiceNotificationContent :
|
||||||
App.Resources.AppResources.BitwardenAutofillServiceNotificationContentOld;
|
AppResources.BitwardenAutofillServiceNotificationContentOld;
|
||||||
|
|
||||||
var builder = new Notification.Builder(this);
|
var builder = new Notification.Builder(this);
|
||||||
builder.SetSmallIcon(Resource.Drawable.notification_sm)
|
builder.SetSmallIcon(Resource.Drawable.notification_sm)
|
||||||
.SetContentTitle(App.Resources.AppResources.BitwardenAutofillService)
|
.SetContentTitle(AppResources.BitwardenAutofillService)
|
||||||
.SetContentText(notificationContent)
|
.SetContentText(notificationContent)
|
||||||
.SetTicker(notificationContent)
|
.SetTicker(notificationContent)
|
||||||
.SetWhen(now)
|
.SetWhen(now)
|
||||||
@@ -362,6 +365,17 @@ namespace Bit.Android
|
|||||||
Resource.Color.primary));
|
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 && */_appSettings.AutofillPersistNotification)
|
if(/*Build.VERSION.SdkInt <= BuildVersionCodes.N && */_appSettings.AutofillPersistNotification)
|
||||||
{
|
{
|
||||||
builder.SetPriority(-2);
|
builder.SetPriority(-2);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using Android.Content;
|
||||||
using Bit.Android.Controls;
|
using Bit.Android.Controls;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
@@ -9,6 +10,10 @@ namespace Bit.Android.Controls
|
|||||||
{
|
{
|
||||||
public class CustomButtonRenderer : ButtonRenderer
|
public class CustomButtonRenderer : ButtonRenderer
|
||||||
{
|
{
|
||||||
|
public CustomButtonRenderer(Context context)
|
||||||
|
: base(context)
|
||||||
|
{ }
|
||||||
|
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
|
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
@@ -17,7 +22,9 @@ namespace Bit.Android.Controls
|
|||||||
Control.TextSize = (float)Device.GetNamedSize(NamedSize.Medium, typeof(Button));
|
Control.TextSize = (float)Device.GetNamedSize(NamedSize.Medium, typeof(Button));
|
||||||
}
|
}
|
||||||
|
|
||||||
Control.FilterTouchesWhenObscured = true;
|
// This will prevent all screen overlay apps from being able to interact with buttons.
|
||||||
|
// Ex: apps that change the screen color for "night mode"
|
||||||
|
// Control.FilterTouchesWhenObscured = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
|||||||
21
src/Android/Controls/CustomLabelRenderer.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using Android.Content;
|
||||||
|
using Bit.Android.Controls;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(typeof(Label), typeof(CustomLabelRenderer))]
|
||||||
|
namespace Bit.Android.Controls
|
||||||
|
{
|
||||||
|
public class CustomLabelRenderer : LabelRenderer
|
||||||
|
{
|
||||||
|
public CustomLabelRenderer(Context context)
|
||||||
|
: base(context)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
Control.SetMaxLines(int.MaxValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using Android.Content;
|
||||||
using Bit.Android.Controls;
|
using Bit.Android.Controls;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
@@ -9,6 +10,10 @@ namespace Bit.Android.Controls
|
|||||||
{
|
{
|
||||||
public class CustomSearchBarRenderer : SearchBarRenderer
|
public class CustomSearchBarRenderer : SearchBarRenderer
|
||||||
{
|
{
|
||||||
|
public CustomSearchBarRenderer(Context context)
|
||||||
|
: base(context)
|
||||||
|
{ }
|
||||||
|
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
|
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
|
|||||||
27
src/Android/Controls/CustomSliderRenderer.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using Bit.Android.Controls;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Android.Content;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
using Android.Support.V4.Content.Res;
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(typeof(Slider), typeof(CustomSliderRenderer))]
|
||||||
|
namespace Bit.Android.Controls
|
||||||
|
{
|
||||||
|
public class CustomSliderRenderer : SliderRenderer
|
||||||
|
{
|
||||||
|
public CustomSliderRenderer(Context context)
|
||||||
|
: base(context)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
if(Control != null)
|
||||||
|
{
|
||||||
|
var thumb = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.slider_thumb, null);
|
||||||
|
Control.SetThumb(thumb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using Android.Content;
|
||||||
using Bit.Android.Controls;
|
using Bit.Android.Controls;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
@@ -10,6 +11,10 @@ namespace Bit.Android.Controls
|
|||||||
{
|
{
|
||||||
public class ExtendedButtonRenderer : CustomButtonRenderer
|
public class ExtendedButtonRenderer : CustomButtonRenderer
|
||||||
{
|
{
|
||||||
|
public ExtendedButtonRenderer(Context context)
|
||||||
|
: base(context)
|
||||||
|
{ }
|
||||||
|
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
|
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
|
|||||||
@@ -6,12 +6,17 @@ using Xamarin.Forms;
|
|||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
using Android.Text.Method;
|
using Android.Text.Method;
|
||||||
using Android.Views;
|
using Android.Views;
|
||||||
|
using Android.Content;
|
||||||
|
|
||||||
[assembly: ExportRenderer(typeof(ExtendedEditor), typeof(ExtendedEditorRenderer))]
|
[assembly: ExportRenderer(typeof(ExtendedEditor), typeof(ExtendedEditorRenderer))]
|
||||||
namespace Bit.Android.Controls
|
namespace Bit.Android.Controls
|
||||||
{
|
{
|
||||||
public class ExtendedEditorRenderer : EditorRenderer
|
public class ExtendedEditorRenderer : EditorRenderer
|
||||||
{
|
{
|
||||||
|
public ExtendedEditorRenderer(Context context)
|
||||||
|
: base(context)
|
||||||
|
{ }
|
||||||
|
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
|
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using Android.Content;
|
||||||
using Android.Graphics;
|
using Android.Graphics;
|
||||||
using Android.Text;
|
using Android.Text;
|
||||||
using Android.Text.Method;
|
using Android.Text.Method;
|
||||||
@@ -8,6 +9,7 @@ using Android.Widget;
|
|||||||
using Bit.Android.Controls;
|
using Bit.Android.Controls;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
using Bit.App.Enums;
|
using Bit.App.Enums;
|
||||||
|
using Plugin.CurrentActivity;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
@@ -16,6 +18,10 @@ namespace Bit.Android.Controls
|
|||||||
{
|
{
|
||||||
public class ExtendedEntryRenderer : EntryRenderer
|
public class ExtendedEntryRenderer : EntryRenderer
|
||||||
{
|
{
|
||||||
|
public ExtendedEntryRenderer(Context context)
|
||||||
|
: base(context)
|
||||||
|
{ }
|
||||||
|
|
||||||
private bool _isPassword;
|
private bool _isPassword;
|
||||||
private bool _toggledPassword;
|
private bool _toggledPassword;
|
||||||
private bool _isDisposed;
|
private bool _isDisposed;
|
||||||
@@ -66,6 +72,11 @@ namespace Bit.Android.Controls
|
|||||||
{
|
{
|
||||||
Control.Typeface = Typeface.Monospace;
|
Control.Typeface = Typeface.Monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(_view.HideCursor)
|
||||||
|
{
|
||||||
|
Control.SetCursorVisible(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToggleIsPassword(object sender, EventArgs e)
|
private void ToggleIsPassword(object sender, EventArgs e)
|
||||||
@@ -91,7 +102,7 @@ namespace Bit.Android.Controls
|
|||||||
}
|
}
|
||||||
|
|
||||||
// show keyboard
|
// show keyboard
|
||||||
var imm = Forms.Context.GetSystemService(global::Android.Content.Context.InputMethodService) as InputMethodManager;
|
var imm = CrossCurrentActivity.Current.Activity.GetSystemService(Context.InputMethodService) as InputMethodManager;
|
||||||
imm.ShowSoftInput(Control, ShowFlags.Forced);
|
imm.ShowSoftInput(Control, ShowFlags.Forced);
|
||||||
imm.ToggleSoftInput(ShowFlags.Forced, HideSoftInputFlags.ImplicitOnly);
|
imm.ToggleSoftInput(ShowFlags.Forced, HideSoftInputFlags.ImplicitOnly);
|
||||||
|
|
||||||
|
|||||||
33
src/Android/Controls/ExtendedListViewRenderer.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using Bit.Android.Controls;
|
||||||
|
using Bit.App.Controls;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Views;
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(typeof(ExtendedListView), typeof(ExtendedListViewRenderer))]
|
||||||
|
namespace Bit.Android.Controls
|
||||||
|
{
|
||||||
|
public class ExtendedListViewRenderer : ListViewRenderer
|
||||||
|
{
|
||||||
|
public ExtendedListViewRenderer(Context context)
|
||||||
|
: base(context)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
|
||||||
|
if(e.NewElement is ExtendedListView listView)
|
||||||
|
{
|
||||||
|
if(listView.BottomPadding > 0)
|
||||||
|
{
|
||||||
|
Control.SetPadding(0, 0, 0, listView.BottomPadding);
|
||||||
|
Control.SetClipToPadding(false);
|
||||||
|
Control.ScrollBarStyle = ScrollbarStyles.OutsideOverlay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using Android.Content;
|
||||||
using Bit.Android.Controls;
|
using Bit.Android.Controls;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
@@ -10,6 +11,10 @@ namespace Bit.Android.Controls
|
|||||||
{
|
{
|
||||||
public class ExtendedPickerRenderer : PickerRenderer
|
public class ExtendedPickerRenderer : PickerRenderer
|
||||||
{
|
{
|
||||||
|
public ExtendedPickerRenderer(Context context)
|
||||||
|
: base(context)
|
||||||
|
{ }
|
||||||
|
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
|
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
|
|||||||
@@ -2,88 +2,434 @@
|
|||||||
using Bit.Android.Controls;
|
using Bit.Android.Controls;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
using Com.Ittianyu.Bottomnavigationviewex;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
using Android.Support.Design.Widget;
|
using RelativeLayout = Android.Widget.RelativeLayout;
|
||||||
using Xamarin.Forms.Platform.Android.AppCompat;
|
using Platform = Xamarin.Forms.Platform.Android.Platform;
|
||||||
using System.Reflection;
|
using Android.Content;
|
||||||
|
using Android.Views;
|
||||||
|
using Android.Widget;
|
||||||
|
using Android.Support.Design.Internal;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using Android.Support.Design.Widget;
|
||||||
|
|
||||||
[assembly: ExportRenderer(typeof(ExtendedTabbedPage), typeof(ExtendedTabbedPageRenderer))]
|
[assembly: ExportRenderer(typeof(ExtendedTabbedPage), typeof(ExtendedTabbedPageRenderer))]
|
||||||
namespace Bit.Android.Controls
|
namespace Bit.Android.Controls
|
||||||
{
|
{
|
||||||
public class ExtendedTabbedPageRenderer : TabbedPageRenderer, TabLayout.IOnTabSelectedListener
|
public class ExtendedTabbedPageRenderer : VisualElementRenderer<ExtendedTabbedPage>,
|
||||||
|
BottomNavigationView.IOnNavigationItemSelectedListener
|
||||||
{
|
{
|
||||||
private TabLayout _tabLayout;
|
public static bool ShouldUpdateSelectedIcon;
|
||||||
|
public static Action<IMenuItem, FileImageSource, bool> MenuItemIconSetter;
|
||||||
|
public static float? BottomBarHeight = 50;
|
||||||
|
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
|
private RelativeLayout _rootLayout;
|
||||||
|
private FrameLayout _pageContainer;
|
||||||
|
private BottomNavigationViewEx _bottomNav;
|
||||||
|
private readonly int _barId;
|
||||||
|
|
||||||
|
public static global::Android.Graphics.Color? BackgroundColor;
|
||||||
|
|
||||||
|
public ExtendedTabbedPageRenderer(Context context)
|
||||||
|
: base(context)
|
||||||
|
{
|
||||||
|
AutoPackage = false;
|
||||||
|
_barId = GenerateViewId();
|
||||||
|
}
|
||||||
|
|
||||||
|
IPageController TabbedController => Element as IPageController;
|
||||||
|
public int LastSelectedIndex { get; internal set; }
|
||||||
|
|
||||||
|
public bool OnNavigationItemSelected(IMenuItem item)
|
||||||
|
{
|
||||||
|
this.SwitchPage(item);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SetupTabItems()
|
||||||
|
{
|
||||||
|
this.SetupTabItems(_bottomNav);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SetupBottomBar()
|
||||||
|
{
|
||||||
|
_bottomNav = this.SetupBottomBar(_rootLayout, _bottomNav, _barId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly Action<IMenuItem, FileImageSource, bool> DefaultMenuItemIconSetter = (menuItem, icon, selected) =>
|
||||||
|
{
|
||||||
|
var tabIconId = ResourceUtils.IdFromTitle(icon, ResourceManager.DrawableClass);
|
||||||
|
menuItem.SetIcon(tabIconId);
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<ExtendedTabbedPage> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
var view = (ExtendedTabbedPage)Element;
|
|
||||||
|
|
||||||
var field = typeof(ExtendedTabbedPageRenderer).BaseType.GetField("_tabLayout",
|
if(e.OldElement != null)
|
||||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
|
||||||
_tabLayout = field?.GetValue(this) as TabLayout;
|
|
||||||
if(_tabLayout != null)
|
|
||||||
{
|
{
|
||||||
var tab = _tabLayout.GetTabAt(0);
|
e.OldElement.ChildAdded -= PagesChanged;
|
||||||
SetSelectedTabIcon(tab, Element.Children[0].Icon, true);
|
e.OldElement.ChildRemoved -= PagesChanged;
|
||||||
|
e.OldElement.ChildrenReordered -= PagesChanged;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void TabLayout.IOnTabSelectedListener.OnTabSelected(TabLayout.Tab tab)
|
if(e.NewElement == null)
|
||||||
{
|
|
||||||
if(Element == null)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var selectedIndex = tab.Position;
|
UpdateIgnoreContainerAreas();
|
||||||
var child = Element.Children[selectedIndex];
|
|
||||||
if(Element.Children.Count > selectedIndex && selectedIndex >= 0)
|
if(_rootLayout == null)
|
||||||
{
|
{
|
||||||
Element.CurrentPage = Element.Children[selectedIndex];
|
SetupNativeView();
|
||||||
}
|
}
|
||||||
|
|
||||||
SetSelectedTabIcon(tab, child.Icon, true);
|
this.HandlePagesChanged();
|
||||||
|
SwitchContent(Element.CurrentPage);
|
||||||
|
|
||||||
|
Element.ChildAdded += PagesChanged;
|
||||||
|
Element.ChildRemoved += PagesChanged;
|
||||||
|
Element.ChildrenReordered += PagesChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TabLayout.IOnTabSelectedListener.OnTabUnselected(TabLayout.Tab tab)
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
var child = Element.Children[tab.Position];
|
base.OnElementPropertyChanged(sender, e);
|
||||||
SetSelectedTabIcon(tab, child.Icon, false);
|
if(e.PropertyName == nameof(TabbedPage.CurrentPage))
|
||||||
|
{
|
||||||
|
SwitchContent(Element.CurrentPage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetSelectedTabIcon(TabLayout.Tab tab, string icon, bool selected)
|
void PagesChanged(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrEmpty(icon))
|
this.HandlePagesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnAttachedToWindow()
|
||||||
|
{
|
||||||
|
base.OnAttachedToWindow();
|
||||||
|
TabbedController?.SendAppearing();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDetachedFromWindow()
|
||||||
|
{
|
||||||
|
base.OnDetachedFromWindow();
|
||||||
|
TabbedController?.SendDisappearing();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
|
||||||
|
{
|
||||||
|
var width = right - left;
|
||||||
|
var height = bottom - top;
|
||||||
|
|
||||||
|
base.OnLayout(changed, left, top, right, bottom);
|
||||||
|
if(width <= 0 || height <= 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(selected)
|
this.Layout(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if(disposing)
|
||||||
{
|
{
|
||||||
var selectedResource = IdFromTitle(string.Format("{0}_selected", icon), ResourceManager.DrawableClass);
|
Element.ChildAdded -= PagesChanged;
|
||||||
if(selectedResource != 0)
|
Element.ChildRemoved -= PagesChanged;
|
||||||
|
Element.ChildrenReordered -= PagesChanged;
|
||||||
|
|
||||||
|
if(_rootLayout != null)
|
||||||
{
|
{
|
||||||
tab.SetIcon(selectedResource);
|
RemoveAllViews();
|
||||||
return;
|
foreach(Page pageToRemove in Element.Children)
|
||||||
|
{
|
||||||
|
var pageRenderer = Platform.GetRenderer(pageToRemove);
|
||||||
|
if(pageRenderer != null)
|
||||||
|
{
|
||||||
|
pageRenderer.View.RemoveFromParent();
|
||||||
|
pageRenderer.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_bottomNav != null)
|
||||||
|
{
|
||||||
|
_bottomNav.SetOnNavigationItemSelectedListener(null);
|
||||||
|
_bottomNav.Dispose();
|
||||||
|
_bottomNav = null;
|
||||||
|
}
|
||||||
|
_rootLayout.Dispose();
|
||||||
|
_rootLayout = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var resource = IdFromTitle(icon, ResourceManager.DrawableClass);
|
base.Dispose(disposing);
|
||||||
tab.SetIcon(resource);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int IdFromTitle(string title, Type type)
|
internal void SetupNativeView()
|
||||||
{
|
{
|
||||||
var name = System.IO.Path.GetFileNameWithoutExtension(title);
|
_rootLayout = this.CreateRoot(_barId, GenerateViewId(), out _pageContainer);
|
||||||
return GetId(type, name);
|
AddView(_rootLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetId(Type type, string propertyName)
|
void SwitchContent(Page page)
|
||||||
|
{
|
||||||
|
this.ChangePage(_pageContainer, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateIgnoreContainerAreas()
|
||||||
|
{
|
||||||
|
foreach(var child in Element.Children)
|
||||||
|
{
|
||||||
|
child.IgnoresContainerArea = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class TabExtensions
|
||||||
|
{
|
||||||
|
public static Rectangle CreateRect(this Context context, int width, int height)
|
||||||
|
{
|
||||||
|
return new Rectangle(0, 0, context.FromPixels(width), context.FromPixels(height));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HandlePagesChanged(this ExtendedTabbedPageRenderer renderer)
|
||||||
|
{
|
||||||
|
renderer.SetupBottomBar();
|
||||||
|
renderer.SetupTabItems();
|
||||||
|
|
||||||
|
if(renderer.Element.Children.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureTabIndex(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EnsureTabIndex(ExtendedTabbedPageRenderer renderer)
|
||||||
|
{
|
||||||
|
var rootLayout = (RelativeLayout)renderer.GetChildAt(0);
|
||||||
|
var bottomNav = (BottomNavigationViewEx)rootLayout.GetChildAt(1);
|
||||||
|
var menu = (BottomNavigationMenu)bottomNav.Menu;
|
||||||
|
|
||||||
|
var itemIndex = menu.FindItemIndex(bottomNav.SelectedItemId);
|
||||||
|
var pageIndex = renderer.Element.Children.IndexOf(renderer.Element.CurrentPage);
|
||||||
|
if(pageIndex >= 0 && pageIndex != itemIndex && pageIndex < bottomNav.ItemCount)
|
||||||
|
{
|
||||||
|
var menuItem = menu.GetItem(pageIndex);
|
||||||
|
bottomNav.SelectedItemId = menuItem.ItemId;
|
||||||
|
|
||||||
|
if(ExtendedTabbedPageRenderer.ShouldUpdateSelectedIcon && ExtendedTabbedPageRenderer.MenuItemIconSetter != null)
|
||||||
|
{
|
||||||
|
ExtendedTabbedPageRenderer.MenuItemIconSetter?.Invoke(menuItem, renderer.Element.CurrentPage.Icon, true);
|
||||||
|
|
||||||
|
if(renderer.LastSelectedIndex != pageIndex)
|
||||||
|
{
|
||||||
|
var lastSelectedPage = renderer.Element.Children[renderer.LastSelectedIndex];
|
||||||
|
var lastSelectedMenuItem = menu.GetItem(renderer.LastSelectedIndex);
|
||||||
|
ExtendedTabbedPageRenderer.MenuItemIconSetter?.Invoke(lastSelectedMenuItem, lastSelectedPage.Icon, false);
|
||||||
|
renderer.LastSelectedIndex = pageIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SwitchPage(this ExtendedTabbedPageRenderer renderer, IMenuItem item)
|
||||||
|
{
|
||||||
|
var rootLayout = (RelativeLayout)renderer.GetChildAt(0);
|
||||||
|
var bottomNav = (BottomNavigationViewEx)rootLayout.GetChildAt(1);
|
||||||
|
var menu = (BottomNavigationMenu)bottomNav.Menu;
|
||||||
|
|
||||||
|
var index = menu.FindItemIndex(item.ItemId);
|
||||||
|
var pageIndex = index % renderer.Element.Children.Count;
|
||||||
|
var currentPageIndex = renderer.Element.Children.IndexOf(renderer.Element.CurrentPage);
|
||||||
|
|
||||||
|
if(currentPageIndex != pageIndex)
|
||||||
|
{
|
||||||
|
renderer.Element.CurrentPage = renderer.Element.Children[pageIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Layout(this ExtendedTabbedPageRenderer renderer, int width, int height)
|
||||||
|
{
|
||||||
|
var rootLayout = (RelativeLayout)renderer.GetChildAt(0);
|
||||||
|
var bottomNav = (BottomNavigationViewEx)rootLayout.GetChildAt(1);
|
||||||
|
|
||||||
|
var Context = renderer.Context;
|
||||||
|
|
||||||
|
rootLayout.Measure(MakeMeasureSpec(width, MeasureSpecMode.Exactly),
|
||||||
|
MakeMeasureSpec(height, MeasureSpecMode.AtMost));
|
||||||
|
|
||||||
|
((IPageController)renderer.Element).ContainerArea =
|
||||||
|
Context.CreateRect(rootLayout.MeasuredWidth, rootLayout.GetChildAt(0).MeasuredHeight);
|
||||||
|
|
||||||
|
rootLayout.Measure(MakeMeasureSpec(width, MeasureSpecMode.Exactly),
|
||||||
|
MakeMeasureSpec(height, MeasureSpecMode.Exactly));
|
||||||
|
rootLayout.Layout(0, 0, rootLayout.MeasuredWidth, rootLayout.MeasuredHeight);
|
||||||
|
|
||||||
|
if(renderer.Element.Children.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tabsHeight = bottomNav.MeasuredHeight;
|
||||||
|
|
||||||
|
var item = (ViewGroup)bottomNav.GetChildAt(0);
|
||||||
|
item.Measure(MakeMeasureSpec(width, MeasureSpecMode.Exactly),
|
||||||
|
MakeMeasureSpec(tabsHeight, MeasureSpecMode.Exactly));
|
||||||
|
|
||||||
|
item.Layout(0, 0, width, tabsHeight);
|
||||||
|
int item_w = width / item.ChildCount;
|
||||||
|
for(int i = 0; i < item.ChildCount; i++)
|
||||||
|
{
|
||||||
|
var frame = (FrameLayout)item.GetChildAt(i);
|
||||||
|
frame.Measure(MakeMeasureSpec(item_w, MeasureSpecMode.Exactly),
|
||||||
|
MakeMeasureSpec(tabsHeight, MeasureSpecMode.Exactly));
|
||||||
|
frame.Layout(i * item_w, 0, i * item_w + item_w, tabsHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetupTabItems(this ExtendedTabbedPageRenderer renderer, BottomNavigationViewEx bottomNav)
|
||||||
|
{
|
||||||
|
var element = renderer.Element;
|
||||||
|
var menu = (BottomNavigationMenu)bottomNav.Menu;
|
||||||
|
menu.ClearAll();
|
||||||
|
|
||||||
|
var tabsCount = Math.Min(element.Children.Count, bottomNav.MaxItemCount);
|
||||||
|
for(int i = 0; i < tabsCount; i++)
|
||||||
|
{
|
||||||
|
var page = element.Children[i];
|
||||||
|
var menuItem = menu.Add(0, i, 0, page.Title);
|
||||||
|
var setter = ExtendedTabbedPageRenderer.MenuItemIconSetter ?? ExtendedTabbedPageRenderer.DefaultMenuItemIconSetter;
|
||||||
|
setter.Invoke(menuItem, page.Icon, renderer.LastSelectedIndex == i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(element.Children.Count > 0)
|
||||||
|
{
|
||||||
|
bottomNav.EnableShiftingMode(false);
|
||||||
|
bottomNav.EnableItemShiftingMode(false);
|
||||||
|
bottomNav.EnableAnimation(false);
|
||||||
|
bottomNav.SetTextVisibility(false);
|
||||||
|
bottomNav.SetBackgroundResource(Resource.Drawable.bottom_nav_bg);
|
||||||
|
bottomNav.SetIconSize(24, 24);
|
||||||
|
bottomNav.SetIconsMarginTop(32);
|
||||||
|
|
||||||
|
if(element.Children.Count > 3)
|
||||||
|
{
|
||||||
|
bottomNav.SetIconSizeAt(3, 29, 29);
|
||||||
|
bottomNav.SetIconMarginTop(3, 28);
|
||||||
|
}
|
||||||
|
|
||||||
|
var stateList = new global::Android.Content.Res.ColorStateList(
|
||||||
|
new int[][] {
|
||||||
|
new int[] { global::Android.Resource.Attribute.StateChecked },
|
||||||
|
new int[] { global::Android.Resource.Attribute.StateEnabled}
|
||||||
|
},
|
||||||
|
new int[] {
|
||||||
|
element.TintColor.ToAndroid(), // Selected
|
||||||
|
Color.FromHex("A1A1A1").ToAndroid() // Normal
|
||||||
|
});
|
||||||
|
|
||||||
|
bottomNav.ItemIconTintList = stateList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BottomNavigationViewEx SetupBottomBar(this ExtendedTabbedPageRenderer renderer,
|
||||||
|
global::Android.Widget.RelativeLayout rootLayout, BottomNavigationViewEx bottomNav, int barId)
|
||||||
|
{
|
||||||
|
if(bottomNav != null)
|
||||||
|
{
|
||||||
|
rootLayout.RemoveView(bottomNav);
|
||||||
|
bottomNav.SetOnNavigationItemSelectedListener(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
var barParams = new global::Android.Widget.RelativeLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MatchParent,
|
||||||
|
ExtendedTabbedPageRenderer.BottomBarHeight.HasValue ?
|
||||||
|
(int)rootLayout.Context.ToPixels(ExtendedTabbedPageRenderer.BottomBarHeight.Value) :
|
||||||
|
ViewGroup.LayoutParams.WrapContent);
|
||||||
|
barParams.AddRule(LayoutRules.AlignParentBottom);
|
||||||
|
bottomNav = new BottomNavigationViewEx(rootLayout.Context)
|
||||||
|
{
|
||||||
|
LayoutParameters = barParams,
|
||||||
|
Id = barId
|
||||||
|
};
|
||||||
|
if(ExtendedTabbedPageRenderer.BackgroundColor.HasValue)
|
||||||
|
{
|
||||||
|
bottomNav.SetBackgroundColor(ExtendedTabbedPageRenderer.BackgroundColor.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomNav.SetOnNavigationItemSelectedListener(renderer);
|
||||||
|
rootLayout.AddView(bottomNav, 1, barParams);
|
||||||
|
|
||||||
|
return bottomNav;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ChangePage(this ExtendedTabbedPageRenderer renderer, FrameLayout pageContainer, Page page)
|
||||||
|
{
|
||||||
|
renderer.Context.HideKeyboard(renderer);
|
||||||
|
if(page == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Platform.GetRenderer(page) == null)
|
||||||
|
{
|
||||||
|
Platform.SetRenderer(page, Platform.CreateRendererWithContext(page, renderer.Context));
|
||||||
|
}
|
||||||
|
|
||||||
|
var pageContent = Platform.GetRenderer(page).View;
|
||||||
|
pageContainer.AddView(pageContent);
|
||||||
|
if(pageContainer.ChildCount > 1)
|
||||||
|
{
|
||||||
|
pageContainer.RemoveViewAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureTabIndex(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RelativeLayout CreateRoot(this ExtendedTabbedPageRenderer renderer, int barId, int pageContainerId, out FrameLayout pageContainer)
|
||||||
|
{
|
||||||
|
var rootLayout = new RelativeLayout(renderer.Context)
|
||||||
|
{
|
||||||
|
LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent),
|
||||||
|
};
|
||||||
|
|
||||||
|
var pageParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent);
|
||||||
|
pageParams.AddRule(LayoutRules.Above, barId);
|
||||||
|
pageContainer = new FrameLayout(renderer.Context)
|
||||||
|
{
|
||||||
|
LayoutParameters = pageParams,
|
||||||
|
Id = pageContainerId
|
||||||
|
};
|
||||||
|
|
||||||
|
rootLayout.AddView(pageContainer, 0, pageParams);
|
||||||
|
return rootLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int MakeMeasureSpec(int size, MeasureSpecMode mode)
|
||||||
|
{
|
||||||
|
return size + (int)mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ResourceUtils
|
||||||
|
{
|
||||||
|
public static int IdFromTitle(string title, Type type)
|
||||||
|
{
|
||||||
|
var name = Path.GetFileNameWithoutExtension(title);
|
||||||
|
var id = GetId(type, name);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetId(Type type, string propertyName)
|
||||||
{
|
{
|
||||||
var props = type.GetFields();
|
var props = type.GetFields();
|
||||||
var prop = props.FirstOrDefault(p => p.Name == propertyName);
|
var prop = props.Select(p => p).FirstOrDefault(p => p.Name == propertyName);
|
||||||
if(prop != null)
|
if(prop != null)
|
||||||
{
|
{
|
||||||
return (int)prop.GetValue(type);
|
return (int)prop.GetValue(type);
|
||||||
|
|||||||
@@ -14,11 +14,25 @@ namespace Bit.Android.Controls
|
|||||||
{
|
{
|
||||||
public class ExtendedTableViewRenderer : TableViewRenderer
|
public class ExtendedTableViewRenderer : TableViewRenderer
|
||||||
{
|
{
|
||||||
|
public ExtendedTableViewRenderer(Context context)
|
||||||
|
: base(context)
|
||||||
|
{ }
|
||||||
|
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
|
protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
Control.Divider = null;
|
Control.Divider = null;
|
||||||
Control.DividerHeight = 0;
|
Control.DividerHeight = 0;
|
||||||
|
|
||||||
|
if(e.NewElement is ExtendedTableView tableView)
|
||||||
|
{
|
||||||
|
if(tableView.BottomPadding > 0)
|
||||||
|
{
|
||||||
|
Control.SetPadding(0, 0, 0, tableView.BottomPadding);
|
||||||
|
Control.SetClipToPadding(false);
|
||||||
|
Control.ScrollBarStyle = ScrollbarStyles.OutsideOverlay;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override TableViewModelRenderer GetModelRenderer(AListView listView, TableView view)
|
protected override TableViewModelRenderer GetModelRenderer(AListView listView, TableView view)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ using Xamarin.Forms.Platform.Android;
|
|||||||
using Android.Webkit;
|
using Android.Webkit;
|
||||||
using AWebkit = Android.Webkit;
|
using AWebkit = Android.Webkit;
|
||||||
using Java.Interop;
|
using Java.Interop;
|
||||||
|
using Android.Content;
|
||||||
|
using Plugin.CurrentActivity;
|
||||||
|
|
||||||
[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
|
[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
|
||||||
namespace Bit.Android.Controls
|
namespace Bit.Android.Controls
|
||||||
@@ -14,13 +16,17 @@ namespace Bit.Android.Controls
|
|||||||
{
|
{
|
||||||
private const string JSFunction = "function invokeCSharpAction(data){jsBridge.invokeAction(data);}";
|
private const string JSFunction = "function invokeCSharpAction(data){jsBridge.invokeAction(data);}";
|
||||||
|
|
||||||
|
public HybridWebViewRenderer(Context context)
|
||||||
|
: base(context)
|
||||||
|
{ }
|
||||||
|
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e)
|
protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
|
|
||||||
if(Control == null)
|
if(Control == null)
|
||||||
{
|
{
|
||||||
var webView = new AWebkit.WebView(Forms.Context);
|
var webView = new AWebkit.WebView(CrossCurrentActivity.Current.Activity);
|
||||||
webView.Settings.JavaScriptEnabled = true;
|
webView.Settings.JavaScriptEnabled = true;
|
||||||
SetNativeControl(webView);
|
SetNativeControl(webView);
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/Android/FirebaseInstanceIdService.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Bit.App;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Firebase.Iid;
|
||||||
|
using Plugin.Settings.Abstractions;
|
||||||
|
using XLabs.Ioc;
|
||||||
|
|
||||||
|
namespace Bit.Android
|
||||||
|
{
|
||||||
|
[Service]
|
||||||
|
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
|
||||||
|
public class FirebaseInstanceIdService : Firebase.Iid.FirebaseInstanceIdService
|
||||||
|
{
|
||||||
|
public override void OnTokenRefresh()
|
||||||
|
{
|
||||||
|
var settings = Resolver.Resolve<ISettings>();
|
||||||
|
settings.AddOrUpdateValue(Constants.PushRegisteredToken, FirebaseInstanceId.Instance.Token);
|
||||||
|
Resolver.Resolve<IPushNotificationService>()?.Register();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/Android/FirebaseMessagingService.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Firebase.Messaging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using XLabs.Ioc;
|
||||||
|
|
||||||
|
namespace Bit.Android
|
||||||
|
{
|
||||||
|
[Service]
|
||||||
|
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
|
||||||
|
public class FirebaseMessagingService : Firebase.Messaging.FirebaseMessagingService
|
||||||
|
{
|
||||||
|
public override void OnMessageReceived(RemoteMessage message)
|
||||||
|
{
|
||||||
|
if(message?.Data == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = message.Data.ContainsKey("data") ? message.Data["data"] : null;
|
||||||
|
if(data == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var obj = JObject.Parse(data);
|
||||||
|
var listener = Resolver.Resolve<IPushNotificationListener>();
|
||||||
|
listener.OnMessage(obj, Device.Android);
|
||||||
|
}
|
||||||
|
catch(JsonReaderException ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine(ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,18 +7,17 @@ using Bit.App.Abstractions;
|
|||||||
using XLabs.Ioc;
|
using XLabs.Ioc;
|
||||||
using Plugin.Settings.Abstractions;
|
using Plugin.Settings.Abstractions;
|
||||||
using Plugin.Connectivity.Abstractions;
|
using Plugin.Connectivity.Abstractions;
|
||||||
using Acr.UserDialogs;
|
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Models.Page;
|
|
||||||
using Bit.App;
|
using Bit.App;
|
||||||
using Android.Nfc;
|
using Android.Nfc;
|
||||||
using Android.Views.InputMethods;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Bit.App.Models;
|
||||||
|
using Bit.App.Enums;
|
||||||
|
|
||||||
namespace Bit.Android
|
namespace Bit.Android
|
||||||
{
|
{
|
||||||
@@ -29,14 +28,13 @@ namespace Bit.Android
|
|||||||
public class MainActivity : FormsAppCompatActivity
|
public class MainActivity : FormsAppCompatActivity
|
||||||
{
|
{
|
||||||
private const string HockeyAppId = "d3834185b4a643479047b86c65293d42";
|
private const string HockeyAppId = "d3834185b4a643479047b86c65293d42";
|
||||||
private DateTime? _lastAction;
|
|
||||||
private Java.Util.Regex.Pattern _otpPattern = Java.Util.Regex.Pattern.Compile("^.*?([cbdefghijklnrtuv]{32,64})$");
|
private Java.Util.Regex.Pattern _otpPattern = Java.Util.Regex.Pattern.Compile("^.*?([cbdefghijklnrtuv]{32,64})$");
|
||||||
private IDeviceActionService _deviceActionService;
|
private IDeviceActionService _deviceActionService;
|
||||||
private ISettings _settings;
|
private ISettings _settings;
|
||||||
|
private AppOptions _appOptions;
|
||||||
|
|
||||||
protected override void OnCreate(Bundle bundle)
|
protected override void OnCreate(Bundle bundle)
|
||||||
{
|
{
|
||||||
var uri = Intent.GetStringExtra("uri");
|
|
||||||
if(!Resolver.IsSet)
|
if(!Resolver.IsSet)
|
||||||
{
|
{
|
||||||
MainApplication.SetIoc(Application);
|
MainApplication.SetIoc(Application);
|
||||||
@@ -73,93 +71,28 @@ namespace Bit.Android
|
|||||||
|
|
||||||
_deviceActionService = Resolver.Resolve<IDeviceActionService>();
|
_deviceActionService = Resolver.Resolve<IDeviceActionService>();
|
||||||
_settings = Resolver.Resolve<ISettings>();
|
_settings = Resolver.Resolve<ISettings>();
|
||||||
|
_appOptions = GetOptions();
|
||||||
LoadApplication(new App.App(
|
LoadApplication(new App.App(
|
||||||
uri,
|
_appOptions,
|
||||||
Intent.GetBooleanExtra("myVaultTile", false),
|
|
||||||
Resolver.Resolve<IAuthService>(),
|
Resolver.Resolve<IAuthService>(),
|
||||||
Resolver.Resolve<IConnectivity>(),
|
Resolver.Resolve<IConnectivity>(),
|
||||||
Resolver.Resolve<IUserDialogs>(),
|
|
||||||
Resolver.Resolve<IDatabaseService>(),
|
Resolver.Resolve<IDatabaseService>(),
|
||||||
Resolver.Resolve<ISyncService>(),
|
Resolver.Resolve<ISyncService>(),
|
||||||
_settings,
|
_settings,
|
||||||
Resolver.Resolve<ILockService>(),
|
Resolver.Resolve<ILockService>(),
|
||||||
Resolver.Resolve<IGoogleAnalyticsService>(),
|
|
||||||
Resolver.Resolve<ILocalizeService>(),
|
Resolver.Resolve<ILocalizeService>(),
|
||||||
Resolver.Resolve<IAppInfoService>(),
|
Resolver.Resolve<IAppInfoService>(),
|
||||||
Resolver.Resolve<IAppSettingsService>(),
|
Resolver.Resolve<IAppSettingsService>(),
|
||||||
_deviceActionService));
|
_deviceActionService));
|
||||||
|
|
||||||
MessagingCenter.Subscribe<Xamarin.Forms.Application>(
|
if(_appOptions?.Uri == null)
|
||||||
Xamarin.Forms.Application.Current, "DismissKeyboard", (sender) =>
|
|
||||||
{
|
{
|
||||||
DismissKeyboard();
|
MessagingCenter.Subscribe<Xamarin.Forms.Application, bool>(Xamarin.Forms.Application.Current,
|
||||||
});
|
"ListenYubiKeyOTP", (sender, listen) => ListenYubiKey(listen));
|
||||||
|
|
||||||
MessagingCenter.Subscribe<Xamarin.Forms.Application>(Xamarin.Forms.Application.Current, "RateApp", (sender) =>
|
MessagingCenter.Subscribe<Xamarin.Forms.Application>(Xamarin.Forms.Application.Current,
|
||||||
{
|
"FinishMainActivity", (sender) => Finish());
|
||||||
RateApp();
|
|
||||||
});
|
|
||||||
|
|
||||||
MessagingCenter.Subscribe<Xamarin.Forms.Application>(Xamarin.Forms.Application.Current, "Accessibility", (sender) =>
|
|
||||||
{
|
|
||||||
OpenAccessibilitySettings();
|
|
||||||
});
|
|
||||||
|
|
||||||
MessagingCenter.Subscribe<Xamarin.Forms.Application, VaultListPageModel.Cipher>(
|
|
||||||
Xamarin.Forms.Application.Current, "Autofill", (sender, args) =>
|
|
||||||
{
|
|
||||||
ReturnCredentials(args);
|
|
||||||
});
|
|
||||||
|
|
||||||
MessagingCenter.Subscribe<Xamarin.Forms.Application>(Xamarin.Forms.Application.Current, "BackgroundApp", (sender) =>
|
|
||||||
{
|
|
||||||
MoveTaskToBack(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
MessagingCenter.Subscribe<Xamarin.Forms.Application, string>(
|
|
||||||
Xamarin.Forms.Application.Current, "LaunchApp", (sender, args) =>
|
|
||||||
{
|
|
||||||
LaunchApp(args);
|
|
||||||
});
|
|
||||||
|
|
||||||
MessagingCenter.Subscribe<Xamarin.Forms.Application, bool>(
|
|
||||||
Xamarin.Forms.Application.Current, "ListenYubiKeyOTP", (sender, listen) =>
|
|
||||||
{
|
|
||||||
ListenYubiKey(listen);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReturnCredentials(VaultListPageModel.Cipher cipher)
|
|
||||||
{
|
|
||||||
Intent data = new Intent();
|
|
||||||
if(cipher == null)
|
|
||||||
{
|
|
||||||
data.PutExtra("canceled", "true");
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
var isPremium = Resolver.Resolve<ITokenService>()?.TokenPremium ?? false;
|
|
||||||
var autoCopyEnabled = !_settings.GetValueOrDefault(Constants.SettingDisableTotpCopy, false);
|
|
||||||
if(isPremium && autoCopyEnabled && _deviceActionService != null && cipher.LoginTotp?.Value != null)
|
|
||||||
{
|
|
||||||
_deviceActionService.CopyToClipboard(App.Utilities.Crypto.Totp(cipher.LoginTotp.Value));
|
|
||||||
}
|
|
||||||
|
|
||||||
data.PutExtra("uri", cipher.LoginUri);
|
|
||||||
data.PutExtra("username", cipher.LoginUsername);
|
|
||||||
data.PutExtra("password", cipher.LoginPassword?.Value ?? null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Parent == null)
|
|
||||||
{
|
|
||||||
SetResult(Result.Ok, data);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Parent.SetResult(Result.Ok, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
Finish();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnPause()
|
protected override void OnPause()
|
||||||
@@ -265,65 +198,6 @@ namespace Bit.Android
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RateApp()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var rateIntent = RateIntentForUrl("market://details");
|
|
||||||
StartActivity(rateIntent);
|
|
||||||
}
|
|
||||||
catch(ActivityNotFoundException)
|
|
||||||
{
|
|
||||||
var rateIntent = RateIntentForUrl("https://play.google.com/store/apps/details");
|
|
||||||
StartActivity(rateIntent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Intent RateIntentForUrl(string url)
|
|
||||||
{
|
|
||||||
var intent = new Intent(Intent.ActionView, global::Android.Net.Uri.Parse($"{url}?id={PackageName}"));
|
|
||||||
var flags = ActivityFlags.NoHistory | ActivityFlags.MultipleTask;
|
|
||||||
if((int)Build.VERSION.SdkInt >= 21)
|
|
||||||
{
|
|
||||||
flags |= ActivityFlags.NewDocument;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// noinspection deprecation
|
|
||||||
flags |= ActivityFlags.ClearWhenTaskReset;
|
|
||||||
}
|
|
||||||
|
|
||||||
intent.AddFlags(flags);
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OpenAccessibilitySettings()
|
|
||||||
{
|
|
||||||
var intent = new Intent(global::Android.Provider.Settings.ActionAccessibilitySettings);
|
|
||||||
StartActivity(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LaunchApp(string packageName)
|
|
||||||
{
|
|
||||||
if(_lastAction.LastActionWasRecent())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_lastAction = DateTime.UtcNow;
|
|
||||||
|
|
||||||
packageName = packageName.Replace("androidapp://", string.Empty);
|
|
||||||
var launchIntent = PackageManager.GetLaunchIntentForPackage(packageName);
|
|
||||||
if(launchIntent == null)
|
|
||||||
{
|
|
||||||
var dialog = Resolver.Resolve<IUserDialogs>();
|
|
||||||
dialog.Alert(string.Format(App.Resources.AppResources.CannotOpenApp, packageName));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
StartActivity(launchIntent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ListenYubiKey(bool listen)
|
private void ListenYubiKey(bool listen)
|
||||||
{
|
{
|
||||||
if(!Utilities.NfcEnabled())
|
if(!Utilities.NfcEnabled())
|
||||||
@@ -368,14 +242,35 @@ namespace Bit.Android
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DismissKeyboard()
|
private AppOptions GetOptions()
|
||||||
{
|
{
|
||||||
try
|
var options = new AppOptions
|
||||||
{
|
{
|
||||||
var imm = (InputMethodManager)GetSystemService(InputMethodService);
|
Uri = Intent.GetStringExtra("uri") ?? Intent.GetStringExtra("autofillFrameworkUri"),
|
||||||
imm.HideSoftInputFromWindow(CurrentFocus.WindowToken, 0);
|
MyVaultTile = Intent.GetBooleanExtra("myVaultTile", false),
|
||||||
|
FromAutofillFramework = Intent.GetBooleanExtra("autofillFramework", false)
|
||||||
|
};
|
||||||
|
|
||||||
|
var fillType = Intent.GetIntExtra("autofillFrameworkFillType", 0);
|
||||||
|
if(fillType > 0)
|
||||||
|
{
|
||||||
|
options.FillType = (CipherType)fillType;
|
||||||
}
|
}
|
||||||
catch { }
|
|
||||||
|
if(Intent.GetBooleanExtra("autofillFrameworkSave", false))
|
||||||
|
{
|
||||||
|
options.SaveType = (CipherType)Intent.GetIntExtra("autofillFrameworkType", 0);
|
||||||
|
options.SaveName = Intent.GetStringExtra("autofillFrameworkName");
|
||||||
|
options.SaveUsername = Intent.GetStringExtra("autofillFrameworkUsername");
|
||||||
|
options.SavePassword = Intent.GetStringExtra("autofillFrameworkPassword");
|
||||||
|
options.SaveCardName = Intent.GetStringExtra("autofillFrameworkCardName");
|
||||||
|
options.SaveCardNumber = Intent.GetStringExtra("autofillFrameworkCardNumber");
|
||||||
|
options.SaveCardExpMonth = Intent.GetStringExtra("autofillFrameworkCardExpMonth");
|
||||||
|
options.SaveCardExpYear = Intent.GetStringExtra("autofillFrameworkCardExpYear");
|
||||||
|
options.SaveCardCode = Intent.GetStringExtra("autofillFrameworkCardCode");
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using Acr.UserDialogs;
|
|
||||||
using Android.App;
|
using Android.App;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
@@ -61,55 +60,6 @@ namespace Bit.Android
|
|||||||
|
|
||||||
RegisterActivityLifecycleCallbacks(this);
|
RegisterActivityLifecycleCallbacks(this);
|
||||||
AppContext = ApplicationContext;
|
AppContext = ApplicationContext;
|
||||||
StartPushService();
|
|
||||||
HandlePushReregistration();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandlePushReregistration()
|
|
||||||
{
|
|
||||||
var pushNotification = Resolver.Resolve<IPushNotificationService>();
|
|
||||||
var settings = Resolver.Resolve<ISettings>();
|
|
||||||
|
|
||||||
// Reregister for push token based on certain conditions
|
|
||||||
// ref https://github.com/rdelrosario/xamarin-plugins/issues/65
|
|
||||||
|
|
||||||
var reregister = false;
|
|
||||||
|
|
||||||
// 1. First time starting the app after a new install
|
|
||||||
if(settings.GetValueOrDefault(FirstLaunchKey, true))
|
|
||||||
{
|
|
||||||
settings.AddOrUpdateValue(FirstLaunchKey, false);
|
|
||||||
reregister = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. App version changed (installed update)
|
|
||||||
var versionCode = Context.ApplicationContext.PackageManager.GetPackageInfo(Context.PackageName, 0).VersionCode;
|
|
||||||
if(settings.GetValueOrDefault(LastVersionCodeKey, -1) != versionCode)
|
|
||||||
{
|
|
||||||
settings.AddOrUpdateValue(LastVersionCodeKey, versionCode);
|
|
||||||
reregister = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. In debug mode
|
|
||||||
if(App.Utilities.Helpers.InDebugMode())
|
|
||||||
{
|
|
||||||
reregister = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Doesn't have a push token currently
|
|
||||||
if(string.IsNullOrWhiteSpace(pushNotification.Token))
|
|
||||||
{
|
|
||||||
reregister = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(reregister)
|
|
||||||
{
|
|
||||||
pushNotification.Unregister();
|
|
||||||
if(Resolver.Resolve<IAuthService>().IsAuthenticated)
|
|
||||||
{
|
|
||||||
pushNotification.Register();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnTerminate()
|
public override void OnTerminate()
|
||||||
@@ -149,33 +99,9 @@ namespace Bit.Android
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void StartPushService()
|
|
||||||
{
|
|
||||||
AppContext.StartService(new Intent(AppContext, typeof(AndroidPushService)));
|
|
||||||
if(Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
|
|
||||||
{
|
|
||||||
PendingIntent pintent = PendingIntent.GetService(AppContext, 0, new Intent(AppContext,
|
|
||||||
typeof(AndroidPushService)), 0);
|
|
||||||
AlarmManager alarm = (AlarmManager)AppContext.GetSystemService(AlarmService);
|
|
||||||
alarm.Cancel(pintent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void StopPushService()
|
|
||||||
{
|
|
||||||
AppContext.StopService(new Intent(AppContext, typeof(AndroidPushService)));
|
|
||||||
if(Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
|
|
||||||
{
|
|
||||||
PendingIntent pintent = PendingIntent.GetService(AppContext, 0, new Intent(AppContext,
|
|
||||||
typeof(AndroidPushService)), 0);
|
|
||||||
AlarmManager alarm = (AlarmManager)AppContext.GetSystemService(AlarmService);
|
|
||||||
alarm.Cancel(pintent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetIoc(Application application)
|
public static void SetIoc(Application application)
|
||||||
{
|
{
|
||||||
UserDialogs.Init(application);
|
Refractored.FabControl.Droid.FloatingActionButtonViewRenderer.Init();
|
||||||
CachedImageRenderer.Init();
|
CachedImageRenderer.Init();
|
||||||
ZXing.Net.Mobile.Forms.Android.Platform.Init();
|
ZXing.Net.Mobile.Forms.Android.Platform.Init();
|
||||||
CrossFingerprint.SetCurrentActivityResolver(() => CrossCurrentActivity.Current.Activity);
|
CrossFingerprint.SetCurrentActivityResolver(() => CrossCurrentActivity.Current.Activity);
|
||||||
@@ -195,6 +121,7 @@ namespace Bit.Android
|
|||||||
container.RegisterSingleton<IKeyDerivationService, BouncyCastleKeyDerivationService>();
|
container.RegisterSingleton<IKeyDerivationService, BouncyCastleKeyDerivationService>();
|
||||||
container.RegisterSingleton<IAuthService, AuthService>();
|
container.RegisterSingleton<IAuthService, AuthService>();
|
||||||
container.RegisterSingleton<IFolderService, FolderService>();
|
container.RegisterSingleton<IFolderService, FolderService>();
|
||||||
|
container.RegisterSingleton<ICollectionService, CollectionService>();
|
||||||
container.RegisterSingleton<ICipherService, CipherService>();
|
container.RegisterSingleton<ICipherService, CipherService>();
|
||||||
container.RegisterSingleton<ISyncService, SyncService>();
|
container.RegisterSingleton<ISyncService, SyncService>();
|
||||||
container.RegisterSingleton<IDeviceActionService, DeviceActionService>();
|
container.RegisterSingleton<IDeviceActionService, DeviceActionService>();
|
||||||
@@ -226,18 +153,17 @@ namespace Bit.Android
|
|||||||
container.RegisterSingleton<ISettingsApiRepository, SettingsApiRepository>();
|
container.RegisterSingleton<ISettingsApiRepository, SettingsApiRepository>();
|
||||||
container.RegisterSingleton<ITwoFactorApiRepository, TwoFactorApiRepository>();
|
container.RegisterSingleton<ITwoFactorApiRepository, TwoFactorApiRepository>();
|
||||||
container.RegisterSingleton<ISyncApiRepository, SyncApiRepository>();
|
container.RegisterSingleton<ISyncApiRepository, SyncApiRepository>();
|
||||||
|
container.RegisterSingleton<ICollectionRepository, CollectionRepository>();
|
||||||
|
container.RegisterSingleton<ICipherCollectionRepository, CipherCollectionRepository>();
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
container.RegisterSingleton(CrossSettings.Current);
|
container.RegisterSingleton(CrossSettings.Current);
|
||||||
container.RegisterSingleton(CrossConnectivity.Current);
|
container.RegisterSingleton(CrossConnectivity.Current);
|
||||||
container.RegisterSingleton(UserDialogs.Instance);
|
|
||||||
container.RegisterSingleton(CrossFingerprint.Current);
|
container.RegisterSingleton(CrossFingerprint.Current);
|
||||||
|
|
||||||
// Push
|
// Push
|
||||||
var pushListener = new PushNotificationListener();
|
container.RegisterSingleton<IPushNotificationListener, PushNotificationListener>();
|
||||||
container.RegisterSingleton<IPushNotificationListener>(pushListener);
|
container.RegisterSingleton<IPushNotificationService, AndroidPushNotificationService>();
|
||||||
CrossPushNotification.Initialize(pushListener, "962181367620");
|
|
||||||
container.RegisterSingleton(CrossPushNotification.Current);
|
|
||||||
|
|
||||||
container.Verify();
|
container.Verify();
|
||||||
Resolver.SetResolver(new SimpleInjectorResolver(container));
|
Resolver.SetResolver(new SimpleInjectorResolver(container));
|
||||||
|
|||||||
BIN
src/Android/Naxam.Ittianyu.BottomNavExtension.dll
Normal file
@@ -3,20 +3,20 @@ using Android.Content;
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Plugin.Settings.Abstractions;
|
using Plugin.Settings.Abstractions;
|
||||||
using System.Diagnostics;
|
using System;
|
||||||
using XLabs.Ioc;
|
using XLabs.Ioc;
|
||||||
|
|
||||||
namespace Bit.Android
|
namespace Bit.Android
|
||||||
{
|
{
|
||||||
[BroadcastReceiver(Name = "com.x8bit.bitwarden.PackageReplacedReceiver", Exported = true)]
|
[BroadcastReceiver(Name = "com.x8bit.bitwarden.PackageReplacedReceiver", Exported = false)]
|
||||||
[IntentFilter(new[] { Intent.ActionMyPackageReplaced })]
|
[IntentFilter(new[] { Intent.ActionMyPackageReplaced })]
|
||||||
public class PackageReplacedReceiver : BroadcastReceiver
|
public class PackageReplacedReceiver : BroadcastReceiver
|
||||||
{
|
{
|
||||||
public override void OnReceive(Context context, Intent intent)
|
public override void OnReceive(Context context, Intent intent)
|
||||||
{
|
{
|
||||||
Debug.WriteLine("App updated!");
|
Console.WriteLine("Bitwarden App Updated!!");
|
||||||
Helpers.PerformUpdateTasks(Resolver.Resolve<ISettings>(),
|
Helpers.PerformUpdateTasks(Resolver.Resolve<ISettings>(),
|
||||||
Resolver.Resolve<IAppInfoService>(),Resolver.Resolve<IDatabaseService>(),
|
Resolver.Resolve<IAppInfoService>(), Resolver.Resolve<IDatabaseService>(),
|
||||||
Resolver.Resolve<ISyncService>());
|
Resolver.Resolve<ISyncService>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.x8bit.bitwarden" android:versionName="1.12.0" android:installLocation="auto" android:versionCode="502" xmlns:tools="http://schemas.android.com/tools">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.x8bit.bitwarden" android:versionName="1.14.0" android:installLocation="auto" android:versionCode="502" xmlns:tools="http://schemas.android.com/tools">
|
||||||
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="23" />
|
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="23" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
@@ -8,11 +8,6 @@
|
|||||||
<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="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-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
||||||
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
|
|
||||||
<uses-permission android:name="com.x8bit.bitwarden.permission.C2D_MESSAGE" />
|
|
||||||
<permission android:name="com.x8bit.bitwarden.permission.C2D_MESSAGE" android:protectionLevel="signature" />
|
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
||||||
@@ -28,6 +23,17 @@
|
|||||||
android:resource="@xml/filepaths" />
|
android:resource="@xml/filepaths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
|
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"
|
||||||
|
android:exported="false" />
|
||||||
|
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
|
||||||
|
android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
||||||
|
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
|
||||||
|
<category android:name="${applicationId}" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
<activity android:name="net.hockeyapp.android.UpdateActivity" android:exported="false" android:icon="@drawable/icon" />
|
<activity android:name="net.hockeyapp.android.UpdateActivity" android:exported="false" android:icon="@drawable/icon" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
10721
src/Android/Resources/Resource.Designer.cs
generated
BIN
src/Android/Resources/drawable-hdpi/autofill_enable.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
src/Android/Resources/drawable-hdpi/autofill_use.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
BIN
src/Android/Resources/drawable-hdpi/cube.png
Normal file
|
After Width: | Height: | Size: 738 B |
|
Before Width: | Height: | Size: 569 B |
BIN
src/Android/Resources/drawable-hdpi/folder_o.png
Normal file
|
After Width: | Height: | Size: 482 B |
BIN
src/Android/Resources/drawable-hdpi/pencil.png
Normal file
|
After Width: | Height: | Size: 495 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 926 B |
|
Before Width: | Height: | Size: 703 B |
|
Before Width: | Height: | Size: 703 B |
|
Before Width: | Height: | Size: 585 B |
BIN
src/Android/Resources/drawable-xhdpi/autofill_enable.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/Android/Resources/drawable-xhdpi/autofill_use.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
BIN
src/Android/Resources/drawable-xhdpi/cube.png
Normal file
|
After Width: | Height: | Size: 941 B |
|
Before Width: | Height: | Size: 680 B |
BIN
src/Android/Resources/drawable-xhdpi/folder_o.png
Normal file
|
After Width: | Height: | Size: 686 B |
BIN
src/Android/Resources/drawable-xhdpi/pencil.png
Normal file
|
After Width: | Height: | Size: 359 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 860 B |
|
Before Width: | Height: | Size: 860 B |
|
Before Width: | Height: | Size: 700 B |
BIN
src/Android/Resources/drawable-xxhdpi/autofill_enable.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
src/Android/Resources/drawable-xxhdpi/autofill_use.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
BIN
src/Android/Resources/drawable-xxhdpi/cube.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 810 B |
BIN
src/Android/Resources/drawable-xxhdpi/folder_o.png
Normal file
|
After Width: | Height: | Size: 816 B |
BIN
src/Android/Resources/drawable-xxhdpi/pencil.png
Normal file
|
After Width: | Height: | Size: 571 B |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
BIN
src/Android/Resources/drawable-xxxhdpi/cube.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
BIN
src/Android/Resources/drawable-xxxhdpi/folder_o.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/Android/Resources/drawable-xxxhdpi/pencil.png
Normal file
|
After Width: | Height: | Size: 681 B |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
BIN
src/Android/Resources/drawable/autofill_enable.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
src/Android/Resources/drawable/autofill_use.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
13
src/Android/Resources/drawable/bottom_nav_bg.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item>
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<solid android:color="#D2D6DF" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item android:top="1dp">
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<solid android:color="#F5F5F7" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
||||||
|
Before Width: | Height: | Size: 741 B After Width: | Height: | Size: 878 B |
|
Before Width: | Height: | Size: 741 B |
BIN
src/Android/Resources/drawable/cube.png
Normal file
|
After Width: | Height: | Size: 533 B |
|
Before Width: | Height: | Size: 398 B |
BIN
src/Android/Resources/drawable/folder_o.png
Normal file
|
After Width: | Height: | Size: 401 B |
BIN
src/Android/Resources/drawable/pencil.png
Normal file
|
After Width: | Height: | Size: 339 B |
|
Before Width: | Height: | Size: 1017 B After Width: | Height: | Size: 593 B |
6
src/Android/Resources/drawable/slider_thumb.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||||
|
<solid android:color="@color/white"/>
|
||||||
|
<stroke android:width="1dp" android:color="#B5B5B5"/>
|
||||||
|
<size android:width="28dp" android:height="28dp"/>
|
||||||
|
</shape>
|
||||||
|
Before Width: | Height: | Size: 520 B |
|
Before Width: | Height: | Size: 520 B |
|
Before Width: | Height: | Size: 416 B |
|
Before Width: | Height: | Size: 274 B |
40
src/Android/Resources/layout/autofill_listitem.axml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingBottom="5dp"
|
||||||
|
android:paddingTop="5dp"
|
||||||
|
android:paddingLeft="10dp"
|
||||||
|
android:paddingRight="10dp"
|
||||||
|
android:background="@color/lightgray"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/icon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
android:maxWidth="20dp"
|
||||||
|
android:maxHeight="20dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:src="@drawable/login" />
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textColor="@color/black"
|
||||||
|
android:text="Name" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textColor="@color/gray"
|
||||||
|
android:text="Username" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
<string name="AutoFillServiceSummary">
|
||||||
|
Assist with filling username and password fields in other apps and on the web.
|
||||||
|
</string>
|
||||||
<string name="AutoFillServiceDescription">
|
<string name="AutoFillServiceDescription">
|
||||||
To allow bitwarden to auto-fill into other Android apps and on the web through your browser, enable the bitwarden
|
It can be difficult and insecure for users to switch between apps to copy/paste username and password information
|
||||||
accessibility service by tapping the toggle switch above, then press OK on the confirmation pop-up. You can then press
|
from their bitwarden vault.\n\nUsing this accessibility service allows bitwarden to detect and read input fields on
|
||||||
the back button twice to return to the main bitwarden app.
|
your device\'s screen. Whenever bitwarden detects a password field on the screen a notification will appear that allows
|
||||||
|
you to quickly access your bitwarden vault and automatically fill (auto-fill) the correct login information into the
|
||||||
|
necessary fields.
|
||||||
</string>
|
</string>
|
||||||
<string name="MyVault">
|
<string name="MyVault">
|
||||||
My Vault
|
My Vault
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-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:description="@string/AutoFillServiceDescription"
|
android:description="@string/AutoFillServiceDescription"
|
||||||
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged|typeViewFocused"
|
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged|typeViewFocused"
|
||||||
android:accessibilityFeedbackType="feedbackGeneric"
|
android:accessibilityFeedbackType="feedbackGeneric"
|
||||||
|
|||||||
3
src/Android/Resources/xml/autofillservice.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<autofill-service
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" />
|
||||||
@@ -33,14 +33,12 @@ namespace Bit.Android.Services
|
|||||||
private readonly bool _oldAndroid;
|
private readonly bool _oldAndroid;
|
||||||
private readonly ISettings _settings;
|
private readonly ISettings _settings;
|
||||||
private readonly KeyStore _keyStore;
|
private readonly KeyStore _keyStore;
|
||||||
private readonly ISecureStorageService _oldKeyStorageService;
|
|
||||||
|
|
||||||
public AndroidKeyStoreStorageService(ISettings settings)
|
public AndroidKeyStoreStorageService(ISettings settings)
|
||||||
{
|
{
|
||||||
_oldAndroid = Build.VERSION.SdkInt < BuildVersionCodes.M;
|
_oldAndroid = Build.VERSION.SdkInt < BuildVersionCodes.M;
|
||||||
_rsaMode = _oldAndroid ? "RSA/ECB/PKCS1Padding" : "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
|
_rsaMode = _oldAndroid ? "RSA/ECB/PKCS1Padding" : "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
|
||||||
|
|
||||||
_oldKeyStorageService = new KeyStoreStorageService(new char[] { });
|
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
|
|
||||||
_keyStore = KeyStore.GetInstance(AndroidKeyStore);
|
_keyStore = KeyStore.GetInstance(AndroidKeyStore);
|
||||||
@@ -53,8 +51,7 @@ namespace Bit.Android.Services
|
|||||||
public bool Contains(string key)
|
public bool Contains(string key)
|
||||||
{
|
{
|
||||||
return _settings.Contains(string.Format(SettingsFormat, key)) ||
|
return _settings.Contains(string.Format(SettingsFormat, key)) ||
|
||||||
_settings.Contains(string.Format(SettingsFormatV1, key)) ||
|
_settings.Contains(string.Format(SettingsFormatV1, key));
|
||||||
_oldKeyStorageService.Contains(key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Delete(string key)
|
public void Delete(string key)
|
||||||
@@ -92,7 +89,7 @@ namespace Bit.Android.Services
|
|||||||
{
|
{
|
||||||
return App.Utilities.Crypto.AesCbcDecrypt(new App.Models.CipherString(cs), aesKey);
|
return App.Utilities.Crypto.AesCbcDecrypt(new App.Models.CipherString(cs), aesKey);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch
|
||||||
{
|
{
|
||||||
Console.WriteLine("Failed to decrypt from secure storage.");
|
Console.WriteLine("Failed to decrypt from secure storage.");
|
||||||
_settings.Remove(formattedKey);
|
_settings.Remove(formattedKey);
|
||||||
@@ -123,7 +120,7 @@ namespace Bit.Android.Services
|
|||||||
var cipherString = App.Utilities.Crypto.AesCbcEncrypt(dataBytes, aesKey);
|
var cipherString = App.Utilities.Crypto.AesCbcEncrypt(dataBytes, aesKey);
|
||||||
_settings.AddOrUpdateValue(formattedKey, cipherString.EncryptedString);
|
_settings.AddOrUpdateValue(formattedKey, cipherString.EncryptedString);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch
|
||||||
{
|
{
|
||||||
Console.WriteLine("Failed to encrypt to secure storage.");
|
Console.WriteLine("Failed to encrypt to secure storage.");
|
||||||
//Utilities.SendCrashEmail(e);
|
//Utilities.SendCrashEmail(e);
|
||||||
@@ -227,7 +224,7 @@ namespace Bit.Android.Services
|
|||||||
return new App.Models.SymmetricCryptoKey(key);
|
return new App.Models.SymmetricCryptoKey(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch
|
||||||
{
|
{
|
||||||
Console.WriteLine("Cannot get AesKey.");
|
Console.WriteLine("Cannot get AesKey.");
|
||||||
_keyStore.DeleteEntry(KeyAlias);
|
_keyStore.DeleteEntry(KeyAlias);
|
||||||
@@ -297,13 +294,6 @@ namespace Bit.Android.Services
|
|||||||
|
|
||||||
private byte[] TryGetAndMigrate(string key)
|
private byte[] TryGetAndMigrate(string key)
|
||||||
{
|
{
|
||||||
if(_oldKeyStorageService.Contains(key))
|
|
||||||
{
|
|
||||||
var value = _oldKeyStorageService.Retrieve(key);
|
|
||||||
Store(key, value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var formattedKeyV1 = string.Format(SettingsFormatV1, key);
|
var formattedKeyV1 = string.Format(SettingsFormatV1, key);
|
||||||
if(_settings.Contains(formattedKeyV1))
|
if(_settings.Contains(formattedKeyV1))
|
||||||
{
|
{
|
||||||
@@ -331,11 +321,6 @@ namespace Bit.Android.Services
|
|||||||
|
|
||||||
private void CleanupOld(string key)
|
private void CleanupOld(string key)
|
||||||
{
|
{
|
||||||
if(_oldKeyStorageService.Contains(key))
|
|
||||||
{
|
|
||||||
_oldKeyStorageService.Delete(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
var formattedKeyV1 = string.Format(SettingsFormatV1, key);
|
var formattedKeyV1 = string.Format(SettingsFormatV1, key);
|
||||||
if(_settings.Contains(formattedKeyV1))
|
if(_settings.Contains(formattedKeyV1))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,560 +1,42 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using Bit.App;
|
||||||
using Android.App;
|
|
||||||
using Android.Content;
|
|
||||||
using Android.OS;
|
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Utilities;
|
using Plugin.Settings.Abstractions;
|
||||||
using System.Threading;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Android.Gms.Gcm.Iid;
|
|
||||||
using Android.Gms.Gcm;
|
|
||||||
using Java.IO;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Android.Support.V4.App;
|
|
||||||
using Android.Media;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Bit.Android.Services
|
namespace Bit.Android.Services
|
||||||
{
|
{
|
||||||
public class AndroidPushNotificationService : IPushNotificationService
|
public class AndroidPushNotificationService : IPushNotificationService
|
||||||
{
|
{
|
||||||
private const string GcmPreferencesKey = "GCMPreferences";
|
private readonly IPushNotificationListener _pushNotificationListener;
|
||||||
private const string Tag = "PushNotification";
|
private readonly ISettings _settings;
|
||||||
|
|
||||||
internal static IPushNotificationListener Listener { get; set; }
|
public AndroidPushNotificationService(
|
||||||
public string Token => GetRegistrationId();
|
IPushNotificationListener pushNotificationListener,
|
||||||
|
ISettings settings)
|
||||||
|
{
|
||||||
|
_pushNotificationListener = pushNotificationListener;
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Token => _settings.GetValueOrDefault(Constants.PushCurrentToken, null);
|
||||||
|
|
||||||
public void Register()
|
public void Register()
|
||||||
{
|
{
|
||||||
|
var registeredToken = _settings.GetValueOrDefault(Constants.PushRegisteredToken, null);
|
||||||
System.Diagnostics.Debug.WriteLine(
|
if(!string.IsNullOrWhiteSpace(registeredToken) && registeredToken != Token)
|
||||||
$"{PushNotificationContants.DomainName} - Register - Registering push notifications");
|
|
||||||
|
|
||||||
if(string.IsNullOrEmpty(CrossPushNotification.SenderId))
|
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine(
|
_pushNotificationListener.OnRegistered(registeredToken, Device.Android);
|
||||||
$"{PushNotificationContants.DomainName} - Register - SenderId is missing.");
|
|
||||||
|
|
||||||
CrossPushNotification.PushNotificationListener.OnError(
|
|
||||||
$"{PushNotificationContants.DomainName} - Register - Sender Id is missing.", Device.Android);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine(
|
_settings.AddOrUpdateValue(Constants.PushLastRegistrationDate, DateTime.UtcNow);
|
||||||
$"{PushNotificationContants.DomainName} - Register - Registering for Push Notifications");
|
|
||||||
|
|
||||||
ThreadPool.QueueUserWorkItem(state =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Intent intent = new Intent(global::Android.App.Application.Context,
|
|
||||||
typeof(PushNotificationRegistrationIntentService));
|
|
||||||
|
|
||||||
global::Android.App.Application.Context.StartService(intent);
|
|
||||||
}
|
|
||||||
catch(Exception ex)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"{Tag} - Error : {ex.Message}");
|
|
||||||
CrossPushNotification.PushNotificationListener.OnError($"{Tag} - Register - {ex.Message}",
|
|
||||||
Device.Android);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unregister()
|
public void Unregister()
|
||||||
{
|
{
|
||||||
ThreadPool.QueueUserWorkItem(state =>
|
// Do we ever need to unregister?
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine(
|
|
||||||
$"{PushNotificationContants.DomainName} - Unregister - Unregistering push notifications");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var instanceID = InstanceID.GetInstance(global::Android.App.Application.Context);
|
|
||||||
instanceID.DeleteToken(CrossPushNotification.SenderId, GoogleCloudMessaging.InstanceIdScope);
|
|
||||||
|
|
||||||
CrossPushNotification.PushNotificationListener.OnUnregistered(Device.Android);
|
|
||||||
StoreRegistrationId(global::Android.App.Application.Context, string.Empty);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch(IOException ex)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"{Tag} - Error : {ex.Message}");
|
|
||||||
CrossPushNotification.PushNotificationListener.OnError(
|
|
||||||
$"{Tag} - Unregister - {ex.Message}", Device.Android);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetRegistrationId()
|
|
||||||
{
|
|
||||||
var context = global::Android.App.Application.Context;
|
|
||||||
var prefs = GetGCMPreferences(context);
|
|
||||||
var registrationId = prefs.GetString(PushNotificationContants.Token, string.Empty);
|
|
||||||
if(string.IsNullOrEmpty(registrationId))
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"{PushNotificationContants.DomainName} - Registration not found.");
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if app was updated; if so, it must clear the registration ID
|
|
||||||
// since the existing registration ID is not guaranteed to work with
|
|
||||||
// the new app version.
|
|
||||||
var registeredVersion = prefs.GetInt(PushNotificationContants.AppVersion, Java.Lang.Integer.MinValue);
|
|
||||||
var currentVersion = GetAppVersion(context);
|
|
||||||
if(registeredVersion != currentVersion)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"{PushNotificationContants.DomainName} - App version changed.");
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
return registrationId;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static ISharedPreferences GetGCMPreferences(Context context)
|
|
||||||
{
|
|
||||||
return context.GetSharedPreferences(GcmPreferencesKey, FileCreationMode.Private);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static int GetAppVersion(Context context)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var packageInfo = context.PackageManager.GetPackageInfo(context.PackageName, 0);
|
|
||||||
return packageInfo.VersionCode;
|
|
||||||
}
|
|
||||||
catch(global::Android.Content.PM.PackageManager.NameNotFoundException e)
|
|
||||||
{
|
|
||||||
// should never happen
|
|
||||||
throw new Java.Lang.RuntimeException("Could not get package name: " + e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void StoreRegistrationId(Context context, string regId)
|
|
||||||
{
|
|
||||||
var prefs = GetGCMPreferences(context);
|
|
||||||
var appVersion = GetAppVersion(context);
|
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine(
|
|
||||||
$"{PushNotificationContants.DomainName} - Saving token on app version {appVersion}");
|
|
||||||
|
|
||||||
var editor = prefs.Edit();
|
|
||||||
editor.PutString(PushNotificationContants.Token, regId);
|
|
||||||
editor.PutInt(PushNotificationContants.AppVersion, appVersion);
|
|
||||||
editor.Commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Service(Exported = false)]
|
|
||||||
public class PushNotificationRegistrationIntentService : IntentService
|
|
||||||
{
|
|
||||||
private const string Tag = "PushNotificationRegistationIntentService";
|
|
||||||
private string[] _topics = new string[] { "global" };
|
|
||||||
private readonly object _syncLock = new object();
|
|
||||||
|
|
||||||
protected override void OnHandleIntent(Intent intent)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var extras = intent.Extras;
|
|
||||||
lock(_syncLock)
|
|
||||||
{
|
|
||||||
var instanceID = InstanceID.GetInstance(global::Android.App.Application.Context);
|
|
||||||
var token = instanceID.GetToken(CrossPushNotification.SenderId,
|
|
||||||
GoogleCloudMessaging.InstanceIdScope, null);
|
|
||||||
CrossPushNotification.PushNotificationListener.OnRegistered(token, Device.Android);
|
|
||||||
AndroidPushNotificationService.StoreRegistrationId(global::Android.App.Application.Context, token);
|
|
||||||
SubscribeTopics(token);
|
|
||||||
System.Diagnostics.Debug.WriteLine($"{token} - Device registered, registration ID={Tag}");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch(Exception ex)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"{ex.Message} - Error : {Tag}");
|
|
||||||
CrossPushNotification.PushNotificationListener.OnError(
|
|
||||||
$"{ex.ToString()} - Register - {Tag}", Device.Android);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SubscribeTopics(string token)
|
|
||||||
{
|
|
||||||
var pubSub = GcmPubSub.GetInstance(this);
|
|
||||||
foreach(var topic in _topics)
|
|
||||||
{
|
|
||||||
pubSub.Subscribe(token, "/topics/" + topic, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Service(Exported = false)]
|
|
||||||
[IntentFilter(new string[] { "com.google.android.gms.iid.InstanceID" })]
|
|
||||||
public class PushNotificationInstanceIDListenerService : InstanceIDListenerService
|
|
||||||
{
|
|
||||||
private const string Tag = "PushNotificationInstanceIDLS";
|
|
||||||
|
|
||||||
public override void OnTokenRefresh()
|
|
||||||
{
|
|
||||||
base.OnTokenRefresh();
|
|
||||||
ThreadPool.QueueUserWorkItem(state =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var intent = new Intent(global::Android.App.Application.Context,
|
|
||||||
typeof(PushNotificationRegistrationIntentService));
|
|
||||||
global::Android.App.Application.Context.StartService(intent);
|
|
||||||
}
|
|
||||||
catch(Exception ex)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"{ex.Message} - Error : {Tag}");
|
|
||||||
CrossPushNotification.PushNotificationListener.OnError(
|
|
||||||
$"{ex.ToString()} - Register - {Tag}", Device.Android);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Service(Exported = false, Name = "pushnotification.plugin.PushNotificationGcmListener")]
|
|
||||||
[IntentFilter(new string[] { "com.google.android.c2dm.intent.RECEIVE" },
|
|
||||||
Categories = new string[] { "com.x8bit.bitwarden" })]
|
|
||||||
public class PushNotificationGcmListener : GcmListenerService
|
|
||||||
{
|
|
||||||
public override void OnMessageReceived(string from, Bundle extras)
|
|
||||||
{
|
|
||||||
if(extras != null && !extras.IsEmpty)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine(
|
|
||||||
$"{PushNotificationContants.DomainName} - GCM Listener - Push Received");
|
|
||||||
|
|
||||||
var parameters = new Dictionary<string, object>();
|
|
||||||
var values = new JObject();
|
|
||||||
foreach(var key in extras.KeySet())
|
|
||||||
{
|
|
||||||
var value = extras.Get(key).ToString();
|
|
||||||
|
|
||||||
if(ValidateJSON(value))
|
|
||||||
{
|
|
||||||
values.Add(key, JObject.Parse(value));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
values.Add(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
parameters.Add(key, extras.Get(key));
|
|
||||||
System.Diagnostics.Debug.WriteLine(
|
|
||||||
$"{PushNotificationContants.DomainName} - GCM Listener - Push Params {key} : {extras.Get(key)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
var context = global::Android.App.Application.Context;
|
|
||||||
CrossPushNotification.PushNotificationListener.OnMessage(values, Device.Android);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var notifyId = 0;
|
|
||||||
var title = context.ApplicationInfo.LoadLabel(context.PackageManager);
|
|
||||||
var message = string.Empty;
|
|
||||||
var tag = string.Empty;
|
|
||||||
|
|
||||||
if(!string.IsNullOrEmpty(CrossPushNotification.NotificationContentTextKey) &&
|
|
||||||
parameters.ContainsKey(CrossPushNotification.NotificationContentTextKey))
|
|
||||||
{
|
|
||||||
message = parameters[CrossPushNotification.NotificationContentTextKey].ToString();
|
|
||||||
}
|
|
||||||
else if(parameters.ContainsKey(PushNotificationContants.Alert))
|
|
||||||
{
|
|
||||||
message = parameters[PushNotificationContants.Alert].ToString();
|
|
||||||
}
|
|
||||||
else if(parameters.ContainsKey(PushNotificationContants.Message))
|
|
||||||
{
|
|
||||||
message = parameters[PushNotificationContants.Message].ToString();
|
|
||||||
}
|
|
||||||
else if(parameters.ContainsKey(PushNotificationContants.Subtitle))
|
|
||||||
{
|
|
||||||
message = parameters[PushNotificationContants.Subtitle].ToString();
|
|
||||||
}
|
|
||||||
else if(parameters.ContainsKey(PushNotificationContants.Text))
|
|
||||||
{
|
|
||||||
message = parameters[PushNotificationContants.Text].ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!string.IsNullOrEmpty(CrossPushNotification.NotificationContentTitleKey) &&
|
|
||||||
parameters.ContainsKey(CrossPushNotification.NotificationContentTitleKey))
|
|
||||||
{
|
|
||||||
title = parameters[CrossPushNotification.NotificationContentTitleKey].ToString();
|
|
||||||
}
|
|
||||||
else if(parameters.ContainsKey(PushNotificationContants.Title))
|
|
||||||
{
|
|
||||||
if(!string.IsNullOrEmpty(message))
|
|
||||||
{
|
|
||||||
title = parameters[PushNotificationContants.Title].ToString();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
message = parameters[PushNotificationContants.Title].ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(string.IsNullOrEmpty(message))
|
|
||||||
{
|
|
||||||
var data = (
|
|
||||||
!string.IsNullOrEmpty(CrossPushNotification.NotificationContentDataKey) &&
|
|
||||||
values[CrossPushNotification.NotificationContentDataKey] != null) ?
|
|
||||||
values[CrossPushNotification.NotificationContentDataKey] :
|
|
||||||
values[PushNotificationContants.Data];
|
|
||||||
|
|
||||||
if(data != null)
|
|
||||||
{
|
|
||||||
if(!string.IsNullOrEmpty(CrossPushNotification.NotificationContentTextKey) &&
|
|
||||||
data[CrossPushNotification.NotificationContentTextKey] != null)
|
|
||||||
{
|
|
||||||
message = data[CrossPushNotification.NotificationContentTextKey].ToString();
|
|
||||||
}
|
|
||||||
else if(data[PushNotificationContants.Alert] != null)
|
|
||||||
{
|
|
||||||
message = data[PushNotificationContants.Alert].ToString();
|
|
||||||
}
|
|
||||||
else if(data[PushNotificationContants.Message] != null)
|
|
||||||
{
|
|
||||||
message = data[PushNotificationContants.Message].ToString();
|
|
||||||
}
|
|
||||||
else if(data[PushNotificationContants.Subtitle] != null)
|
|
||||||
{
|
|
||||||
message = data[PushNotificationContants.Subtitle].ToString();
|
|
||||||
}
|
|
||||||
else if(data[PushNotificationContants.Text] != null)
|
|
||||||
{
|
|
||||||
message = data[PushNotificationContants.Text].ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!string.IsNullOrEmpty(CrossPushNotification.NotificationContentTitleKey) &&
|
|
||||||
data[CrossPushNotification.NotificationContentTitleKey] != null)
|
|
||||||
{
|
|
||||||
title = data[CrossPushNotification.NotificationContentTitleKey].ToString();
|
|
||||||
}
|
|
||||||
else if(data[PushNotificationContants.Title] != null)
|
|
||||||
{
|
|
||||||
if(!string.IsNullOrEmpty(message))
|
|
||||||
{
|
|
||||||
title = data[PushNotificationContants.Title].ToString();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
message = data[PushNotificationContants.Title].ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(parameters.ContainsKey(PushNotificationContants.Id))
|
|
||||||
{
|
|
||||||
var str = parameters[PushNotificationContants.Id].ToString();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
notifyId = Convert.ToInt32(str);
|
|
||||||
}
|
|
||||||
catch(Exception)
|
|
||||||
{
|
|
||||||
// Keep the default value of zero for the notify_id, but log the conversion problem.
|
|
||||||
System.Diagnostics.Debug.WriteLine("Failed to convert {0} to an integer", str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(parameters.ContainsKey(PushNotificationContants.Tag))
|
|
||||||
{
|
|
||||||
tag = parameters[PushNotificationContants.Tag].ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!parameters.ContainsKey(PushNotificationContants.Silent) ||
|
|
||||||
!System.Boolean.Parse(parameters[PushNotificationContants.Silent].ToString()))
|
|
||||||
{
|
|
||||||
if(CrossPushNotification.PushNotificationListener.ShouldShowNotification())
|
|
||||||
{
|
|
||||||
CreateNotification(title, message, notifyId, tag, extras);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch(Java.Lang.Exception ex)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine(ex.ToString());
|
|
||||||
}
|
|
||||||
catch(Exception ex1)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine(ex1.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateNotification(string title, string message, int notifyId, string tag, Bundle extras)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine(
|
|
||||||
$"{PushNotificationContants.DomainName} - PushNotification - Message {title} : {message}");
|
|
||||||
|
|
||||||
NotificationCompat.Builder builder = null;
|
|
||||||
var context = global::Android.App.Application.Context;
|
|
||||||
|
|
||||||
if(CrossPushNotification.SoundUri == null)
|
|
||||||
{
|
|
||||||
CrossPushNotification.SoundUri = RingtoneManager.GetDefaultUri(RingtoneType.Notification);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if(CrossPushNotification.IconResource == 0)
|
|
||||||
{
|
|
||||||
CrossPushNotification.IconResource = context.ApplicationInfo.Icon;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var name = context.Resources.GetResourceName(CrossPushNotification.IconResource);
|
|
||||||
if(name == null)
|
|
||||||
{
|
|
||||||
CrossPushNotification.IconResource = context.ApplicationInfo.Icon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(global::Android.Content.Res.Resources.NotFoundException ex)
|
|
||||||
{
|
|
||||||
CrossPushNotification.IconResource = context.ApplicationInfo.Icon;
|
|
||||||
System.Diagnostics.Debug.WriteLine(ex.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
var resultIntent = context.PackageManager.GetLaunchIntentForPackage(context.PackageName);
|
|
||||||
if(extras != null)
|
|
||||||
{
|
|
||||||
resultIntent.PutExtras(extras);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a PendingIntent; we're only using one PendingIntent (ID = 0):
|
|
||||||
const int pendingIntentId = 0;
|
|
||||||
var resultPendingIntent = PendingIntent.GetActivity(context, pendingIntentId,
|
|
||||||
resultIntent, PendingIntentFlags.OneShot);
|
|
||||||
|
|
||||||
// Build the notification
|
|
||||||
builder = new NotificationCompat.Builder(context)
|
|
||||||
.SetAutoCancel(true) // dismiss the notification from the notification area when the user clicks on it
|
|
||||||
.SetContentIntent(resultPendingIntent) // start up this activity when the user clicks the intent.
|
|
||||||
.SetContentTitle(title) // Set the title
|
|
||||||
.SetSound(CrossPushNotification.SoundUri)
|
|
||||||
.SetSmallIcon(CrossPushNotification.IconResource) // This is the icon to display
|
|
||||||
.SetContentText(message); // the message to display.
|
|
||||||
|
|
||||||
if(Build.VERSION.SdkInt >= BuildVersionCodes.JellyBean)
|
|
||||||
{
|
|
||||||
// Using BigText notification style to support long message
|
|
||||||
var style = new NotificationCompat.BigTextStyle();
|
|
||||||
style.BigText(message);
|
|
||||||
builder.SetStyle(style);
|
|
||||||
}
|
|
||||||
|
|
||||||
var notificationManager = (NotificationManager)context.GetSystemService(NotificationService);
|
|
||||||
notificationManager.Notify(tag, notifyId, builder.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool ValidateJSON(string s)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
JObject.Parse(s);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch(JsonReaderException ex)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine(ex.ToString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[BroadcastReceiver(Exported = true, Permission = "com.google.android.c2dm.permission.SEND")]
|
|
||||||
[IntentFilter(new string[] { "com.google.android.c2dm.intent.RECEIVE" },
|
|
||||||
Categories = new string[] { "com.x8bit.bitwarden" })]
|
|
||||||
public class PushNotificationsReceiver : GcmReceiver
|
|
||||||
{ }
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
public class AndroidPushService : Service
|
|
||||||
{
|
|
||||||
public override void OnCreate()
|
|
||||||
{
|
|
||||||
base.OnCreate();
|
|
||||||
System.Diagnostics.Debug.WriteLine("Push Notification Service - Created");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine("Push Notification Service - Started");
|
|
||||||
return StartCommandResult.Sticky;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IBinder OnBind(Intent intent)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine("Push Notification Service - Binded");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnDestroy()
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine("Push Notification Service - Destroyed");
|
|
||||||
base.OnDestroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class CrossPushNotification
|
|
||||||
{
|
|
||||||
private static Lazy<IPushNotificationService> Implementation = new Lazy<IPushNotificationService>(
|
|
||||||
() => new AndroidPushNotificationService(),
|
|
||||||
LazyThreadSafetyMode.PublicationOnly);
|
|
||||||
public static bool IsInitialized => PushNotificationListener != null;
|
|
||||||
public static IPushNotificationListener PushNotificationListener { get; private set; }
|
|
||||||
|
|
||||||
public static string SenderId { get; set; }
|
|
||||||
public static string NotificationContentTitleKey { get; set; }
|
|
||||||
public static string NotificationContentTextKey { get; set; }
|
|
||||||
public static string NotificationContentDataKey { get; set; }
|
|
||||||
public static int IconResource { get; set; }
|
|
||||||
public static global::Android.Net.Uri SoundUri { get; set; }
|
|
||||||
|
|
||||||
public static void Initialize<T>(T listener, string senderId) where T : IPushNotificationListener
|
|
||||||
{
|
|
||||||
SenderId = senderId;
|
|
||||||
|
|
||||||
if(PushNotificationListener == null)
|
|
||||||
{
|
|
||||||
PushNotificationListener = listener;
|
|
||||||
System.Diagnostics.Debug.WriteLine("PushNotification plugin initialized.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine("PushNotification plugin already initialized.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Initialize<T>(string senderId) where T : IPushNotificationListener, new()
|
|
||||||
{
|
|
||||||
Initialize(new T(), senderId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IPushNotificationService Current
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if(!IsInitialized)
|
|
||||||
{
|
|
||||||
throw new Exception("Not initialized.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var ret = Implementation.Value;
|
|
||||||
if(ret == null)
|
|
||||||
{
|
|
||||||
throw new Exception("Not in PCL");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||