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

Compare commits

...

115 Commits

Author SHA1 Message Date
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
939 changed files with 17508 additions and 214998 deletions

26
.github/CODEOWNERS vendored
View File

@@ -1,18 +1,26 @@
# 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
# DevOps for Actions and other workflow changes
.github/workflows @bitwarden/dept-devops
# DevOps for Version Bumping
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
## 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,14 +29,14 @@ 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

17
.github/renovate.json vendored
View File

@@ -2,22 +2,21 @@
"$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 +31,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.

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

@@ -0,0 +1,350 @@
---
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@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
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-13
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@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
with:
nuget-version: 6.4.0
- name: Set up .NET
uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
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@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0
if: failure()
env:
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
with:
status: ${{ job.status }}

View File

@@ -10,6 +10,11 @@ on:
- ".github/workflows/**"
workflow_dispatch:
env:
main_app_folder_path: src/App
main_app_project_path: src/App/App.csproj
target-net-version: net8.0
jobs:
cloc:
name: CLOC
@@ -63,19 +68,27 @@ 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
with:
nuget-version: 5.9.0
nuget-version: 6.4.0
- name: Set up .NET
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
with:
dotnet-version: '3.1.x'
dotnet-version: '8.0.x'
- name: Set up MSBuild
uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # v1.3.1
uses: microsoft/setup-msbuild@ede762b26a2de8d110bb5a3db4d7e0e080c0e917 # v1.3.3
# 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: Setup Windows builder
run: choco install checksum --no-progress
@@ -83,7 +96,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
@@ -99,39 +113,43 @@ jobs:
with:
fetch-depth: 0
- name: Decrypt secrets
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
run: |
mkdir -p ~/secrets
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
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
- 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 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 "########################################"
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 +158,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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: com.x8bit.bitwarden.aab
path: ./com.x8bit.bitwarden.aab
@@ -228,7 +234,7 @@ jobs:
- name: Upload Prod .apk artifact
if: ${{ matrix.variant == 'prod' }}
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: com.x8bit.bitwarden.apk
path: ./com.x8bit.bitwarden.apk
@@ -236,7 +242,7 @@ jobs:
- name: Upload Other .apk artifact
if: ${{ matrix.variant != 'prod' }}
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: com.x8bit.bitwarden.${{ matrix.variant }}.apk
path: ./com.x8bit.bitwarden.${{ matrix.variant }}.apk
@@ -256,7 +262,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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: bw-android-apk-sha256.txt
path: ./bw-android-apk-sha256.txt
@@ -264,7 +270,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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: bw-android-${{ matrix.variant }}-apk-sha256.txt
path: ./bw-android-${{ matrix.variant }}-apk-sha256.txt
@@ -277,26 +283,39 @@ 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
with:
nuget-version: 5.9.0
nuget-version: 6.4.0
- name: Set up .NET
uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
with:
dotnet-version: '8.0.x'
- name: Set up MSBuild
uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # v1.3.1
uses: microsoft/setup-msbuild@ede762b26a2de8d110bb5a3db4d7e0e080c0e917 # v1.3.3
# 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: Setup Windows builder
run: choco install checksum --no-progress
@@ -318,54 +337,42 @@ jobs:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Decrypt secrets
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
run: |
mkdir -p ~/secrets
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./src/Android/app_fdroid-keystore.jks ./.github/secrets/app_fdroid-keystore.jks.gpg
- name: Download secrets
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
FILE: app_fdroid-keystore.jks
run: |
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 "########################################"
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 +382,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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: com.x8bit.bitwarden-fdroid.apk
path: ./com.x8bit.bitwarden-fdroid.apk
@@ -460,7 +426,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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: bw-fdroid-apk-sha256.txt
path: ./bw-fdroid-apk-sha256.txt
@@ -469,18 +435,36 @@ jobs:
ios:
name: Apple iOS
runs-on: macos-12
runs-on: macos-13
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@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
with:
nuget-version: 5.9.0
nuget-version: 6.4.0
- name: Set up .NET
uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
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
msbuild -version
dotnet --info
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
@@ -491,7 +475,7 @@ jobs:
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 +486,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))
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 +559,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 +583,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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: Bitwarden iOS
path: |
@@ -686,10 +640,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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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 +674,23 @@ 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: Validate app in App Store
if: |
(github.ref == 'refs/heads/master'
&& 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'
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.ipa" \
--username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
- name: Deploy to App Store
if: |
(github.ref == 'refs/heads/main'
@@ -756,7 +721,7 @@ jobs:
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- 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 +733,13 @@ jobs:
secrets: "crowdin-api-token"
- name: Upload Sources
uses: crowdin/github-action@965d501f160af7b1f88aed4c29154b0caf1e94b9 # v1.9.0
uses: crowdin/github-action@c953b17499daa6be3e5afbf7a63616fb02d8b18d # v1.19.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 +757,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 }}

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

@@ -0,0 +1,53 @@
---
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@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
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

@@ -15,10 +15,10 @@ jobs:
_CROWDIN_PROJECT_ID: "269690"
steps:
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- 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,7 +30,7 @@ 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@c953b17499daa6be3e5afbf7a63616fb02d8b18d # v1.19.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}

View File

@@ -12,6 +12,6 @@ jobs:
pull-requests: write
runs-on: ubuntu-20.04
steps:
- uses: actions/labeler@ac9175f8a1f3625fd0d4fb234536d26811351594 # v4.3.0
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0
with:
sync-labels: true

View File

@@ -28,7 +28,7 @@ jobs:
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 +38,15 @@ jobs:
fi
- name: Checkout repo
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- 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 +55,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 }}'
@@ -67,16 +67,16 @@ jobs:
- 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: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
with:
workflow: build.yml
workflow_conclusion: success
branch: ${{ steps.branch.outputs.branch-name }}
- 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: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
with:
workflow: build.yml
workflow_conclusion: success
@@ -86,8 +86,8 @@ jobs:
run: zip -r Bitwarden\ iOS.zip Bitwarden\ iOS
- 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 +103,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'
@@ -126,11 +126,11 @@ jobs:
if: inputs.fdroid_publish
steps:
- name: Checkout repo
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- 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: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
with:
workflow: build.yml
workflow_conclusion: success
@@ -138,8 +138,8 @@ jobs:
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: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
with:
workflow: build.yml
workflow_conclusion: success
@@ -147,7 +147,7 @@ jobs:
name: com.x8bit.bitwarden-fdroid.apk
- name: Set up Node
uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: '16.x'
@@ -176,13 +176,19 @@ jobs:
- name: Install Node dependencies
run: npm install
- name: Decrypt secrets
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Download secrets
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
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
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:
@@ -211,5 +217,5 @@ jobs:
cd $GITHUB_WORKSPACE
- name: Deploy to gh-pages
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
if: ${{ inputs.release_type != 'Dry Run' }}
run: npm run deploy

View File

@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-20.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,5 @@
---
name: Version Auto Bump
name: Auto Bump Mobile Version
on:
push:
@@ -7,34 +7,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,13 +1,13 @@
---
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
@@ -15,13 +15,36 @@ on:
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: Checkout Branch
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
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 +55,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 +110,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 +210,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 +224,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,16 +240,18 @@ 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 }}
@@ -174,8 +259,8 @@ jobs:
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
@@ -183,14 +268,28 @@ jobs:
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

1
.gitignore vendored
View File

@@ -31,6 +31,7 @@ Components/
x64/
x86/
!src/lib/x86/
!src/App/Platforms/Android/lib/x86/
build/
bld/
[Bb]in/

16
Directory.Build.props Normal file
View File

@@ -0,0 +1,16 @@
<Project>
<PropertyGroup>
<MauiVersion>8.0.7</MauiVersion>
<ReleaseCodesignProvision>Automatic:AppStore</ReleaseCodesignProvision>
<ReleaseCodesignKey>iPhone Distribution</ReleaseCodesignKey>
<IncludeBitwardeniOSExtensions>True</IncludeBitwardeniOSExtensions>
<IncludeBitwardenWatchOSApp>True</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,4 +1,4 @@
[![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)
@@ -6,13 +6,13 @@
<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 [Mobile section](https://contributing.bitwarden.com/getting-started/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.
# We're Hiring!
@@ -20,6 +20,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

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

6
nuget.config Normal file
View File

@@ -0,0 +1,6 @@
<?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" />
</packageSources>
</configuration>

2
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": "3.2.3"
}
},
"node_modules/array-union": {

View File

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

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()
{
}
}
}

View File

@@ -1,23 +0,0 @@
using Android.Widget;
using Bit.Droid.Effects;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportEffect(typeof(RemoveFontPaddingEffect), nameof(RemoveFontPaddingEffect))]
namespace Bit.Droid.Effects
{
public class RemoveFontPaddingEffect : PlatformEffect
{
protected override void OnAttached()
{
if (Control is TextView textView)
{
textView.SetIncludeFontPadding(false);
}
}
protected override void OnDetached()
{
}
}
}

View File

@@ -1,30 +0,0 @@
using Android.Views;
using Bit.Droid.Effects;
using Google.Android.Material.BottomNavigation;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ResolutionGroupName("Bitwarden")]
[assembly: ExportEffect(typeof(TabBarEffect), "TabBarEffect")]
namespace Bit.Droid.Effects
{
public class TabBarEffect : PlatformEffect
{
protected override void OnAttached()
{
if (!(Container.GetChildAt(0) is ViewGroup layout))
{
return;
}
if (!(layout.GetChildAt(1) is BottomNavigationView bottomNavigationView))
{
return;
}
bottomNavigationView.LabelVisibilityMode = LabelVisibilityMode.LabelVisibilityLabeled;
}
protected override void OnDetached()
{
}
}
}

View File

@@ -1,28 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("BitwardenAndroid")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Bitwarden Inc.")]
[assembly: AssemblyProduct("Bitwarden")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -1,69 +0,0 @@
using System.ComponentModel;
using Android.Content;
using Android.Content.Res;
using Android.Views.InputMethods;
using Bit.Droid.Renderers;
using Bit.Droid.Utilities;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(Editor), typeof(CustomEditorRenderer))]
namespace Bit.Droid.Renderers
{
public class CustomEditorRenderer : EditorRenderer
{
public CustomEditorRenderer(Context context)
: base(context)
{ }
// Workaround for issue described here:
// https://github.com/xamarin/Xamarin.Forms/issues/8291#issuecomment-617456651
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
EditText.Enabled = false;
EditText.Enabled = true;
}
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
UpdateBorderColor();
if (Control != null && e.NewElement != null)
{
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
Control.PaddingBottom + 20);
Control.ImeOptions = Control.ImeOptions | (ImeAction)ImeFlags.NoPersonalizedLearning |
(ImeAction)ImeFlags.NoExtractUi;
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Entry.TextColorProperty.PropertyName)
{
UpdateBorderColor();
}
}
private void UpdateBorderColor()
{
if (Control != null)
{
var states = new[]
{
new[] { Android.Resource.Attribute.StateFocused }, // focused
new[] { -Android.Resource.Attribute.StateFocused }, // unfocused
};
var colors = new int[]
{
ThemeHelpers.PrimaryColor,
ThemeHelpers.MutedColor
};
Control.BackgroundTintList = new ColorStateList(states, colors);
}
}
}
}

View File

@@ -1,107 +0,0 @@
using System.ComponentModel;
using Android.Content;
using Android.Content.Res;
using Android.Graphics;
using Android.Text;
using Android.Views.InputMethods;
using Android.Widget;
using Bit.Droid.Renderers;
using Bit.Droid.Utilities;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(Entry), typeof(CustomEntryRenderer))]
namespace Bit.Droid.Renderers
{
public class CustomEntryRenderer : EntryRenderer
{
public CustomEntryRenderer(Context context)
: base(context)
{ }
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
UpdateBorderColor();
if (Control != null && e.NewElement != null)
{
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
Control.PaddingBottom + 20);
Control.ImeOptions = Control.ImeOptions | (ImeAction)ImeFlags.NoPersonalizedLearning |
(ImeAction)ImeFlags.NoExtractUi;
}
}
// Workaround for bug preventing long-press -> copy/paste on Android 11
// See https://issuetracker.google.com/issues/37095917
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
Control.Enabled = false;
Control.Enabled = true;
}
// Workaround for failure to disable text prediction on non-password fields
// see https://github.com/xamarin/Xamarin.Forms/issues/10857
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
// Check if changed property is "IsPassword", otherwise ignore
if (e.PropertyName == Entry.IsPasswordProperty.PropertyName)
{
// Check if field type is text, otherwise ignore (numeric passwords, etc.)
EditText.InputType = Element.Keyboard.ToInputType();
bool isText = (EditText.InputType & InputTypes.ClassText) == InputTypes.ClassText,
isNumber = (EditText.InputType & InputTypes.ClassNumber) == InputTypes.ClassNumber;
if (isText || isNumber)
{
if (Element.IsPassword)
{
// Element is a password field, set inputType to TextVariationPassword which disables
// predictive text by default
EditText.InputType = EditText.InputType |
(isText ? InputTypes.TextVariationPassword : InputTypes.NumberVariationPassword);
}
else
{
// Element is not a password field, set inputType to TextVariationVisiblePassword to
// disable predictive text while still displaying the content.
EditText.InputType = EditText.InputType |
(isText ? InputTypes.TextVariationVisiblePassword : InputTypes.NumberVariationNormal);
}
// The workaround above forces a reset of the style properties, so we need to re-apply the font.
// see https://xamarin.github.io/bugzilla-archives/33/33666/bug.html
var typeface = Typeface.CreateFromAsset(Context.Assets, "RobotoMono_Regular.ttf");
if (Control is TextView label)
{
label.Typeface = typeface;
}
}
}
else if (e.PropertyName == Entry.TextColorProperty.PropertyName)
{
UpdateBorderColor();
}
}
private void UpdateBorderColor()
{
if (Control != null)
{
var states = new[]
{
new[] { Android.Resource.Attribute.StateFocused }, // focused
new[] { -Android.Resource.Attribute.StateFocused }, // unfocused
};
var colors = new int[]
{
ThemeHelpers.PrimaryColor,
ThemeHelpers.MutedColor
};
Control.BackgroundTintList = new ColorStateList(states, colors);
}
}
}
}

