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

Compare commits

..

214 Commits

Author SHA1 Message Date
mpbw2
b543459f8d Link to legacy docs in readme (#3472) 2025-05-14 19:06:43 +01:00
mpbw2
d0609024df Adding legacy contributing docs (#3471)
Co-authored-by: Matt Portune <mportune@macbook-work.lan>
2025-05-14 12:01:24 -04:00
Álison Fernandes
a798ae0761 [PM-7014] Track MessagePack fork and turn off CI / recurring workflows (#3456)
* Remove messagepack submodule

* Migrate MessagePack fork to this repo

* Disable build CI

* Disable Renovate

* Disable Crowdin recurring job

* Address workflow linter errors
2025-01-06 18:59:54 -03:00
Vince Grassia
7f8b19a0a7 BRE-540 - Update Crowdin Sync workflow to use GitHub App (#3454) 2025-01-06 16:55:08 -05:00
github-actions[bot]
e50175abe1 Autosync the updated translations (#3446)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-12-13 14:30:02 +00:00
github-actions[bot]
8f435b2f26 Autosync the updated translations (#3445)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-12-06 11:02:38 +00:00
github-actions[bot]
cdbdec8943 Autosync the updated translations (#3443)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-11-29 13:00:47 +00:00
github-actions[bot]
3d42da2a29 Autosync the updated translations (#3442)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-11-22 11:15:15 +00:00
github-actions[bot]
16303e581b Autosync the updated translations (#3439)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-11-15 12:37:46 +00:00
github-actions[bot]
bc4b03b994 Autosync the updated translations (#3438)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-11-08 00:45:53 +00:00
github-actions[bot]
484eb03eca Autosync the updated translations (#3435)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-11-01 09:55:55 +01:00
github-actions[bot]
d59f80776c Autosync the updated translations (#3431)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-10-25 11:40:08 +02:00
Opeyemi
44849cb0a2 [BRE-365] - Enable Android and F-droid build (#3430) 2024-10-22 11:42:16 -04:00
Opeyemi
65794c02b1 [BRE-372] - Clean up document start (#3427) 2024-10-21 15:22:35 -03:00
Álison Fernandes
8bc90b9649 [PM-13677] Disable WatchOS (#3426)
* Disable watchos

* enable testflight validation

* Revert "enable testflight validation"

This reverts commit 75012f0854.
2024-10-18 10:29:03 -03:00
github-actions[bot]
466bb81cb7 Autosync the updated translations (#3423)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-10-18 09:22:56 +02:00
Michał Chęciński
c957e46ff8 [BRE-101] Remove dept-devops from CODEOWNERS (#3289)
* BRE-101: Remove dept-devops from CODEOWNERS

* Fix

* Update CODEOWNERS for CI/CD Partnership Phase 2

* Update .github/CODEOWNERS

Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>

* Fix

---------

Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>
2024-10-17 13:01:25 -03:00
Opeyemi
d2d442253b [BRE-365] - Update Apple Store Creds to Team Issuer Credential (#3421)
* update Apple store creds to team issuer credential

* update

* Update key id for altool

---------

Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>
2024-10-14 16:20:23 +01:00
Bitwarden DevOps
f1a2714e8a Bumped version to 2024.10.111 (#3417) 2024-10-11 16:13:06 +00:00
Opeyemi
9083bb1ab8 disable android build (#3416) 2024-10-11 10:02:56 -06:00
Bitwarden DevOps
a69420257d Bumped version to 2024.10.0 (#3415) 2024-10-11 14:24:32 +00:00
Opeyemi
ef81365ae0 [BRE-365] - Disable OS build (#3414)
* disable ios build
2024-10-11 16:12:29 +02:00
github-actions[bot]
81ad6b3cc4 Autosync the updated translations (#3413)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-10-11 07:53:38 +00:00
Álison Fernandes
5ca0167dbb [PM-13357] Disable Cipher Key Encryption (#3408)
* Enables the cipher key encryption flag

* Revert "Enables the cipher key encryption flag"

This reverts commit f933c7d69f.
2024-10-10 12:38:19 +02:00
Álison Fernandes
e1f3f3dfd9 [PM-13356] Fix iOS build by pinning dotnet and MAUI version (#3410)
* Pinning MauiVersion hardcoded to 8.0.7 to avoid using the MauiVersion variable. Also pinning .NET version to use to 8.0.7 so it's the same one as the latest public release.

* Pinned dotnet version to 8.0.303 which was the latest one of the latest public release

* Added global json to pin .NET version to use and added diagnostic logs on dotnet publish, also commented other workflows.

* Set TargetPlatformVersion in Directory Build props

* test moving global.json

* Check out the repo first so global.json is available

* Remove workloadVersion from global.json

* pin maui install version

* Try workload restore

* Adding workload restore

* Use ios workload instead

* Set specific MAUI workload to install to 8.0.402

* Fix MAUI workload install to 8.0.300

* Add step to remove dotnet

* Update sdk / maui to 8.0.402

* Bumped xcode to 15.4

* Revert build.yml comments

* Revert csproj and build.props

* Remove diag flag

* Android / F-droid: Install pinned version of maui workload instead of latest

* Android / F-Droid: checkout repo earlier in the build process so global.json is used

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2024-10-10 11:51:28 +02:00
github-actions[bot]
53a5a9caaa Autosync the updated translations (#3403)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-10-04 10:32:25 +02:00
github-actions[bot]
94381eebf0 Autosync the updated translations (#3395)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-09-27 08:47:17 +00:00
github-actions[bot]
b43eaf4a0a Autosync the updated translations (#3390)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-09-20 11:17:59 +00:00
github-actions[bot]
b89d8214d2 Autosync the updated translations (#3385)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-09-13 09:50:40 +00:00
github-actions[bot]
902ce67867 Autosync the updated translations (#3384)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-08-30 10:22:37 +00:00
github-actions[bot]
062b15aae9 Autosync the updated translations (#3382)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-08-23 10:45:37 +02:00
github-actions[bot]
dcf441afd7 Autosync the updated translations (#3379)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-08-16 11:59:32 +02:00
github-actions[bot]
c987840936 Autosync the updated translations (#3370)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-08-09 10:02:10 +00:00
Vince Grassia
6e93983711 Fix F-Droid build and release process (#3374) 2024-08-05 16:07:43 -03:00
Bitwarden DevOps
70de8245fe Bumped version to 2024.7.2 (#3364) 2024-07-26 14:52:38 +00:00
github-actions[bot]
77d9920aab Autosync the updated translations (#3362)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-07-26 10:37:21 +00:00
github-actions[bot]
b908f1c721 Autosync the updated translations (#3359)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-07-19 19:21:08 +00:00
GitHubArchive
46a249de46 [deps]: Update gh minor (#3298)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-16 15:06:47 -04:00
Bitwarden DevOps
837628e9bc Bumped version to 2024.7.1 (#3355) 2024-07-16 14:31:41 +00:00
Vince Grassia
3262fdc9ec Fix build pipeline MAUI version (#3354) 2024-07-16 14:51:29 +02:00
github-actions[bot]
c763b0072b Autosync the updated translations (#3343)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-07-12 11:27:59 +02:00
Carlos Gonçalves
b81ce44cc7 pm-9483 Update model attachments (#3344) 2024-07-08 15:27:11 +01:00
Álison Fernandes
8a43bb4655 [PM-9340] Updates the Bugs template to reference the new native apps (#3336)
* Update bug.yml to reference the new native apps

* Replacing table with a GitHub Alert.

https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts

* Added Alert to README

* Fix template syntax error

* Improved bug templated and added config links

* Update .github/ISSUE_TEMPLATE/config.yml

Co-authored-by: Matt Bishop <mbishop@bitwarden.com>

---------

Co-authored-by: Matt Bishop <mbishop@bitwarden.com>
2024-07-03 09:26:39 -03:00
Vince Grassia
44f8901519 Fix increment version steps (#3342) 2024-07-02 19:02:31 -07:00
Bitwarden DevOps
463d837fa8 Bumped version to 2024.7.0 (#3341) 2024-07-01 17:15:59 +00:00
Matt Czech
ad6ffad5d1 [PM-9043] BIT-2356: Update casing of Bitwarden Help Center string (#3305) 2024-06-19 21:00:59 +00:00
Federico Maccaroni
9edcc8b4f7 [AC-2788] Remove unassigned items feature flag and logic (#3300) 2024-06-19 08:57:04 -03:00
Álison Fernandes
cf5c5aa114 [PM-8392] Enables the Cipher Key Encryption preprocessor define (#3266) 2024-06-18 13:41:49 +01:00
github-actions[bot]
36c7beb1e6 Autosync Crowdin Translations (#3297)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-06-14 08:23:03 +00:00
Bitwarden DevOps
f6d798af94 Bump version to 2024.6.1 (#3296) 2024-06-13 15:50:57 +00:00
Michał Chęciński
08ed70d5b1 Fix release (#3294) 2024-06-13 08:40:18 -07:00
André Bispo
33e0187460 [PM-8814] Remove EU feature flag (#3291) 2024-06-12 14:32:04 +01:00
github-actions[bot]
2b78859d06 Autosync Crowdin Translations (#3283)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-06-07 06:12:26 +00:00
Federico Maccaroni
81205154c4 [PM-8666] Fix Password Autofill from Quick Type on iOS < 17 (#3282) 2024-06-06 16:30:51 -03:00
Michał Chęciński
14d2660b61 [BRE-94] Replace dawidd6/action-download-artifact with bitwarden/gh-actions/download-artifacts (#3278) 2024-06-04 16:25:56 +02:00
renovate[bot]
bad5673724 [deps]: Update crowdin/github-action action to v2 (#3271)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-03 16:51:08 -04:00
Bitwarden DevOps
67b76a777e Bump version to 2024.6.0 (#3274) 2024-06-03 18:33:00 +00:00
github-actions[bot]
02f5936fce Autosync Crowdin Translations (#3269)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-05-31 06:15:27 +00:00
github-actions[bot]
fc208b08d7 Autosync the updated translations (#3267)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-05-29 20:07:32 +00:00
Alex Urbina
f165135147 BRE-87 ADD: Slack notification enable feature and check in version-bump workflow (#3264) 2024-05-27 15:33:11 -06:00
github-actions[bot]
d458f17ad6 Autosync the updated translations (#3257)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-05-24 12:09:19 +00:00
Alex Urbina
c3bd4b84b1 BRE-40 ADD: step to report upcoming release version to Slack (#3247) 2024-05-21 11:15:00 -06:00
renovate[bot]
0af78d0e03 [deps]: Update gh minor (#3252)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-21 11:06:29 -04:00
github-actions[bot]
e86a01a7db Autosync the updated translations (#3255)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-05-20 08:33:29 +00:00
github-actions[bot]
e16074a73e Autosync the updated translations (#3250)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-05-17 10:36:01 +00:00
Vince Grassia
a333e72448 Update F-Droid release process (#3249) 2024-05-17 00:15:17 -03:00
Bitwarden DevOps
ffb7b3b8ac Bumped version to 2024.5.2 (#3248) 2024-05-16 22:33:03 +00:00
Federico Maccaroni
c8d0db9f31 PM-8051 Fix 2FA launch from iOS autofill, was missing passing appOptions to the App (#3244) 2024-05-15 13:25:27 -03:00
Federico Maccaroni
8566f5c00a PM-8051 Fix WebAuthenticator getting the Window properly on iOS with the workaround to use Share app window when coming from iOS extension (#3239) 2024-05-13 14:26:37 -03:00
renovate[bot]
b65f18d8e2 [deps]: Update gh minor (#3235)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-13 09:23:54 -04:00
github-actions[bot]
7a3816007b Autosync the updated translations (#3233)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-05-13 12:34:45 +00:00
Federico Maccaroni
477b1cca44 [PM-7255] Fix autofill cancelling the request producing inconsistent behavior (#3230)
* PM-7255 Fix autofill cancelling the request on password autofill because of wrong safeguard

* PM-7255 Clear code no longer used
2024-05-08 16:54:00 -03:00
Bitwarden DevOps
dee9524b2c Bumped version to 2024.5.1 (#3227) 2024-05-06 22:37:03 +00:00
renovate[bot]
f5572511c6 [deps]: Update nuget/setup-nuget action to v2 (#3222)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-06 10:18:57 -04:00
renovate[bot]
9a17da009c [deps]: Update ubuntu to v22 (#3224)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-06 10:18:38 -04:00
Álison Fernandes
b5443c79d2 [PM-7922] Updating labeler.yml to the new format required by V5 (#3219) 2024-05-04 15:51:16 +01:00
Joseph Flinn
9dc620b492 Revert "Bumped version to 2024.5.1 (#3217)" (#3218)
This reverts commit 1f966e6cbe.
2024-05-03 13:02:44 -07:00
Bitwarden DevOps
1f966e6cbe Bumped version to 2024.5.1 (#3217) 2024-05-03 19:37:30 +00:00
Bitwarden DevOps
a4fa03656e Bumped version to 2024.5.0 (#3216) 2024-05-03 19:35:24 +00:00
Federico Maccaroni
12385d9add [PM-7877] Added loading dialog when unlocking with PIN (#3215)
* PM-7877 Added loading dialog when unlocking with PIN

* PM-7877 Added exception logging on unlock with PIN
2024-05-03 14:22:46 -03:00
github-actions[bot]
13ca0fd4cb Autosync the updated translations (#3213)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-05-03 14:06:00 +00:00
Federico Maccaroni
0b28b954fe PM-7855 Fix null check to _appOptions to avoid NRE detected on AppCenter (#3209) 2024-05-02 20:07:52 -03:00
André Bispo
e4841bb322 [PM-7869] Add AppOptions to decryption options navigation (#3210) 2024-05-02 17:41:32 +01:00
Federico Maccaroni
98621341a2 PM-7698 Fix crash because the catch logic was wrong and it was allowing certain exception to bubble up and explode, so move the if internally. (#3208) 2024-05-01 10:52:52 -03:00
renovate[bot]
2023fe6644 [deps]: Update macos to v14 (#3202)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-29 10:23:30 -04:00
renovate[bot]
567a23e29f [deps]: Update actions/checkout action to v4.1.4 (#3200)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-29 09:59:50 -04:00
André Bispo
8b65d99442 [PM-7960] Revert change from passkey provider pr (#3196)
* [PM-7690] Move UI thread invocation to viewmodel command
2024-04-29 14:53:07 +01:00
Federico Maccaroni
f80ec1b221 PM-7746 Added specific validation messages for (non) privileged apps validation on Fido2 flows. Also fixed typo on "privileged" and updated UT (#3198) 2024-04-26 13:59:03 -03:00
André Bispo
ba1183234b [PM-7690] Fix login with master password vault unlock after SSO TDE decryption options (#3192) 2024-04-26 13:23:23 +01:00
github-actions[bot]
5946af9eec Autosync the updated translations (#3194)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-04-26 12:08:27 +00:00
Federico Maccaroni
b091051633 [PM-5894] Implement Passkey Providers (#3173)
* PM-3349 PM-3350 Added the partial MAUI Community Toolkit implementation for TouchEffect. This is a temporary solution until they finalize this and add it to their nuget package.
This allows implementing the LongPressCommand in AccountSwitchingOverlay and also have the "Ripple effect" animation when touching an item in Android

* PM-3349 PM-3350 Changed SendViewCell and its binding to be directly against the ViewModel

* PM-3350 Fixed iOS Share extension lazy views loading and an issue with the avatar loading. Also discovered issue with TapGestureRecognizer not working on MAUI Embedding

* PM-3350 Fixed iOS Extensions navigation to several pages and improved avoiding duplicate calls to OnNavigatedTo

* PM-3350 Updated PCL Crypto to latest alpha version to fix "Dll not found NCrypt" issue

* PM-3350 Removed workaround for iOS issue with Avatar icon as it's now fixed in latest .Net8 release.

* PM-3349 PM-3350 Removed AsyncCommand "wrapper" and added AsyncRelayCommand directly in all ViewModels that were using the other one.

* PM-3350 Added watchOS app to main project and fixed some csproj conditions for runtime identifiers on iOS.

* PM-3350 Fixed/Updated all MAUI-Migration TODOs

* PM-3350 Fixed account toolbar item and TitleView on SendAddOnlyPage, also removed comments on AvatarImageSource given the workaround is not needed anymore to draw the image successfully.

* PM-3350 Updated AppCenter package to latest version 5.0.3 and updated some things into MAUI style

* PM-3350 Added workaround for iOS Avatar icon again.

* PM-3349 Added workaround for Android to avoid issues with setting MainPage when app is in background. They are now kept on a Queue to be executed after the app has resumed.
Updated some things on App.xaml.cs to the new MAUI style

* PM-3349 PM-3350 Fixed issue where creating an account with weak/exposed password would get stuck after the Captcha (if a captcha is shown)
Changed App.xaml.cs NavigateImpl to be private

* PM-3349 Started to configure build.yml for MAUI Android

* PM-3349 build.yml update paths for MAUI Android

* PM-3349 build.yml commented verify format and just set qa as variant on MAUI Android for faster checks on CI

* PM-3349 PM-3350 build.cake updated paths

* PM-3349 build.yml updated env helpers variables and set specific csproj to build on Android so not to build iOS extensions

* PM-3349 build.yml add Android "prod" variant

* PM-3350 build.yml updated iOS build and ignore Android build to try the CI faster

* PM-3350 build.yml changed nuget restore for dotnet restore on iOS build to fix issue on restoring due to msbuild

* PM-3350 build.yml Upgraded iOS build to run on macos-13 image which has XCode 15, and set the XCode 15 version as currently the default one is 14.x

* PM-3350 build.yml try to fix ILLINK warnings and changed image to be macos-13-arm64 to see if the build is faster

* PM-3350 build.yml changed image back to be macos-13 to see if the build is faster

* PM-3350 Added Document.Build.props to disable trimming on publish

* PM-3350 build.yml disable trimming on publish so it's faster

* PM-3350 added linkskip for iOS csprojs

* PM-3350 iOS projs disable linking and set Newstandkit as weak framework

* Update build.yml disabling iOS job to avoid long running process of publish until we can fix that

* PM-3349 PM-3350 Workaround to fix issues with text getting cropped/truncated when a Label has both Multiline and LinebreakMode set

* PM-3349 build.yml enabled android build workflow

* PM-3349 build.yml configured FDROID job for MAUI

* PM-3350 iOS extensions TapGestureRecognizer try Window workaround

* PM-3350 iOS applied workaround on the iOS Autofill and Share extension to maui embed the navigation page with its content page in the Window

* PM-3349 PM-3350 Added workaround for More Options to work on Search and Groupings Page
Updated some code to MAUI Style also

* PM-3349 PM-3350 Added the ability for users to press "Continue" button as a fallback when using the Yubikey if the "SubmitCommand" doesn't trigger automatically.

* PM-3349 PM-3350 Fix for text getting cut/truncated in both account switcher and ciphers/search lists
Issue is due to MAUI but can be avoided by using slightly different layout

* PM-3350 iOS updated CFBundlerShortVersionString to latest one 2023.10.1

* PM-3350 fix build.yml Bitwarden.ipa AppStore exported file

* PM-3350 build.yml added step to validate app for submitting into App Store and have better logs of it

* PM-3350 build.yml Added several fixes like not using MtouchUseLLVM on the iOS builds to fix they taking forever to build and some changes on the automation CI to do a debug build for the moment

* PM-3350 Improved MTouch linking and extra args on iOS related csprojs

* PM-3349 PM-3350 Added MAUI label on self-host settings and on about settings to differentiate from XF app

* PM-3349 PM-3350 build.yml uncommented jobs so we have a more complete workflow

* PM-3349 PM-3350 Minor change: removed unneeded HorizontalTextAlignment from Label.

* PM-3349 Replaced CrossCurrentActivity plugin with MAUI internal CurrentActivity

* PM-3350 Fix iOS extensions navigation and Window/RootViewController handling for TapGestureRecognizer to work

* PM-3350 Cleared left ClipLogger from the iOS extensions debug logging.

* PM-3349 PM-3350 Refactored cipher bindings to have a simpler approach reusing a new CipherItemViewModel to avoid unwanted issues in the app

* PM-3349 Added base structure for avoiding Android Autofill crash. This workaround works but it's not complete as it can't handle the entire workflow when showing CipherSelectionPAge (like checking if it should show LockPage)

* PM-3350 Bumped iOS version

* PM-3350 Changed linker to use default mode given that "Full" is presenting some problems as the linker is stripping things it shouldn't and we're trying to solve it. So for now we will use the mode "Link SDK assemblies only" so QA can test.

* PM-3349 Fix for app crashing on Android when Dark mode is enabled
Removed unused button style for android

* Proof of concept for having multiple window in Android for autofill support and navigating with the help of an Extended splash page.

* PM-3350 Fix crash on Release by adding Interpreter on iOS and also adding System.Security.Cryptography to be ignored by the linker

* PM-3350 Apply Cryptography TrimmerRootAssembly only to iOS

* PM-3350 Updated Plugin.Fingerprint so biometrics work

* Update .github/workflows/build.yml setup-xcode commit hash

Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>

* PM-3349 PM-3350 Enabled argon2id and fixed one issue with the Uris when getting the icon image

* PM-3349 Upgraded Android targetSdkVersion to 34

* minor change (public to private fields)

* minor improvemments on autofill-redirect

* PM-3349 Commented the Deploy step for Android job given that we're using the hotfix-rc branch for testing iOS on TestFlight

* PM-3349 Uncommented the Deploy step for Android job

* PM-3349 Ensure "_isResumed=true" is set on App.xaml.cs:Bootstrap

* Reusing App.xaml.cs Navigation for the Android RedirectPage
Some other cleanup and changes

* Improved autofill workaround to better handle switching between windows.

* PM-3349 minor fix to add space in HomePage between the region picker labels.

* Added some comments and improvemments.

* PM-3349 Added Window events unsubscription of events. Also changed code to avoid potentially having multiple autofillwindow

* PM-3349 Minor ui fix (space between buttons in delete account page)

* initial commit of android credential provider service (wip)

* Revert "initial commit of android credential provider service (wip)"

This reverts commit 6011b63958.

* PM-3350 Fix for Delete Account buttons on iOS

* PM-3349 PM-3350 Changed search icon used in app to avoid issue with icon size on iOS

* PM-3349 Added custom window so that we can always get the current Active Window. This is used to support the Android Autofil and multi-window scenarios.

* PM-3349 Fix for icon and text spacing in some list items

* PM-3349 Minor aligment improvemment for region selection in HomePage

* PM-3349 Changed the "track color" for the Android switch so that the color is different from the "thumb"

* PM-3350 Updated version to 2024.1.0 on iOS

* PM-5154 Start implementing Passkeys Autofill in iOS

* PM-5154 Continue Passkeys Autofill in iOS

* initial commit of android credential provider service (wip)

* add cred manager project to build config

* PM-5154 Added Fido2AuthenticationService to provide us a wrapper for the actual implementation

* PM-3349 Fix Picker selection style by doing a custom PickerHandler for Android which uses SetSingleChoiceItems(...) to provide with the appropriate UI

* PM-3350 Updated MauiVersion to 8.0.4-nightly.* to have the TapGestureRecognizer fix applied. This is done on the Directory.Build.props so we don't have to change it on every csproj. Also removed the workaround of TapGestureHack and fix the Show environment picker to work on the extensions as well.

* PM-3350 Added nuget.config so we add the nuget package source for MAUI Nightly builds

* Bump main iOS version

* PM-3350 Removed "iOS" old folder project that has been moved into the MAUI Single app project.

* PM-3350 Improved code safety adding a lot of try...catch and logging throughout the app. Also made the invoking on main thread safer on several places of the app. Additionally, on the GroupingsPageViewModel changed the code removing the old Xamarin hack and just using Replace directly instead of Clearing first to see if that fixes the crash we're having sometimes on the app.

* PM-3350 PM-3349 Updated Unit Test projects to NET 8.0 and fixed it to work with Core project reference. Also fixed a test that was breaking due to CIpherKey creation being wrong. Added "UT" as a constant to add when building/running Core.Test project so we have something on the context that tells us that is for a UT. With this I had to remove FFImageLoading on UT context because it doesn't support NET 8.0

* PM-3350 PM-3349 Updated Readme with MAUI and main branch

* PM-3350 PM-3349 Enable running Core tests

* PM-3350 Fix build.yml format

* PM-3349 Fix navigation when coming from autofill with Accessibility Services enabled. The user was getting into Home page instead of where they were, with this workaround the app navigates as if the account has been switched, leaving the user as closely as possible to where they were, basically on the first screen for the current state of the user.

* PM-3350 PM-3349 Added property to Directory.Build.props to enable Unit Testing globally so Test runners work

* Improve TOTP scan performance on Android

* Move Android camera/scan changes to xaml

* PM-3350 Testing UseInterpreter false on CI build

* PM-3350 Enabled back UseInterpreter on iOS Release given that it crashes on startup without it.

* PM-3349 PM-3350 Improved code safety with try...catch, better invoke on main thread and better null handling.

* PM-3349 PM-3350 Updated XCode version on build.yml to 15.1

* PM-3350 Removed TapGesture Window MAUI hack from iOS.Extension and iOS.ShareExtension

* PM-3350 Fixed CancellationTokenSource proper disposal

* PM-3350 Fix Avatar toolbar icon on extensions to load properly and to take advantage of using directly SkiaSharp to do the native conversion to UIImage. Also improved the toolbar item so that size is set appropriately.

* PM-3349 PM-3350 Fix external link icon

* PM-3350 Added new style to prevent spell check and text prediction

* Fix merge from main

* PM-3350 Commented event collection upload on the timer and when sending the app to background to see if that prevents the app from crashing on release mode.

* PM-3350 Added check for state migration version before trying to migrate LiteDB values into Prefs when there's no need to and that may be inducing crashes on backgrounded iOS apps.

* PM-3350 Try to disable Interpreter to have better crash knowledge. This time testing if avoiding loading the argon2id lib we're able to not use the interpreter.

* PM-5928 Fix circle animation to be shown on verification codes list on each item

* PM-3350 Go back to use Interpreter and added some Directory.Build.props to easily change Codesign properties and also include/exclude iOS extensions / WatchOS from the build.

* PM-3350 Enabled iOS extensions and WatchOS app to be included based on the Directory.Build.props

* PM-3350 Go back to include argon2id and interpreter

* Removing error/loading placeholders of icons on the cells to see if that is causing the background crash on iOS; so we can test this in TestFlight

* [PM-5910] Workaround for for sliding elements in Duo 2FA flow (#2967)

* workaround for sliding elements in duo 2fa flow

* restrict workaround to Android

* restrict workaround to Android

* Revert "restrict workaround to Android"

This reverts commit c2753d5dc4.

* Revert "restrict workaround to Android"

This reverts commit 69688cfb98.

* PM-5902 fix for account switcher not dismissing when tapping outside (#2974)

* PM-3350 Fix iossimulator-x64 argon2id load so we can test on simulators and also made easier to maintain loading the argon2id library on the iOS projects by setting a general Directory.Build.props that is shared.

* PM-5903 Changed App.xaml.cs SetOption to only update the needed properties instead of replacing the existing Options object which would cause the AccountSwitcher button bug (#2973)

* [PM-5896] Fix MAUI iOS Background crash due to lock files on suspension (#2969)

* PM-5896 Fix background crash on iOS due to lock files when app gets suspended. Changed loading and error placeholders of the CachedImage to not be used and use default icon of IconLabel instead changing visibility.

* PM-5896 Changed methods to be protected so that they don't get removed by the linker.

* PM-5896 Added stub class and references to it so to have stronger references to Icon_Success and Icon_Error so the linker doesn't remove them.

* PM-3349 Removed commented code from build.yml regarding FDroid that is not needed anymore.

* PM-3349 PM-3350 Fix crash on iOS AppGroup container URL because of sln config on AndroidX Credentials. Changed the project reference of Credentials to be a local NuGet.

* PM-6077 Separated Android and iOS HybridWebViewHandler so that it can be used on iOS.Core (#2983)

* [PM-5907] Fix for incorrect TOTP white text color on label when using light theme on iOS (#2982)

* PM-5907 workaround for incorrect textcolor when programmatically changing text on Entry

* Update src/Core/Pages/Vault/CipherAddEditPage.xaml.cs

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* [PM-5906] Fix for incorrect Send MaxAccess white text color on label when using light theme on iOS (#2981)

* PM-5906 workaround for incorrect textcolor when programmatically changing text on Entry

* Update src/Core/Pages/Send/SendAddEditPage.xaml.cs

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* PM-3349 PM-3350 Fixed Unit tests because of referencing FFImageLoading when it's not possible

* PM-3349 Added exception on gitignore so the nupkg for the AndroidX Credentials is added

* [PM-5731] Create C# WebAuthn authenticator to support maui apps (#2951)

* [PM-5731] feat: implement get assertion params object

* [PM-5731] feat: add first test

* [PM-5731] feat: add rp mismatch test

* [PM-5731] feat: ask for credentials when found

* [PM-5731] feat: find discoverable credentials

* [PM-5731] feat: add tests for successful UV requests

* [PM-5731] feat: add user does not consent test

* [PM-5731] feat: check for UV when reprompt is active

* [PM-5731] fix: tests a bit, needed some additional "arrange" steps

* [PM-5731] feat: add support for counter

* [PM-5731] feat: implement assertion without signature

* [PM-5732] feat: finish authenticator assertion implementation

note: CryptoFunctionService still needs Sign implemenation

* [PM-5731] chore: minor clean up

* [PM-5731] feat: scaffold make credential

* [PM-5731] feat: start implementing attestation

* [PM-5731] feat: implement credential exclusion

* [PM-5731] feat: add new credential confirmaiton

* [PM-5731] feat: implement credential creation

* [PM-5731] feat: add user verification checks

* [PM-5731] feat: add unknown error handling

* [PM-5731] chore: clean up unusued params

* [PM-5731] feat: partial attestation implementation

* [PM-5731] feat: implement key generation

* [PM-5731] feat: return public key in DER format

* [PM-5731] feat: implement signing

* [PM-5731] feat: remove logging

* [PM-5731] chore: use primary constructor

* [PM-5731] chore: add Async to method names

* [PM-5731] feat: add support for silent discoverability

* [PM-5731] feat: add support for specifying user presence requirement

* [PM-5731] feat: ensure unlocked vault

* [PM-5731] chore: clean up and refactor assertion tests

* [PM-5731] chore: clean up and refactor attestation tests

* [PM-5731] chore: add user presence todo comment

* [PM-5731] feat: scaffold fido2 client

* PM-5731 Fix build updating discoverable flag

* [PM-5731] fix: failing test

* [PM-5731] feat: add sameOriginWithAncestor and user id length checks

* [PM-5731] feat: add incomplete rpId verification

* [PM-5731] chore: document uri helpers

* [PM-5731] feat: implement fido2 client createCredential

* [PM-5731] feat: implement credential assertion in client

* fix wrong signature format

(cherry picked from commit a1c9ebf01f)

* [PM-5731] fix: issues after cherry-pick

* Fix incompatible GUID conversions

(cherry picked from commit c801b2fc3a)

* [PM-5731] chore: remove default constructor

* [PM-5731] feat: refactor user interface to increase flexibility

* [PM-5731] feat: implement generic assertion user interface class

* [PM-5731] feat: remove ability to make user presence optional

* [PM-5731] chore: remove logging comments

* [PM-5731] feat: add native reprompt support to the authenticator

* [PM-5731] feat: allow pre and post UV

* [PM-5731] chore: add `Async` to method name. Remove `I` from struct

* [PM-5731] fix: discoverable string repr lowercase

* [PM-5731] chore: don't use C# 12 features

* [PM-5731] fix: replace magic strings and numbers with contants and enums

* [PM-5731] fix: use UTC creation date

* [PM-5731] fix: formatting

* [PM-5731] chore: use properties for public fields

* [PM-5731] chore: remove TODO

* [PM-5731] fix: IsValidRpId

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
Co-authored-by: mpbw2 <59324545+mpbw2@users.noreply.github.com>

* [PM-5154] Implement Passkeys on iOS (#3017)

* [PM-5731] feat: implement get assertion params object

* [PM-5731] feat: add first test

* [PM-5731] feat: add rp mismatch test

* [PM-5731] feat: ask for credentials when found

* [PM-5731] feat: find discoverable credentials

* [PM-5731] feat: add tests for successful UV requests

* [PM-5731] feat: add user does not consent test

* [PM-5731] feat: check for UV when reprompt is active

* [PM-5731] fix: tests a bit, needed some additional "arrange" steps

* [PM-5731] feat: add support for counter

* [PM-5731] feat: implement assertion without signature

* [PM-5732] feat: finish authenticator assertion implementation

note: CryptoFunctionService still needs Sign implemenation

* [PM-5731] chore: minor clean up

* [PM-5731] feat: scaffold make credential

* [PM-5731] feat: start implementing attestation

* [PM-5731] feat: implement credential exclusion

* [PM-5731] feat: add new credential confirmaiton

* [PM-5731] feat: implement credential creation

* [PM-5731] feat: add user verification checks

* [PM-5731] feat: add unknown error handling

* [PM-5731] chore: clean up unusued params

* [PM-5731] feat: partial attestation implementation

* [PM-5731] feat: implement key generation

* [PM-5731] feat: return public key in DER format

* [PM-5731] feat: implement signing

* [PM-5731] feat: remove logging

* [PM-5731] chore: use primary constructor

* [PM-5731] chore: add Async to method names

* [PM-5731] feat: add support for silent discoverability

* [PM-5731] feat: add support for specifying user presence requirement

* [PM-5731] feat: ensure unlocked vault

* [PM-5731] chore: clean up and refactor assertion tests

* [PM-5731] chore: clean up and refactor attestation tests

* [PM-5731] chore: add user presence todo comment

* [PM-5731] feat: scaffold fido2 client

* PM-5731 Fix build updating discoverable flag

* [PM-5731] fix: failing test

* [PM-5731] feat: add sameOriginWithAncestor and user id length checks

* [PM-5731] feat: add incomplete rpId verification

* [PM-5731] chore: document uri helpers

* [PM-5731] feat: implement fido2 client createCredential

* Added iOS passkeys integration, warning this branch has lots of logs to ease "debugging" extensions.

* [PM-5731] feat: implement credential assertion in client

* PM-5154 Fixed select passkey flow and started implementing create passkey on iOS

* fix wrong signature format

* PM-5154 [Passkeys iOS] Fix Credential ID handling on bytes and string formats. Fix Discoverable to be lowercase on set so it doesn't break parsing on clients. Added UserDisplayName on Fido2 entities. Extracted the Guid Standard/Raw format helpers to a extensions class.

* Fix incompatible GUID conversions

* PM-5154 [Passkeys iOS] Added custom UI flow for passkey creation

* PM-5154 [Passkeys iOS] Updated UI for passkey creation

* PM-5154 [Passkeys iOS] Refactored and added cipher selection for passkey creation on autofill search.

* PM-5154 [Passkeys iOS] Fixed empty top space on autofill password list

---------

Co-authored-by: Andreas Coroiu <andreas.coroiu@gmail.com>
Co-authored-by: mpbw2 <59324545+mpbw2@users.noreply.github.com>

* feat: optimize assertion network calls (#3021)

The server only needs to be updated if we have changed the counter. New passkeys that leave their counters at zero can therefore skip this step.

* [PM-5154] Implement iOS Passkey -> Add login item (#3019)

* PM-5154 Implement iOS passkey add login

* PM-5154 Added Username to Create new login for passkey, for this the param was changed to the Fido2ConfirmNewCredentialParams object so we have access to the proper values. Also added back RpId to the params to have access to it when creating the vault item. Finally added loading to saving the passkey as new login

* [PM-6513] Omit creating CredentialIdentity if it throws an exception (#3040)

* PM-6513 Omit creating CredentialIdentity if that throws, so it doesn't affect other ciphers. E.g. if a Passkey doesn't have a UserName it will throw here and it shouldn't break replacing all the other identities.

* PM-6513 Added fallback values to passkey username not being set

* Fix FIDO2 client bugs (#3056)

* fix: blockedUris null issue

* fix: trailing slash in origin breaking check

* [PM-6466] Implement passkeys User Verification (#3044)

* PM-6441 Implement passkeys User Verification

* PM-6441 Reorganized UserVerificationMediatorService so everything is not in the same file

* PM-6441 Fix Unit tests

* PM-6441 Refactor UserVerification on Fido2Authenticator and Client services to be of an enum type so we can see which specific preference the RP sent and to be passed into the user verification mediator service to perform the correct flow depending on that. Also updated Unit tests.

* PM-6441 Changed user verification logic a bit so if preference is Preferred and the app has the ability to verify the user then enforce required UV and fix issue on on Discouraged to take into account MP reprompt

* [PM-6474] Remove header on Save passkey as new login (#3054)

* PM-6474 Removed header on empty list view on iOS Autofill create passkey flow

* PM-6474 Fix TableView being hidden on Logins scene

* [PM-6496] Improved iOS extensions cipher cell UI (#3058)

* PM-6496 Improved iOS extensions cipher list to have an updated UI for each cell

* PM-6496 Improved UI on iOS extensions list cells

* [PM-5154] Implement combined view for passwords and passkeys on iOS Autofill extension (#3075)

* PM-5154 Implemented combined view of passwords and passkeys and improved search and items UI

* PM-5154 Code improvement from PR feedback

* PM-5154 Code improvement to log unknown exceptions

* PM-6685 Fix race condition issue where the biometrics check is being done before the iOS extension is being shown. So when we need the UI, we wait until ViewDidAppear happens. (#3078)

* PM-6468 Implemented copy TOTP if needed after using a Fido2 credential. Also added the Fido2MediatorService to have one point to interact with the authentication and also to add any new logic we need. (#3082)

* PM-6706 Add maximum attempts to UV with MP and with PIN (#3079)

* [PM-6848] Improved User verification on passkeys creation (#3099)

* PM-6848 Updated cancellation flow on passkey user verification and improved UV enforcement on creation

* PM-6848 Added null checks to help diagnosing if NRE is presented

* PM-6706 Fixed UV attempts to be maximum 5 attempts and not 6 (#3103)

* PM-6793 Updated autofill settings copy (#3102)

* [PM-6655] Add username empty fallback on passkey (#3101)

* PM-6655 Added fallback "Unknown account" to passkey username and moved it so it can be shared with Android

* PM-6655 Improved code lines formatting

* PM-6844 Fix passkey creation cipher list empty label on small devices (#3104)

* [PM-6798] Fix account switch on autofill (#3106)

* PM-6798 Force state update when opening the Autofill extension

* PM-6798 Fix InitAppIfNeededAsync to be awaited and also ignored Fido2AuthenticatorException from logging them to AppCenter since they don't add much information and we're logging in other places what we need

* PM-6475 Fix dark theme on iOS Autofill extension (#3114)

* PM-6850 Removed duplicate MP Reprompt on passkey creation item selection (#3118)

* PM-6538 Removed non-discoverable passkeys filter for credentials that go to the ASStore (#3117)

* [PM-6655] Add null fallback cipher name on passkeys (#3116)

* PM-6655 Fixed fallback value on passkeys to take into account CipherView.Name. Also removed non-discoverable passkey filter on adding credentials to the ASStore and also added the fallback consideration on the passkeys list iOS extension

* PM-6655 Restored non-discoverable filter on credentials set for ASStore on this PR

* PM-6798 Fix account switch on iOS Autofill extension and also changed to Try... actions for TaskCompletionSource to avoid exceptions on some occasions. (#3121)

Co-authored-by: Álison Fernandes <vvolkgang@users.noreply.github.com>

* PM-6209 Removed MAUI label from environment and about pages (#2990)

* [PM-7186] Fallback to password list on exception (#3127)

* PM-7186 Added fallback in case of exception that loads password list

* PM-7186 Added back the error message removed in last commit.

* feat: add support for `credProps.rk` extension (#3132)

* PM-7186 Remove error message when showing password list as a fallback with user interaction (#3133)

* [PM-5153] Android Passkey Implementation (#3020)

* Initial WIP implementation for the app unlock flow when called from Passkey. Still needs code organization and to be finished.
Also added a new Window workaround in App.xaml.cs to allow CredentialProviderSelectionActivity to launch separately.

* Added missing IDeviceActionService.cs implementation for iOS to build.

* Added Async to ReturnToPasskeyAfterUnlockMethod
Changed i18n to AppResource.Unlock
Removed unecessary cast

* minor code change (added comment)

* Added back the case for loading a specific Window for CredentialProviverSelectionActivity

* Added fix for Intent not passing properly to CredentialProviderSelectionActivity
Added Activity cancellation on error during execution of ReturnToPasskeyAfterUnlockAsync()

* Added WIP code for Android passkey implementation. Currently returns a mostly complete response that is missing the ClientDataJson

* Added WIP code for creating passkeys on Android. Still missing unlock flow and response of passkey creation is still not correct.
Removed unused throw NotImplementedException from Fido2ClientService
Added CredentialCreationActivity for passkey creation
Added alternative code on CredentialProviderSelectionActivity to try to debug issue with response not being valid

* Started working on logic to adding unlock flow. It's already handling the unlock but not passing the PendingIntentHandler info for CredentialCreation to CredentialCreationActivity

* Changed "cross-platform" to "platform"

* Created CredentialHelpers.cs class to share code used for Populating Passkeys in Android.

* Added Passkey Credential Creation shared code to CredentialHelpers.
Unlock flow for Passkey creation should now be working also.

* Updated code for checking if the CredentialProviderService has been enabled by the user or not. Still WIP, somes notes in code due to Credential API not being complete.
Also changed the disable code to open the Credential Settings.

* Replaced the AndroidX.Credential helpers with custom JSON creation to fix the response for Credential Creation

* minor code cleanup on CredentialProviderSelectionActivity

* added todo comment

* Feature/maui migraton passkeys android unlock fix andreas (#3077)

* fix: bitwarden providing too many/wrong credentials

* feat: use authenticator instead of client

---------

Co-authored-by: Dinis Vieira <dinisvieira@outlook.com>

* Removed / commented some older Passkey Proof of concept code.
Auth and creation of passkey should still work both when device is unlocked (and not)
Added some initial code in AutofillCiphersPageViewModel and CipherAddEditPageViewModel for handling Passkey creation

* PM-6829 Implemented Fido2...UserInterfaces on Android and necessary logic to get/make a credential with those

* Added IFido2MediatorService registrations
Inverted two IsLockedAsync checks

* Added navigation to autofillCipher when creating passkey

* Updated LockPage to avoid multiple executions of SubmitAsync

* Added new flow for creating new passkey on Android with the Cipher page for editing details

* Changed the Credential Provider Switch to an external link control

* Added i18n for Passkey Settings

* Cleanup of older Credentials code used for Android Fido2 POC.
Removed CredentialCreationActivity as it's no longer needed

* fixed merge conflict/error and added error check to Fido2 navigation in App.xaml.cs

* Removed from MainActivity casts from DeviceActionService
Changed CredentialProviderServiceActivity to handle Fido errors and exceptions gracefully and show the user an error. Still not with the correct messages.

* Added some error messages. Still need to confirm the Text Resource to use and change.

* Changed some messages to use AppResources

* Cleanup of Credential Android code and added exception result if the clientCreateCredentialResult is null

* Updated Add new item button text when creating a new passkey

* Added AccountSwitchedException for the Fido Mediator Service

* Removed TODO that is no longer needed

* Updated some todo messages in Android AutofillHandler

* When authenticating a passkey on Android the "showDialog" callback can be called and there's no MainPage available so it was changed for that specific scenario to use _deviceActionService instead of MainPage.

---------

Co-authored-by: Andreas Coroiu <andreas.coroiu@gmail.com>
Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* [PM-7369] Show passkey icon on android when the item has a Fido2 credential (#3148)

* PM-7369 Show passkey icon on android when the item has a Fido2 credential

* PM-7369 alternative way to show passkey icon only in scenarios where we are trying to create a passkey

* PM-7369 moved logic to show passkey icon to CipherItemViewModel

* Update src/Core/Utilities/IconGlyphConverter.cs

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* PM-7379 Fix creating the PendingIntent for a passkey credential on Android so it has different request codes amongst each other so the extras are not overriden by the last credential entry created. (#3149)

* PM-7365 Fix setting HasUnlockedInThisTransaction on passkey creation on android (#3153)

* PM-7367 Fix empty items state placeholder on Android cipher selection page (#3160)

* [PM-7366] Select cipher on search on Fido2 creation (#3154)

* PM-7366 Implemented cipher selection on search on passkey creation

* PM-7366 Fix typo

* [PM-7385] Fix for allowing switching accounts while creating a passkey of Android (#3155)

* PM-7385 Fixed for allowing switching accounts while creating a passkey on Android.
This fixes also include scenarios where we need to unlock the vault after switching
Also fixed the issue where tapping on cipher won't do anything after switching.

* PM-7385 ensure the Options.Fido2CredentialAction and FromFido2Framework are reset when the Credential flow is started to avoid erratic behaviors when switching accounts, app is in background or other edge case scenarios.
These properties where replaced by calls to _fido2MakeCredentialConfirmationUserInterface.IsConfirmingNewCredential instead.

* Minor changes and added comments

* [PM-7385] Implemented several changes suggested in PR for better/cleaner code.

* PM-7385 Added several minor code improvemments.

* PM-7385 Fix IFido2MakeCredentialConfirmationUserInterface resolve and usage to be constrained to Android. (#3164)

* PM-7385 Fix unit tests for Fido2 service (#3167)

* PM-7518 Updated favicon placeholder color on iOS Autofill extension. (#3165)

* PM-7365 Fix UserVerification on Fido2 credential creation on Android by updating the HasUnlockedInThisTransaction flag when a new transaction starts. (#3168)

* PM-5154 Avoid logging Fido2AuthenticatorExceptions (#3169)

* PM-7258 Updated Android Credential creation details on description to be localized and passed the user email for further details. (#3162)

* [PM-7257] android add support for web authn resident key credential property in our net mobile app 2 (#3170)

* [PM-7257] feat: add ability to override `clientDataHash`

* [PM-7257] feat: add support for clientDataHash and extensions

* PM-7257 Updated the origin to be the correct one and not the android one to be passed to the Fido2Client

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* [PM-7365] Fix UV not being performed on Fido2 credential creation Android (#3171)

* PM-7365 Fix UV not being performed on Fido2 credential creation on Android

* PM-7365 Fix PublicKeyCredentialCreationOptions mapping from json on AuthenticatorSelection so mainly userVerification has correct value

* PM-7585 Show error message when Origin is null, given no support for passkeys from native apps yet (#3175)

* PM-7623 Fix proper implementation of IFido2GetAssertionUserInterface now that the Fido2ClientService is being used for passkey autofill (#3174)

* PM-7553 Fix native apps passkeys autofill and creation (#3188)

* [PM-7658] Implement Fido2 privileged apps verification (#3190)

* PM-7553 Fix native apps passkeys autofill and creation

* PM-7658 Implemented Fido2 priviliged apps verification

* [PM-7576] Implemented digital asset links verification on Fido2 flows (#3191)

* PM-7553 Fix native apps passkeys autofill and creation

* PM-7658 Implemented Fido2 priviliged apps verification

* PM-7576 Implemented digital asset links verification on Fido2 flows for native apps.

* PM-7576 Renamed to ValidateAssetLinksAndGetOriginAsync to go along with Google naming and also changed method to private given that public is not necessary

* PM-7576 Moved digital asset links verification to a Core service AssetLinksService and added unit tests for it.

---------

Co-authored-by: Dinis Vieira <dinisvieira@outlook.com>
Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>
Co-authored-by: mpbw2 <59324545+mpbw2@users.noreply.github.com>
Co-authored-by: Andreas Coroiu <andreas.coroiu@gmail.com>
Co-authored-by: Álison Fernandes <vvolkgang@users.noreply.github.com>
2024-04-25 21:26:12 +01:00
github-actions[bot]
06488539b0 Autosync the updated translations (#3172)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-04-23 13:03:59 +00:00
Bitwarden DevOps
8f77822b1b Bumped version to 2024.4.2 (#3184) 2024-04-23 11:30:56 +00:00
Álison Fernandes
3c1105b35d [PM-7631] Handle new FCMv1 format (#3176)
* handle new FCMv1 format

* optimizations

* restore formatting

* revert formatting

* revert formatting

---------

Co-authored-by: Kyle Spearrin <kyle.spearrin@gmail.com>
2024-04-22 15:12:21 -04:00
renovate[bot]
c847449db8 [deps]: Update gh minor (#3177)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-22 14:19:55 -04:00
renovate[bot]
c2771eb3c7 [deps]: Update microsoft/setup-msbuild action to v2 (#3178)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-22 14:19:37 -04:00
Federico Maccaroni
6e41731dcb PM-6971 Added PrivacyInfo file to iOS (#3163) 2024-04-17 19:11:13 -03:00
Bitwarden DevOps
7c90b35592 Bumped version to 2024.4.1 (#3161) 2024-04-15 21:31:08 +00:00
renovate[bot]
93f9dc4498 [deps]: Update gh minor (#3124)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-15 16:34:05 -04:00
renovate[bot]
e033832261 [deps]: Update actions/setup-dotnet action to v4 (#3139)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-15 10:04:36 -06:00
Álison Fernandes
fa5d92fbf7 [PM-7407] Updates the self-host release date in the unassigned items alert message (#3158) 2024-04-15 15:58:56 +02:00
github-actions[bot]
e672cb132f Autosync the updated translations (#3151)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-04-12 19:51:04 +00:00
Federico Maccaroni
e7a7eed7e8 [PM-7407] Implemented check for organizations with unassigned items (#3150) 2024-04-12 15:52:39 -03:00
Bitwarden DevOps
43a4915323 Bumped version to 2024.4.0 (#3143) 2024-04-08 14:50:08 +00:00
Álison Fernandes
b1ae3cc325 iOS Beta variants now have their own Encryption Export Compliance Code (#3136) 2024-04-08 12:16:48 +01:00
JohanGallardo
b9dada07ea Fixed broken mobile documentation link in README (#3142) 2024-04-08 09:14:30 +00:00
github-actions[bot]
58442389df Autosync the updated translations (#3122)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-04-08 06:02:32 +00:00
Álison Fernandes
a3378d33ae Removed cake from Platform team ownership (#3137) 2024-04-05 23:37:21 +01:00
renovate[bot]
2e1982b08e [deps]: Update actions/labeler action to v5 (#2895)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-03 08:51:28 -06:00
renovate[bot]
e9e9b6f7bc [deps]: Update actions/checkout action to v4 (#2756)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-03 08:48:11 -06:00
Opeyemi
9be8fec219 [DEVOPS-1822] - Upload Mobile Beta Native Build (#3015)
* Upload  mobile beta native build

---------

Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>
Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
Co-authored-by: Álison Fernandes <vvolkgang@users.noreply.github.com>
Co-authored-by: MtnBurrit0 <77340197+mimartin12@users.noreply.github.com>
2024-04-03 15:09:04 +01:00
Bitwarden DevOps
9db32ca019 Bumped version to 2024.3.3 (#3113) 2024-03-25 14:20:35 +00:00
Dinis Vieira
f04ff7777a Added specific try catch in Android launchApp to avoid the app crashing when trying to launch app package name that are not installed on the device. (#3092) 2024-03-22 16:31:15 +00:00
github-actions[bot]
64775694e0 Autosync the updated translations (#3105)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-03-22 16:07:23 +00:00
Dinis Vieira
3c0007a21a [PM-7009] Improved exception messages for the Broadcast Service message callback function (#3091)
* Improved exception messages for the Broadcast Service message callback function

* Update src/Core/App.xaml.cs

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* Update src/Core/App.xaml.cs

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* Update src/Core/App.xaml.cs

Additional Null Check

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* Update src/Core/App.xaml.cs

Additional Null Check

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* Update src/Core/App.xaml.cs

Additional Null Check

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* Update src/Core/App.xaml.cs

Additional Null Check

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* Update src/Core/App.xaml.cs

Additional Null Check

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2024-03-22 12:29:12 -03:00
Bitwarden DevOps
35ff235010 Bumped version to 2024.3.2 (#3096) 2024-03-19 15:15:29 +00:00
Bitwarden DevOps
01bd5a7b8d Bumped version to 2024.3.1 (#3095) 2024-03-19 14:43:30 +00:00
Vince Grassia
3fce8c76bc Add Cleanup RC Branch workflow (#3093) 2024-03-18 11:36:21 -06:00
github-actions[bot]
3b64d7b979 Autosync the updated translations (#3083)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-03-15 23:31:54 +00:00
Federico Maccaroni
f343a2cdbb [PM-6852 ] Fix F-Droid build constant (#3085)
* Fix FDROID trackers removal by changing publish to build to see if doing this it adds the corresponding CustomConstants

* Changed parameters in new line delimiter to the one used in bash to see if that fixes passing the corresponding parameters to the build

* Revert "Changed parameters in new line delimiter to the one used in bash to see if that fixes passing the corresponding parameters to the build"

This reverts commit 608b23d115.

* Enable FDROID constant by replacing the content of Directory.Build.props in the clean stage of F-Droid
2024-03-15 17:01:15 -03:00
Vince Grassia
9a9fb85ad8 Add version codes to GitHub step summary (#3081) 2024-03-14 10:06:51 -06:00
Bitwarden DevOps
e7f9d64edb Bumped version to 2024.3.0 (#3080) 2024-03-14 11:10:21 -04:00
Vince Grassia
459d20c019 DEVOPS-1840 - Update for automatic version bump calculation (#3043) 2024-03-12 14:10:11 -06:00
github-actions[bot]
a8529fa4b7 Autosync the updated translations (#3064)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-03-12 08:41:52 +00:00
Dinis Vieira
d1e82c9f1d [PM-6751]Added missing query intent for CustomTabs that might be responsible for the Exception in WebAuthenticator (#3071)
* Added missing query intent for CustomTabs that might be responsible for the crashes in WebAuthenticator

* minor formatting issue on AndroidManifest.xml

* Fix formatting in AndroidManifest
2024-03-11 19:36:07 -03:00
Dinis Vieira
9bc2901255 [PM-6726] Fix for Android 14 devices crashing when using the Tiles (#3069)
* Fix for Android 14 devices crashing when using the TileService.
Also added fix for an "hidden" crash in accessibility autofill

* Shared StartActivityAndCollapseFromTileService in AndroidHelpers

* Update src/App/Platforms/Android/Utilities/AndroidHelpers.cs

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* Updated name of StartActivityAndCollapseWithIntent method name used by TileService

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2024-03-11 18:06:37 -03:00
Vince Grassia
e3441845cd DEVOPS-1866 - Fix F-Droid Signing (#3063) 2024-03-07 23:45:15 +00:00
Vince Grassia
3f463647a0 Add login step to be able to download secrets (#3061) 2024-03-07 07:18:32 -08:00
Bitwarden DevOps
4f169a6fe3 Bumped version to 2024.2.2 (#3060) 2024-03-07 15:07:46 +00:00
Vince Grassia
82c2e91446 Update release workflow with proper paths (#3059) 2024-03-07 15:53:27 +01:00
renovate[bot]
7482808857 [deps]: Update chrnorm/deployment-status action to v2.0.3 (#3050)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-06 13:52:31 -05:00
Opeyemi
fd233fa27f Update Failure Job (#3055) 2024-03-06 13:58:41 +01:00
Federico Maccaroni
19f238d9bb [PM-6539] Fix Autofill Extension TDE without MP flow (#3049)
* PM-6539 Fix Autofill Extension TDE without MP updating PromptSSO to work in MAUI and also Generator view. WebAuthenticator copied with UIWindow gotten as it was in Xamarin forms to work. Also fix one NRE on state migration.

* PM-6539 Remove unnecessary using
2024-03-05 18:09:20 -03:00
Vince Grassia
6f6487ccc9 Fix GoogleServices file location (#3053) 2024-03-04 08:11:54 -07:00
Dinis Vieira
dd3dc82595 PM-6552 Added missing using (#3052) 2024-03-04 13:29:22 +00:00
Dinis Vieira
40c80f082d [PM-6552] Fix for Android Window issues when opening Autofill/Accessibility (#3051)
* PM-6552 Removed several of the Window Workarounds for Android. Now always relying on the AndroidNavigationRedirectPage.
AndroidNavigationRedirectPage now more resilient to failure and navigates to HomePage as fallback.

* Update src/Core/Pages/AndroidNavigationRedirectPage.xaml.cs

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2024-03-04 13:03:09 +00:00
André Bispo
bca5b95446 [PM-4760] Admin Recovery Permissions prompted to set MP. (#2912)
* [PM-4760] Add force password reset check on sync complete.

* [PM-4760] Log error on exception
2024-03-01 19:43:18 +00:00
Dinis Vieira
602627b5fa PM-6552 Fix for App only showing Home (Login) Page after closed after opening Accessibility Settings (#3047) 2024-03-01 19:17:45 +00:00
github-actions[bot]
6f32afb919 Autosync the updated translations (#3045)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-03-01 12:26:42 +01:00
Daniel James Smith
2ca47a4da4 Update ownership of translations (#3046)
Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
2024-03-01 11:10:15 +00:00
Dinis Vieira
4ff56ba11e PM-5916 Fix for incorrect fonts in fingerprint phrases (#3042) 2024-02-29 09:57:25 +00:00
Vince Grassia
22d0cc681c Change version to proper value (#3041) 2024-02-28 11:49:30 -07:00
André Bispo
4e0a18cce5 [PM-6506] Fix double execution of command on returnType Go (#3039)
* [PM-6506] Fix double execution of command on returnType Go

* [PM-6506] Hide keyboard on environment page close

* [PM-6506] Task guard

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2024-02-28 17:49:46 +00:00
Vince Grassia
c9fdfa7a15 DEVOPS-1746 - Update iOS distribution certificate and profiles (#3018) 2024-02-28 09:48:16 -03:00
Vince Grassia
850a7e754a DEVOPS-1834 - Apply fix for signing issue (#3038) 2024-02-27 20:18:24 +00:00
Dinis Vieira
67c5f79625 [PM-5917] Fix for send arrow now being touch sensitive to expand collapse (#3036)
* PM-5917 fix for send arrow not being tappable

* Added min width to send icon button so that it has correct spacing like in Android and old Xamarin Forms app.

* Updated min width from previous commit to 25 instead of 20 for more equivalent look to xamarin forms app on iOS
2024-02-26 23:45:59 +00:00
Federico Maccaroni
04e7cfe06d [PM-6428] Implement Legacy Secure Storage (#3027) 2024-02-26 19:25:08 -03:00
Álison Fernandes
d6c2ebe4c2 [PM-6480] Update MAUI to 8.0.7 (#3035)
* Update MAUI to 8.0.7

Updates MAUI to the future SR2 release version.

* Using the released version instead of nightly
2024-02-26 22:24:44 +00:00
Dinis Vieira
2a28294f91 PM-5912 Added default min height and corner radius for iOS buttons. Also removed incorrect style class from one button. (#3031) 2024-02-26 19:23:06 -03:00
Dinis Vieira
8584bbaecc PM-6301 Removed IsRefreshing=true in RefreshAsync as it can trigger the RefreshView to trigger the RefreshView command again (#3026) 2024-02-26 17:28:18 +00:00
Dinis Vieira
2f3cded9c5 PM-6309 Fix to ensure the Icon and Icon placeholder visibility states is updated correctly based on website icons visibility choice (#3033) 2024-02-26 17:16:54 +00:00
github-actions[bot]
eff0ea7ce7 Autosync the updated translations (#3025)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-02-26 15:29:26 +00:00
renovate[bot]
6c3a53dd76 [deps]: Update dawidd6/action-download-artifact action to v3.1.2 (#3028)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-26 09:37:03 -05:00
Vince Grassia
cf8d801c55 Add .NET 3.1 to fix Google Publisher project signing (#3024) 2024-02-22 13:45:42 -07:00
Federico Maccaroni
eaa6844742 Update build.yml to go back to net3.1 Publisher (#3023) 2024-02-22 17:01:33 -03:00
Federico Maccaroni
29e2f728e0 Update Publisher.csproj to go back to net 3.1 to see if that fixes the build (#3022) 2024-02-22 16:39:09 -03:00
Opeyemi
fe160a570f Add stub for DEVOPS-1822 (#3016) 2024-02-21 15:27:43 +00:00
Dinis Vieira
a508bea4b0 [PM-6291] Fix Device Login Pending Requests screen not displaying anything (#3012)
* PM-6291 Changed Passwordless Request Login Page Layout structure so that it can display properly

* PM-6291 Additional changes to allow iOS to show the correct with on the collectionview items
2024-02-21 10:55:26 +00:00
Dinis Vieira
a73923c4f7 [PM-5909] Fix Font MAUI Sizes (#3014)
* PM-5909 Set the default FontSize for Entry, Editor, SearchBar and Picker on Android so that the fonts have a similar size to the one in the Xamarin Forms app.

* PM-5909 Set the default FontSize for Entry, Editor, SearchBar and Picker on iOS so that the fonts have a similar size to the one in the Xamarin Forms app.

* PM-5909 Added spacing in specific scenario for Send Groups (between icon and text)
2024-02-20 22:06:47 +00:00
renovate[bot]
11465e8975 [deps]: Update gh minor (#3011)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-20 16:01:34 -05:00
André Bispo
4c88524f0e [PM-4615] [PM-6217] Add new DUO frameless 2fa flow (#2956)
* [PM-4615] Update DUO 2FA screen to support DUO frameless flow.
2024-02-20 18:46:47 +00:00
Opeyemi
f1c20e03bc Remove individual linter file (#3010) 2024-02-15 11:15:13 -05:00
github-actions[bot]
920a2273c5 Autosync Crowdin Translations (#3009)
* Autosync the updated translations

* Add whitespace to build.yml to trigger workflow linter

* Remove whitespace from build.yml

---------

Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
2024-02-15 12:52:13 -03:00
mpbw2
96a9978ef8 fix resources path for crowdin (#3008) 2024-02-14 10:26:36 -05:00
Dinis Vieira
1ae388cb03 [PM-5900] Fix for Device Login push notifications cause the app to show HomePage (#2989)
* PM-5900 Fix for Device Login push notifications cause the app to show the HomePage

* PM-5900 Added edge case scenario if intent / packageName / packageManager is null
2024-02-13 22:07:09 +00:00
Vince Grassia
c6aaf5002f Add xmllint install step (#3006) 2024-02-12 12:37:52 -07:00
Bitwarden DevOps
75be6504e1 Bumped version to 2024.2.2 (#3005) 2024-02-12 19:20:44 +00:00
renovate[bot]
cae1825e3f [deps]: Update gh minor (#2992)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-12 14:09:14 -05:00
Vince Grassia
c23100d281 Update CODEOWNERS file for Version Bump automation (#3004) 2024-02-12 14:03:12 -05:00
Vince Grassia
c9c0d0b4d6 Fix while loop (#3003) 2024-02-12 18:20:29 +00:00
mpbw2
fec0743e4d Fix for Android builds (#3001)
* enable unit test flag

* revert UT flag and modify flags in ThemeManager
2024-02-12 12:45:52 -05:00
Vince Grassia
118dcf164c Fix all paths (#2999) 2024-02-12 07:51:51 -08:00
Vince Grassia
bd03b6b5aa Version Bump workflow - Fix location of AndroidManifest.xml file (#2998) 2024-02-12 07:35:19 -08:00
Federico Maccaroni
333917c00d Fix Android build UT Failing because of precompiler directives. (#2991) 2024-02-12 09:34:43 -05:00
Vince Grassia
450101d9e4 Update auto version bump workflow (#2987) 2024-02-12 07:56:08 -05:00
Federico Maccaroni
4e50f1697d PM-6209 Removed MAUI label from environment and about pages (#2990) 2024-02-09 12:08:45 -03:00
Dinis Vieira
3c96ae2220 [PM-5913] Fix for MAUI not reporting the correct Theme when resuming an iOS App (#2988)
* PM-5913 Fix for incorrect iOS Theme when resuming iOS app. Also added minor DeviceInfo change and InvokeOnMainThread is on ThemeManager even though it can be moved elsewhere.

* PM-5913 removed redundant iOS check and changed #elif to #else
2024-02-09 14:28:43 +00:00
Vince Grassia
fdbd16a6fd Add build version to step summary (#2986) 2024-02-09 11:31:25 +01:00
Federico Maccaroni
39a34bd8c4 [PM-3349] [PM-3350] MAUI Migration Initial (#2806)
* PM-3349 PM-3350 MAUI Migration Initial

* PM-3349 PM-3350 MAUI Migration fix nullable exception bindings and AsyncCommand canExecute null exception

* PM-3349 PM-3350 MAUI Migration fix nullable bindings and fallbacks

* PM-3349: Android
Added CustomTabbedPageHandler for Android to handle the tab "reselection" for PopToRoot.
Commented support for Windows in App.csproj
Disabled Interpreter on Android to avoid very slow app in Debug (during Login for example)
Added some null checks that were causing crashes (on GeneratorPageVM and PickerVM)
Minor TabsPage cleanup

* TabBarEffect removed and it's behavior is now taken care of by CustomTabbedPageHandler

* PM-3349 PM-3350 Add null checks on CipherDetailsPageVM to avoid crash opening Secure Notes.

* PM-3349 PM-3350 MAUI Migration Start iOS extensions

* Changes to solution to hopefully fix Config Mappings

* PM-3349 Removed Deploy from iOS.Autofill to allow running Android
Changed MainApplication SpecialFolder.Personal to SpecialFolder.LocalApplicationData

* PM-3350 MAUI Migration Fix iOS Autofill extension

* PM-3349 Changed UseMauiApp init so that Android Handlers still get added

* PM-3349 Implemented HybridWebViewHandler for Android which enables 2nd factor auth flows
Ensured CustomTabbedPageHandler had it's DisconnectHandler called
Some minor code upgrades of older obsolete Xamarin Forms code.

* PM-3349 Implemented HybridWebViewHandler for iOS

* hardcoded AccountViewCell Avatar image to 40x40 to avoid current iOS/Android bugs where they fill much larger space.

* PM-3349 PM-3350
Added (migrated) CustomNavigationHandler (which should partially fix the AvatarIcon in the NavBar in iOS)
Added (migrated) CustomContentPageHandler (which should mostly place the AvatarIcon in the navBar in the correct place for iOS)
Added Task.Delay (workaround) to allow the Avatar to load in iOS on the LoginPage
Added workaround for iOS bug with the toolbar size (more info in comment in AvatarImageSource.cs)
Went through the AccountViewCell MAUI-Migration comments. (and deleted/added more comments as needed)
Migrated some Device calls to DeviceInfo and MainThread
Added (migrated) CustomTabbedHandler (for managing the iOS TabBar)

* PM-3349 Replaced the FabShadowEffect with the new MAUI Shadow to fix the buggy shadows on the Android Fab Button.

* PM-3349 ToolbarHandler created for setting text on Android go back buttons.

* PM-3350 Migrated the CustomViewCellRenderer for iOS

* PM-3350 Removed ButtonHandlerMappings and some other code related with fonts as MAUI is taking care of Accessibility and no custom code should be needed
Migrated SelectableLabelRenderer to Handler
Cleaned LabelHandlerMappings and added logic to migrate the CustomLabelRenderer

* Enabled argon2Id for iOS

* PM-3349
Added Argon libraries for Android
minor change to gitignore so that the Argon x86 lib is not ignored on the Android platform

* PM-3350 Migrated some Device to DeviceInfo and added temporary workaround with some comments to be able to see the Generated Password on iOS

* PM-3350 Added some missing images in iOS

* PM-3349 PRM-3350 Replaced XZing with Camera.MAUI for QRCodes

* Checked some [MAUI-Migration] and deleted when it's working as intended.
SearchBarHandlerMapping: IME options working as intended
SliderHandlerMappings: The MAUI "replacement" for Color.Default seems to be White so the old use case doesn't seem to be needed anymore.

* PM-3350 Checked some [MAUI-Migration] and changed as needed.
TimePickerHandlerMappings: Remove old code for forcing the Wheel. After testing without it wheel picker is still used so this code shouldn't be needed anymore.
AppDelegate.ContinueUserActivity: Uncommented and changed the iOS ContinueUserActivity. It needs to call Platform.ContinueUserActivity according with Xamarin Essentials migration docs.

* PM-3349 Fixed white tint color not appearing on images added as MAU IImage SVG
PM-3349 PM-3350 Fix for Avatar text not adjusting to white/black color correctly

* PM-3350 Removed MAUI Splash Screen. Fixed iOS Privacy Screen logo (hardcoded image to avoid it getting cropped)

* PM-3350 Quick workaround to allow 2nd factor auth to not get stuck in iOS in modals.
Updated some older "Device" code to the newer MAUI code.

* PM-3350 Removed duplicate reference to LaunchScreen.storyboard

* PM-3349 PM-3350 Minor change to HomePage to set fixed Image height otherwise it takes more space than it did in the old Xamarin Forms app.
Added HIdeSoftInputOnTapped on several pages (the ones with Entry controls) to allow hiding the keyboard when tapping "outside" of it. (just like we did in Xamarin Forms app)

* PM-3350 Added Scrollview on HomePage so that the "Create account" button can be accessed in smaller devices like iPhone SE.

* PM-3349 Added Handler that enables the ExtendedDatePicker to get IsFocused events in Android. This is a workaround for fixing the current bug where it's not possible to select the "current day" in the expiration date of a Send.
Fix for TimePicker not displaying default Time Value
Updated some "Device" code to the new MAUI "DeviceInfo"

* PM-3349 PM-3350 Migrated IconLabelButton Frames to Borders to fix issue with TapGestureRecognizer in Android
Also fixed some minor "styles" for normal Button and IconLabelButton (both Android and iOS)

* PM-3349 Fix for TabGestureRecognizer not working inside the StackLayout area of IconLabelButton

* PM-3349 Fix for Android buttons having all letters in Caps

* PM-3349 PM-3350 Started using OnNavigatedTo/From instead of On(Dis)Appearing for LoginPage and LoginSSOPage to avoid the "Modal loading" issues in iOS
Also had to add IsInitialized logic to these pages because OnNavigatedTo can be called twice in some scenario.
Some minor migrations of Device to DeviceInfo was also done

* PM-3350 Fixed iOS extensions (iOS.Extension and iOS.ShareExtension)  to load and commented argon2id from debug configuration until we have the .a compiled again with the new platform/arch

* PM-3350 Added configurations for Release mode (no FDroid yet)

* PM-3349 PM-3350 Migrated remaining AutomationProperties to SemanticProperties.
All 'IsInAccessibleTree="True"' were deleted.
'IsInAccessibleTree="False"' were kept and stayed in code.

* PM-3349 PM-3350 Changed binding set for CipherViewCell so it updates accordingly

* PM-3349 PM-3350 Changed AccountViewCell and its binding to be directly against the ViewModel

* PM-3349 Fix for HTML Label on Android. Color hex doesn't need to be cropped anymore.

* PM-3350 Fix for colored html text on iOS

* PM-3349 PM-3350 Added the partial MAUI Community Toolkit implementation for TouchEffect. This is a temporary solution until they finalize this and add it to their nuget package.
This allows implementing the LongPressCommand in AccountSwitchingOverlay and also have the "Ripple effect" animation when touching an item in Android

* PM-3349 PM-3350 Changed SendViewCell and its binding to be directly against the ViewModel

* PM-3350 Fixed iOS Share extension lazy views loading and an issue with the avatar loading. Also discovered issue with TapGestureRecognizer not working on MAUI Embedding

* PM-3350 Fixed iOS Extensions navigation to several pages and improved avoiding duplicate calls to OnNavigatedTo

* PM-3350 Updated PCL Crypto to latest alpha version to fix "Dll not found NCrypt" issue

* PM-3350 Removed workaround for iOS issue with Avatar icon as it's now fixed in latest .Net8 release.

* PM-3349 PM-3350 Removed AsyncCommand "wrapper" and added AsyncRelayCommand directly in all ViewModels that were using the other one.

* PM-3350 Added watchOS app to main project and fixed some csproj conditions for runtime identifiers on iOS.

* PM-3350 Fixed/Updated all MAUI-Migration TODOs

* PM-3350 Fixed account toolbar item and TitleView on SendAddOnlyPage, also removed comments on AvatarImageSource given the workaround is not needed anymore to draw the image successfully.

* PM-3350 Updated AppCenter package to latest version 5.0.3 and updated some things into MAUI style

* PM-3350 Added workaround for iOS Avatar icon again.

* PM-3349 Added workaround for Android to avoid issues with setting MainPage when app is in background. They are now kept on a Queue to be executed after the app has resumed.
Updated some things on App.xaml.cs to the new MAUI style

* PM-3349 PM-3350 Fixed issue where creating an account with weak/exposed password would get stuck after the Captcha (if a captcha is shown)
Changed App.xaml.cs NavigateImpl to be private

* PM-3349 Started to configure build.yml for MAUI Android

* PM-3349 build.yml update paths for MAUI Android

* PM-3349 build.yml commented verify format and just set qa as variant on MAUI Android for faster checks on CI

* PM-3349 PM-3350 build.cake updated paths

* PM-3349 build.yml updated env helpers variables and set specific csproj to build on Android so not to build iOS extensions

* PM-3349 build.yml add Android "prod" variant

* PM-3350 build.yml updated iOS build and ignore Android build to try the CI faster

* PM-3350 build.yml changed nuget restore for dotnet restore on iOS build to fix issue on restoring due to msbuild

* PM-3350 build.yml Upgraded iOS build to run on macos-13 image which has XCode 15, and set the XCode 15 version as currently the default one is 14.x

* PM-3350 build.yml try to fix ILLINK warnings and changed image to be macos-13-arm64 to see if the build is faster

* PM-3350 build.yml changed image back to be macos-13 to see if the build is faster

* PM-3350 Added Document.Build.props to disable trimming on publish

* PM-3350 build.yml disable trimming on publish so it's faster

* PM-3350 added linkskip for iOS csprojs

* PM-3350 iOS projs disable linking and set Newstandkit as weak framework

* Update build.yml disabling iOS job to avoid long running process of publish until we can fix that

* PM-3349 PM-3350 Workaround to fix issues with text getting cropped/truncated when a Label has both Multiline and LinebreakMode set

* PM-3349 build.yml enabled android build workflow

* PM-3349 build.yml configured FDROID job for MAUI

* PM-3350 iOS extensions TapGestureRecognizer try Window workaround

* PM-3350 iOS applied workaround on the iOS Autofill and Share extension to maui embed the navigation page with its content page in the Window

* PM-3349 PM-3350 Added workaround for More Options to work on Search and Groupings Page
Updated some code to MAUI Style also

* PM-3349 PM-3350 Added the ability for users to press "Continue" button as a fallback when using the Yubikey if the "SubmitCommand" doesn't trigger automatically.

* PM-3349 PM-3350 Fix for text getting cut/truncated in both account switcher and ciphers/search lists
Issue is due to MAUI but can be avoided by using slightly different layout

* PM-3350 iOS updated CFBundlerShortVersionString to latest one 2023.10.1

* PM-3350 fix build.yml Bitwarden.ipa AppStore exported file

* PM-3350 build.yml added step to validate app for submitting into App Store and have better logs of it

* PM-3350 build.yml Added several fixes like not using MtouchUseLLVM on the iOS builds to fix they taking forever to build and some changes on the automation CI to do a debug build for the moment

* PM-3350 Improved MTouch linking and extra args on iOS related csprojs

* PM-3349 PM-3350 Added MAUI label on self-host settings and on about settings to differentiate from XF app

* PM-3349 PM-3350 build.yml uncommented jobs so we have a more complete workflow

* PM-3349 PM-3350 Minor change: removed unneeded HorizontalTextAlignment from Label.

* PM-3349 Replaced CrossCurrentActivity plugin with MAUI internal CurrentActivity

* PM-3350 Fix iOS extensions navigation and Window/RootViewController handling for TapGestureRecognizer to work

* PM-3350 Cleared left ClipLogger from the iOS extensions debug logging.

* PM-3349 PM-3350 Refactored cipher bindings to have a simpler approach reusing a new CipherItemViewModel to avoid unwanted issues in the app

* PM-3349 Added base structure for avoiding Android Autofill crash. This workaround works but it's not complete as it can't handle the entire workflow when showing CipherSelectionPAge (like checking if it should show LockPage)

* PM-3350 Bumped iOS version

* PM-3350 Changed linker to use default mode given that "Full" is presenting some problems as the linker is stripping things it shouldn't and we're trying to solve it. So for now we will use the mode "Link SDK assemblies only" so QA can test.

* PM-3349 Fix for app crashing on Android when Dark mode is enabled
Removed unused button style for android

* Proof of concept for having multiple window in Android for autofill support and navigating with the help of an Extended splash page.

* PM-3350 Fix crash on Release by adding Interpreter on iOS and also adding System.Security.Cryptography to be ignored by the linker

* PM-3350 Apply Cryptography TrimmerRootAssembly only to iOS

* PM-3350 Updated Plugin.Fingerprint so biometrics work

* Update .github/workflows/build.yml setup-xcode commit hash

Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>

* PM-3349 PM-3350 Enabled argon2id and fixed one issue with the Uris when getting the icon image

* PM-3349 Upgraded Android targetSdkVersion to 34

* minor change (public to private fields)

* minor improvemments on autofill-redirect

* PM-3349 Commented the Deploy step for Android job given that we're using the hotfix-rc branch for testing iOS on TestFlight

* PM-3349 Uncommented the Deploy step for Android job

* PM-3349 Ensure "_isResumed=true" is set on App.xaml.cs:Bootstrap

* Reusing App.xaml.cs Navigation for the Android RedirectPage
Some other cleanup and changes

* Improved autofill workaround to better handle switching between windows.

* PM-3349 minor fix to add space in HomePage between the region picker labels.

* Added some comments and improvemments.

* PM-3349 Added Window events unsubscription of events. Also changed code to avoid potentially having multiple autofillwindow

* PM-3349 Minor ui fix (space between buttons in delete account page)

* initial commit of android credential provider service (wip)

* Revert "initial commit of android credential provider service (wip)"

This reverts commit 6011b63958.

* PM-3350 Fix for Delete Account buttons on iOS

* PM-3349 PM-3350 Changed search icon used in app to avoid issue with icon size on iOS

* PM-3349 Added custom window so that we can always get the current Active Window. This is used to support the Android Autofil and multi-window scenarios.

* PM-3349 Fix for icon and text spacing in some list items

* PM-3349 Minor aligment improvemment for region selection in HomePage

* PM-3349 Changed the "track color" for the Android switch so that the color is different from the "thumb"

* PM-3350 Updated version to 2024.1.0 on iOS

* PM-3349 Fix Picker selection style by doing a custom PickerHandler for Android which uses SetSingleChoiceItems(...) to provide with the appropriate UI

* PM-3350 Updated MauiVersion to 8.0.4-nightly.* to have the TapGestureRecognizer fix applied. This is done on the Directory.Build.props so we don't have to change it on every csproj. Also removed the workaround of TapGestureHack and fix the Show environment picker to work on the extensions as well.

* PM-3350 Added nuget.config so we add the nuget package source for MAUI Nightly builds

* Bump main iOS version

* PM-3350 Removed "iOS" old folder project that has been moved into the MAUI Single app project.

* PM-3350 Improved code safety adding a lot of try...catch and logging throughout the app. Also made the invoking on main thread safer on several places of the app. Additionally, on the GroupingsPageViewModel changed the code removing the old Xamarin hack and just using Replace directly instead of Clearing first to see if that fixes the crash we're having sometimes on the app.

* PM-3350 PM-3349 Updated Unit Test projects to NET 8.0 and fixed it to work with Core project reference. Also fixed a test that was breaking due to CIpherKey creation being wrong. Added "UT" as a constant to add when building/running Core.Test project so we have something on the context that tells us that is for a UT. With this I had to remove FFImageLoading on UT context because it doesn't support NET 8.0

* PM-3350 PM-3349 Updated Readme with MAUI and main branch

* PM-3350 PM-3349 Enable running Core tests

* PM-3350 Fix build.yml format

* PM-3349 Fix navigation when coming from autofill with Accessibility Services enabled. The user was getting into Home page instead of where they were, with this workaround the app navigates as if the account has been switched, leaving the user as closely as possible to where they were, basically on the first screen for the current state of the user.

* PM-3350 PM-3349 Added property to Directory.Build.props to enable Unit Testing globally so Test runners work

* Improve TOTP scan performance on Android

* Move Android camera/scan changes to xaml

* PM-3350 Testing UseInterpreter false on CI build

* PM-3350 Enabled back UseInterpreter on iOS Release given that it crashes on startup without it.

* PM-3349 PM-3350 Improved code safety with try...catch, better invoke on main thread and better null handling.

* PM-3349 PM-3350 Updated XCode version on build.yml to 15.1

* PM-3350 Removed TapGesture Window MAUI hack from iOS.Extension and iOS.ShareExtension

* PM-3350 Fixed CancellationTokenSource proper disposal

* PM-3350 Fix Avatar toolbar icon on extensions to load properly and to take advantage of using directly SkiaSharp to do the native conversion to UIImage. Also improved the toolbar item so that size is set appropriately.

* PM-3349 PM-3350 Fix external link icon

* PM-3350 Added new style to prevent spell check and text prediction

* Fix merge from main

* PM-3350 Commented event collection upload on the timer and when sending the app to background to see if that prevents the app from crashing on release mode.

* PM-3350 Added check for state migration version before trying to migrate LiteDB values into Prefs when there's no need to and that may be inducing crashes on backgrounded iOS apps.

* PM-3350 Try to disable Interpreter to have better crash knowledge. This time testing if avoiding loading the argon2id lib we're able to not use the interpreter.

* PM-5928 Fix circle animation to be shown on verification codes list on each item

* PM-3350 Go back to use Interpreter and added some Directory.Build.props to easily change Codesign properties and also include/exclude iOS extensions / WatchOS from the build.

* PM-3350 Enabled iOS extensions and WatchOS app to be included based on the Directory.Build.props

* PM-3350 Go back to include argon2id and interpreter

* Removing error/loading placeholders of icons on the cells to see if that is causing the background crash on iOS; so we can test this in TestFlight

* [PM-5910] Workaround for for sliding elements in Duo 2FA flow (#2967)

* workaround for sliding elements in duo 2fa flow

* restrict workaround to Android

* restrict workaround to Android

* Revert "restrict workaround to Android"

This reverts commit c2753d5dc4.

* Revert "restrict workaround to Android"

This reverts commit 69688cfb98.

* PM-5902 fix for account switcher not dismissing when tapping outside (#2974)

* PM-3350 Fix iossimulator-x64 argon2id load so we can test on simulators and also made easier to maintain loading the argon2id library on the iOS projects by setting a general Directory.Build.props that is shared.

* PM-5903 Changed App.xaml.cs SetOption to only update the needed properties instead of replacing the existing Options object which would cause the AccountSwitcher button bug (#2973)

* [PM-5896] Fix MAUI iOS Background crash due to lock files on suspension (#2969)

* PM-5896 Fix background crash on iOS due to lock files when app gets suspended. Changed loading and error placeholders of the CachedImage to not be used and use default icon of IconLabel instead changing visibility.

* PM-5896 Changed methods to be protected so that they don't get removed by the linker.

* PM-5896 Added stub class and references to it so to have stronger references to Icon_Success and Icon_Error so the linker doesn't remove them.

* PM-3349 Removed commented code from build.yml regarding FDroid that is not needed anymore.

* PM-6077 Separated Android and iOS HybridWebViewHandler so that it can be used on iOS.Core (#2983)

* [PM-5907] Fix for incorrect TOTP white text color on label when using light theme on iOS (#2982)

* PM-5907 workaround for incorrect textcolor when programmatically changing text on Entry

* Update src/Core/Pages/Vault/CipherAddEditPage.xaml.cs

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* [PM-5906] Fix for incorrect Send MaxAccess white text color on label when using light theme on iOS (#2981)

* PM-5906 workaround for incorrect textcolor when programmatically changing text on Entry

* Update src/Core/Pages/Send/SendAddEditPage.xaml.cs

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* PM-3349 PM-3350 Fixed Unit tests because of referencing FFImageLoading when it's not possible

---------

Co-authored-by: Dinis Vieira <dinisvieira@outlook.com>
Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>
Co-authored-by: mpbw2 <59324545+mpbw2@users.noreply.github.com>
2024-02-08 16:05:26 -03:00
Bitwarden DevOps
f30158adf5 Bumped version to 2024.2.1 (#2985) 2024-02-07 21:25:16 +00:00
renovate[bot]
c6a086fe62 [deps]: Update gh minor (#2977)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-05 09:08:52 -05:00
renovate[bot]
b217451ea9 [deps]: Update crazy-max/ghaction-import-gpg action to v6 (#2978)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-05 09:08:33 -05:00
renovate[bot]
8cb7d5e1a3 [deps]: Update dawidd6/action-download-artifact action to v3 (#2979)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-05 09:03:08 -05:00
Vince Grassia
82b837ef33 Update Version Bump workflow logic (#2975) 2024-02-05 08:49:22 -05:00
André Bispo
ebb2a288a1 [PM-5856] Add visibility to label. (#2955) 2024-01-30 12:30:23 +00:00
Todd Martin
de7ae27a77 [PM-5638] Update minimum version for vault item encryption to 2024.2.0 (#2966) 2024-01-29 17:54:30 -05:00
Vince Grassia
d3dd2e9342 Add files for Version Bump automation (#2965) 2024-01-29 14:15:06 -05:00
Bitwarden DevOps
a5878d3341 Bumped version to 2024.2.0 (#2964) 2024-01-29 13:30:13 -03:00
renovate[bot]
1dc55f78e7 [deps]: Update gh minor (#2960)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-29 10:38:18 -05:00
renovate[bot]
37b62b317f [deps]: Update actions/stale action to v9 (#2961)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-29 10:36:54 -05:00
renovate[bot]
528e412458 [deps]: Update actions/upload-artifact action to v4 (#2962)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-29 10:34:12 -05:00
github-actions[bot]
0f22f2750e Autosync the updated translations (#2963)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-01-29 11:18:32 +00:00
Todd Martin
7bbb711175 [PM-5638] Bump minimum server version for vault item encryption (#2959)
Co-authored-by: Carlos Gonçalves <cgoncalves@bitwarden.com>
2024-01-26 17:03:39 -05:00
github-actions[bot]
fd80a9ce7c Autosync the updated translations (#2957)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-01-26 10:52:01 +00:00
mpbw2
3f10a6be24 Add privacy policy link to about screen (#2954) 2024-01-24 15:07:39 -05:00
renovate[bot]
f3537b1a74 [deps]: Update gh minor (#2544)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-24 10:07:10 -05:00
Vince Grassia
849a0c24b0 Update Renovate config (#2953) 2024-01-24 10:05:30 -05:00
Carlos Gonçalves
f6a58e469f [PM-4739] Implement checksum uri validation (#2893)
* PM-4739 Implement checksum uri validation

* PM-4739 Add missing field

* PM-4739 Fix PR comments

* PM-4739 Remove unnecessary comment

* PM-4739 Add try catch and log exception

* PM-4739 Added missing files from last commit

* PM-4739 Change arg name

* [PM-5461] Fix item saving with blank URI (#2948)

* PM-5461 Fix item saving with blank URI

* PM-5461 Fix PR comment
2024-01-24 13:15:24 +00:00
renovate[bot]
983937c9eb [deps]: Update actions/setup-node action to v4 (#2868)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-23 15:02:04 -05:00
renovate[bot]
b2f93d3d4b [deps]: Update actions/setup-dotnet action to v4 (#2947)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-22 10:31:17 -05:00
Matt Bishop
64c694e593 Fix code ownership (#2946) 2024-01-19 17:31:32 -05:00
renovate[bot]
56b9e3f615 Pin dependency gh-pages to 3.2.3 (#2542)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-19 17:05:36 -05:00
Matt Bishop
7558f60a44 Fix Renovate config (#2945) 2024-01-19 17:04:54 -05:00
github-actions[bot]
e66ac9dd44 Autosync the updated translations (#2944)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-01-19 09:45:12 +00:00
Daniel James Smith
d6c139cb8a Import-link routes to import page after login (#2939)
Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
2024-01-16 12:02:06 -03:00
Daniel James Smith
6b7c6eac71 Import-link routes to import page after login (#2937)
Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
2024-01-16 11:49:40 +00:00
github-actions[bot]
9e1d6c7b03 Autosync the updated translations (#2936)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-01-12 06:46:31 +01:00
Bitwarden DevOps
e107b893ea Bumped version to 2024.1.1 (#2934) 2024-01-10 17:30:04 +00:00
André Bispo
5de02c863f [PM-5633] Ignore ArgumentOutOfRangeException to collect more data about the crash (#2933) 2024-01-10 17:02:54 +00:00
André Bispo
0e95d4d4ca [PM-5542] Update sso endpoint (#2930) 2024-01-09 21:32:42 +00:00
github-actions[bot]
a42b88b666 Autosync the updated translations (#2929)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-01-05 11:37:30 +01:00
Bitwarden DevOps
af6866cee1 Bumped version to 2024.1.0 (#2928) 2024-01-02 14:40:03 +00:00
André Bispo
0cec49f121 [PM-4584] Add device identifier to request headers. (#2909) 2024-01-02 13:10:37 +00:00
github-actions[bot]
d091922017 Autosync the updated translations (#2927)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-01-02 11:49:43 +01:00
1157 changed files with 40100 additions and 216696 deletions

32
.github/CODEOWNERS vendored
View File

@@ -1,18 +1,17 @@
# Please sort lines alphabetically, this will ensure we don't accidentally add duplicates.
# Please sort into logical groups with comment headers. Sort groups in order of specificity.
# For example, default owners should always be the first group.
# Sort lines alphabetically within these groups to avoid accidentally adding duplicates.
#
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
# The following owners will be the default owners for everything in the repo.
# Unless a later match takes precedence
# @bitwarden/tech-leads
# Default file owners
* @bitwarden/dept-development-mobile
@bitwarden/dept-development-mobile
## Auth team files ##
## Platform team files ##
appIcons @bitwarden/team-platform-dev
build.cake @bitwarden/team-platform-dev
## Vault team files ##
src/watchOS @bitwarden/team-vault-dev
@@ -21,17 +20,32 @@ src/watchOS @bitwarden/team-vault-dev
src/Core/Services/EmailForwarders @bitwarden/team-tools-dev
## Crowdin Sync files ##
src/App/Resources @bitwarden/team-tools-dev
src/Core/Resources/Localization @bitwarden/team-tools-dev
src/watchOS/bitwarden/bitwarden\ WatchKit\ Extension/Localization @bitwarden/team-tools-dev
store/apple @bitwarden/team-tools-dev
store/google @bitwarden/team-tools-dev
## Locales ##
src/App/Resources/AppResources.Designer.cs
src/App/Resources/AppResources.resx
src/Core/Resources/Localization/AppResources.Designer.cs
src/Core/Resources/Localization/AppResources.resx
src/watchOS/bitwarden/bitwarden\ WatchKit\ Extension/Localization/en.lproj
store/apple/en
store/google/en
## Utils ##
store/google/Publisher
## These workflows have joint ownership ##
.github/workflows/build.yml @bitwarden/dept-bre @bitwarden/dept-development-mobile
.github/workflows/build-beta.yml @bitwarden/dept-bre @bitwarden/dept-development-mobile
.github/workflows/cleanup-rc-branch.yml @bitwarden/dept-bre @bitwarden/dept-development-mobile
.github/workflows/release.yml @bitwarden/dept-bre @bitwarden/dept-development-mobile
.github/workflows/version-auto-bump.yml @bitwarden/dept-bre @bitwarden/dept-development-mobile
.github/workflows/version-bump.yml @bitwarden/dept-bre @bitwarden/dept-development-mobile
# Shared ownership for version bump automation
src/App/Platforms/Android/AndroidManifest.xml
src/iOS.Autofill/Info.plist
src/iOS.Extension/Info.plist
src/iOS.ShareExtension/Info.plist
src/App/Platforms/iOS/Info.plist

View File

@@ -6,8 +6,20 @@ body:
attributes:
value: |
Thanks for taking the time to fill out this bug report!
> [!WARNING]
> Testing the new Bitwarden Beta apps? Submit your report in [bitwarden/android](https://github.com/bitwarden/android) or [bitwarden/ios](https://github.com/bitwarden/ios)
Please do not submit feature requests. The [Community Forums](https://community.bitwarden.com) has a section for submitting, voting for, and discussing product feature requests.
- type: checkboxes
id: production
attributes:
label: Production Build
options:
- label: I'm using the legacy Bitwarden app pubicly available in App Store / Play Store and I'm aware that Bitwarden Beta bugs should be reported in [bitwarden/android](https://github.com/bitwarden/android) or [bitwarden/ios](https://github.com/bitwarden/ios)
validations:
required: true
- type: textarea
id: reproduce
attributes:
@@ -73,9 +85,3 @@ body:
description: What version of our software are you running? (go to "Settings" → "About" in the app)
validations:
required: true
- type: checkboxes
id: beta
attributes:
label: Beta
options:
- label: Using a pre-release version of the application.

View File

@@ -1,5 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Native Android Beta Bug Reports
url: https://github.com/bitwarden/android/issues
about: Bugs found in the new native Android Beta app should be reported in [bitwarden/android](https://github.com/bitwarden/android)
- name: Native iOS BETA Bug Reports
url: https://github.com/bitwarden/ios/issues
about: Bugs found in the new native iOS Beta app should be reported in [bitwarden/ios](https://github.com/bitwarden/ios)
- name: Customer Support
url: https://bitwarden.com/contact/
about: Please contact our customer support for account issues and general customer support.

35
.github/labeler.yml vendored
View File

@@ -1,19 +1,26 @@
android:
- src/App/*
- src/Core/*
- src/Android/*
- changed-files:
- any-glob-to-any-file:
- src/App/*
- src/Core/*
- src/Android/*
- 'src/Xamarin.AndroidX.Credentials/*'
iOS:
- src/App/*
- src/Core/*
- lib/ios/*
- src/iOS/*
- 'src/iOS.Autofill/*'
- 'src/iOS.Core/*'
- 'src/iOS.Extension/*'
- 'src/iOS.ShareExtension/*'
- 'src/iOS.Widget/*'
- src/watchOS/*
- changed-files:
- any-glob-to-any-file:
- src/App/*
- src/Core/*
- lib/ios/*
- src/iOS/*
- 'src/iOS.Autofill/*'
- 'src/iOS.Core/*'
- 'src/iOS.Extension/*'
- 'src/iOS.ShareExtension/*'
- 'src/iOS.Widget/*'
- src/watchOS/*
watchOS:
- src/watchOS/*
- changed-files:
- any-glob-to-any-file:
- src/watchOS/*

18
.github/renovate.json vendored
View File

@@ -1,23 +1,23 @@
{
"enabled": false,
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base",
"github>bitwarden/renovate-config:pin-actions",
":combinePatchMinorReleases",
":dependencyDashboard",
":maintainLockFilesWeekly",
":pinAllExceptPeerDependencies",
":prConcurrentLimit10",
":rebaseStalePrs",
"schedule:weekends",
":separateMajorReleases"
":separateMajorReleases",
"group:monorepos",
"schedule:weekends"
],
"enabledManagers": ["cargo", "github-actions", "npm", "nuget"],
"enabledManagers": ["github-actions", "npm", "nuget"],
"commitMessagePrefix": "[deps]:",
"commitMessageTopic": "{{depName}}",
"packageRules": [
{
"groupName": "cargo minor",
"matchManagers": ["cargo"],
"matchUpdateTypes": ["minor", "patch"]
},
{
"groupName": "gh minor",
"matchManagers": ["github-actions"],
@@ -32,6 +32,6 @@
"groupName": "nuget minor",
"matchManagers": ["nuget"],
"matchUpdateTypes": ["minor", "patch"]
},
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,3 +0,0 @@
<EFBFBD>
 K<>Y#<23>(<28><><EFBFBD><EFBFBD>EI֐߄T?)l<><6C><EFBFBD><18><><10>"=<3D>|<7C>'e<><0E>m<EFBFBD>/~<7E><>' F<><46>><3E><><EFBFBD><EFBFBD>l<EFBFBD>b<EFBFBD>[<5B>+R<><52>iL<69><4C>"<22><><EFBFBD>~V:<3A><>p<EFBFBD>a<17>ڵel%8t<38><74><EFBFBD>y<<3C>n<EFBFBD><6E><EFBFBD>aU<61>w<16>JD<4A><44><1F><>We<57>9<EFBFBD><39><EFBFBD><EFBFBD><x8d<38>O<EFBFBD>j\<14>ד<EFBFBD><D793><EFBFBD>Vq<56><71>֋
Ǻ<EFBFBD>-<2D>#<23><><11><>]$<24>(<28>l,<2C>Br<42><02><>d<><64><EFBFBD>•a-<2D><><EFBFBD>:<3A><>:<3A><04>9b,!Em<02><19><>Qf<>D<EFBFBD>g<EFBFBD><06><0E>x(P<>ȡ~<7E>͹<EFBFBD><CDB9> <09><>[<06><>!:<3A>;f<><66>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,4 +1,3 @@
---
name: Automatic responses
on:
issues:
@@ -7,7 +6,7 @@ on:
jobs:
close-issue:
name: 'Close issue with automatic response'
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
permissions:
issues: write
steps:

349
.github/workflows/build-beta.yml vendored Normal file
View File

@@ -0,0 +1,349 @@
name: Build Beta
on:
workflow_dispatch:
inputs:
ref:
description: 'Branch or tag to build'
required: true
default: 'main'
type: string
env:
main_app_folder_path: src/App
main_app_project_path: src/App/App.csproj
target-net-version: net8.0
jobs:
setup:
name: Setup
runs-on: ubuntu-22.04
outputs:
rc_branch_exists: ${{ steps.branch-check.outputs.rc_branch_exists }}
hotfix_branch_exists: ${{ steps.branch-check.outputs.hotfix_branch_exists }}
steps:
- name: Checkout repo
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
submodules: 'true'
- name: Check if special branches exist
id: branch-check
run: |
if [[ $(git ls-remote --heads origin rc) ]]; then
echo "rc_branch_exists=1" >> $GITHUB_OUTPUT
else
echo "rc_branch_exists=0" >> $GITHUB_OUTPUT
fi
if [[ $(git ls-remote --heads origin hotfix-rc) ]]; then
echo "hotfix_branch_exists=1" >> $GITHUB_OUTPUT
else
echo "hotfix_branch_exists=0" >> $GITHUB_OUTPUT
fi
ios:
name: Apple iOS
runs-on: macos-14
needs: setup
env:
_IOS_FOLDER_PATH: src/App/Platforms/iOS
_APP_OUTPUT_NAME: App
_APP_CI_OUTPUT_FILENAME: App_x64_Debug
steps:
- name: Set XCode version
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
with:
xcode-version: 15.1
- name: Setup NuGet
uses: nuget/setup-nuget@a21f25cd3998bf370fde17e3f1b4c12c175172f9 # v2.0.0
with:
nuget-version: 6.4.0
- name: Set up .NET
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
with:
dotnet-version: '8.0.x'
# This step might be obsolete at some point as .NET MAUI workloads
# are starting to come pre-installed on the GH Actions build agents.
- name: Install MAUI Workload
run: dotnet workload install maui --ignore-failed-sources
- name: Print environment
run: |
nuget help | grep Version
dotnet --info
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0
ref: ${{ inputs.ref }}
submodules: 'true'
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve secrets
id: retrieve-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: "bitwarden-ci"
secrets: "appcenter-ios-token"
- name: Download Provisioning Profiles secrets
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: profiles
run: |
mkdir -p $HOME/secrets
profiles=(
"dist_beta_autofill.mobileprovision"
"dist_beta_bitwarden.mobileprovision"
"dist_beta_extension.mobileprovision"
"dist_beta_share_extension.mobileprovision"
"dist_beta_bitwarden_watch_app.mobileprovision"
"dist_beta_bitwarden_watch_app_extension.mobileprovision"
)
for FILE in "${profiles[@]}"
do
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
--file $HOME/secrets/$FILE --output none
done
- name: Download Google Services secret
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
FILE: GoogleService-Info.plist
run: |
mkdir -p $HOME/secrets
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
--file $HOME/secrets/$FILE --output none
- name: Increment version
run: |
BUILD_NUMBER=$((100 + $GITHUB_RUN_NUMBER))
echo "##### Setting CFBundleVersion $BUILD_NUMBER"
echo "### CFBundleVersion $BUILD_NUMBER" >> $GITHUB_STEP_SUMMARY
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./${{ env._IOS_FOLDER_PATH }}/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Extension/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.ShareExtension/Info.plist
cd src/watchOS/bitwarden
agvtool new-version -all $BUILD_NUMBER
- name: Update Entitlements
run: |
echo "##### Updating Entitlements"
perl -0777 -pi.bak -e 's/<key>aps-environment<\/key>\s*<string>development<\/string>/<key>aps-environment<\/key>\n\t<string>beta<\/string>/' ./${{ env._IOS_FOLDER_PATH }}/Entitlements.plist
- name: Get certificates
run: |
mkdir -p $HOME/certificates
az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/ios-distribution |
jq -r .value | base64 -d > $HOME/certificates/ios-distribution.p12
- name: Set up Keychain
env:
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
MOBILE_KEY_PASSWORD: ${{ secrets.IOS_KEY_PASSWORD }}
DIST_CERT_PASSWORD: ${{ secrets.IOS_DIST_CERT_PASSWORD }}
run: |
security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p $KEYCHAIN_PASSWORD build.keychain
security set-keychain-settings -lut 1200 build.keychain
security import $HOME/certificates/ios-distribution.p12 -k build.keychain -P "" -T /usr/bin/codesign \
-T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain
- name: Set up provisioning profiles
run: |
AUTOFILL_PROFILE_PATH=$HOME/secrets/dist_beta_autofill.mobileprovision
BITWARDEN_PROFILE_PATH=$HOME/secrets/dist_beta_bitwarden.mobileprovision
EXTENSION_PROFILE_PATH=$HOME/secrets/dist_beta_extension.mobileprovision
SHARE_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_beta_share_extension.mobileprovision
WATCH_APP_PROFILE_PATH=$HOME/secrets/dist_beta_bitwarden_watch_app.mobileprovision
WATCH_APP_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_beta_bitwarden_watch_app_extension.mobileprovision
PROFILES_DIR_PATH=$HOME/Library/MobileDevice/Provisioning\ Profiles
mkdir -p "$PROFILES_DIR_PATH"
AUTOFILL_UUID=$(grep UUID -A1 -a $AUTOFILL_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
cp $AUTOFILL_PROFILE_PATH "$PROFILES_DIR_PATH/$AUTOFILL_UUID.mobileprovision"
BITWARDEN_UUID=$(grep UUID -A1 -a $BITWARDEN_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
cp $BITWARDEN_PROFILE_PATH "$PROFILES_DIR_PATH/$BITWARDEN_UUID.mobileprovision"
EXTENSION_UUID=$(grep UUID -A1 -a $EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
cp $EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$EXTENSION_UUID.mobileprovision"
SHARE_EXTENSION_UUID=$(grep UUID -A1 -a $SHARE_EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
cp $SHARE_EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$SHARE_EXTENSION_UUID.mobileprovision"
WATCH_APP_UUID=$(grep UUID -A1 -a $WATCH_APP_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
cp $WATCH_APP_PROFILE_PATH "$PROFILES_DIR_PATH/$WATCH_APP_UUID.mobileprovision"
WATCH_APP_EXTENSION_UUID=$(grep UUID -A1 -a $WATCH_APP_EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
cp $WATCH_APP_EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$WATCH_APP_EXTENSION_UUID.mobileprovision"
- name: Restore packages
run: |
dotnet restore
dotnet tool restore
- name: Setup iOS build CAKE (Testing)
run: dotnet cake build.cake --target iOS --variant beta
- name: Bulid WatchApp
run: |
echo "##### Build WatchApp with Release Configuration"
xcodebuild archive -workspace ./src/watchOS/bitwarden/bitwarden.xcodeproj/project.xcworkspace -configuration Release -scheme bitwarden\ WatchKit\ App -archivePath ./src/watchOS/bitwarden
echo "##### Done"
- name: Archive Build for App Store
shell: pwsh
run: |
Write-Output "##### Archive for Release ios-arm64"
dotnet publish ${{ env.main_app_project_path }} -c Release -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=ios-arm64 /p:ArchiveOnBuild=true /p:MtouchUseLlvm=false
Write-Output "##### Done"
- name: Archive Build for Mobile Automation
shell: pwsh
run: |
Write-Output "##### Archive Debug for iossimulator-x64"
dotnet build ${{ env.main_app_project_path }} -c Debug -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=iossimulator-x64 /p:ArchiveOnBuild=true /p:MtouchUseLlvm=false
Write-Output "##### Done"
ls ~/Library/Developer/Xcode/Archives
- name: Export .ipa for App Store
env:
EXPORT_OPTIONS_PATH: ./.github/resources/export-options-app-store.plist
EXPORT_PATH: ./bitwarden-export
run: |
ARCHIVE_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive"
xcodebuild -exportArchive -archivePath $ARCHIVE_PATH -exportPath $EXPORT_PATH \
-exportOptionsPlist $EXPORT_OPTIONS_PATH
- name: Export .app for Automation CI
env:
ARCHIVE_PATH: ./${{ env.main_app_folder_path }}/bin/Debug/${{ env.target-net-version }}-ios/iossimulator-x64
EXPORT_PATH: ./bitwarden-export
run: |
zip -r -q ${{ env._APP_CI_OUTPUT_FILENAME }}.app.zip $ARCHIVE_PATH
mv ${{ env._APP_CI_OUTPUT_FILENAME }}.app.zip $EXPORT_PATH
- name: Show Bitwarden Export
shell: bash
run: ls -a -R ./bitwarden-export
- name: Copy all dSYMs files to upload
env:
EXPORT_PATH: ./bitwarden-export
WATCH_ARCHIVE_DSYMS_PATH: ./src/watchOS/bitwarden.xcarchive/dSYMs/
WATCH_DSYMS_EXPORT_PATH: ./bitwarden-export/Watch_dSYMs
run: |
ARCHIVE_DSYMS_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive/dSYMs"
cp -r -v $ARCHIVE_DSYMS_PATH $EXPORT_PATH
mkdir $WATCH_DSYMS_EXPORT_PATH
cp -r -v $WATCH_ARCHIVE_DSYMS_PATH $WATCH_DSYMS_EXPORT_PATH
- name: Upload App Store .ipa & dSYMs artifacts
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: Bitwarden iOS
path: |
./bitwarden-export/Bitwarden*.ipa
./bitwarden-export/dSYMs/*.*
if-no-files-found: error
- name: Upload .app file for Automation CI
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: ${{ env._APP_CI_OUTPUT_FILENAME }}.app.zip
path: ./bitwarden-export/${{ env._APP_CI_OUTPUT_FILENAME }}.app.zip
if-no-files-found: error
- name: Install AppCenter CLI
run: npm install -g appcenter-cli
- name: Upload dSYMs to App Center
env:
APPCENTER_IOS_TOKEN: ${{ steps.retrieve-secrets.outputs.appcenter-ios-token }}
run: appcenter crashes upload-symbols -a bitwarden/bitwarden -s "./bitwarden-export/dSYMs" --token $APPCENTER_IOS_TOKEN
- name: Upload Watch dSYMs to Firebase Crashlytics
run: |
echo "##### Uploading Watch dSYMs to Firebase"
find "$HOME/Library/Developer/XCode/DerivedData" -name "upload-symbols" -exec chmod +x {} \; -exec {} -gsp "./src/watchOS/bitwarden/GoogleService-Info.plist" -p ios "./bitwarden-export/Watch_dSYMs" \;
- name: Validate app in App Store
env:
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
run: |
xcrun altool --validate-app --type ios --file "./bitwarden-export/Bitwarden Beta.ipa" \
--username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
shell: bash
- name: Deploy to App Store
env:
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
run: |
xcrun altool --upload-app --type ios --file "./bitwarden-export/Bitwarden Beta.ipa" \
--username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
check-failures:
name: Check for failures
if: always()
runs-on: ubuntu-22.04
needs:
- setup
- ios
steps:
- name: Check if any job failed
if: |
(github.ref == 'refs/heads/main'
|| github.ref == 'refs/heads/rc'
|| github.ref == 'refs/heads/hotfix-rc')
&& contains(needs.*.result, 'failure')
run: exit 1
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
if: failure()
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve secrets
id: retrieve-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@main
if: failure()
with:
keyvault: "bitwarden-ci"
secrets: "devops-alerts-slack-webhook-url"
- name: Notify Slack on failure
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
if: failure()
env:
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
with:
status: ${{ job.status }}

View File

@@ -1,22 +1,22 @@
---
name: Build
on:
push:
branches-ignore:
- "l10n_master"
- "gh-pages"
paths-ignore:
- ".github/workflows/**"
workflow_dispatch:
env:
main_app_folder_path: src/App
main_app_project_path: src/App/App.csproj
target-net-version: net8.0
dotnet-version: '8.0.402'
maui-workload-version: '8.0.402'
jobs:
cloc:
name: CLOC
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Set up CLOC
run: |
@@ -35,7 +35,7 @@ jobs:
hotfix_branch_exists: ${{ steps.branch-check.outputs.hotfix_branch_exists }}
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
submodules: 'true'
@@ -63,19 +63,31 @@ jobs:
fail-fast: false
matrix:
variant: ["prod", "qa"]
env:
_ANDROID_FOLDER_PATH: src\App\Platforms\Android
_ANDROID_FOLDER_PATH_BASH: src/App/Platforms/Android
steps:
- name: Setup NuGet
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
- name: Checkout repo
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
nuget-version: 5.9.0
fetch-depth: 0
- name: Setup NuGet
uses: nuget/setup-nuget@a21f25cd3998bf370fde17e3f1b4c12c175172f9 # v2.0.0
with:
nuget-version: 6.4.0
- name: Set up .NET
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
with:
dotnet-version: '3.1.x'
dotnet-version: ${{ env.dotnet-version }}
- name: Install MAUI Workload
run: |
dotnet workload install maui --version ${{ env.maui-workload-version }}
- name: Set up MSBuild
uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # v1.3.1
uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2.0.0
- name: Setup Windows builder
run: choco install checksum --no-progress
@@ -83,7 +95,8 @@ jobs:
- name: Install Microsoft OpenJDK 11
run: |
choco install microsoft-openjdk11 --no-progress
Write-Output "JAVA_HOME=$(Get-ChildItem -Path 'C:\Program Files\Microsoft\jdk*' | Select -First 1 -ExpandProperty FullName)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Output "JAVA_HOME=$(Get-ChildItem -Path 'C:\Program Files\Microsoft\jdk*' | `
Select -First 1 -ExpandProperty FullName)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Output "Java Home: $env:JAVA_HOME"
- name: Print environment
@@ -94,44 +107,43 @@ jobs:
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
fetch-depth: 0
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Decrypt secrets
- name: Download secrets
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
run: |
mkdir -p ~/secrets
mkdir -p $HOME/secrets
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./src/Android/app_play-keystore.jks ./.github/secrets/app_play-keystore.jks.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./src/Android/app_upload-keystore.jks ./.github/secrets/app_upload-keystore.jks.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/play_creds.json ./.github/secrets/play_creds.json.gpg
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name app_play-keystore.jks --file ./${{ env._ANDROID_FOLDER_PATH_BASH }}/app_play-keystore.jks --output none
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name app_upload-keystore.jks --file ./${{ env._ANDROID_FOLDER_PATH_BASH }}/app_upload-keystore.jks --output none
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name play_creds.json --file $HOME/secrets/play_creds.json --output none
shell: bash
- name: Decrypt secrets - Google Services
- name: Download secrets - Google Services
if: ${{ matrix.variant == 'prod' }}
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
run: |
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./src/Android/google-services.json ./.github/secrets/google-services.json.gpg
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name google-services.json --file ./${{ env._ANDROID_FOLDER_PATH_BASH }}/google-services.json --output none
shell: bash
- name: Increment version
run: |
BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))
echo "########################################"
echo "##### Setting Version Code $BUILD_NUMBER"
echo "########################################"
BUILD_NUMBER=$((11000 + $GITHUB_RUN_NUMBER))
echo "##### Setting Android Version Code to $BUILD_NUMBER" | tee -a $GITHUB_STEP_SUMMARY
sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
./src/Android/Properties/AndroidManifest.xml
./${{ env._ANDROID_FOLDER_PATH_BASH }}/AndroidManifest.xml
shell: bash
- name: Restore packages
@@ -140,87 +152,75 @@ jobs:
- name: Restore tools
run: dotnet tool restore
- name: Verify Format
run: dotnet tool run dotnet-format --check
# - name: Run Core tests
# run: |
# dotnet test test/Core.Test/Core.Test.csproj --logger "trx;LogFileName=test-results.trx" `
# /p:CustomConstants=UT
- name: Run Core tests
run: dotnet test test/Core.Test/Core.Test.csproj --logger "trx;LogFileName=test-results.trx"
- name: Report test results
uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226 # v1.6.0
if: always()
with:
name: Test Results
path: "**/test-results.trx"
reporter: dotnet-trx
fail-on-error: true
# - name: Report test results
# uses: dorny/test-reporter@eaa763f6ffc21c7a37837f56cd5f9737f27fc6c8 # v1.8.0
# if: always()
# with:
# name: Test Results
# path: "**/test-results.trx"
# reporter: dotnet-trx
# fail-on-error: true
- name: Build Play Store publisher
if: ${{ matrix.variant == 'prod' }}
run: dotnet build ./store/google/Publisher/Publisher.csproj -p:Configuration=Release
run: dotnet build .\store\google\Publisher\Publisher.csproj /p:Configuration=Release
- name: Setup Android build (${{ matrix.variant }})
run: dotnet cake build.cake --target Android --variant ${{ matrix.variant }}
- name: Build Android
run: |
$configuration = "Release";
Write-Output "########################################"
Write-Output "##### Build $configuration Configuration"
Write-Output "########################################"
msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" "/p:Configuration=$configuration"
- name: Sign Android Build
- name: Build & Sign Android
env:
PLAY_KEYSTORE_PASSWORD: ${{ secrets.PLAY_KEYSTORE_PASSWORD }}
UPLOAD_KEYSTORE_PASSWORD: ${{ secrets.UPLOAD_KEYSTORE_PASSWORD }}
run: |
$androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
$projToBuild = "$($env:GITHUB_WORKSPACE)/${{ env.main_app_project_path }}";
$packageName = "com.x8bit.bitwarden";
if ("${{ matrix.variant }}" -ne "prod")
{
$packageName = "com.x8bit.bitwarden.${{ matrix.variant }}";
}
Write-Output "########################################"
Write-Output "##### Sign Google Play Bundle Release Configuration"
Write-Output "########################################"
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" `
"/p:AndroidSigningKeyAlias=upload" "/p:AndroidSigningKeyPass=$($env:UPLOAD_KEYSTORE_PASSWORD)" `
"/p:AndroidSigningKeyStore=$("app_upload-keystore.jks")" `
"/p:AndroidSigningStorePass=$($env:UPLOAD_KEYSTORE_PASSWORD)" "/p:AndroidPackageFormat=aab" "/v:quiet"
$signingUploadKeyStore = "$($env:GITHUB_WORKSPACE)\${{ env._ANDROID_FOLDER_PATH }}\app_upload-keystore.jks"
dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android `
/p:AndroidPackageFormats=aab `
/p:AndroidKeyStore=true `
/p:AndroidSigningKeyStore=$signingUploadKeyStore `
/p:AndroidSigningKeyAlias=upload `
/p:AndroidSigningKeyPass="$($env:UPLOAD_KEYSTORE_PASSWORD)" `
/p:AndroidSigningStorePass="$($env:UPLOAD_KEYSTORE_PASSWORD)" --no-restore
Write-Output "########################################"
Write-Output "##### Copy Google Play Bundle to project root"
Write-Output "########################################"
$signedAabPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/Release/$($packageName)-Signed.aab");
$signedAabDestPath = $($env:GITHUB_WORKSPACE + "/$($packageName).aab");
$signedAabPath = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_folder_path }}\bin\Release\${{ env.target-net-version }}-android\publish\$($packageName)-Signed.aab";
$signedAabDestPath = "$($env:GITHUB_WORKSPACE)\$($packageName).aab";
Copy-Item $signedAabPath $signedAabDestPath
Write-Output "########################################"
Write-Output "##### Sign APK Release Configuration"
Write-Output "########################################"
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" `
"/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:PLAY_KEYSTORE_PASSWORD)" `
"/p:AndroidSigningKeyStore=$("app_play-keystore.jks")" `
"/p:AndroidSigningStorePass=$($env:PLAY_KEYSTORE_PASSWORD)" "/v:quiet"
$signingPlayKeyStore = "$($env:GITHUB_WORKSPACE)\${{ env._ANDROID_FOLDER_PATH }}\app_play-keystore.jks"
dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android `
/p:AndroidKeyStore=true `
/p:AndroidSigningKeyStore=$signingPlayKeyStore `
/p:AndroidSigningKeyAlias=bitwarden `
/p:AndroidSigningKeyPass="$($env:PLAY_KEYSTORE_PASSWORD)" `
/p:AndroidSigningStorePass="$($env:PLAY_KEYSTORE_PASSWORD)" --no-restore
Write-Output "########################################"
Write-Output "##### Copy Release APK to project root"
Write-Output "########################################"
$signedApkPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/Release/$($packageName)-Signed.apk");
$signedApkDestPath = $($env:GITHUB_WORKSPACE + "/$($packageName).apk");
$signedApkPath = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_folder_path }}\bin\Release\${{ env.target-net-version }}-android\publish\$($packageName)-Signed.apk";
$signedApkDestPath = "$($env:GITHUB_WORKSPACE)\$($packageName).apk";
Copy-Item $signedApkPath $signedApkDestPath
- name: Upload Prod .aab artifact
if: ${{ matrix.variant == 'prod' }}
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: com.x8bit.bitwarden.aab
path: ./com.x8bit.bitwarden.aab
@@ -228,7 +228,7 @@ jobs:
- name: Upload Prod .apk artifact
if: ${{ matrix.variant == 'prod' }}
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: com.x8bit.bitwarden.apk
path: ./com.x8bit.bitwarden.apk
@@ -236,7 +236,7 @@ jobs:
- name: Upload Other .apk artifact
if: ${{ matrix.variant != 'prod' }}
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: com.x8bit.bitwarden.${{ matrix.variant }}.apk
path: ./com.x8bit.bitwarden.${{ matrix.variant }}.apk
@@ -256,7 +256,7 @@ jobs:
- name: Upload .apk sha file for prod
if: ${{ matrix.variant == 'prod' }}
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: bw-android-apk-sha256.txt
path: ./bw-android-apk-sha256.txt
@@ -264,7 +264,7 @@ jobs:
- name: Upload .apk sha file for other
if: ${{ matrix.variant != 'prod' }}
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: bw-android-${{ matrix.variant }}-apk-sha256.txt
path: ./bw-android-${{ matrix.variant }}-apk-sha256.txt
@@ -277,26 +277,43 @@ jobs:
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|| github.ref == 'refs/heads/hotfix-rc' ) }}
run: |
PUBLISHER_PATH="$GITHUB_WORKSPACE/store/google/Publisher/bin/Release/netcoreapp3.1/Publisher.dll"
CREDS_PATH="$HOME/secrets/play_creds.json"
AAB_PATH="$GITHUB_WORKSPACE/com.x8bit.bitwarden.aab"
TRACK="internal"
$publisherPath = "$($env:GITHUB_WORKSPACE)\store\google\Publisher\bin\Release\net8.0\Publisher.dll"
$credsPath = "$($HOME)\secrets\play_creds.json"
$aabPath = "$($env:GITHUB_WORKSPACE)\com.x8bit.bitwarden.aab"
$track = "internal"
dotnet $PUBLISHER_PATH $CREDS_PATH $AAB_PATH $TRACK
shell: bash
dotnet $publisherPath $credsPath $aabPath $track
f-droid:
name: F-Droid Build
runs-on: windows-2022
env:
_ANDROID_FOLDER_PATH: src\App\Platforms\Android
_ANDROID_FOLDER_PATH_BASH: src/App/Platforms/Android
_ANDROID_MANIFEST_PATH: src/App/Platforms/Android/AndroidManifest.xml
steps:
- name: Setup NuGet
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
- name: Checkout repo
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
nuget-version: 5.9.0
fetch-depth: 0
- name: Setup NuGet
uses: nuget/setup-nuget@a21f25cd3998bf370fde17e3f1b4c12c175172f9 # v2.0.0
with:
nuget-version: 6.4.0
- name: Set up .NET
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
with:
dotnet-version: ${{ env.dotnet-version }}
- name: Install MAUI Workload
run: |
dotnet workload install maui --version ${{ env.maui-workload-version }}
- name: Set up MSBuild
uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # v1.3.1
uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2.0.0
- name: Setup Windows builder
run: choco install checksum --no-progress
@@ -315,57 +332,42 @@ jobs:
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Decrypt secrets
- name: Download secrets
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
FILE: app_fdroid-keystore.jks
run: |
mkdir -p ~/secrets
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./src/Android/app_fdroid-keystore.jks ./.github/secrets/app_fdroid-keystore.jks.gpg
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
--file ${{ env._ANDROID_FOLDER_PATH_BASH }}/$FILE --output none
shell: bash
- name: Increment version
run: |
BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))
echo "########################################"
echo "##### Setting Version Code $BUILD_NUMBER"
echo "########################################"
BUILD_NUMBER=$((11000 + $GITHUB_RUN_NUMBER))
echo "##### Setting F-Droid Version Code to $BUILD_NUMBER" | tee -a $GITHUB_STEP_SUMMARY
sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
./src/Android/Properties/AndroidManifest.xml
./${{ env._ANDROID_MANIFEST_PATH }}
shell: bash
- name: Clean for F-Droid
run: |
$androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
$appPath = $($env:GITHUB_WORKSPACE + "/src/App/App.csproj");
$corePath = $($env:GITHUB_WORKSPACE + "/src/Core/Core.csproj");
$directoryBuildProps = $($env:GITHUB_WORKSPACE + "/Directory.Build.props");
$androidManifest = $($env:GITHUB_WORKSPACE + "/src/Android/Properties/AndroidManifest.xml");
$androidManifest = $($env:GITHUB_WORKSPACE + "/${{ env._ANDROID_MANIFEST_PATH }}");
Write-Output "########################################"
Write-Output "##### Clean Android and App"
Write-Output "########################################"
msbuild "$($androidPath)" "/t:Clean" "/p:Configuration=FDroid"
msbuild "$($appPath)" "/t:Clean" "/p:Configuration=FDroid"
Write-Output "########################################"
Write-Output "##### Backup project files"
Write-Output "########################################"
Write-Output "##### Back up project files"
Copy-Item $androidManifest $($androidManifest + ".original");
Copy-Item $androidPath $($androidPath + ".original");
Copy-Item $appPath $($appPath + ".original");
Copy-Item $directoryBuildProps $($directoryBuildProps + ".original");
Write-Output "########################################"
Write-Output "##### Cleanup Android Manifest"
Write-Output "########################################"
$xml=New-Object XML;
$xml.Load($androidManifest);
@@ -375,80 +377,39 @@ jobs:
$xml.Save($androidManifest);
Write-Output "########################################"
Write-Output "##### Uninstall from Android.csproj"
Write-Output "########################################"
Write-Output "##### Enabling FDROID constant"
$xml=New-Object XML;
$xml.Load($androidPath);
$ns=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
$ns.AddNamespace("ns", $xml.DocumentElement.NamespaceURI);
$firebaseNode=$xml.SelectSingleNode(`
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Firebase.Messaging']", $ns);
$firebaseNode.ParentNode.RemoveChild($firebaseNode);
$daggerNode=$xml.SelectSingleNode(`
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Google.Dagger']", $ns);
$daggerNode.ParentNode.RemoveChild($daggerNode);
$safetyNetNode=$xml.SelectSingleNode(`
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.GooglePlayServices.SafetyNet']", $ns);
$safetyNetNode.ParentNode.RemoveChild($safetyNetNode);
$xml.Save($androidPath);
Write-Output "########################################"
Write-Output "##### Uninstall from Core.csproj"
Write-Output "########################################"
$xml=New-Object XML;
$xml.Load($corePath);
$appCenterNode=$xml.SelectSingleNode("/Project/ItemGroup/PackageReference[@Include='Microsoft.AppCenter.Crashes']");
$appCenterNode.ParentNode.RemoveChild($appCenterNode);
$xml.Save($corePath);
(Get-Content $directoryBuildProps).Replace('<!-- <CustomConstants>FDROID</CustomConstants> -->', '<CustomConstants>FDROID</CustomConstants>') | Set-Content $directoryBuildProps
- name: Restore packages
run: nuget restore
run: dotnet restore
- name: Build for F-Droid
run: |
$configuration = "FDroid";
Write-Output "########################################"
Write-Output "##### Build $configuration Configuration"
Write-Output "########################################"
msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" "/p:Configuration=$configuration"
- name: Sign for F-Droid
- name: Build & Sign F-Droid
env:
FDROID_KEYSTORE_PASSWORD: ${{ secrets.FDROID_KEYSTORE_PASSWORD }}
run: |
Write-Output "########################################"
Write-Output "##### Sign FDroid Configuration"
Write-Output "########################################"
$projToBuild = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_project_path }}";
$packageName = "com.x8bit.bitwarden";
msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" `
"/t:SignAndroidPackage" "/p:Configuration=FDroid" "/p:AndroidKeyStore=true" `
"/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:FDROID_KEYSTORE_PASSWORD)" `
"/p:AndroidSigningKeyStore=$("app_fdroid-keystore.jks")" `
"/p:AndroidSigningStorePass=$($env:FDROID_KEYSTORE_PASSWORD)" "/v:quiet"
Write-Output "##### Sign FDroid"
$signingFdroidKeyStore = "$($env:GITHUB_WORKSPACE)\${{ env._ANDROID_FOLDER_PATH }}\app_fdroid-keystore.jks"
dotnet build $projToBuild -c Release -f ${{ env.target-net-version }}-android `
/p:AndroidKeyStore=true `
/p:AndroidSigningKeyStore=$signingFdroidKeyStore `
/p:AndroidSigningKeyAlias=bitwarden `
/p:AndroidSigningKeyPass="$($env:FDROID_KEYSTORE_PASSWORD)" `
/p:AndroidSigningStorePass="$($env:FDROID_KEYSTORE_PASSWORD)" ` --no-restore
Write-Output "########################################"
Write-Output "##### Copy FDroid apk to project root"
Write-Output "########################################"
$signedApkPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/FDroid/com.x8bit.bitwarden-Signed.apk");
$signedApkDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden-fdroid.apk");
$signedApkPath = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_folder_path }}\bin\Release\${{ env.target-net-version }}-android\$($packageName)-Signed.apk";
$signedApkDestPath = "$($env:GITHUB_WORKSPACE)\com.x8bit.bitwarden-fdroid.apk";
Copy-Item $signedApkPath $signedApkDestPath
- name: Upload F-Droid .apk artifact
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: com.x8bit.bitwarden-fdroid.apk
path: ./com.x8bit.bitwarden-fdroid.apk
@@ -460,7 +421,7 @@ jobs:
-t sha256 | Out-File -Encoding ASCII ./bw-fdroid-apk-sha256.txt
- name: Upload F-Droid sha file
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: bw-fdroid-apk-sha256.txt
path: ./bw-fdroid-apk-sha256.txt
@@ -469,29 +430,45 @@ jobs:
ios:
name: Apple iOS
runs-on: macos-12
runs-on: macos-14
needs: setup
env:
_IOS_FOLDER_PATH: src/App/Platforms/iOS
_APP_OUTPUT_NAME: App
_APP_CI_OUTPUT_FILENAME: App_x64_Debug
steps:
- name: Setup NuGet
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
- name: Checkout repo
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
nuget-version: 5.9.0
submodules: 'true'
- name: Set XCode version
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
with:
xcode-version: 15.4
- name: Setup NuGet
uses: nuget/setup-nuget@a21f25cd3998bf370fde17e3f1b4c12c175172f9 # v2.0.0
with:
nuget-version: 6.4.0
- name: Set up .NET
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
with:
dotnet-version: ${{ env.dotnet-version }}
- name: Install MAUI Workload
run: dotnet workload install maui --version ${{ env.maui-workload-version }}
- name: Print environment
run: |
nuget help | grep Version
msbuild -version
dotnet --info
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
submodules: 'true'
- name: Login to Azure - CI Subscription
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
@@ -502,71 +479,71 @@ jobs:
keyvault: "bitwarden-ci"
secrets: "appcenter-ios-token"
- name: Decrypt secrets
- name: Download Provisioning Profiles secrets
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: profiles
run: |
mkdir -p ~/secrets
mkdir -p $HOME/secrets
profiles=(
"dist_autofill.mobileprovision"
"dist_bitwarden.mobileprovision"
"dist_extension.mobileprovision"
"dist_share_extension.mobileprovision"
"dist_bitwarden_watch_app.mobileprovision"
"dist_bitwarden_watch_app_extension.mobileprovision"
)
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/bitwarden-mobile-key.p12 ./.github/secrets/bitwarden-mobile-key.p12.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/iphone-distribution-cert.p12 ./.github/secrets/iphone-distribution-cert.p12.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/dist_autofill.mobileprovision ./.github/secrets/dist_autofill.mobileprovision.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/dist_bitwarden.mobileprovision ./.github/secrets/dist_bitwarden.mobileprovision.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/dist_extension.mobileprovision ./.github/secrets/dist_extension.mobileprovision.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/dist_share_extension.mobileprovision \
./.github/secrets/dist_share_extension.mobileprovision.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/dist_watch_app.mobileprovision \
./.github/secrets/dist_watch_app.mobileprovision.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/dist_watch_app_extension.mobileprovision \
./.github/secrets/dist_watch_app_extension.mobileprovision.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./src/watchOS/bitwarden/GoogleService-Info.plist ./.github/secrets/GoogleService-Info.plist.gpg
for FILE in "${profiles[@]}"
do
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
--file $HOME/secrets/$FILE --output none
done
- name: Download Google Services secret
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
FILE: GoogleService-Info.plist
run: |
mkdir -p $HOME/secrets
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
--file src/watchOS/bitwarden/$FILE --output none
- name: Increment version
run: |
BUILD_NUMBER=$((100 + $GITHUB_RUN_NUMBER))
BUILD_NUMBER=$((8000 + $GITHUB_RUN_NUMBER))
echo "##### Setting iOS CFBundleVersion to $BUILD_NUMBER" | tee -a $GITHUB_STEP_SUMMARY
echo "########################################"
echo "##### Setting CFBundleVersion $BUILD_NUMBER"
echo "########################################"
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./${{ env._IOS_FOLDER_PATH }}/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Extension/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.ShareExtension/Info.plist
cd src/watchOS/bitwarden
agvtool new-version -all $BUILD_NUMBER
agvtool new-version -all $BUILD_NUMBER
- name: Update Entitlements
run: |
echo "########################################"
echo "##### Updating Entitlements"
echo "########################################"
perl -0777 -pi.bak -e 's/<key>aps-environment<\/key>\s*<string>development<\/string>/<key>aps-environment<\/key>\n\t<string>production<\/string>/' ./${{ env._IOS_FOLDER_PATH }}/Entitlements.plist
perl -0777 -pi.bak -e 's/<key>aps-environment<\/key>\s*<string>development<\/string>/<key>aps-environment<\/key>\n\t<string>production<\/string>/' ./src/iOS/Entitlements.plist
- name: Get certificates
run: |
mkdir -p $HOME/certificates
az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/ios-distribution |
jq -r .value | base64 -d > $HOME/certificates/ios-distribution.p12
- name: Set up Keychain
env:
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
MOBILE_KEY_PASSWORD: ${{ secrets.IOS_KEY_PASSWORD }}
DIST_CERT_PASSWORD: ${{ secrets.IOS_DIST_CERT_PASSWORD }}
run: |
security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p $KEYCHAIN_PASSWORD build.keychain
security set-keychain-settings -lut 1200 build.keychain
security import ~/secrets/bitwarden-mobile-key.p12 -k build.keychain -P $MOBILE_KEY_PASSWORD \
-T /usr/bin/codesign -T /usr/bin/security
security import ~/secrets/iphone-distribution-cert.p12 -k build.keychain -P $DIST_CERT_PASSWORD \
-T /usr/bin/codesign -T /usr/bin/security
security import $HOME/certificates/ios-distribution.p12 -k build.keychain -P "" -T /usr/bin/codesign \
-T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain
- name: Set up provisioning profiles
@@ -575,8 +552,8 @@ jobs:
BITWARDEN_PROFILE_PATH=$HOME/secrets/dist_bitwarden.mobileprovision
EXTENSION_PROFILE_PATH=$HOME/secrets/dist_extension.mobileprovision
SHARE_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_share_extension.mobileprovision
WATCH_APP_PROFILE_PATH=$HOME/secrets/dist_watch_app.mobileprovision
WATCH_APP_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_watch_app_extension.mobileprovision
WATCH_APP_PROFILE_PATH=$HOME/secrets/dist_bitwarden_watch_app.mobileprovision
WATCH_APP_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_bitwarden_watch_app_extension.mobileprovision
PROFILES_DIR_PATH=$HOME/Library/MobileDevice/Provisioning\ Profiles
mkdir -p "$PROFILES_DIR_PATH"
@@ -599,85 +576,55 @@ jobs:
WATCH_APP_EXTENSION_UUID=$(grep UUID -A1 -a $WATCH_APP_EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
cp $WATCH_APP_EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$WATCH_APP_EXTENSION_UUID.mobileprovision"
- name: Restore packages
run: dotnet restore
- name: Bulid WatchApp
run: |
echo "########################################"
echo "##### Build WatchApp with Release Configuration"
echo "########################################"
xcodebuild archive -workspace ./src/watchOS/bitwarden/bitwarden.xcodeproj/project.xcworkspace -configuration Release -scheme bitwarden\ WatchKit\ App -archivePath ./src/watchOS/bitwarden
echo "########################################"
echo "##### Done"
echo "########################################"
- name: Restore packages
run: nuget restore
- name: Archive Build for App Store
run: |
$configuration = "AppStore";
$platform = "iPhone";
Write-Output "########################################"
Write-Output "##### Archive $configuration Configuration for $platform Platform"
Write-Output "########################################"
msbuild "$($env:GITHUB_WORKSPACE + "/src/iOS/iOS.csproj")" "/p:Platform=$platform" `
"/p:Configuration=$configuration" "/p:ArchiveOnBuild=true" "/t:`"Build`""
Write-Output "########################################"
Write-Output "##### Done"
Write-Output "########################################"
shell: pwsh
echo "##### Archive for Release ios-arm64"
dotnet publish ${{ env.main_app_project_path }} -c Release -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=ios-arm64 /p:ArchiveOnBuild=true /p:MtouchUseLlvm=false
- name: Archive Build for Mobile Automation
run: |
$configuration = "Release";
$platform = "iPhoneSimulator";
Write-Output "########################################"
Write-Output "##### Archive $configuration Configuration for $platform Platform"
Write-Output "########################################"
msbuild "$($env:GITHUB_WORKSPACE + "/src/iOS/iOS.csproj")" "/p:Platform=$platform" `
"/p:Configuration=$configuration" "/p:ArchiveOnBuild=true" "/t:`"Build`""
Write-Output "########################################"
Write-Output "##### Done"
Write-Output "########################################"
ls ~/Library/Developer/Xcode/Archives
shell: pwsh
echo "##### Archive Debug for iossimulator-x64"
dotnet build ${{ env.main_app_project_path }} -c Debug -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=iossimulator-x64 /p:ArchiveOnBuild=true /p:MtouchUseLlvm=false
ls $HOME/Library/Developer/Xcode/Archives
- name: Export .ipa for App Store
env:
EXPORT_OPTIONS_PATH: ./.github/resources/export-options-app-store.plist
EXPORT_PATH: ./bitwarden-export
run: |
EXPORT_OPTIONS_PATH="./.github/resources/export-options-app-store.plist"
ARCHIVE_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive"
EXPORT_PATH="./bitwarden-export"
xcodebuild -exportArchive -archivePath $ARCHIVE_PATH -exportPath $EXPORT_PATH \
-exportOptionsPlist $EXPORT_OPTIONS_PATH
- name: Export .app for Automation CI
env:
ARCHIVE_PATH: ./${{ env.main_app_folder_path }}/bin/Debug/${{ env.target-net-version }}-ios/iossimulator-x64
EXPORT_PATH: ./bitwarden-export
run: |
ARCHIVE_PATH="./src/iOS/bin/iPhoneSimulator/Release/BitwardeniOS.app"
EXPORT_PATH="./bitwarden-export"
zip -r -q BitwardeniOS.app.zip $ARCHIVE_PATH
mv BitwardeniOS.app.zip $EXPORT_PATH
zip -r -q ${{ env._APP_CI_OUTPUT_FILENAME }}.app.zip $ARCHIVE_PATH
mv ${{ env._APP_CI_OUTPUT_FILENAME }}.app.zip $EXPORT_PATH
- name: Copy all dSYMs files to upload
env:
EXPORT_PATH: ./bitwarden-export
WATCH_ARCHIVE_DSYMS_PATH: ./src/watchOS/bitwarden.xcarchive/dSYMs/
WATCH_DSYMS_EXPORT_PATH: ./bitwarden-export/Watch_dSYMs
run: |
ARCHIVE_DSYMS_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive/dSYMs"
EXPORT_PATH="./bitwarden-export"
WATCH_ARCHIVE_DSYMS_PATH="./src/watchOS/bitwarden.xcarchive/dSYMs/"
WATCH_DSYMS_EXPORT_PATH="$EXPORT_PATH/Watch_dSYMs"
cp -r -v $ARCHIVE_DSYMS_PATH $EXPORT_PATH
mkdir $WATCH_DSYMS_EXPORT_PATH
cp -r -v $WATCH_ARCHIVE_DSYMS_PATH $WATCH_DSYMS_EXPORT_PATH
- name: Upload App Store .ipa & dSYMs artifacts
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: Bitwarden iOS
path: |
@@ -686,10 +633,10 @@ jobs:
if-no-files-found: error
- name: Upload .app file for Automation CI
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: BitwardeniOS.app.zip
path: ./bitwarden-export/BitwardeniOS.app.zip
name: ${{ env._APP_CI_OUTPUT_FILENAME }}.app.zip
path: ./bitwarden-export/${{ env._APP_CI_OUTPUT_FILENAME }}.app.zip
if-no-files-found: error
- name: Install AppCenter CLI
@@ -720,12 +667,31 @@ jobs:
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|| github.ref == 'refs/heads/hotfix-rc'
run: |
echo "########################################"
echo "##### Uploading Watch dSYMs to Firebase"
echo "########################################"
find "$HOME/Library/Developer/XCode/DerivedData" -name "upload-symbols" -exec chmod +x {} \; -exec {} -gsp "./src/watchOS/bitwarden/GoogleService-Info.plist" -p ios "./bitwarden-export/Watch_dSYMs" \;
- name: Set up private auth key
run: |
mkdir ~/private_keys
cat << EOF > ~/private_keys/AuthKey_U362LJ87AA.p8
${{ secrets.APP_STORE_CONNECT_AUTH_KEY }}
EOF
- name: Validate app in App Store
if: |
(github.ref == 'refs/heads/main'
&& needs.setup.outputs.rc_branch_exists == 0
&& needs.setup.outputs.hotfix_branch_exists == 0)
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|| github.ref == 'refs/heads/hotfix-rc'
run: |
xcrun altool \
--validate-app \
--type ios \
--file "./bitwarden-export/Bitwarden.ipa" \
--apiKey "U362LJ87AA" \
--apiIssuer ${{ secrets.APP_STORE_CONNECT_TEAM_ISSUER }}
- name: Deploy to App Store
if: |
(github.ref == 'refs/heads/main'
@@ -733,13 +699,13 @@ jobs:
&& needs.setup.outputs.hotfix_branch_exists == 0)
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|| github.ref == 'refs/heads/hotfix-rc'
env:
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
run: |
xcrun altool --upload-app --type ios --file "./bitwarden-export/Bitwarden.ipa" \
--username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
xcrun altool \
--upload-app \
--type ios \
--file "./bitwarden-export/Bitwarden.ipa" \
--apiKey "U362LJ87AA" \
--apiIssuer ${{ secrets.APP_STORE_CONNECT_TEAM_ISSUER }}
crowdin-push:
name: Crowdin Push
@@ -753,10 +719,10 @@ jobs:
_CROWDIN_PROJECT_ID: "269690"
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Login to Azure - CI Subscription
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
@@ -768,13 +734,13 @@ jobs:
secrets: "crowdin-api-token"
- name: Upload Sources
uses: crowdin/github-action@965d501f160af7b1f88aed4c29154b0caf1e94b9 # v1.9.0
uses: crowdin/github-action@61ac8b980551f674046220c3e104bddae2916ac5 # v2.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
with:
config: crowdin.yml
crowdin_branch_name: main
crowdin_branch_name: main
upload_sources: true
upload_translations: false
@@ -792,30 +758,14 @@ jobs:
steps:
- name: Check if any job failed
if: |
(github.ref == 'refs/heads/main')
|| (github.ref == 'refs/heads/rc')
|| (github.ref == 'refs/heads/hotfix-rc')
env:
CLOC_STATUS: ${{ needs.cloc.result }}
ANDROID_STATUS: ${{ needs.android.result }}
F_DROID_STATUS: ${{ needs.f-droid.result }}
IOS_STATUS: ${{ needs.ios.result }}
CROWDIN_PUSH_STATUS: ${{ needs.crowdin-push.result }}
run: |
if [ "$CLOC_STATUS" = "failure" ]; then
exit 1
elif [ "$ANDROID_STATUS" = "failure" ]; then
exit 1
elif [ "$F_DROID_STATUS" = "failure" ]; then
exit 1
elif [ "$IOS_STATUS" = "failure" ]; then
exit 1
elif [ "$CROWDIN_PUSH_STATUS" = "failure" ]; then
exit 1
fi
(github.ref == 'refs/heads/main'
|| github.ref == 'refs/heads/rc'
|| github.ref == 'refs/heads/hotfix-rc')
&& contains(needs.*.result, 'failure')
run: exit 1
- name: Login to Azure - CI Subscription
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
if: failure()
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
@@ -829,7 +779,7 @@ jobs:
secrets: "devops-alerts-slack-webhook-url"
- name: Notify Slack on failure
uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
if: failure()
env:
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}

52
.github/workflows/cleanup-rc-branch.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: Cleanup RC Branch
on:
push:
tags:
- v**
jobs:
delete-rc:
name: Delete RC Branch
runs-on: ubuntu-22.04
steps:
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve bot secrets
id: retrieve-bot-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: bitwarden-ci
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
- name: Checkout main
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
ref: main
token: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
- name: Check if a RC branch exists
id: branch-check
run: |
hotfix_rc_branch_check=$(git ls-remote --heads origin hotfix-rc | wc -l)
rc_branch_check=$(git ls-remote --heads origin rc | wc -l)
if [[ "${hotfix_rc_branch_check}" -gt 0 ]]; then
echo "hotfix-rc branch exists." | tee -a $GITHUB_STEP_SUMMARY
echo "name=hotfix-rc" >> $GITHUB_OUTPUT
elif [[ "${rc_branch_check}" -gt 0 ]]; then
echo "rc branch exists." | tee -a $GITHUB_STEP_SUMMARY
echo "name=rc" >> $GITHUB_OUTPUT
fi
- name: Delete RC branch
env:
BRANCH_NAME: ${{ steps.branch-check.outputs.name }}
run: |
if ! [[ -z "$BRANCH_NAME" ]]; then
git push --quiet origin --delete $BRANCH_NAME
echo "Deleted $BRANCH_NAME branch." | tee -a $GITHUB_STEP_SUMMARY
fi

View File

@@ -1,24 +1,30 @@
---
name: Crowdin Sync
on:
workflow_dispatch:
inputs: {}
schedule:
- cron: '0 0 * * 5'
jobs:
crowdin-sync:
name: Autosync
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
env:
_CROWDIN_PROJECT_ID: "269690"
steps:
- name: Generate GH App token
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
id: app-token
with:
app-id: ${{ secrets.BW_GHAPP_ID }}
private-key: ${{ secrets.BW_GHAPP_KEY }}
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
token: ${{ steps.app-token.outputs.token }}
- name: Login to Azure - CI Subscription
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
@@ -30,9 +36,9 @@ jobs:
secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase"
- name: Download translations
uses: crowdin/github-action@965d501f160af7b1f88aed4c29154b0caf1e94b9 # v1.9.0
uses: crowdin/github-action@61ac8b980551f674046220c3e104bddae2916ac5 # v2.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
with:
config: crowdin.yml

View File

@@ -1,4 +1,3 @@
---
name: Enforce PR labels
on:
@@ -7,7 +6,7 @@ on:
jobs:
enforce-label:
name: EnforceLabel
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Enforce Label
uses: yogevbd/enforce-label-action@a3c219da6b8fa73f6ba62b68ff09c469b3a1c024 # 2.2.2

View File

@@ -1,4 +1,3 @@
---
name: "Pull Request Labeler"
on:
@@ -10,8 +9,9 @@ jobs:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/labeler@ac9175f8a1f3625fd0d4fb234536d26811351594 # v4.3.0
- name: Label PR
uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0
with:
sync-labels: true

View File

@@ -1,4 +1,3 @@
---
name: Release
run-name: Release ${{ inputs.release_type }}
@@ -23,12 +22,12 @@ on:
jobs:
release:
name: Create Release
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
outputs:
branch-name: ${{ steps.branch.outputs.branch-name }}
steps:
- name: Branch check
if: github.event.inputs.release_type != 'Dry Run'
if: inputs.release_type != 'Dry Run'
run: |
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
echo "==================================="
@@ -38,15 +37,15 @@ jobs:
fi
- name: Checkout repo
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Check Release Version
id: version
uses: bitwarden/gh-actions/release-version-check@main
with:
release-type: ${{ github.event.inputs.release_type }}
release-type: ${{ inputs.release_type }}
project-type: xamarin
file: src/Android/Properties/AndroidManifest.xml
file: src/App/Platforms/Android/AndroidManifest.xml
- name: Get branch name
id: branch
@@ -55,8 +54,8 @@ jobs:
echo "branch-name=$BRANCH_NAME" >> $GITHUB_OUTPUT
- name: Create GitHub deployment
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: chrnorm/deployment-action@d42cde7132fcec920de534fffc3be83794335c00 # v2.0.5
if: ${{ inputs.release_type != 'Dry Run' }}
uses: chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1 # v2.0.7
id: deployment
with:
token: '${{ secrets.GITHUB_TOKEN }}'
@@ -65,29 +64,35 @@ jobs:
description: 'Deployment ${{ steps.version.outputs.version }} from branch ${{ steps.branch.outputs.branch-name }}'
task: release
- name: Download all artifacts
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0
if: ${{ inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build.yml
workflow_conclusion: success
branch: ${{ steps.branch.outputs.branch-name }}
skip_unpack: true
- name: Dry Run - Download all artifacts
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0
if: ${{ inputs.release_type == 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build.yml
workflow_conclusion: success
branch: main
skip_unpack: true
- name: Prep Bitwarden iOS release asset
run: zip -r Bitwarden\ iOS.zip Bitwarden\ iOS
- name: Unzip release assets
run: |
unzip bw-android-apk-sha256.txt.zip -d bw-android-apk-sha256.txt
unzip bw-fdroid-apk-sha256.txt.zip -d bw-fdroid-apk-sha256.txt
unzip com.x8bit.bitwarden-fdroid.apk.zip -d com.x8bit.bitwarden-fdroid.apk
unzip com.x8bit.bitwarden.aab.zip -d com.x8bit.bitwarden.aab
unzip com.x8bit.bitwarden.apk.zip -d com.x8bit.bitwarden.apk
- name: Create release
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: ncipollo/release-action@6c75be85e571768fa31b40abf38de58ba0397db5 # v1.13.0
if: ${{ inputs.release_type != 'Dry Run' }}
uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0
with:
artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab,
./com.x8bit.bitwarden.apk/com.x8bit.bitwarden.apk,
@@ -103,16 +108,16 @@ jobs:
draft: true
- name: Update deployment status to Success
if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1
if: ${{ inputs.release_type != 'Dry Run' && success() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with:
token: '${{ secrets.GITHUB_TOKEN }}'
state: 'success'
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
- name: Update deployment status to Failure
if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1
if: ${{ inputs.release_type != 'Dry Run' && failure() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with:
token: '${{ secrets.GITHUB_TOKEN }}'
state: 'failure'
@@ -121,40 +126,36 @@ jobs:
f-droid:
name: F-Droid Release
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
needs: release
if: inputs.fdroid_publish
steps:
- name: Checkout repo
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Download F-Droid .apk artifact
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0
if: ${{ inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build.yml
workflow_conclusion: success
branch: ${{ needs.release.outputs.branch-name }}
name: com.x8bit.bitwarden-fdroid.apk
- name: Dry Run - Download F-Droid .apk artifact
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0
if: ${{ inputs.release_type == 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build.yml
workflow_conclusion: success
branch: main
name: com.x8bit.bitwarden-fdroid.apk
- name: Set up Node
uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: '16.x'
- name: Set up F-Droid server
run: |
sudo apt-get -qq update
sudo apt-get -qqy install --no-install-recommends fdroidserver wget
run: pip install git+https://gitlab.com/fdroid/fdroidserver.git
- name: Set up Git credentials
env:
@@ -167,49 +168,85 @@ jobs:
- name: Print environment
run: |
node --version
npm --version
git --version
echo "Node Version: $(node --version)"
echo "NPM Version: $(npm --version)"
echo "Git Version: $(git --version)"
echo "F-Droid Server Version: $(fdroid --version)"
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Install Node dependencies
run: npm install
- name: Decrypt secrets
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve secrets
id: retrieve-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: "bitwarden-ci"
secrets: "github-gpg-private-key,
github-gpg-private-key-passphrase,
github-pat-bitwarden-devops-bot-mobile-fdroid"
- name: Import GPG key
uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0
with:
gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}
git_user_signingkey: true
git_commit_gpgsign: true
- name: Setup git
run: |
mkdir -p ~/secrets
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./store/fdroid/keystore.jks ./.github/secrets/store_fdroid-keystore.jks.gpg
git config --local user.email "106330231+bitwarden-devops-bot@users.noreply.github.com"
git config --local user.name "bitwarden-devops-bot"
- name: Download secrets
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
run: |
mkdir -p $HOME/secrets
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name store_fdroid-keystore.jks --file ./store/fdroid/keystore.jks --output none
- name: Compile for F-Droid Store
env:
FDROID_STORE_KEYSTORE_PASSWORD: ${{ secrets.FDROID_STORE_KEYSTORE_PASSWORD }}
run: |
cd $GITHUB_WORKSPACE
# Create required directories.
mkdir dist
cp CNAME ./dist
cd store
chmod 600 fdroid/config.py fdroid/keystore.jks
mkdir -p temp/fdroid
mkdir -p store/temp/fdroid
mkdir -p store/fdroid/repo
# Configure F-Droid server.
cp CNAME dist/
chmod 600 store/fdroid/config.yml store/fdroid/keystore.jks
TEMP_DIR="$GITHUB_WORKSPACE/store/temp/fdroid"
cd fdroid
echo "keypass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
echo "keystorepass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
echo "local_copy_dir=\"$TEMP_DIR\"" >>config.py
mkdir -p repo
mv $GITHUB_WORKSPACE/com.x8bit.bitwarden-fdroid.apk ./repo/
echo "keypass: $FDROID_STORE_KEYSTORE_PASSWORD" >> store/fdroid/config.yml
echo "keystorepass: $FDROID_STORE_KEYSTORE_PASSWORD" >> store/fdroid/config.yml
echo "local_copy_dir: $TEMP_DIR" >> store/fdroid/config.yml
mv $GITHUB_WORKSPACE/com.x8bit.bitwarden-fdroid.apk store/fdroid/repo/
# Run update and deploy.
cd store/fdroid
fdroid update
fdroid server update
cd ..
rm -rf temp/fdroid/archive
mv -v temp/fdroid ../dist
cd fdroid
cp index.html btn.png qr.png ../../dist/fdroid
cd $GITHUB_WORKSPACE
fdroid deploy
cd ../..
# Move files for distribution.
rm -rf store/temp/fdroid/archive
mv -v store/temp/fdroid dist
cp store/fdroid/index.html store/fdroid/btn.png store/fdroid/qr.png dist/fdroid
- name: Deploy to gh-pages
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
run: npm run deploy
if: ${{ inputs.release_type != 'Dry Run' }}
env:
TOKEN: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-mobile-fdroid }}
run: |
git remote set-url origin https://git:${TOKEN}@github.com/${GITHUB_REPOSITORY}.git
npm run deploy -- -u "bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>"

View File

@@ -1,4 +1,3 @@
---
name: 'Close stale issues and PRs'
on:
workflow_dispatch:
@@ -8,10 +7,10 @@ on:
jobs:
stale:
name: 'Check for stale issues and PRs'
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: 'Run stale action'
uses: actions/stale@f7176fd3007623b69d27091f9b9d4ab7995f0a06 # v5.2.1
uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
with:
stale-issue-label: 'needs-reply'
stale-pr-label: 'needs-changes'

View File

@@ -1,5 +1,4 @@
---
name: Version Auto Bump
name: Auto Bump Mobile Version
on:
push:
@@ -7,34 +6,25 @@ on:
- v**
jobs:
setup:
name: "Setup"
bump-version:
name: Bump Mobile Version
runs-on: ubuntu-22.04
outputs:
version_number: ${{ steps.version.outputs.new-version }}
steps:
- name: Checkout Branch
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Calculate bumped version
id: version
- name: Retrieve bot secrets
id: retrieve-bot-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: bitwarden-ci
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
- name: Trigger Version Bump workflow
env:
RELEASE_TAG: ${{ github.ref }}
GH_TOKEN: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
run: |
CURR_MAJOR=$(echo $RELEASE_TAG | sed -r 's/refs\/tags\/v([0-9]{4}\.[0-9]{1,2})\.([0-9]{1,2})/\1/')
CURR_PATCH=$(echo $RELEASE_TAG | sed -r 's/refs\/tags\/v([0-9]{4}\.[0-9]{1,2})\.([0-9]{1,2})/\2/')
echo "Current Major: $CURR_MAJOR"
echo "Current Patch: $CURR_PATCH"
NEW_PATCH=$((CURR_PATCH+1))
NEW_VER=$CURR_MAJOR.$NEW_PATCH
echo "New Version: $NEW_VER"
echo "new-version=$NEW_VER" >> $GITHUB_OUTPUT
trigger_version_bump:
name: Bump version to ${{ needs.setup.outputs.version_number }}
needs: setup
uses: ./.github/workflows/version-bump.yml
with:
version_number: ${{ needs.setup.outputs.version_number }}
secrets: inherit
echo '{"cut_rc_branch": "false"}' | \
gh workflow run version-bump.yml --json --repo bitwarden/mobile

View File

@@ -1,27 +1,61 @@
---
name: Version Bump
run-name: Version Bump - v${{ inputs.version_number }}
on:
workflow_dispatch:
inputs:
version_number:
description: "New version (example: '2024.1.0')"
required: true
version_number_override:
description: "New version override (leave blank for automatic calculation, example: '2024.1.0')"
required: false
type: string
cut_rc_branch:
description: "Cut RC branch?"
default: true
type: boolean
enable_slack_notification:
description: "Enable Slack notifications for upcoming release?"
default: false
type: boolean
jobs:
bump_version:
name: "Bump Version to v${{ inputs.version_number }}"
name: Bump Version
runs-on: ubuntu-22.04
outputs:
version: ${{ steps.set-final-version-output.outputs.version }}
steps:
- name: Login to Azure - CI Subscription
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
- name: Validate version input
if: ${{ inputs.version_number_override != '' }}
uses: bitwarden/gh-actions/version-check@main
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
version: ${{ inputs.version_number_override }}
- name: Slack Notification Check
run: |
if [[ "${{ inputs.enable_slack_notification }}" == true ]]; then
echo "Slack notifications enabled."
else
echo "Slack notifications disabled."
fi
- name: Checkout Branch
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
ref: main
- name: Check if RC branch exists
if: ${{ inputs.cut_rc_branch == true }}
run: |
remote_rc_branch_check=$(git ls-remote --heads origin rc | wc -l)
if [[ "${remote_rc_branch_check}" -gt 0 ]]; then
echo "Remote RC branch exists."
echo "Please delete current RC branch before running again."
exit 1
fi
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve secrets
id: retrieve-secrets
@@ -32,39 +66,46 @@ jobs:
github-gpg-private-key-passphrase,
github-pat-bitwarden-devops-bot-repo-scope"
- name: Checkout Branch
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
with:
ref: main
repository: bitwarden/mobile
- name: Import GPG key
uses: crazy-max/ghaction-import-gpg@d6f3f49f3345e29369fe57596a3ca8f94c4d2ca7 # v5.4.0
uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0
with:
gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}
git_user_signingkey: true
git_commit_gpgsign: true
- name: Setup git
run: |
git config --local user.email "106330231+bitwarden-devops-bot@users.noreply.github.com"
git config --local user.name "bitwarden-devops-bot"
- name: Create Version Branch
id: create-branch
run: |
NAME=version_bump_${{ github.ref_name }}_${{ inputs.version_number }}
NAME=version_bump_${{ github.ref_name }}_$(date +"%Y-%m-%d")
git switch -c $NAME
echo "name=$NAME" >> $GITHUB_OUTPUT
- name: Install xmllint
run: sudo apt install -y libxml2-utils
run: |
sudo apt-get update
sudo apt-get install -y libxml2-utils
- name: Verify input version
env:
NEW_VERSION: ${{ inputs.version_number }}
- name: Get current version
id: current-version
run: |
CURRENT_VERSION=$(xmllint --xpath '
string(/manifest/@*[local-name()="versionName"
string(/manifest/@*[local-name()="versionName"
and namespace-uri()="http://schemas.android.com/apk/res/android"])
' src/Android/Properties/AndroidManifest.xml)
' src/App/Platforms/Android/AndroidManifest.xml)
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
- name: Verify input version
if: ${{ inputs.version_number_override != '' }}
env:
CURRENT_VERSION: ${{ steps.current-version.outputs.version }}
NEW_VERSION: ${{ inputs.version_number_override }}
run: |
# Error if version has not changed.
if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
echo "Version has not changed."
@@ -80,40 +121,93 @@ jobs:
exit 1
fi
- name: Bump Version - Android XML
uses: bitwarden/gh-actions/version-bump@main
- name: Calculate next release version
if: ${{ inputs.version_number_override == '' }}
id: calculate-next-version
uses: bitwarden/gh-actions/version-next@main
with:
version: ${{ inputs.version_number }}
file_path: "src/Android/Properties/AndroidManifest.xml"
version: ${{ steps.current-version.outputs.version }}
- name: Bump Version - iOS.Autofill
- name: Bump Version - Android XML - Version Override
if: ${{ inputs.version_number_override != '' }}
id: bump-version-override
uses: bitwarden/gh-actions/version-bump@main
with:
file_path: "src/App/Platforms/Android/AndroidManifest.xml"
version: ${{ inputs.version_number_override }}
- name: Bump Version - Android XML - Automatic Calculation
if: ${{ inputs.version_number_override == '' }}
id: bump-version-automatic
uses: bitwarden/gh-actions/version-bump@main
with:
file_path: "src/App/Platforms/Android/AndroidManifest.xml"
version: ${{ steps.calculate-next-version.outputs.version }}
- name: Bump Version - iOS.Autofill - Version Override
if: ${{ inputs.version_number_override != '' }}
uses: bitwarden/gh-actions/version-bump@main
with:
version: ${{ inputs.version_number }}
file_path: "src/iOS.Autofill/Info.plist"
version: ${{ inputs.version_number_override }}
- name: Bump Version - iOS.Extension
- name: Bump Version - iOS.Autofill - Automatic Calculation
if: ${{ inputs.version_number_override == '' }}
uses: bitwarden/gh-actions/version-bump@main
with:
file_path: "src/iOS.Autofill/Info.plist"
version: ${{ steps.calculate-next-version.outputs.version }}
- name: Bump Version - iOS.Extension - Version Override
if: ${{ inputs.version_number_override != '' }}
uses: bitwarden/gh-actions/version-bump@main
with:
version: ${{ inputs.version_number }}
file_path: "src/iOS.Extension/Info.plist"
version: ${{ inputs.version_number_override }}
- name: Bump Version - iOS.ShareExtension
- name: Bump Version - iOS.Extension - Automatic Calculation
if: ${{ inputs.version_number_override == '' }}
uses: bitwarden/gh-actions/version-bump@main
with:
file_path: "src/iOS.Extension/Info.plist"
version: ${{ steps.calculate-next-version.outputs.version }}
- name: Bump Version - iOS.ShareExtension - Version Override
if: ${{ inputs.version_number_override != '' }}
uses: bitwarden/gh-actions/version-bump@main
with:
version: ${{ inputs.version_number }}
file_path: "src/iOS.ShareExtension/Info.plist"
version: ${{ inputs.version_number_override }}
- name: Bump Version - iOS
- name: Bump Version - iOS.ShareExtension - Automatic Calculation
if: ${{ inputs.version_number_override == '' }}
uses: bitwarden/gh-actions/version-bump@main
with:
version: ${{ inputs.version_number }}
file_path: "src/iOS/Info.plist"
file_path: "src/iOS.ShareExtension/Info.plist"
version: ${{ steps.calculate-next-version.outputs.version }}
- name: Setup git
- name: Bump Version - iOS - Version Override
if: ${{ inputs.version_number_override != '' }}
uses: bitwarden/gh-actions/version-bump@main
with:
file_path: "src/App/Platforms/iOS/Info.plist"
version: ${{ inputs.version_number_override }}
- name: Bump Version - iOS - Automatic Calculation
if: ${{ inputs.version_number_override == '' }}
uses: bitwarden/gh-actions/version-bump@main
with:
file_path: "src/App/Platforms/iOS/Info.plist"
version: ${{ steps.calculate-next-version.outputs.version }}
- name: Set Job output
id: set-final-version-output
run: |
git config --local user.email "106330231+bitwarden-devops-bot@users.noreply.github.com"
git config --local user.name "bitwarden-devops-bot"
if [[ "${{ steps.bump-version-override.outcome }}" == "success" ]]; then
echo "version=${{ inputs.version_number_override }}" >> $GITHUB_OUTPUT
elif [[ "${{ steps.bump-version-automatic.outcome }}" == "success" ]]; then
echo "version=${{ steps.calculate-next-version.outputs.version }}" >> $GITHUB_OUTPUT
fi
- name: Check if version changed
id: version-changed
@@ -127,7 +221,7 @@ jobs:
- name: Commit files
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
run: git commit -m "Bumped version to ${{ inputs.version_number }}" -a
run: git commit -m "Bumped version to ${{ steps.set-final-version-output.outputs.version }}" -a
- name: Push changes
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
@@ -141,7 +235,7 @@ jobs:
env:
GH_TOKEN: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
PR_BRANCH: ${{ steps.create-branch.outputs.name }}
TITLE: "Bump version to ${{ inputs.version_number }}"
TITLE: "Bump version to ${{ steps.set-final-version-output.outputs.version }}"
run: |
PR_URL=$(gh pr create --title "$TITLE" \
--base "main" \
@@ -157,40 +251,64 @@ jobs:
- [X] Other
## Objective
Automated version bump to ${{ inputs.version_number }}")
Automated version bump to ${{ steps.set-final-version-output.outputs.version }}")
echo "pr_number=${PR_URL##*/}" >> $GITHUB_OUTPUT
- name: Approve PR
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ steps.create-pr.outputs.pr_number }}
run: gh pr review $PR_NUMBER --approve
- name: Merge PR
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
env:
GH_TOKEN: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
PR_NUMBER: ${{ steps.create-pr.outputs.pr_number }}
run: gh pr merge $PR_NUMBER --squash --auto --delete-branch
- name: Report upcoming release version to Slack
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' && inputs.enable_slack_notification == true }}
uses: bitwarden/gh-actions/report-upcoming-release-version@main
with:
version: ${{ steps.set-final-version-output.outputs.version }}
project: ${{ github.repository }}
AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
cut_rc:
name: Cut RC branch
needs: bump_version
if: ${{ inputs.cut_rc_branch == true }}
needs: bump_version
runs-on: ubuntu-22.04
steps:
- name: Checkout Branch
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
ref: main
- name: Check if RC branch exists
- name: Install xmllint
run: |
remote_rc_branch_check=$(git ls-remote --heads origin rc | wc -l)
if [[ "${remote_rc_branch_check}" -gt 0 ]]; then
echo "Remote RC branch exists."
echo "Please delete current RC branch before running again."
exit 1
fi
sudo apt-get update
sudo apt-get install -y libxml2-utils
- name: Verify version has been updated
env:
NEW_VERSION: ${{ needs.bump_version.outputs.version }}
run: |
# Wait for version to change.
while : ; do
echo "Waiting for version to be updated..."
git pull --force
CURRENT_VERSION=$(xmllint --xpath '
string(/manifest/@*[local-name()="versionName"
and namespace-uri()="http://schemas.android.com/apk/res/android"])
' src/App/Platforms/Android/AndroidManifest.xml)
# If the versions don't match we continue the loop, otherwise we break out of the loop.
[[ "$NEW_VERSION" != "$CURRENT_VERSION" ]] || break
sleep 10
done
- name: Cut RC branch
run: |

View File

@@ -1,11 +0,0 @@
---
name: Workflow Linter
on:
pull_request:
paths:
- .github/workflows/**
jobs:
call-workflow:
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@main

18
.gitignore vendored
View File

@@ -31,6 +31,7 @@ Components/
x64/
x86/
!src/lib/x86/
!src/App/Platforms/Android/lib/x86/
build/
bld/
[Bb]in/
@@ -147,6 +148,7 @@ publish/
# NuGet Packages
*.nupkg
!**/Xamarin.AndroidX.Credentials.1.0.0.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
@@ -294,17 +296,11 @@ iOSInjectionProject/
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm
.build/
# xcode / swift package manager - used by the MessagePack lib
/.build
/Packages
/*.xcodeproj
.swiftpm
# CocoaPods
# We recommend against adding the Pods directory to your .gitignore. However

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "lib/MessagePack"]
path = lib/MessagePack
url = https://github.com/bitwarden/MessagePack.git

15
Directory.Build.props Normal file
View File

@@ -0,0 +1,15 @@
<Project>
<PropertyGroup>
<MauiVersion>8.0.7</MauiVersion>
<ReleaseCodesignProvision>Automatic:AppStore</ReleaseCodesignProvision>
<ReleaseCodesignKey>iPhone Distribution</ReleaseCodesignKey>
<IncludeBitwardeniOSExtensions>True</IncludeBitwardeniOSExtensions>
<IncludeBitwardenWatchOSApp>False</IncludeBitwardenWatchOSApp>
<Argon2IdLoadMtouchExtraArgs>-gcc_flags "-L$(ProjectDir)../../lib/ios -largon2 -force_load $(ProjectDir)../../lib/ios/libargon2.a"</Argon2IdLoadMtouchExtraArgs>
<!-- Uncomment this when Unit Testing-->
<!-- <CustomConstants>UT</CustomConstants> -->
<!-- Uncomment this when building FDROID-->
<!-- <CustomConstants>FDROID</CustomConstants> -->
</PropertyGroup>
</Project>

View File

@@ -1,18 +1,22 @@
[![Github Workflow build on master](https://github.com/bitwarden/mobile/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/bitwarden/mobile/actions/workflows/build.yml?query=branch:master)
[![Github Workflow build on main](https://github.com/bitwarden/mobile/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/bitwarden/mobile/actions/workflows/build.yml?query=branch:main)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/bitwarden-mobile/localized.svg)](https://crowdin.com/project/bitwarden-mobile)
[![Join the chat at https://gitter.im/bitwarden/Lobby](https://badges.gitter.im/bitwarden/Lobby.svg)](https://gitter.im/bitwarden/Lobby)
# Bitwarden Mobile Application
> [!TIP]
> Looking for the new native apps? Head on over to [bitwarden/android](https://github.com/bitwarden/android) and [bitwarden/ios](https://github.com/bitwarden/ios)
<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://mobileapp.bitwarden.com/fdroid/" target="_blank"><img alt="Get it on F-Droid" src="https://i.imgur.com/HDicnzz.png" width="154" 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# using .NET MAUI.
<img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-android-myvault.png" alt="" width="325" height="650" /> <img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-ios-myvault.png" alt="" width="300" height="650" />
# Build/Run
Please refer to the [Mobile section](https://contributing.bitwarden.com/getting-started/clients/mobile/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started.
Please refer to the [Legacy Contributing Documentation](https://github.com/bitwarden/mobile/tree/main/docs/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started.
# We're Hiring!
@@ -20,6 +24,6 @@ Interested in contributing in a big way? Consider joining our team! We're hiring
# Contribute
Code contributions are welcome! Please commit any pull requests against the `master` branch. Learn more about how to contribute by reading the [Contributing Guidelines](https://contributing.bitwarden.com/contributing/). Check out the [Contributing Documentation](https://contributing.bitwarden.com/) for how to get started with your first contribution.
Code contributions are welcome! Please commit any pull requests against the `main` branch. Learn more about how to contribute by reading the [Contributing Guidelines](https://contributing.bitwarden.com/contributing/). Check out the [Contributing Documentation](https://contributing.bitwarden.com/) for how to get started with your first contribution.
Security audits and feedback are welcome. Please open an issue or email us privately if the report is sensitive in nature. You can read our security policy in the [`SECURITY.md`](SECURITY.md) file.

View File

@@ -1,471 +1,233 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29009.5
# Visual Studio Version 17
VisualStudioVersion = 17.8.34112.27
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Android", "src\Android\Android.csproj", "{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "src\App\App.csproj", "{971FDF07-E288-4239-B47A-E9E7E912193B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App", "src\App\App.csproj", "{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "src\Core\Core.csproj", "{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "src\Core\Core.csproj", "{4B8A8C41-9820-4341-974C-41E65B7F4366}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Playground", "test\Playground\Playground.csproj", "{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D10CA4A9-F866-40E1-B658-F69051236C71}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{8904C536-C67D-420F-9971-51B26574C3AA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "store", "store", "{92470CBD-9047-4C3C-8EA3-D972D6622D84}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "google", "google", "{2E399654-26A2-46F6-B9CA-1B496A3F370A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{76690DFB-B7F4-4781-83E4-113FDC450AFE}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.gitignore = .gitignore
.github\workflows\build.yml = .github\workflows\build.yml
CONTRIBUTING.md = CONTRIBUTING.md
crowdin.yml = crowdin.yml
README.md = README.md
SECURITY.md = SECURITY.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Publisher", "store\google\Publisher\Publisher.csproj", "{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Core", "src\iOS.Core\iOS.Core.csproj", "{E71F3053-056C-4381-9638-048ED73BDFF6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS", "src\iOS\iOS.csproj", "{599E0201-420A-4C3E-A7BA-5349F72E0B15}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "iOS.Core", "src\iOS.Core\iOS.Core.csproj", "{E71F3053-056C-4381-9638-048ED73BDFF6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Extension", "src\iOS.Extension\iOS.Extension.csproj", "{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "test\Common\Common.csproj", "{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Test", "test\Core.Test\Core.Test.csproj", "{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.ShareExtension", "src\iOS.ShareExtension\iOS.ShareExtension.csproj", "{F8C3F648-EA5A-4719-8005-85D1690B1655}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{83449CC4-1F76-4CFE-92B1-D2E13A62506F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{BB702EBD-3B79-4ECA-A2A6-1237B07F0AF0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Test", "test\Core.Test\Core.Test.csproj", "{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "test\Common\Common.csproj", "{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
Ad-Hoc|iPhone = Ad-Hoc|iPhone
Ad-Hoc|iPhoneSimulator = Ad-Hoc|iPhoneSimulator
AppStore|Any CPU = AppStore|Any CPU
AppStore|iPhone = AppStore|iPhone
AppStore|iPhoneSimulator = AppStore|iPhoneSimulator
Debug|Any CPU = Debug|Any CPU
Debug|iPhone = Debug|iPhone
Debug|iPhoneSimulator = Debug|iPhoneSimulator
FDroid|Any CPU = FDroid|Any CPU
FDroid|iPhone = FDroid|iPhone
FDroid|iPhoneSimulator = FDroid|iPhoneSimulator
Release|Any CPU = Release|Any CPU
Release|iPhone = Release|iPhone
Debug|iPhoneSimulator = Debug|iPhoneSimulator
Release|iPhoneSimulator = Release|iPhoneSimulator
Debug|iPhone = Debug|iPhone
Release|iPhone = Release|iPhone
AppStore|iPhoneSimulator = AppStore|iPhoneSimulator
AppStore|iPhone = AppStore|iPhone
Ad-Hoc|iPhoneSimulator = Ad-Hoc|iPhoneSimulator
Ad-Hoc|iPhone = Ad-Hoc|iPhone
FDroid|Any CPU = FDroid|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|Any CPU.Deploy.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhone.Deploy.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|Any CPU.Build.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|Any CPU.Deploy.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhone.Build.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhone.Deploy.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhoneSimulator.Deploy.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhone.Build.0 = Debug|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhone.Deploy.0 = Debug|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|Any CPU.Build.0 = FDroid|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|Any CPU.Deploy.0 = FDroid|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhone.Build.0 = FDroid|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhone.Deploy.0 = FDroid|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhoneSimulator.Deploy.0 = FDroid|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|Any CPU.Build.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|Any CPU.Deploy.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhone.ActiveCfg = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhone.Build.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhone.Deploy.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.Deploy.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.Deploy.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.Build.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.Deploy.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.Build.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.Deploy.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.Deploy.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhone.Build.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhone.Deploy.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|Any CPU.Build.0 = FDroid|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|iPhone.Build.0 = FDroid|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|Any CPU.Build.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|Any CPU.Deploy.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhone.ActiveCfg = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhone.Build.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhone.Deploy.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|Any CPU.Build.0 = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhone.Build.0 = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|iPhone.Build.0 = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|Any CPU.Build.0 = FDroid|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|iPhone.Build.0 = FDroid|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|Any CPU.Build.0 = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|iPhone.ActiveCfg = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|iPhone.Build.0 = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|iPhone.Build.0 = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|iPhone.Build.0 = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|Any CPU.Build.0 = FDroid|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|iPhone.Build.0 = FDroid|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|Any CPU.Build.0 = Release|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|iPhone.ActiveCfg = Release|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|iPhone.Build.0 = Release|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|iPhone.Build.0 = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|iPhone.Build.0 = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|Any CPU.Build.0 = FDroid|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|iPhone.Build.0 = FDroid|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|Any CPU.Build.0 = Release|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhone.ActiveCfg = Release|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhone.Build.0 = Release|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|Any CPU.ActiveCfg = AppStore|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|Any CPU.Build.0 = AppStore|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.Build.0 = AppStore|iPhone
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|Any CPU.Build.0 = Release|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|Any CPU.Deploy.0 = Release|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|iPhone.Build.0 = Debug|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|iPhone.ActiveCfg = Release|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|iPhone.Build.0 = Release|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.AppStore|iPhone.Build.0 = Release|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
{971FDF07-E288-4239-B47A-E9E7E912193B}.FDroid|Any CPU.Build.0 = Debug|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|Any CPU.Build.0 = Release|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|iPhone.Build.0 = Debug|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|iPhone.ActiveCfg = Release|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|iPhone.Build.0 = Release|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.AppStore|iPhone.Build.0 = Release|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.FDroid|Any CPU.Build.0 = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.ActiveCfg = Debug|iPhone
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.Build.0 = Debug|iPhone
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.Build.0 = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|iPhone.Build.0 = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|Any CPU.Build.0 = Release|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhone.ActiveCfg = Release|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhone.Build.0 = Release|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|iPhone.Build.0 = AppStore|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.ActiveCfg = Debug|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.Build.0 = Debug|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.Deploy.0 = Debug|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|Any CPU.ActiveCfg = FDroid|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|iPhone.ActiveCfg = FDroid|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|iPhone.Build.0 = FDroid|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|iPhoneSimulator
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|iPhoneSimulator.Build.0 = FDroid|iPhoneSimulator
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|Any CPU.ActiveCfg = Release|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhone.ActiveCfg = Release|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhone.Build.0 = Release|iPhone
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|Any CPU.Build.0 = AppStore|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.Build.0 = AppStore|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.ActiveCfg = Debug|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Build.0 = Debug|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Deploy.0 = Debug|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.ActiveCfg = Release|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.Build.0 = Release|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhone.ActiveCfg = Release|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhone.Build.0 = Release|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|Any CPU.ActiveCfg = Release|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.ActiveCfg = Release|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.Build.0 = Release|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|Any CPU.Build.0 = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhone.Build.0 = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhone.Build.0 = Debug|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|Any CPU.ActiveCfg = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|Any CPU.Build.0 = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhone.ActiveCfg = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhone.Build.0 = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhoneSimulator.ActiveCfg = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhoneSimulator.Build.0 = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|Any CPU.Build.0 = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhone.ActiveCfg = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhone.Build.0 = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|Any CPU.Build.0 = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhone.Build.0 = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhone.Build.0 = Debug|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|Any CPU.ActiveCfg = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|Any CPU.Build.0 = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhone.ActiveCfg = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhone.Build.0 = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhoneSimulator.ActiveCfg = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhoneSimulator.Build.0 = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|Any CPU.Build.0 = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhone.ActiveCfg = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhone.Build.0 = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|Any CPU.ActiveCfg = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|Any CPU.Build.0 = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.ActiveCfg = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.Build.0 = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|Any CPU.ActiveCfg = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|Any CPU.Build.0 = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhone.ActiveCfg = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhone.Build.0 = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhone.ActiveCfg = Debug|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhone.Build.0 = Debug|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.ActiveCfg = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.Build.0 = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|iPhone.ActiveCfg = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|iPhone.Build.0 = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.ActiveCfg = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.Build.0 = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhone.ActiveCfg = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhone.Build.0 = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|Any CPU.Build.0 = AppStore|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhone.Build.0 = AppStore|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.ActiveCfg = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.Build.0 = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.Deploy.0 = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|Any CPU.ActiveCfg = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|Any CPU.Build.0 = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhone.ActiveCfg = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhone.Build.0 = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|Any CPU.ActiveCfg = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.ActiveCfg = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.Build.0 = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.Build.0 = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhone.ActiveCfg = Release|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhone.Build.0 = Release|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.Build.0 = Release|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.Build.0 = Debug|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.Build.0 = Debug|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|Any CPU.ActiveCfg = Release|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|Any CPU.Build.0 = Release|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Build.0 = Debug|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.ActiveCfg = Release|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.Build.0 = Release|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.Build.0 = Release|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.Build.0 = Debug|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.Build.0 = Release|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhone.Build.0 = Debug|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhone.ActiveCfg = Release|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhone.Build.0 = Release|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhone.Build.0 = Release|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.Build.0 = Debug|Any CPU
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|Any CPU.Build.0 = Release|Any CPU
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|iPhone.ActiveCfg = Release|Any CPU
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|iPhone.Build.0 = Release|Any CPU
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhone.Build.0 = Release|Any CPU
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.FDroid|Any CPU.Build.0 = Debug|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|Any CPU.Build.0 = Release|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhone.Build.0 = Debug|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhone.ActiveCfg = Release|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhone.Build.0 = Release|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhone.Build.0 = Release|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.FDroid|Any CPU.Build.0 = Debug|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|Any CPU.Build.0 = Release|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhone.Build.0 = Debug|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhone.ActiveCfg = Release|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhone.Build.0 = Release|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhone.Build.0 = Release|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.FDroid|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{4B8A8C41-9820-4341-974C-41E65B7F4366} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3} = {8904C536-C67D-420F-9971-51B26574C3AA}
{2E399654-26A2-46F6-B9CA-1B496A3F370A} = {92470CBD-9047-4C3C-8EA3-D972D6622D84}
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D} = {2E399654-26A2-46F6-B9CA-1B496A3F370A}
{E71F3053-056C-4381-9638-048ED73BDFF6} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{599E0201-420A-4C3E-A7BA-5349F72E0B15} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39} = {8904C536-C67D-420F-9971-51B26574C3AA}
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA}
{F8C3F648-EA5A-4719-8005-85D1690B1655} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}
SolutionGuid = {3B3A9B6C-D325-4BB3-97D3-8070630C5D3B}
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
Policies = $0
$0.DotNetNamingPolicy = $1
$1.DirectoryNamespaceAssociation = PrefixedHierarchical
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{971FDF07-E288-4239-B47A-E9E7E912193B} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
{83449CC4-1F76-4CFE-92B1-D2E13A62506F} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
{E71F3053-056C-4381-9638-048ED73BDFF6} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
{F8C3F648-EA5A-4719-8005-85D1690B1655} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6} = {BB702EBD-3B79-4ECA-A2A6-1237B07F0AF0}
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44} = {BB702EBD-3B79-4ECA-A2A6-1237B07F0AF0}
EndGlobalSection
EndGlobal

View File

@@ -15,16 +15,18 @@ abstract record VariantConfig(
string AppName,
string AndroidPackageName,
string iOSBundleId,
string ApsEnvironment
string ApsEnvironment,
string DistProvisioningProfilePrefix
);
const string BASE_BUNDLE_ID_DROID = "com.x8bit.bitwarden";
const string BASE_BUNDLE_ID_IOS = "com.8bit.bitwarden";
record Dev(): VariantConfig("Bitwarden Dev", $"{BASE_BUNDLE_ID_DROID}.dev", $"{BASE_BUNDLE_ID_IOS}.dev", "development");
record QA(): VariantConfig("Bitwarden QA", $"{BASE_BUNDLE_ID_DROID}.qa", $"{BASE_BUNDLE_ID_IOS}.qa", "development");
record Beta(): VariantConfig("Bitwarden Beta", $"{BASE_BUNDLE_ID_DROID}.beta", $"{BASE_BUNDLE_ID_IOS}.beta", "production");
record Prod(): VariantConfig("Bitwarden", $"{BASE_BUNDLE_ID_DROID}", $"{BASE_BUNDLE_ID_IOS}", "production");
//NOTE: Beta iOS variants have a different ITSEncryptionExportComplianceCode
record Dev(): VariantConfig("Bitwarden Dev", $"{BASE_BUNDLE_ID_DROID}.dev", $"{BASE_BUNDLE_ID_IOS}.dev", "development", "Dist:");
record QA(): VariantConfig("Bitwarden QA", $"{BASE_BUNDLE_ID_DROID}.qa", $"{BASE_BUNDLE_ID_IOS}.qa", "development", "Dist:");
record Beta(): VariantConfig("Bitwarden Beta", $"{BASE_BUNDLE_ID_DROID}.beta", $"{BASE_BUNDLE_ID_IOS}.beta", "production", "Dist: Beta");
record Prod(): VariantConfig("Bitwarden", $"{BASE_BUNDLE_ID_DROID}", $"{BASE_BUNDLE_ID_IOS}", "production", "Dist:");
VariantConfig GetVariant() => variant.ToLower() switch{
"qa" => new QA(),
@@ -67,7 +69,7 @@ Task("UpdateAndroidManifest")
.Does(()=>
{
var buildVariant = GetVariant();
var manifestPath = Path.Combine(_slnPath, "src", "Android", "Properties", "AndroidManifest.xml");
var manifestPath = Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "AndroidManifest.xml");
// Cake.AndroidAppManifest doesn't currently enable us to access nested items so, quick (not ideal) fix:
var manifestText = FileReadText(manifestPath);
@@ -119,26 +121,26 @@ Task("UpdateAndroidCodeFiles")
//We're not using _androidPackageName here because the codefile is currently slightly different string than the one in AndroidManifest.xml
var keyName = "com.8bit.bitwarden";
var fixedPackageName = buildVariant.AndroidPackageName.Replace("x8bit", "8bit");
var filePath = Path.Combine(_slnPath, "src", "Android", "Services", "BiometricService.cs");
var filePath = Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Services", "BiometricService.cs");
ReplaceInFile(filePath, keyName, fixedPackageName);
var packageFileList = new string[] {
Path.Combine(_slnPath, "src", "Android", "MainActivity.cs"),
Path.Combine(_slnPath, "src", "Android", "MainApplication.cs"),
Path.Combine(_slnPath, "src", "Android", "Constants.cs"),
Path.Combine(_slnPath, "src", "Android", "Accessibility", "AccessibilityService.cs"),
Path.Combine(_slnPath, "src", "Android", "Autofill", "AutofillHelpers.cs"),
Path.Combine(_slnPath, "src", "Android", "Autofill", "AutofillService.cs"),
Path.Combine(_slnPath, "src", "Android", "Receivers", "ClearClipboardAlarmReceiver.cs"),
Path.Combine(_slnPath, "src", "Android", "Receivers", "EventUploadReceiver.cs"),
Path.Combine(_slnPath, "src", "Android", "Receivers", "PackageReplacedReceiver.cs"),
Path.Combine(_slnPath, "src", "Android", "Receivers", "RestrictionsChangedReceiver.cs"),
Path.Combine(_slnPath, "src", "Android", "Services", "DeviceActionService.cs"),
Path.Combine(_slnPath, "src", "Android", "Services", "FileService.cs"),
Path.Combine(_slnPath, "src", "Android", "Tiles", "AutofillTileService.cs"),
Path.Combine(_slnPath, "src", "Android", "Tiles", "GeneratorTileService.cs"),
Path.Combine(_slnPath, "src", "Android", "Tiles", "MyVaultTileService.cs"),
Path.Combine(_slnPath, "src", "Android", "google-services.json"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "MainActivity.cs"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "MainApplication.cs"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Constants.cs"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Accessibility", "AccessibilityService.cs"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Autofill", "AutofillHelpers.cs"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Autofill", "AutofillService.cs"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Receivers", "ClearClipboardAlarmReceiver.cs"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Receivers", "EventUploadReceiver.cs"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Receivers", "PackageReplacedReceiver.cs"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Receivers", "RestrictionsChangedReceiver.cs"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Services", "DeviceActionService.cs"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Services", "FileService.cs"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Tiles", "AutofillTileService.cs"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Tiles", "GeneratorTileService.cs"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Tiles", "MyVaultTileService.cs"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "google-services.json"),
Path.Combine(_slnPath, "store", "google", "Publisher", "Program.cs"),
};
@@ -148,7 +150,7 @@ Task("UpdateAndroidCodeFiles")
}
var labelFileList = new string[] {
Path.Combine(_slnPath, "src", "Android", "Autofill", "AutofillService.cs"),
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Autofill", "AutofillService.cs"),
};
foreach(string path in labelFileList)
@@ -197,7 +199,8 @@ private void UpdateiOSInfoPlist(string plistPath, VariantConfig buildVariant, Gi
var prevBundleId = plist["CFBundleIdentifier"];
var prevBundleName = plist["CFBundleName"];
//var newVersion = CreateBuildNumber(prevVersion).ToString();
var newVersionName = GetVersionName(prevVersionName, buildVariant, git);
// we need to maintain version formatting here composed of one to three period-separated integers, so we cannot use the GetVersionName method as in Android for non-Prod.
var newVersionName = prevVersionName;
var newBundleId = GetiOSBundleId(buildVariant, projectType);
var newBundleName = GetiOSBundleName(buildVariant, projectType);
@@ -219,6 +222,11 @@ private void UpdateiOSInfoPlist(string plistPath, VariantConfig buildVariant, Gi
plist["NSExtension"]["NSExtensionAttributes"]["NSExtensionActivationRule"] = keyText.Replace("com.8bit.bitwarden", buildVariant.iOSBundleId);
}
if(buildVariant is Beta)
{
plist["ITSEncryptionExportComplianceCode"] = "3dd3e32f-efa6-4d99-b410-28aa28b1cb77";
}
SerializePlist(plistFile, plist);
Information($"Changed app name from {prevBundleName} to {newBundleName}");
@@ -228,12 +236,15 @@ private void UpdateiOSInfoPlist(string plistPath, VariantConfig buildVariant, Gi
Information($"{plistPath} updated with success!");
}
private void UpdateiOSEntitlementsPlist(string entitlementsPath, VariantConfig buildVariant)
private void UpdateiOSEntitlementsPlist(string entitlementsPath, VariantConfig buildVariant, bool updateApsEnv)
{
var EntitlementlistFile = File(entitlementsPath);
dynamic Entitlements = DeserializePlist(EntitlementlistFile);
Entitlements["aps-environment"] = buildVariant.ApsEnvironment;
if (updateApsEnv)
{
Entitlements["aps-environment"] = buildVariant.ApsEnvironment;
}
Entitlements["keychain-access-groups"] = new List<string>() { "$(AppIdentifierPrefix)" + buildVariant.iOSBundleId };
Entitlements["com.apple.security.application-groups"] = new List<string>() { $"group.{buildVariant.iOSBundleId}" };;
@@ -272,9 +283,10 @@ private void UpdateWatchPbxproj(string pbxprojPath, string newVersion)
const string pattern = @"MARKETING_VERSION = [^;]*;";
fileText = Regex.Replace(fileText, pattern, $"MARKETING_VERSION = {newVersion};");
FileWriteText(pbxprojPath, fileText);
Information($"{pbxprojPath} modified successfully.");
Information($"{pbxprojPath} modified Marketing Version successfully.");
}
/// <summary>
@@ -315,7 +327,7 @@ private void UpdateAppleIcons(string target, string appiconsetTarget)
Task("UpdateiOSIcons")
.Does(()=>{
UpdateAppleIcons("ios", Path.Combine(_slnPath, "src", "iOS", "Resources", "Assets.xcassets", "AppIcons.appiconset"));
UpdateAppleIcons("ios", Path.Combine(_slnPath, "src", "App", "Platforms", "iOS", "Resources", "Assets.xcassets", "AppIcons.appiconset"));
UpdateAppleIcons("watch", Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden WatchKit App", "Assets.xcassets", "AppIcon.appiconset"));
// TODO: Update complication icons when they start working
});
@@ -324,10 +336,10 @@ Task("UpdateiOSPlist")
.IsDependentOn("GetGitInfo")
.Does(()=> {
var buildVariant = GetVariant();
var infoPath = Path.Combine(_slnPath, "src", "iOS", "Info.plist");
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS", "Entitlements.plist");
var infoPath = Path.Combine(_slnPath, "src", "App", "Platforms", "iOS", "Info.plist");
var entitlementsPath = Path.Combine(_slnPath, "src", "App", "Platforms", "iOS", "Entitlements.plist");
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.MainApp);
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant);
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant, true);
});
Task("UpdateiOSAutofillPlist")
@@ -338,7 +350,7 @@ Task("UpdateiOSAutofillPlist")
var infoPath = Path.Combine(_slnPath, "src", "iOS.Autofill", "Info.plist");
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.Autofill", "Entitlements.plist");
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.Autofill);
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant);
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant, false);
});
Task("UpdateiOSExtensionPlist")
@@ -349,7 +361,7 @@ Task("UpdateiOSExtensionPlist")
var infoPath = Path.Combine(_slnPath, "src", "iOS.Extension", "Info.plist");
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.Extension", "Entitlements.plist");
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.Extension);
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant);
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant, false);
});
Task("UpdateiOSShareExtensionPlist")
@@ -360,7 +372,7 @@ Task("UpdateiOSShareExtensionPlist")
var infoPath = Path.Combine(_slnPath, "src", "iOS.ShareExtension", "Info.plist");
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.ShareExtension", "Entitlements.plist");
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.ShareExtension);
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant);
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant, false);
});
Task("UpdateiOSCodeFiles")
@@ -397,6 +409,22 @@ Task("UpdateWatchKitAppInfoPlist")
UpdateWatchKitAppInfoPlist(infoPath, buildVariant);
});
Task("UpdateDistProfiles")
.IsDependentOn("UpdateiOSCodeFiles")
.Does(()=> {
var buildVariant = GetVariant();
var filesToReplace = new string[] {
Path.Combine(".github", "resources", "export-options-app-store.plist"),
Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden.xcodeproj", "project.pbxproj")
};
foreach(string path in filesToReplace)
{
ReplaceInFile(path, "Dist:", buildVariant.DistProvisioningProfilePrefix);
}
});
#endregion iOS
#region Main Tasks
@@ -418,6 +446,7 @@ Task("iOS")
.IsDependentOn("UpdateiOSCodeFiles")
.IsDependentOn("UpdateWatchProject")
.IsDependentOn("UpdateWatchKitAppInfoPlist")
.IsDependentOn("UpdateDistProfiles")
.Does(()=>
{
Information("iOS app updated");
@@ -437,4 +466,4 @@ Options:
});
#endregion Main Tasks
RunTarget(target);
RunTarget(target);

View File

@@ -2,9 +2,9 @@ project_id_env: _CROWDIN_PROJECT_ID
api_token_env: CROWDIN_API_TOKEN
preserve_hierarchy: true
files:
- source: /src/App/Resources/AppResources.resx
dest: /src/App/Resources/%original_file_name%
translation: /src/App/Resources/AppResources.%two_letters_code%.resx
- source: /src/Core/Resources/Localization/AppResources.resx
dest: /src/Core/Resources/Localization/%original_file_name%
translation: /src/Core/Resources/Localization/AppResources.%two_letters_code%.resx
update_option: update_as_unapproved
languages_mapping:
two_letters_code:

View File

@@ -0,0 +1,79 @@
---
sidebar_position: 5
---
# .NET MAUI (legacy)
:::warning Legacy
This represents the **legacy** mobile app architecture done in .NET MAUI.
:::
The mobile .NET MAUI clients are Android and iOS applications with extensions and watchOS. They are
all located at https://github.com/bitwarden/mobile.
Principal structure is a follows:
- `App`: Main .NET MAUI project that shares code between both platforms (Android & iOS). One can see
specific platform code under the `Platforms` folder.
- `Core`: Shared code having both logical and UI parts of the app. Several classes are a port from
the Web Clients to C#. Here one can find most of the UI and logic since it's shared between App
and the iOS extensions.
- `iOS.Core`: Shared code used by the main iOS app and its extensions
- `iOS.Autofill`: iOS extension that handles Autofill
- `iOS.Extensions`: iOS extension that handles Autofill from the bottom sheet extension
- `iOS.ShareExtension`: iOS extension that handles sharing files through Send
- `watchOS`: All the code specific to the watchOS platform
- `bitwarden`: Stub iOS app so that the watchOS app has a companion app on Xcode
- `bitwarden WatchKit App`: Main Watch app where we set assets.
- `bitwarden WatchKit Extension`: All the logic and presentation logic for the Watch app is here
## Dependencies diagram
Below is a simplified dependencies diagram of the mobile repository.
```kroki type=plantuml
@startuml
skinparam BackgroundColor transparent
skinparam componentStyle rectangle
skinparam linetype ortho
title Simplified Dependencies Diagram
component "Core"
component "App"
component "iOS.Core"
component "iOS.Autofill"
component "iOS.Extension"
component "iOS.ShareExtension"
component "watchOS" {
component "bitwarden"
component "bitwarden WatchKit App"
component "bitwarden WatchKit Extension"
}
[App] --> [Core]
[iOS.Core] --> [App]
[App] --> [iOS.Core]
[App] --> [iOS.Autofill]
[App] --> [iOS.Extension]
[App] --> [iOS.ShareExtension]
[App] --> [bitwarden WatchKit App]
[iOS.Autofill] --> [Core]
[iOS.Autofill] --> [iOS.Core]
[iOS.Extension] --> [Core]
[iOS.Extension] --> [iOS.Core]
[iOS.ShareExtension] --> [Core]
[iOS.ShareExtension] --> [iOS.Core]
[bitwarden] --> [bitwarden WatchKit App]
[bitwarden WatchKit App] --> [bitwarden WatchKit Extension]
@enduml
```

View File

@@ -0,0 +1,26 @@
---
sidebar_position: 1
---
# Overview
:::warning Legacy
This represents the **legacy** mobile app overview architecture done in .NET MAUI.
:::
The overall architecture of the mobile applications is pretty similar to the
[web clients](../../clients/overview.md) one following a layered architecture:
- State
- Services
- Presentation
Even though the State and Services layers are pretty similar to the web ones the Presentation layer
differs:
## Presentation
The presentation layer is implemented using .NET MAUI for the mobile apps, except for the watchOS
one which uses SwiftUI [see ADR](../../adr/0017-watchOS-use-swift.md)

View File

@@ -0,0 +1,186 @@
# watchOS
:::warning Legacy
This represents the **legacy** watchOS app architecture done in .NET MAUI.
:::
## Overall architecture
The watchOS application is organized as follows:
- `src/watchOS`: All the code specific to the watchOS platform
- `bitwarden`: Stub iOS app so that the watchOS app has a companion app on Xcode
- `bitwarden WatchKit App`: Main Watch app where we set assets.
- `bitwarden WatchKit Extension`: All the logic and presentation logic for the Watch app is here
So almost all the things related to the watch app will be in the **WatchKit Extension**, the
WatchKit App one will be only for assets and some configs.
Then in the Extension we have a layered architecture:
- State (it's a really simplified version of the iOS state)
- Persistence (here we use `CoreData` to interact with the Database)
- Services (totp generation, crypto services and business logic)
- Presentation (use `SwiftUI` for the UI with an MVVM pattern)
## Integration with iOS
The watchOS app is developed using `Xcode` and `Swift` and we need to integrate it to the .NET MAUI
iOS application.
For this, the `iOS.csproj` has been adapted taking a
[solution](https://github.com/xamarin/xamarin-macios/issues/10070#issuecomment-1033428823) provided
in the `Xamarin.Forms` GitHub repository and modified to our needs:
```xml
<PropertyGroup>
<WatchAppBuildPath Condition=" '$(Configuration)' == 'Debug' ">$(Home)/Library/Developer/Xcode/DerivedData/bitwarden-cbtqsueryycvflfzbsoteofskiyr/Build/Products</WatchAppBuildPath>
<WatchAppBuildPath Condition=" '$(Configuration)' != 'Debug' ">$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)\..'))/watchOS/bitwarden.xcarchive/Products/Applications/bitwarden.app/Watch</WatchAppBuildPath>
<WatchAppBundle>Bitwarden.app</WatchAppBundle>
<WatchAppConfiguration Condition=" '$(Platform)' == 'iPhoneSimulator' ">watchsimulator</WatchAppConfiguration>
<WatchAppConfiguration Condition=" '$(Platform)' == 'iPhone' ">watchos</WatchAppConfiguration>
<WatchAppBundleFullPath Condition=" '$(Configuration)' == 'Debug' ">$(WatchAppBuildPath)/$(Configuration)-$(WatchAppConfiguration)/$(WatchAppBundle)</WatchAppBundleFullPath>
<WatchAppBundleFullPath Condition=" '$(Configuration)' != 'Debug' ">$(WatchAppBuildPath)/$(WatchAppBundle)</WatchAppBundleFullPath>
</PropertyGroup>
...
<ItemGroup Condition=" '$(Configuration)' == 'Debug' AND Exists('$(WatchAppBundleFullPath)') ">
<_ResolvedWatchAppReferences Include="$(WatchAppBundleFullPath)" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)' != 'Debug' ">
<_ResolvedWatchAppReferences Include="$(WatchAppBundleFullPath)" />
</ItemGroup>
<PropertyGroup Condition=" '$(_ResolvedWatchAppReferences)' != '' ">
<CodesignExtraArgs>--deep</CodesignExtraArgs>
</PropertyGroup>
<Target Name="PrintWatchAppBundleStatus" BeforeTargets="Build">
<Message Text="WatchAppBundleFullPath: '$(WatchAppBundleFullPath)' exists" Condition=" Exists('$(WatchAppBundleFullPath)') " />
<Message Text="WatchAppBundleFullPath: '$(WatchAppBundleFullPath)' does NOT exist" Condition=" !Exists('$(WatchAppBundleFullPath)') " />
</Target>
```
So on the `PropertyGroup` the `WatchAppBundleFullPath` is assembled together depending on the
Configuration and the Platform taking the output of the Xcode watchOS app build. Then there are some
`ItemGroup` to include the watch app depending on if it exists and the Configuration. The task
`_ResolvedWatchAppReferences` is the one responsible to peek into the `Bitwarden.app` built by Xcode
and if it finds a Watch app, it will just bundle it to the Xamarin iOS application. Finally, if the
Watch app is bundled, deep signing is enabled and the build path is printed.
:::caution
As one can see in the csproj, to bundle the watchOS app into the iOS app one needs to target the
correct platform. So if one is going to use a device, target the device on Xcode to build the
watchOS app and after the build is done one can go to VS4M to build the iOS app (which will bundle
the watchOS one) and run it on the device.
:::
## Synchronization between iPhone and Watch
In order to sync data between the iPhone and the Watch apps the
[Watch Connectivity Framework](https://developer.apple.com/documentation/watchconnectivity) is used.
So there is a Watch Connectivity Manager on each side that is the interface used for the services on
each platform to communicate.
For the sync communication, mainly
[updateApplicationContext](https://developer.apple.com/documentation/watchconnectivity/wcsession/1615621-updateapplicationcontext)
is used given that it always have the latest data sent available, it's sent in the background and
the counterpart device doesn't necessarily needs to be in range (so it's cached until it can be
delivered). Additionally,
[sendMessage](https://developer.apple.com/documentation/watchconnectivity/wcsession/1615687-sendmessage)
is also used to signal the counterpart of some action to take quickly (like triggering a sync from
the Watch).
The `WatchDTO` is the object that is sent in the synchronization that has all the information for
the Watch.
```kroki type=plantuml
title= iOS part
@startuml
title iOS
participant C as "Caller"
participant BWDS as "BaseWatchDeviceService"
participant WDS as "WatchDeviceService"
participant WCSM as "WCSessionManager"
boundary WCF as "Watch Connectivity Framework"
group Sync
C->>BWDS: SyncDataToWatchAsync(...)
BWDS->BWDS: GetStateAsync(...)
BWDS->>WDS: SendDataToWatchAsync(...)
WDS->>WCSM: SendBackgroundHighPriorityMessage(...)
WCSM->>WCF: UpdateApplicationContext(...)
end
@enduml
```
```kroki type=plantuml
title= iOS part
@startuml
title watchOS
boundary WCF as "Watch Connectivity Framework"
participant WCM as "WatchConnectivityManager"
participant SS as "StateService"
participant ES as "EnvironmentService"
participant CS as "CipherService"
participant WCS as "watchConnectivitySubject"
group Sync
WCF->>WCM: didReceiveApplicationContext(...)
WCM->>SS: update state
WCM->>ES: update environment
WCM->>CS: saveCiphers(...)
WCM->>WCS: fire notification change to subscribers
end
@enduml
```
## States
The next ones are the states in which the Watch application can be at a given time:
- **Valid:** Everything it's ok and the user can see the vault ciphers with TOTP
- **Need Login:** The user needs to log in using the iPhone
- **Need Setup:** The user needs to set up an account with "Connect to Watch" enabled on their
iPhone
- **Need Premium:** The current account is not a premium account
- **Need 2FA item:** The current account doesn't have any cipher with TOTP set up
- **Syncing:** Displayed when changing accounts and syncing the new vault TOTPs
- **Need Device Owner Auth:** The user needs to set up an Apple Watch Passcode in order to use the
app
## Persistence and encryption
On the Watch [CoreData](https://developer.apple.com/documentation/coredata) is used as persistence
for the ciphers. So in order to encrypt the data in them a Value Transformer in each encrypted
attribute is used: `StringEncryptionTransformer`.
Inside the transformer a call to the `CryptoService` is used that ends up using
[AES.GCM](https://developer.apple.com/documentation/cryptokit/aes/gcm) to encrypt the data with a
256 bits [SymmetricKey](https://developer.apple.com/documentation/cryptokit/symmetrickey). The key
is generated/loaded the first time something needs to be encrypted and stored in the device
Keychain.
## Crash reporting
On all the other mobile applications, [AppCenter](https://appcenter.ms/) is being used as Crash
reporting tool. However, it doesn't have support for watchOS (nor its internal library to handle
crashes).
So, on the watchOS app [Firebase Crashlytics](https://firebase.google.com/docs/crashlytics) is used
with basic crash reporting enabled (there is no handled error logging here yet). For this to work a
`GoogleService-Info.plist` file is needed which is injected on the CI.
At the moment of writing this document, no plist is configured for dev environment so `Crashlytics`
is enabled on **non-DEBUG** configurations.
There is a `Log` class to log errors happened in the app, but it's only enabled in **DEBUG**
configuration.

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

View File

@@ -0,0 +1,177 @@
# Android
:::warning Legacy
Getting started the **legacy** Android app done in .NET MAUI.
:::
## Requirements
Before you start, you should have the recommended [Tools and Libraries](../../../tools/index.md)
installed. You will also need to install:
1. Visual Studio 2022 / VS Code
2. [.NET 8 (latest)](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
- Note: Even if you have an ARM64 based Mac (M1, M2, M3, etc.), you can install all x64 SDKs to
run Android
- On Visual Studio for Mac you may need to turn on the feature for .NET 8 by going to Visual
Studio > Preferences > Preview Features > Use the .NET 8 SDK
3. .NET MAUI Workload
- You can install this by running `dotnet workload install maui`
4. Android SDK 34
- You can use the SDK manager in [Visual Studio][xamarin-vs], or [Android
Studio][android-studio] to install this
To make sure you have the Android SDK and Emulator installed:
1. Open Visual Studio
2. Click Tools > SDK Manager (under the Android subheading)
3. Click the Tools tab
4. Make sure the following items are installed:
- Android SDK tools (at least one version of the command-line tools)
- Android SDK Platform-Tools
- Android SDK Build Tools (at least one version)
- Android Emulator
5. Click Apply Changes if you've marked anything for installation
If you've missed anything, Visual Studio should prompt you anyway.
## Android Development Setup
To set up a new virtual Android device for debugging:
1. Click Tools > Device Manager (under the Android subheading)
2. Click New Device
3. Set up the device you want to emulate - you can just choose the Base Device and leave the
default settings if you're unsure
4. Visual Studio will then download the image for that device. The download progress is shown in
the progress in the Android Device Manager dialog.
5. Once this has completed, the emulated Android device will be available as a build target under
App > Debug > (name of device)
### ARM64 Macs
1. Install and open Android Studio
2. In the top navbar, click on Android Studio > Settings > Appearance & Behavior (tab) > System
Settings > Android SDK
3. In the SDK Platforms tab, ensure the "Show Package Details" checkbox is checked (located in the
bottom-right)
4. Bellow each Android API you'll see several System Images, pick one of the `ARM 64 v8a` and wait
for it to download
5. Go to View > Tool Windows > Device Manager
6. Inside Device Manager, create a device using the previously downloaded system image
![Android SDK configuration](android-sdk.png)
## F-Droid
On `App.csproj` and `Core.csproj` we can now pass `/p:CustomConstants=FDROID` when
building/releasing so that the `FDROID` constant is added to the defined ones at the project level
and we can use that with precompiler directives, e.g.:
```c#
#if FDROID
// perform operation only for FDROID.
#endif
```
## Building
There are currently a few problems on Visual Studio for Mac for building correctly the projects, so
if you encounter some errors build using the CLI (previously removing bin/obj folders):
```
dotnet build -f net8.0-android -c Debug
```
## Testing and Debugging
### Using the Android Emulator
In order to access `localhost:<port>` resources in the Android Emulator when debugging using Visual
Studio on your Mac natively, you'll need to configure the endpoint addresses using
`<http://10.0.2.2:<port>`\> in order to access `localhost`, which maps the Android proxy by design.
[xamarin-vs]: https://learn.microsoft.com/en-us/xamarin/android/get-started/installation/android-sdk
[android-studio]: https://developer.android.com/studio/releases/platforms
### Using Server Tunneling
Instead of configuring your device or emulator, you can instead use a
[proxy tunnel to your local server](../../../server/tunnel.md) and have your app connect to it
directly.
### Push Notifications
The default configuration for the Android app is to register itself to the same environment as
Bitwarden's QA Cloud. This means that if you try to debug the app using the production endpoints you
won't be able to receive Live Sync updates or Passwordless login requests.
<Bitwarden>
So, in order to receive notifications while debugging, you have two options:
- Use QA Cloud endpoints for the Api and Identity, or
- Use a local server setup where the Api is connected to QA Azure Notification Hub
</Bitwarden>
### Testing Passwordless Locally
Before you can start testing and debugging passwordless logins, make sure your local server setup is
running correctly ([server setup](../../../server/guide.md)). You should also be able to deploy your
Android app to your device or emulator.
:::note
Debugging and testing passwordless authentication is limited by
[push notifications](#push-notifications).
:::
Testing passwordless notifications:
1. Start your local server (`Api`, `Identity`, `Notifications`)
2. Make sure your mobile device can [connect to your local server](#using-server-tunneling)
3. [Start the web client](../../../clients/web-vault/index.mdx), as you will need it to make login
requests
4. Deploy the Android app to your device or emulator
5. After deployment, open the app, login to your QA account and activate passwordless login requests
in settings
6. Open the web vault using your preferred browser (ex: http://localhost:8080)
7. Enter the email address of an account that has previously authenticated on that device (i.e. is a
"known device") and click Continue. When presented with the login options, click click Login with
Device.
8. Check mobile device for the notification
<Bitwarden>
## AndroidX Credentials
Currently, the
[androidx.credentials](https://developer.android.com/jetpack/androidx/releases/credentials) official
binding has some bugs and we cannot use it yet. Because of this, we made a binding ourselves which
is located in here:
[Xamarin.AndroidX.Credentials](https://github.com/bitwarden/xamarin.androidx.credentials).
As of today, we are using version 1.2.0.
In the projects, the package is added as a local NuGet package located in
`lib/android/Xamarin.AndroidX.Credentials` and this source is already configured in the
`nuget.config` file.
In the case a change is needed on the binding, create a new local NuGet package and replace it in
the aforementioned source.
:::warning
Do not add the project to the solution and as a project reference to the `App.csproj` /
`Core.csproj` this will strangely make the iOS app crash on start because of solution configuration.
Even though we couldn't find the root cause, this is the effect caused by this action.
:::
</Bitwarden>

View File

@@ -0,0 +1,107 @@
---
sidebar_position: 4
---
# .NET MAUI (legacy)
:::warning Legacy
Getting started the **legacy** mobile app done in .NET MAUI.
:::
## Configure Git blame
We recommend that you configure git to ignore the Prettier revision:
```bash
git config blame.ignoreRevsFile .git-blame-ignore-revs
```
## Android Development
See the [Android Mobile app](./android/index.md) page to set up an Android development environment.
## iOS Development
<Bitwarden>
See the [iOS Mobile app](./ios/index.mdx) page to set up an iOS development environment.
</Bitwarden>
<Community>
Unfortunately, iOS development requires provisioning profiles and other capabilities only available
to internal team members. We do not have any documentation for community developers at this time.
</Community>
## watchOS Development
<Bitwarden>
See the [watchOS app](./watchos) page to set up an watchOS development environment.
</Bitwarden>
<Community>
Unfortunately, watchOS development requires provisioning profiles and other capabilities only
available to internal team members. We do not have any documentation for community developers at
this time.
</Community>
## Unit tests
:::info TL;DR;
In order to run unit tests add the argument `/p:CustomConstants=UT` on the `dotnet` command for
building/running. To work on Unit testing or use a Test runner uncomment the `CustomConstants` line
on the `Directory.Build.props`
:::
Given that the `Core.csproj` is a MAUI project with `net8.0-android;net8.0-ios` target frameworks
and we need `net8.0` for the tests we need a way to add that. The `Core.Test.csproj` has `net8.0` as
a target so by adding the the argument `/p:CustomConstants=UT` we add `UT` as a constant to use in
the projects. With that in place the next things happen:
- `UT` is added as a constant to use by precompiler directives
- `Core.csproj` is changed to add `net8.0` as a target framework for unit tests
- `FFImageLoading` is removed as a reference given that it doesn't support `net8.0`. Because of
this, now we have a wrapped `CachedImage` that uses the library one if it's not `UT` and a custom
one with NOOP implementation for `UT`
So if one wants to build the test project, one needs to go to `test/Core.Test` and run:
```bash
dotnet build -f net8.0 /p:CustomConstants=UT
```
and to run the tests go to the same folder and run:
```bash
dotnet test -f net8.0 /p:CustomConstants=UT
```
Finally, when working on the `Core.Test` project or when wanting to use a Test runner, go to the
`Directory.Build.props` (located in the root) and uncomment the line referencing `CustomConstants`
so that everything is loaded accordingly in the project. Because of some issues, the referenced
projects, e.g. `Core`, are only included when the `UT` constant is in place. By uncommenting this
line the projects will be referenced and one can work on that project or run the tests from a Test
runner.
## Custom constants
There are custom constants to be used by the parameter `/p:CustomConstants={Value}` when
building/running/releasing:
- `FDROID`: This is used to indicate that it's and F-Droid build/release
([want to know more?](./android/index.md#f-droid))
- `UT`: This is used when building/running the test projects or when working on one of them
([want to know more?](#unit-tests))
These constants are added to the defined ones, so anyone can use them in the code with precompiler
directives.

View File

@@ -0,0 +1,440 @@
---
sidebar_custom_props:
access: bitwarden
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
# iOS
:::warning Legacy
Getting started the **legacy** iOS app done in .NET MAUI.
:::
## Requirements
1. Visual Studio 2022 / VS Code
2. [.NET 8 (latest)](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
- On Visual Studio for Mac you may need to turn on the feature for .NET 8 by going to Visual
Studio > Preferences > Preview Features > Use the .NET 8 SDK
3. .NET MAUI Workload
- You can install this by running `dotnet workload install maui`
4. A Mac with Xcode 15.0 installed
## Apple Developer Account Setup
1. Accept your invite to the Bitwarden Apple Developer team. You should get a request in your email
with the subject "You're invited to join a development team." Click the link, "Accept Invitation"
and you'll be prompted to create an Apple ID for your Bitwarden email address. If you didn't
receive this email, contact the IT department (@IT in slack). Accept the terms and conditions and
complete the sign up flow
2. Go to [Apple ID Online](https://appleid.apple.com/) and log in with your new Apple ID. Set up
2-factor authentication (using mobile phone and/or trusted device) - this is critical because
Apple no longer allows "developer" accounts without MFA, but it won't tell you that when your
build fails locally
3. Go to [App Store Connect](https://appstoreconnect.apple.com/) and accept the terms and conditions
4. Ensure you have access to the Bitwarden team and team app profiles
5. Go to [Apple Developer Account](https://developer.apple.com/account/) and go to the
"Certificates, IDs & Profiles" menu item. Check that you can see the 8bit Solutions LLC
Certificates in the Certificates section, and the Bitwarden profiles in the Profiles section. If
any of this is missing, ask the IT department (@IT #tech-support in slack) for the additional
roles / permissions
## macOS Setup
Next, you need to get your Mac environment set up for building and running the Bitwarden iOS mobile
project. This requires creating the necessary developer provisioning profiles for code signing and
execution on your Mac through Xcode. Visual Studio has a simple process to get all of the
provisioning profiles, however this is prone to fail without much feedback. Try the Visual Studio
instructions ("The Easy Way") first, and fallback to the Xcode instructions (“The Hard Way”) if
required.
### Visual Studio: The Easy Way
1. Open Visual Studio for Mac
2. Go to Preferences > Publishing > Apple Developer Accounts
3. Click “Add”, choose "Enterprise Account", and sign in with your previously configured Apple
Developer account
:::note
If you receive a "Failed to synchronize with Apple Developer Portal" error, youre missing
additional roles / permissions.
:::
After signing in successfully, you should see your account in the list and “Bitwarden Inc” in
the account teams list
4. Click “View Details…”
5. If you dont have a valid Apple Development certificate, click Create certificate > Apple
Development
6. Click “Download All Profiles”
7. You should now be able to run the app by setting
`iOS > Debug | iPhone Simulator > [pick any iOS Simulator]` in the top left corner and pressing
Play
![](./run-debug.png)
If this worked, you can skip the next section.
If you only have the option "Generic Simulator", with a message to lower the 'Deployment Target',
your version of MAUI may not yet support the version of Xcode that you are using (as discussed
[here](https://github.com/xamarin/xamarin-macios/issues/15954#issuecomment-1246025735)).
![](./troubleshoot-generic-simulator.png)
To work around this issue, try [downloading](https://developer.apple.com/download/all/) and
installing an older version of Xcode from Apple (you can look for guidance on which Xcode version to
use from the Xamarin.iOS [release notes](https://github.com/xamarin/xamarin-macios/releases) (this
applies to MAUI as well). After installing the new version of Xcode, restart Visual Studio and load
your project to verify your available simulator options.
:::note
If you need multiple versions of Xcode installed on your development machine, you can rename the
`Xcode.app` file extracted from your download to something else (e.g. "Xcode_14_2.app") before
placing it in your Applications folder. You can then switch between Xcode versions by using
`xcode-select` from the command line. e.g.:
```shell
sudo xcode-select -s /Applications/Xcode_14_2.app
```
You may achieve similar results with tooling such as
[Xcodes.app](https://github.com/XcodesOrg/XcodesApp)
:::
### Xcode: The Hard Way
:::note
If you're the next person to follow these instructions, please commit and upload the Xcode project
files you create so we can streamline this process.
:::
Only try these instructions if the Visual Studio instructions above didn't work for you.
1. Open Xcode
2. Accept any defaults, ensure any extensions/add-ons have been installed, etc.
3. Create new project... > iOS > App
4. Use the following options for your new project:
- Product Name: "bitwarden"
- Team: Bitwarden Inc (if this is missing, double check your Apple Developer Account setup
above)
- Organization Identifier: "com.8bit"
- Bundle Identifier (automatically generated): "com.8bit.bitwarden"
- Language: Objective-C
- User Interface: Storyboard
- Leave all other checkboxes unchecked (or uncheck them)
![Example configuration for new Xcode project](./new-project-options.png)
5. Click Next, save to the default location and then click "Create"
6. On the project configuration page, click the "Signing & Capabilities" tab
7. Make sure you have the following defaults:
- Automatically manage signing: (checked)
- Team: Bitwarden Inc
- Provisioning Profile: Xcode Managed Profile
- Signing Certificate: your Apple ID/Name
![Example configuration for Signing & Capabilities screen](./signing-and-capabilities.png)
8. From the menu bar, click Product > Build
9. Repeat Steps 3-8, with the following changes in step 4:
- Product Name: "find-login-action-extension"
- Organization Identifier: "com.8bit.bitwarden"
- Bundle Identifier (automatically generated): "com.8bit.bitwarden.find-login-action-extension"
10. Repeat Steps 3-8, with the following changes in step 4:
- Product Name: "autofill"
- Organization Identifier: "com.8bit.bitwarden"
- Bundle Identifier (automatically generated): "com.8bit.bitwarden.autofill"
11. Repeat Steps 3-8, with the following changes in step 4:
- Product Name: "share-extension"
- Organization Identifier: "com.8bit.bitwarden"
- Bundle Identifier (automatically generated): "com.8bit.bitwarden.share-extension"
12. If you have a physical device (e.g. iPhone or iPad) that you want to use for testing, you will
also need to do the following for each of the Xcode projects you just created:
- connect the device with a cable
- select your device as as the build target in Xcode
- from the menu bar, click Product > Build
- agree to register your device if asked
:::note
Sometimes these profiles can mess up. If you have issues running on your physical device (or
simulator) try running `rm -r ~/Library/MobileDevice/Provisioning\ Profiles` to clear them out.
Build each Xcode project again to regenerate them.
:::
## Visual Studio
Next, we need to configure your Visual Studio environment for development.
<Tabs groupId="os">
<TabItem value="win" label="Windows" default>
1. Connect to the Mac that you just completed the above steps on
2. Open Visual Studio and click Tools > iOS > Pair to Mac
3. Scan for and select your machine. If you don't see it, click the "Add Mac..." button and put in
the Mac name or IP address. If you don't know your Mac name (or you're in a Windows VM on your
Mac), go to your Mac and open System Preferences > Sharing and look for the ".local" address of
your machine
4. Provide your Username and Password for macOS when prompted
5. Once paired, close the Pair Mac window
6. Change your active build profile to Debug > iPhoneSimulator > iOS
7. Rebuild the iOS project from Solution Explorer
8. You can now debug using the iOS Simulator
</TabItem>
<TabItem value="mac" label="macOS">
1. Check that command line tools are installed:
1. Open Xcode
2. From the menu bar, click Xcode > Preferences > Locations
3. Make sure an Xcode version is selected under "Command Line Tools"
2. Open Visual Studio for Mac
3. Open the mobile solution file (`bitwarden-mobile.sln`) in the root of your local mobile
repository
4. In the top bar, you should be able to select App > Debug > select your model and click run (or
your physical device if you set one up)
</TabItem>
</Tabs>
## Building
To build from the CLI, navigate to the application directory:
For device:
```
cd src/App
dotnet build -f net8.0-ios -c Debug -r ios-arm64
```
For simulator:
```
cd src/App
dotnet build -f net8.0-ios -c Debug -r iossimulator-x64
```
You can also use the IDE but keep in mind:
:::tip Visual Studio for Mac
There are currently a few problems on Visual Studio for Mac for building correctly the projects, so
if you encounter some errors, build using the CLI being into the `src/App` folder (previously
removing bin/obj folders).
:::
:::tip Argon2Id
If you find any errors regarding argon2Id library when building for simulator, please be sure that
you are building for runtime identifier `iossimulator-x64` as currently the library doesn't support
`iossimulator-arm64`.
:::
:::tip Troubleshooting common mistakes
If you find the next error:
> `error NETSDK1134`: Building a solution with a specific RuntimeIdentifier is not supported. If you
> would like to publish for a single RID, specify the RID at the individual project level instead
you almost surely are trying to build the app from the root folder. Instead go to `src/App` and try
building again.
:::
### Argon2Id library loading
The Argon2Id library (`libargon2.a`) is loaded using `MTouchExtraArgs` in almost all projects of the
solution. In order to make this simpler a property was added into **Directory.Build.props** called
`Argon2IdLoadMtouchExtraArgs` which has the code to fill in the extra args parameter. Each project
is configured with this property so this is only added on the correct runtime identifiers and we can
build the app successfully on each case.
### Ignoring extensions / watchOS app
Sometimes we need to quickly build the app or maybe some configuration on the iOS extensions or the
watchOS app gets in the way. In order to have a fast way to only care about the main app two
properties were added to the **Directory.Build.Props** to help with this:
- `IncludeBitwardeniOSExtensions`: If `True` then all the iOS extensions will be included on the
building of the main app, otherwise they will be skipped.
- `IncludeBitwardenWatchOSApp`: If `True` then the watchOS app will be included on the building of
the main app, otherwise it will be skipped.
:::warning Shared code
Toggling these off can provide a faster developer experience which is really useful in a lot of
scenarios, but always bear in mind that a lot of things are shared between the main app and the
extensions so before pushing your work, test again with everything enabled just in case.
:::
### Release mode locally
There are some issues that require us to build the app on **Release** configuration but locally
without going through the CI/CD pipeline. The problem is that we don't have the code signing details
for Distribution locally. To overcome this we can use the same `CodesignProvision` and `CodesignKey`
we use for **Debug** but on the **Release** config. The thing is that it's a bit cumbersome to
change that on every project so two properties were added to the **Directory.Build.Props** to help
with this:
- `ReleaseCodesignProvision`: `CodesignProvision` for Release config on all projects
- `ReleaseCodesignKey`: `CodesignKey` for Release config on all projects
By replacing their values, all projects will have their values applied so it's easier to build the
app in **Release** mode locally.
## Debugging
### iPhone Simulator
The iPhone Simulator has access to localhost and you can point the client at your local dev server
as usual. However, the app will require https by default. To allow http for testing purposes, follow
these steps.
1. Open `src/App/Platforms/iOS/Info.plist` in Visual Studio Code or another editor so that you can
edit the raw XML. (Don't use the Property List Editor in Visual Studio.)
2. Add the following code in the top-level `<dict>` element:
```xml
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
</dict>
```
3. Save and exit `Info.plist`
4. Press <kbd>Command</kbd> + <kbd>B</kbd> to force a new build before launching
5. Don't push these changes :)
### iPhone device
The device doesnt have direct access to your Macs localhost, so you can follow
[this guide to connect them](https://ymoondhra.medium.com/how-to-run-localhost-on-your-iphone-4110a54d1896).
After you do that, youll have to also modify the `Info.plist` to allow http for testing purposes as
explained before on the simulator testing.
Its also highly likely that you need to change the `launchSettings.json` on Server, on `Properties`
of each project. There you need to change the `applicationUrl` of `iisSettings -> iisExpress` and of
`profiles -> Identify` so that instead of `localhost` it says `name.local` where `name` is the
computer name you set on Macs Sharing config.
Before you actually test on the app, open a browser and try to connect to the `Api` by going to
`http://name.local:4000/alive` . If this doesnt work then review the steps on the guide or the
server configuration. Make sure you have your `User secrets` up to date as well.
Finally, youll have to configure the `Api` and `Identity` urls on the phone to use
`http://name.local:4000` and `http://name.local:33656` where `name` is the computer name you set on
Macs Sharing config.
### iOS Extensions
1. Set the iOS Extension project as Startup project
2. Press Run
3. You will receive a popup saying "Waiting for the debugger to connect..."
4. Dont open the Bitwarden app (otherwise the debugger will connect to it instead of the
extension). Instead trigger the extension
5. Your extension breakpoints should now be hit
For example: if you want to debug the **iOS.Autofill** extension, you would complete steps 1 - 3,
then go to your iOS device, open a browser, go to a login, tap the key icon and open Bitwarden from
the bottom popup.
### Using Server Tunneling
Instead of configuring your device or emulator to ignore SSL certificates, you can instead use a
[proxy tunnel to your local server](../../../server/tunnel.md) and have your app connect to it
directly.
### Push Notifications (Live Sync & Passwordless)
Push notifications are not currently available for debug deployments. They are only supported on
TestFlight and production builds.

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,101 @@
---
sidebar_custom_props:
access: bitwarden
---
# watchOS
:::warning Legacy
Getting started the **legacy** watchOS app done in .NET MAUI.
:::
## Requirements
Follow the [iOS Setup](../ios/index.mdx).
In order for everything to work properly **devices** are needed. On simulators the synchronization
won't work (and some other parts may not work as well). Also, have Bluetooth enabled if possible to
ease the sync between devices and the debugging communication.
It's recommended to read the
[Watch Architecture](../../../../../architecture/mobile-clients/net-maui-legacy/watchOS) as well.
## macOS Setup
Having followed the macOS setup of iOS, no additional configuration is needed given that when the
project is opened in Xcode it will automatically have the provisioning profiles set up.
## Debugging
There are two parts from where to debug:
- From Visual Studio for Mac (the iOS app)
- From Xcode (the watchOS app)
For now, there is no way to debug both apps (iOS and watchOS) at the same time given that from MAUI
there is no access to debug information of the watchOS app and from Xcode an iOS stub app is
installed on the iPhone to debug it. So, at the moment of debugging one needs to choose which part
to have information about, therefore whether to debug from VS4M or from Xcode.
:::caution
When debugging from Xcode the MAUI iOS app will be replaced with the stub one from Xcode. So any
configuration on the iOS app will be lost (like server urls)
When debugging from VS4M, uninstall the previous watchOS app (if any) from the Apple Watch in
between builds to have it always up to date (there are times that if one doesn't uninstall the
previous watchOS app it doesn't get updated)
:::
:::tip
If one needs to get the logs or use the _Console_ app to see the logs from the watch then one needs
to install the `sysdiagnose` profile for watchOS from Apple Developer site
[here](https://developer.apple.com/bug-reporting/profiles-and-logs/?name=sysdiagnose) into the
paired iPhone and after that restart both devices in order for the logs to work.
:::
### Building
Given that the MAUI iOS app needs the output of the build of Xcode, one needs to build the watchOS
app from Xcode first and then from VS4M build the iOS app to run it on the device.
The output of Xcode build is stored in a location pretty similar to the next one that is configured
in the `iOS.csproj`:
```xml
<PropertyGroup>
<WatchAppBuildPath Condition=" '$(Configuration)' == 'Debug' ">$(Home)/Library/Developer/Xcode/DerivedData/bitwarden-cbtqsueryycvflfzbsoteofskiyr/Build/Products</WatchAppBuildPath>
```
It's highly likely that the folder `bitwarden-cbtqsueryycvflfzbsoteofskiyr` won't be the same on
every Mac. So one needs to change that part in `iOS.csproj` to the one created automatically by
Xcode locally.
To know exactly which is the path: Open the Project in Xcode -> Go to Product -> Show Build Folder
in Finder.
_This needs to be improved to have a fixed location or an easier way to get it automatically._
:::caution
One needs to take special attention to target the same platform on both IDEs. Therefore when running
on a device, target the device both in Xcode and on VS4M when building so that the watchOS app is
bundled correctly. Also one needs to make sure that "bitwarden WatchKit app" scheme is selected.
:::
### Synchronization
There is no way to debug the synchronization completely at the same time for the reasons
aforementioned.
So one can debug one end (iOS) or the other (watchOS).
If needed to check something on both ends "at the same time" (like to check why a message is not
sent/arrived), one needs to use console logging or adapt part of the MAUI code to the iOS stub app
on Xcode and debug the synchronization from Xcode.

View File

@@ -1,7 +1,6 @@
{
"sdk": {
"version": "7.0.400",
"rollForward": "latestPatch",
"allowPrerelease": false
"version": "8.0.402",
"rollForward": "disable"
}
}

Submodule lib/MessagePack deleted from 1ecb15e311

View File

@@ -0,0 +1,19 @@
Copyright 2018 Read Evaluate Press, LLC
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,32 @@
Pod::Spec.new do |s|
s.name = 'MessagePack-FlightSchool'
s.module_name = 'MessagePack'
s.version = '1.2.4'
s.summary = 'A MessagePack encoder and decoder for Codable types.'
s.description = <<-DESC
This functionality is discussed in Chapter 7 of
Flight School Guide to Swift Codable.
DESC
s.homepage = 'https://flight.school/books/codable/'
s.license = { type: 'MIT', file: 'LICENSE.md' }
s.author = { 'Mattt' => 'mattt@flight.school' }
s.social_media_url = 'https://twitter.com/mattt'
s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.10'
s.watchos.deployment_target = '2.0'
s.tvos.deployment_target = '9.0'
s.source = { git: 'https://github.com/Flight-School/MessagePack.git',
tag: s.version.to_s }
s.source_files = 'Sources/**/*.swift'
s.swift_version = '4.2'
s.static_framework = true
end

View File

@@ -0,0 +1,13 @@
import MessagePack
let encoder = MessagePackEncoder()
let value: String = "hello"
let encodedData = try encoder.encode(value)
print("Bytes: ", encodedData.map{ String($0, radix: 16, uppercase: true) })
let decoder = MessagePackDecoder()
let decodedValue = try decoder.decode(String.self, from: encodedData)
decodedValue == value

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='5.0' target-platform='macos' executeOnSourceChanges='false'>
<timeline fileName='timeline.xctimeline'/>
</playground>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@@ -0,0 +1,543 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXAggregateTarget section */
"MessagePack::MessagePackPackageTests::ProductTarget" /* MessagePackPackageTests */ = {
isa = PBXAggregateTarget;
buildConfigurationList = OBJ_57 /* Build configuration list for PBXAggregateTarget "MessagePackPackageTests" */;
buildPhases = (
);
dependencies = (
OBJ_60 /* PBXTargetDependency */,
);
name = MessagePackPackageTests;
productName = MessagePackPackageTests;
};
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
1BC312FF2992DE9C00177F2A /* DataSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BC312FE2992DE9C00177F2A /* DataSpec.swift */; };
OBJ_38 /* AnyCodingKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* AnyCodingKey.swift */; };
OBJ_39 /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* Box.swift */; };
OBJ_40 /* KeyedDecodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* KeyedDecodingContainer.swift */; };
OBJ_41 /* MessagePackDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* MessagePackDecoder.swift */; };
OBJ_42 /* SingleValueDecodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* SingleValueDecodingContainer.swift */; };
OBJ_43 /* UnkeyedDecodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* UnkeyedDecodingContainer.swift */; };
OBJ_44 /* KeyedEncodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_17 /* KeyedEncodingContainer.swift */; };
OBJ_45 /* MessagePackEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_18 /* MessagePackEncoder.swift */; };
OBJ_46 /* SingleValueEncodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_19 /* SingleValueEncodingContainer.swift */; };
OBJ_47 /* UnkeyedEncodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_20 /* UnkeyedEncodingContainer.swift */; };
OBJ_48 /* FixedWidthInteger+Bytes.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_21 /* FixedWidthInteger+Bytes.swift */; };
OBJ_55 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; };
OBJ_66 /* Airport.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_24 /* Airport.swift */; };
OBJ_67 /* MessagePackDecodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_25 /* MessagePackDecodingTests.swift */; };
OBJ_68 /* MessagePackEncodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_26 /* MessagePackEncodingTests.swift */; };
OBJ_69 /* MessagePackPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_27 /* MessagePackPerformanceTests.swift */; };
OBJ_70 /* MessagePackRoundTripTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_28 /* MessagePackRoundTripTests.swift */; };
OBJ_72 /* MessagePack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "MessagePack::MessagePack::Product" /* MessagePack.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
1BC312FC2989A1AD00177F2A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = OBJ_1 /* Project object */;
proxyType = 1;
remoteGlobalIDString = "MessagePack::MessagePack";
remoteInfo = MessagePack;
};
1BC312FD2989A1B200177F2A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = OBJ_1 /* Project object */;
proxyType = 1;
remoteGlobalIDString = "MessagePack::MessagePackTests";
remoteInfo = MessagePackTests;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
1BC312FE2992DE9C00177F2A /* DataSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSpec.swift; sourceTree = "<group>"; };
"MessagePack::MessagePack::Product" /* MessagePack.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = MessagePack.framework; sourceTree = BUILT_PRODUCTS_DIR; };
"MessagePack::MessagePackTests::Product" /* MessagePackTests.xctest */ = {isa = PBXFileReference; lastKnownFileType = file; path = MessagePackTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
OBJ_10 /* Box.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Box.swift; sourceTree = "<group>"; };
OBJ_12 /* KeyedDecodingContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedDecodingContainer.swift; sourceTree = "<group>"; };
OBJ_13 /* MessagePackDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagePackDecoder.swift; sourceTree = "<group>"; };
OBJ_14 /* SingleValueDecodingContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleValueDecodingContainer.swift; sourceTree = "<group>"; };
OBJ_15 /* UnkeyedDecodingContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnkeyedDecodingContainer.swift; sourceTree = "<group>"; };
OBJ_17 /* KeyedEncodingContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedEncodingContainer.swift; sourceTree = "<group>"; };
OBJ_18 /* MessagePackEncoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagePackEncoder.swift; sourceTree = "<group>"; };
OBJ_19 /* SingleValueEncodingContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleValueEncodingContainer.swift; sourceTree = "<group>"; };
OBJ_20 /* UnkeyedEncodingContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnkeyedEncodingContainer.swift; sourceTree = "<group>"; };
OBJ_21 /* FixedWidthInteger+Bytes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FixedWidthInteger+Bytes.swift"; sourceTree = "<group>"; };
OBJ_24 /* Airport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Airport.swift; sourceTree = "<group>"; };
OBJ_25 /* MessagePackDecodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagePackDecodingTests.swift; sourceTree = "<group>"; };
OBJ_26 /* MessagePackEncodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagePackEncodingTests.swift; sourceTree = "<group>"; };
OBJ_27 /* MessagePackPerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagePackPerformanceTests.swift; sourceTree = "<group>"; };
OBJ_28 /* MessagePackRoundTripTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagePackRoundTripTests.swift; sourceTree = "<group>"; };
OBJ_29 /* MessagePack.xcworkspace */ = {isa = PBXFileReference; lastKnownFileType = wrapper.workspace; path = MessagePack.xcworkspace; sourceTree = SOURCE_ROOT; };
OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
OBJ_9 /* AnyCodingKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyCodingKey.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
OBJ_49 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
OBJ_71 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 0;
files = (
OBJ_72 /* MessagePack.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
OBJ_11 /* Decoder */ = {
isa = PBXGroup;
children = (
OBJ_12 /* KeyedDecodingContainer.swift */,
OBJ_13 /* MessagePackDecoder.swift */,
OBJ_14 /* SingleValueDecodingContainer.swift */,
OBJ_15 /* UnkeyedDecodingContainer.swift */,
);
path = Decoder;
sourceTree = "<group>";
};
OBJ_16 /* Encoder */ = {
isa = PBXGroup;
children = (
OBJ_17 /* KeyedEncodingContainer.swift */,
OBJ_18 /* MessagePackEncoder.swift */,
OBJ_19 /* SingleValueEncodingContainer.swift */,
OBJ_20 /* UnkeyedEncodingContainer.swift */,
);
path = Encoder;
sourceTree = "<group>";
};
OBJ_22 /* Tests */ = {
isa = PBXGroup;
children = (
OBJ_23 /* MessagePackTests */,
);
name = Tests;
sourceTree = SOURCE_ROOT;
};
OBJ_23 /* MessagePackTests */ = {
isa = PBXGroup;
children = (
OBJ_24 /* Airport.swift */,
OBJ_25 /* MessagePackDecodingTests.swift */,
OBJ_26 /* MessagePackEncodingTests.swift */,
OBJ_27 /* MessagePackPerformanceTests.swift */,
OBJ_28 /* MessagePackRoundTripTests.swift */,
);
name = MessagePackTests;
path = Tests/MessagePackTests;
sourceTree = SOURCE_ROOT;
};
OBJ_30 /* Products */ = {
isa = PBXGroup;
children = (
"MessagePack::MessagePackTests::Product" /* MessagePackTests.xctest */,
"MessagePack::MessagePack::Product" /* MessagePack.framework */,
);
name = Products;
sourceTree = BUILT_PRODUCTS_DIR;
};
OBJ_5 /* */ = {
isa = PBXGroup;
children = (
OBJ_6 /* Package.swift */,
OBJ_7 /* Sources */,
OBJ_22 /* Tests */,
OBJ_29 /* MessagePack.xcworkspace */,
OBJ_30 /* Products */,
);
name = "";
sourceTree = "<group>";
};
OBJ_7 /* Sources */ = {
isa = PBXGroup;
children = (
OBJ_8 /* MessagePack */,
);
name = Sources;
sourceTree = SOURCE_ROOT;
};
OBJ_8 /* MessagePack */ = {
isa = PBXGroup;
children = (
OBJ_9 /* AnyCodingKey.swift */,
OBJ_10 /* Box.swift */,
OBJ_11 /* Decoder */,
OBJ_16 /* Encoder */,
OBJ_21 /* FixedWidthInteger+Bytes.swift */,
1BC312FE2992DE9C00177F2A /* DataSpec.swift */,
);
name = MessagePack;
path = Sources/MessagePack;
sourceTree = SOURCE_ROOT;
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
"MessagePack::MessagePack" /* MessagePack */ = {
isa = PBXNativeTarget;
buildConfigurationList = OBJ_34 /* Build configuration list for PBXNativeTarget "MessagePack" */;
buildPhases = (
OBJ_37 /* Sources */,
OBJ_49 /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = MessagePack;
productName = MessagePack;
productReference = "MessagePack::MessagePack::Product" /* MessagePack.framework */;
productType = "com.apple.product-type.framework";
};
"MessagePack::MessagePackTests" /* MessagePackTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = OBJ_62 /* Build configuration list for PBXNativeTarget "MessagePackTests" */;
buildPhases = (
OBJ_65 /* Sources */,
OBJ_71 /* Frameworks */,
);
buildRules = (
);
dependencies = (
OBJ_73 /* PBXTargetDependency */,
);
name = MessagePackTests;
productName = MessagePackTests;
productReference = "MessagePack::MessagePackTests::Product" /* MessagePackTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
"MessagePack::SwiftPMPackageDescription" /* MessagePackPackageDescription */ = {
isa = PBXNativeTarget;
buildConfigurationList = OBJ_51 /* Build configuration list for PBXNativeTarget "MessagePackPackageDescription" */;
buildPhases = (
OBJ_54 /* Sources */,
);
buildRules = (
);
dependencies = (
);
name = MessagePackPackageDescription;
productName = MessagePackPackageDescription;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
OBJ_1 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 9999;
};
buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "MessagePack" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
);
mainGroup = OBJ_5 /* */;
productRefGroup = OBJ_30 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
"MessagePack::MessagePack" /* MessagePack */,
"MessagePack::SwiftPMPackageDescription" /* MessagePackPackageDescription */,
"MessagePack::MessagePackPackageTests::ProductTarget" /* MessagePackPackageTests */,
"MessagePack::MessagePackTests" /* MessagePackTests */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
OBJ_37 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 0;
files = (
OBJ_38 /* AnyCodingKey.swift in Sources */,
OBJ_39 /* Box.swift in Sources */,
OBJ_40 /* KeyedDecodingContainer.swift in Sources */,
OBJ_41 /* MessagePackDecoder.swift in Sources */,
OBJ_42 /* SingleValueDecodingContainer.swift in Sources */,
OBJ_43 /* UnkeyedDecodingContainer.swift in Sources */,
OBJ_44 /* KeyedEncodingContainer.swift in Sources */,
OBJ_45 /* MessagePackEncoder.swift in Sources */,
OBJ_46 /* SingleValueEncodingContainer.swift in Sources */,
OBJ_47 /* UnkeyedEncodingContainer.swift in Sources */,
1BC312FF2992DE9C00177F2A /* DataSpec.swift in Sources */,
OBJ_48 /* FixedWidthInteger+Bytes.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
OBJ_54 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 0;
files = (
OBJ_55 /* Package.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
OBJ_65 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 0;
files = (
OBJ_66 /* Airport.swift in Sources */,
OBJ_67 /* MessagePackDecodingTests.swift in Sources */,
OBJ_68 /* MessagePackEncodingTests.swift in Sources */,
OBJ_69 /* MessagePackPerformanceTests.swift in Sources */,
OBJ_70 /* MessagePackRoundTripTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
OBJ_60 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = "MessagePack::MessagePackTests" /* MessagePackTests */;
targetProxy = 1BC312FD2989A1B200177F2A /* PBXContainerItemProxy */;
};
OBJ_73 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = "MessagePack::MessagePack" /* MessagePack */;
targetProxy = 1BC312FC2989A1AD00177F2A /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
OBJ_3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_NS_ASSERTIONS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
MACOSX_DEPLOYMENT_TARGET = 10.10;
ONLY_ACTIVE_ARCH = YES;
OTHER_SWIFT_FLAGS = "-DXcode";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "SWIFT_PACKAGE DEBUG";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
USE_HEADERMAP = NO;
};
name = Debug;
};
OBJ_35 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ENABLE_TESTABILITY = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
);
HEADER_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = MessagePack.xcodeproj/MessagePack_Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
OTHER_CFLAGS = "$(inherited)";
OTHER_LDFLAGS = "$(inherited)";
OTHER_SWIFT_FLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = MessagePack;
PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)";
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
SWIFT_VERSION = 4.0;
TARGET_NAME = MessagePack;
};
name = Debug;
};
OBJ_36 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ENABLE_TESTABILITY = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
);
HEADER_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = MessagePack.xcodeproj/MessagePack_Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
OTHER_CFLAGS = "$(inherited)";
OTHER_LDFLAGS = "$(inherited)";
OTHER_SWIFT_FLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = MessagePack;
PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)";
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
SWIFT_VERSION = 4.0;
TARGET_NAME = MessagePack;
};
name = Release;
};
OBJ_4 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DYLIB_INSTALL_NAME_BASE = "@rpath";
GCC_OPTIMIZATION_LEVEL = s;
MACOSX_DEPLOYMENT_TARGET = 10.10;
OTHER_SWIFT_FLAGS = "-DXcode";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
USE_HEADERMAP = NO;
};
name = Release;
};
OBJ_52 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
LD = /usr/bin/true;
OTHER_SWIFT_FLAGS = "-swift-version 4 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk";
SWIFT_VERSION = 4.0;
};
name = Debug;
};
OBJ_53 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
LD = /usr/bin/true;
OTHER_SWIFT_FLAGS = "-swift-version 4 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk";
SWIFT_VERSION = 4.0;
};
name = Release;
};
OBJ_58 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Debug;
};
OBJ_59 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Release;
};
OBJ_63 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
);
HEADER_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = MessagePack.xcodeproj/MessagePackTests_Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks";
OTHER_CFLAGS = "$(inherited)";
OTHER_LDFLAGS = "$(inherited)";
OTHER_SWIFT_FLAGS = "$(inherited)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
SWIFT_VERSION = 4.0;
TARGET_NAME = MessagePackTests;
};
name = Debug;
};
OBJ_64 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
);
HEADER_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = MessagePack.xcodeproj/MessagePackTests_Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks";
OTHER_CFLAGS = "$(inherited)";
OTHER_LDFLAGS = "$(inherited)";
OTHER_SWIFT_FLAGS = "$(inherited)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
SWIFT_VERSION = 4.0;
TARGET_NAME = MessagePackTests;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
OBJ_2 /* Build configuration list for PBXProject "MessagePack" */ = {
isa = XCConfigurationList;
buildConfigurations = (
OBJ_3 /* Debug */,
OBJ_4 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
OBJ_34 /* Build configuration list for PBXNativeTarget "MessagePack" */ = {
isa = XCConfigurationList;
buildConfigurations = (
OBJ_35 /* Debug */,
OBJ_36 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
OBJ_51 /* Build configuration list for PBXNativeTarget "MessagePackPackageDescription" */ = {
isa = XCConfigurationList;
buildConfigurations = (
OBJ_52 /* Debug */,
OBJ_53 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
OBJ_57 /* Build configuration list for PBXAggregateTarget "MessagePackPackageTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
OBJ_58 /* Debug */,
OBJ_59 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
OBJ_62 /* Build configuration list for PBXNativeTarget "MessagePackTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
OBJ_63 /* Debug */,
OBJ_64 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = OBJ_1 /* Project object */;
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
<false/>
</dict>
</plist>

View File

@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "MessagePack::MessagePack"
BuildableName = "MessagePack.framework"
BlueprintName = "MessagePack"
ReferencedContainer = "container:MessagePack.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "MessagePack::MessagePackTests"
BuildableName = "MessagePackTests.xctest"
BlueprintName = "MessagePackTests"
ReferencedContainer = "container:MessagePack.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "MessagePack::MessagePack"
BuildableName = "MessagePack.framework"
BlueprintName = "MessagePack"
ReferencedContainer = "container:MessagePack.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "MessagePack::MessagePack"
BuildableName = "MessagePack.framework"
BlueprintName = "MessagePack"
ReferencedContainer = "container:MessagePack.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "MessagePack::MessagePack"
BuildableName = "MessagePack.framework"
BlueprintName = "MessagePack"
ReferencedContainer = "container:MessagePack.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:MessagePack.playground">
</FileRef>
<FileRef
location = "group:MessagePack.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
<false/>
</dict>
</plist>

View File

@@ -0,0 +1,28 @@
// swift-tools-version:4.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "MessagePack",
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "MessagePack",
targets: ["MessagePack"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "MessagePack",
dependencies: []),
.testTarget(
name: "MessagePackTests",
dependencies: ["MessagePack"]),
]
)

87
lib/MessagePack/README.md Normal file
View File

@@ -0,0 +1,87 @@
# MessagePack
[![Build Status][build status badge]][build status]
A [MessagePack](https://msgpack.org/) encoder and decoder for `Codable` types.
This functionality is discussed in Chapter 7 of
[Flight School Guide to Swift Codable](https://flight.school/books/codable).
## Requirements
- Swift 4.2+
## Usage
### Encoding Messages
```swift
import MessagePack
let encoder = MessagePackEncoder()
let value = try! encoder.encode(["a": 1, "b": 2, "c": 3])
// [0x83, 0xA1, 0x62, 0x02, 0xA1, 0x61, 0x01, 0xA1, 0x63, 0x03]
```
### Decoding Messages
```swift
import MessagePack
let decoder = MessagePackDecoder()
let data = Data(bytes: [0xCB, 0x40, 0x09, 0x21, 0xF9, 0xF0, 0x1B, 0x86, 0x6E])
let value = try! decoder.decode(Double.self, from: data)
// 3.14159
```
## Installation
### Swift Package Manager
Add the MessagePack package to your target dependencies in `Package.swift`:
```swift
import PackageDescription
let package = Package(
name: "YourProject",
dependencies: [
.package(
url: "https://github.com/Flight-School/MessagePack",
from: "1.2.3"
),
]
)
```
Then run the `swift build` command to build your project.
### CocoaPods
You can install `MessagePack` via CocoaPods,
by adding the following line to your `Podfile`:
```ruby
pod 'MessagePack-FlightSchool', '~> 1.2.4'
```
Run the `pod install` command to download the library
and integrate it into your Xcode project.
> **Note**
> The module name for this library is "MessagePack" ---
> that is, to use it, you add `import MessagePack` to the top of your Swift code
> just as you would by any other installation method.
> The pod is called "MessagePack-FlightSchool"
> because there's an existing pod with the name "MessagePack".
## License
MIT
## Contact
Mattt ([@mattt](https://twitter.com/mattt))
[build status]: https://github.com/Flight-School/MessagePack/actions?query=workflow%3ACI
[build status badge]: https://github.com/Flight-School/MessagePack/workflows/CI/badge.svg

View File

@@ -0,0 +1,28 @@
struct AnyCodingKey: CodingKey, Equatable {
var stringValue: String
var intValue: Int?
init?(stringValue: String) {
self.stringValue = stringValue
self.intValue = nil
}
init?(intValue: Int) {
self.stringValue = "\(intValue)"
self.intValue = intValue
}
init<Key>(_ base: Key) where Key : CodingKey {
if let intValue = base.intValue {
self.init(intValue: intValue)!
} else {
self.init(stringValue: base.stringValue)!
}
}
}
extension AnyCodingKey: Hashable {
var hashValue: Int {
return self.intValue?.hashValue ?? self.stringValue.hashValue
}
}

View File

@@ -0,0 +1,44 @@
import Foundation
struct Box<Value> {
let value: Value
init(_ value: Value) {
self.value = value
}
}
extension Box: Encodable where Value: Encodable {
func encode(to encoder: Encoder) throws {
try self.value.encode(to: encoder)
}
}
extension Box: Decodable where Value: Decodable {
init(from decoder: Decoder) throws {
self.init(try Value(from: decoder))
}
}
extension Box where Value == Data {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self.init(try container.decode(Value.self))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self.value)
}
}
extension Box where Value == Date {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self.init(try container.decode(Value.self))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self.value)
}
}

View File

@@ -0,0 +1,60 @@
import Foundation
public struct DataSpec {
let name: String
let isObj: Bool
let isArray: Bool
let dataSpecBuilder: DataSpecBuilder?
init(_ name: String, _ isObj: Bool, _ isArray: Bool, _ dataSpecBuilder: DataSpecBuilder?) {
self.name = name
self.isObj = isObj
self.isArray = isArray
self.dataSpecBuilder = dataSpecBuilder
}
}
public class DataSpecBuilder : NSCopying {
var specs: [DataSpec] = []
var specsIterator: IndexingIterator<[DataSpec]>
init() {
specsIterator = IndexingIterator(_elements: [])
}
func append(_ name: String) -> DataSpecBuilder {
return append(DataSpec(name, false, false, nil))
}
func appendObj(_ name: String, _ dataSpecBuilder: DataSpecBuilder) -> DataSpecBuilder {
return append(DataSpec(name, true, false, dataSpecBuilder))
}
func appendArray(_ name: String) -> DataSpecBuilder {
return append(DataSpec(name, false, true, nil))
}
func appendArray(_ name: String, _ dataSpecBuilder: DataSpecBuilder) -> DataSpecBuilder {
return append(DataSpec(name, false, true, dataSpecBuilder))
}
func append(_ spec: DataSpec) -> DataSpecBuilder {
specs.append(spec)
return self
}
func build() -> DataSpecBuilder {
specsIterator = specs.makeIterator()
return self
}
func next() -> DataSpec {
return specsIterator.next()!
}
public func copy(with zone: NSZone? = nil) -> Any {
let b = DataSpecBuilder()
b.specs = specs
return b.build()
}
}

View File

@@ -0,0 +1,178 @@
import Foundation
extension _MessagePackDecoder {
final class KeyedContainer<Key> where Key: CodingKey {
lazy var nestedContainers: [String: MessagePackDecodingContainer] = {
guard let count = self.count else {
return [:]
}
var nestedContainers: [String: MessagePackDecodingContainer] = [:]
let unkeyedContainer = UnkeyedContainer(data: self.data.suffix(from: self.index), codingPath: self.codingPath, userInfo: self.userInfo)
if currentSpec != nil && currentSpec!.isObj {
unkeyedContainer.count = count
} else {
unkeyedContainer.count = count * 2
}
do {
var iterator = unkeyedContainer.nestedContainers.makeIterator()
for _ in 0..<count {
var key: String = ""
if currentSpec == nil || !currentSpec!.isObj {
guard let keyContainer = iterator.next() as? _MessagePackDecoder.SingleValueContainer else {
fatalError() // FIXME
}
key = try keyContainer.decode(String.self)
}
guard let container = iterator.next() else {
fatalError() // FIXME
}
if currentSpec != nil && currentSpec!.isObj {
key = container.currentSpec!.name
}
container.codingPath += [AnyCodingKey(stringValue: key)!]
nestedContainers[key] = container
}
} catch {
fatalError("\(error)") // FIXME
}
self.index = unkeyedContainer.index
return nestedContainers
}()
lazy var count: Int? = {
do {
let format = try self.readByte()
if currentSpec != nil && currentSpec!.isObj && 0x90...0x9f ~= format {
return Int(format & 0x0F)
}
switch format {
case 0x80...0x8f:
return Int(format & 0x0F)
case 0xde:
return Int(try read(UInt16.self))
case 0xdf:
return Int(try read(UInt32.self))
default:
return nil
}
} catch {
return nil
}
}()
var data: Data
var index: Data.Index
var codingPath: [CodingKey]
var userInfo: [CodingUserInfoKey: Any]
var currentSpec: DataSpec?
func nestedCodingPath(forKey key: CodingKey) -> [CodingKey] {
return self.codingPath + [key]
}
init(data: Data, codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) {
self.codingPath = codingPath
self.userInfo = userInfo
self.data = data
self.index = self.data.startIndex
}
func checkCanDecodeValue(forKey key: Key) throws {
guard self.contains(key) else {
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "key not found: \(key)")
throw DecodingError.keyNotFound(key, context)
}
}
}
}
extension _MessagePackDecoder.KeyedContainer: KeyedDecodingContainerProtocol {
var allKeys: [Key] {
return self.nestedContainers.keys.map{ Key(stringValue: $0)! }
}
func contains(_ key: Key) -> Bool {
return self.nestedContainers.keys.contains(key.stringValue)
}
func decodeNil(forKey key: Key) throws -> Bool {
try checkCanDecodeValue(forKey: key)
let nestedContainer = self.nestedContainers[key.stringValue]
switch nestedContainer {
case let singleValueContainer as _MessagePackDecoder.SingleValueContainer:
return singleValueContainer.decodeNil()
case is _MessagePackDecoder.UnkeyedContainer,
is _MessagePackDecoder.KeyedContainer<AnyCodingKey>:
return false
default:
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "cannot decode nil for key: \(key)")
throw DecodingError.typeMismatch(Any?.self, context)
}
}
func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T : Decodable {
try checkCanDecodeValue(forKey: key)
let container = self.nestedContainers[key.stringValue]!
let decoder = MessagePackDecoder()
if userInfo.keys.contains(MessagePackDecoder.dataSpecKey) {
decoder.userInfo[MessagePackDecoder.dataSpecKey] = container.currentSpec!.dataSpecBuilder?.copy() as? DataSpecBuilder
if container.currentSpec!.isArray {
decoder.userInfo[MessagePackDecoder.isArrayDataSpecKey] = true
}
}
let value = try decoder.decode(T.self, from: container.data)
return value
}
func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
try checkCanDecodeValue(forKey: key)
guard let unkeyedContainer = self.nestedContainers[key.stringValue] as? _MessagePackDecoder.UnkeyedContainer else {
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "cannot decode nested container for key: \(key)")
}
return unkeyedContainer
}
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
try checkCanDecodeValue(forKey: key)
guard let keyedContainer = self.nestedContainers[key.stringValue] as? _MessagePackDecoder.KeyedContainer<NestedKey> else {
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "cannot decode nested container for key: \(key)")
}
return KeyedDecodingContainer(keyedContainer)
}
func superDecoder() throws -> Decoder {
return _MessagePackDecoder(data: self.data)
}
func superDecoder(forKey key: Key) throws -> Decoder {
let decoder = _MessagePackDecoder(data: self.data)
decoder.codingPath = [key]
return decoder
}
}
extension _MessagePackDecoder.KeyedContainer: MessagePackDecodingContainer {}

View File

@@ -0,0 +1,168 @@
import Foundation
/**
An object that decodes instances of a data type from MessagePack objects.
*/
final public class MessagePackDecoder {
public init() {}
/**
A dictionary you use to customize the decoding process
by providing contextual information.
*/
public var userInfo: [CodingUserInfoKey : Any] = [:]
/**
Returns a value of the type you specify,
decoded from a MessagePack object.
- Parameters:
- type: The type of the value to decode
from the supplied MessagePack object.
- data: The MessagePack object to decode.
- Throws: `DecodingError.dataCorrupted(_:)`
if the data is not valid MessagePack.
*/
public func decode<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable {
let decoder = _MessagePackDecoder(data: data)
decoder.userInfo = self.userInfo
decoder.userInfo[MessagePackDecoder.nonMatchingFloatDecodingStrategyKey] = nonMatchingFloatDecodingStrategy
switch type {
case is Data.Type:
let box = try Box<Data>(from: decoder)
return box.value as! T
case is Date.Type:
let box = try Box<Date>(from: decoder)
return box.value as! T
default:
return try T(from: decoder)
}
}
/**
The strategy used by a decoder when it encounters format mismatches for floating point values.
*/
public var nonMatchingFloatDecodingStrategy: NonMatchingFloatDecodingStrategy = .strict
/**
The strategies for decoding floating point values when their format doesn't match.
*/
public enum NonMatchingFloatDecodingStrategy {
/// Throws a DecodingError.typeMismatch
case strict
/// Performs a cast
case cast
}
internal static var nonMatchingFloatDecodingStrategyKey: CodingUserInfoKey {
return CodingUserInfoKey(rawValue: "nonMatchingFloatDecodingStrategyKey")!
}
static var dataSpecKey : CodingUserInfoKey {
return CodingUserInfoKey(rawValue: "dataSpecKey")!
}
static var isArrayDataSpecKey : CodingUserInfoKey {
return CodingUserInfoKey(rawValue: "isArrayDataSpecKey")!
}
}
// MARK: - TopLevelDecoder
#if canImport(Combine)
import Combine
extension MessagePackDecoder: TopLevelDecoder {
public typealias Input = Data
}
#endif
// MARK: -
final class _MessagePackDecoder {
var codingPath: [CodingKey] = []
var userInfo: [CodingUserInfoKey : Any] = [:]
var container: MessagePackDecodingContainer?
fileprivate var data: Data
init(data: Data) {
self.data = data
}
}
extension _MessagePackDecoder: Decoder {
fileprivate func assertCanCreateContainer() {
precondition(self.container == nil)
}
func container<Key>(keyedBy type: Key.Type) -> KeyedDecodingContainer<Key> where Key : CodingKey {
assertCanCreateContainer()
let container = KeyedContainer<Key>(data: self.data, codingPath: self.codingPath, userInfo: self.userInfo)
if userInfo.keys.contains(MessagePackDecoder.dataSpecKey) {
container.currentSpec = DataSpec("", true, false, nil)
}
self.container = container
return KeyedDecodingContainer(container)
}
func unkeyedContainer() -> UnkeyedDecodingContainer {
assertCanCreateContainer()
let container = UnkeyedContainer(data: self.data, codingPath: self.codingPath, userInfo: self.userInfo)
self.container = container
return container
}
func singleValueContainer() -> SingleValueDecodingContainer {
assertCanCreateContainer()
let container = SingleValueContainer(data: self.data, codingPath: self.codingPath, userInfo: self.userInfo)
self.container = container
return container
}
}
protocol MessagePackDecodingContainer: class {
var codingPath: [CodingKey] { get set }
var userInfo: [CodingUserInfoKey : Any] { get }
var data: Data { get set }
var index: Data.Index { get set }
var currentSpec: DataSpec? { get set }
}
extension MessagePackDecodingContainer {
func readByte() throws -> UInt8 {
return try read(1).first!
}
func read(_ length: Int) throws -> Data {
let nextIndex = self.index.advanced(by: length)
guard nextIndex <= self.data.endIndex else {
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Unexpected end of data")
throw DecodingError.dataCorrupted(context)
}
defer { self.index = nextIndex }
return self.data.subdata(in: self.index..<nextIndex)
}
func read<T>(_ type: T.Type) throws -> T where T : FixedWidthInteger {
let stride = MemoryLayout<T>.stride
let bytes = [UInt8](try read(stride))
return T(bytes: bytes)
}
}

View File

@@ -0,0 +1,226 @@
import Foundation
#if os(Linux)
let NSEC_PER_SEC: UInt64 = 1000000000
#endif
extension _MessagePackDecoder {
final class SingleValueContainer {
var codingPath: [CodingKey]
var userInfo: [CodingUserInfoKey: Any]
var data: Data
var index: Data.Index
var currentSpec: DataSpec?
init(data: Data, codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) {
self.codingPath = codingPath
self.userInfo = userInfo
self.data = data
self.index = self.data.startIndex
}
func checkCanDecode<T>(_ type: T.Type, format: UInt8) throws {
guard self.index <= self.data.endIndex else {
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Unexpected end of data")
}
guard self.data[self.index] == format else {
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Invalid format: \(format)")
throw DecodingError.typeMismatch(type, context)
}
}
var nonMatchingFloatDecodingStrategy: MessagePackDecoder.NonMatchingFloatDecodingStrategy {
return userInfo[MessagePackDecoder.nonMatchingFloatDecodingStrategyKey] as? MessagePackDecoder.NonMatchingFloatDecodingStrategy ?? .strict
}
}
}
extension _MessagePackDecoder.SingleValueContainer: SingleValueDecodingContainer {
func decodeNil() -> Bool {
let format = try? readByte()
return format == 0xc0
}
func decode(_ type: Bool.Type) throws -> Bool {
let format = try readByte()
switch format {
case 0xc2: return false
case 0xc3: return true
default:
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Invalid format: \(format)")
throw DecodingError.typeMismatch(Bool.self, context)
}
}
func decode(_ type: String.Type) throws -> String {
let length: Int
let format = try readByte()
switch format {
case 0xa0...0xbf:
length = Int(format - 0xa0)
case 0xd9:
length = Int(try read(UInt8.self))
case 0xda:
length = Int(try read(UInt16.self))
case 0xdb:
length = Int(try read(UInt32.self))
default:
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Invalid format for String length: \(format)")
}
let data = try read(length)
guard let string = String(data: data, encoding: .utf8) else {
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Couldn't decode string with UTF-8 encoding")
throw DecodingError.dataCorrupted(context)
}
return string
}
func decode(_ type: Double.Type) throws -> Double {
let format = try readByte()
switch format {
case 0xca:
switch nonMatchingFloatDecodingStrategy {
case .strict:
break
case .cast:
let bitPattern = try read(UInt32.self)
return Double(Float(bitPattern: bitPattern))
}
case 0xcb:
let bitPattern = try read(UInt64.self)
return Double(bitPattern: bitPattern)
default:
break
}
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Invalid format: \(format)")
throw DecodingError.typeMismatch(Double.self, context)
}
func decode(_ type: Float.Type) throws -> Float {
let format = try readByte()
switch format {
case 0xca:
let bitPattern = try read(UInt32.self)
return Float(bitPattern: bitPattern)
case 0xcb:
switch nonMatchingFloatDecodingStrategy {
case .strict:
break
case .cast:
let bitPattern = try read(UInt64.self)
return Float(Double(bitPattern: bitPattern))
}
default:
break
}
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Invalid format: \(format)")
throw DecodingError.typeMismatch(Float.self, context)
}
func decode<T>(_ type: T.Type) throws -> T where T : BinaryInteger & Decodable {
let format = try readByte()
var t: T?
switch format {
case 0x00...0x7f:
t = T(format)
case 0xcc:
t = T(exactly: try read(UInt8.self))
case 0xcd:
t = T(exactly: try read(UInt16.self))
case 0xce:
t = T(exactly: try read(UInt32.self))
case 0xcf:
t = T(exactly: try read(UInt64.self))
case 0xd0:
t = T(exactly: try read(Int8.self))
case 0xd1:
t = T(exactly: try read(Int16.self))
case 0xd2:
t = T(exactly: try read(Int32.self))
case 0xd3:
t = T(exactly: try read(Int64.self))
case 0xe0...0xff:
t = T(exactly: Int8(bitPattern: format))
default:
t = nil
}
guard let value = t else {
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Invalid format: \(format)")
throw DecodingError.typeMismatch(T.self, context)
}
return value
}
func decode(_ type: Date.Type) throws -> Date {
let format = try readByte()
var seconds: TimeInterval
var nanoseconds: TimeInterval
switch format {
case 0xd6:
_ = try read(Int8.self) // -1
nanoseconds = 0
seconds = TimeInterval(try read(UInt32.self))
case 0xd7:
_ = try read(Int8.self) // -1
let bitPattern = try read(UInt64.self)
nanoseconds = TimeInterval(UInt32(bitPattern >> 34))
seconds = TimeInterval(UInt32(bitPattern & 0x03_FF_FF_FF_FF))
case 0xc7:
_ = try read(Int8.self) // 12
_ = try read(Int8.self) // -1
nanoseconds = TimeInterval(try read(UInt32.self))
seconds = TimeInterval(try read(Int64.self))
default:
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Invalid format: \(format)")
throw DecodingError.typeMismatch(Date.self, context)
}
let timeInterval = TimeInterval(seconds) + nanoseconds / Double(NSEC_PER_SEC)
return Date(timeIntervalSince1970: timeInterval)
}
func decode(_ type: Data.Type) throws -> Data {
let length: Int
let format = try readByte()
switch format {
case 0xc4:
length = Int(try read(UInt8.self))
case 0xc5:
length = Int(try read(UInt16.self))
case 0xc6:
length = Int(try read(UInt32.self))
default:
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Invalid format for Data length: \(format)")
}
return self.data.subdata(in: self.index..<self.index.advanced(by: length))
}
func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
switch type {
case is Data.Type:
return try decode(Data.self) as! T
case is Date.Type:
return try decode(Date.self) as! T
default:
let decoder = _MessagePackDecoder(data: self.data)
let value = try T(from: decoder)
if let nextIndex = decoder.container?.index {
self.index = nextIndex
}
return value
}
}
}
extension _MessagePackDecoder.SingleValueContainer: MessagePackDecodingContainer {}

View File

@@ -0,0 +1,235 @@
import Foundation
extension _MessagePackDecoder {
final class UnkeyedContainer {
var codingPath: [CodingKey]
var nestedCodingPath: [CodingKey] {
return self.codingPath + [AnyCodingKey(intValue: self.count ?? 0)!]
}
var userInfo: [CodingUserInfoKey: Any]
var data: Data
var index: Data.Index
var currentSpec: DataSpec?
lazy var count: Int? = {
do {
let format = try self.readByte()
switch format {
case 0x90...0x9f:
return Int(format & 0x0F)
case 0xdc:
return Int(try read(UInt16.self))
case 0xdd:
return Int(try read(UInt32.self))
default:
return nil
}
} catch {
return nil
}
}()
var currentIndex: Int = 0
lazy var nestedContainers: [MessagePackDecodingContainer] = {
guard let count = self.count else {
return []
}
var nestedContainers: [MessagePackDecodingContainer] = []
do {
for _ in 0..<count {
let container = try self.decodeContainer()
nestedContainers.append(container)
}
} catch {
fatalError("\(error)") // FIXME
}
self.currentIndex = 0
return nestedContainers
}()
init(data: Data, codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) {
self.codingPath = codingPath
self.userInfo = userInfo
self.data = data
self.index = self.data.startIndex
}
var isAtEnd: Bool {
guard let count = self.count else {
return true
}
return currentIndex >= count
}
func checkCanDecodeValue() throws {
guard !self.isAtEnd else {
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Unexpected end of data")
}
}
}
}
extension _MessagePackDecoder.UnkeyedContainer: UnkeyedDecodingContainer {
func decodeNil() throws -> Bool {
try checkCanDecodeValue()
defer { self.currentIndex += 1 }
let nestedContainer = self.nestedContainers[self.currentIndex]
switch nestedContainer {
case let singleValueContainer as _MessagePackDecoder.SingleValueContainer:
return singleValueContainer.decodeNil()
case is _MessagePackDecoder.UnkeyedContainer,
is _MessagePackDecoder.KeyedContainer<AnyCodingKey>:
return false
default:
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "cannot decode nil for index: \(self.currentIndex)")
throw DecodingError.typeMismatch(Any?.self, context)
}
}
func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
try checkCanDecodeValue()
defer { self.currentIndex += 1 }
if userInfo.keys.contains(MessagePackDecoder.isArrayDataSpecKey) {
currentSpec = DataSpec("", false, true, (userInfo[MessagePackDecoder.dataSpecKey] as? DataSpecBuilder)?.copy() as? DataSpecBuilder)
}
let container = self.nestedContainers[self.currentIndex]
let decoder = MessagePackDecoder()
if userInfo.keys.contains(MessagePackDecoder.dataSpecKey) {
decoder.userInfo[MessagePackDecoder.dataSpecKey] = (userInfo[MessagePackDecoder.dataSpecKey] as? DataSpecBuilder)?.copy() as? DataSpecBuilder
}
let value = try decoder.decode(T.self, from: container.data)
return value
}
func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
try checkCanDecodeValue()
defer { self.currentIndex += 1 }
let container = self.nestedContainers[self.currentIndex] as! _MessagePackDecoder.UnkeyedContainer
return container
}
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
try checkCanDecodeValue()
defer { self.currentIndex += 1 }
let container = self.nestedContainers[self.currentIndex] as! _MessagePackDecoder.KeyedContainer<NestedKey>
return KeyedDecodingContainer(container)
}
func superDecoder() throws -> Decoder {
return _MessagePackDecoder(data: self.data)
}
}
extension _MessagePackDecoder.UnkeyedContainer {
func decodeContainer() throws -> MessagePackDecodingContainer {
try checkCanDecodeValue()
defer { self.currentIndex += 1 }
let startIndex = self.index
var currDataSpec: DataSpec? = nil
if currentSpec != nil && currentSpec!.isArray && currentSpec!.dataSpecBuilder != nil {
currDataSpec = DataSpec("", true, false, currentSpec!.dataSpecBuilder!.copy() as? DataSpecBuilder)
} else {
let dataSpec = self.userInfo[MessagePackDecoder.dataSpecKey] as? DataSpecBuilder
if let currDS = dataSpec?.next() {
currDataSpec = DataSpec(currDS.name, currDS.isObj, currDS.isArray, currDS.dataSpecBuilder?.copy() as? DataSpecBuilder)
}
}
let length: Int
let format = try self.readByte()
switch format {
case 0x00...0x7f,
0xc0, 0xc2, 0xc3,
0xe0...0xff:
length = 0
case 0xcc, 0xd0, 0xd4:
length = 1
case 0xcd, 0xd1, 0xd5:
length = 2
case 0xca, 0xce, 0xd2:
length = 4
case 0xcb, 0xcf, 0xd3:
length = 8
case 0xd6:
length = 5
case 0xd7:
length = 9
case 0xd8:
length = 16
case 0xa0...0xbf:
length = Int(format - 0xa0)
case 0xc4, 0xc7, 0xd9:
length = Int(try read(UInt8.self))
case 0xc5, 0xc8, 0xda:
length = Int(try read(UInt16.self))
case 0xc6, 0xc9, 0xdb:
length = Int(try read(UInt32.self))
case 0x80...0x8f, 0xde, 0xdf:
let container = _MessagePackDecoder.KeyedContainer<AnyCodingKey>(data: self.data.suffix(from: startIndex), codingPath: self.nestedCodingPath, userInfo: self.userInfo)
container.currentSpec = currDataSpec
_ = container.nestedContainers // FIXME
self.index = container.index
return container
case 0x90...0x9f, 0xdc, 0xdd:
if currDataSpec != nil && currDataSpec!.isObj {
var objUserInfo = self.userInfo
objUserInfo[MessagePackDecoder.dataSpecKey] = currDataSpec!.dataSpecBuilder!
let container = _MessagePackDecoder.KeyedContainer<AnyCodingKey>(data: self.data.suffix(from: startIndex), codingPath: self.nestedCodingPath, userInfo: objUserInfo)
container.currentSpec = currDataSpec
_ = container.nestedContainers // FIXME
self.index = container.index
return container
}
var arrUserInfo = self.userInfo
if currDataSpec != nil && currDataSpec!.isArray {
arrUserInfo[MessagePackDecoder.dataSpecKey] = currDataSpec!.dataSpecBuilder!
}
let container = _MessagePackDecoder.UnkeyedContainer(data: self.data.suffix(from: startIndex), codingPath: self.nestedCodingPath, userInfo: arrUserInfo)
container.currentSpec = currDataSpec
_ = container.nestedContainers // FIXME
self.index = container.index
return container
default:
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Invalid format: \(format)")
}
let range: Range<Data.Index> = startIndex..<self.index.advanced(by: length)
self.index = range.upperBound
let container = _MessagePackDecoder.SingleValueContainer(data: self.data.subdata(in: range), codingPath: self.codingPath, userInfo: self.userInfo)
container.currentSpec = currDataSpec
return container
}
}
extension _MessagePackDecoder.UnkeyedContainer: MessagePackDecodingContainer {}

View File

@@ -0,0 +1,90 @@
import Foundation
extension _MessagePackEncoder {
final class KeyedContainer<Key> where Key: CodingKey {
private var storage: [AnyCodingKey: _MessagePackEncodingContainer] = [:]
var codingPath: [CodingKey]
var userInfo: [CodingUserInfoKey: Any]
func nestedCodingPath(forKey key: CodingKey) -> [CodingKey] {
return self.codingPath + [key]
}
init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) {
self.codingPath = codingPath
self.userInfo = userInfo
}
}
}
extension _MessagePackEncoder.KeyedContainer: KeyedEncodingContainerProtocol {
func encodeNil(forKey key: Key) throws {
var container = self.nestedSingleValueContainer(forKey: key)
try container.encodeNil()
}
func encode<T>(_ value: T, forKey key: Key) throws where T : Encodable {
var container = self.nestedSingleValueContainer(forKey: key)
try container.encode(value)
}
private func nestedSingleValueContainer(forKey key: Key) -> SingleValueEncodingContainer {
let container = _MessagePackEncoder.SingleValueContainer(codingPath: self.nestedCodingPath(forKey: key), userInfo: self.userInfo)
self.storage[AnyCodingKey(key)] = container
return container
}
func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
let container = _MessagePackEncoder.UnkeyedContainer(codingPath: self.nestedCodingPath(forKey: key), userInfo: self.userInfo)
self.storage[AnyCodingKey(key)] = container
return container
}
func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
let container = _MessagePackEncoder.KeyedContainer<NestedKey>(codingPath: self.nestedCodingPath(forKey: key), userInfo: self.userInfo)
self.storage[AnyCodingKey(key)] = container
return KeyedEncodingContainer(container)
}
func superEncoder() -> Encoder {
fatalError("Unimplemented") // FIXME
}
func superEncoder(forKey key: Key) -> Encoder {
fatalError("Unimplemented") // FIXME
}
}
extension _MessagePackEncoder.KeyedContainer: _MessagePackEncodingContainer {
var data: Data {
var data = Data()
let length = storage.count
if let uint16 = UInt16(exactly: length) {
if length <= 15 {
data.append(0x80 + UInt8(length))
} else {
data.append(0xde)
data.append(contentsOf: uint16.bytes)
}
} else if let uint32 = UInt32(exactly: length) {
data.append(0xdf)
data.append(contentsOf: uint32.bytes)
} else {
fatalError()
}
for (key, container) in self.storage {
let keyContainer = _MessagePackEncoder.SingleValueContainer(codingPath: self.codingPath, userInfo: self.userInfo)
try! keyContainer.encode(key.stringValue)
data.append(keyContainer.data)
data.append(container.data)
}
return data
}
}

View File

@@ -0,0 +1,99 @@
import Foundation
/**
An object that encodes instances of a data type as MessagePack objects.
*/
final public class MessagePackEncoder {
public init() {}
/**
A dictionary you use to customize the encoding process
by providing contextual information.
*/
public var userInfo: [CodingUserInfoKey : Any] = [:]
/**
Returns a MessagePack-encoded representation of the value you supply.
- Parameters:
- value: The value to encode as MessagePack.
- Throws: `EncodingError.invalidValue(_:_:)`
if the value can't be encoded as a MessagePack object.
*/
public func encode<T>(_ value: T) throws -> Data where T : Encodable {
let encoder = _MessagePackEncoder()
encoder.userInfo = self.userInfo
switch value {
case let data as Data:
try Box<Data>(data).encode(to: encoder)
case let date as Date:
try Box<Date>(date).encode(to: encoder)
default:
try value.encode(to: encoder)
}
return encoder.data
}
}
// MARK: - TopLevelEncoder
#if canImport(Combine)
import Combine
extension MessagePackEncoder: TopLevelEncoder {
public typealias Input = Data
}
#endif
// MARK: -
protocol _MessagePackEncodingContainer {
var data: Data { get }
}
class _MessagePackEncoder {
var codingPath: [CodingKey] = []
var userInfo: [CodingUserInfoKey : Any] = [:]
fileprivate var container: _MessagePackEncodingContainer?
var data: Data {
return container?.data ?? Data()
}
}
extension _MessagePackEncoder: Encoder {
fileprivate func assertCanCreateContainer() {
precondition(self.container == nil)
}
func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {
assertCanCreateContainer()
let container = KeyedContainer<Key>(codingPath: self.codingPath, userInfo: self.userInfo)
self.container = container
return KeyedEncodingContainer(container)
}
func unkeyedContainer() -> UnkeyedEncodingContainer {
assertCanCreateContainer()
let container = UnkeyedContainer(codingPath: self.codingPath, userInfo: self.userInfo)
self.container = container
return container
}
func singleValueContainer() -> SingleValueEncodingContainer {
assertCanCreateContainer()
let container = SingleValueContainer(codingPath: self.codingPath, userInfo: self.userInfo)
self.container = container
return container
}
}

View File

@@ -0,0 +1,264 @@
import Foundation
extension _MessagePackEncoder {
final class SingleValueContainer {
private var storage: Data = Data()
fileprivate var canEncodeNewValue = true
fileprivate func checkCanEncode(value: Any?) throws {
guard self.canEncodeNewValue else {
let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Attempt to encode value through single value container when previously value already encoded.")
throw EncodingError.invalidValue(value as Any, context)
}
}
var codingPath: [CodingKey]
var userInfo: [CodingUserInfoKey: Any]
init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) {
self.codingPath = codingPath
self.userInfo = userInfo
}
}
}
extension _MessagePackEncoder.SingleValueContainer: SingleValueEncodingContainer {
func encodeNil() throws {
try checkCanEncode(value: nil)
defer { self.canEncodeNewValue = false }
self.storage.append(0xc0)
}
func encode(_ value: Bool) throws {
try checkCanEncode(value: nil)
defer { self.canEncodeNewValue = false }
switch value {
case false:
self.storage.append(0xc2)
case true:
self.storage.append(0xc3)
}
}
func encode(_ value: String) throws {
try checkCanEncode(value: value)
defer { self.canEncodeNewValue = false }
guard let data = value.data(using: .utf8) else {
let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot encode string using UTF-8 encoding.")
throw EncodingError.invalidValue(value, context)
}
let length = data.count
if let uint8 = UInt8(exactly: length) {
if (uint8 <= 31) {
self.storage.append(0xa0 + uint8)
} else {
self.storage.append(0xd9)
self.storage.append(contentsOf: uint8.bytes)
}
} else if let uint16 = UInt16(exactly: length) {
self.storage.append(0xda)
self.storage.append(contentsOf: uint16.bytes)
} else if let uint32 = UInt32(exactly: length) {
self.storage.append(0xdb)
self.storage.append(contentsOf: uint32.bytes)
} else {
let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot encode string with length \(length).")
throw EncodingError.invalidValue(value, context)
}
self.storage.append(data)
}
func encode(_ value: Double) throws {
try checkCanEncode(value: value)
defer { self.canEncodeNewValue = false }
self.storage.append(0xcb)
self.storage.append(contentsOf: value.bitPattern.bytes)
}
func encode(_ value: Float) throws {
try checkCanEncode(value: value)
defer { self.canEncodeNewValue = false }
self.storage.append(0xca)
self.storage.append(contentsOf: value.bitPattern.bytes)
}
func encode<T>(_ value: T) throws where T : BinaryInteger & Encodable {
try checkCanEncode(value: value)
defer { self.canEncodeNewValue = false }
if value < 0 {
if let int8 = Int8(exactly: value) {
return try encode(int8)
} else if let int16 = Int16(exactly: value) {
return try encode(int16)
} else if let int32 = Int32(exactly: value) {
return try encode(int32)
} else if let int64 = Int64(exactly: value) {
return try encode(int64)
}
} else {
if let uint8 = UInt8(exactly: value) {
return try encode(uint8)
} else if let uint16 = UInt16(exactly: value) {
return try encode(uint16)
} else if let uint32 = UInt32(exactly: value) {
return try encode(uint32)
} else if let uint64 = UInt64(exactly: value) {
return try encode(uint64)
}
}
let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot encode integer \(value).")
throw EncodingError.invalidValue(value, context)
}
func encode(_ value: Int8) throws {
try checkCanEncode(value: value)
defer { self.canEncodeNewValue = false }
if (value >= 0 && value <= 127) {
self.storage.append(UInt8(value))
} else if (value < 0 && value >= -31) {
self.storage.append(0xe0 + (0x1f & UInt8(truncatingIfNeeded: value)))
} else {
self.storage.append(0xd0)
self.storage.append(contentsOf: value.bytes)
}
}
func encode(_ value: Int16) throws {
try checkCanEncode(value: value)
defer { self.canEncodeNewValue = false }
self.storage.append(0xd1)
self.storage.append(contentsOf: value.bytes)
}
func encode(_ value: Int32) throws {
try checkCanEncode(value: value)
defer { self.canEncodeNewValue = false }
self.storage.append(0xd2)
self.storage.append(contentsOf: value.bytes)
}
func encode(_ value: Int64) throws {
try checkCanEncode(value: value)
defer { self.canEncodeNewValue = false }
self.storage.append(0xd3)
self.storage.append(contentsOf: value.bytes)
}
func encode(_ value: UInt8) throws {
try checkCanEncode(value: value)
defer { self.canEncodeNewValue = false }
if (value <= 127) {
self.storage.append(value)
} else {
self.storage.append(0xcc)
self.storage.append(contentsOf: value.bytes)
}
}
func encode(_ value: UInt16) throws {
try checkCanEncode(value: value)
defer { self.canEncodeNewValue = false }
self.storage.append(0xcd)
self.storage.append(contentsOf: value.bytes)
}
func encode(_ value: UInt32) throws {
try checkCanEncode(value: value)
defer { self.canEncodeNewValue = false }
self.storage.append(0xce)
self.storage.append(contentsOf: value.bytes)
}
func encode(_ value: UInt64) throws {
try checkCanEncode(value: value)
defer { self.canEncodeNewValue = false }
self.storage.append(0xcf)
self.storage.append(contentsOf: value.bytes)
}
func encode(_ value: Date) throws {
try checkCanEncode(value: value)
defer { self.canEncodeNewValue = false }
let timeInterval = value.timeIntervalSince1970
let (integral, fractional) = modf(timeInterval)
let seconds = Int64(integral)
let nanoseconds = UInt32(fractional * Double(NSEC_PER_SEC))
if seconds < 0 || seconds > UInt32.max {
self.storage.append(0xc7)
self.storage.append(0x0C)
self.storage.append(0xFF)
self.storage.append(contentsOf: nanoseconds.bytes)
self.storage.append(contentsOf: seconds.bytes)
} else if nanoseconds > 0 {
self.storage.append(0xd7)
self.storage.append(0xFF)
self.storage.append(contentsOf: ((UInt64(nanoseconds) << 34) + UInt64(seconds)).bytes)
} else {
self.storage.append(0xd6)
self.storage.append(0xFF)
self.storage.append(contentsOf: UInt32(seconds).bytes)
}
}
func encode(_ value: Data) throws {
let length = value.count
if let uint8 = UInt8(exactly: length) {
self.storage.append(0xc4)
self.storage.append(uint8)
self.storage.append(value)
} else if let uint16 = UInt16(exactly: length) {
self.storage.append(0xc5)
self.storage.append(contentsOf: uint16.bytes)
self.storage.append(value)
} else if let uint32 = UInt32(exactly: length) {
self.storage.append(0xc6)
self.storage.append(contentsOf: uint32.bytes)
self.storage.append(value)
} else {
let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot encode data of length \(value.count).")
throw EncodingError.invalidValue(value, context)
}
}
func encode<T>(_ value: T) throws where T : Encodable {
try checkCanEncode(value: value)
defer { self.canEncodeNewValue = false }
switch value {
case let data as Data:
try self.encode(data)
case let date as Date:
try self.encode(date)
default:
let encoder = _MessagePackEncoder()
try value.encode(to: encoder)
self.storage.append(encoder.data)
}
}
}
extension _MessagePackEncoder.SingleValueContainer: _MessagePackEncodingContainer {
var data: Data {
return storage
}
}

View File

@@ -0,0 +1,88 @@
import Foundation
extension _MessagePackEncoder {
final class UnkeyedContainer {
private var storage: [_MessagePackEncodingContainer] = []
var count: Int {
return storage.count
}
var codingPath: [CodingKey]
var nestedCodingPath: [CodingKey] {
return self.codingPath + [AnyCodingKey(intValue: self.count)!]
}
var userInfo: [CodingUserInfoKey: Any]
init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) {
self.codingPath = codingPath
self.userInfo = userInfo
}
}
}
extension _MessagePackEncoder.UnkeyedContainer: UnkeyedEncodingContainer {
func encodeNil() throws {
var container = self.nestedSingleValueContainer()
try container.encodeNil()
}
func encode<T>(_ value: T) throws where T : Encodable {
var container = self.nestedSingleValueContainer()
try container.encode(value)
}
private func nestedSingleValueContainer() -> SingleValueEncodingContainer {
let container = _MessagePackEncoder.SingleValueContainer(codingPath: self.nestedCodingPath, userInfo: self.userInfo)
self.storage.append(container)
return container
}
func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
let container = _MessagePackEncoder.KeyedContainer<NestedKey>(codingPath: self.nestedCodingPath, userInfo: self.userInfo)
self.storage.append(container)
return KeyedEncodingContainer(container)
}
func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
let container = _MessagePackEncoder.UnkeyedContainer(codingPath: self.nestedCodingPath, userInfo: self.userInfo)
self.storage.append(container)
return container
}
func superEncoder() -> Encoder {
fatalError("Unimplemented") // FIXME
}
}
extension _MessagePackEncoder.UnkeyedContainer: _MessagePackEncodingContainer {
var data: Data {
var data = Data()
let length = storage.count
if let uint16 = UInt16(exactly: length) {
if uint16 <= 15 {
data.append(UInt8(0x90 + uint16))
} else {
data.append(0xdc)
data.append(contentsOf: uint16.bytes)
}
} else if let uint32 = UInt32(exactly: length) {
data.append(0xdd)
data.append(contentsOf: uint32.bytes)
} else {
fatalError()
}
for container in storage {
data.append(container.data)
}
return data
}
}

View File

@@ -0,0 +1,20 @@
extension FixedWidthInteger {
init(bytes: [UInt8]) {
self = bytes.withUnsafeBufferPointer {
$0.baseAddress!.withMemoryRebound(to: Self.self, capacity: 1) {
$0.pointee
}
}.bigEndian
}
var bytes: [UInt8] {
let capacity = MemoryLayout<Self>.size
var mutableValue = self.bigEndian
return withUnsafePointer(to: &mutableValue) {
return $0.withMemoryRebound(to: UInt8.self, capacity: capacity) {
return Array(UnsafeBufferPointer(start: $0, count: capacity))
}
}
}
}

View File

@@ -0,0 +1,8 @@
import XCTest
@testable import MessagePackTests
XCTMain([
testCase(MessagePackDecodingTests.allTests),
testCase(MessagePackEncodingTests.allTests),
testCase(MessagePackRoundTripTests.allTests),
])

View File

@@ -0,0 +1,63 @@
struct Airport: Codable, Equatable {
let name: String
let iata: String
let icao: String
let coordinates: [Double]
struct Runway: Codable, Equatable {
enum Surface: String, Codable, Equatable {
case rigid, flexible, gravel, sealed, unpaved, other
}
let direction: String
let distance: Int
let surface: Surface
}
let runways: [Runway]
let instrumentApproachProcedures: [String]
static var example: Airport {
return Airport(
name: "Portland International Airport",
iata: "PDX",
icao: "KPDX",
coordinates: [-122.5975,
45.5886111111111],
runways: [
Airport.Runway(
direction: "3/21",
distance: 1829,
surface: .flexible
)
],
instrumentApproachProcedures: [
"HI-ILS OR LOC RWY 28",
"HI-ILS OR LOC/DME RWY 10",
"ILS OR LOC RWY 10L",
"ILS OR LOC RWY 10R",
"ILS OR LOC RWY 28L",
"ILS OR LOC RWY 28R",
"ILS RWY 10R (SA CAT I)",
"ILS RWY 10R (CAT II - III)",
"RNAV (RNP) Y RWY 28L",
"RNAV (RNP) Y RWY 28R",
"RNAV (RNP) Z RWY 10L",
"RNAV (RNP) Z RWY 10R",
"RNAV (RNP) Z RWY 28L",
"RNAV (RNP) Z RWY 28R",
"RNAV (GPS) X RWY 28L",
"RNAV (GPS) X RWY 28R",
"RNAV (GPS) Y RWY 10L",
"RNAV (GPS) Y RWY 10R",
"LOC/DME RWY 21",
"VOR-A",
"HI-TACAN RWY 10",
"TACAN RWY 28",
"COLUMBIA VISUAL RWY 10L/",
"MILL VISUAL RWY 28L/R"
]
)
}
}

View File

@@ -0,0 +1,301 @@
import XCTest
@testable import MessagePack
class MessagePackDecodingTests: XCTestCase {
var decoder: MessagePackDecoder!
override func setUp() {
self.decoder = MessagePackDecoder()
}
func assertTypeMismatch<T>(_ expression: @autoclosure () throws -> T,
_ message: @autoclosure () -> String = "",
file: StaticString = #file,
line: UInt = #line) -> Any.Type? {
var error: Error?
XCTAssertThrowsError(expression, message,
file: file, line: line) {
error = $0
}
guard case .typeMismatch(let type, _) = error as? DecodingError else {
XCTFail(file: file, line: line)
return nil
}
return type
}
func testDecodeNil() {
let data = Data(bytes: [0xC0])
let value = try! decoder.decode(Int?.self, from: data)
XCTAssertNil(value)
}
func testDecodeFalse() {
let data = Data(bytes: [0xc2])
let value = try! decoder.decode(Bool.self, from: data)
XCTAssertEqual(value, false)
}
func testDecodeTrue() {
let data = Data(bytes: [0xc3])
let value = try! decoder.decode(Bool.self, from: data)
XCTAssertEqual(value, true)
}
func testDecodeInt() {
let data = Data(bytes: [0x2A])
let value = try! decoder.decode(Int.self, from: data)
XCTAssertEqual(value, 42)
}
func testDecodeNegativeInt() {
let data = Data(bytes: [0xFF])
let value = try! decoder.decode(Int.self, from: data)
XCTAssertEqual(value, -1)
}
func testDecodeUInt() {
let data = Data(bytes: [0xCC, 0x80])
let value = try! decoder.decode(Int.self, from: data)
XCTAssertEqual(value, 128)
}
func testDecodeFloat() {
let data = Data(bytes: [0xCA, 0x40, 0x48, 0xF5, 0xC3])
let value = try! decoder.decode(Float.self, from: data)
XCTAssertEqual(value, 3.14)
}
func testDecodeFloatToDouble() {
let data = Data(bytes: [0xCA, 0x40, 0x48, 0xF5, 0xC3])
let type = assertTypeMismatch(try decoder.decode(Double.self, from: data))
XCTAssertTrue(type is Double.Type)
decoder.nonMatchingFloatDecodingStrategy = .cast
let value = try! decoder.decode(Double.self, from: data)
XCTAssertEqual(value, 3.14, accuracy: 1e-6)
}
func testDecodeDouble() {
let data = Data(bytes: [0xCB, 0x40, 0x09, 0x21, 0xF9, 0xF0, 0x1B, 0x86, 0x6E])
let value = try! decoder.decode(Double.self, from: data)
XCTAssertEqual(value, 3.14159)
}
func testDecodeDoubleToFloat() {
let data = Data(bytes: [0xCB, 0x40, 0x09, 0x21, 0xF9, 0xF0, 0x1B, 0x86, 0x6E])
let type = assertTypeMismatch(try decoder.decode(Float.self, from: data))
XCTAssertTrue(type is Float.Type)
decoder.nonMatchingFloatDecodingStrategy = .cast
let value = try! decoder.decode(Float.self, from: data)
XCTAssertEqual(value, 3.14159)
}
func testDecodeFixedArray() {
let data = Data(bytes: [0x93, 0x01, 0x02, 0x03])
let value = try! decoder.decode([Int].self, from: data)
XCTAssertEqual(value, [1, 2, 3])
}
func testDecodeVariableArray() {
let data = Data(bytes: [0xdc] + [0x00, 0x10] + Array(0x01...0x10))
let value = try! decoder.decode([Int].self, from: data)
XCTAssertEqual(value, Array(1...16))
}
func testDecodeFixedDictionary() {
let data = Data(bytes: [0x83, 0xA1, 0x62, 0x02, 0xA1, 0x61, 0x01, 0xA1, 0x63, 0x03])
let value = try! decoder.decode([String: Int].self, from: data)
XCTAssertEqual(value, ["a": 1, "b": 2, "c": 3])
}
func testDecodeData() {
let data = Data(bytes: [0xC4, 0x05, 0x68, 0x65, 0x6C, 0x6C, 0x6F])
let value = try! decoder.decode(Data.self, from: data)
XCTAssertEqual(value, "hello".data(using: .utf8))
}
func testDecodeDate() {
let data = Data(bytes: [0xD6, 0xFF, 0x00, 0x00, 0x00, 0x01])
let date = Date(timeIntervalSince1970: 1)
let value = try! decoder.decode(Date.self, from: data)
XCTAssertEqual(value, date)
}
func testDecodeDistantPast() {
let data = Data(bytes: [0xC7, 0x0C, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xF1, 0x88, 0x6B, 0x66, 0x00])
let date = Date.distantPast
let value = try! decoder.decode(Date.self, from: data)
XCTAssertEqual(value, date)
}
func testDecodeDistantFuture() {
let data = Data(bytes: [0xC7, 0x0C, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xEC, 0x31, 0x88, 0x00])
let date = Date.distantFuture
let value = try! decoder.decode(Date.self, from: data)
XCTAssertEqual(value, date)
}
func testDecodeArrayWithDate() {
let data = Data(bytes: [0x91, 0xD6, 0xFF, 0x00, 0x00, 0x00, 0x01])
let date = Date(timeIntervalSince1970: 1)
let value = try! decoder.decode([Date].self, from: data)
XCTAssertEqual(value, [date])
}
func testDecodeDictionaryWithDate() {
let data = Data(bytes: [0x81, 0xA1, 0x31, 0xD6, 0xFF, 0x00, 0x00, 0x00, 0x01])
let date = Date(timeIntervalSince1970: 1)
let value = try! decoder.decode([String: Date].self, from: data)
XCTAssertEqual(value, ["1": date])
}
func testDecodeBv() {
let b64 = "lK50ZXN0aW5nIHN0cmluZyeSpnF3ZXF3ZagxMjNpY29uc5OU2SRlMTZkYTYwMi0zMjE1LTRiZDYtYjY5MC00Y2Q4NmEwZmU3NjSoQ2lwaGVyIDEBk61jaXBodXNlcm5hbWUxrWFkZmFmZHcyMzQxMzGSkblodHRwczovL3d3dy5nb29nbGUuY29tLmFykbVodHRwczovL3d3dy5hcHBsZS5jb22U2SRhNjExMWU2Ny1hMTMwLTRiM2ItODM5NS0xZjIzMDFjNjk3ZjeoQ2lwaGVyIDIBk6g0MzEzMjEzMatqbGpsbHl1bHVpecCU2SRiOGIwODM3MC0xNGU0LTQzZmUtYjBkOS04ZjJlMDlmODJkYzWoQ2lwaGVyIDMBk6twaW9waW9waXBpb6x6eGN6eHZ6eHZ4enaSkbdodHRwczovL3d3dy52aXNhLmNvbS5hcpG1aHR0cHM6Ly93d3cuZG9ja3MuY29t" // array mode with envData and ciphers
// let b64 = "hKFirnRlc3Rpbmcgc3RyaW5noWMnp2VudkRhdGGCpGJhc2WmcXdlcXdlpWljb25zqDEyM2ljb25zp2NpcGhlcnOThKJpZNkkMDA4YmE0NDctZjU0Mi00OWVjLWJjYTktMDMzZTQ2OTU0YTBipG5hbWWoQ2lwaGVyIDGkdHlwZQGlbG9naW6DqHVzZXJuYW1lrWNpcGh1c2VybmFtZTGkdG90cK1hZGZhZmR3MjM0MTMxpHVyaXOSgaN1cmm5aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS5hcoGjdXJptWh0dHBzOi8vd3d3LmFwcGxlLmNvbYSiaWTZJDQ1ZTBhODJiLTgyZGQtNDJiZi05ODhhLTAyYTkyNGM4Yzg5M6RuYW1lqENpcGhlciAypHR5cGUBpWxvZ2lug6h1c2VybmFtZag0MzEzMjEzMaR0b3Rwq2psamxseXVsdWl5pHVyaXPAhKJpZNkkZTBjZWU5NDEtZDI1Ni00MjdiLWJkNWUtNDMxMmMwN2U1NDI5pG5hbWWoQ2lwaGVyIDOkdHlwZQGlbG9naW6DqHVzZXJuYW1lq3Bpb3Bpb3BpcGlvpHRvdHCsenhjenh2enh2eHp2pHVyaXOSgaN1cmm3aHR0cHM6Ly93d3cudmlzYS5jb20uYXKBo3VyabVodHRwczovL3d3dy5kb2Nrcy5jb20=" // dict mode with envData and ciphers
do {
if let d = Data(base64Encoded: b64) {
let decoder = MessagePackDecoder()
decoder.userInfo[MessagePackDecoder.dataSpecKey] = DataSpecBuilder()
.append("b")
.append("c")
.appendObj("envData", DataSpecBuilder()
.append("base")
.append("icons")
.build())
.appendArray("ciphers", DataSpecBuilder()
.append("id")
.append("name")
.append("type")
.appendObj("login", DataSpecBuilder()
.append("username")
.append("totp")
.appendArray("uris", DataSpecBuilder()
.append("uri")
.build())
.build())
.build())
.build()
let codTest = try decoder.decode(CodableTest.self, from: d)
XCTAssertEqual(codTest.b, "testing string")
XCTAssertEqual(codTest.envData.base, "qweqwe")
XCTAssertEqual(codTest.envData.icons, "123icons")
XCTAssertTrue(codTest.ciphers!.count > 1)
} else {
XCTAssertEqual(1, 0)
}
} catch let error {
XCTFail("E: \(error)")
}
}
static var allTests = [
("testDecodeNil", testDecodeNil),
("testDecodeFalse", testDecodeFalse),
("testDecodeTrue", testDecodeTrue),
("testDecodeInt", testDecodeInt),
("testDecodeUInt", testDecodeUInt),
("testDecodeFloat", testDecodeFloat),
("testDecodeFloatToDouble", testDecodeFloatToDouble),
("testDecodeDouble", testDecodeDouble),
("testDecodeDoubleToFloat", testDecodeDoubleToFloat),
("testDecodeFixedArray", testDecodeFixedArray),
("testDecodeFixedDictionary", testDecodeFixedDictionary),
("testDecodeData", testDecodeData),
("testDecodeDistantPast", testDecodeDistantPast),
("testDecodeDistantFuture", testDecodeDistantFuture),
("testDecodeArrayWithDate", testDecodeArrayWithDate),
("testDecodeDictionaryWithDate", testDecodeDictionaryWithDate),
("testDecodeBv", testDecodeBv)
]
}
struct CodableTest : Codable {
enum CodingKeys: Int, CodingKey {
case b
case c
case envData
case ciphers
}
var b: String
var c: Int
var envData: EnvironmentUrlDataDto
var ciphers: [Cipher]?
func printt() {
print("B: \(b)")
print("C: \(c)")
print("ENVDATA")
envData.printt()
if let cs = ciphers {
print("CIPHERS")
for c in cs {
c.printt()
print("----------------------------")
}
}
print("###########################")
}
}
struct EnvironmentUrlDataDto : Codable {
var base: String?
var icons: String?
func printt() {
print("Base: \(base ?? "")")
print("Icons: \(icons ?? "")")
}
}
struct Cipher:Identifiable,Codable{
enum CodingKeys: Int, CodingKey {
case id
case name
case login
}
var id:String
var name:String?
var userId:String?
var login:Login
func printt() {
print("id: \(id)")
print("name: \(name ?? "")")
print("LOGIN")
login.printt()
}
}
struct Login:Codable{
var username:String?
var totp:String?
var uris:[LoginUri]?
func printt() {
print("username: \(username ?? "")")
print("totp: \(totp ?? "")")
print("URIS")
if let us = uris {
for u in us {
u.printt()
print("----------------------------")
}
}
}
}
struct LoginUri:Codable{
var uri:String?
func printt() {
print("Uri: \(uri ?? "")")
}
}

View File

@@ -0,0 +1,127 @@
import XCTest
@testable import MessagePack
class MessagePackEncodingTests: XCTestCase {
var encoder: MessagePackEncoder!
override func setUp() {
self.encoder = MessagePackEncoder()
}
func testEncodeNil() {
let value = try! encoder.encode(nil as Int?)
XCTAssertEqual(value, Data(bytes: [0xc0]))
}
func testEncodeFalse() {
let value = try! encoder.encode(false)
XCTAssertEqual(value, Data(bytes: [0xc2]))
}
func testEncodeTrue() {
let value = try! encoder.encode(true)
XCTAssertEqual(value, Data(bytes: [0xc3]))
}
func testEncodeInt() {
let value = try! encoder.encode(42 as Int)
XCTAssertEqual(value, Data(bytes: [0x2A]))
}
func testEncodeUInt() {
let value = try! encoder.encode(128 as UInt)
XCTAssertEqual(value, Data(bytes: [0xCC, 0x80]))
}
func testEncodeFloat() {
let value = try! encoder.encode(3.14 as Float)
XCTAssertEqual(value, Data(bytes: [0xCA, 0x40, 0x48, 0xF5, 0xC3]))
}
func testEncodeDouble() {
let value = try! encoder.encode(3.14159 as Double)
XCTAssertEqual(value, Data(bytes: [0xCB, 0x40, 0x09, 0x21, 0xF9, 0xF0, 0x1B, 0x86, 0x6E]))
}
func testEncodeString() {
let value = try! encoder.encode("hello")
XCTAssertEqual(value, Data(bytes: [0xA5, 0x68, 0x65, 0x6C, 0x6C, 0x6F]))
}
func testEncodeFixedArray() {
let value = try! encoder.encode([1, 2, 3])
XCTAssertEqual(value, Data(bytes: [0x93, 0x01, 0x02, 0x03]))
}
func testEncodeVariableArray() {
let value = try! encoder.encode(Array(1...16))
XCTAssertEqual(value, Data(bytes: [0xdc] + [0x00, 0x10] + Array(0x01...0x10)))
}
func testEncodeFixedDictionary() {
let value = try! encoder.encode(["a": 1])
XCTAssertEqual(value, Data(bytes: [0x81, 0xA1, 0x61, 0x01]))
}
func testEncodeVariableDictionary() {
let letters = "abcdefghijklmnopqrstuvwxyz".unicodeScalars
let dictionary = Dictionary(uniqueKeysWithValues: zip(letters.map { String($0) }, 1...26))
let value = try! encoder.encode(dictionary)
XCTAssertEqual(value.count, 81)
XCTAssert(value.starts(with: [0xde] + [0x00, 0x1A]))
}
func testEncodeData() {
let data = "hello".data(using: .utf8)
let value = try! encoder.encode(data)
XCTAssertEqual(value, Data(bytes: [0xC4, 0x05, 0x68, 0x65, 0x6C, 0x6C, 0x6F]))
}
func testEncodeDate() {
let date = Date(timeIntervalSince1970: 1)
let value = try! encoder.encode(date)
XCTAssertEqual(value, Data(bytes: [0xD6, 0xFF, 0x00, 0x00, 0x00, 0x01]))
}
func testEncodeDistantPast() {
let date = Date.distantPast
let value = try! encoder.encode(date)
XCTAssertEqual(value, Data(bytes: [0xC7, 0x0C, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xF1, 0x88, 0x6B, 0x66, 0x00]))
}
func testEncodeDistantFuture() {
let date = Date.distantFuture
let value = try! encoder.encode(date)
XCTAssertEqual(value, Data(bytes: [0xC7, 0x0C, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xEC, 0x31, 0x88, 0x00]))
}
func testEncodeArrayWithDate() {
let date = Date(timeIntervalSince1970: 1)
let value = try! encoder.encode([date])
XCTAssertEqual(value, Data(bytes: [0x91, 0xD6, 0xFF, 0x00, 0x00, 0x00, 0x01]))
}
func testEncodeDictionaryWithDate() {
let date = Date(timeIntervalSince1970: 1)
let value = try! encoder.encode(["1": date])
XCTAssertEqual(value, Data(bytes: [0x81, 0xA1, 0x31, 0xD6, 0xFF, 0x00, 0x00, 0x00, 0x01]))
}
static var allTests = [
("testEncodeFalse", testEncodeFalse),
("testEncodeTrue", testEncodeTrue),
("testEncodeInt", testEncodeInt),
("testEncodeUInt", testEncodeUInt),
("testEncodeFloat", testEncodeFloat),
("testEncodeDouble", testEncodeDouble),
("testEncodeFixedArray", testEncodeFixedArray),
("testEncodeVariableArray", testEncodeVariableArray),
("testEncodeFixedDictionary", testEncodeFixedDictionary),
("testEncodeVariableDictionary", testEncodeVariableDictionary),
("testEncodeDate", testEncodeDate),
("testEncodeDistantPast", testEncodeDistantPast),
("testEncodeDistantFuture", testEncodeDistantFuture),
("testEncodeArrayWithDate", testEncodeArrayWithDate),
("testEncodeDictionaryWithDate", testEncodeDictionaryWithDate)
]
}

View File

@@ -0,0 +1,23 @@
import XCTest
@testable import MessagePack
class MessagePackPerformanceTests: XCTestCase {
var encoder: MessagePackEncoder!
var decoder: MessagePackDecoder!
override func setUp() {
self.encoder = MessagePackEncoder()
self.decoder = MessagePackDecoder()
}
func testPerformance() {
let count = 100
let values = [Airport](repeating: .example, count: count)
self.measure {
let encoded = try! encoder.encode(values)
let decoded = try! decoder.decode([Airport].self, from: encoded)
XCTAssertEqual(decoded.count, count)
}
}
}

View File

@@ -0,0 +1,106 @@
import XCTest
@testable import MessagePack
class MessagePackRoundTripTests: XCTestCase {
var encoder: MessagePackEncoder!
var decoder: MessagePackDecoder!
override func setUp() {
self.encoder = MessagePackEncoder()
self.decoder = MessagePackDecoder()
}
func testRoundTripAirport() {
let value = Airport.example
let encoded = try! encoder.encode(value)
let decoded = try! decoder.decode(Airport.self, from: encoded)
XCTAssertEqual(value.name, decoded.name)
XCTAssertEqual(value.iata, decoded.iata)
XCTAssertEqual(value.icao, decoded.icao)
XCTAssertEqual(value.coordinates[0], decoded.coordinates[0], accuracy: 0.01)
XCTAssertEqual(value.coordinates[1], decoded.coordinates[1], accuracy: 0.01)
XCTAssertEqual(value.runways[0].direction, decoded.runways[0].direction)
XCTAssertEqual(value.runways[0].distance, decoded.runways[0].distance)
XCTAssertEqual(value.runways[0].surface, decoded.runways[0].surface)
}
func testRoundTripParachutePack() {
struct Parachute: Codable, Equatable {
enum Canopy: String, Codable, Equatable {
case round, cruciform, rogalloWing, annular, ramAir
}
let canpoy: Canopy
let surfaceArea: Double
}
struct ParachutePack: Codable, Equatable {
let main: Parachute?
let reserve: Parachute?
}
let value = ParachutePack(main: Parachute(canpoy: .ramAir, surfaceArea: 200), reserve: nil)
let encoded = try! encoder.encode(value)
let decoded = try! decoder.decode(ParachutePack.self, from: encoded)
XCTAssertEqual(value, decoded)
}
func testRoundTripArray() {
let count: UInt8 = 100
var bytes: [UInt8] = [0xdc, 0x00, count]
var encoded: [Int] = []
for n in 1...count {
bytes.append(n)
encoded.append(Int(n))
}
let data = Data(bytes: bytes)
let decoded = try! decoder.decode([Int].self, from: data)
XCTAssertEqual(encoded, decoded)
}
func testRoundTripDictionary() {
let (a, z): (UInt8, UInt8) = (0x61, 0x7a)
var bytes: [UInt8] = [0xde, 0x00, 0x1A]
var encoded: [String: Int] = [:]
for n in a...z {
bytes.append(contentsOf: [0xA1, n, n])
encoded[String(Unicode.Scalar(n))] = Int(n)
}
let data = Data(bytes: bytes)
let decoded = try! decoder.decode([String: Int].self, from: data)
XCTAssertEqual(encoded, decoded)
}
func testRoundTripDate() {
var bytes: [UInt8] = [0xD6, 0xFF]
let dateComponents = DateComponents(year: 2018, month: 4, day: 20)
let encoded = Calendar.current.date(from: dateComponents)!
let secondsSince1970 = UInt32(encoded.timeIntervalSince1970)
bytes.append(contentsOf: secondsSince1970.bytes)
let data = Data(bytes: bytes)
let decoded = try! decoder.decode(Date.self, from: data)
XCTAssertEqual(encoded, decoded)
}
func testRoundTripDateWithNanoseconds() {
let encoded = Date()
let data = try! self.encoder.encode(encoded)
let decoded = try! self.decoder.decode(Date.self, from: data)
XCTAssertEqual(encoded.timeIntervalSinceReferenceDate, decoded.timeIntervalSinceReferenceDate, accuracy: 0.0001)
}
static var allTests = [
("testRoundTripAirport", testRoundTripAirport),
("testRoundTripArray", testRoundTripArray),
("testRoundTripDictionary", testRoundTripDictionary),
("testRoundTripDate", testRoundTripDate),
("testRoundTripDateWithNanoseconds", testRoundTripDateWithNanoseconds)
]
}

View File

@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Xamarin.AndroidX.Credentials</name>
</assembly>
<members>
</members>
</doc>

7
nuget.config Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="MAUI Nightly builds" value="https://pkgs.dev.azure.com/xamarin/public/_packaging/maui-nightly/nuget/v3/index.json" />
<add key="Local AndroidX Credentials" value="lib/android/Xamarin.AndroidX.Credentials" />
</packageSources>
</configuration>

169
package-lock.json generated
View File

@@ -8,7 +8,7 @@
"name": "bitwarden-mobile",
"version": "0.0.0",
"devDependencies": {
"gh-pages": "^3.2.3"
"gh-pages": "^6.1.1"
}
},
"node_modules/array-union": {
@@ -33,13 +33,11 @@
}
},
"node_modules/async": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==",
"dev": true,
"dependencies": {
"lodash": "^4.17.14"
}
"license": "MIT"
},
"node_modules/balanced-match": {
"version": "1.0.2",
@@ -58,10 +56,14 @@
}
},
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
"integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=16"
}
},
"node_modules/commondir": {
"version": "1.0.1",
@@ -76,10 +78,11 @@
"dev": true
},
"node_modules/email-addresses": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz",
"integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==",
"dev": true
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz",
"integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==",
"dev": true,
"license": "MIT"
},
"node_modules/escape-string-regexp": {
"version": "1.0.5",
@@ -147,17 +150,18 @@
}
},
"node_modules/fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=6 <7 || >=8"
"node": ">=14.14"
}
},
"node_modules/fs.realpath": {
@@ -167,17 +171,18 @@
"dev": true
},
"node_modules/gh-pages": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz",
"integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==",
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.1.1.tgz",
"integrity": "sha512-upnohfjBwN5hBP9w2dPE7HO5JJTHzSGMV1JrLrHvNuqmjoYHg6TBrCcnEoorjG/e0ejbuvnwyKMdTyM40PEByw==",
"dev": true,
"license": "MIT",
"dependencies": {
"async": "^2.6.1",
"commander": "^2.18.0",
"email-addresses": "^3.0.1",
"async": "^3.2.4",
"commander": "^11.0.0",
"email-addresses": "^5.0.0",
"filenamify": "^4.3.0",
"find-cache-dir": "^3.3.1",
"fs-extra": "^8.1.0",
"fs-extra": "^11.1.1",
"globby": "^6.1.0"
},
"bin": {
@@ -225,10 +230,11 @@
}
},
"node_modules/graceful-fs": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
"dev": true
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true,
"license": "ISC"
},
"node_modules/inflight": {
"version": "1.0.6",
@@ -247,10 +253,14 @@
"dev": true
},
"node_modules/jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
@@ -267,12 +277,6 @@
"node": ">=8"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"node_modules/make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@@ -448,12 +452,13 @@
}
},
"node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4.0.0"
"node": ">= 10.0.0"
}
},
"node_modules/wrappy": {
@@ -480,13 +485,10 @@
"dev": true
},
"async": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
"dev": true,
"requires": {
"lodash": "^4.17.14"
}
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==",
"dev": true
},
"balanced-match": {
"version": "1.0.2",
@@ -505,9 +507,9 @@
}
},
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
"integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
"dev": true
},
"commondir": {
@@ -523,9 +525,9 @@
"dev": true
},
"email-addresses": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz",
"integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz",
"integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==",
"dev": true
},
"escape-string-regexp": {
@@ -573,14 +575,14 @@
}
},
"fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
"dev": true,
"requires": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
}
},
"fs.realpath": {
@@ -590,17 +592,17 @@
"dev": true
},
"gh-pages": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz",
"integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==",
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.1.1.tgz",
"integrity": "sha512-upnohfjBwN5hBP9w2dPE7HO5JJTHzSGMV1JrLrHvNuqmjoYHg6TBrCcnEoorjG/e0ejbuvnwyKMdTyM40PEByw==",
"dev": true,
"requires": {
"async": "^2.6.1",
"commander": "^2.18.0",
"email-addresses": "^3.0.1",
"async": "^3.2.4",
"commander": "^11.0.0",
"email-addresses": "^5.0.0",
"filenamify": "^4.3.0",
"find-cache-dir": "^3.3.1",
"fs-extra": "^8.1.0",
"fs-extra": "^11.1.1",
"globby": "^6.1.0"
}
},
@@ -632,9 +634,9 @@
}
},
"graceful-fs": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true
},
"inflight": {
@@ -654,12 +656,13 @@
"dev": true
},
"jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.6"
"graceful-fs": "^4.1.6",
"universalify": "^2.0.0"
}
},
"locate-path": {
@@ -671,12 +674,6 @@
"p-locate": "^4.1.0"
}
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@@ -801,9 +798,9 @@
}
},
"universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true
},
"wrappy": {

View File

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

View File

@@ -1,327 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{c9e5eea5-ca05-42a1-839b-61506e0a37df}</TemplateGuid>
<OutputType>Library</OutputType>
<RootNamespace>Bit.Droid</RootNamespace>
<AssemblyName>BitwardenAndroid</AssemblyName>
<AndroidApplication>True</AndroidApplication>
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
<AndroidResgenClass>Resource</AndroidResgenClass>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
<TargetFrameworkVersion>v13.0</TargetFrameworkVersion>
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>3</WarningLevel>
<AndroidSupportedAbis />
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>false</DebugSymbols>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidManagedSymbols>true</AndroidManagedSymbols>
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'FDroid|AnyCPU'">
<DebugSymbols>false</DebugSymbols>
<OutputPath>bin\FDroid\</OutputPath>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<DefineConstants>FDROID</DefineConstants>
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="Mono.Android.Export" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
<Reference Include="System.Net.Http" Condition="'$(Configuration)'=='FDroid'" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Plugin.CurrentActivity">
<Version>2.1.0.4</Version>
</PackageReference>
<PackageReference Include="Portable.BouncyCastle">
<Version>1.9.0</Version>
</PackageReference>
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.6.1.3" />
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.18" />
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0.21" />
<PackageReference Include="Xamarin.AndroidX.Core" Version="1.10.1.2" />
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.4.0.2" />
<PackageReference Include="Xamarin.Essentials">
<Version>1.8.0</Version>
</PackageReference>
<PackageReference Include="Xamarin.Firebase.Messaging">
<Version>123.1.2.2</Version>
</PackageReference>
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.9.0.2" />
<PackageReference Include="Xamarin.Google.Dagger" Version="2.46.1.2" />
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
<Version>118.0.1.5</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Accessibility\AccessibilityActivity.cs" />
<Compile Include="Accessibility\AccessibilityHelpers.cs" />
<Compile Include="Accessibility\Credentials.cs" />
<Compile Include="Accessibility\AccessibilityService.cs" />
<Compile Include="Accessibility\Browser.cs" />
<Compile Include="Accessibility\NodeList.cs" />
<Compile Include="Accessibility\KnownUsernameField.cs" />
<Compile Include="Autofill\AutofillConstants.cs" />
<Compile Include="Autofill\AutofillHelpers.cs" />
<Compile Include="Autofill\AutofillService.cs" />
<Compile Include="Autofill\AutofillExternalSelectionActivity.cs" />
<Compile Include="Autofill\Field.cs" />
<Compile Include="Autofill\FieldCollection.cs" />
<Compile Include="Autofill\FilledItem.cs" />
<Compile Include="Autofill\Parser.cs" />
<Compile Include="Autofill\SavedItem.cs" />
<Compile Include="Effects\FabShadowEffect.cs" />
<Compile Include="Effects\FixedSizeEffect.cs" />
<Compile Include="Effects\TabBarEffect.cs" />
<Compile Include="Push\FirebaseMessagingService.cs" />
<Compile Include="Receivers\ClearClipboardAlarmReceiver.cs" />
<Compile Include="Receivers\RestrictionsChangedReceiver.cs" />
<Compile Include="Receivers\EventUploadReceiver.cs" />
<Compile Include="Receivers\PackageReplacedReceiver.cs" />
<Compile Include="Renderers\ExtendedGridRenderer.cs" />
<Compile Include="Renderers\ExtendedDatePickerRenderer.cs" />
<Compile Include="Renderers\CustomTabbedRenderer.cs" />
<Compile Include="Renderers\ExtendedStackLayoutRenderer.cs" />
<Compile Include="Renderers\ExtendedStepperRenderer.cs" />
<Compile Include="Renderers\CustomSwitchRenderer.cs" />
<Compile Include="Renderers\ExtendedTimePickerRenderer.cs" />
<Compile Include="Renderers\ExtendedSliderRenderer.cs" />
<Compile Include="Renderers\CustomEditorRenderer.cs" />
<Compile Include="Renderers\CustomPickerRenderer.cs" />
<Compile Include="Renderers\CustomEntryRenderer.cs" />
<Compile Include="Renderers\CustomSearchBarRenderer.cs" />
<Compile Include="Renderers\HybridWebViewRenderer.cs" />
<Compile Include="Services\AndroidPushNotificationService.cs" />
<Compile Include="Services\AndroidLogService.cs" />
<Compile Include="MainApplication.cs" />
<Compile Include="MainActivity.cs" />
<Compile Include="Resources\Resource.designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\BiometricService.cs" />
<Compile Include="Services\CryptoPrimitiveService.cs" />
<Compile Include="Services\DeviceActionService.cs" />
<Compile Include="Services\LocalizeService.cs" />
<Compile Include="Tiles\AutofillTileService.cs" />
<Compile Include="Tiles\GeneratorTileService.cs" />
<Compile Include="Tiles\MyVaultTileService.cs" />
<Compile Include="Utilities\AndroidHelpers.cs" />
<Compile Include="Utilities\ThemeHelpers.cs" />
<Compile Include="WebAuthCallbackActivity.cs" />
<Compile Include="Renderers\SelectableLabelRenderer.cs" />
<Compile Include="Services\ClipboardService.cs" />
<Compile Include="Utilities\IntentExtensions.cs" />
<Compile Include="Renderers\CustomPageRenderer.cs" />
<Compile Include="Effects\NoEmojiKeyboardEffect.cs" />
<Compile Include="Receivers\NotificationDismissReceiver.cs" />
<Compile Include="Services\FileService.cs" />
<Compile Include="Services\AutofillHandler.cs" />
<Compile Include="Constants.cs" />
<Compile Include="Effects\RemoveFontPaddingEffect.cs" />
<Compile Include="Services\WatchDeviceService.cs" />
<Compile Include="Renderers\CustomLabelRenderer.cs" />
</ItemGroup>
<ItemGroup>
<AndroidAsset Include="Assets\bwi-font.ttf" />
<AndroidAsset Include="Assets\RobotoMono_Regular.ttf" />
<AndroidAsset Include="Assets\MaterialIcons_Regular.ttf" />
<None Include="8bit.keystore.enc" />
<GoogleServicesJson Include="google-services.json" />
<GoogleServicesJson Include="google-services.json.enc" />
<None Include="fdroid-keystore.jks.enc" />
<AndroidNativeLibrary Include="lib\arm64-v8a\libargon2.so" />
<AndroidNativeLibrary Include="lib\armeabi-v7a\libargon2.so" />
<AndroidNativeLibrary Include="lib\x86\libargon2.so" />
<AndroidNativeLibrary Include="lib\x86_64\libargon2.so" />
<None Include="Properties\AndroidManifest.xml" />
<None Include="upload-keystore.jks.enc" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\logo_legacy.png" />
<AndroidResource Include="Resources\drawable-hdpi\logo_white_legacy.png" />
<AndroidResource Include="Resources\drawable-xhdpi\logo_legacy.png" />
<AndroidResource Include="Resources\drawable-xhdpi\logo_white_legacy.png" />
<AndroidResource Include="Resources\drawable-xxhdpi\logo_legacy.png" />
<AndroidResource Include="Resources\drawable-xxhdpi\logo_white_legacy.png" />
<AndroidResource Include="Resources\drawable\card.xml" />
<AndroidResource Include="Resources\drawable\cog_environment.xml" />
<AndroidResource Include="Resources\drawable\cog_settings.xml" />
<AndroidResource Include="Resources\drawable\icon.xml" />
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
<AndroidResource Include="Resources\drawable\ic_launcher_monochrome.xml" />
<AndroidResource Include="Resources\drawable\ic_warning.xml" />
<AndroidResource Include="Resources\drawable\id.xml" />
<AndroidResource Include="Resources\drawable\info.xml" />
<AndroidResource Include="Resources\drawable\list_item_bg.xml" />
<AndroidResource Include="Resources\drawable\lock.xml" />
<AndroidResource Include="Resources\drawable\login.xml" />
<AndroidResource Include="Resources\drawable\logo.xml" />
<AndroidResource Include="Resources\drawable\logo_white.xml" />
<AndroidResource Include="Resources\drawable\send.xml" />
<AndroidResource Include="Resources\drawable\pencil.xml" />
<AndroidResource Include="Resources\drawable\plus.xml" />
<AndroidResource Include="Resources\drawable\generate.xml" />
<AndroidResource Include="Resources\drawable\search.xml" />
<AndroidResource Include="Resources\drawable\shield.xml" />
<AndroidResource Include="Resources\drawable-v23\splash_screen.xml" />
<AndroidResource Include="Resources\drawable-v23\splash_screen_dark.xml" />
<AndroidResource Include="Resources\drawable\switch_thumb.xml" />
<AndroidResource Include="Resources\layout\progress_dialog_layout.xml" />
<AndroidResource Include="Resources\layout\Tabbar.axml" />
<AndroidResource Include="Resources\layout\Toolbar.axml" />
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher_round.xml" />
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher.png" />
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_round.png" />
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher.png" />
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_round.png" />
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher.png" />
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_round.png" />
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher.png" />
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_round.png" />
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher.png" />
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_round.png" />
<AndroidResource Include="Resources\values-night\styles.xml" />
<AndroidResource Include="Resources\values\styles.xml" />
<AndroidResource Include="Resources\values\colors.xml" />
<AndroidResource Include="Resources\values\manifest.xml" />
<AndroidResource Include="Resources\values-v30\manifest.xml" />
<AndroidResource Include="Resources\drawable-v26\splash_screen_round.xml" />
<AndroidResource Include="Resources\drawable\logo_rounded.xml" />
<AndroidResource Include="Resources\drawable-night-v26\splash_screen_round.xml" />
<AndroidResource Include="Resources\drawable\ic_notification.xml">
<SubType></SubType>
<Generator></Generator>
</AndroidResource>
<AndroidResource Include="Resources\layout\validatable_input_dialog_layout.xml">
<SubType></SubType>
<Generator></Generator>
</AndroidResource>
<AndroidResource Include="Resources\drawable\empty_uris_placeholder.xml">
<SubType></SubType>
<Generator></Generator>
</AndroidResource>
<AndroidResource Include="Resources\drawable\empty_uris_placeholder_dark.xml">
<SubType></SubType>
<Generator></Generator>
</AndroidResource>
<AndroidResource Include="Resources\drawable\empty_login_requests.xml">
<SubType></SubType>
<Generator></Generator>
</AndroidResource>
<AndroidResource Include="Resources\drawable\empty_login_requests_dark.xml">
<SubType></SubType>
<Generator></Generator>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\splash_screen.xml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\App\App.csproj">
<Project>{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}</Project>
<Name>App</Name>
</ProjectReference>
<ProjectReference Include="..\Core\Core.csproj">
<Project>{4b8a8c41-9820-4341-974c-41e65b7f4366}</Project>
<Name>Core</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\xml\accessibilityservice.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\xml\autofillservice.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\xml\filepaths.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\xml\network_security_config.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\values\strings.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\values\ic_launcher_background.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\layout\autofill_listitem.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\yubikey.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\yubikey.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\yubikey.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\slider_thumb.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\splash_screen_dark.xml">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\values\dimens.xml">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\xml\app_restrictions.xml">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\values-v30\" />
<Folder Include="Resources\drawable-v26\" />
<Folder Include="Resources\drawable-night-v26\" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project>

View File

@@ -1,30 +0,0 @@
using Android.Graphics.Drawables;
using Bit.Droid.Effects;
using Bit.Droid.Utilities;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportEffect(typeof(FabShadowEffect), "FabShadowEffect")]
namespace Bit.Droid.Effects
{
public class FabShadowEffect : PlatformEffect
{
protected override void OnAttached ()
{
if (Control is Android.Widget.Button button)
{
var gd = new GradientDrawable();
gd.SetColor(ThemeHelpers.FabColor);
gd.SetCornerRadius(100);
button.SetBackground(gd);
button.Elevation = 6;
button.TranslationZ = 20;
}
}
protected override void OnDetached ()
{
}
}
}

View File

@@ -1,23 +0,0 @@
using Android.Widget;
using Bit.Droid.Effects;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportEffect(typeof(FixedSizeEffect), "FixedSizeEffect")]
namespace Bit.Droid.Effects
{
public class FixedSizeEffect : PlatformEffect
{
protected override void OnAttached()
{
if (Element is Label label && Control is TextView textView)
{
textView.SetTextSize(Android.Util.ComplexUnitType.Pt, (float)label.FontSize);
}
}
protected override void OnDetached()
{
}
}
}

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