View File

@@ -1,43 +0,0 @@
using System.ComponentModel;
using Android.Content;
using Android.OS;
using Bit.App.Controls;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(CustomLabel), typeof(CustomLabelRenderer))]
namespace Bit.Droid.Renderers
{
public class CustomLabelRenderer : LabelRenderer
{
public CustomLabelRenderer(Context context)
: base(context)
{ }
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (Control != null && e.NewElement is CustomLabel label)
{
if (label.FontWeight.HasValue && Build.VERSION.SdkInt >= BuildVersionCodes.P)
{
Control.Typeface = Android.Graphics.Typeface.Create(null, label.FontWeight.Value, false);
}
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var label = sender as CustomLabel;
switch (e.PropertyName)
{
case nameof(CustomLabel.AutomationId):
Control.ContentDescription = label.AutomationId;
break;
}
base.OnElementPropertyChanged(sender, e);
}
}
}

View File

@@ -1,31 +0,0 @@
using System;
using Android.App;
using Android.Content;
using AndroidX.AppCompat.Widget;
using Bit.App.Resources;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ContentPage), typeof(CustomPageRenderer))]
namespace Bit.Droid.Renderers
{
public class CustomPageRenderer : PageRenderer
{
public CustomPageRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
base.OnElementChanged(e);
Activity context = (Activity)this.Context;
var toolbar = context.FindViewById<Toolbar>(Resource.Id.toolbar);
if(toolbar != null)
{
toolbar.NavigationContentDescription = AppResources.TapToGoBack;
}
}
}
}

View File

@@ -1,57 +0,0 @@
using System.ComponentModel;
using Android.Content;
using Android.Content.Res;
using Bit.Droid.Renderers;
using Bit.Droid.Utilities;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(Picker), typeof(CustomPickerRenderer))]
namespace Bit.Droid.Renderers
{
public class CustomPickerRenderer : PickerRenderer
{
public CustomPickerRenderer(Context context)
: base(context)
{ }
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
UpdateBorderColor();
if (Control != null && e.NewElement != null)
{
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
Control.PaddingBottom + 20);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Picker.TextColorProperty.PropertyName)
{
UpdateBorderColor();
}
}
private void UpdateBorderColor()
{
if (Control != null)
{
var states = new[]
{
new[] { Android.Resource.Attribute.StateFocused }, // focused
new[] { -Android.Resource.Attribute.StateFocused }, // unfocused
};
var colors = new int[]
{
ThemeHelpers.PrimaryColor,
ThemeHelpers.MutedColor
};
Control.BackgroundTintList = new ColorStateList(states, colors);
}
}
}
}

View File

@@ -1,33 +0,0 @@
using Android.Content;
using Android.Views.InputMethods;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(SearchBar), typeof(CustomSearchBarRenderer))]
namespace Bit.Droid.Renderers
{
public class CustomSearchBarRenderer : SearchBarRenderer
{
public CustomSearchBarRenderer(Context context)
: base(context)
{ }
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
if (Control != null && e.NewElement != null)
{
try
{
var magId = Resources.GetIdentifier("android:id/search_mag_icon", null, null);
var magImage = (Android.Widget.ImageView)Control.FindViewById(magId);
magImage.LayoutParameters = new Android.Widget.LinearLayout.LayoutParams(0, 0);
}
catch { }
Control.SetImeOptions(Control.ImeOptions | (ImeAction)ImeFlags.NoPersonalizedLearning |
(ImeAction)ImeFlags.NoExtractUi);
}
}
}
}

View File

@@ -1,67 +0,0 @@
using System.ComponentModel;
using Android.Content;
using Android.Content.Res;
using Android.Graphics.Drawables;
using Android.OS;
using AndroidX.Core.Content.Resources;
using Bit.Droid.Renderers;
using Bit.Droid.Utilities;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(Switch), typeof(CustomSwitchRenderer))]
namespace Bit.Droid.Renderers
{
public class CustomSwitchRenderer : SwitchRenderer
{
public CustomSwitchRenderer(Context context)
: base(context)
{}
protected override void OnElementChanged(ElementChangedEventArgs<Switch> e)
{
base.OnElementChanged(e);
UpdateColors();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Switch.OnColorProperty.PropertyName)
{
UpdateColors();
}
}
private void UpdateColors()
{
if (Build.VERSION.SdkInt <= BuildVersionCodes.LollipopMr1)
{
// Android 5.x doesn't support ThumbTintList, and using SwitchCompat on every version after 5.x
// doesn't apply tinting the way we want. Let 5.x to do its own thing here.
return;
}
if (Control != null)
{
Control.SetHintTextColor(ThemeHelpers.MutedColor);
var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.switch_thumb, null);
if (t is GradientDrawable thumb)
{
Control.ThumbDrawable = thumb;
}
var thumbStates = new[]
{
new[] { Android.Resource.Attribute.StateChecked }, // checked
new[] { -Android.Resource.Attribute.StateChecked }, // unchecked
};
var thumbColors = new int[]
{
ThemeHelpers.SwitchOnColor,
ThemeHelpers.SwitchThumbColor
};
Control.ThumbTintList = new ColorStateList(thumbStates, thumbColors);
}
}
}
}

View File

@@ -1,66 +0,0 @@
using Android.Content;
using Android.Views;
using Bit.App.Pages;
using Bit.Droid.Renderers;
using Google.Android.Material.BottomNavigation;
using Google.Android.Material.Navigation;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Xamarin.Forms.Platform.Android.AppCompat;
[assembly: ExportRenderer(typeof(TabbedPage), typeof(CustomTabbedRenderer))]
namespace Bit.Droid.Renderers
{
public class CustomTabbedRenderer : TabbedPageRenderer, NavigationBarView.IOnItemReselectedListener
{
private TabbedPage _page;
public CustomTabbedRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
_page = e.NewElement;
GetBottomNavigationView()?.SetOnItemReselectedListener(this);
}
else
{
_page = e.OldElement;
}
}
private BottomNavigationView GetBottomNavigationView()
{
for (var i = 0; i < ViewGroup.ChildCount; i++)
{
var childView = ViewGroup.GetChildAt(i);
if (childView is ViewGroup viewGroup)
{
for (var j = 0; j < viewGroup.ChildCount; j++)
{
var childRelativeLayoutView = viewGroup.GetChildAt(j);
if (childRelativeLayoutView is BottomNavigationView bottomNavigationView)
{
return bottomNavigationView;
}
}
}
}
return null;
}
public void OnNavigationItemReselected(IMenuItem item)
{
if (_page?.CurrentPage?.Navigation != null && _page.CurrentPage.Navigation.NavigationStack.Count > 0)
{
if (_page is TabsPage tabsPage)
{
tabsPage.OnPageReselected();
}
Device.BeginInvokeOnMainThread(async () => await _page.CurrentPage.Navigation.PopToRootAsync());
}
}
}
}

View File

@@ -1,50 +0,0 @@
using System.ComponentModel;
using Android.Content;
using Android.Views;
using Bit.App.Controls;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ExtendedDatePicker), typeof(ExtendedDatePickerRenderer))]
namespace Bit.Droid.Renderers
{
public class ExtendedDatePickerRenderer : DatePickerRenderer
{
public ExtendedDatePickerRenderer(Context context)
: base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
{
base.OnElementChanged(e);
if (Control != null && Element is ExtendedDatePicker element)
{
// center text
Control.Gravity = GravityFlags.CenterHorizontal;
// use placeholder until NullableDate set
if (!element.NullableDate.HasValue)
{
Control.Text = element.PlaceHolder;
}
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == DatePicker.DateProperty.PropertyName ||
e.PropertyName == DatePicker.FormatProperty.PropertyName)
{
if (Control != null && Element is ExtendedDatePicker element)
{
if (Element.Format == element.PlaceHolder)
{
Control.Text = element.PlaceHolder;
return;
}
}
}
base.OnElementPropertyChanged(sender, e);
}
}
}

View File

@@ -1,23 +0,0 @@
using Android.Content;
using Bit.App.Controls;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ExtendedGrid), typeof(ExtendedGridRenderer))]
namespace Bit.Droid.Renderers
{
public class ExtendedGridRenderer : ViewRenderer
{
public ExtendedGridRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<View> elementChangedEvent)
{
base.OnElementChanged(elementChangedEvent);
if (elementChangedEvent.NewElement != null)
{
SetBackgroundResource(Resource.Drawable.list_item_bg);
}
}
}
}

View File

@@ -1,56 +0,0 @@
using System.ComponentModel;
using Android.Content;
using Android.Graphics.Drawables;
using AndroidX.Core.Content.Resources;
using Bit.App.Controls;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ExtendedSlider), typeof(ExtendedSliderRenderer))]
namespace Bit.Droid.Renderers
{
public class ExtendedSliderRenderer : SliderRenderer
{
public ExtendedSliderRenderer(Context context)
: base(context)
{}
protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
{
base.OnElementChanged(e);
UpdateColor();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == ExtendedSlider.ThumbBorderColorProperty.PropertyName)
{
UpdateColor();
}
}
private void UpdateColor()
{
if (Control != null && Element is ExtendedSlider view)
{
var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.slider_thumb, null);
if (t is GradientDrawable thumb)
{
if (view.ThumbColor == Color.Default)
{
thumb.SetColor(Color.White.ToAndroid());
}
else
{
thumb.SetColor(view.ThumbColor.ToAndroid());
}
thumb.SetStroke(3, view.ThumbBorderColor.ToAndroid());
Control.SetThumb(thumb);
}
}
}
}
}

View File

@@ -1,23 +0,0 @@
using Android.Content;
using Bit.App.Controls;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ExtendedStackLayout), typeof(ExtendedStackLayoutRenderer))]
namespace Bit.Droid.Renderers
{
public class ExtendedStackLayoutRenderer : ViewRenderer
{
public ExtendedStackLayoutRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<View> elementChangedEvent)
{
base.OnElementChanged(elementChangedEvent);
if (elementChangedEvent.NewElement != null)
{
SetBackgroundResource(Resource.Drawable.list_item_bg);
}
}
}
}

View File

@@ -1,72 +0,0 @@
using System.ComponentModel;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Bit.App.Controls;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ExtendedStepper), typeof(ExtendedStepperRenderer))]
namespace Bit.Droid.Renderers
{
public class ExtendedStepperRenderer : StepperRenderer
{
public ExtendedStepperRenderer(Context context)
: base(context)
{}
protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
{
base.OnElementChanged(e);
UpdateBgColor();
UpdateFgColor();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == ExtendedStepper.StepperBackgroundColorProperty.PropertyName)
{
UpdateBgColor();
}
else if (e.PropertyName == ExtendedStepper.StepperForegroundColorProperty.PropertyName)
{
UpdateFgColor();
}
}
private void UpdateBgColor()
{
if (Control != null && Element is ExtendedStepper view)
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
{
Control.GetChildAt(0)?.Background?.SetColorFilter(
new BlendModeColorFilter(view.StepperBackgroundColor.ToAndroid(), BlendMode.Multiply));
Control.GetChildAt(1)?.Background?.SetColorFilter(
new BlendModeColorFilter(view.StepperBackgroundColor.ToAndroid(), BlendMode.Multiply));
}
else
{
Control.GetChildAt(0)?.Background?.SetColorFilter(
view.StepperBackgroundColor.ToAndroid(), PorterDuff.Mode.Multiply);
Control.GetChildAt(1)?.Background?.SetColorFilter(
view.StepperBackgroundColor.ToAndroid(), PorterDuff.Mode.Multiply);
}
}
}
private void UpdateFgColor()
{
if (Control != null && Element is ExtendedStepper view)
{
var btn0 = Control.GetChildAt(0) as Android.Widget.Button;
btn0?.SetTextColor(view.StepperForegroundColor.ToAndroid());
var btn1 = Control.GetChildAt(1) as Android.Widget.Button;
btn1?.SetTextColor(view.StepperForegroundColor.ToAndroid());
}
}
}
}

View File

@@ -1,50 +0,0 @@
using System.ComponentModel;
using Android.Content;
using Android.Views;
using Bit.App.Controls;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ExtendedTimePicker), typeof(ExtendedTimePickerRenderer))]
namespace Bit.Droid.Renderers
{
public class ExtendedTimePickerRenderer : TimePickerRenderer
{
public ExtendedTimePickerRenderer(Context context)
: base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
{
base.OnElementChanged(e);
if (Control != null && Element is ExtendedTimePicker element)
{
// center text
Control.Gravity = GravityFlags.CenterHorizontal;
// use placeholder until NullableTime set
if (!element.NullableTime.HasValue)
{
Control.Text = element.PlaceHolder;
}
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == TimePicker.TimeProperty.PropertyName ||
e.PropertyName == TimePicker.FormatProperty.PropertyName)
{
if (Control != null && Element is ExtendedTimePicker element)
{
if (Element.Format == element.PlaceHolder)
{
Control.Text = element.PlaceHolder;
return;
}
}
}
base.OnElementPropertyChanged(sender, e);
}
}
}

View File

@@ -1,98 +0,0 @@
using System;
using Bit.App.Controls;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Android.Webkit;
using AWebkit = Android.Webkit;
using Java.Interop;
using Android.Content;
using Bit.Droid.Renderers;
using System.ComponentModel;
[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
namespace Bit.Droid.Renderers
{
public class HybridWebViewRenderer : ViewRenderer<HybridWebView, AWebkit.WebView>
{
private const string JSFunction = "function invokeCSharpAction(data){jsBridge.invokeAction(data);}";
private readonly Context _context;
public HybridWebViewRenderer(Context context)
: base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
var webView = new AWebkit.WebView(_context);
webView.Settings.JavaScriptEnabled = true;
webView.SetWebViewClient(new JSWebViewClient(string.Format("javascript: {0}", JSFunction)));
SetNativeControl(webView);
}
if (e.OldElement != null)
{
Control.RemoveJavascriptInterface("jsBridge");
var hybridWebView = e.OldElement as HybridWebView;
hybridWebView.Cleanup();
}
if (e.NewElement != null)
{
Control.AddJavascriptInterface(new JSBridge(this), "jsBridge");
Control.LoadUrl(Element.Uri);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == HybridWebView.UriProperty.PropertyName)
{
Control.LoadUrl(Element.Uri);
}
}
public class JSBridge : Java.Lang.Object
{
private readonly WeakReference<HybridWebViewRenderer> _hybridWebViewRenderer;
public JSBridge(HybridWebViewRenderer hybridRenderer)
{
_hybridWebViewRenderer = new WeakReference<HybridWebViewRenderer>(hybridRenderer);
}
[JavascriptInterface]
[Export("invokeAction")]
public void InvokeAction(string data)
{
if (_hybridWebViewRenderer != null &&
_hybridWebViewRenderer.TryGetTarget(out HybridWebViewRenderer hybridRenderer))
{
hybridRenderer.Element.InvokeAction(data);
}
}
}
public class JSWebViewClient : WebViewClient
{
private readonly string _javascript;
public JSWebViewClient(string javascript)
{
_javascript = javascript;
}
public override void OnPageFinished(AWebkit.WebView view, string url)
{
base.OnPageFinished(view, url);
view.EvaluateJavascript(_javascript, null);
}
}
}
}

View File

@@ -1,25 +0,0 @@
using System;
using Android.Content;
using Bit.App.Controls;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(SelectableLabel), typeof(SelectableLabelRenderer))]
namespace Bit.Droid.Renderers
{
public class SelectableLabelRenderer : LabelRenderer
{
public SelectableLabelRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.SetTextIsSelectable(true);
}
}
}
}

View File

@@ -1,441 +1,260 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<RootNamespace>Bit.App</RootNamespace>
<AssemblyName>BitwardenApp</AssemblyName>
<Configurations>Debug;Release;FDroid</Configurations>
<PropertyGroup>
<TargetFrameworks>net8.0-android;net8.0-ios</TargetFrameworks>
<!-- Uncomment to also build for Windows platform.-->
<!--<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>-->
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
<OutputType>Exe</OutputType>
<RootNamespace>Bit.App</RootNamespace>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<!-- Display name -->
<ApplicationTitle>Bitwarden</ApplicationTitle>
<!-- App Identifier -->
<ApplicationId Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">com.8bit.bitwarden</ApplicationId>
<ApplicationId Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">com.x8bit.bitwarden</ApplicationId>
<ApplicationIdGuid>ccf4766c-a36c-4647-900c-0ea7d323ccc6</ApplicationIdGuid>
<!-- Versions -->
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">11.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
<ForceSimulatorX64ArchitectureInIDE>true</ForceSimulatorX64ArchitectureInIDE>
</PropertyGroup>
<PropertyGroup>
<DefineConstants Condition=" '$(CustomConstants)' != '' ">$(DefineConstants);$(CustomConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-android|AnyCPU'">
<AndroidEnableMultiDex>True</AndroidEnableMultiDex>
<UseInterpreter>False</UseInterpreter>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Plugin.Fingerprint" Version="2.1.5" />
<PackageReference Include="SkiaSharp.Views.Forms" Version="2.88.3" />
<PackageReference Include="Xamarin.CommunityToolkit" Version="2.0.6" />
<PackageReference Include="Xamarin.Essentials" Version="1.8.0" />
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2612" />
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
<PackageReference Include="MessagePack" Version="2.4.59" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Pages\Accounts\EnvironmentPage.xaml.cs">
<DependentUpon>EnvironmentPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Accounts\HintPage.xaml.cs">
<DependentUpon>HintPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Accounts\LockPage.xaml.cs">
<DependentUpon>LockPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Accounts\TwoFactorPage.xaml.cs">
<DependentUpon>TwoFactorPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Accounts\RegisterPage.xaml.cs">
<DependentUpon>RegisterPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Accounts\LoginPage.xaml.cs">
<DependentUpon>LoginPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Generator\GeneratorPage.xaml.cs">
<DependentUpon>GeneratorPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Generator\GeneratorHistoryPage.xaml.cs">
<DependentUpon>GeneratorHistoryPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Settings\AutofillPage.xaml.cs">
<DependentUpon>AutofillPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Settings\ExtensionPage.xaml.cs">
<DependentUpon>ExtensionPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Settings\FolderAddEditPage.xaml.cs">
<DependentUpon>FolderAddEditPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Settings\FoldersPage.xaml.cs">
<DependentUpon>FoldersPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Settings\ExportVaultPage.xaml.cs">
<DependentUpon>ExportVaultPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Vault\AttachmentsPage.xaml.cs">
<DependentUpon>AttachmentsPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Vault\AutofillCiphersPage.xaml.cs">
<DependentUpon>AutofillCiphersPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Vault\CollectionsPage.xaml.cs">
<DependentUpon>CollectionsPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Vault\ScanPage.xaml.cs">
<DependentUpon>ScanPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Vault\SharePage.xaml.cs">
<DependentUpon>SharePage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Vault\CiphersPage.xaml.cs">
<DependentUpon>CiphersPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Vault\PasswordHistoryPage.xaml.cs">
<DependentUpon>PasswordHistoryPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Vault\CipherDetailsPage.xaml.cs">
<DependentUpon>CipherDetailsPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Vault\CipherAddEditPage.xaml.cs">
<DependentUpon>CipherAddEditPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Settings\SettingsPage\SettingsPage.xaml.cs">
<DependentUpon>SettingsPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Vault\GroupingsPage\GroupingsPage.xaml.cs">
<DependentUpon>GroupingsPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Accounts\LoginSsoPage.xaml.cs">
<DependentUpon>LoginSsoPage.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Pages\Accounts\SetPasswordPage.xaml.cs">
<DependentUpon>ResetMasterPasswordPage.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Pages\Send\SendGroupingsPage\SendGroupingsPage.xaml.cs">
<DependentUpon>SendGroupingsPage.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Remove="Pages\Accounts\AccountsPopupPage.xaml.cs" />
<Compile Update="Pages\Accounts\LoginPasswordlessPage.xaml.cs">
<DependentUpon>LoginPasswordlessPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Accounts\LoginPasswordlessRequestPage.xaml.cs">
<DependentUpon>LoginPasswordlessRequestPage.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\" />
<Folder Include="Behaviors\" />
<Folder Include="Lists\" />
<Folder Include="Lists\ItemLayouts\" />
<Folder Include="Lists\DataTemplateSelectors\" />
<Folder Include="Lists\ItemLayouts\CustomFields\" />
<Folder Include="Lists\ItemViewModels\" />
<Folder Include="Lists\ItemViewModels\CustomFields\" />
<Folder Include="Controls\AccountSwitchingOverlay\" />
<Folder Include="Utilities\AccountManagement\" />
<Folder Include="Controls\DateTime\" />
<Folder Include="Controls\IconLabelButton\" />
<Folder Include="Controls\PasswordStrengthProgressBar\" />
<Folder Include="Utilities\Automation\" />
<Folder Include="Utilities\Prompts\" />
<Folder Include="Controls\Settings\" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Remove="Pages\Accounts\AccountsPopupPage.xaml" />
</ItemGroup>
<ItemGroup>
<Compile Update="Styles\Black.xaml.cs">
<DependentUpon>Black.xaml</DependentUpon>
</Compile>
<Compile Update="Styles\Nord.xaml.cs">
<DependentUpon>Nord.xaml</DependentUpon>
</Compile>
<Compile Update="Styles\Variables.xaml.cs">
<DependentUpon>Variables.xaml</DependentUpon>
</Compile>
<Compile Update="Styles\Light.xaml.cs">
<DependentUpon>Light.xaml</DependentUpon>
</Compile>
<Compile Update="Styles\Dark.xaml.cs">
<DependentUpon>Dark.xaml</DependentUpon>
</Compile>
<Compile Update="Styles\iOS.xaml.cs">
<DependentUpon>iOS.xaml</DependentUpon>
</Compile>
<Compile Update="Styles\Android.xaml.cs">
<DependentUpon>Android.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Update="Resources\AppResources.cs.Designer.cs">
<DependentUpon>AppResources.cs.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.da.Designer.cs">
<DependentUpon>AppResources.da.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.de.Designer.cs">
<DependentUpon>AppResources.de.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>AppResources.resx</DependentUpon>
</Compile>
<Compile Update="Resources\AppResources.es.Designer.cs">
<DependentUpon>AppResources.es.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.fi.Designer.cs">
<DependentUpon>AppResources.fi.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.fr.Designer.cs">
<DependentUpon>AppResources.fr.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.hi.Designer.cs">
<DependentUpon>AppResources.hi.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.hr.Designer.cs">
<DependentUpon>AppResources.hr.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.hu.Designer.cs">
<DependentUpon>AppResources.hu.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.id.Designer.cs">
<DependentUpon>AppResources.id.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.it.Designer.cs">
<DependentUpon>AppResources.it.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.ja.Designer.cs">
<DependentUpon>AppResources.ja.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.nl.Designer.cs">
<DependentUpon>AppResources.nl.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.pl.Designer.cs">
<DependentUpon>AppResources.pl.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.pt-BR.Designer.cs">
<DependentUpon>AppResources.pt-BR.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.pt-PT.Designer.cs">
<DependentUpon>AppResources.pt-PT.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.ro.Designer.cs">
<DependentUpon>AppResources.ro.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.ru.Designer.cs">
<DependentUpon>AppResources.ru.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.sk.Designer.cs">
<DependentUpon>AppResources.sk.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.sv.Designer.cs">
<DependentUpon>AppResources.sv.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.th.Designer.cs">
<DependentUpon>AppResources.th.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.tr.Designer.cs">
<DependentUpon>AppResources.tr.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.uk.Designer.cs">
<DependentUpon>AppResources.uk.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.vi.Designer.cs">
<DependentUpon>AppResources.vi.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.zh-Hans.Designer.cs">
<DependentUpon>AppResources.zh-Hans.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Resources\AppResources.zh-Hant.Designer.cs">
<DependentUpon>AppResources.zh-Hant.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources\AppResources.cs.resx">
<LastGenOutput>AppResources.cs.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.da.resx">
<LastGenOutput>AppResources.da.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.de.resx">
<LastGenOutput>AppResources.de.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.es.resx">
<LastGenOutput>AppResources.es.Designer.cs</LastGenOutput>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.fi.resx">
<LastGenOutput>AppResources.fi.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.fr.resx">
<LastGenOutput>AppResources.fr.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.hi.resx">
<LastGenOutput>AppResources.hi.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.hr.resx">
<LastGenOutput>AppResources.hr.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.hu.resx">
<LastGenOutput>AppResources.hu.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.id.resx">
<LastGenOutput>AppResources.id.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.it.resx">
<LastGenOutput>AppResources.it.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.ja.resx">
<LastGenOutput>AppResources.ja.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.nl.resx">
<LastGenOutput>AppResources.nl.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.pl.resx">
<LastGenOutput>AppResources.pl.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.pt-BR.resx">
<LastGenOutput>AppResources.pt-BR.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.pt-PT.resx">
<LastGenOutput>AppResources.pt-PT.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.ro.resx">
<LastGenOutput>AppResources.ro.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.ru.resx">
<LastGenOutput>AppResources.ru.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.sk.resx">
<LastGenOutput>AppResources.sk.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.sv.resx">
<LastGenOutput>AppResources.sv.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.th.resx">
<LastGenOutput>AppResources.th.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.tr.resx">
<LastGenOutput>AppResources.tr.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.uk.resx">
<LastGenOutput>AppResources.uk.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.vi.resx">
<LastGenOutput>AppResources.vi.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.zh-Hans.resx">
<LastGenOutput>AppResources.zh-Hans.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AppResources.zh-Hant.resx">
<LastGenOutput>AppResources.zh-Hant.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Remove="Behaviors\" />
<None Remove="Xamarin.CommunityToolkit" />
<None Remove="Lists\" />
<None Remove="Lists\DataTemplates\" />
<None Remove="Lists\DataTemplateSelectors\" />
<None Remove="Lists\DataTemplates\CustomFields\" />
<None Remove="Lists\ItemViewModels\" />
<None Remove="Lists\ItemViewModels\CustomFields\" />
<None Remove="Controls\AccountSwitchingOverlay\" />
<None Remove="Utilities\AccountManagement\" />
<None Remove="Controls\DateTime\" />
<None Remove="Controls\IconLabelButton\" />
<None Remove="MessagePack" />
<None Remove="MessagePack.MSBuild.Tasks" />
<None Remove="Controls\PasswordStrengthProgressBar\" />
<None Remove="Utilities\Automation\" />
<None Remove="Utilities\Prompts\" />
<None Remove="Controls\Settings\" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-android|AnyCPU'">
<AndroidEnableMultiDex>True</AndroidEnableMultiDex>
<UseInterpreter>False</UseInterpreter>
<DebugSymbols>False</DebugSymbols>
<RunAOTCompilation>False</RunAOTCompilation>
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-ios|AnyCPU'">
<CreatePackage>false</CreatePackage>
<CodesignProvision>Automatic</CodesignProvision>
<CodesignKey>iPhone Developer</CodesignKey>
<CodesignEntitlements>Platforms\iOS\Entitlements.plist</CodesignEntitlements>
<UseInterpreter>true</UseInterpreter>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(RuntimeIdentifier)'=='Debug|net8.0-ios|iossimulator-x64'">
<MtouchExtraArgs>$(Argon2IdLoadMtouchExtraArgs)</MtouchExtraArgs>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(RuntimeIdentifier)'=='Debug|net8.0-ios|ios-arm64'">
<MtouchExtraArgs>$(Argon2IdLoadMtouchExtraArgs)</MtouchExtraArgs>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-ios|AnyCPU'">
<CreatePackage>false</CreatePackage>
<CodesignProvision>$(ReleaseCodesignProvision)</CodesignProvision>
<CodesignKey>$(ReleaseCodesignKey)</CodesignKey>
<CodesignEntitlements>Platforms\iOS\Entitlements.plist</CodesignEntitlements>
<UseInterpreter>true</UseInterpreter>
<MtouchExtraArgs>$(Argon2IdLoadMtouchExtraArgs)</MtouchExtraArgs>
</PropertyGroup>
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
<!--This is needed for PCLCrypto to work correctly-->
<TrimmerRootAssembly Include="System.Security.Cryptography" />
</ItemGroup>
<ItemGroup>
<AndroidNativeLibrary Include="Platforms\Android\lib\arm64-v8a\libargon2.so" />
<AndroidNativeLibrary Include="Platforms\Android\lib\armeabi-v7a\libargon2.so" />
<AndroidNativeLibrary Include="Platforms\Android\lib\x86\libargon2.so" />
<AndroidNativeLibrary Include="Platforms\Android\lib\x86_64\libargon2.so" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Camera.MAUI" Version="1.4.4" />
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
<PackageReference Include="CsvHelper" Version="30.0.1" />
<PackageReference Include="LiteDB" Version="5.0.17" />
<PackageReference Include="MessagePack" Version="2.5.124" />
<PackageReference Include="MessagePack.MSBuild.Tasks" Version="2.5.124">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="zxcvbn-core" Version="7.0.92" />
<PackageReference Include="CommunityToolkit.Maui" Version="5.2.0" />
<PackageReference Include="Plugin.Fingerprint" Version="3.0.0-beta.1" />
<PackageReference Include="SkiaSharp.Views.Maui.Controls" Version="2.88.4-preview.84" />
<PackageReference Include="SkiaSharp.Views.Maui.Controls.Compatibility" Version="2.88.4-preview.84" />
<PackageReference Include="FFImageLoadingCompat.Maui" Version="0.1.1" />
<PackageReference Include="AsyncAwaitBestPractices.MVVM" Version="6.0.6" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="PCLCrypto" Version="2.1.40-alpha" />
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Effects\" />
<Folder Include="Resources\" />
<Folder Include="Resources\AppIcon\" />
<Folder Include="Resources\Splash\" />
<Folder Include="Platforms\Android\Accessibility\" />
<Folder Include="Platforms\Android\Autofill\" />
<Folder Include="Platforms\Android\Push\" />
<Folder Include="Platforms\Android\Receivers\" />
<Folder Include="Platforms\Android\Services\" />
<Folder Include="Platforms\Android\Tiles\" />
<Folder Include="Platforms\Android\Utilities\" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.18" />
<PackageReference Include="Xamarin.AndroidX.Activity.Ktx" Version="1.7.2.1" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android' AND !$(DefineConstants.Contains(FDROID))">
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet" Version="118.0.1.5" />
<PackageReference Include="Xamarin.Firebase.Messaging" Version="123.1.2.2" />
</ItemGroup>
<ItemGroup Condition="!$(DefineConstants.Contains(FDROID))">
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="5.0.3" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Platforms\Android\Resources\mipmap-mdpi\ic_launcher.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-mdpi\ic_launcher_round.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-xxhdpi\ic_launcher.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-xxhdpi\ic_launcher_round.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-xxxhdpi\ic_launcher.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-xxxhdpi\ic_launcher_round.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-hdpi\ic_launcher.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-hdpi\ic_launcher_round.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-mdpi\ic_launcher.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-mdpi\ic_launcher_round.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-hdpi\ic_launcher.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-hdpi\ic_launcher_round.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-xxxhdpi\ic_launcher.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-xxxhdpi\ic_launcher_round.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-xxhdpi\ic_launcher.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-xxhdpi\ic_launcher_round.png" />
<BundleResource Include="Platforms\Android\Resources\drawable-xhdpi\logo_legacy.png" />
<BundleResource Include="Platforms\Android\Resources\drawable-xhdpi\yubikey.png" />
<BundleResource Include="Platforms\Android\Resources\drawable-xhdpi\logo_white_legacy.png" />
<BundleResource Include="Platforms\Android\Resources\drawable-xxhdpi\logo_legacy.png" />
<BundleResource Include="Platforms\Android\Resources\drawable-xxhdpi\yubikey.png" />
<BundleResource Include="Platforms\Android\Resources\drawable-xxhdpi\logo_white_legacy.png" />
<BundleResource Include="Platforms\Android\Resources\drawable-hdpi\logo_legacy.png" />
<BundleResource Include="Platforms\Android\Resources\drawable-hdpi\yubikey.png" />
<BundleResource Include="Platforms\Android\Resources\drawable-hdpi\logo_white_legacy.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-xhdpi\ic_launcher.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-xhdpi\ic_launcher_round.png" />
<BundleResource Include="Platforms\iOS\Resources\logo.png" />
<BundleResource Include="Platforms\iOS\Resources\logo_white%402x.png" />
<BundleResource Include="Platforms\iOS\Resources\more_vert%402x.png" />
<BundleResource Include="Platforms\iOS\Resources\logo_white%403x.png" />
<BundleResource Include="Platforms\iOS\Resources\logo%403x.png" />
<BundleResource Include="Platforms\iOS\Resources\more_vert%403x.png" />
<BundleResource Include="Platforms\iOS\Resources\more_vert.png" />
<BundleResource Include="Platforms\iOS\Resources\logo_white.png" />
<BundleResource Include="Platforms\iOS\Resources\logo%402x.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\iOS.Core\iOS.Core.csproj" Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'" />
<ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup>
<ItemGroup>
<!-- App Icon -->
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
</ItemGroup>
<ItemGroup>
<MauiImage Include="Resources\cog_settings.svg">
<BaseSize>24,24</BaseSize>
</MauiImage>
<MauiImage Include="Resources\ext_act.png" />
<MauiImage Include="Resources\ext_more.png" />
<MauiImage Include="Resources\ext_use.png" />
<MauiImage Include="Resources\generate.svg">
<BaseSize>24,24</BaseSize>
</MauiImage>
<MauiImage Include="Resources\info.svg">
<BaseSize>24,24</BaseSize>
</MauiImage>
<MauiImage Include="Resources\lock.svg">
<BaseSize>24,24</BaseSize>
</MauiImage>
<MauiImage Include="Resources\login.svg">
<BaseSize>24,24</BaseSize>
</MauiImage>
<MauiImage Include="Resources\logo_white.png" />
<MauiImage Include="Resources\logo.png" />
<MauiImage Include="Resources\more_vert.svg">
<BaseSize>24,24</BaseSize>
</MauiImage>
<MauiImage Include="Resources\more.svg">
<BaseSize>24,24</BaseSize>
</MauiImage>
<MauiImage Include="Resources\plus.svg" TintColor="#FFFFFFFF">
<BaseSize>24,24</BaseSize>
</MauiImage>
<MauiImage Include="Resources\send.svg">
<BaseSize>24,24</BaseSize>
</MauiImage>
<MauiImage Include="Resources\yubikey.png" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios' AND '$(IncludeBitwardeniOSExtensions)' == 'True'">
<ProjectReference Include="..\iOS.Autofill\iOS.Autofill.csproj">
<IsAppExtension>true</IsAppExtension>
<IsWatchApp>false</IsWatchApp>
</ProjectReference>
<ProjectReference Include="..\iOS.Extension\iOS.Extension.csproj">
<IsAppExtension>true</IsAppExtension>
<IsWatchApp>false</IsWatchApp>
</ProjectReference>
<ProjectReference Include="..\iOS.ShareExtension\iOS.ShareExtension.csproj">
<IsAppExtension>true</IsAppExtension>
<IsWatchApp>false</IsWatchApp>
</ProjectReference>
</ItemGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net8.0-ios' AND '$(IncludeBitwardenWatchOSApp)' == 'True'">
<WatchAppBuildPath Condition=" '$(Configuration)' == 'Debug' ">$(Home)/Library/Developer/Xcode/DerivedData/bitwarden-acgkbpwvmebfiofokotvoerzkqcl/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="'$(RuntimeIdentifier)'!='ios-arm64'">watchsimulator</WatchAppConfiguration>
<WatchAppConfiguration Condition="'$(RuntimeIdentifier)'=='ios-arm64'">watchos</WatchAppConfiguration>
<WatchAppBundleFullPath Condition=" '$(Configuration)' == 'Debug' ">$(WatchAppBuildPath)/$(Configuration)-$(WatchAppConfiguration)/$(WatchAppBundle)</WatchAppBundleFullPath>
<WatchAppBundleFullPath Condition=" '$(Configuration)' != 'Debug' ">$(WatchAppBuildPath)/$(WatchAppBundle)</WatchAppBundleFullPath>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net8.0-ios' AND Exists('$(WatchAppBundleFullPath)') ">
<_ResolvedWatchAppReferences Include="$(WatchAppBundleFullPath)" />
</ItemGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net8.0-ios' AND Exists('$(WatchAppBundleFullPath)') ">
<CreateAppBundleDependsOn>
_CopyWatchOS2AppsToBundle;
$(CreateAppBundleDependsOn);
</CreateAppBundleDependsOn>
</PropertyGroup>
<ItemGroup>
<GoogleServicesJson Include="Platforms\Android\google-services.json" />
<GoogleServicesJson Include="Platforms\Android\google-services.json.enc" />
</ItemGroup>
<ItemGroup>
<None Remove="Platforms\iOS\Resources\logo.png" />
<None Remove="Platforms\iOS\Resources\logo_white%402x.png" />
<None Remove="Platforms\iOS\Resources\more_vert%402x.png" />
<None Remove="Platforms\iOS\Resources\logo_white%403x.png" />
<None Remove="Platforms\iOS\Resources\logo%403x.png" />
<None Remove="Platforms\iOS\Resources\more_vert%403x.png" />
<None Remove="Platforms\iOS\Resources\more_vert.png" />
<None Remove="Platforms\iOS\Resources\logo_white.png" />
<None Remove="Platforms\iOS\Resources\logo%402x.png" />
</ItemGroup>
</Project>

View File

@@ -1,543 +0,0 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Models;
using Bit.App.Pages;
using Bit.App.Resources;
using Bit.App.Services;
using Bit.App.Utilities;
using Bit.App.Utilities.AccountManagement;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Response;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace Bit.App
{
public partial class App : Application, IAccountsManagerHost
{
public const string POP_ALL_AND_GO_TO_TAB_GENERATOR_MESSAGE = "popAllAndGoToTabGenerator";
public const string POP_ALL_AND_GO_TO_TAB_MYVAULT_MESSAGE = "popAllAndGoToTabMyVault";
public const string POP_ALL_AND_GO_TO_TAB_SEND_MESSAGE = "popAllAndGoToTabSend";
public const string POP_ALL_AND_GO_TO_AUTOFILL_CIPHERS_MESSAGE = "popAllAndGoToAutofillCiphers";
private readonly IBroadcasterService _broadcasterService;
private readonly IMessagingService _messagingService;
private readonly IStateService _stateService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly ISyncService _syncService;
private readonly IAuthService _authService;
private readonly IDeviceActionService _deviceActionService;
private readonly IFileService _fileService;
private readonly IAccountsManager _accountsManager;
private readonly IPushNotificationService _pushNotificationService;
private readonly IConfigService _configService;
private static bool _isResumed;
// these variables are static because the app is launching new activities on notification click, creating new instances of App.
private static bool _pendingCheckPasswordlessLoginRequests;
private static object _processingLoginRequestLock = new object();
public App(AppOptions appOptions)
{
Options = appOptions ?? new AppOptions();
if (Options.IosExtension)
{
Current = this;
return;
}
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_authService = ServiceContainer.Resolve<IAuthService>("authService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_fileService = ServiceContainer.Resolve<IFileService>();
_accountsManager = ServiceContainer.Resolve<IAccountsManager>("accountsManager");
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>();
_configService = ServiceContainer.Resolve<IConfigService>();
_accountsManager.Init(() => Options, this);
Bootstrap();
_broadcasterService.Subscribe(nameof(App), async (message) =>
{
try
{
if (message.Command == "showDialog")
{
var details = message.Data as DialogDetails;
var confirmed = true;
var confirmText = string.IsNullOrWhiteSpace(details.ConfirmText) ?
AppResources.Ok : details.ConfirmText;
Device.BeginInvokeOnMainThread(async () =>
{
if (!string.IsNullOrWhiteSpace(details.CancelText))
{
confirmed = await Current.MainPage.DisplayAlert(details.Title, details.Text, confirmText,
details.CancelText);
}
else
{
await Current.MainPage.DisplayAlert(details.Title, details.Text, confirmText);
}
_messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
});
}
else if (message.Command == AppHelpers.RESUMED_MESSAGE_COMMAND)
{
if (Device.RuntimePlatform == Device.iOS)
{
ResumedAsync().FireAndForget();
}
}
else if (message.Command == "slept")
{
if (Device.RuntimePlatform == Device.iOS)
{
await SleptAsync();
}
}
else if (message.Command == "migrated")
{
await Task.Delay(1000);
await _accountsManager.NavigateOnAccountChangeAsync();
}
else if (message.Command == POP_ALL_AND_GO_TO_TAB_GENERATOR_MESSAGE ||
message.Command == POP_ALL_AND_GO_TO_TAB_MYVAULT_MESSAGE ||
message.Command == POP_ALL_AND_GO_TO_TAB_SEND_MESSAGE ||
message.Command == POP_ALL_AND_GO_TO_AUTOFILL_CIPHERS_MESSAGE ||
message.Command == DeepLinkContext.NEW_OTP_MESSAGE)
{
if (message.Command == DeepLinkContext.NEW_OTP_MESSAGE)
{
Options.OtpData = new OtpData((string)message.Data);
}
await Device.InvokeOnMainThreadAsync(async () =>
{
if (Current.MainPage is TabsPage tabsPage)
{
while (tabsPage.Navigation.ModalStack.Count > 0)
{
await tabsPage.Navigation.PopModalAsync(false);
}
if (message.Command == POP_ALL_AND_GO_TO_AUTOFILL_CIPHERS_MESSAGE)
{
Current.MainPage = new NavigationPage(new CipherSelectionPage(Options));
}
else if (message.Command == POP_ALL_AND_GO_TO_TAB_MYVAULT_MESSAGE)
{
Options.MyVaultTile = false;
tabsPage.ResetToVaultPage();
}
else if (message.Command == POP_ALL_AND_GO_TO_TAB_GENERATOR_MESSAGE)
{
Options.GeneratorTile = false;
tabsPage.ResetToGeneratorPage();
}
else if (message.Command == POP_ALL_AND_GO_TO_TAB_SEND_MESSAGE)
{
tabsPage.ResetToSendPage();
}
else if (message.Command == DeepLinkContext.NEW_OTP_MESSAGE)
{
tabsPage.ResetToVaultPage();
await tabsPage.Navigation.PushModalAsync(new NavigationPage(new CipherSelectionPage(Options)));
}
}
});
}
else if (message.Command == "convertAccountToKeyConnector")
{
Device.BeginInvokeOnMainThread(async () =>
{
await Application.Current.MainPage.Navigation.PushModalAsync(
new NavigationPage(new RemoveMasterPasswordPage()));
});
}
else if (message.Command == Constants.ForceUpdatePassword)
{
Device.BeginInvokeOnMainThread(async () =>
{
await Application.Current.MainPage.Navigation.PushModalAsync(
new NavigationPage(new UpdateTempPasswordPage()));
});
}
else if (message.Command == Constants.ForceSetPassword)
{
await Device.InvokeOnMainThreadAsync(() => Application.Current.MainPage.Navigation.PushModalAsync(
new NavigationPage(new SetPasswordPage(orgIdentifier: (string)message.Data))));
}
else if (message.Command == "syncCompleted")
{
await _configService.GetAsync(true);
}
else if (message.Command == Constants.PasswordlessLoginRequestKey
|| message.Command == "unlocked"
|| message.Command == AccountsManagerMessageCommands.ACCOUNT_SWITCH_COMPLETED)
{
lock (_processingLoginRequestLock)
{
// lock doesn't allow for async execution
CheckPasswordlessLoginRequestsAsync().Wait();
}
}
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
});
}
private async Task CheckPasswordlessLoginRequestsAsync()
{
if (!_isResumed)
{
_pendingCheckPasswordlessLoginRequests = true;
return;
}
_pendingCheckPasswordlessLoginRequests = false;
if (await _vaultTimeoutService.IsLockedAsync())
{
return;
}
var notification = await _stateService.GetPasswordlessLoginNotificationAsync();
if (notification == null)
{
return;
}
if (await CheckShouldSwitchActiveUserAsync(notification))
{
return;
}
// Delay to wait for the vault page to appear
await Task.Delay(2000);
// if there is a request modal opened ignore all incoming requests
if (App.Current.MainPage.Navigation.ModalStack.Any(p => p is NavigationPage navPage && navPage.CurrentPage is LoginPasswordlessPage))
{
return;
}
var loginRequestData = await _authService.GetPasswordlessLoginRequestByIdAsync(notification.Id);
var page = new LoginPasswordlessPage(new LoginPasswordlessDetails()
{
PubKey = loginRequestData.PublicKey,
Id = loginRequestData.Id,
IpAddress = loginRequestData.RequestIpAddress,
Email = await _stateService.GetEmailAsync(),
FingerprintPhrase = loginRequestData.FingerprintPhrase,
RequestDate = loginRequestData.CreationDate,
DeviceType = loginRequestData.RequestDeviceType,
Origin = loginRequestData.Origin
});
await _stateService.SetPasswordlessLoginNotificationAsync(null);
_pushNotificationService.DismissLocalNotification(Constants.PasswordlessNotificationId);
if (!loginRequestData.IsExpired)
{
await Device.InvokeOnMainThreadAsync(() => Application.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(page)));
}
}
private async Task<bool> CheckShouldSwitchActiveUserAsync(PasswordlessRequestNotification notification)
{
var activeUserId = await _stateService.GetActiveUserIdAsync();
if (notification.UserId == activeUserId)
{
return false;
}
var notificationUserEmail = await _stateService.GetEmailAsync(notification.UserId);
Device.BeginInvokeOnMainThread(async () =>
{
try
{
var result = await _deviceActionService.DisplayAlertAsync(AppResources.LogInRequested, string.Format(AppResources.LoginAttemptFromXDoYouWantToSwitchToThisAccount, notificationUserEmail), AppResources.Cancel, AppResources.Ok);
if (result == AppResources.Ok)
{
await _stateService.SetActiveUserAsync(notification.UserId);
_messagingService.Send(AccountsManagerMessageCommands.SWITCHED_ACCOUNT);
}
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
});
return true;
}
public AppOptions Options { get; private set; }
protected async override void OnStart()
{
System.Diagnostics.Debug.WriteLine("XF App: OnStart");
_isResumed = true;
await ClearCacheIfNeededAsync();
Prime();
if (string.IsNullOrWhiteSpace(Options.Uri))
{
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
_stateService);
if (!updated)
{
SyncIfNeeded();
}
}
if (_pendingCheckPasswordlessLoginRequests)
{
_messagingService.Send(Constants.PasswordlessLoginRequestKey);
}
if (Device.RuntimePlatform == Device.Android)
{
await _vaultTimeoutService.CheckVaultTimeoutAsync();
// Reset delay on every start
_vaultTimeoutService.DelayLockAndLogoutMs = null;
}
await _configService.GetAsync();
_messagingService.Send("startEventTimer");
}
protected async override void OnSleep()
{
System.Diagnostics.Debug.WriteLine("XF App: OnSleep");
_isResumed = false;
if (Device.RuntimePlatform == Device.Android)
{
var isLocked = await _vaultTimeoutService.IsLockedAsync();
if (!isLocked)
{
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
}
if (!SetTabsPageFromAutofill(isLocked))
{
ClearAutofillUri();
}
await SleptAsync();
}
}
protected override void OnResume()
{
System.Diagnostics.Debug.WriteLine("XF App: OnResume");
_isResumed = true;
if (_pendingCheckPasswordlessLoginRequests)
{
_messagingService.Send(Constants.PasswordlessLoginRequestKey);
}
if (Device.RuntimePlatform == Device.Android)
{
ResumedAsync().FireAndForget();
}
}
private async Task SleptAsync()
{
await _vaultTimeoutService.CheckVaultTimeoutAsync();
await ClearSensitiveFieldsAsync();
_messagingService.Send("stopEventTimer");
}
private async Task ResumedAsync()
{
await _stateService.CheckExtensionActiveUserAndSwitchIfNeededAsync();
await _vaultTimeoutService.CheckVaultTimeoutAsync();
await ClearSensitiveFieldsAsync();
_messagingService.Send("startEventTimer");
await UpdateThemeAsync();
await ClearCacheIfNeededAsync();
Prime();
SyncIfNeeded();
if (Current.MainPage is NavigationPage navPage && navPage.CurrentPage is LockPage lockPage)
{
await lockPage.PromptBiometricAfterResumeAsync();
}
}
public async Task UpdateThemeAsync()
{
await Device.InvokeOnMainThreadAsync(() =>
{
ThemeManager.SetTheme(Current.Resources);
_messagingService.Send(ThemeManager.UPDATED_THEME_MESSAGE_KEY);
});
}
private async Task ClearSensitiveFieldsAsync()
{
await Device.InvokeOnMainThreadAsync(() =>
{
_messagingService.Send(Constants.ClearSensitiveFields);
});
}
private void SetCulture()
{
// Calendars are removed by linker. ref https://bugzilla.xamarin.com/show_bug.cgi?id=59077
new System.Globalization.ThaiBuddhistCalendar();
new System.Globalization.HijriCalendar();
new System.Globalization.UmAlQuraCalendar();
}
private async Task ClearCacheIfNeededAsync()
{
var lastClear = await _stateService.GetLastFileCacheClearAsync();
if ((DateTime.UtcNow - lastClear.GetValueOrDefault(DateTime.MinValue)).TotalDays >= 1)
{
var task = Task.Run(() => _fileService.ClearCacheAsync());
}
}
private void ClearAutofillUri()
{
if (Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(Options.Uri))
{
Options.Uri = null;
}
}
private bool SetTabsPageFromAutofill(bool isLocked)
{
if (Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(Options.Uri) &&
!Options.FromAutofillFramework)
{
Task.Run(() =>
{
Device.BeginInvokeOnMainThread(() =>
{
Options.Uri = null;
if (isLocked)
{
Current.MainPage = new NavigationPage(new LockPage());
}
else
{
Current.MainPage = new TabsPage();
}
});
});
return true;
}
return false;
}
private void Prime()
{
Task.Run(() =>
{
var word = EEFLongWordList.Instance.List[1];
var parsedDomain = DomainName.TryParse("https://bitwarden.com", out var domainName);
});
}
private void Bootstrap()
{
InitializeComponent();
SetCulture();
ThemeManager.SetTheme(Current.Resources);
Current.RequestedThemeChanged += (s, a) =>
{
UpdateThemeAsync();
};
Current.MainPage = new NavigationPage(new HomePage(Options));
_accountsManager.NavigateOnAccountChangeAsync().FireAndForget();
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
}
private void SyncIfNeeded()
{
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
{
return;
}
Task.Run(async () =>
{
var lastSync = await _syncService.GetLastSyncAsync();
if (lastSync == null || ((DateTime.UtcNow - lastSync) > TimeSpan.FromMinutes(30)))
{
await Task.Delay(1000);
await _syncService.FullSyncAsync(false);
}
});
}
public async Task SetPreviousPageInfoAsync()
{
PreviousPageInfo lastPageBeforeLock = null;
if (Current.MainPage is TabbedPage tabbedPage && tabbedPage.Navigation.ModalStack.Count > 0)
{
var topPage = tabbedPage.Navigation.ModalStack[tabbedPage.Navigation.ModalStack.Count - 1];
if (topPage is NavigationPage navPage)
{
if (navPage.CurrentPage is CipherDetailsPage cipherDetailsPage)
{
lastPageBeforeLock = new PreviousPageInfo
{
Page = "view",
CipherId = cipherDetailsPage.ViewModel.CipherId
};
}
else if (navPage.CurrentPage is CipherAddEditPage cipherAddEditPage && cipherAddEditPage.ViewModel.EditMode)
{
lastPageBeforeLock = new PreviousPageInfo
{
Page = "edit",
CipherId = cipherAddEditPage.ViewModel.CipherId
};
}
}
}
await _stateService.SetPreviousPageInfoAsync(lastPageBeforeLock);
}
public void Navigate(NavigationTarget navTarget, INavigationParams navParams)
{
switch (navTarget)
{
case NavigationTarget.HomeLogin:
Current.MainPage = new NavigationPage(new HomePage(Options));
break;
case NavigationTarget.Login:
if (navParams is LoginNavigationParams loginParams)
{
Current.MainPage = new NavigationPage(new LoginPage(loginParams.Email, Options));
}
break;
case NavigationTarget.Lock:
if (navParams is LockNavigationParams lockParams)
{
Current.MainPage = new NavigationPage(new LockPage(Options, lockParams.AutoPromptBiometric));
}
else
{
Current.MainPage = new NavigationPage(new LockPage(Options));
}
break;
case NavigationTarget.Home:
Current.MainPage = new TabsPage(Options);
break;
case NavigationTarget.AddEditCipher:
Current.MainPage = new NavigationPage(new CipherAddEditPage(appOptions: Options));
break;
case NavigationTarget.AutofillCiphers:
case NavigationTarget.OtpCipherSelection:
Current.MainPage = new NavigationPage(new CipherSelectionPage(Options));
break;
case NavigationTarget.SendAddEdit:
Current.MainPage = new NavigationPage(new SendAddEditPage(Options));
break;
}
}
}
}

View File

@@ -1,67 +0,0 @@
using System;
using Bit.App.Pages;
using Bit.App.Utilities;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public partial class AuthenticatorViewCell : ExtendedGrid
{
public static readonly BindableProperty CipherProperty = BindableProperty.Create(
nameof(Cipher), typeof(CipherView), typeof(AuthenticatorViewCell), default(CipherView), BindingMode.TwoWay);
public static readonly BindableProperty WebsiteIconsEnabledProperty = BindableProperty.Create(
nameof(WebsiteIconsEnabled), typeof(bool?), typeof(AuthenticatorViewCell));
public static readonly BindableProperty TotpSecProperty = BindableProperty.Create(
nameof(TotpSec), typeof(long), typeof(AuthenticatorViewCell));
public AuthenticatorViewCell()
{
InitializeComponent();
}
public Command CopyCommand { get; set; }
public CipherView Cipher
{
get => GetValue(CipherProperty) as CipherView;
set => SetValue(CipherProperty, value);
}
public bool? WebsiteIconsEnabled
{
get => (bool)GetValue(WebsiteIconsEnabledProperty);
set => SetValue(WebsiteIconsEnabledProperty, value);
}
public long TotpSec
{
get => (long)GetValue(TotpSecProperty);
set => SetValue(TotpSecProperty, value);
}
public bool ShowIconImage
{
get => WebsiteIconsEnabled ?? false
&& !string.IsNullOrWhiteSpace(Cipher.Login?.Uri)
&& IconImageSource != null;
}
private string _iconImageSource = string.Empty;
public string IconImageSource
{
get
{
if (_iconImageSource == string.Empty) // default value since icon source can return null
{
_iconImageSource = IconImageHelper.GetLoginIconImage(Cipher);
}
return _iconImageSource;
}
}
}
}

View File

@@ -1,180 +0,0 @@
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Bit.Core.Utilities;
using SkiaSharp;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class AvatarImageSource : StreamImageSource
{
private readonly string _text;
private readonly string _id;
private readonly string _color;
public override bool Equals(object obj)
{
if (obj is null)
{
return false;
}
if (obj is AvatarImageSource avatar)
{
return avatar._id == _id && avatar._text == _text && avatar._color == _color;
}
return base.Equals(obj);
}
public override int GetHashCode() => _id?.GetHashCode() ?? _text?.GetHashCode() ?? -1;
public AvatarImageSource(string userId = null, string name = null, string email = null, string color = null)
{
_id = userId;
_text = name;
if (string.IsNullOrWhiteSpace(_text))
{
_text = email;
}
_color = color;
}
public override Func<CancellationToken, Task<Stream>> Stream => GetStreamAsync;
private Task<Stream> GetStreamAsync(CancellationToken userToken = new CancellationToken())
{
OnLoadingStarted();
userToken.Register(CancellationTokenSource.Cancel);
var result = Draw();
OnLoadingCompleted(CancellationTokenSource.IsCancellationRequested);
return Task.FromResult(result);
}
private Stream Draw()
{
string chars;
string upperCaseText = null;
if (string.IsNullOrEmpty(_text))
{
chars = "..";
}
else if (_text?.Length > 1)
{
upperCaseText = _text.ToUpper();
chars = GetFirstLetters(upperCaseText, 2);
}
else
{
chars = upperCaseText = _text.ToUpper();
}
var bgColor = _color ?? CoreHelpers.StringToColor(_id ?? upperCaseText, "#33ffffff");
var textColor = CoreHelpers.TextColorFromBgColor(bgColor);
var size = 50;
using (var bitmap = new SKBitmap(size * 2,
size * 2,
SKImageInfo.PlatformColorType,
SKAlphaType.Premul))
{
using (var canvas = new SKCanvas(bitmap))
{
canvas.Clear(SKColors.Transparent);
using (var paint = new SKPaint
{
IsAntialias = true,
Style = SKPaintStyle.Fill,
StrokeJoin = SKStrokeJoin.Miter,
Color = SKColor.Parse(bgColor)
})
{
var midX = canvas.LocalClipBounds.Size.ToSizeI().Width / 2;
var midY = canvas.LocalClipBounds.Size.ToSizeI().Height / 2;
var radius = midX - midX / 5;
using (var circlePaint = new SKPaint
{
IsAntialias = true,
Style = SKPaintStyle.Fill,
StrokeJoin = SKStrokeJoin.Miter,
Color = SKColor.Parse(bgColor)
})
{
canvas.DrawCircle(midX, midY, radius, circlePaint);
var typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Normal);
var textSize = midX / 1.3f;
using (var textPaint = new SKPaint
{
IsAntialias = true,
Style = SKPaintStyle.Fill,
Color = SKColor.Parse(textColor),
TextSize = textSize,
TextAlign = SKTextAlign.Center,
Typeface = typeface
})
{
var rect = new SKRect();
textPaint.MeasureText(chars, ref rect);
canvas.DrawText(chars, midX, midY + rect.Height / 2, textPaint);
using (var img = SKImage.FromBitmap(bitmap))
{
var data = img.Encode(SKEncodedImageFormat.Png, 100);
return data?.AsStream(true);
}
}
}
}
}
}
}
private string GetFirstLetters(string data, int charCount)
{
var sanitizedData = data.Trim();
var parts = sanitizedData.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length > 1 && charCount <= 2)
{
var text = string.Empty;
for (var i = 0; i < charCount; i++)
{
text += parts[i][0];
}
return text;
}
if (sanitizedData.Length > 2)
{
return sanitizedData.Substring(0, 2);
}
return sanitizedData;
}
private Color StringToColor(string str)
{
if (str == null)
{
return Color.FromHex("#33ffffff");
}
var hash = 0;
for (var i = 0; i < str.Length; i++)
{
hash = str[i] + ((hash << 5) - hash);
}
var color = "#FF";
for (var i = 0; i < 3; i++)
{
var value = (hash >> (i * 8)) & 0xff;
var base16 = "00" + Convert.ToString(value, 16);
color += base16.Substring(base16.Length - 2);
}
return Color.FromHex(color);
}
}
}

View File

@@ -1,82 +0,0 @@
using System;
using System.Windows.Input;
using Bit.App.Abstractions;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public partial class CipherViewCell : ExtendedGrid
{
private const int ICON_COLUMN_DEFAULT_WIDTH = 40;
private const int ICON_IMAGE_DEFAULT_WIDTH = 22;
public static readonly BindableProperty CipherProperty = BindableProperty.Create(
nameof(Cipher), typeof(CipherView), typeof(CipherViewCell), default(CipherView), BindingMode.OneWay);
public static readonly BindableProperty WebsiteIconsEnabledProperty = BindableProperty.Create(
nameof(WebsiteIconsEnabled), typeof(bool?), typeof(CipherViewCell));
public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create(
nameof(ButtonCommand), typeof(ICommand), typeof(CipherViewCell));
public CipherViewCell()
{
InitializeComponent();
var fontScale = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService").GetSystemFontSizeScale();
_iconColumn.Width = new GridLength(ICON_COLUMN_DEFAULT_WIDTH * fontScale, GridUnitType.Absolute);
_iconImage.WidthRequest = ICON_IMAGE_DEFAULT_WIDTH * fontScale;
_iconImage.HeightRequest = ICON_IMAGE_DEFAULT_WIDTH * fontScale;
}
public bool? WebsiteIconsEnabled
{
get => (bool)GetValue(WebsiteIconsEnabledProperty);
set => SetValue(WebsiteIconsEnabledProperty, value);
}
public CipherView Cipher
{
get => GetValue(CipherProperty) as CipherView;
set => SetValue(CipherProperty, value);
}
public ICommand ButtonCommand
{
get => GetValue(ButtonCommandProperty) as ICommand;
set => SetValue(ButtonCommandProperty, value);
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == CipherProperty.PropertyName)
{
if (Cipher == null)
{
return;
}
BindingContext = new CipherViewCellViewModel(Cipher, WebsiteIconsEnabled ?? false);
}
else if (propertyName == WebsiteIconsEnabledProperty.PropertyName)
{
if (Cipher == null)
{
return;
}
((CipherViewCellViewModel)BindingContext).WebsiteIconsEnabled = WebsiteIconsEnabled ?? false;
}
}
private void MoreButton_Clicked(object sender, EventArgs e)
{
var cipher = ((sender as MiButton)?.BindingContext as CipherViewCellViewModel)?.Cipher;
if (cipher != null)
{
ButtonCommand?.Execute(cipher);
}
}
}
}

View File

@@ -1,51 +0,0 @@
using Bit.App.Utilities;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
namespace Bit.App.Controls
{
public class CipherViewCellViewModel : ExtendedViewModel
{
private CipherView _cipher;
private bool _websiteIconsEnabled;
private string _iconImageSource = string.Empty;
public CipherViewCellViewModel(CipherView cipherView, bool websiteIconsEnabled)
{
Cipher = cipherView;
WebsiteIconsEnabled = websiteIconsEnabled;
}
public CipherView Cipher
{
get => _cipher;
set => SetProperty(ref _cipher, value);
}
public bool WebsiteIconsEnabled
{
get => _websiteIconsEnabled;
set => SetProperty(ref _websiteIconsEnabled, value);
}
public bool ShowIconImage
{
get => WebsiteIconsEnabled
&& !string.IsNullOrWhiteSpace(Cipher.LaunchUri)
&& IconImageSource != null;
}
public string IconImageSource
{
get
{
if (_iconImageSource == string.Empty) // default value since icon source can return null
{
_iconImageSource = IconImageHelper.GetIconImage(Cipher);
}
return _iconImageSource;
}
}
}
}

View File

@@ -1,20 +0,0 @@
using System.Linq;
using Xamarin.CommunityToolkit.Converters;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedCollectionView : CollectionView
{
public string ExtraDataForLogging { get; set; }
}
public class SelectionChangedEventArgsConverter : BaseNullableConverterOneWay<SelectionChangedEventArgs, object>
{
public override object? ConvertFrom(SelectionChangedEventArgs? value)
{
return value?.CurrentSelection.FirstOrDefault();
}
}
}

View File

@@ -1,8 +0,0 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedGrid : Grid
{
}
}

View File

@@ -1,19 +0,0 @@
using Bit.App.Utilities;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedSearchBar : SearchBar
{
public ExtendedSearchBar()
{
if (Device.RuntimePlatform == Device.iOS)
{
if (ThemeManager.UsingLightTheme)
{
TextColor = Color.Black;
}
}
}
}
}

View File

@@ -1,8 +0,0 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedStackLayout : StackLayout
{
}
}

View File

@@ -1,42 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Frame xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Controls.IconLabelButton"
xmlns:controls="clr-namespace:Bit.App.Controls"
x:Name="_iconLabelButton"
HeightRequest="45"
Padding="1"
StyleClass="btn-icon-secondary"
BackgroundColor="{Binding IconLabelBorderColor, Source={x:Reference _iconLabelButton}}"
BorderColor="Transparent"
HasShadow="False">
<Frame.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ButtonCommand, Source={x:Reference _iconLabelButton}}" />
</Frame.GestureRecognizers>
<Frame
Margin="0"
Padding="0"
CornerRadius="{Binding CornerRadius, Source={x:Reference _iconLabelButton}}"
BackgroundColor="{Binding IconLabelBackgroundColor, Source={x:Reference _iconLabelButton}}"
BorderColor="Transparent"
IsClippedToBounds="True"
HasShadow="False">
<StackLayout
Orientation="Horizontal"
HorizontalOptions="Center">
<controls:IconLabel
VerticalOptions="Center"
HorizontalTextAlignment="Center"
FontSize="Large"
TextColor="{Binding IconLabelColor, Source={x:Reference _iconLabelButton}}"
Text="{Binding Icon, Source={x:Reference _iconLabelButton}}">
</controls:IconLabel>
<Label
VerticalOptions="Center"
HorizontalTextAlignment="Center"
TextColor="{Binding IconLabelColor, Source={x:Reference _iconLabelButton}}"
FontSize="Medium"
Text="{Binding Label, Source={x:Reference _iconLabelButton}}"/>
</StackLayout>
</Frame>
</Frame>

View File

@@ -1,20 +0,0 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class MonoEntry : Entry
{
public MonoEntry()
{
switch (Device.RuntimePlatform)
{
case Device.iOS:
FontFamily = "Menlo-Regular";
break;
case Device.Android:
FontFamily = "RobotoMono_Regular.ttf#Roboto Mono";
break;
}
}
}
}

View File

@@ -1,20 +0,0 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class MonoLabel : Label
{
public MonoLabel()
{
switch (Device.RuntimePlatform)
{
case Device.iOS:
FontFamily = "Menlo-Regular";
break;
case Device.Android:
FontFamily = "RobotoMono_Regular.ttf#Roboto Mono";
break;
}
}
}
}

View File

@@ -1,68 +0,0 @@
using System;
using Bit.App.Abstractions;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public partial class SendViewCell : ExtendedGrid
{
public static readonly BindableProperty SendProperty = BindableProperty.Create(
nameof(Send), typeof(SendView), typeof(SendViewCell), default(SendView), BindingMode.OneWay);
public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create(
nameof(ButtonCommand), typeof(Command<SendView>), typeof(SendViewCell));
public static readonly BindableProperty ShowOptionsProperty = BindableProperty.Create(
nameof(ShowOptions), typeof(bool), typeof(SendViewCell), true, BindingMode.OneWay);
public SendViewCell()
{
InitializeComponent();
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_iconColumn.Width = new GridLength(40 * deviceActionService.GetSystemFontSizeScale(), GridUnitType.Absolute);
}
public SendView Send
{
get => GetValue(SendProperty) as SendView;
set => SetValue(SendProperty, value);
}
public Command<SendView> ButtonCommand
{
get => GetValue(ButtonCommandProperty) as Command<SendView>;
set => SetValue(ButtonCommandProperty, value);
}
public bool ShowOptions
{
get => (bool)GetValue(ShowOptionsProperty);
set => SetValue(ShowOptionsProperty, value);
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == SendProperty.PropertyName)
{
if (Send == null)
{
return;
}
BindingContext = new SendViewCellViewModel(Send, ShowOptions);
}
}
private void MoreButton_Clicked(object sender, EventArgs e)
{
var send = ((sender as MiButton)?.BindingContext as SendViewCellViewModel)?.Send;
if (send != null)
{
ButtonCommand?.Execute(send);
}
}
}
}

View File

@@ -1,11 +0,0 @@
using Xamarin.Forms;
namespace Bit.App.Effects
{
public class FabShadowEffect : RoutingEffect
{
public FabShadowEffect()
: base("Bitwarden.FabShadowEffect")
{ }
}
}

View File

@@ -1,11 +0,0 @@
using Xamarin.Forms;
namespace Bit.App.Effects
{
public class FixedSizeEffect : RoutingEffect
{
public FixedSizeEffect()
: base("Bitwarden.FixedSizeEffect")
{ }
}
}

View File

@@ -1,12 +0,0 @@
using System;
using Xamarin.Forms;
namespace Bit.App.Effects
{
public class NoEmojiKeyboardEffect : RoutingEffect
{
public NoEmojiKeyboardEffect()
: base("Bitwarden.NoEmojiKeyboardEffect")
{ }
}
}

View File

@@ -1,13 +0,0 @@
using System;
using Xamarin.Forms;
namespace Bit.App.Effects
{
public class RemoveFontPaddingEffect : RoutingEffect
{
public RemoveFontPaddingEffect()
: base("Bitwarden.RemoveFontPaddingEffect")
{ }
}
}

View File

@@ -1,33 +0,0 @@
using Xamarin.Forms;
namespace Bit.App.Effects
{
public enum ScrollContentInsetAdjustmentBehavior
{
Automatic,
ScrollableAxes,
Never,
Always
}
public class ScrollViewContentInsetAdjustmentBehaviorEffect : RoutingEffect
{
public static readonly BindableProperty ContentInsetAdjustmentBehaviorProperty =
BindableProperty.CreateAttached("ContentInsetAdjustmentBehavior", typeof(ScrollContentInsetAdjustmentBehavior), typeof(ScrollViewContentInsetAdjustmentBehaviorEffect), ScrollContentInsetAdjustmentBehavior.Automatic);
public static ScrollContentInsetAdjustmentBehavior GetContentInsetAdjustmentBehavior(BindableObject view)
{
return (ScrollContentInsetAdjustmentBehavior)view.GetValue(ContentInsetAdjustmentBehaviorProperty);
}
public static void SetContentInsetAdjustmentBehavior(BindableObject view, ScrollContentInsetAdjustmentBehavior value)
{
view.SetValue(ContentInsetAdjustmentBehaviorProperty, value);
}
public ScrollViewContentInsetAdjustmentBehaviorEffect()
: base($"Bitwarden.{nameof(ScrollViewContentInsetAdjustmentBehaviorEffect)}")
{
}
}
}

View File

@@ -1,11 +0,0 @@
using Xamarin.Forms;
namespace Bit.App.Effects
{
public class TabBarEffect : RoutingEffect
{
public TabBarEffect()
: base("Bitwarden.TabBarEffect")
{ }
}
}

41
src/App/MauiProgram.cs Normal file
View File

@@ -0,0 +1,41 @@
namespace Bit.App
{
public class MauiProgram
{
public static MauiApp CreateMauiApp()
{
return Core.MauiProgram.ConfigureMauiAppBuilder(
effects =>
{
#if IOS
iOS.Core.Utilities.iOSCoreHelpers.ConfigureMAUIEffects(effects);
#endif
},
handlers =>
{
#if ANDROID
Bit.App.Handlers.EntryHandlerMappings.Setup();
Bit.App.Handlers.EditorHandlerMappings.Setup();
Bit.App.Handlers.LabelHandlerMappings.Setup();
Bit.App.Handlers.PickerHandlerMappings.Setup();
Bit.App.Handlers.SearchBarHandlerMappings.Setup();
Bit.App.Handlers.SwitchHandlerMappings.Setup();
Bit.App.Handlers.DatePickerHandlerMappings.Setup();
Bit.App.Handlers.SliderHandlerMappings.Setup();
Bit.App.Handlers.StepperHandlerMappings.Setup();
Bit.App.Handlers.TimePickerHandlerMappings.Setup();
Bit.App.Handlers.ButtonHandlerMappings.Setup();
Bit.App.Handlers.ToolbarHandlerMappings.Setup();
handlers.AddHandler(typeof(Bit.App.Controls.HybridWebView), typeof(Bit.App.Handlers.HybridWebViewHandler));
handlers.AddHandler(typeof(Bit.App.Pages.TabsPage), typeof(Bit.App.Handlers.CustomTabbedPageHandler));
handlers.AddHandler(typeof(Bit.App.Controls.ExtendedDatePicker), typeof(Bit.App.Handlers.ExtendedDatePickerHandler));
#else
iOS.Core.Utilities.iOSCoreHelpers.ConfigureMAUIHandlers(handlers);
#endif
},
initUseMauiApp: true
).Build();
}
}
}

View File

@@ -1,145 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.HomePage"
xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="pages:HomeViewModel"
x:Name="_page"
Title="{Binding PageTitle}">
<ContentPage.BindingContext>
<pages:HomeViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<controls:ExtendedToolbarItem
x:Name="_accountAvatar"
x:Key="accountAvatar"
IconImageSource="{Binding AvatarImageSource}"
Command="{Binding Source={x:Reference _accountListOverlay}, Path=ToggleVisibililtyCommand}"
Order="Primary"
Priority="-1"
UseOriginalImage="True"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Account}"
AutomationId="AccountIconButton" />
<ToolbarItem x:Name="_closeButton" Text="{u:I18n Close}" Command="{Binding CloseCommand}" Order="Primary" Priority="-1"/>
</ContentPage.ToolbarItems>
<ContentPage.Resources>
<ResourceDictionary>
<u:InverseBoolConverter x:Key="inverseBool" />
<StackLayout x:Name="_mainLayout" x:Key="mainLayout" Spacing="30" Padding="20, 50, 20, 0">
<Image
x:Name="_logo"
Source="logo.png"
VerticalOptions="Center" />
<Label Text="{u:I18n LoginOrCreateNewAccount}"
StyleClass="text-lg"
HorizontalTextAlignment="Center"/>
<StackLayout
StyleClass="box-row">
<Label
Text="{u:I18n EmailAddress}"
StyleClass="box-label" />
<Entry
x:Name="_email"
Text="{Binding Email}"
Keyboard="Email"
StyleClass="box-value"
ReturnType="Go"
ReturnCommand="{Binding ContinueCommand}"
AutomationId="EmailAddressEntry"
>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{DynamicResource MutedColor}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Entry>
<StackLayout
Orientation="Horizontal"
Margin="0, 6, 0 ,0">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding ShowEnvironmentPickerCommand}" />
</StackLayout.GestureRecognizers>
<Label
Text="{Binding RegionText}"
FontSize="13"
TextColor="{DynamicResource MutedColor}"
VerticalOptions="Center"
VerticalTextAlignment="Center"/>
<controls:IconLabel
Text="{Binding SelectedEnvironmentName}"
FontSize="13"
TextColor="{DynamicResource PrimaryColor}"
VerticalOptions="Center"
VerticalTextAlignment="Center"
AutomationId="RegionSelectorDropdown"/>
</StackLayout>
<StackLayout
Orientation="Horizontal"
Margin="0, 20, 0 ,0">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding RememberEmailCommand}" />
</StackLayout.GestureRecognizers>
<Label
Text="{u:I18n RememberMe}"
StyleClass="text-sm"
HorizontalOptions="FillAndExpand"
VerticalOptions="Center"
VerticalTextAlignment="Center"
/>
<Switch
Scale="0.8"
IsToggled="{Binding RememberEmail}"
VerticalOptions="Center"
AutomationId="RememberMeSwitch"
/>
</StackLayout>
</StackLayout>
<Button Text="{u:I18n Continue}"
StyleClass="btn-primary"
IsEnabled="{Binding CanContinue}"
Command="{Binding ContinueCommand}"
AutomationId="ContinueButton"
/>
<Label FormattedText="{Binding CreateAccountText}"
Margin="0, 10"
StyleClass="box-footer-label"
AutomationId="CreateAccountLabel">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding CreateAccountCommand}" />
</Label.GestureRecognizers>
</Label>
</StackLayout>
</ResourceDictionary>
</ContentPage.Resources>
<AbsoluteLayout
x:Name="_absLayout"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<ContentView
x:Name="_mainContent"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
</ContentView>
<controls:AccountSwitchingOverlayView
x:Name="_accountListOverlay"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
AbsoluteLayout.LayoutFlags="All"
MainPage="{Binding Source={x:Reference _page}}"
BindingContext="{Binding AccountSwitchingOverlayViewModel}"/>
</AbsoluteLayout>
</pages:BaseContentPage>

View File

@@ -1,162 +0,0 @@
using System;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.App.Utilities;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
namespace Bit.App.Pages
{
public class BaseContentPage : ContentPage
{
private IStateService _stateService;
private IDeviceActionService _deviceActionService;
protected int ShowModalAnimationDelay = 400;
protected int ShowPageAnimationDelay = 100;
public BaseContentPage()
{
if (Device.RuntimePlatform == Device.iOS)
{
On<iOS>().SetUseSafeArea(true);
On<iOS>().SetModalPresentationStyle(UIModalPresentationStyle.FullScreen);
}
}
public DateTime? LastPageAction { get; set; }
public bool IsThemeDirty { get; set; }
protected async override void OnAppearing()
{
base.OnAppearing();
if (IsThemeDirty)
{
UpdateOnThemeChanged();
}
await SaveActivityAsync();
}
public bool DoOnce(Action action = null, int milliseconds = 1000)
{
if (LastPageAction.HasValue && (DateTime.UtcNow - LastPageAction.Value).TotalMilliseconds < milliseconds)
{
// Last action occurred recently.
return false;
}
LastPageAction = DateTime.UtcNow;
action?.Invoke();
return true;
}
public virtual Task UpdateOnThemeChanged()
{
IsThemeDirty = false;
return Task.CompletedTask;
}
protected void SetActivityIndicator(ContentView targetView = null)
{
var indicator = new ActivityIndicator
{
IsRunning = true,
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.Center,
Color = ThemeManager.GetResourceColor("PrimaryColor"),
};
if (targetView != null)
{
targetView.Content = indicator;
}
else
{
Content = indicator;
}
}
protected async Task LoadOnAppearedAsync(View sourceView, bool fromModal, Func<Task> workFunction,
ContentView targetView = null)
{
async Task DoWorkAsync()
{
await workFunction.Invoke();
if (sourceView != null)
{
if (targetView != null)
{
targetView.Content = sourceView;
}
else
{
Content = sourceView;
}
}
}
if (Device.RuntimePlatform == Device.iOS)
{
await DoWorkAsync();
return;
}
await Task.Run(async () =>
{
await Task.Delay(fromModal ? ShowModalAnimationDelay : ShowPageAnimationDelay);
Device.BeginInvokeOnMainThread(async () => await DoWorkAsync());
});
}
protected void RequestFocus(InputView input)
{
Task.Run(async () =>
{
await Task.Delay(ShowModalAnimationDelay);
Device.BeginInvokeOnMainThread(() => input.Focus());
});
}
protected async Task<bool> ShowAccountSwitcherAsync()
{
return await _stateService.GetActiveUserIdAsync() != null;
}
protected async Task<AvatarImageSource> GetAvatarImageSourceAsync(bool useCurrentActiveAccount = true)
{
if (useCurrentActiveAccount)
{
var user = await _stateService.GetActiveUserCustomDataAsync(a => (a?.Profile?.UserId, a?.Profile?.Name, a?.Profile?.Email, a?.Profile?.AvatarColor));
return new AvatarImageSource(user.UserId, user.Name, user.Email, user.AvatarColor);
}
return new AvatarImageSource();
}
private void SetServices()
{
if (_stateService == null)
{
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
}
if (_deviceActionService == null)
{
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
}
}
private async Task SaveActivityAsync()
{
SetServices();
if (await _stateService.GetActiveUserIdAsync() == null)
{
// Fresh install and/or all users logged out won't have an active user, skip saving last active time
return;
}
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
}
}
}

View File

@@ -1,73 +0,0 @@
using System;
using System.Threading.Tasks;
using System.Windows.Input;
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Exceptions;
using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public abstract class BaseViewModel : ExtendedViewModel
{
private string _pageTitle = string.Empty;
private AvatarImageSource _avatar;
private LazyResolve<IDeviceActionService> _deviceActionService = new LazyResolve<IDeviceActionService>();
private LazyResolve<IPlatformUtilsService> _platformUtilsService = new LazyResolve<IPlatformUtilsService>();
private LazyResolve<ILogger> _logger = new LazyResolve<ILogger>();
public string PageTitle
{
get => _pageTitle;
set => SetProperty(ref _pageTitle, value);
}
public AvatarImageSource AvatarImageSource
{
get => _avatar ?? new AvatarImageSource();
set => SetProperty(ref _avatar, value);
}
public ContentPage Page { get; set; }
protected void HandleException(Exception ex, string message = null)
{
if (ex is ApiException apiException && apiException.Error != null)
{
message = apiException.Error.GetSingleMessage();
}
Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
{
await _deviceActionService.Value.HideLoadingAsync();
await _platformUtilsService.Value.ShowDialogAsync(message ?? AppResources.GenericErrorMessage);
}).FireAndForget();
_logger.Value.Exception(ex);
}
protected AsyncCommand CreateDefaultAsyncCommnad(Func<Task> execute, Func<object, bool> canExecute = null)
{
return new AsyncCommand(execute,
canExecute,
ex => HandleException(ex),
allowsMultipleExecutions: false);
}
protected async Task<bool> HasConnectivityAsync()
{
if (Connectivity.NetworkAccess == NetworkAccess.None)
{
await _platformUtilsService.Value.ShowDialogAsync(
AppResources.InternetConnectionRequiredMessage,
AppResources.InternetConnectionRequiredTitle);
return false;
}
return true;
}
}
}

View File

@@ -1,19 +0,0 @@
using System.Windows.Input;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public partial class AutofillSettingsPageViewModel
{
public bool SupportsiOSAutofill => Device.RuntimePlatform == Device.iOS && _deviceActionService.SupportsAutofillServices();
public ICommand GoToPasswordAutofillCommand { get; private set; }
public ICommand GoToAppExtensionCommand { get; private set; }
private void InitIOSCommands()
{
GoToPasswordAutofillCommand = CreateDefaultAsyncCommnad(() => Page.Navigation.PushModalAsync(new NavigationPage(new AutofillPage())));
GoToAppExtensionCommand = CreateDefaultAsyncCommnad(() => Page.Navigation.PushModalAsync(new NavigationPage(new ExtensionPage())));
}
}
}

View File

@@ -1,134 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<pages:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
x:Class="Bit.App.Pages.LoginPasswordlessRequestsListPage"
xmlns:pages="clr-namespace:Bit.App.Pages"
x:DataType="pages:LoginPasswordlessRequestsListViewModel"
xmlns:models="clr-namespace:Bit.Core.Models.Response;assembly=BitwardenCore"
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities"
Title="{Binding PageTitle}">
<ContentPage.BindingContext>
<pages:LoginPasswordlessRequestsListViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
</ContentPage.ToolbarItems>
<ContentPage.Resources>
<ResourceDictionary>
<u:DateTimeConverter x:Key="dateTime" />
<u:InverseBoolConverter x:Key="inverseBool" />
<xct:ItemSelectedEventArgsConverter x:Key="ItemSelectedEventArgsConverter" />
<controls:SelectionChangedEventArgsConverter x:Key="SelectionChangedEventArgsConverter" />
<DataTemplate
x:Key="loginRequestTemplate"
x:DataType="models:PasswordlessLoginResponse">
<Grid
Padding="10, 0"
RowSpacing="0"
RowDefinitions="*, Auto, *, 10"
ColumnDefinitions="*, *"
AutomationId="LoginRequestCell">
<Label
Text="{u:I18n FingerprintPhrase}"
FontSize="Small"
Padding="0, 10, 0 ,0"
FontAttributes="Bold"/>
<controls:MonoLabel
FormattedText="{Binding FingerprintPhrase}"
Grid.Row="1"
Grid.ColumnSpan="2"
FontSize="Small"
Padding="0, 5, 0, 10"
VerticalTextAlignment="Center"
TextColor="{DynamicResource FingerprintPhrase}"
AutomationId="FingerprintPhraseLabel" />
<Label
Grid.Row="2"
HorizontalOptions="Start"
HorizontalTextAlignment="Start"
Text="{Binding RequestDeviceType}"
StyleClass="list-header-sub"
AutomationId="RequestDeviceLabel" />
<Label
Grid.Row="2"
Grid.Column="1"
HorizontalOptions="End"
HorizontalTextAlignment="End"
Text="{Binding CreationDate, Converter={StaticResource dateTime}}"
StyleClass="list-header-sub"
AutomationId="RequestDateLabel" />
<BoxView
StyleClass="list-section-separator-top, list-section-separator-top-platform"
VerticalOptions="End"
Grid.Row="3"
Grid.ColumnSpan="2"/>
</Grid>
</DataTemplate>
<StackLayout
x:Key="mainLayout"
x:Name="_mainLayout"
Padding="0, 10">
<RefreshView
IsRefreshing="{Binding IsRefreshing}"
Command="{Binding RefreshCommand}"
VerticalOptions="FillAndExpand"
BackgroundColor="{DynamicResource BackgroundColor}">
<StackLayout>
<Image
x:Name="_emptyPlaceholder"
Source="empty_login_requests"
HorizontalOptions="Center"
WidthRequest="160"
HeightRequest="160"
Margin="0,70,0,0"
IsVisible="{Binding HasLoginRequests, Converter={StaticResource inverseBool}}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n NoPendingRequests}" />
<controls:CustomLabel
StyleClass="box-label-regular"
Text="{u:I18n NoPendingRequests}"
FontAttributes="{OnPlatform iOS=Bold}"
FontWeight="500"
HorizontalTextAlignment="Center"
Margin="14,10,14,0"/>
<controls:ExtendedCollectionView
ItemsSource="{Binding LoginRequests}"
ItemTemplate="{StaticResource loginRequestTemplate}"
SelectionMode="Single"
IsVisible="{Binding HasLoginRequests}"
ExtraDataForLogging="Login requests page" >
<controls:ExtendedCollectionView.Behaviors>
<xct:EventToCommandBehavior
EventName="SelectionChanged"
Command="{Binding AnswerRequestCommand}"
EventArgsConverter="{StaticResource SelectionChangedEventArgsConverter}" />
</controls:ExtendedCollectionView.Behaviors>
</controls:ExtendedCollectionView>
</StackLayout>
</RefreshView>
<controls:IconLabelButton
VerticalOptions="End"
Margin="10,0"
Icon="{Binding Source={x:Static core:BitwardenIcons.Trash}}"
Label="{u:I18n DeclineAllRequests}"
ButtonCommand="{Binding DeclineAllRequestsCommand}"
IsVisible="{Binding HasLoginRequests}"
AutomationId="DeleteAllRequestsButton" />
</StackLayout>
</ResourceDictionary>
</ContentPage.Resources>
<ContentView
x:Name="_mainContent">
</ContentView>
</pages:BaseContentPage>

View File

@@ -1,121 +0,0 @@
using System;
using System.Threading.Tasks;
using Bit.App.Resources;
using Bit.App.Utilities;
using Bit.Core.Abstractions;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class GroupingsPageTOTPListItem : ExtendedViewModel, IGroupingsPageListItem
{
private readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
private readonly ITotpService _totpService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IClipboardService _clipboardService;
private CipherView _cipher;
private bool _websiteIconsEnabled;
private string _iconImageSource = string.Empty;
private double _progress;
private string _totpSec;
private string _totpCodeFormatted;
private TotpHelper _totpTickHelper;
public GroupingsPageTOTPListItem(CipherView cipherView, bool websiteIconsEnabled)
{
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
Cipher = cipherView;
WebsiteIconsEnabled = websiteIconsEnabled;
CopyCommand = new AsyncCommand(CopyToClipboardAsync,
onException: ex => _logger.Value.Exception(ex),
allowsMultipleExecutions: false);
_totpTickHelper = new TotpHelper(cipherView);
}
public AsyncCommand CopyCommand { get; set; }
public CipherView Cipher
{
get => _cipher;
set => SetProperty(ref _cipher, value);
}
public string TotpCodeFormatted
{
get => _totpCodeFormatted;
set => SetProperty(ref _totpCodeFormatted, value,
additionalPropertyNames: new string[]
{
nameof(TotpCodeFormattedStart),
nameof(TotpCodeFormattedEnd),
});
}
public string TotpSec
{
get => _totpSec;
set => SetProperty(ref _totpSec, value);
}
public double Progress
{
get => _progress;
set => SetProperty(ref _progress, value);
}
public bool WebsiteIconsEnabled
{
get => _websiteIconsEnabled;
set => SetProperty(ref _websiteIconsEnabled, value);
}
public bool ShowIconImage
{
get => WebsiteIconsEnabled
&& !string.IsNullOrWhiteSpace(Cipher.Login?.Uri)
&& IconImageSource != null;
}
public string IconImageSource
{
get
{
if (_iconImageSource == string.Empty) // default value since icon source can return null
{
_iconImageSource = IconImageHelper.GetLoginIconImage(Cipher);
}
return _iconImageSource;
}
}
public string TotpCodeFormattedStart => TotpCodeFormatted?.Split(' ')[0];
public string TotpCodeFormattedEnd => TotpCodeFormatted?.Split(' ')[1];
public async Task CopyToClipboardAsync()
{
await _clipboardService.CopyTextAsync(TotpCodeFormatted?.Replace(" ", string.Empty));
_platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, AppResources.VerificationCodeTotp));
}
public async Task TotpTickAsync()
{
await _totpTickHelper.GenerateNewTotpValues();
MainThread.BeginInvokeOnMainThread(() =>
{
TotpSec = _totpTickHelper.TotpSec;
Progress = _totpTickHelper.Progress;
TotpCodeFormatted = _totpTickHelper.TotpCodeFormatted;
});
}
}
}

View File

@@ -6,7 +6,7 @@ using Android.Views;
using System;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Bit.Droid.Utilities;
using Bit.App.Droid.Utilities;
namespace Bit.Droid.Accessibility
{

View File

@@ -10,8 +10,13 @@ using Android.Runtime;
using Android.Views;
using Android.Views.Accessibility;
using Android.Widget;
using Bit.App.Resources;
using Bit.Core.Resources.Localization;
using Bit.Core;
using Application = Android.App.Application;
using View = Android.Views.View;
using Resource = Bit.Core.Resource;
using Point = Android.Graphics.Point;
using Rect = Android.Graphics.Rect;
namespace Bit.Droid.Accessibility
{
@@ -705,7 +710,7 @@ namespace Bit.Droid.Accessibility
public static LinearLayout GetOverlayView(Context context)
{
var inflater = (LayoutInflater)context.GetSystemService(Context.LayoutInflaterService);
var view = (LinearLayout)inflater.Inflate(Resource.Layout.autofill_listitem, null);
var view = (LinearLayout)inflater.Inflate(Bit.Core.Resource.Layout.autofill_listitem, null);
var text1 = (TextView)view.FindViewById(Resource.Id.text1);
var text2 = (TextView)view.FindViewById(Resource.Id.text2);
var icon = (ImageView)view.FindViewById(Resource.Id.icon);

View File

@@ -9,9 +9,10 @@ using Android.Runtime;
using Android.Views;
using Android.Views.Accessibility;
using Android.Widget;
using Bit.App.Resources;
using Bit.Core.Resources.Localization;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using View = Android.Views.View;
namespace Bit.Droid.Accessibility
{

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2023.12.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2024.4.0" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@@ -43,6 +43,9 @@
<!-- Support for Xamarin.Essentials.Browser.OpenAsync (for Android > 11) -->
<!-- Related docs: https://learn.microsoft.com/en-us/xamarin/essentials/open-browser?tabs=android -->
<queries>
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http" />

View File

@@ -4,14 +4,14 @@ using Android.Content.PM;
using Android.OS;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Bit.Droid.Utilities;
using Bit.App.Droid.Utilities;
namespace Bit.Droid.Autofill
{
[Activity(
NoHistory = true,
LaunchMode = LaunchMode.SingleTop)]
public class AutofillExternalSelectionActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
public class AutofillExternalSelectionActivity : MauiAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{

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