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

Compare commits

...

200 Commits

Author SHA1 Message Date
Federico Maccaroni
7a437a0649 Update ThemeManager.cs to fix build failing 2024-02-12 14:36:41 -03: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
github-actions[bot]
f14be2a3a2 Autosync the updated translations (#2919)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-12-29 00:59:08 +00:00
github-actions[bot]
8ee744b746 Autosync the updated translations (#2918)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-12-28 13:07:30 +00:00
github-actions[bot]
15a03ba573 Autosync the updated translations (#2913)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-12-22 10:20:55 +00:00
Vince Grassia
82711a0235 Merge _cut_rc.yml into version-bump.yml (#2908) 2023-12-18 10:59:41 -07:00
github-actions[bot]
e6635564aa Autosync the updated translations (#2906)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-12-15 15:20:45 +01:00
Vince Grassia
6c078fe343 Update version bump workflow (#2905) 2023-12-15 13:30:15 +01:00
Joseph Flinn
743e71ff92 Fix branch (#2903) 2023-12-13 05:56:34 -05:00
github-actions[bot]
7b579b7aa5 Autosync the updated translations (#2902)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-12-12 20:09:30 +00:00
Joseph Flinn
fe10fd7766 Point workflows to main (#2896) 2023-12-12 11:12:27 -08:00
Vince Grassia
3c0de8aacc Add token to checkout step (#2901) 2023-12-12 09:38:12 -08:00
Vince Grassia
18d9a77f25 Fix version bump workflow on call (#2900) 2023-12-12 08:55:24 -08:00
Vince Grassia
9eca82a62b Update version bump workflow (#2898) 2023-12-12 10:22:22 -05:00
mpbw2
b90e030b8f [PM-4837] Hide TOTP seed copy button when Can view, except password permission set (#2869)
* Hide TOTP seed copy button when Can view, except password permission set

* additional check

* removal of null check
2023-12-11 16:40:09 -05:00
github-actions[bot]
9a28419a4e Autosync the updated translations (#2894)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-12-08 10:56:26 +00:00
github-actions[bot]
f4c468e6a1 Bumped version to 2023.12.1 (#2892)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-12-06 13:35:08 -05:00
github-actions[bot]
2c346eb710 Bumped version to 2023.12.0 (#2891)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-12-06 10:47:08 -05:00
Daniel James Smith
9c0908f7b7 Remove tools ownership for store/google/Publisher (#2890) 2023-12-06 08:15:24 -05:00
Bahasnyldz
827fbbc9ce Add Cromite browser (#2640) 2023-12-04 18:37:34 -03:00
Federico Maccaroni
5b249bed67 PM-5064 Fix lock interaction between biometrics and vault timeout never (#2885) 2023-12-04 12:13:13 -03:00
Federico Maccaroni
afbcb212f6 [PM-4896] Fix null reference exception on the region when setting env urls (#2876)
* PM-4896 Fix null reference exception on the region

* PM-4896 Updated dotnet version to set up in build workflow

* PM-4896 Add NET 3.1.x and NET 7.0.x to Android build

* PM-4896 Reversed to NET 3.1.x  Android build

* PM-4896 Removed changes on build.yml for net version name
2023-12-01 12:30:27 -03:00
github-actions[bot]
a71c28536d Autosync the updated translations (#2884)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-12-01 12:06:17 +01:00
Vince Grassia
ba5fa8a518 Fix Build workflow - Install OpenJDK 11 (#2883) 2023-11-27 17:18:28 -05:00
github-actions[bot]
65ea5574de Autosync the updated translations (#2880)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-11-27 11:36:59 +00:00
github-actions[bot]
f013f69669 Autosync the updated translations (#2872)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-11-19 09:33:27 +01:00
cubemike99
f98dfa6581 [PM-4800] Send item domain name to fastmail (#2867)
* Send item domain name to fastmail

- Added a metadata field (forDomain:) to the Fastmail Forwarder API
  request that's set to the domain name of the item being added to the
  vault, or to "" if the username generator is being used in standalone
  mode. This allows the user's Fastmail account to display the domain
  name for the username that was generated.

* Minor changes for readability

* dotnet format

---------

Co-authored-by:  Audrey  <ajensen@bitwarden.com>
2023-11-17 17:17:25 -05:00
Federico Maccaroni
0723999652 [PM-4857] Hide "Allow screen capture" on iOS (#2873)
* PM-4857 Hide "Allow screen capture" on iOS

* PM-4857 Try to fix FDroid build by forcing .NET 7

* PM-4857 Try to fix FDroid build by forcing .NET 7, adding rollForward and disable allowPrerelease to the global json

* PM-4857 Changed global.json to use 7.0.400 so FDroid pass in CI
2023-11-17 19:14:25 -03:00
github-actions[bot]
96343eccf7 Autosync the updated translations (#2863)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-11-10 07:22:22 +00:00
André Bispo
793c5fef6f [PM-3273][PM-4679] New owner/admin permission on login (#2837)
* [PM-3273] Add property for password set. Add labels. Update sync service.

* [PM-3273] Set password needs set in state. Read value on sync and nav to page.

* [PM-3273] Add navigation to Set Password on vault landing if needed.

* [PM-3273] Update SetPasswordPage copy

* [PM-3273] Add ManageResetPassword to Org Permissions, handle it on sync.

* [PM-3273] Change user has master password state when set master password is complete.

* [PM-3273] Code clean up

* [PM-3273] Remove unnecessary property from account profile

* [PM-3273] Add check for remembered org identifier

* [PM-4679] Added logging calls for future checks.

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2023-11-09 17:21:00 +00:00
Vince Grassia
3a13ba4efa Update 'master' to 'main' (#2861) 2023-11-09 10:18:34 -05:00
André Bispo
c5288d3921 [PM-891] Remove check for biometrics from IsLocked method. (#2853) 2023-11-07 16:46:31 +00:00
André Bispo
9506595fdd [PM-2671] Update mobile client to use regions (#2798)
* [PM-2671] Update mobile client to use regions

* [PM-2671] Refactor

* [PM-2671] Move migration of region to migration service.

* [PM-2671] Move comment

* [PM-2671] Change method name

* [PM-2671] Change method name on usages

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2023-11-07 12:15:32 +00:00
André Bispo
7a65bf7fd7 [PM-3340] Update timeout action for users without master password. (#2818)
* [PM-3340] Update timeout action for users without master password.

* [PM-3340] PR fixes and refactor

* [PM-3340] Raise command can execute.

* [PM-3340] Fix converter name

* [PM-3340] Fix variable naming

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2023-11-06 15:28:54 +00:00
github-actions[bot]
d0ce89fedb Autosync the updated translations (#2851)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-11-06 15:11:43 +00:00
Daniel James Smith
3c94ea4579 Assign CrowdinPRs to team-tools-dev (#2860)
Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
2023-11-06 16:03:06 +01:00
github-actions[bot]
658c1eaf64 Bumped version to 2023.10.1 (#2849)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-11-01 14:07:28 -04:00
github-actions[bot]
02b0265767 Bumped version to 2023.10.0 (#2847)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-10-31 11:07:07 -04:00
github-actions[bot]
bd2481b3e4 Autosync the updated translations (#2840)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-10-30 08:21:49 +00:00
Jake Fink
12c72b2833 migrate old enc key (#2732) 2023-10-27 12:19:41 -04:00
aj-rosado
2e5fb414b5 [PM-1835] Add ForwardEmail alias to Username Generator (#2803)
* Add ForwardEmail alias to Username Generator

* remove unnecessary initializer

* Corrected order of alias Generators

* PM-4307 - Trigger ForwardEmailDomainName PropertyChanged after initialization
2023-10-26 13:58:07 +01:00
github-actions[bot]
4dda7a6634 Autosync the updated translations (#2836)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-10-23 09:51:53 +00:00
github-actions[bot]
a1808f64b3 Autosync the updated translations (#2833)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-10-20 08:53:18 +00:00
Federico Maccaroni
142c3145f0 PM-4404 Added CreationDate to Fido2Credential objects and updated the UI bindings accordingly (#2832) 2023-10-19 17:46:26 -03:00
Federico Maccaroni
72de17bd1d PM-4314 Removed move passkey to organization duplicate check (#2828) 2023-10-17 12:36:54 -03:00
André Bispo
ed3467515e [PM-3531] Add missing automation ids. (#2814) 2023-10-14 00:39:57 +01:00
Jake Fink
21fc56457d fix isLocked logic and add comments (#2802) 2023-10-13 13:41:52 -04:00
ifernandezdiaz
bc2eb212a6 Adding missing ids (#2823)
* Adding missing ids

* Fixing repeated IDs
2023-10-13 12:12:47 -03:00
André Bispo
a1912526c2 [PM-3532] Code clean up. DeviceType delete. (#2762) 2023-10-13 14:51:19 +01:00
github-actions[bot]
9d0209751c Autosync the updated translations (#2822)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-10-13 08:19:14 +00:00
Federico Maccaroni
f2936c95fa [PM-4054] Rename Fido2Key to Fido2Credential (#2821)
* PM-4054 Renamed Fido2Key to Fido2Credential on the entire codebase

* PM-4054 Renamed file Fido2KeyApi to Fido2CredentialApi
2023-10-12 16:51:19 -03:00
mpbw2
bb2f1f0f5f [PM-3741] [PM-3750] Improvements to local storage handling (#2795)
* [PM-3741] [PM-3750] Improvements to local storage handling

* Update src/Android/MainActivity.cs

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

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2023-10-12 08:58:11 -04:00
github-actions[bot]
5a0c2115a1 Bumped version to 2023.9.3 (#2820)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-10-11 15:42:03 -07:00
aj-rosado
a67f50b145 Added CultureInfo in char.ToLower at GetServiceRegistrationName method (#2810) 2023-10-10 16:22:39 +01:00
github-actions[bot]
757e5ea647 Autosync the updated translations (#2805)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-10-06 07:46:57 +00:00
André Bispo
b23f29511c [PM-868] Re-open app to item could crash the app (#2813)
* [PM-868] Check for previous page before loading vault. Remove exception throw.

* [PM-868] Continue to throw exceptions
2023-10-04 15:48:04 +01:00
Federico Maccaroni
71731bb9b7 PM-2658 Removed deprecated strings resources. (#2812) 2023-10-03 15:40:55 -03:00
aj-rosado
f2be840a7d Added GetOrDeriveMasterKey to UserVerificationService (#2808) 2023-10-03 12:54:22 +01:00
André Bispo
685e0f407a [PM-2915] Fix config service null exception error bug (#2778) 2023-10-02 20:43:42 +01:00
Federico Maccaroni
bbef0f8c93 PM-4138 Remove hyphen from clip-board => clipboard on resources. (#2804) 2023-09-28 12:55:18 -04:00
Federico Maccaroni
3cdf5ccd3b [PM-115] Cipher key encryption update (#2421)
* PM-115 Added new cipher key and encryption/decryption mechanisms on cipher

* PM-115 fix format

* PM-115 removed ForceKeyRotation from new cipher encryption model given that another approach will be taken

* [PM-1690] Added minimum server version restriction to cipher key encryption (#2463)

* PM-1690 added minimum server version restriction to cipher key encryption and also change the force key rotation flag

* PM-1690 Updated min server version for new cipher encryption key and fixed configService registration

* PM-1690 removed forcekeyrotation

* PM-115 Temporarily Changed cipher key new encryption config to help testing (this change should be reseted eventually)

* PM-2456 Fix attachment encryption on new cipher item encryption model (#2556)

* PM-2531 Fix new cipher encryption on adding attachments on ciphers with no item level key (#2559)

* PM-115 Changed temporarily cipher key encryption min server version to 2023.6.0 to test

* PM-115 Reseted cipher key encryption minimum server version to 2023.5.0 and disable new cipher key on local cipher creation

* Added Key value to the cipher export model (#2628)

* Update Constants.cs

Updated minimum encryption server version to 2023.9.0 so QA can test its behavior

* PM-115 Fix file format

* PM-115 Changed new encryption off and minimum new encryption server version to 2023.8.0 for testing purposes

* PM-115 Changed CIpher key encryption minimum server version to 2023.9.0

* PM-3737 Remove suffix on client version sent to server (#2779)

* PM-115 QA testing server min version and enable new cipher key encryption

* PM-115 Disable new cipher encryption creation and change minimum server encryption version to 2023.9.1

---------

Co-authored-by: aj-rosado <109146700+aj-rosado@users.noreply.github.com>
2023-09-28 10:00:20 -03:00
Federico Maccaroni
e97a37222a [PM-2658] Settings Reorganization feature (#2702)
* [PM-2658] Settings Reorganization Init (#2697)

* PM-2658 Started settings reorganization (settings main + vault + about)

* PM-2658 Added settings controls based on templates and implemented OtherSettingsPage

* PM-2658 Fix format

* [PM-3512] Settings Appearance (#2703)

* PM-3512 Implemented new Appearance Settings

* PM-3512 Fix format

* [PM-3510] Implement Account Security Settings view (#2714)

* PM-3510 Implemented Security settings view

* PM-3510 Fix format

* PM-3510 Added empty placeholder to pending login requests and also improved a11y on security settings view.

* PM-3511 Implemented autofill settings view (#2735)

* [PM-3695] Add Connect to Watch to Other settings (#2736)

* PM-3511 Implemented autofill settings view

* PM-3695 Add Connect to watch setting to other settings view

* [PM-3693] Clear old Settings approach (#2737)

* PM-3511 Implemented autofill settings view

* PM-3693 Remove old Settings approach

* PM-3845 Fix default dark theme description verbiage (#2759)

* PM-3839 Fix allow screen capture and submit crash logs to init their state when the page appears (#2760)

* PM-3834 Fix dialogs strings on settings (#2758)

* [PM-3834] Fix import items link (#2782)

* PM-3834 Fix import items link

* PM-3834 Fix import items link, removed old link.

* [PM-4092] Fix vault timeout policies on new Settings (#2796)

* PM-4092 Fix vault timeout policy on settings for disabling controls and reset timeout when surpassing maximum

* PM-4092 Removed testing hardcoding of policy data
2023-09-27 16:26:12 -03:00
André Bispo
218a30b510 [PM-3446] User without MP, item with MP does not show on Android keyboard for autofill (#2764)
* [PM-3446] Check if user has mp and allow autofill to use items with mp re-prompt
2023-09-26 17:25:47 +01:00
github-actions[bot]
828043ec97 Bumped version to 2023.9.2 (#2797)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-09-26 11:05:07 -04:00
André Bispo
b25c8b0842 [PM-3893] Make PreLogin and Register endpoint use identity endpoints (#2772) 2023-09-25 16:28:58 +01:00
Federico Maccaroni
a4a0d31fc6 [PM-3811] Passkeys unification (#2774)
* PM-3811 Unified passkeys view and moved both inside Login as an array of FIdo2Key

* PM-3811 Passkeys unification => updated cipher details view an helpers

* PM-3811 Updated passkeys creation date time format
2023-09-22 14:55:35 +00:00
ifernandezdiaz
6ef6cf5d84 Adding missing IDs (#2786) 2023-09-22 11:24:30 -03:00
github-actions[bot]
597f629920 Autosync the updated translations (#2785)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-09-22 08:04:57 +00:00
github-actions[bot]
b8cef16711 Bumped version to 2023.9.1 (#2784)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-09-20 18:36:32 -04:00
Jake Fink
c4f6ae9077 [PM-3726] prevent legacy user login (#2769)
* [PM-3726] prevent legacy user login

* [PM-3726] prevent unlock or auto key migration if legacy user

* [PM-3726] add legacy checks to lock page and refactor

* [PM-3726] rethrow exception from pin

* formatting

* [PM-3726] add changes to LockViewController, consolidate logout calls

* formatting

* [PM-3726] pr feedback

* generate resx

* formatting
2023-09-20 15:56:51 -04:00
github-actions[bot]
8b9658d2c5 Bumped version to 2023.9.0 (#2783)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-09-20 12:34:30 -04:00
André Bispo
43bf0fbdb3 [PM-3086] Account switcher endpoint use domain string for Bitwarden production environments (#2773) 2023-09-19 10:35:37 +01:00
André Bispo
11922c6f49 [PM-3522] Keep variable value after logout. (#2761) 2023-09-19 10:33:01 +01:00
André Bispo
a6f05338c2 [PM-3393] Excessive Invalid Biometric unlock attempts should automatically log out TDE users (#2747)
* [PM-3393] Log user out on biometric exceed attempts

* [PM-3393] Move duplicated code to AppHelpers

* [PM-3393] Update copy on new pop up

* [PM-3393] Moved VaultTimeoutService to LazyResolve.

* [PM-3382] Change IVaultTimeoutService for messaging

* [PM-3393] Use default values.
2023-09-19 10:32:23 +01:00
Federico Maccaroni
b932824b5a Make dept-development-mobile default code owner (#2780) 2023-09-18 18:23:16 -03:00
github-actions[bot]
efd1671f48 Autosync the updated translations (#2771)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-09-14 14:14:00 +00:00
André Bispo
3e2005e5ed [PM-3606] TDE user with 2FA isn't able to autofill on iOS (#2723)
* [PM-3606] Fix 2FA for autofill

* [PM-3606] Fix autofill when user doesn't have a login method available.

* [PM-3606] PR fixes

* [PM-3606] Add logout logic to other extension projects

* [PM-3606] Move code to base class.

* Transform into property instead of field

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

* Remove double ";"

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

* [PM-3606] Fix iOS extension by changing base class of LockPasswordViewController

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2023-09-09 17:38:14 -04:00
Will Browning
382eee2ed3 [PM-3556] Change anonaddy to addy io (#2711)
* Update AppResources.af.resx

* Update AnonAddy references

* Reverted AnonAddy to AddyIo refactor, keeping text and url changes

---------

Co-authored-by: Andre Rosado <arosado@bitwarden.com>
2023-09-08 15:23:51 +01:00
github-actions[bot]
b0f1dd00ee Autosync the updated translations (#2751)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-09-08 09:33:37 +00:00
André Bispo
5961a001ab [PM-3551] Expired SSO token cached (#2718) 2023-09-08 07:48:37 +01:00
André Bispo
9026dd10e5 [PM-3593] Fix enable biometric on autofill when there is not MP (#2717) 2023-09-07 16:30:46 +01:00
ifernandezdiaz
355261679d Adding missing IDs for Set Password and Update Password pages (#2748) 2023-09-07 11:24:48 -03:00
ifernandezdiaz
7f14ec9b5d QA-508 - Build app for automation CI (#2705)
* Adding build steps for .app

* Uploading .app artifact

* Fixing ARCHIVE_PATH variable

* Fixing missing OutputPath

* Fixing Bitwarden .app file name

* Fixing wrong .app location

* Adding Fede's suggestion

* Update .github/workflows/build.yml

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

---------

Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>
2023-09-06 14:25:59 -03:00
Opeyemi
0c72626916 UPDATE: all workflows (#2743) 2023-09-06 15:30:47 +01:00
André Bispo
f21fae7fea [PM-3382] User cannot select Email as a secondary 2FA option following SSO (#2719)
* [PM-3382] Update mobile client to receive and use SsoEmail2faSessionToken

* [PM-3382] Fix null 2fa email with local email on MP login.
2023-09-06 10:26:11 +01:00
github-actions[bot]
6d4792bc24 Autosync the updated translations (#2741)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-09-01 12:46:27 +00:00
mpbw2
dbadf8c56f [PM-3222] Migration of data from LiteDB to shared pref storage (#2724)
* Migration of data from LiteDB to shared pref storage

* tweaks
2023-08-30 10:55:20 -04:00
André Bispo
4d0f9d1c03 [PM-3543] [PM-3607] Fix password re-prompt when editing and on autofill. (#2713)
* [PM-3543] [PM-3507] Fix password re-prompt when editing and on autofill.
2023-08-30 09:38:46 +01:00
André Bispo
68759fc608 [PM-3547] Change logic to set user key for inactive account (#2715) 2023-08-29 10:28:51 +01:00
github-actions[bot]
47be3d6aef Bumped version to 2023.8.1 (#2730)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-08-28 16:34:38 -04:00
github-actions[bot]
7ec5c8ccfd Bumped version to 2023.8.0 (#2725)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-08-25 19:52:35 -04:00
Jake Fink
819aabb330 don't clear key needed for bio/auto migration in pin migration (#2721) 2023-08-25 09:47:37 -04:00
github-actions[bot]
9c7ff853d7 Autosync the updated translations (#2720)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-08-25 09:03:57 +00:00
mpbw2
e30f9903d1 fix for TDE pref naming collision (#2712)
* fix for TDE pref naming collision

* fix case
2023-08-22 15:51:11 -04:00
André Bispo
249406e3a8 [PM-3545] Fix biometric unlock for autofill (#2710)
* [PM-3545] Fix biometric unlock for autofill

* [PM-3545] Reuse existing method
2023-08-21 20:30:22 +01:00
github-actions[bot]
8cae840c68 Autosync the updated translations (#2704)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-08-19 14:00:16 +00:00
André Bispo
e274c04107 [PM-3513] Show error SSO policy (#2707)
* [PM-3513] Show API error when SSO policy is enforced.
2023-08-18 23:05:52 +01:00
Robyn MacCallum
7043be67dd Update CODEOWNERS (#2701)
* Update CODEOWNERS

* Update CODEOWNERS
2023-08-18 11:20:26 -04:00
Federico Maccaroni
afb8c515d6 [PM-3071] Remove share on save toggle on Send view (#2659)
* PM-3071 Removed share on save toggle on Send view and now it's done automatically, same for copy after saving from the Share extension

* PM-3071 Fix alignments on Share extension send view
2023-08-17 16:42:08 -03:00
Todd Martin
bfcfd367dd Trusted Device Encryption feature (#2656)
* [PM-1208] Add Device approval options screen. View model waiting for additional logic to be added.

* [PM-1208] Add device related api endpoint. Add AccoundDecryptOptions model and property to user Account.

* [PM-1208] Add continue button and not you option

* [PM-1379] add DeviceTrustCryptoService with establish trust logic (#2535)

* [PM-1379] add DeviceCryptoService with establish trust logic

* PM-1379 update api location and other minor refactors

* pm-1379 fix encoding

* update trusted device keys api call to Put

* [PM-1379] rename DeviceCryptoService to DeviceTrustCryptoService
- refactors to prevent side effects

* [PM-1379] rearrange methods in DeviceTrustCryptoService

* [PM-1379] rearrange methods in abstraction

* [PM-1379] deconstruct tuples

* [PM-1379] remove extra tasks

* [PM-2583] Answer auth request with mp field as null if doesn't have it. (#2609)

* [PM-2287][PM-2289][PM-2293] Approval Options (#2608)

* [PM-2293] Add AuthRequestType to PasswordlessLoginPage.

* [PM-2293] Add Actions to ApproveWithDevicePage

* [PM-2293] Change screen text based on AuthRequestType

* [PM-2293] Refactor AuthRequestType enum. Add label. Remove unnecessary actions.

* [PM-2293] Change boolean variable expression.

* [PM-2293] Trust device after admin request login.

* code format

* [PM-2287] Add trust device to master password unlock. Change trust device method. Remove email from SSO login page.

* [PM-2293] Fix state variable get set.

* [PM-2287][PM-2289][PM-2293] Rename method

* [PM-1201] Change timeout actions available based on hasMasterPassword (#2610)

* [PM-1201] Change timeout actions available based on hasMasterPassword

* [PM-2731] add user key and master key types

* [PM-2713] add new state for new keys and obsolete old ones
- UserKey
- MasterKey
- UserKeyMasterKey (enc UserKey from User Table)

* [PM-271] add UserKey and MasterKey support to crypto service

* [PM-2713] rename key hash to password hash & begin add methods to crypto service

* [PM-2713] continue organizing crypto service

* [PM-2713] more updates to crypto service

* [PM-2713] add new pin methods to state service

* [PM-2713] fix signature of GetUserKeyPin

* [PM-2713] add make user key method to crypto service

* [PM-2713] refresh pin key when setting user key

* [PM-2713] use new MakeMasterKey method

* [PM-2713] add toggle method to crypto service for keys

* [PM-2713] converting calls to new crypto service api

* [PM-2713] add migration for pin on lock screens

* [PM-2713] more conversions to new crypto service api

* [PM-2713] convert cipher service and others to crypto service api

* [PM-2713] More conversions to crypto api

* [PM-2713] use new crypto service api in auth service

* [PM-2713] remove unused cached values in crypto service

* [PM-2713] set decrypt and set user key in login helper

* fix bad merge

* Update crypto service api call to fix build

* [PM-1208] Fix app resource file

* [PM-1208] Fix merge

* [PM-1208] Fix merge

* [PM-2713] optimize async code in crypto service

* [PM-2713] rename password hash to master key hash

* [PM-2713] fix casting issues and pin

* [PM-2713] remove extra comment

* [PM-2713] remove broken casting

* [PM-2297] Login with trusted device (Flow 2) (#2623)

* [PM-2297] Add DecryptUserKeyWithDeviceKey method

* [PM-2297] Add methods to DeviceTrustCryptoService update decryption options model

* [PM-2297] Update account decryption options model

* [PM-2297] Fix TrustedDeviceOption and DeviceResponse model. Change StateService device key get set to have default user id

* [PM-2297] Update navigation to decryption options

* [PM-2297] Add missing action navigations to iOS extensions

* [PM-2297] Fix trust device bug/typo

* [PM-2297] Fix model bug

* [PM-2297] Fix state var crash

* [PM-2297] Add trust device login logic to auth service

* [PM-2297] Refactor auth service key connector code

* [PM-2297] Remove reconciledOptions for deviceKey in state service

* [PM-2297] Remove unnecessary user id params

* [PM-2289] [PM-2293] TDE Login with device Admin Request (#2642)

* [PM-2713] deconstruct new key pair

* [PM-2713] rename PrivateKey methods to UserPrivateKey on crypto service

* [PM-2713] rename PinLockEnum to PinLockType

* [PM-2713] don't pass user key as param when encrypting

* [PM-2713] rename toggle method, don't reset enc user key

* [PM-2713] pr feedback

* [PM-2713] PR feedback

* [PM-2713] rename get pin lock type method

* [PM-2713] revert feedback for build

* [PM-2713] rename state methods

* [PM-2713] combine makeDataEncKey methods

* [PM-2713] consolidate attachment key creation
- also fix ios files missed during symbol rename

* [PM-2713] replace generic with inherited class

* rename account keys to be more descriptive

* [PM-2713] add auto unlock key to mobile

* [PM-1208] Add TDE flows for new users (#2655)

* [PM-1208] Create new user on SSO. Logout if not password is setup or has pending admin auth request.

* [PM-1208] Fix new user UserKey decryption.

* [PM-1208] Add new user continue to vault logic. Auto enrol user on continue.

* [PM-1208] Trust device only if needed

* [PM-1208] Add logic for New User SSO.

* [PM-1208] Add logic for New User SSO (missing file).

* [PM-2713] set user key on set password page

* [PM-2713] set enc user key during kc onboarding

* fix formatting

* [PM-2713] make method async again
- returning null from a task thats not async throws

* [PM-2713] clear service cache when adding new account

* Fix build after merge

* [PM-3313] Fix Android SSO Login (#2663)

* [PM-3313] Catch exception on AuthPendingRequest

* [PM-3313] Fix lock timeout action if user doesn't have a master password.

* code format

* [PM-3313] Null email in Approval Options screen (#2664)

* [PM-3313] Fix null email in approval options screen

* [PM-3320][PM-3321] Fix labels and UI tweaks (#2666)

* [PM-3320] Fix UI copy and remember me default ON.

* [PM-3321] Fix UI on Log in with device screen.

* [PM-3337] Fix admin request deny error (#2669)

* [PM-3342] Not you button logs user out. (#2672)

* [PM-3319] Check for admin request in Lock page (#2668)

* [PM-3319] Ignore admin auth request when choosing mp as decryption option.

* [PM-2289] Change header title based on auth request type (#2670)

* [PM-2289] Change header title based on auth request type

* [PM-3333] Check for purged admin auth requests (#2671)

* [PM-3333] Check for purged admin auth requests

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

---------

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

* [PM-3341] Vault Timeout Action not persisted correctly (#2673)

* [PM-3341] Fix timeout action change when navigating

* [PM-3357] Fix copy for Login Initiated (#2674)

* [PM-3362] Fix auth request approval (#2675)

* [PM-3362] Fix auth request approval

* [PM-3362] Add new exception type

* [PM-3102] Update Master password reprompt to be based on MP instead of Key Connector (#2653)

* PM-3102 Added check to see if a user has master password set replacing previous usage of key connector.

* PM-3102 Fix formatting

* [PM-2713] Final merge from Key Migration branch to TDE Feature branch (#2667)

* [PM-2713] add async to key connector service methods

* [PM-2713] rename ephemeral pin key

* add state for biometric key and accept UserKey instead of string for auto key

* Get UserKey from bio state on unlock

* PM-2713 Fix auto-migrating EncKeyEncrypted into MasterKey encrypted UserKey when requesting DecryptUserKeyWithMasterKeyAsync is called

* renaming bio key and fix build

* PM-3194 Fix biometrics button to be shown on upgrade when no UserKey is present yet

* revert removal of key connector service from auth service

* PM-2713 set user key when using KC

* clear enc user key after migration

* use is true for nullable bool

* PR feedback, refactor kc service

---------

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

* Fix app fresh install user login with master password. (#2676)

* [PM-3303] Fix biometric login after key migration (#2679)

* [PM-3303] Add condition to biometric unlock

* [PM-3381] Fix TDE login 2FA flow (#2678)

* [PM-3381] Check for vault lock on 2FA screen

* [PM-3381] Move logic to ViewModel

* [PM-3381] Fix null vm error

* [PM-3379] Fix key rotation on trusted device. (#2680)

* [PM-3381] Update login flows (#2683)

* [PM-3381] Update login flows

* [PM-3381] Remove _authingWithSso parameter

* PM-3385 Fix MP reprompt item level when no MP hash is stored like logging in with TDE. Also refactor code to be more maintainable (#2687)

* PM-3386 Fix MP reprompt / OTP decision to be also based on the master key hash. (#2688)

* PM-3450 Fix has master password with mp key hash check (#2689)

* [PM-3394] Fix login with device for passwordless approvals (#2686)

* set activeUserId to null when logging in a new account
- Also stop the user key from being set in inactive accounts

* get token for login with device if approving device doesn't have master key

* add comment

* simplify logic

* check for route instead of using isAuthenticated
- we don't clear the user id when logging in new account
- this means we can't trust the state service, so we have to base our logic off the route in login with device

* use authenticated auth request for tde login with device

* [PM-3394] Add authingWithSso parameter to LoginPasswordlessRequestPage.

* pr feedback

* [PM-3394] Refactor condition

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

---------

Co-authored-by: André Bispo <abispo@bitwarden.com>
Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* [PM-3462] Handle force password reset on mobile with TDE (#2694)

* [PM-3462] Handle force password reset on mobile with TDE

* [PM-3462] update references to refactored crypto method
- fix kc bug, we were sending private key instead of user key to server
- rename kc service method to be correct

* [PM-3462] Update TwoFactorPage login logic

* [PM-3462] Added pending admin request check to TwoFactorPage

* [PM-3462] Added new exception types for null keys

---------

Co-authored-by: André Bispo <abispo@bitwarden.com>

* [PM-1029] Fix Async suffix in ApiService. Add UserKeyNullExceptions.

* [PM 3513] Fix passwordless 2fa login with device on mobile (#2700)

* [PM-3513] Fix 2FA for normal login with device with users without mp

* move _userKey

---------

Co-authored-by: André Bispo <abispo@bitwarden.com>

* clear encrypted pin on logout (#2699)

---------

Co-authored-by: André Bispo <abispo@bitwarden.com>
Co-authored-by: Jake Fink <jfink@bitwarden.com>
Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2023-08-17 15:19:35 -04:00
Federico Maccaroni
a23454bc53 [PM-3508] Fix Release iPhoneSimulator configuration for iOS / Extensions (#2698)
* PM-3508 Fix Release iPhoneSimulator configuration for iOS / Extensions

* PM-3508 Fix --deep space on watch app references
2023-08-16 15:55:34 -03:00
mpbw2
6f7100ae4f lib updates (#2696) 2023-08-16 12:48:54 -04:00
ifernandezdiaz
01ac20e6e4 Adding missing AutomationIDs on LoginPasswordlessRequestListPage (#2693) 2023-08-16 11:09:07 -03:00
github-actions[bot]
8474f536ff Autosync the updated translations (#2677)
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>
2023-08-15 13:48:01 +00:00
Daniel James Smith
f426c0e370 Create section for crowdin sync (#2692) 2023-08-15 15:47:08 +02:00
Daniel James Smith
420dc09fd1 Update codeowners (#2691)
* Set team-leads-eng as owners for translations

This is needed to Crowdin sync PRs can be merged.

* Add team-tools as owner of the email-forwarders

* Fix unescaped whitespace

* Remove team-leads-eng from owning English resources
2023-08-15 15:32:57 +02:00
Federico Maccaroni
6d4793d592 [PM-1768] Set up CODEOWNERS file (#2464)
* PM-1768 Add CODEOWNERS file with some initial setup of folders that don't need to be moved and can have their owners assigned already.

* Update CODEOWNERS

Removed entire projects owners cause I didn't consider auth inside of the extensions.
2023-08-10 13:58:03 -03:00
Bernd Schoolmann
eea7c6b7d7 [PM-2901] Synchronize sends on send creation/update/deletion notification (#2606)
* Add sync on send create/update/delete notification

* Update send notifications to only sync sends

* Fix incorrect notification type in PushNotificationListenerService

Co-authored-by: aj-rosado <109146700+aj-rosado@users.noreply.github.com>

* Invert if to improve readability

* Simplify shouldUpdate logic in SyncUpsertSendAsync

* Further simplify SyncService code

* Fix if condition in SyncService

Co-authored-by: aj-rosado <109146700+aj-rosado@users.noreply.github.com>

* Fixed whitespace formatting

---------

Co-authored-by: aj-rosado <109146700+aj-rosado@users.noreply.github.com>
Co-authored-by: Andre Rosado <arosado@bitwarden.com>
2023-08-08 14:59:42 +01:00
Bernd Schoolmann
ec93a61275 [PM-3092] Clarify argon2 ios autofill warning (#2630)
* Clarify argon2 ios warning

* Update Argon2 insufficient memory warning message
2023-08-07 10:41:40 -04:00
Federico Maccaroni
c34d1da6e6 PM-3298 Updated region selector to be logging in on and also its options (#2657) 2023-08-04 11:44:39 -03:00
github-actions[bot]
c4e64e082b Autosync the updated translations (#2660)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-08-04 10:55:27 +00:00
Federico Maccaroni
5aaff1ea20 PM-3249 Removed back button from block autofill uris to be aligned to other views (#2654) 2023-08-02 10:54:01 -03:00
github-actions[bot]
0271a4db4c Autosync the updated translations (#2650)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-07-28 08:19:12 +00:00
Federico Maccaroni
375718f945 PM-3227 Avoid clone on discoverable passkeys. (#2648) 2023-07-27 18:35:09 -03:00
Federico Maccaroni
9eda015371 PM-3165 Finish task completion source when cancelling the dialog tapping on the background. (#2647) 2023-07-27 17:49:52 -03:00
Federico Maccaroni
ea81acb3bf [PM-1575] Display Passkeys (#2523)
* PM-1575 Added new models for Fido2Key

* PM-1575 Added discoverable passkeys and WIP non-discoverable ones

* PM-1575 Fix format

* PM-1575 Added non-discoverable passkeys to login UI

* PM-1575 Added copy application icon to Fido2Key UI

* PM-1575 Updated bwi font with the updated passkey icon

* PM-1575 For now just display Available for two-step login on non-discoverable passkey inside of a cipher login

* PM-1575 Fix non-discoverable passkey visibility

* PM-1575 remove Passkeys as a filter in the vault list

* PM-1575 Display error toast if there is a duplicate passkey when moving a cipher to an org

* Revert "PM-1575 Display error toast if there is a duplicate passkey when moving a cipher to an org"

This reverts commit 78e6353602.

* [PM-2378] Display error toast on duplicate Passkey when moving cipher to an organization (#2594)

* PM-2378 Display error toast if there is a duplicate passkey when moving a cipher to an org

* PM-3097 Fix issue when moving cipher with passkey to an org where the uniqueness should be taken into consideration on different passkeys types and also the Username (#2632)

* PM-3096 Fix non-discoverable passkey to be taken into account when encrypting a cipher which was causing the passkey to be removed when moving to an org (#2637)
2023-07-26 17:59:49 -03:00
René Wang
174549e5bc fix image alt text error (#2641) 2023-07-24 18:27:20 +00:00
Federico Maccaroni
87b1d18872 PM-2320 fix duplicated resource (#2638) 2023-07-21 15:58:28 -03:00
github-actions[bot]
ae9ba810ff Autosync the updated translations (#2634)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-07-21 09:30:20 +00:00
Federico Maccaroni
dd52ff0dcc [PM-2320] Improve Android block Auto-fill URIs (#2616)
* PM-2320 Added new view for block autofill URIs on Android

* PM-2320 Fix formatting

* PM-2320 Improved validations on block autofill uris

* PM-2320 Improved autofill block uris placeholder colors on different themes
2023-07-18 11:25:38 -03:00
github-actions[bot]
c678c17ebc Bumped version to 2023.7.1 (#2625)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-07-18 14:23:08 +00:00
Federico Maccaroni
cd9e49b13b ac-1425 added main thread invocations when updating the vault properties to fix cases where the screen stays blank and doesn't update (#2604) 2023-07-17 16:53:30 -03:00
Federico Maccaroni
6d7970f767 [AC-762] Configure Crowdin to localize watch app (#2552)
* AC-762 Added localization files to watch project

* AC-762 Added crowdin config for watchOS localizable files
2023-07-17 13:35:54 -03:00
mpbw2
9adc4d3080 Catch additional exception types when validating intents (#2618) 2023-07-17 08:40:35 -04:00
ifernandezdiaz
1f20f70d13 Fixing show value id button (#2620) 2023-07-16 20:13:55 -03:00
Vince Grassia
a25da68437 Fix syntax in Version Auto Bump workflow (#2615) 2023-07-13 11:55:16 -04:00
Vince Grassia
fdc0313d10 Fix Build Workflow (#2613) 2023-07-13 10:05:57 -04:00
github-actions[bot]
f31c87b52e Bumped version to 2023.7.0 (#2612)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-07-12 17:34:23 +00:00
Cesar Gonzalez
1e79e1182f [PM-1063] Re-prompt for Master Password Can be Bypassed When Using Gboard Inline Autofill (#2593) 2023-07-11 08:15:37 -05:00
github-actions[bot]
11947ce99a Autosync the updated translations (#2603)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-07-07 05:13:18 +00:00
mpbw2
4abb472998 Revert "reset lock delay when returning from activity result (#2539)" (#2597)
This reverts commit 0288a6659c.
2023-07-03 09:56:10 -04:00
github-actions[bot]
1d541e5b8e Autosync the updated translations (#2595)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-06-30 07:06:12 +00:00
ifernandezdiaz
175b9936b6 [PM-2798] Fixing Toolbar locators in FolderAddEditPage (#2592)
* Fixing Toolbar locators

* Adding Matt's suggestions
2023-06-29 15:50:01 -03:00
ifernandezdiaz
72e67bd6f2 [PM-2691] Adding AutomationIDs for Vault Page sections (#2580)
* Adding IDs for Vault Page sections

* Removing extra spaces

* Adding Matt's comments

* Fixing Filters Id bug

* Adding Fede's suggestions

* Fixing Settings Ids issues

* Fixing AutomationIds issues with RecyclerViews + implementing AutomationId helper class

* Adding Fede's suggestion

* Adding latest Fede's suggestions
2023-06-29 15:37:08 -03:00
ifernandezdiaz
216c6abcf6 [PM-2737] Adding AutomationIDs for Send page elements (#2583)
* Adding AutomationIDs for Send page elements

* Fixing some spaces

* Adding Matt's suggestion

* Adding Fede's suggestion

* Removing unnecesarry breaks
2023-06-28 14:07:03 -04:00
Federico Maccaroni
1014563c75 [PM-192] Refactor forwarded email providers (#2579)
* PM-192 Refactor Forwarded email providers to use better patterns and code reuse.

* PM-192 fix format
2023-06-27 18:49:38 -03:00
ifernandezdiaz
3506269811 [PM-2688] Adding IDs for Options and Folders pages (#2585)
* Adding IDs for Options and Folders pages

* Fixing extra spaces
2023-06-26 10:31:57 -03:00
ifernandezdiaz
31487a31bb [PM-2748] Refactoring locator strategy for Cipher Details page (#2586)
* Refactoring locator strategy for Cipher Details page

* Fixing extra spaces
2023-06-26 10:30:13 -03:00
ifernandezdiaz
1407aa5655 [PM-2678] Adding IDs for Settings Page elements (#2584)
* Adding IDS for Settings elements

* Adding IDS for Settings elements
2023-06-23 13:31:24 -03:00
github-actions[bot]
16f59e2698 Autosync the updated translations (#2582)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-06-23 05:40:37 +00:00
ifernandezdiaz
d876b54f45 Adding IDs for AttachmentPage elements (#2577) 2023-06-20 17:49:26 -03:00
ifernandezdiaz
6644e3b449 [PM-2545] Adding Automation IDs for CipherDetailsPage elements (#2576)
* Adding IDs to CipherDetailsPage

* Fixing extra spaces

* Fixing extra space
2023-06-20 16:26:43 -03:00
ifernandezdiaz
8d98d1d5bd [PM-2612] Adding AutomationIDs for LoginPasswordlessPage elements (#2574)
* Adding AutomationIDs for LoginPasswordlessPag elements

* Adding AutomationIDs for LoginPasswordlessRequest page elements

* Fixing missing space
2023-06-20 15:12:15 -03:00
ifernandezdiaz
3e9711f8f2 [PM-2611] Adding IDs for Cipher/Send search results (#2575)
* Adding IDs for Cipher/Send search results

* Adding missing spaces
2023-06-20 11:28:54 -03:00
github-actions[bot]
3af37f01d3 Autosync the updated translations (#2570)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-06-19 09:05:12 +00:00
ifernandezdiaz
43d2d386b1 [PM-2645] Adding IDs for Account Switching elements (#2572)
* Adding IDs for Account Switching elements

* Fixing Active/Inactive vault icon IDs
2023-06-16 16:20:09 -03:00
ifernandezdiaz
bc5c11b47f Adding AutomationIDs on Generator page elements (#2569)
* Adding AutomationIDs on Generator pages

* Adding missing spaces
2023-06-15 16:11:55 -03:00
ifernandezdiaz
52843b4181 [PM-2544] Adding AutomationIDs for CipherAddEditViewPage elements (#2564)
* Adding AutomationIDs for Add/Edit Items page

* Adding IDs to CustomFields

* Adding Matt's suggestions

* Adding newest suggestions
2023-06-14 09:34:38 -03:00
Federico Maccaroni
98705e443f PM-2575 Fixed extension freeze when using the return button on the keyboard when unlocking the extension. Also added way to prevent multiple executions of checking the password and logging exceptions. (#2568) 2023-06-13 22:38:08 +02:00
mpbw2
1332ef7b43 Enhancement to login field detection for Android autofill (#2561) 2023-06-13 13:54:28 -04:00
mpbw2
04e30c2146 Update F-Droid listing author name (#2501)
Update F-Droid listing author name to `Bitwarden Inc`
2023-06-13 18:46:41 +02:00
Opeyemi
f604da13a1 add more comment to missing actions (#2567) 2023-06-13 15:57:02 +01:00
github-actions[bot]
dcf9acb51c Autosync the updated translations (#2562)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-06-09 09:19:34 +02:00
Federico Maccaroni
3b087c50ae PM-1076 added warning on unlocking iOS extensions when the kdf type is argon2id and the memory is higher than 48MB, to let the user know that unlocking might crash the extension (#2560) 2023-06-07 16:21:51 +02:00
ifernandezdiaz
1c13ed9895 [PS-2558] Mobile Automation - Starting automationIDs additions to our codebase (#2558)
* Adding locators for Environment, Hope, Login and Register pages

* Adding Locators on LockPage

* Adding Álison's suggestions
2023-06-06 21:00:01 -03:00
Federico Maccaroni
eeb634e698 PM-1798 Added accessibility names on entries on cipher add (#2550) 2023-06-05 18:58:38 +02:00
github-actions[bot]
8bc2df6c8a Autosync the updated translations (#2555)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-06-04 16:23:34 +02:00
github-actions[bot]
7cd40d4d89 Bumped version to 2023.5.1 (#2554)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-06-01 12:18:12 -04:00
1097 changed files with 47205 additions and 19607 deletions

46
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,46 @@
# 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
# Default file owners
* @bitwarden/dept-development-mobile
# DevOps for Actions and other workflow changes
.github/workflows @bitwarden/dept-devops
# DevOps for Version Bumping
src/Android/Properties/AndroidManifest.xml
src/iOS.Autofill/Info.plist
src/iOS.Extension/Info.plist
src/iOS.ShareExtension/Info.plist
src/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
## Tools team files ##
src/Core/Services/EmailForwarders @bitwarden/team-tools-dev
## Crowdin Sync files ##
src/App/Resources @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/watchOS/bitwarden/bitwarden\ WatchKit\ Extension/Localization/en.lproj
store/apple/en
store/google/en
## Utils ##
store/google/Publisher

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"]
},
}
]
}

View File

@@ -9,15 +9,19 @@ on:
paths-ignore:
- ".github/workflows/**"
workflow_dispatch:
inputs: {}
env:
main_app_folder_path: src/App
main_app_project_path: src/App/App.csproj
target-net-version: net8.0
jobs:
cloc:
name: CLOC
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up CLOC
run: |
@@ -27,16 +31,15 @@ jobs:
- name: Print lines of code
run: cloc --vcs git --exclude-dir Resources,store,test,Properties --include-lang C#,XAML
setup:
name: Setup
runs-on: ubuntu-20.04
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@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
submodules: 'true'
@@ -54,8 +57,6 @@ jobs:
else
echo "hotfix_branch_exists=0" >> $GITHUB_OUTPUT
fi
shell: bash
android:
name: Android
@@ -65,37 +66,36 @@ jobs:
fail-fast: false
matrix:
variant: ["prod", "qa"]
env:
android_folder_path: 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@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
- name: Work Around for broken Windows 2022 Runner Image
- name: Install Microsoft OpenJDK 11
run: |
Set-Location "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
$InstallPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise"
$componentsToAdd = @(
"Component.Xamarin"
)
[string]$workloadArgs = $componentsToAdd | ForEach-Object {" --add " + $_}
$Arguments = ('/c', "vs_installer.exe", 'modify', '--installPath', "`"$InstallPath`"",$workloadArgs, '--quiet', '--norestart', '--nocache')
$process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden
if ($process.ExitCode -eq 0)
{
Write-Host "components have been successfully added"
}
else
{
Write-Host "components were not installed"
exit 1
}
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: $env:JAVA_HOME"
- name: Print environment
run: |
nuget help | grep Version
@@ -105,9 +105,10 @@ jobs:
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
fetch-depth: 0
- name: Decrypt secrets
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
@@ -115,20 +116,22 @@ jobs:
mkdir -p ~/secrets
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./src/Android/app_play-keystore.jks ./.github/secrets/app_play-keystore.jks.gpg
--output ./${{ env.main_app_folder_path }}/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
--output ./${{ env.main_app_folder_path }}/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
shell: bash
- name: Decrypt secrets - Google Services
if: ${{ matrix.variant == 'prod' }}
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
run: |
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./src/Android/google-services.json ./.github/secrets/google-services.json.gpg
--output ./${{ env.android_folder_path }}/google-services.json ./.github/secrets/google-services.json.gpg
shell: bash
- name: Increment version
run: |
BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))
@@ -136,9 +139,9 @@ jobs:
echo "########################################"
echo "##### Setting Version Code $BUILD_NUMBER"
echo "########################################"
sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
./src/Android/Properties/AndroidManifest.xml
./${{ env.android_folder_path }}/AndroidManifest.xml
shell: bash
- name: Restore packages
@@ -146,18 +149,15 @@ jobs:
- name: Restore tools
run: dotnet tool restore
shell: pwsh
- name: Verify Format
run: dotnet tool run dotnet-format --check
shell: pwsh
# - 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"
shell: pwsh
run: dotnet test test/Core.Test/Core.Test.csproj --logger "trx;LogFileName=test-results.trx" /p:CustomConstants=UT
- name: Report test results
uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226 # v1.6.0
uses: dorny/test-reporter@eaa763f6ffc21c7a37837f56cd5f9737f27fc6c8 # v1.8.0
if: always()
with:
name: Test Results
@@ -175,20 +175,20 @@ jobs:
- name: Build Android
run: |
$configuration = "Release";
$projToBuild = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_project_path }}");
Write-Output "########################################"
Write-Output "##### Build $configuration Configuration"
Write-Output "########################################"
msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" "/p:Configuration=$configuration"
shell: pwsh
dotnet build $projToBuild -c $configuration -f ${{ env.target-net-version }}-android
- name: Sign Android Build
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")
@@ -199,16 +199,13 @@ jobs:
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"
dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android /p:AndroidPackageFormats=aab /p:AndroidKeyStore=true /p:AndroidSigningKeyStore=$("app_upload-keystore.jks") /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");
$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
@@ -216,23 +213,20 @@ jobs:
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"
dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android /p:AndroidKeyStore=true /p:AndroidSigningKeyStore=$("app_play-keystore.jks") /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");
$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
shell: pwsh
- name: Upload Prod .aab artifact
if: ${{ matrix.variant == 'prod' }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
with:
name: com.x8bit.bitwarden.aab
path: ./com.x8bit.bitwarden.aab
@@ -240,7 +234,7 @@ jobs:
- name: Upload Prod .apk artifact
if: ${{ matrix.variant == 'prod' }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
with:
name: com.x8bit.bitwarden.apk
path: ./com.x8bit.bitwarden.apk
@@ -248,7 +242,7 @@ jobs:
- name: Upload Other .apk artifact
if: ${{ matrix.variant != 'prod' }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
with:
name: com.x8bit.bitwarden.${{ matrix.variant }}.apk
path: ./com.x8bit.bitwarden.${{ matrix.variant }}.apk
@@ -268,7 +262,7 @@ jobs:
- name: Upload .apk sha file for prod
if: ${{ matrix.variant == 'prod' }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
with:
name: bw-android-apk-sha256.txt
path: ./bw-android-apk-sha256.txt
@@ -276,20 +270,20 @@ jobs:
- name: Upload .apk sha file for other
if: ${{ matrix.variant != 'prod' }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
with:
name: bw-android-${{ matrix.variant }}-apk-sha256.txt
path: ./bw-android-${{ matrix.variant }}-apk-sha256.txt
if-no-files-found: error
- name: Deploy to Play Store
if: ${{ matrix.variant == 'prod' && (( github.ref == 'refs/heads/master'
if: ${{ matrix.variant == 'prod' && (( github.ref == 'refs/heads/main'
&& needs.setup.outputs.rc_branch_exists == 0
&& needs.setup.outputs.hotfix_branch_exists == 0)
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|| github.ref == 'refs/heads/hotfix-rc' ) }}
run: |
PUBLISHER_PATH="$GITHUB_WORKSPACE/store/google/Publisher/bin/Release/netcoreapp3.1/Publisher.dll"
PUBLISHER_PATH="$GITHUB_WORKSPACE/store/google/Publisher/bin/Release/net7.0/Publisher.dll"
CREDS_PATH="$HOME/secrets/play_creds.json"
AAB_PATH="$GITHUB_WORKSPACE/com.x8bit.bitwarden.aab"
TRACK="internal"
@@ -301,37 +295,36 @@ jobs:
f-droid:
name: F-Droid Build
runs-on: windows-2022
env:
android_folder_path: 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@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.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
- name: Work Around for broken Windows 2022 Runner Image
- name: Install Microsoft OpenJDK 11
run: |
Set-Location "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
$InstallPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise"
$componentsToAdd = @(
"Component.Xamarin"
)
[string]$workloadArgs = $componentsToAdd | ForEach-Object {" --add " + $_}
$Arguments = ('/c', "vs_installer.exe", 'modify', '--installPath', "`"$InstallPath`"",$workloadArgs, '--quiet', '--norestart', '--nocache')
$process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden
if ($process.ExitCode -eq 0)
{
Write-Host "components have been successfully added"
}
else
{
Write-Host "components were not installed"
exit 1
}
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: $env:JAVA_HOME"
- name: Print environment
run: |
@@ -342,7 +335,7 @@ jobs:
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Decrypt secrets
env:
@@ -351,7 +344,7 @@ jobs:
mkdir -p ~/secrets
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./src/Android/app_fdroid-keystore.jks ./.github/secrets/app_fdroid-keystore.jks.gpg
--output ./${{ env.main_app_folder_path }}/app_fdroid-keystore.jks ./.github/secrets/app_fdroid-keystore.jks.gpg
shell: bash
- name: Increment version
@@ -363,30 +356,21 @@ jobs:
echo "########################################"
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");
$appPath = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_project_path }}");
$corePath = $($env:GITHUB_WORKSPACE + "/src/Core/Core.csproj");
$androidManifest = $($env:GITHUB_WORKSPACE + "/src/Android/Properties/AndroidManifest.xml");
Write-Output "########################################"
Write-Output "##### Clean Android and App"
Write-Output "########################################"
msbuild "$($androidPath)" "/t:Clean" "/p:Configuration=FDroid"
msbuild "$($appPath)" "/t:Clean" "/p:Configuration=FDroid"
$androidManifest = $($env:GITHUB_WORKSPACE + "/${{ env.android_manifest_path }}");
Write-Output "########################################"
Write-Output "##### Backup project files"
Write-Output "########################################"
Copy-Item $androidManifest $($androidManifest + ".original");
Copy-Item $androidPath $($androidPath + ".original");
Copy-Item $appPath $($appPath + ".original");
Write-Output "########################################"
@@ -401,83 +385,44 @@ jobs:
$xml.Save($androidManifest);
Write-Output "########################################"
Write-Output "##### Uninstall from Android.csproj"
Write-Output "########################################"
$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);
shell: pwsh
- name: Restore packages
run: nuget restore
run: dotnet restore
- name: Build for F-Droid
run: |
$configuration = "FDroid";
$configuration = "Release";
$projToBuild = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_project_path }}");
Write-Output "########################################"
Write-Output "##### Build $configuration Configuration"
Write-Output "##### Build $configuration FDROID
Write-Output "########################################"
msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" "/p:Configuration=$configuration"
shell: pwsh
dotnet build $projToBuild -c $configuration -f ${{ env.target-net-version }}-android /p:CustomConstants="FDROID"
- name: Sign for F-Droid
env:
FDROID_KEYSTORE_PASSWORD: ${{ secrets.FDROID_KEYSTORE_PASSWORD }}
run: |
$projToBuild = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_project_path }}");
$packageName = "com.x8bit.bitwarden";
Write-Output "########################################"
Write-Output "##### Sign FDroid Configuration"
Write-Output "##### Sign FDroid"
Write-Output "########################################"
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"
dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android /p:AndroidKeyStore=true /p:AndroidSigningKeyStore=$("app_fdroid-keystore.jks") /p:AndroidSigningKeyAlias=bitwarden /p:AndroidSigningKeyPass="$($env:FDROID_KEYSTORE_PASSWORD)" /p:AndroidSigningStorePass="$($env:FDROID_KEYSTORE_PASSWORD)" /p:CustomConstants="FDROID" --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");
$signedApkPath = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_folder_path }}/bin/Release/${{ env.target-net-version }}-android/publish/$($packageName)-Signed.apk");
$signedApkDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden-fdroid.apk");
Copy-Item $signedApkPath $signedApkDestPath
shell: pwsh
- name: Upload F-Droid .apk artifact
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
with:
name: com.x8bit.bitwarden-fdroid.apk
path: ./com.x8bit.bitwarden-fdroid.apk
@@ -489,54 +434,64 @@ jobs:
-t sha256 | Out-File -Encoding ASCII ./bw-fdroid-apk-sha256.txt
- name: Upload F-Droid sha file
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
with:
name: bw-fdroid-apk-sha256.txt
path: ./bw-fdroid-apk-sha256.txt
if-no-files-found: error
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@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.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"
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
submodules: 'true'
- name: Login to Azure - CI Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve secrets
id: retrieve-secrets
env:
KEYVAULT: bitwarden-ci
SECRETS: |
appcenter-ios-token
run: |
for i in ${SECRETS//,/ }
do
VALUE=$(az keyvault secret show --vault-name $KEYVAULT --name $i --query value --output tsv)
echo "::add-mask::$VALUE"
echo "$i=$VALUE" >> $GITHUB_OUTPUT
done
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: "bitwarden-ci"
secrets: "appcenter-ios-token"
- name: Decrypt secrets
env:
@@ -565,7 +520,6 @@ jobs:
./.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
shell: bash
- name: Increment version
run: |
@@ -575,14 +529,14 @@ jobs:
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
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
cd ../../..
shell: bash
- name: Update Entitlements
run: |
@@ -590,9 +544,8 @@ jobs:
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>/' ./src/iOS/Entitlements.plist
shell: bash
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
- name: Set up Keychain
env:
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
@@ -608,7 +561,6 @@ jobs:
security import ~/secrets/iphone-distribution-cert.p12 -k build.keychain -P $DIST_CERT_PASSWORD \
-T /usr/bin/codesign -T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain
shell: bash
- name: Set up provisioning profiles
run: |
@@ -639,7 +591,9 @@ 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"
shell: bash
- name: Restore packages
run: dotnet restore
- name: Bulid WatchApp
run: |
@@ -652,21 +606,27 @@ jobs:
echo "########################################"
echo "##### Done"
echo "########################################"
shell: bash
- name: Restore packages
run: nuget restore
- name: Archive Build for App Store
run: |
$configuration = "AppStore";
$platform = "iPhone";
Write-Output "########################################"
Write-Output "##### Archive for Release ios-arm64
Write-Output "########################################"
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 "########################################"
Write-Output "##### Archive $configuration Configuration for $platform Platform"
Write-Output "##### Done"
Write-Output "########################################"
msbuild "$($env:GITHUB_WORKSPACE + "/src/iOS/iOS.csproj")" "/p:Platform=$platform" `
"/p:Configuration=$configuration" "/p:ArchiveOnBuild=true" "/t:`"Build`""
shell: pwsh
- name: Archive Build for Mobile Automation
run: |
Write-Output "########################################"
Write-Output "##### Archive Debug for iossimulator-x64
Write-Output "########################################"
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 "########################################"
Write-Output "##### Done"
@@ -682,7 +642,14 @@ jobs:
xcodebuild -exportArchive -archivePath $ARCHIVE_PATH -exportPath $EXPORT_PATH \
-exportOptionsPlist $EXPORT_OPTIONS_PATH
shell: bash
- name: Export .app for Automation CI
run: |
ARCHIVE_PATH="./${{ env.main_app_folder_path }}/bin/Debug/${{ env.target-net-version }}-ios/iossimulator-x64"
EXPORT_PATH="./bitwarden-export"
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
run: |
@@ -695,10 +662,9 @@ jobs:
cp -r -v $ARCHIVE_DSYMS_PATH $EXPORT_PATH
mkdir $WATCH_DSYMS_EXPORT_PATH
cp -r -v $WATCH_ARCHIVE_DSYMS_PATH $WATCH_DSYMS_EXPORT_PATH
shell: bash
- name: Upload App Store .ipa & dSYMs artifacts
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
with:
name: Bitwarden iOS
path: |
@@ -706,9 +672,16 @@ jobs:
./bitwarden-export/dSYMs/*.*
if-no-files-found: error
- name: Upload .app file for Automation CI
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
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
if: |
(github.ref == 'refs/heads/master'
(github.ref == 'refs/heads/main'
&& needs.setup.outputs.rc_branch_exists == 0
&& needs.setup.outputs.hotfix_branch_exists == 0)
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
@@ -717,7 +690,7 @@ jobs:
- name: Upload dSYMs to App Center
if: |
(github.ref == 'refs/heads/master'
(github.ref == 'refs/heads/main'
&& needs.setup.outputs.rc_branch_exists == 0
&& needs.setup.outputs.hotfix_branch_exists == 0)
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
@@ -725,25 +698,22 @@ jobs:
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
shell: bash
- name: Upload Watch dSYMs to Firebase Crashlytics
if: |
(github.ref == 'refs/heads/master'
(github.ref == 'refs/heads/main'
&& needs.setup.outputs.rc_branch_exists == 0
&& needs.setup.outputs.hotfix_branch_exists == 0)
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|| github.ref == 'refs/heads/hotfix-rc'
run: |
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" \;
shell: bash
- name: Deploy to App Store
- name: Validate app in App Store
if: |
(github.ref == 'refs/heads/master'
&& needs.setup.outputs.rc_branch_exists == 0
@@ -754,52 +724,59 @@ jobs:
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
run: |
xcrun altool --upload-app --type ios --file "./bitwarden-export/Bitwarden.ipa" \
xcrun altool --validate-app --type ios --file "./bitwarden-export/Bitwarden.ipa" \
--username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
shell: bash
- name: Deploy to App Store
if: |
(github.ref == 'refs/heads/main'
&& needs.setup.outputs.rc_branch_exists == 0
&& needs.setup.outputs.hotfix_branch_exists == 0)
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|| github.ref == 'refs/heads/hotfix-rc'
env:
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
run: |
xcrun altool --upload-app --type ios --file "./bitwarden-export/Bitwarden.ipa" \
--username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
crowdin-push:
name: Crowdin Push
if: github.ref == 'refs/heads/master'
if: github.ref == 'refs/heads/main'
needs:
- android
- f-droid
- ios
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
env:
_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@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve secrets
id: retrieve-secrets
env:
KEYVAULT: bitwarden-ci
SECRETS: |
crowdin-api-token
run: |
for i in ${SECRETS//,/ }
do
VALUE=$(az keyvault secret show --vault-name $KEYVAULT --name $i --query value --output tsv)
echo "::add-mask::$VALUE"
echo "$i=$VALUE" >> $GITHUB_OUTPUT
done
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: "bitwarden-ci"
secrets: "crowdin-api-token"
- name: Upload Sources
uses: crowdin/github-action@ecd7eb0ef6f3cfa16293c79e9cbc4bc5b5fd9c49 # v1.4.9
uses: crowdin/github-action@97bef4fd3f1b853eb105bc99b8d0d563760e024c # v1.17.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
with:
config: crowdin.yml
crowdin_branch_name: master
crowdin_branch_name: main
upload_sources: true
upload_translations: false
@@ -807,7 +784,7 @@ jobs:
check-failures:
name: Check for failures
if: always()
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
needs:
- cloc
- android
@@ -817,7 +794,7 @@ jobs:
steps:
- name: Check if any job failed
if: |
(github.ref == 'refs/heads/master')
(github.ref == 'refs/heads/main')
|| (github.ref == 'refs/heads/rc')
|| (github.ref == 'refs/heads/hotfix-rc')
env:
@@ -840,25 +817,18 @@ jobs:
fi
- name: Login to Azure - CI Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
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()
env:
KEYVAULT: bitwarden-ci
SECRETS: |
devops-alerts-slack-webhook-url
run: |
for i in ${SECRETS//,/ }
do
VALUE=$(az keyvault secret show --vault-name $KEYVAULT --name $i --query value --output tsv)
echo "::add-mask::$VALUE"
echo "$i=$VALUE" >> $GITHUB_OUTPUT
done
with:
keyvault: "bitwarden-ci"
secrets: "devops-alerts-slack-webhook-url"
- name: Notify Slack on failure
uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0

View File

@@ -15,28 +15,28 @@ jobs:
_CROWDIN_PROJECT_ID: "269690"
steps:
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
- name: Login to Azure - CI Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
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@34ecb67b2a357795dc893549df0795e7383ff50f
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: "bitwarden-ci"
secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase"
- name: Download translations
uses: crowdin/github-action@ecd7eb0ef6f3cfa16293c79e9cbc4bc5b5fd9c49 # v1.4.9
uses: crowdin/github-action@97bef4fd3f1b853eb105bc99b8d0d563760e024c # v1.17.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
with:
config: crowdin.yml
crowdin_branch_name: master
crowdin_branch_name: main
upload_sources: false
upload_translations: false
download_translations: true

View File

@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Enforce Label
uses: yogevbd/enforce-label-action@a3c219da6b8fa73f6ba62b68ff09c469b3a1c024 # v2.2.2
uses: yogevbd/enforce-label-action@a3c219da6b8fa73f6ba62b68ff09c469b3a1c024 # 2.2.2
with:
BANNED_LABELS: "hold,needs-qa"
BANNED_LABELS_DESCRIPTION: "PRs with the hold or needs-qa labels cannot be merged"

View File

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

View File

@@ -38,11 +38,11 @@ jobs:
fi
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
- name: Check Release Version
id: version
uses: bitwarden/gh-actions/release-version-check@34ecb67b2a357795dc893549df0795e7383ff50f
uses: bitwarden/gh-actions/release-version-check@main
with:
release-type: ${{ github.event.inputs.release_type }}
project-type: xamarin
@@ -68,7 +68,7 @@ jobs:
- name: Download all artifacts
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0
with:
workflow: build.yml
workflow_conclusion: success
@@ -76,18 +76,18 @@ jobs:
- name: Dry Run - Download all artifacts
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0
with:
workflow: build.yml
workflow_conclusion: success
branch: master
branch: main
- name: Prep Bitwarden iOS release asset
run: zip -r Bitwarden\ iOS.zip Bitwarden\ iOS
- name: Create release
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: ncipollo/release-action@40bb172bd05f266cf9ba4ff965cb61e9ee5f6d01 # v1.9.0
uses: ncipollo/release-action@6c75be85e571768fa31b40abf38de58ba0397db5 # v1.13.0
with:
artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab,
./com.x8bit.bitwarden.apk/com.x8bit.bitwarden.apk,
@@ -126,11 +126,11 @@ jobs:
if: inputs.fdroid_publish
steps:
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
- name: Download F-Droid .apk artifact
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0
with:
workflow: build.yml
workflow_conclusion: success
@@ -139,17 +139,17 @@ jobs:
- name: Dry Run - Download F-Droid .apk artifact
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0
with:
workflow: build.yml
workflow_conclusion: success
branch: master
branch: main
name: com.x8bit.bitwarden-fdroid.apk
- name: Set up Node
uses: actions/setup-node@1f8c6b94b26d0feae1e387ca63ccbdc44d27b561 # v2.5.1
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
with:
node-version: '10.x'
node-version: '16.x'
- name: Set up F-Droid server
run: |

View File

@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: 'Run stale action'
uses: actions/stale@3cc123766321e9f15a6676375c154ccffb12a358 # v5.0.0
uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
with:
stale-issue-label: 'needs-reply'
stale-pr-label: 'needs-changes'
@@ -27,4 +27,4 @@ jobs:
If youre still working on this, please respond here after youve made the changes weve requested and our team will re-open it for further review.
Please make sure to resolve any conflicts with the master branch before requesting another review.
Please make sure to resolve any conflicts with the main branch before requesting another review.

View File

@@ -1,5 +1,5 @@
---
name: Version Auto Bump
name: Auto Bump Mobile Version
on:
push:
@@ -7,14 +7,12 @@ 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@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Calculate bumped version
id: version
@@ -29,17 +27,23 @@ jobs:
NEW_PATCH=$((CURR_PATCH+1))
NEW_VER=$CURR_MAJOR.$NEW_PATCH
echo "New Version: $NEW_VER"
echo "new-version=$NEW_VER" >> $GITHUB_OUTPUT
echo "new_version=$NEW_VER" >> $GITHUB_OUTPUT
trigger_version_bump:
name: "Version bump"
runs-on: ubuntu-22.04
needs:
- setup
steps:
- name: Bump version to ${{ needs.setup.outputs.version_number }}
uses: ./.github/workflows/version-bump.yml
secrets:
AZURE_PROD_KV_CREDENTIALS: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
version_number: ${{ needs.setup.outputs.version_number }}
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: "Bump version to ${{ steps.version.outputs.new_version }}"
env:
GH_TOKEN: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
run: |
echo '{"cut_rc_branch": "false", "version_number": "${{ steps.version.outputs.new_version }}"}' | \
gh workflow run version-bump.yml --json --repo bitwarden/mobile

View File

@@ -1,43 +1,54 @@
---
name: Version Bump
run-name: Version Bump - v${{ inputs.version_number }}
on:
workflow_dispatch:
inputs:
version_number:
description: "New Version"
required: true
workflow_call:
inputs:
version_number:
required: true
type: string
secrets:
AZURE_PROD_KV_CREDENTIALS:
description: "New version (example: '2024.1.0')"
required: true
cut_rc_branch:
description: "Cut RC branch?"
default: true
type: boolean
jobs:
bump_version:
name: "Create version_bump_${{ github.event.inputs.version_number }} branch"
runs-on: ubuntu-20.04
name: "Bump Version to v${{ inputs.version_number }}"
runs-on: ubuntu-22.04
steps:
- name: Checkout Branch
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- name: Login to Azure - CI Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
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@34ecb67b2a357795dc893549df0795e7383ff50f
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: "bitwarden-ci"
secrets: "github-gpg-private-key, github-gpg-private-key-passphrase"
secrets: "github-gpg-private-key,
github-gpg-private-key-passphrase,
github-pat-bitwarden-devops-bot-repo-scope"
- 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: Import GPG key
uses: crazy-max/ghaction-import-gpg@111c56156bcc6918c056dbef52164cfa583dc549 # v5.2.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 }}
@@ -45,37 +56,68 @@ jobs:
git_commit_gpgsign: true
- name: Create Version Branch
run: git switch -c version_bump_${{ github.event.inputs.version_number }}
id: create-branch
run: |
NAME=version_bump_${{ github.ref_name }}_${{ inputs.version_number }}
git switch -c $NAME
echo "name=$NAME" >> $GITHUB_OUTPUT
- name: Install xmllint
run: sudo apt install -y libxml2-utils
- name: Verify input version
env:
NEW_VERSION: ${{ inputs.version_number }}
run: |
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)
# Error if version has not changed.
if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
echo "Version has not changed."
exit 1
fi
# Check if version is newer.
printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V
if [ $? -eq 0 ]; then
echo "Version check successful."
else
echo "Version check failed."
exit 1
fi
- name: Bump Version - Android XML
uses: bitwarden/gh-actions/version-bump@34ecb67b2a357795dc893549df0795e7383ff50f
uses: bitwarden/gh-actions/version-bump@main
with:
version: ${{ github.event.inputs.version_number }}
file_path: "./src/Android/Properties/AndroidManifest.xml"
version: ${{ inputs.version_number }}
file_path: "src/App/Platforms/Android/AndroidManifest.xml"
- name: Bump Version - iOS.Autofill
uses: bitwarden/gh-actions/version-bump@34ecb67b2a357795dc893549df0795e7383ff50f
uses: bitwarden/gh-actions/version-bump@main
with:
version: ${{ github.event.inputs.version_number }}
file_path: "./src/iOS.Autofill/Info.plist"
version: ${{ inputs.version_number }}
file_path: "src/iOS.Autofill/Info.plist"
- name: Bump Version - iOS.Extension
uses: bitwarden/gh-actions/version-bump@34ecb67b2a357795dc893549df0795e7383ff50f
uses: bitwarden/gh-actions/version-bump@main
with:
version: ${{ github.event.inputs.version_number }}
file_path: "./src/iOS.Extension/Info.plist"
version: ${{ inputs.version_number }}
file_path: "src/iOS.Extension/Info.plist"
- name: Bump Version - iOS.ShareExtension
uses: bitwarden/gh-actions/version-bump@34ecb67b2a357795dc893549df0795e7383ff50f
uses: bitwarden/gh-actions/version-bump@main
with:
version: ${{ github.event.inputs.version_number }}
file_path: "./src/iOS.ShareExtension/Info.plist"
version: ${{ inputs.version_number }}
file_path: "src/iOS.ShareExtension/Info.plist"
- name: Bump Version - iOS
uses: bitwarden/gh-actions/version-bump@34ecb67b2a357795dc893549df0795e7383ff50f
uses: bitwarden/gh-actions/version-bump@main
with:
version: ${{ github.event.inputs.version_number }}
file_path: "./src/iOS/Info.plist"
version: ${{ inputs.version_number }}
file_path: "src/App/Platforms/iOS/Info.plist"
- name: Setup git
run: |
@@ -94,22 +136,24 @@ jobs:
- name: Commit files
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
run: git commit -m "Bumped version to ${{ github.event.inputs.version_number }}" -a
run: git commit -m "Bumped version to ${{ inputs.version_number }}" -a
- name: Push changes
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
run: git push -u origin version_bump_${{ github.event.inputs.version_number }}
env:
PR_BRANCH: ${{ steps.create-branch.outputs.name }}
run: git push -u origin $PR_BRANCH
- name: Create Version PR
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
id: create-pr
env:
PR_BRANCH: "version_bump_${{ github.event.inputs.version_number }}"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
BASE_BRANCH: master
TITLE: "Bump version to ${{ github.event.inputs.version_number }}"
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 }}"
run: |
gh pr create --title "$TITLE" \
--base "$BASE" \
PR_URL=$(gh pr create --title "$TITLE" \
--base "main" \
--head "$PR_BRANCH" \
--label "version update" \
--label "automated pr" \
@@ -122,4 +166,48 @@ jobs:
- [X] Other
## Objective
Automated version bump to ${{ github.event.inputs.version_number }}"
Automated version bump to ${{ inputs.version_number }}")
echo "pr_number=${PR_URL##*/}" >> $GITHUB_OUTPUT
- name: Approve PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ steps.create-pr.outputs.pr_number }}
run: gh pr review $PR_NUMBER --approve
- name: Merge PR
env:
GH_TOKEN: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
PR_NUMBER: ${{ steps.create-pr.outputs.pr_number }}
run: gh pr merge $PR_NUMBER --squash --auto --delete-branch
cut_rc:
name: Cut RC branch
needs: bump_version
if: ${{ inputs.cut_rc_branch == true }}
runs-on: ubuntu-22.04
steps:
- name: Checkout Branch
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
ref: main
- name: Verify version has been updated
env:
NEW_VERSION: ${{ inputs.version_number }}
run: |
# Wait for version to change.
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)
sleep 10
done while [[ "$NEW_VERSION" != "$CURRENT_VERSION" ]]
- name: Cut RC branch
run: |
git switch --quiet --create rc
git push --quiet --set-upstream origin rc

View File

@@ -8,4 +8,4 @@ on:
jobs:
call-workflow:
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@34ecb67b2a357795dc893549df0795e7383ff50f
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/

13
Directory.Build.props Normal file
View File

@@ -0,0 +1,13 @@
<Project>
<PropertyGroup>
<MauiVersion>8.0.4-nightly.*</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> -->
</PropertyGroup>
</Project>

View File

@@ -1,12 +1,12 @@
[![Github Workflow build on master](https://github.com/bitwarden/mobile/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/bitwarden/mobile/actions/workflows/build.yml?query=branch:master)
[![Github Workflow build on main](https://github.com/bitwarden/mobile/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/bitwarden/mobile/actions/workflows/build.yml?query=branch:main)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/bitwarden-mobile/localized.svg)](https://crowdin.com/project/bitwarden-mobile)
[![Join the chat at https://gitter.im/bitwarden/Lobby](https://badges.gitter.im/bitwarden/Lobby.svg)](https://gitter.im/bitwarden/Lobby)
# Bitwarden Mobile Application
<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 Google Play" 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>
<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" />
@@ -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

@@ -67,7 +67,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 +119,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 +148,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)
@@ -315,7 +315,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,8 +324,8 @@ 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);
});

View File

@@ -38,3 +38,15 @@ files:
pt-PT: pt-PT
en-GB: en-GB
en-IN: en-IN
- source: "/src/watchOS/bitwarden/bitwarden WatchKit Extension/Localization/en.lproj/Localizable.strings"
dest: "/src/watchOS/bitwarden/bitwarden WatchKit Extension/Localization/en.lproj/%original_file_name%"
translation: "/src/watchOS/bitwarden/bitwarden WatchKit Extension/Localization//%two_letters_code%.lproj/%original_file_name%"
update_option: update_as_unapproved
languages_mapping:
two_letters_code:
zh-CN: zh-Hans
zh-TW: zh-Hant
pt-BR: pt-BR
pt-PT: pt-PT
en-GB: en-GB
en-IN: en-IN

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,306 +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.5.1.1" />
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.16" />
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0.19" />
<PackageReference Include="Xamarin.AndroidX.Core" Version="1.10.0" />
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.3.1.1" />
<PackageReference Include="Xamarin.Essentials">
<Version>1.7.5</Version>
</PackageReference>
<PackageReference Include="Xamarin.Firebase.Messaging">
<Version>123.1.1.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.8.0" />
<PackageReference Include="Xamarin.Google.Dagger" Version="2.44.2.1" />
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
<Version>118.0.1.3</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" />
</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>
</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,7 +0,0 @@
namespace Bit.Droid
{
public static class Constants
{
public const string PACKAGE_NAME = "com.x8bit.bitwarden";
}
}

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,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,444 +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.7.5" />
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2578" />
<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\AutofillServicesPage.xaml.cs">
<DependentUpon>AutofillServicesPage.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\Settings\OptionsPage.xaml.cs">
<DependentUpon>OptionsPage.xaml</DependentUpon>
</Compile>
<Compile Update="Pages\Settings\SyncPage.xaml.cs">
<DependentUpon>SyncPage.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\" />
</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\" />
</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,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.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;
}
}
}
}

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,136 +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}" />
<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}">
<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"/>
</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"/>
</StackLayout>
</StackLayout>
<Button Text="{u:I18n Continue}"
StyleClass="btn-primary"
IsEnabled="{Binding CanContinue}"
Command="{Binding ContinueCommand}" />
<Label FormattedText="{Binding CreateAccountText}"
Margin="0, 10"
StyleClass="box-footer-label">
<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,204 +0,0 @@
using System;
using System.Threading.Tasks;
using Bit.App.Models;
using Bit.App.Resources;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public partial class LockPage : BaseContentPage
{
private readonly IBroadcasterService _broadcasterService;
private readonly AppOptions _appOptions;
private readonly bool _autoPromptBiometric;
private readonly LockPageViewModel _vm;
private bool _promptedAfterResume;
private bool _appeared;
public LockPage(AppOptions appOptions = null, bool autoPromptBiometric = true)
{
_appOptions = appOptions;
_autoPromptBiometric = autoPromptBiometric;
InitializeComponent();
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>();
_vm = BindingContext as LockPageViewModel;
_vm.Page = this;
_vm.UnlockedAction = () => Device.BeginInvokeOnMainThread(async () => await UnlockedAsync());
if (Device.RuntimePlatform == Device.iOS)
{
ToolbarItems.Add(_moreItem);
}
else
{
ToolbarItems.Add(_logOut);
}
}
public Entry SecretEntry
{
get
{
if (_vm?.PinLock ?? false)
{
return _pin;
}
return _masterPassword;
}
}
public async Task PromptBiometricAfterResumeAsync()
{
if (_vm.BiometricLock)
{
await Task.Delay(500);
if (!_promptedAfterResume)
{
_promptedAfterResume = true;
await _vm?.PromptBiometricAsync();
}
}
}
protected override async void OnAppearing()
{
base.OnAppearing();
_broadcasterService.Subscribe(nameof(LockPage), message =>
{
if (message.Command == Constants.ClearSensitiveFields)
{
Device.BeginInvokeOnMainThread(_vm.ResetPinPasswordFields);
}
});
if (_appeared)
{
return;
}
_appeared = true;
_mainContent.Content = _mainLayout;
_accountAvatar?.OnAppearing();
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
await _vm.InitAsync();
_vm.FocusSecretEntry += PerformFocusSecretEntry;
if (!_vm.BiometricLock)
{
RequestFocus(SecretEntry);
}
else
{
if (_vm.UsingKeyConnector && !_vm.PinLock)
{
_passwordGrid.IsVisible = false;
_unlockButton.IsVisible = false;
}
if (_autoPromptBiometric)
{
var tasks = Task.Run(async () =>
{
await Task.Delay(500);
Device.BeginInvokeOnMainThread(async () => await _vm.PromptBiometricAsync());
});
}
}
}
private void PerformFocusSecretEntry(int? cursorPosition)
{
Device.BeginInvokeOnMainThread(() =>
{
SecretEntry.Focus();
if (cursorPosition.HasValue)
{
SecretEntry.CursorPosition = cursorPosition.Value;
}
});
}
protected override bool OnBackButtonPressed()
{
if (_accountListOverlay.IsVisible)
{
_accountListOverlay.HideAsync().FireAndForget();
return true;
}
return false;
}
protected override void OnDisappearing()
{
base.OnDisappearing();
_accountAvatar?.OnDisappearing();
_broadcasterService.Unsubscribe(nameof(LockPage));
}
private void Unlock_Clicked(object sender, EventArgs e)
{
if (DoOnce())
{
var tasks = Task.Run(async () =>
{
await Task.Delay(50);
Device.BeginInvokeOnMainThread(async () => await _vm.SubmitAsync());
});
}
}
private async void LogOut_Clicked(object sender, EventArgs e)
{
await _accountListOverlay.HideAsync();
if (DoOnce())
{
await _vm.LogOutAsync();
}
}
private async void Biometric_Clicked(object sender, EventArgs e)
{
if (DoOnce())
{
await _vm.PromptBiometricAsync();
}
}
private async void More_Clicked(object sender, System.EventArgs e)
{
await _accountListOverlay.HideAsync();
if (!DoOnce())
{
return;
}
var selection = await DisplayActionSheet(AppResources.Options,
AppResources.Cancel, null, AppResources.LogOut);
if (selection == AppResources.LogOut)
{
await _vm.LogOutAsync();
}
}
private async Task UnlockedAsync()
{
if (AppHelpers.SetAlternateMainPage(_appOptions))
{
return;
}
var previousPage = await AppHelpers.ClearPreviousPage();
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
}
}
}

View File

@@ -1,468 +0,0 @@
using System;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.App.Resources;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Domain;
using Bit.Core.Models.Request;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.Helpers;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class LockPageViewModel : BaseViewModel
{
private readonly IApiService _apiService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IDeviceActionService _deviceActionService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly ICryptoService _cryptoService;
private readonly IMessagingService _messagingService;
private readonly IEnvironmentService _environmentService;
private readonly IStateService _stateService;
private readonly IBiometricService _biometricService;
private readonly IKeyConnectorService _keyConnectorService;
private readonly ILogger _logger;
private readonly IWatchDeviceService _watchDeviceService;
private readonly WeakEventManager<int?> _secretEntryFocusWeakEventManager = new WeakEventManager<int?>();
private readonly IPolicyService _policyService;
private readonly IPasswordGenerationService _passwordGenerationService;
private string _email;
private string _masterPassword;
private string _pin;
private bool _showPassword;
private bool _pinLock;
private bool _biometricLock;
private bool _biometricIntegrityValid = true;
private bool _biometricButtonVisible;
private bool _usingKeyConnector;
private string _biometricButtonText;
private string _loggedInAsText;
private string _lockedVerifyText;
private bool _isPinProtected;
private bool _isPinProtectedWithKey;
public LockPageViewModel()
{
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
_logger = ServiceContainer.Resolve<ILogger>("logger");
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
_policyService = ServiceContainer.Resolve<IPolicyService>();
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>();
PageTitle = AppResources.VerifyMasterPassword;
TogglePasswordCommand = new Command(TogglePassword);
SubmitCommand = new Command(async () => await SubmitAsync());
AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger)
{
AllowAddAccountRow = true,
AllowActiveAccountSelection = true
};
}
public string MasterPassword
{
get => _masterPassword;
set => SetProperty(ref _masterPassword, value);
}
public string Pin
{
get => _pin;
set => SetProperty(ref _pin, value);
}
public bool ShowPassword
{
get => _showPassword;
set => SetProperty(ref _showPassword, value,
additionalPropertyNames: new string[]
{
nameof(ShowPasswordIcon),
nameof(PasswordVisibilityAccessibilityText),
});
}
public bool PinLock
{
get => _pinLock;
set => SetProperty(ref _pinLock, value);
}
public bool UsingKeyConnector
{
get => _usingKeyConnector;
}
public bool BiometricLock
{
get => _biometricLock;
set => SetProperty(ref _biometricLock, value);
}
public bool BiometricIntegrityValid
{
get => _biometricIntegrityValid;
set => SetProperty(ref _biometricIntegrityValid, value);
}
public bool BiometricButtonVisible
{
get => _biometricButtonVisible;
set => SetProperty(ref _biometricButtonVisible, value);
}
public string BiometricButtonText
{
get => _biometricButtonText;
set => SetProperty(ref _biometricButtonText, value);
}
public string LoggedInAsText
{
get => _loggedInAsText;
set => SetProperty(ref _loggedInAsText, value);
}
public string LockedVerifyText
{
get => _lockedVerifyText;
set => SetProperty(ref _lockedVerifyText, value);
}
public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; }
public Command SubmitCommand { get; }
public Command TogglePasswordCommand { get; }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
public Action UnlockedAction { get; set; }
public event Action<int?> FocusSecretEntry
{
add => _secretEntryFocusWeakEventManager.AddEventHandler(value);
remove => _secretEntryFocusWeakEventManager.RemoveEventHandler(value);
}
public async Task InitAsync()
{
(_isPinProtected, _isPinProtectedWithKey) = await _vaultTimeoutService.IsPinLockSetAsync();
PinLock = (_isPinProtected && await _stateService.GetPinProtectedKeyAsync() != null) ||
_isPinProtectedWithKey;
BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasKeyAsync();
// Users with key connector and without biometric or pin has no MP to unlock with
_usingKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
if (_usingKeyConnector && !(BiometricLock || PinLock))
{
await _vaultTimeoutService.LogOutAsync();
return;
}
_email = await _stateService.GetEmailAsync();
if (string.IsNullOrWhiteSpace(_email))
{
await _vaultTimeoutService.LogOutAsync();
_logger.Exception(new NullReferenceException("Email not found in storage"));
return;
}
var webVault = _environmentService.GetWebVaultUrl(true);
if (string.IsNullOrWhiteSpace(webVault))
{
webVault = "https://bitwarden.com";
}
var webVaultHostname = CoreHelpers.GetHostname(webVault);
LoggedInAsText = string.Format(AppResources.LoggedInAsOn, _email, webVaultHostname);
if (PinLock)
{
PageTitle = AppResources.VerifyPIN;
LockedVerifyText = AppResources.VaultLockedPIN;
}
else
{
if (_usingKeyConnector)
{
PageTitle = AppResources.UnlockVault;
LockedVerifyText = AppResources.VaultLockedIdentity;
}
else
{
PageTitle = AppResources.VerifyMasterPassword;
LockedVerifyText = AppResources.VaultLockedMasterPassword;
}
}
if (BiometricLock)
{
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
if (!_biometricIntegrityValid)
{
BiometricButtonVisible = false;
return;
}
BiometricButtonVisible = true;
BiometricButtonText = AppResources.UseBiometricsToUnlock;
if (Device.RuntimePlatform == Device.iOS)
{
var supportsFace = await _deviceActionService.SupportsFaceBiometricAsync();
BiometricButtonText = supportsFace ? AppResources.UseFaceIDToUnlock :
AppResources.UseFingerprintToUnlock;
}
}
}
public async Task SubmitAsync()
{
if (PinLock && string.IsNullOrWhiteSpace(Pin))
{
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
string.Format(AppResources.ValidationFieldRequired, AppResources.PIN),
AppResources.Ok);
return;
}
if (!PinLock && string.IsNullOrWhiteSpace(MasterPassword))
{
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
AppResources.Ok);
return;
}
ShowPassword = false;
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
if (PinLock)
{
var failed = true;
try
{
if (_isPinProtected)
{
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
kdfConfig,
await _stateService.GetPinProtectedKeyAsync());
var encKey = await _cryptoService.GetEncKeyAsync(key);
var protectedPin = await _stateService.GetProtectedPinAsync();
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
failed = decPin != Pin;
if (!failed)
{
Pin = string.Empty;
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
await SetKeyAndContinueAsync(key);
}
}
else
{
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email, kdfConfig);
failed = false;
Pin = string.Empty;
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
await SetKeyAndContinueAsync(key);
}
}
catch
{
failed = true;
}
if (failed)
{
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
if (invalidUnlockAttempts >= 5)
{
_messagingService.Send("logout");
return;
}
await _platformUtilsService.ShowDialogAsync(AppResources.InvalidPIN,
AppResources.AnErrorHasOccurred);
}
}
else
{
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdfConfig);
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
var passwordValid = false;
MasterPasswordPolicyOptions enforcedMasterPasswordOptions = null;
if (storedKeyHash != null)
{
passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(MasterPassword, key);
}
else
{
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
var request = new PasswordVerificationRequest();
request.MasterPasswordHash = keyHash;
try
{
var response = await _apiService.PostAccountVerifyPasswordAsync(request);
enforcedMasterPasswordOptions = response.MasterPasswordPolicy;
passwordValid = true;
var localKeyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
await _cryptoService.SetKeyHashAsync(localKeyHash);
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", e.GetType(), e.StackTrace);
}
await _deviceActionService.HideLoadingAsync();
}
if (passwordValid)
{
if (_isPinProtected)
{
var protectedPin = await _stateService.GetProtectedPinAsync();
var encKey = await _cryptoService.GetEncKeyAsync(key);
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email, kdfConfig);
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
}
if (await RequirePasswordChangeAsync(enforcedMasterPasswordOptions))
{
// Save the ForcePasswordResetReason to force a password reset after unlock
await _stateService.SetForcePasswordResetReasonAsync(
ForcePasswordResetReason.WeakMasterPasswordOnLogin);
}
MasterPassword = string.Empty;
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
await SetKeyAndContinueAsync(key);
// Re-enable biometrics
if (BiometricLock & !BiometricIntegrityValid)
{
await _biometricService.SetupBiometricAsync();
}
}
else
{
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
if (invalidUnlockAttempts >= 5)
{
_messagingService.Send("logout");
return;
}
await _platformUtilsService.ShowDialogAsync(AppResources.InvalidMasterPassword,
AppResources.AnErrorHasOccurred);
}
}
}
/// <summary>
/// Checks if the master password requires updating to meet the enforced policy requirements
/// </summary>
/// <param name="options"></param>
private async Task<bool> RequirePasswordChangeAsync(MasterPasswordPolicyOptions options = null)
{
// If no policy options are provided, attempt to load them from the policy service
var enforcedOptions = options ?? await _policyService.GetMasterPasswordPolicyOptions();
// No policy to enforce on login/unlock
if (!(enforcedOptions is { EnforceOnLogin: true }))
{
return false;
}
var strength = _passwordGenerationService.PasswordStrength(
MasterPassword, _passwordGenerationService.GetPasswordStrengthUserInput(_email))?.Score;
if (!strength.HasValue)
{
_logger.Error("Unable to evaluate master password strength during unlock");
return false;
}
return !await _policyService.EvaluateMasterPassword(
strength.Value,
MasterPassword,
enforcedOptions
);
}
public async Task LogOutAsync()
{
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.LogoutConfirmation,
AppResources.LogOut, AppResources.Yes, AppResources.Cancel);
if (confirmed)
{
_messagingService.Send("logout");
}
}
public void ResetPinPasswordFields()
{
try
{
MasterPassword = string.Empty;
Pin = string.Empty;
ShowPassword = false;
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
}
public void TogglePassword()
{
ShowPassword = !ShowPassword;
var secret = PinLock ? Pin : MasterPassword;
_secretEntryFocusWeakEventManager.RaiseEvent(string.IsNullOrEmpty(secret) ? 0 : secret.Length, nameof(FocusSecretEntry));
}
public async Task PromptBiometricAsync()
{
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
BiometricButtonVisible = BiometricIntegrityValid;
if (!BiometricLock || !BiometricIntegrityValid)
{
return;
}
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
PinLock ? AppResources.PIN : AppResources.MasterPassword,
() => _secretEntryFocusWeakEventManager.RaiseEvent((int?)null, nameof(FocusSecretEntry)));
await _stateService.SetBiometricLockedAsync(!success);
if (success)
{
await DoContinueAsync();
}
}
private async Task SetKeyAndContinueAsync(SymmetricCryptoKey key)
{
var hasKey = await _cryptoService.HasKeyAsync();
if (!hasKey)
{
await _cryptoService.SetKeyAsync(key);
}
await DoContinueAsync();
}
private async Task DoContinueAsync()
{
await _stateService.SetBiometricLockedAsync(false);
_watchDeviceService.SyncDataToWatchAsync().FireAndForget();
_messagingService.Send("unlocked");
UnlockedAction?.Invoke();
}
}
}

View File

@@ -1,76 +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.LoginPasswordlessRequestPage"
xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="pages:LoginPasswordlessRequestViewModel"
Title="{Binding PageTitle}">
<ContentPage.BindingContext>
<pages:LoginPasswordlessRequestViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Command="{Binding CloseCommand}" Order="Primary" Priority="-1" x:Name="_closeItem"/>
</ContentPage.ToolbarItems>
<ScrollView>
<StackLayout
Padding="7, 0, 7, 20">
<Label
Text="{u:I18n LogInInitiated}"
FontSize="Title"
FontAttributes="Bold"
Margin="0,14,0,21"/>
<Label
Text="{u:I18n ANotificationHasBeenSentToYourDevice}"
FontSize="Small"
Margin="0,0,0,10"/>
<Label
Text="{u:I18n PleaseMakeSureYourVaultIsUnlockedAndTheFingerprintPhraseMatchesOnTheOtherDevice}"
FontSize="Small"
Margin="0,0,0,24"/>
<Label
Text="{u:I18n FingerprintPhrase}"
FontSize="Small"
FontAttributes="Bold"/>
<controls:MonoLabel
FormattedText="{Binding FingerprintPhrase}"
FontSize="Medium"
TextColor="{DynamicResource FingerprintPhrase}"/>
<Label
Text="{u:I18n ResendNotification}"
StyleClass="text-md"
HorizontalOptions="Start"
Margin="0,40,0,0"
TextColor="{DynamicResource HyperlinkColor}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding CreatePasswordlessLoginCommand}" />
</Label.GestureRecognizers>
</Label>
<StackLayout
Orientation="Horizontal"
Margin="0,30,0,0">
<Label
Text="{u:I18n NeedAnotherOption}"
FontSize="Small"
VerticalTextAlignment="End"/>
<Label
Text="{u:I18n ViewAllLoginOptions}"
StyleClass="text-md"
VerticalTextAlignment="End"
VerticalOptions="CenterAndExpand"
Margin="5, 0"
TextColor="{DynamicResource HyperlinkColor}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding CloseCommand}" />
</Label.GestureRecognizers>
</Label>
</StackLayout>
</StackLayout>
</ScrollView>
</pages:BaseContentPage>

View File

@@ -1,65 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.App.Models;
using Bit.App.Utilities;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public partial class LoginPasswordlessRequestPage : BaseContentPage
{
private LoginPasswordlessRequestViewModel _vm;
private readonly AppOptions _appOptions;
public LoginPasswordlessRequestPage(string email, AppOptions appOptions = null)
{
InitializeComponent();
_appOptions = appOptions;
_vm = BindingContext as LoginPasswordlessRequestViewModel;
_vm.Page = this;
_vm.Email = email;
_vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
_vm.LogInSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await LogInSuccessAsync());
_vm.UpdateTempPasswordAction = () => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
_vm.CloseAction = () => { Navigation.PopModalAsync(); };
_vm.CreatePasswordlessLoginCommand.Execute(null);
}
protected override void OnAppearing()
{
base.OnAppearing();
_vm.StartCheckLoginRequestStatus();
}
protected override void OnDisappearing()
{
base.OnDisappearing();
_vm.StopCheckLoginRequestStatus();
}
private async Task StartTwoFactorAsync()
{
var page = new TwoFactorPage(false, _appOptions);
await Navigation.PushModalAsync(new NavigationPage(page));
}
private async Task LogInSuccessAsync()
{
if (AppHelpers.SetAlternateMainPage(_appOptions))
{
return;
}
var previousPage = await AppHelpers.ClearPreviousPage();
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
}
private async Task UpdateTempPasswordAsync()
{
var page = new UpdateTempPasswordPage();
await Navigation.PushModalAsync(new NavigationPage(page));
}
}
}

View File

@@ -1,194 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Domain;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class LoginPasswordlessRequestViewModel : CaptchaProtectedViewModel
{
private const int REQUEST_TIME_UPDATE_PERIOD_IN_SECONDS = 4;
private IDeviceActionService _deviceActionService;
private IAuthService _authService;
private ISyncService _syncService;
private II18nService _i18nService;
private IStateService _stateService;
private IPlatformUtilsService _platformUtilsService;
private IEnvironmentService _environmentService;
private ILogger _logger;
protected override II18nService i18nService => _i18nService;
protected override IEnvironmentService environmentService => _environmentService;
protected override IDeviceActionService deviceActionService => _deviceActionService;
protected override IPlatformUtilsService platformUtilsService => _platformUtilsService;
private CancellationTokenSource _checkLoginRequestStatusCts;
private Task _checkLoginRequestStatusTask;
private string _fingerprintPhrase;
private string _email;
private string _requestId;
private string _requestAccessCode;
// Item1 publicKey, Item2 privateKey
private Tuple<byte[], byte[]> _requestKeyPair;
public LoginPasswordlessRequestViewModel()
{
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>();
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>();
_environmentService = ServiceContainer.Resolve<IEnvironmentService>();
_authService = ServiceContainer.Resolve<IAuthService>();
_syncService = ServiceContainer.Resolve<ISyncService>();
_i18nService = ServiceContainer.Resolve<II18nService>();
_stateService = ServiceContainer.Resolve<IStateService>();
_logger = ServiceContainer.Resolve<ILogger>();
PageTitle = AppResources.LogInWithAnotherDevice;
CreatePasswordlessLoginCommand = new AsyncCommand(CreatePasswordlessLoginAsync,
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
CloseCommand = new AsyncCommand(() => Device.InvokeOnMainThreadAsync(CloseAction),
onException: _logger.Exception,
allowsMultipleExecutions: false);
}
public Action StartTwoFactorAction { get; set; }
public Action LogInSuccessAction { get; set; }
public Action UpdateTempPasswordAction { get; set; }
public Action CloseAction { get; set; }
public ICommand CreatePasswordlessLoginCommand { get; }
public ICommand CloseCommand { get; }
public string FingerprintPhrase
{
get => _fingerprintPhrase;
set => SetProperty(ref _fingerprintPhrase, value);
}
public string Email
{
get => _email;
set => SetProperty(ref _email, value);
}
public void StartCheckLoginRequestStatus()
{
try
{
_checkLoginRequestStatusCts?.Cancel();
_checkLoginRequestStatusCts = new CancellationTokenSource();
_checkLoginRequestStatusTask = new TimerTask(_logger, CheckLoginRequestStatus, _checkLoginRequestStatusCts).RunPeriodic(TimeSpan.FromSeconds(REQUEST_TIME_UPDATE_PERIOD_IN_SECONDS));
}
catch (Exception ex)
{
_logger.Exception(ex);
}
}
public void StopCheckLoginRequestStatus()
{
try
{
_checkLoginRequestStatusCts?.Cancel();
_checkLoginRequestStatusCts?.Dispose();
_checkLoginRequestStatusCts = null;
}
catch (Exception ex)
{
_logger.Exception(ex);
}
}
private async Task CheckLoginRequestStatus()
{
if (string.IsNullOrEmpty(_requestId) || string.IsNullOrEmpty(_requestAccessCode))
{
return;
}
try
{
var response = await _authService.GetPasswordlessLoginResponseAsync(_requestId, _requestAccessCode);
if (response.RequestApproved == null || !response.RequestApproved.Value)
{
return;
}
StopCheckLoginRequestStatus();
var authResult = await _authService.LogInPasswordlessAsync(Email, _requestAccessCode, _requestId, _requestKeyPair.Item2, response.Key, response.MasterPasswordHash);
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
if (await HandleCaptchaAsync(authResult.CaptchaSiteKey, authResult.CaptchaNeeded, CheckLoginRequestStatus))
{
return;
}
if (authResult.TwoFactor)
{
StartTwoFactorAction?.Invoke();
}
else if (authResult.ForcePasswordReset)
{
UpdateTempPasswordAction?.Invoke();
}
else
{
_syncService.FullSyncAsync(true).FireAndForget();
LogInSuccessAction?.Invoke();
}
}
catch (Exception ex)
{
StartCheckLoginRequestStatus();
HandleException(ex);
}
}
private async Task CreatePasswordlessLoginAsync()
{
await Device.InvokeOnMainThreadAsync(() => _deviceActionService.ShowLoadingAsync(AppResources.Loading));
var response = await _authService.PasswordlessCreateLoginRequestAsync(_email);
if (response != null)
{
FingerprintPhrase = response.FingerprintPhrase;
_requestId = response.Id;
_requestAccessCode = response.RequestAccessCode;
_requestKeyPair = response.RequestKeyPair;
}
await _deviceActionService.HideLoadingAsync();
}
private void HandleException(Exception ex)
{
Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
{
await _deviceActionService.HideLoadingAsync();
await _platformUtilsService.ShowDialogAsync(AppResources.GenericErrorMessage);
}).FireAndForget();
_logger.Exception(ex);
}
}
}

View File

@@ -1,156 +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();
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
}
}
}

View File

@@ -1,51 +0,0 @@
using System;
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core.Utilities;
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);
}
}
}

View File

@@ -1,70 +0,0 @@
using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Enums;
using Bit.Core.Models.View;
namespace Bit.App.Pages
{
public class SendGroupingsPageListItem : ISendGroupingsPageListItem
{
private string _icon;
private string _name;
public SendView Send { get; set; }
public SendType? Type { get; set; }
public string ItemCount { get; set; }
public bool ShowOptions { get; set; }
public string Name
{
get
{
if (_name != null)
{
return _name;
}
if (Type != null)
{
switch (Type.Value)
{
case SendType.Text:
_name = AppResources.TypeText;
break;
case SendType.File:
_name = AppResources.TypeFile;
break;
default:
break;
}
}
return _name;
}
}
public string Icon
{
get
{
if (_icon != null)
{
return _icon;
}
if (Type != null)
{
switch (Type.Value)
{
case SendType.Text:
_icon = BitwardenIcons.FileText;
break;
case SendType.File:
_icon = BitwardenIcons.File;
break;
default:
break;
}
}
return _icon;
}
}
}
}

View File

@@ -1,131 +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.AutofillServicesPage"
xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="pages:AutofillServicesPageViewModel"
Title="{Binding PageTitle}">
<ContentPage.BindingContext>
<pages:AutofillServicesPageViewModel />
</ContentPage.BindingContext>
<ScrollView>
<StackLayout Padding="0" Spacing="20">
<StackLayout
StyleClass="box"
IsVisible="{Binding AutofillServiceVisible}">
<StackLayout StyleClass="box-row, box-row-switch">
<Label
Text="{u:I18n AutofillService}"
StyleClass="box-label-regular"
HorizontalOptions="StartAndExpand" />
<RelativeLayout HorizontalOptions="End">
<Switch
x:Name="AutofillServiceSwitch"
IsToggled="{Binding AutofillServiceToggled}"
StyleClass="box-value"
HorizontalOptions="End" />
<Button
Clicked="ToggleAutofillService"
StyleClass="box-overlay"
RelativeLayout.XConstraint="0"
RelativeLayout.YConstraint="0"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToView, ElementName=AutofillServiceSwitch, Property=Width}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToView, ElementName=AutofillServiceSwitch, Property=Height}" />
</RelativeLayout>
</StackLayout>
<Label
Text="{u:I18n AutofillServiceDescription}"
StyleClass="box-footer-label, box-footer-label-switch" />
</StackLayout>
<StackLayout
StyleClass="box"
IsVisible="{Binding InlineAutofillVisible}">
<StackLayout StyleClass="box-row, box-row-switch">
<Label
Text="{u:I18n InlineAutofill}"
StyleClass="box-label-regular"
IsEnabled="{Binding InlineAutofillEnabled}"
HorizontalOptions="StartAndExpand" />
<RelativeLayout HorizontalOptions="End">
<Switch
x:Name="InlineAutofillSwitch"
IsEnabled="{Binding InlineAutofillEnabled}"
IsToggled="{Binding InlineAutofillToggled}"
StyleClass="box-value"
HorizontalOptions="End" />
<Button
Clicked="ToggleInlineAutofill"
StyleClass="box-overlay"
RelativeLayout.XConstraint="0"
RelativeLayout.YConstraint="0"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToView, ElementName=InlineAutofillSwitch, Property=Width}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToView, ElementName=InlineAutofillSwitch, Property=Height}" />
</RelativeLayout>
</StackLayout>
<Label
Text="{u:I18n InlineAutofillDescription}"
StyleClass="box-footer-label, box-footer-label-switch"
IsEnabled="{Binding InlineAutofillEnabled}"/>
</StackLayout>
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row, box-row-switch">
<Label
Text="{u:I18n Accessibility}"
StyleClass="box-label-regular"
HorizontalOptions="StartAndExpand" />
<RelativeLayout HorizontalOptions="End">
<Switch
x:Name="AccessibilitySwitch"
IsToggled="{Binding AccessibilityToggled}"
StyleClass="box-value"
HorizontalOptions="End" />
<Button
Command="{Binding ToggleAccessibilityCommand}"
StyleClass="box-overlay"
RelativeLayout.XConstraint="0"
RelativeLayout.YConstraint="0"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToView, ElementName=AccessibilitySwitch, Property=Width}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToView, ElementName=AccessibilitySwitch, Property=Height}" />
</RelativeLayout>
</StackLayout>
<Label
Text="{Binding AccessibilityDescriptionLabel}"
StyleClass="box-footer-label, box-footer-label-switch" />
</StackLayout>
<StackLayout
StyleClass="box"
IsVisible="{Binding DrawOverVisible}">
<StackLayout StyleClass="box-row, box-row-switch">
<Label
Text="{u:I18n DrawOver}"
StyleClass="box-label-regular"
IsEnabled="{Binding DrawOverEnabled}"
HorizontalOptions="StartAndExpand" />
<RelativeLayout HorizontalOptions="End">
<Switch
x:Name="DrawOverSwitch"
IsEnabled="{Binding DrawOverEnabled}"
IsToggled="{Binding DrawOverToggled}"
StyleClass="box-value"
HorizontalOptions="End" />
<Button
Clicked="ToggleDrawOver"
StyleClass="box-overlay"
RelativeLayout.XConstraint="0"
RelativeLayout.YConstraint="0"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToView, ElementName=DrawOverSwitch, Property=Width}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToView, ElementName=DrawOverSwitch, Property=Height}" />
</RelativeLayout>
</StackLayout>
<Label
Text="{Binding DrawOverDescriptionLabel}"
StyleClass="box-footer-label, box-footer-label-switch"
IsEnabled="{Binding InlineAutofillEnabled}"/>
</StackLayout>
</StackLayout>
</ScrollView>
</pages:BaseContentPage>

View File

@@ -1,66 +0,0 @@
using System;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public partial class AutofillServicesPage : BaseContentPage
{
private readonly AutofillServicesPageViewModel _vm;
private readonly SettingsPage _settingsPage;
private DateTime? _timerStarted = null;
private TimeSpan _timerMaxLength = TimeSpan.FromMinutes(5);
public AutofillServicesPage(SettingsPage settingsPage)
{
InitializeComponent();
_vm = BindingContext as AutofillServicesPageViewModel;
_vm.Page = this;
_settingsPage = settingsPage;
}
protected async override void OnAppearing()
{
await _vm.InitAsync();
_vm.UpdateEnabled();
_timerStarted = DateTime.UtcNow;
Device.StartTimer(new TimeSpan(0, 0, 0, 0, 500), () =>
{
if (_timerStarted == null || (DateTime.UtcNow - _timerStarted) > _timerMaxLength)
{
return false;
}
_vm.UpdateEnabled();
return true;
});
base.OnAppearing();
}
protected override void OnDisappearing()
{
_timerStarted = null;
_settingsPage.BuildList();
base.OnDisappearing();
}
private void ToggleAutofillService(object sender, EventArgs e)
{
if (DoOnce())
{
_vm.ToggleAutofillService();
}
}
private void ToggleInlineAutofill(object sender, EventArgs e)
{
_vm.ToggleInlineAutofill();
}
private void ToggleDrawOver(object sender, EventArgs e)
{
if (DoOnce())
{
_vm.ToggleDrawOver();
}
}
}
}

View File

@@ -1,231 +0,0 @@
using System.Threading.Tasks;
using System.Windows.Input;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.App.Services;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel;
namespace Bit.App.Pages
{
public class AutofillServicesPageViewModel : BaseViewModel
{
private readonly IDeviceActionService _deviceActionService;
private readonly IAutofillHandler _autofillHandler;
private readonly IStateService _stateService;
private readonly MobileI18nService _i18nService;
private readonly IPlatformUtilsService _platformUtilsService;
readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
private bool _autofillServiceToggled;
private bool _inlineAutofillToggled;
private bool _accessibilityToggled;
private bool _drawOverToggled;
private bool _inited;
public AutofillServicesPageViewModel()
{
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_autofillHandler = ServiceContainer.Resolve<IAutofillHandler>();
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
PageTitle = AppResources.AutofillServices;
ToggleAccessibilityCommand = new AsyncCommand(ToggleAccessibilityAsync,
onException: ex => _logger.Value.Exception(ex),
allowsMultipleExecutions: false);
}
#region Autofill Service
public bool AutofillServiceVisible
{
get => _deviceActionService.SystemMajorVersion() >= 26;
}
public bool AutofillServiceToggled
{
get => _autofillServiceToggled;
set => SetProperty(ref _autofillServiceToggled, value,
additionalPropertyNames: new string[]
{
nameof(InlineAutofillEnabled)
});
}
#endregion
#region Inline Autofill
public bool InlineAutofillVisible
{
get => _deviceActionService.SystemMajorVersion() >= 30;
}
public bool InlineAutofillEnabled
{
get => AutofillServiceToggled;
}
public bool InlineAutofillToggled
{
get => _inlineAutofillToggled;
set
{
if (SetProperty(ref _inlineAutofillToggled, value))
{
var task = UpdateInlineAutofillToggledAsync();
}
}
}
#endregion
#region Accessibility
public ICommand ToggleAccessibilityCommand { get; }
public string AccessibilityDescriptionLabel
{
get
{
if (_deviceActionService.SystemMajorVersion() <= 22)
{
// Android 5
return _i18nService.T("AccessibilityDescription");
}
if (_deviceActionService.SystemMajorVersion() == 23)
{
// Android 6
return _i18nService.T("AccessibilityDescription2");
}
if (_deviceActionService.SystemMajorVersion() == 24 || _deviceActionService.SystemMajorVersion() == 25)
{
// Android 7
return _i18nService.T("AccessibilityDescription3");
}
// Android 8+
return _i18nService.T("AccessibilityDescription4");
}
}
public bool AccessibilityToggled
{
get => _accessibilityToggled;
set => SetProperty(ref _accessibilityToggled, value,
additionalPropertyNames: new string[]
{
nameof(DrawOverEnabled)
});
}
#endregion
#region Draw-Over
public bool DrawOverVisible
{
get => _deviceActionService.SystemMajorVersion() >= 23;
}
public string DrawOverDescriptionLabel
{
get
{
if (_deviceActionService.SystemMajorVersion() <= 23)
{
// Android 6
return _i18nService.T("DrawOverDescription");
}
if (_deviceActionService.SystemMajorVersion() == 24 || _deviceActionService.SystemMajorVersion() == 25)
{
// Android 7
return _i18nService.T("DrawOverDescription2");
}
// Android 8+
return _i18nService.T("DrawOverDescription3");
}
}
public bool DrawOverEnabled
{
get => AccessibilityToggled;
}
public bool DrawOverToggled
{
get => _drawOverToggled;
set => SetProperty(ref _drawOverToggled, value);
}
#endregion
public async Task InitAsync()
{
InlineAutofillToggled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
_inited = true;
}
public void ToggleAutofillService()
{
if (!AutofillServiceToggled)
{
_deviceActionService.OpenAutofillSettings();
}
else
{
_autofillHandler.DisableAutofillService();
}
}
public void ToggleInlineAutofill()
{
if (!InlineAutofillEnabled)
{
return;
}
InlineAutofillToggled = !InlineAutofillToggled;
}
public async Task ToggleAccessibilityAsync()
{
if (!_autofillHandler.AutofillAccessibilityServiceRunning())
{
var accept = await _platformUtilsService.ShowDialogAsync(AppResources.AccessibilityDisclosureText,
AppResources.AccessibilityServiceDisclosure, AppResources.Accept,
AppResources.Decline);
if (!accept)
{
return;
}
}
_deviceActionService.OpenAccessibilitySettings();
}
public void ToggleDrawOver()
{
if (!DrawOverEnabled)
{
return;
}
_deviceActionService.OpenAccessibilityOverlayPermissionSettings();
}
public void UpdateEnabled()
{
AutofillServiceToggled =
_autofillHandler.SupportsAutofillService() && _autofillHandler.AutofillServiceEnabled();
AccessibilityToggled = _autofillHandler.AutofillAccessibilityServiceRunning();
DrawOverToggled = _autofillHandler.AutofillAccessibilityOverlayPermitted();
}
private async Task UpdateInlineAutofillToggledAsync()
{
if (_inited)
{
await _stateService.SetInlineAutofillEnabledAsync(InlineAutofillToggled);
}
}
}
}

View File

@@ -1,170 +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.OptionsPage"
xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="pages:OptionsPageViewModel"
Title="{Binding PageTitle}">
<ContentPage.BindingContext>
<pages:OptionsPageViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
</ContentPage.ToolbarItems>
<ScrollView Padding="0, 0, 0, 20">
<StackLayout Padding="0" Spacing="20">
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row, box-row-input, box-row-input-options-platform">
<Label
Text="{u:I18n Theme}"
StyleClass="box-label" />
<Picker
x:Name="_themePicker"
ItemsSource="{Binding ThemeOptions, Mode=OneTime}"
SelectedIndex="{Binding ThemeSelectedIndex}"
StyleClass="box-value" />
</StackLayout>
<Label
StyleClass="box-footer-label"
Text="{u:I18n ThemeDescription}" />
</StackLayout>
<StackLayout
StyleClass="box"
IsVisible="{Binding ShowAutoDarkThemeOptions}">
<StackLayout StyleClass="box-row, box-row-input, box-row-input-options-platform">
<Label
Text="{u:I18n DefaultDarkTheme}"
StyleClass="box-label" />
<Picker
x:Name="_autoDarkThemePicker"
ItemsSource="{Binding AutoDarkThemeOptions, Mode=OneTime}"
SelectedIndex="{Binding AutoDarkThemeSelectedIndex}"
StyleClass="box-value" />
</StackLayout>
<Label
StyleClass="box-footer-label"
Text="{u:I18n DefaultDarkThemeDescription}" />
</StackLayout>
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row, box-row-input, box-row-input-options-platform">
<Label
Text="{u:I18n DefaultUriMatchDetection}"
StyleClass="box-label" />
<Picker
x:Name="_uriMatchPicker"
ItemsSource="{Binding UriMatchOptions, Mode=OneTime}"
SelectedIndex="{Binding UriMatchSelectedIndex}"
StyleClass="box-value" />
</StackLayout>
<Label
Text="{u:I18n DefaultUriMatchDetectionDescription}"
StyleClass="box-footer-label" />
</StackLayout>
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row, box-row-input, box-row-input-options-platform">
<Label
Text="{u:I18n ClearClipboard}"
StyleClass="box-label" />
<Picker
x:Name="_clearClipboardPicker"
ItemsSource="{Binding ClearClipboardOptions, Mode=OneTime}"
SelectedIndex="{Binding ClearClipboardSelectedIndex}"
StyleClass="box-value" />
</StackLayout>
<Label
Text="{u:I18n ClearClipboardDescription}"
StyleClass="box-footer-label" />
</StackLayout>
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row, box-row-input, box-row-input-options-platform">
<Label
Text="{u:I18n Language}"
StyleClass="box-label" />
<Picker
x:Name="_languagePicker"
ItemsSource="{Binding LocalesOptions, Mode=OneTime}"
SelectedItem="{Binding SelectedLocale}"
ItemDisplayBinding="{Binding Value}"
StyleClass="box-value" />
</StackLayout>
<Label
Text="{u:I18n LanguageChangeRequiresAppRestart}"
StyleClass="box-footer-label" />
</StackLayout>
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row, box-row-switch">
<Label
Text="{u:I18n CopyTotpAutomatically}"
StyleClass="box-label-regular"
HorizontalOptions="StartAndExpand" />
<Switch
IsToggled="{Binding AutoTotpCopy}"
StyleClass="box-value"
HorizontalOptions="End" />
</StackLayout>
<Label
Text="{u:I18n CopyTotpAutomaticallyDescription}"
StyleClass="box-footer-label, box-footer-label-switch" />
</StackLayout>
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row, box-row-switch">
<Label
Text="{u:I18n ShowWebsiteIcons}"
StyleClass="box-label-regular"
HorizontalOptions="StartAndExpand" />
<Switch
IsToggled="{Binding Favicon}"
StyleClass="box-value"
HorizontalOptions="End" />
</StackLayout>
<Label
Text="{u:I18n ShowWebsiteIconsDescription}"
StyleClass="box-footer-label, box-footer-label-switch" />
</StackLayout>
<StackLayout StyleClass="box" IsVisible="{Binding ShowAndroidAutofillSettings}">
<StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n AutofillService, Header=True}"
StyleClass="box-header, box-header-platform" />
</StackLayout>
<StackLayout StyleClass="box-row, box-row-switch">
<Label
Text="{u:I18n AskToAddLogin}"
StyleClass="box-label-regular"
HorizontalOptions="StartAndExpand" />
<Switch
IsToggled="{Binding AutofillSavePrompt}"
StyleClass="box-value"
HorizontalOptions="End" />
</StackLayout>
<Label
Text="{u:I18n AskToAddLoginDescription}"
StyleClass="box-footer-label, box-footer-label-switch" />
</StackLayout>
<StackLayout StyleClass="box" IsVisible="{Binding ShowAndroidAutofillSettings}">
<StackLayout StyleClass="box-row, box-row-input">
<Label
Text="{u:I18n AutofillBlockedUris}"
StyleClass="box-label" />
<Editor
x:Name="_autofillBlockedUrisEditor"
Text="{Binding AutofillBlockedUris}"
StyleClass="box-value"
AutoSize="TextChanges"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
Keyboard="Url"
Unfocused="AutofillBlockedUrisEditor_Unfocused" />
</StackLayout>
<Label
Text="{u:I18n AutofillBlockedUrisDescription}"
StyleClass="box-footer-label" />
</StackLayout>
</StackLayout>
</ScrollView>
</pages:BaseContentPage>

View File

@@ -1,66 +0,0 @@
using Bit.App.Abstractions;
using Bit.App.Resources;
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 partial class OptionsPage : BaseContentPage
{
private readonly IAutofillHandler _autofillHandler;
private readonly OptionsPageViewModel _vm;
public OptionsPage()
{
_autofillHandler = ServiceContainer.Resolve<IAutofillHandler>();
InitializeComponent();
_vm = BindingContext as OptionsPageViewModel;
_vm.Page = this;
_themePicker.ItemDisplayBinding = new Binding("Value");
_autoDarkThemePicker.ItemDisplayBinding = new Binding("Value");
_uriMatchPicker.ItemDisplayBinding = new Binding("Value");
_clearClipboardPicker.ItemDisplayBinding = new Binding("Value");
if (Device.RuntimePlatform == Device.Android)
{
ToolbarItems.RemoveAt(0);
_vm.ShowAndroidAutofillSettings = _autofillHandler.SupportsAutofillService();
}
else
{
_themePicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
_autoDarkThemePicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
_uriMatchPicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
_clearClipboardPicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
_languagePicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
}
}
protected async override void OnAppearing()
{
base.OnAppearing();
await _vm.InitAsync();
}
protected async override void OnDisappearing()
{
base.OnDisappearing();
await _vm.UpdateAutofillBlockedUris();
}
private async void AutofillBlockedUrisEditor_Unfocused(object sender, FocusEventArgs e)
{
await _vm.UpdateAutofillBlockedUris();
}
private async void Close_Clicked(object sender, System.EventArgs e)
{
if (DoOnce())
{
await Navigation.PopModalAsync();
}
}
}
}

View File

@@ -1,338 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.App.Resources;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class OptionsPageViewModel : BaseViewModel
{
private readonly IStateService _stateService;
private readonly IMessagingService _messagingService;
private readonly II18nService _i18nService;
private readonly IPlatformUtilsService _platformUtilsService;
private bool _autofillSavePrompt;
private string _autofillBlockedUris;
private bool _favicon;
private bool _autoTotpCopy;
private int _clearClipboardSelectedIndex;
private int _themeSelectedIndex;
private int _autoDarkThemeSelectedIndex;
private int _uriMatchSelectedIndex;
private KeyValuePair<string, string> _selectedLocale;
private bool _inited;
private bool _updatingAutofill;
private bool _showAndroidAutofillSettings;
public OptionsPageViewModel()
{
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_i18nService = ServiceContainer.Resolve<II18nService>();
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>();
PageTitle = AppResources.Options;
var iosIos = Device.RuntimePlatform == Device.iOS;
ClearClipboardOptions = new List<KeyValuePair<int?, string>>
{
new KeyValuePair<int?, string>(null, AppResources.Never),
new KeyValuePair<int?, string>(10, AppResources.TenSeconds),
new KeyValuePair<int?, string>(20, AppResources.TwentySeconds),
new KeyValuePair<int?, string>(30, AppResources.ThirtySeconds),
new KeyValuePair<int?, string>(60, AppResources.OneMinute)
};
if (!iosIos)
{
ClearClipboardOptions.Add(new KeyValuePair<int?, string>(120, AppResources.TwoMinutes));
ClearClipboardOptions.Add(new KeyValuePair<int?, string>(300, AppResources.FiveMinutes));
}
ThemeOptions = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>(null, AppResources.ThemeDefault),
new KeyValuePair<string, string>(ThemeManager.Light, AppResources.Light),
new KeyValuePair<string, string>(ThemeManager.Dark, AppResources.Dark),
new KeyValuePair<string, string>(ThemeManager.Black, AppResources.Black),
new KeyValuePair<string, string>(ThemeManager.Nord, AppResources.Nord),
new KeyValuePair<string, string>(ThemeManager.SolarizedDark, AppResources.SolarizedDark),
};
AutoDarkThemeOptions = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>(ThemeManager.Dark, AppResources.Dark),
new KeyValuePair<string, string>(ThemeManager.Black, AppResources.Black),
new KeyValuePair<string, string>(ThemeManager.Nord, AppResources.Nord),
new KeyValuePair<string, string>(ThemeManager.SolarizedDark, AppResources.SolarizedDark),
};
UriMatchOptions = new List<KeyValuePair<UriMatchType?, string>>
{
new KeyValuePair<UriMatchType?, string>(UriMatchType.Domain, AppResources.BaseDomain),
new KeyValuePair<UriMatchType?, string>(UriMatchType.Host, AppResources.Host),
new KeyValuePair<UriMatchType?, string>(UriMatchType.StartsWith, AppResources.StartsWith),
new KeyValuePair<UriMatchType?, string>(UriMatchType.RegularExpression, AppResources.RegEx),
new KeyValuePair<UriMatchType?, string>(UriMatchType.Exact, AppResources.Exact),
new KeyValuePair<UriMatchType?, string>(UriMatchType.Never, AppResources.Never),
};
LocalesOptions = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>(null, AppResources.DefaultSystem)
};
LocalesOptions.AddRange(_i18nService.LocaleNames.ToList());
}
public List<KeyValuePair<int?, string>> ClearClipboardOptions { get; set; }
public List<KeyValuePair<string, string>> ThemeOptions { get; set; }
public List<KeyValuePair<string, string>> AutoDarkThemeOptions { get; set; }
public List<KeyValuePair<UriMatchType?, string>> UriMatchOptions { get; set; }
public List<KeyValuePair<string, string>> LocalesOptions { get; }
public int ClearClipboardSelectedIndex
{
get => _clearClipboardSelectedIndex;
set
{
if (SetProperty(ref _clearClipboardSelectedIndex, value))
{
SaveClipboardChangedAsync().FireAndForget();
}
}
}
public int ThemeSelectedIndex
{
get => _themeSelectedIndex;
set
{
if (SetProperty(ref _themeSelectedIndex, value,
additionalPropertyNames: new[] { nameof(ShowAutoDarkThemeOptions) })
)
{
SaveThemeAsync().FireAndForget();
}
}
}
public bool ShowAutoDarkThemeOptions => ThemeOptions[ThemeSelectedIndex].Key == null;
public int AutoDarkThemeSelectedIndex
{
get => _autoDarkThemeSelectedIndex;
set
{
if (SetProperty(ref _autoDarkThemeSelectedIndex, value))
{
SaveThemeAsync().FireAndForget();
}
}
}
public int UriMatchSelectedIndex
{
get => _uriMatchSelectedIndex;
set
{
if (SetProperty(ref _uriMatchSelectedIndex, value))
{
SaveDefaultUriAsync().FireAndForget();
}
}
}
public KeyValuePair<string, string> SelectedLocale
{
get => _selectedLocale;
set
{
if (SetProperty(ref _selectedLocale, value))
{
UpdateCurrentLocaleAsync().FireAndForget();
}
}
}
public bool Favicon
{
get => _favicon;
set
{
if (SetProperty(ref _favicon, value))
{
UpdateFaviconAsync().FireAndForget();
}
}
}
public bool AutoTotpCopy
{
get => _autoTotpCopy;
set
{
if (SetProperty(ref _autoTotpCopy, value))
{
UpdateAutoTotpCopyAsync().FireAndForget();
}
}
}
public bool AutofillSavePrompt
{
get => _autofillSavePrompt;
set
{
if (SetProperty(ref _autofillSavePrompt, value))
{
UpdateAutofillSavePromptAsync().FireAndForget();
}
}
}
public string AutofillBlockedUris
{
get => _autofillBlockedUris;
set => SetProperty(ref _autofillBlockedUris, value);
}
public bool ShowAndroidAutofillSettings
{
get => _showAndroidAutofillSettings;
set => SetProperty(ref _showAndroidAutofillSettings, value);
}
public async Task InitAsync()
{
AutofillSavePrompt = !(await _stateService.GetAutofillDisableSavePromptAsync()).GetValueOrDefault();
var blockedUrisList = await _stateService.GetAutofillBlacklistedUrisAsync();
AutofillBlockedUris = blockedUrisList != null ? string.Join(", ", blockedUrisList) : null;
AutoTotpCopy = !(await _stateService.GetDisableAutoTotpCopyAsync() ?? false);
Favicon = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
var theme = await _stateService.GetThemeAsync();
ThemeSelectedIndex = ThemeOptions.FindIndex(k => k.Key == theme);
var autoDarkTheme = await _stateService.GetAutoDarkThemeAsync() ?? "dark";
AutoDarkThemeSelectedIndex = AutoDarkThemeOptions.FindIndex(k => k.Key == autoDarkTheme);
var defaultUriMatch = await _stateService.GetDefaultUriMatchAsync();
UriMatchSelectedIndex = defaultUriMatch == null ? 0 :
UriMatchOptions.FindIndex(k => (int?)k.Key == defaultUriMatch);
var clearClipboard = await _stateService.GetClearClipboardAsync();
ClearClipboardSelectedIndex = ClearClipboardOptions.FindIndex(k => k.Key == clearClipboard);
var appLocale = _stateService.GetLocale();
SelectedLocale = appLocale == null ? LocalesOptions.First() : LocalesOptions.FirstOrDefault(kv => kv.Key == appLocale);
_inited = true;
}
private async Task UpdateAutoTotpCopyAsync()
{
if (_inited)
{
// TODO: [PS-961] Fix negative function names
await _stateService.SetDisableAutoTotpCopyAsync(!AutoTotpCopy);
}
}
private async Task UpdateFaviconAsync()
{
if (_inited)
{
// TODO: [PS-961] Fix negative function names
await _stateService.SetDisableFaviconAsync(!Favicon);
}
}
private async Task SaveClipboardChangedAsync()
{
if (_inited && ClearClipboardSelectedIndex > -1)
{
await _stateService.SetClearClipboardAsync(ClearClipboardOptions[ClearClipboardSelectedIndex].Key);
}
}
private async Task SaveThemeAsync()
{
if (_inited && ThemeSelectedIndex > -1)
{
await _stateService.SetThemeAsync(ThemeOptions[ThemeSelectedIndex].Key);
await _stateService.SetAutoDarkThemeAsync(AutoDarkThemeOptions[AutoDarkThemeSelectedIndex].Key);
ThemeManager.SetTheme(Application.Current.Resources);
_messagingService.Send("updatedTheme");
}
}
private async Task SaveDefaultUriAsync()
{
if (_inited && UriMatchSelectedIndex > -1)
{
await _stateService.SetDefaultUriMatchAsync((int?)UriMatchOptions[UriMatchSelectedIndex].Key);
}
}
private async Task UpdateAutofillSavePromptAsync()
{
if (_inited)
{
// TODO: [PS-961] Fix negative function names
await _stateService.SetAutofillDisableSavePromptAsync(!AutofillSavePrompt);
}
}
public async Task UpdateAutofillBlockedUris()
{
if (_inited)
{
if (string.IsNullOrWhiteSpace(AutofillBlockedUris))
{
await _stateService.SetAutofillBlacklistedUrisAsync(null);
AutofillBlockedUris = null;
return;
}
try
{
var csv = AutofillBlockedUris;
var urisList = new List<string>();
foreach (var uri in csv.Split(','))
{
if (string.IsNullOrWhiteSpace(uri))
{
continue;
}
var cleanedUri = uri.Replace(System.Environment.NewLine, string.Empty).Trim();
if (!cleanedUri.StartsWith("http://") && !cleanedUri.StartsWith("https://") &&
!cleanedUri.StartsWith(Constants.AndroidAppProtocol))
{
continue;
}
urisList.Add(cleanedUri);
}
await _stateService.SetAutofillBlacklistedUrisAsync(urisList);
AutofillBlockedUris = string.Join(", ", urisList);
}
catch { }
}
}
private async Task UpdateCurrentLocaleAsync()
{
if (!_inited)
{
return;
}
_stateService.SetLocale(SelectedLocale.Key);
await _platformUtilsService.ShowDialogAsync(string.Format(AppResources.LanguageChangeXDescription, SelectedLocale.Value), AppResources.Language, AppResources.Ok);
}
}
}

View File

@@ -1,6 +0,0 @@
namespace Bit.App.Pages
{
public interface ISettingsPageListItem
{
}
}

View File

@@ -1,119 +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.SettingsPage"
xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="pages:SettingsPageViewModel"
Title="{Binding PageTitle}">
<ContentPage.BindingContext>
<pages:SettingsPageViewModel />
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<u:InverseBoolConverter x:Key="inverseBool" />
<u:StringHasValueConverter x:Key="stringHasValue" />
<DataTemplate
x:Key="regularTemplate"
x:DataType="pages:SettingsPageListItem">
<controls:ExtendedStackLayout Orientation="Horizontal"
StyleClass="list-row, list-row-platform">
<Frame
IsVisible="{Binding UseFrame}"
Padding="10"
HasShadow="False"
BackgroundColor="Transparent"
BorderColor="{DynamicResource PrimaryColor}">
<Label
Text="{Binding Name, Mode=OneWay}"
StyleClass="text-muted, text-sm, text-bold"
HorizontalTextAlignment="Center" />
</Frame>
<Label IsVisible="{Binding UseFrame, Converter={StaticResource inverseBool}}"
Text="{Binding Name, Mode=OneWay}"
LineBreakMode="{Binding LineBreakMode}"
HorizontalOptions="StartAndExpand"
VerticalOptions="CenterAndExpand"
StyleClass="list-title"/>
<Label Text="{Binding SubLabel, Mode=OneWay}"
IsVisible="{Binding ShowSubLabel}"
HorizontalOptions="End"
HorizontalTextAlignment="End"
VerticalOptions="CenterAndExpand"
TextColor="{Binding SubLabelColor}"
StyleClass="list-sub" />
</controls:ExtendedStackLayout>
</DataTemplate>
<DataTemplate
x:Key="timePickerTemplate"
x:DataType="pages:SettingsPageListItem">
<controls:ExtendedStackLayout Orientation="Horizontal"
StyleClass="list-row, list-row-platform">
<Frame
IsVisible="{Binding UseFrame}"
Padding="10"
HasShadow="False"
BackgroundColor="Transparent"
BorderColor="{DynamicResource PrimaryColor}">
<Label
Text="{Binding Name, Mode=OneWay}"
StyleClass="text-muted, text-sm, text-bold"
HorizontalTextAlignment="Center" />
</Frame>
<Label IsVisible="{Binding UseFrame, Converter={StaticResource inverseBool}}"
Text="{Binding Name, Mode=OneWay}"
LineBreakMode="{Binding LineBreakMode}"
HorizontalOptions="StartAndExpand"
VerticalOptions="CenterAndExpand"
StyleClass="list-title"/>
<TimePicker Time="{Binding Time}" Format="HH:mm"
PropertyChanged="OnTimePickerPropertyChanged"
HorizontalOptions="End"
VerticalOptions="Center"
FontSize="Small"
TextColor="{Binding SubLabelColor}"
StyleClass="list-sub" Margin="-5"/>
<controls:ExtendedStackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="ActivateTimePicker"/>
</controls:ExtendedStackLayout.GestureRecognizers>
</controls:ExtendedStackLayout>
</DataTemplate>
<DataTemplate
x:Key="headerTemplate"
x:DataType="pages:SettingsPageHeaderListItem">
<StackLayout
Padding="0" Spacing="0" VerticalOptions="FillAndExpand"
StyleClass="list-row-header-container, list-row-header-container-platform">
<BoxView
StyleClass="list-section-separator-top, list-section-separator-top-platform" />
<StackLayout StyleClass="list-row-header, list-row-header-platform">
<Label
Text="{Binding Title}"
StyleClass="list-header, list-header-platform" />
</StackLayout>
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
</StackLayout>
</DataTemplate>
<pages:SettingsPageListItemSelector
x:Key="listItemDataTemplateSelector"
HeaderTemplate="{StaticResource headerTemplate}"
RegularTemplate="{StaticResource regularTemplate}"
TimePickerTemplate="{StaticResource timePickerTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<controls:ExtendedCollectionView
ItemsSource="{Binding GroupedItems}"
VerticalOptions="FillAndExpand"
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
SelectionMode="Single"
SelectionChanged="RowSelected"
StyleClass="list, list-platform"
ExtraDataForLogging="Settings Page" />
</pages:BaseContentPage>

View File

@@ -1,73 +0,0 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Bit.App.Controls;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public partial class SettingsPage : BaseContentPage
{
private readonly TabsPage _tabsPage;
private SettingsPageViewModel _vm;
public SettingsPage(TabsPage tabsPage)
{
_tabsPage = tabsPage;
InitializeComponent();
_vm = BindingContext as SettingsPageViewModel;
_vm.Page = this;
}
public async Task InitAsync()
{
await _vm.InitAsync();
}
public void BuildList()
{
_vm.BuildList();
}
protected override bool OnBackButtonPressed()
{
if (Device.RuntimePlatform == Device.Android && _tabsPage != null)
{
_tabsPage.ResetToVaultPage();
return true;
}
return base.OnBackButtonPressed();
}
void ActivateTimePicker(object sender, EventArgs args)
{
var stackLayout = (ExtendedStackLayout)sender;
SettingsPageListItem item = (SettingsPageListItem)stackLayout.BindingContext;
if (item.ShowTimeInput)
{
var timePicker = stackLayout.Children.Where(x => x is TimePicker).FirstOrDefault();
((TimePicker)timePicker)?.Focus();
}
}
async void OnTimePickerPropertyChanged(object sender, PropertyChangedEventArgs args)
{
var s = (TimePicker)sender;
var time = s.Time.TotalMinutes;
if (s.IsFocused && args.PropertyName == "Time")
{
await _vm.VaultTimeoutAsync(false, (int)time);
}
}
private void RowSelected(object sender, SelectionChangedEventArgs e)
{
((ExtendedCollectionView)sender).SelectedItem = null;
if (e.CurrentSelection?.FirstOrDefault() is SettingsPageListItem item)
{
_vm?.ExecuteSettingItemCommand.Execute(item);
}
}
}
}

View File

@@ -1,12 +0,0 @@
namespace Bit.App.Pages
{
public class SettingsPageHeaderListItem : ISettingsPageListItem
{
public SettingsPageHeaderListItem(string title)
{
Title = title;
}
public string Title { get; }
}
}

View File

@@ -1,29 +0,0 @@
using System.Collections.Generic;
namespace Bit.App.Pages
{
public class SettingsPageListGroup : List<SettingsPageListItem>
{
public SettingsPageListGroup(List<SettingsPageListItem> groupItems, string name, bool doUpper = true,
bool first = false)
{
AddRange(groupItems);
if (string.IsNullOrWhiteSpace(name))
{
Name = "-";
}
else if (doUpper)
{
Name = name.ToUpperInvariant();
}
else
{
Name = name;
}
First = first;
}
public bool First { get; set; }
public string Name { get; set; }
}
}

View File

@@ -1,26 +0,0 @@
using System;
using System.Threading.Tasks;
using Bit.App.Resources;
using Bit.App.Utilities;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class SettingsPageListItem : ISettingsPageListItem
{
public string Icon { get; set; }
public string Name { get; set; }
public string SubLabel { get; set; }
public TimeSpan? Time { get; set; }
public bool UseFrame { get; set; }
public Func<Task> ExecuteAsync { get; set; }
public bool SubLabelTextEnabled => SubLabel == AppResources.On;
public string LineBreakMode => SubLabel == null ? "TailTruncation" : "";
public bool ShowSubLabel => SubLabel.Length != 0;
public bool ShowTimeInput => Time != null;
public Color SubLabelColor => SubLabelTextEnabled ?
ThemeManager.GetResourceColor("SuccessColor") :
ThemeManager.GetResourceColor("MutedColor");
}
}

View File

@@ -1,24 +0,0 @@
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class SettingsPageListItemSelector : DataTemplateSelector
{
public DataTemplate HeaderTemplate { get; set; }
public DataTemplate RegularTemplate { get; set; }
public DataTemplate TimePickerTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
if (item is SettingsPageHeaderListItem)
{
return HeaderTemplate;
}
if (item is SettingsPageListItem listItem)
{
return listItem.ShowTimeInput ? TimePickerTemplate : RegularTemplate;
}
return null;
}
}
}

View File

@@ -1,873 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Pages.Accounts;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Domain;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class SettingsPageViewModel : BaseViewModel
{
private readonly IPlatformUtilsService _platformUtilsService;
private readonly ICryptoService _cryptoService;
private readonly IStateService _stateService;
private readonly IDeviceActionService _deviceActionService;
private readonly IAutofillHandler _autofillHandler;
private readonly IEnvironmentService _environmentService;
private readonly IMessagingService _messagingService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly ISyncService _syncService;
private readonly IBiometricService _biometricService;
private readonly IPolicyService _policyService;
private readonly ILocalizeService _localizeService;
private readonly IKeyConnectorService _keyConnectorService;
private readonly IClipboardService _clipboardService;
private readonly ILogger _loggerService;
private readonly IPushNotificationService _pushNotificationService;
private readonly IAuthService _authService;
private readonly IWatchDeviceService _watchDeviceService;
private const int CustomVaultTimeoutValue = -100;
private bool _supportsBiometric;
private bool _pin;
private bool _biometric;
private bool _screenCaptureAllowed;
private string _lastSyncDate;
private string _vaultTimeoutDisplayValue;
private string _vaultTimeoutActionDisplayValue;
private bool _showChangeMasterPassword;
private bool _reportLoggingEnabled;
private bool _approvePasswordlessLoginRequests;
private bool _shouldConnectToWatch;
private readonly static List<KeyValuePair<string, int?>> VaultTimeoutOptions =
new List<KeyValuePair<string, int?>>
{
new KeyValuePair<string, int?>(AppResources.Immediately, 0),
new KeyValuePair<string, int?>(AppResources.OneMinute, 1),
new KeyValuePair<string, int?>(AppResources.FiveMinutes, 5),
new KeyValuePair<string, int?>(AppResources.FifteenMinutes, 15),
new KeyValuePair<string, int?>(AppResources.ThirtyMinutes, 30),
new KeyValuePair<string, int?>(AppResources.OneHour, 60),
new KeyValuePair<string, int?>(AppResources.FourHours, 240),
new KeyValuePair<string, int?>(AppResources.OnRestart, -1),
new KeyValuePair<string, int?>(AppResources.Never, null),
new KeyValuePair<string, int?>(AppResources.Custom, CustomVaultTimeoutValue),
};
private readonly static List<KeyValuePair<string, VaultTimeoutAction>> VaultTimeoutActionOptions =
new List<KeyValuePair<string, VaultTimeoutAction>>
{
new KeyValuePair<string, VaultTimeoutAction>(AppResources.Lock, VaultTimeoutAction.Lock),
new KeyValuePair<string, VaultTimeoutAction>(AppResources.LogOut, VaultTimeoutAction.Logout),
};
private Policy _vaultTimeoutPolicy;
private int? _vaultTimeout;
private List<KeyValuePair<string, int?>> _vaultTimeoutOptions = VaultTimeoutOptions;
private List<KeyValuePair<string, VaultTimeoutAction>> _vaultTimeoutActionOptions = VaultTimeoutActionOptions;
public SettingsPageViewModel()
{
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_autofillHandler = ServiceContainer.Resolve<IAutofillHandler>();
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
_loggerService = ServiceContainer.Resolve<ILogger>("logger");
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>();
_authService = ServiceContainer.Resolve<IAuthService>();
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
GroupedItems = new ObservableRangeCollection<ISettingsPageListItem>();
PageTitle = AppResources.Settings;
ExecuteSettingItemCommand = new AsyncCommand<SettingsPageListItem>(item => item.ExecuteAsync(), onException: _loggerService.Exception, allowsMultipleExecutions: false);
}
public ObservableRangeCollection<ISettingsPageListItem> GroupedItems { get; set; }
public IAsyncCommand<SettingsPageListItem> ExecuteSettingItemCommand { get; }
public async Task InitAsync()
{
_supportsBiometric = await _platformUtilsService.SupportsBiometricAsync();
var lastSync = await _syncService.GetLastSyncAsync();
if (lastSync != null)
{
lastSync = lastSync.Value.ToLocalTime();
_lastSyncDate = string.Format("{0} {1}",
_localizeService.GetLocaleShortDate(lastSync.Value),
_localizeService.GetLocaleShortTime(lastSync.Value));
}
_vaultTimeoutPolicy = null;
_vaultTimeoutOptions = VaultTimeoutOptions;
_vaultTimeoutActionOptions = VaultTimeoutActionOptions;
_vaultTimeout = await _vaultTimeoutService.GetVaultTimeout();
_vaultTimeoutDisplayValue = _vaultTimeoutOptions.FirstOrDefault(o => o.Value == _vaultTimeout).Key;
_vaultTimeoutDisplayValue ??= _vaultTimeoutOptions.Where(o => o.Value == CustomVaultTimeoutValue).First().Key;
var action = await _vaultTimeoutService.GetVaultTimeoutAction() ?? VaultTimeoutAction.Lock;
_vaultTimeoutActionDisplayValue = _vaultTimeoutActionOptions.FirstOrDefault(o => o.Value == action).Key;
if (await _policyService.PolicyAppliesToUser(PolicyType.MaximumVaultTimeout))
{
// if we have a vault timeout policy, we need to filter the timeout options
_vaultTimeoutPolicy = (await _policyService.GetAll(PolicyType.MaximumVaultTimeout)).First();
var policyMinutes = _vaultTimeoutPolicy.GetInt(Policy.MINUTES_KEY);
_vaultTimeoutOptions = _vaultTimeoutOptions.Where(t =>
t.Value <= policyMinutes &&
(t.Value > 0 || t.Value == CustomVaultTimeoutValue) &&
t.Value != null).ToList();
}
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
_pin = pinSet.Item1 || pinSet.Item2;
_biometric = await _vaultTimeoutService.IsBiometricLockSetAsync();
_screenCaptureAllowed = await _stateService.GetScreenCaptureAllowedAsync();
if (_vaultTimeoutDisplayValue == null)
{
_vaultTimeoutDisplayValue = AppResources.Custom;
}
_showChangeMasterPassword = IncludeLinksWithSubscriptionInfo() &&
!await _keyConnectorService.GetUsesKeyConnector();
_reportLoggingEnabled = await _loggerService.IsEnabled();
_approvePasswordlessLoginRequests = await _stateService.GetApprovePasswordlessLoginsAsync();
_shouldConnectToWatch = await _stateService.GetShouldConnectToWatchAsync();
BuildList();
}
public async Task AboutAsync()
{
var debugText = string.Format("{0}: {1} ({2})", AppResources.Version,
_platformUtilsService.GetApplicationVersion(), _deviceActionService.GetBuildNumber());
#if DEBUG
var pushNotificationsRegistered = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService").IsRegisteredForPush;
var pnServerRegDate = await _stateService.GetPushLastRegistrationDateAsync();
var pnServerError = await _stateService.GetPushInstallationRegistrationErrorAsync();
var pnServerRegDateMessage = default(DateTime) == pnServerRegDate ? "-" : $"{pnServerRegDate.GetValueOrDefault().ToShortDateString()}-{pnServerRegDate.GetValueOrDefault().ToShortTimeString()} UTC";
var errorMessage = string.IsNullOrEmpty(pnServerError) ? string.Empty : $"Push Notifications Server Registration error: {pnServerError}";
var text = string.Format("© Bitwarden Inc. 2015-{0}\n\n{1}\nPush Notifications registered:{2}\nPush Notifications Server Last Date :{3}\n{4}", DateTime.Now.Year, debugText, pushNotificationsRegistered, pnServerRegDateMessage, errorMessage);
#else
var text = string.Format("© Bitwarden Inc. 2015-{0}\n\n{1}", DateTime.Now.Year, debugText);
#endif
var copy = await _platformUtilsService.ShowDialogAsync(text, AppResources.Bitwarden, AppResources.Copy,
AppResources.Close);
if (copy)
{
await _clipboardService.CopyTextAsync(debugText);
}
}
public void Help()
{
_platformUtilsService.LaunchUri("https://bitwarden.com/help/");
}
public async Task FingerprintAsync()
{
List<string> fingerprint;
try
{
fingerprint = await _cryptoService.GetFingerprintAsync(await _stateService.GetActiveUserIdAsync());
}
catch (Exception e) when (e.Message == "No public key available.")
{
return;
}
var phrase = string.Join("-", fingerprint);
var text = string.Format("{0}:\n\n{1}", AppResources.YourAccountsFingerprint, phrase);
var learnMore = await _platformUtilsService.ShowDialogAsync(text, AppResources.FingerprintPhrase,
AppResources.LearnMore, AppResources.Close);
if (learnMore)
{
_platformUtilsService.LaunchUri("https://bitwarden.com/help/fingerprint-phrase/");
}
}
public void Rate()
{
_deviceActionService.RateApp();
}
public void Import()
{
_platformUtilsService.LaunchUri("https://bitwarden.com/help/import-data/");
}
public void WebVault()
{
_platformUtilsService.LaunchUri(_environmentService.GetWebVaultUrl());
}
public async Task ShareAsync()
{
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.LearnOrgConfirmation,
AppResources.LearnOrg, AppResources.Yes, AppResources.Cancel);
if (confirmed)
{
_platformUtilsService.LaunchUri("https://bitwarden.com/help/about-organizations/");
}
}
public async Task TwoStepAsync()
{
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.TwoStepLoginConfirmation,
AppResources.TwoStepLogin, AppResources.Yes, AppResources.Cancel);
if (confirmed)
{
_platformUtilsService.LaunchUri($"{_environmentService.GetWebVaultUrl()}/#/settings");
}
}
public async Task ChangePasswordAsync()
{
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.ChangePasswordConfirmation,
AppResources.ChangeMasterPassword, AppResources.Yes, AppResources.Cancel);
if (confirmed)
{
_platformUtilsService.LaunchUri($"{_environmentService.GetWebVaultUrl()}/#/settings");
}
}
public async Task LogOutAsync()
{
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.LogoutConfirmation,
AppResources.LogOut, AppResources.Yes, AppResources.Cancel);
if (confirmed)
{
_messagingService.Send("logout");
}
}
public async Task LockAsync()
{
await _vaultTimeoutService.LockAsync(true, true);
}
public async Task VaultTimeoutAsync(bool promptOptions = true, int? newTimeout = 0)
{
var oldTimeout = _vaultTimeout;
var options = _vaultTimeoutOptions.Select(
o => o.Key == _vaultTimeoutDisplayValue ? $"✓ {o.Key}" : o.Key).ToArray();
if (promptOptions)
{
var selection = await Page.DisplayActionSheet(AppResources.VaultTimeout,
AppResources.Cancel, null, options);
if (selection == null || selection == AppResources.Cancel)
{
return;
}
var cleanSelection = selection.Replace("✓ ", string.Empty);
var selectionOption = _vaultTimeoutOptions.FirstOrDefault(o => o.Key == cleanSelection);
// Check if the selected Timeout action is "Never" and if it's different from the previous selected value
if (selectionOption.Value == null && selectionOption.Value != oldTimeout)
{
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.NeverLockWarning,
AppResources.Warning, AppResources.Yes, AppResources.Cancel);
if (!confirmed)
{
return;
}
}
_vaultTimeoutDisplayValue = selectionOption.Key;
newTimeout = selectionOption.Value;
}
if (_vaultTimeoutPolicy != null)
{
var maximumTimeout = _vaultTimeoutPolicy.GetInt(Policy.MINUTES_KEY);
if (newTimeout > maximumTimeout)
{
await _platformUtilsService.ShowDialogAsync(AppResources.VaultTimeoutToLarge, AppResources.Warning);
var timeout = await _vaultTimeoutService.GetVaultTimeout();
_vaultTimeoutDisplayValue = _vaultTimeoutOptions.FirstOrDefault(o => o.Value == timeout).Key ??
AppResources.Custom;
return;
}
}
await _vaultTimeoutService.SetVaultTimeoutOptionsAsync(newTimeout,
GetVaultTimeoutActionFromKey(_vaultTimeoutActionDisplayValue));
if (newTimeout != CustomVaultTimeoutValue)
{
_vaultTimeout = newTimeout;
}
if (oldTimeout != newTimeout)
{
await Device.InvokeOnMainThreadAsync(BuildList);
}
}
public async Task LoggerReportingAsync()
{
var options = new[]
{
CreateSelectableOption(AppResources.Yes, _reportLoggingEnabled),
CreateSelectableOption(AppResources.No, !_reportLoggingEnabled),
};
var selection = await Page.DisplayActionSheet(AppResources.SubmitCrashLogsDescription, AppResources.Cancel, null, options);
if (selection == null || selection == AppResources.Cancel)
{
return;
}
await _loggerService.SetEnabled(CompareSelection(selection, AppResources.Yes));
_reportLoggingEnabled = await _loggerService.IsEnabled();
BuildList();
}
public async Task ApproveLoginRequestsAsync()
{
var options = new[]
{
CreateSelectableOption(AppResources.Yes, _approvePasswordlessLoginRequests),
CreateSelectableOption(AppResources.No, !_approvePasswordlessLoginRequests),
};
var selection = await Page.DisplayActionSheet(AppResources.UseThisDeviceToApproveLoginRequestsMadeFromOtherDevices, AppResources.Cancel, null, options);
if (selection == null || selection == AppResources.Cancel)
{
return;
}
_approvePasswordlessLoginRequests = CompareSelection(selection, AppResources.Yes);
await _stateService.SetApprovePasswordlessLoginsAsync(_approvePasswordlessLoginRequests);
BuildList();
if (!_approvePasswordlessLoginRequests || await _pushNotificationService.AreNotificationsSettingsEnabledAsync())
{
return;
}
var openAppSettingsResult = await _platformUtilsService.ShowDialogAsync(AppResources.ReceivePushNotificationsForNewLoginRequests, title: string.Empty, confirmText: AppResources.Settings, cancelText: AppResources.NoThanks);
if (openAppSettingsResult)
{
_deviceActionService.OpenAppSettings();
}
}
public async Task VaultTimeoutActionAsync()
{
if (_vaultTimeoutPolicy != null &&
!string.IsNullOrEmpty(_vaultTimeoutPolicy.GetString(Policy.ACTION_KEY)))
{
// do nothing if we have a policy set
return;
}
var options = _vaultTimeoutActionOptions.Select(o =>
o.Key == _vaultTimeoutActionDisplayValue ? $"✓ {o.Key}" : o.Key).ToArray();
var selection = await Page.DisplayActionSheet(AppResources.VaultTimeoutAction,
AppResources.Cancel, null, options);
if (selection == null || selection == AppResources.Cancel)
{
return;
}
var cleanSelection = selection.Replace("✓ ", string.Empty);
if (cleanSelection == AppResources.LogOut)
{
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.VaultTimeoutLogOutConfirmation,
AppResources.Warning, AppResources.Yes, AppResources.Cancel);
if (!confirmed)
{
// Reset to lock and continue process as if lock were selected
cleanSelection = AppResources.Lock;
}
}
var selectionOption = _vaultTimeoutActionOptions.FirstOrDefault(o => o.Key == cleanSelection);
var changed = _vaultTimeoutActionDisplayValue != selectionOption.Key;
_vaultTimeoutActionDisplayValue = selectionOption.Key;
await _vaultTimeoutService.SetVaultTimeoutOptionsAsync(_vaultTimeout,
selectionOption.Value);
if (changed)
{
_messagingService.Send("vaultTimeoutActionChanged");
}
BuildList();
}
public async Task UpdatePinAsync()
{
_pin = !_pin;
if (_pin)
{
var pin = await _deviceActionService.DisplayPromptAync(AppResources.EnterPIN,
AppResources.SetPINDescription, null, AppResources.Submit, AppResources.Cancel, true);
if (!string.IsNullOrWhiteSpace(pin))
{
var masterPassOnRestart = false;
if (!await _keyConnectorService.GetUsesKeyConnector())
{
masterPassOnRestart = await _platformUtilsService.ShowDialogAsync(
AppResources.PINRequireMasterPasswordRestart, AppResources.UnlockWithPIN,
AppResources.Yes, AppResources.No);
}
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
var email = await _stateService.GetEmailAsync();
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email, kdfConfig);
var key = await _cryptoService.GetKeyAsync();
var pinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
if (masterPassOnRestart)
{
var encPin = await _cryptoService.EncryptAsync(pin);
await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
await _stateService.SetPinProtectedKeyAsync(pinProtectedKey);
}
else
{
await _stateService.SetPinProtectedAsync(pinProtectedKey.EncryptedString);
}
}
else
{
_pin = false;
}
}
if (!_pin)
{
await _cryptoService.ClearPinProtectedKeyAsync();
await _vaultTimeoutService.ClearAsync();
}
BuildList();
}
public async Task UpdateBiometricAsync()
{
var current = _biometric;
if (_biometric)
{
_biometric = false;
}
else if (await _platformUtilsService.SupportsBiometricAsync())
{
_biometric = await _platformUtilsService.AuthenticateBiometricAsync(null,
Device.RuntimePlatform == Device.Android ? "." : null);
}
if (_biometric == current)
{
return;
}
if (_biometric)
{
await _biometricService.SetupBiometricAsync();
await _stateService.SetBiometricUnlockAsync(true);
}
else
{
await _stateService.SetBiometricUnlockAsync(null);
}
await _stateService.SetBiometricLockedAsync(false);
await _cryptoService.ToggleKeyAsync();
BuildList();
}
public void BuildList()
{
//TODO: Refactor this once navigation is abstracted so that it doesn't depend on Page, e.g. Page.Navigation.PushModalAsync...
var doUpper = Device.RuntimePlatform != Device.Android;
var autofillItems = new List<SettingsPageListItem>();
if (Device.RuntimePlatform == Device.Android)
{
autofillItems.Add(new SettingsPageListItem
{
Name = AppResources.AutofillServices,
SubLabel = _autofillHandler.AutofillServicesEnabled() ? AppResources.On : AppResources.Off,
ExecuteAsync = () => Page.Navigation.PushModalAsync(new NavigationPage(new AutofillServicesPage(Page as SettingsPage)))
});
}
else
{
if (_deviceActionService.SystemMajorVersion() >= 12)
{
autofillItems.Add(new SettingsPageListItem
{
Name = AppResources.PasswordAutofill,
ExecuteAsync = () => Page.Navigation.PushModalAsync(new NavigationPage(new AutofillPage()))
});
}
autofillItems.Add(new SettingsPageListItem
{
Name = AppResources.AppExtension,
ExecuteAsync = () => Page.Navigation.PushModalAsync(new NavigationPage(new ExtensionPage()))
});
}
var manageItems = new List<SettingsPageListItem>
{
new SettingsPageListItem
{
Name = AppResources.Folders,
ExecuteAsync = () => Page.Navigation.PushModalAsync(new NavigationPage(new FoldersPage()))
},
new SettingsPageListItem
{
Name = AppResources.Sync,
SubLabel = _lastSyncDate,
ExecuteAsync = () => Page.Navigation.PushModalAsync(new NavigationPage(new SyncPage()))
}
};
var securityItems = new List<SettingsPageListItem>
{
new SettingsPageListItem
{
Name = AppResources.VaultTimeout,
SubLabel = _vaultTimeoutDisplayValue,
ExecuteAsync = () => VaultTimeoutAsync() },
new SettingsPageListItem
{
Name = AppResources.VaultTimeoutAction,
SubLabel = _vaultTimeoutActionDisplayValue,
ExecuteAsync = () => VaultTimeoutActionAsync()
},
new SettingsPageListItem
{
Name = AppResources.UnlockWithPIN,
SubLabel = _pin ? AppResources.On : AppResources.Off,
ExecuteAsync = () => UpdatePinAsync()
},
new SettingsPageListItem
{
Name = AppResources.ApproveLoginRequests,
SubLabel = _approvePasswordlessLoginRequests ? AppResources.On : AppResources.Off,
ExecuteAsync = () => ApproveLoginRequestsAsync()
},
new SettingsPageListItem
{
Name = AppResources.LockNow,
ExecuteAsync = () => LockAsync()
},
new SettingsPageListItem
{
Name = AppResources.TwoStepLogin,
ExecuteAsync = () => TwoStepAsync()
}
};
if (_approvePasswordlessLoginRequests)
{
manageItems.Add(new SettingsPageListItem
{
Name = AppResources.PendingLogInRequests,
ExecuteAsync = () => PendingLoginRequestsAsync()
});
}
if (_supportsBiometric || _biometric)
{
var biometricName = AppResources.Biometrics;
if (Device.RuntimePlatform == Device.iOS)
{
biometricName = _deviceActionService.SupportsFaceBiometric() ? AppResources.FaceID :
AppResources.TouchID;
}
var item = new SettingsPageListItem
{
Name = string.Format(AppResources.UnlockWith, biometricName),
SubLabel = _biometric ? AppResources.On : AppResources.Off,
ExecuteAsync = () => UpdateBiometricAsync()
};
securityItems.Insert(2, item);
}
if (_vaultTimeoutDisplayValue == AppResources.Custom)
{
securityItems.Insert(1, new SettingsPageListItem
{
Name = AppResources.Custom,
Time = TimeSpan.FromMinutes(Math.Abs((double)_vaultTimeout.GetValueOrDefault())),
});
}
if (_vaultTimeoutPolicy != null)
{
var policyMinutes = _vaultTimeoutPolicy.GetInt(Policy.MINUTES_KEY);
var policyAction = _vaultTimeoutPolicy.GetString(Policy.ACTION_KEY);
if (policyMinutes.HasValue || !string.IsNullOrWhiteSpace(policyAction))
{
string policyAlert;
if (policyMinutes.HasValue && string.IsNullOrWhiteSpace(policyAction))
{
policyAlert = string.Format(AppResources.VaultTimeoutPolicyInEffect,
Math.Floor((float)policyMinutes / 60),
policyMinutes % 60);
}
else if (!policyMinutes.HasValue && !string.IsNullOrWhiteSpace(policyAction))
{
policyAlert = string.Format(AppResources.VaultTimeoutActionPolicyInEffect,
policyAction == Policy.ACTION_LOCK ? AppResources.Lock : AppResources.LogOut);
}
else
{
policyAlert = string.Format(AppResources.VaultTimeoutPolicyWithActionInEffect,
Math.Floor((float)policyMinutes / 60),
policyMinutes % 60,
policyAction == Policy.ACTION_LOCK ? AppResources.Lock : AppResources.LogOut);
}
securityItems.Insert(0, new SettingsPageListItem
{
Name = policyAlert,
UseFrame = true,
});
}
}
if (Device.RuntimePlatform == Device.Android)
{
securityItems.Add(new SettingsPageListItem
{
Name = AppResources.AllowScreenCapture,
SubLabel = _screenCaptureAllowed ? AppResources.On : AppResources.Off,
ExecuteAsync = () => SetScreenCaptureAllowedAsync()
});
}
var accountItems = new List<SettingsPageListItem>();
if (Device.RuntimePlatform == Device.iOS)
{
accountItems.Add(new SettingsPageListItem
{
Name = AppResources.ConnectToWatch,
SubLabel = _shouldConnectToWatch ? AppResources.On : AppResources.Off,
ExecuteAsync = () => ToggleWatchConnectionAsync()
});
}
accountItems.Add(new SettingsPageListItem
{
Name = AppResources.FingerprintPhrase,
ExecuteAsync = () => FingerprintAsync()
});
accountItems.Add(new SettingsPageListItem
{
Name = AppResources.LogOut,
ExecuteAsync = () => LogOutAsync()
});
if (_showChangeMasterPassword)
{
accountItems.Insert(0, new SettingsPageListItem
{
Name = AppResources.ChangeMasterPassword,
ExecuteAsync = () => ChangePasswordAsync()
});
}
var toolsItems = new List<SettingsPageListItem>
{
new SettingsPageListItem
{
Name = AppResources.ImportItems,
ExecuteAsync = () => Device.InvokeOnMainThreadAsync(() => Import())
},
new SettingsPageListItem
{
Name = AppResources.ExportVault,
ExecuteAsync = () => Page.Navigation.PushModalAsync(new NavigationPage(new ExportVaultPage()))
}
};
if (IncludeLinksWithSubscriptionInfo())
{
toolsItems.Add(new SettingsPageListItem
{
Name = AppResources.LearnOrg,
ExecuteAsync = () => ShareAsync()
});
toolsItems.Add(new SettingsPageListItem
{
Name = AppResources.WebVault,
ExecuteAsync = () => Device.InvokeOnMainThreadAsync(() => WebVault())
});
}
var otherItems = new List<SettingsPageListItem>
{
new SettingsPageListItem
{
Name = AppResources.Options,
ExecuteAsync = () => Page.Navigation.PushModalAsync(new NavigationPage(new OptionsPage()))
},
new SettingsPageListItem
{
Name = AppResources.About,
ExecuteAsync = () => AboutAsync()
},
new SettingsPageListItem
{
Name = AppResources.HelpAndFeedback,
ExecuteAsync = () => Device.InvokeOnMainThreadAsync(() => Help())
},
#if !FDROID
new SettingsPageListItem
{
Name = AppResources.SubmitCrashLogs,
SubLabel = _reportLoggingEnabled ? AppResources.On : AppResources.Off,
ExecuteAsync = () => LoggerReportingAsync()
},
#endif
new SettingsPageListItem
{
Name = AppResources.RateTheApp,
ExecuteAsync = () => Device.InvokeOnMainThreadAsync(() => Rate())
},
new SettingsPageListItem
{
Name = AppResources.DeleteAccount,
ExecuteAsync = () => Page.Navigation.PushModalAsync(new NavigationPage(new DeleteAccountPage()))
}
};
// TODO: improve this. Leaving this as is to reduce error possibility on the hotfix.
var settingsListGroupItems = new List<SettingsPageListGroup>()
{
new SettingsPageListGroup(autofillItems, AppResources.Autofill, doUpper, true),
new SettingsPageListGroup(manageItems, AppResources.Manage, doUpper),
new SettingsPageListGroup(securityItems, AppResources.Security, doUpper),
new SettingsPageListGroup(accountItems, AppResources.Account, doUpper),
new SettingsPageListGroup(toolsItems, AppResources.Tools, doUpper),
new SettingsPageListGroup(otherItems, AppResources.Other, doUpper)
};
// TODO: refactor this
if (Device.RuntimePlatform == Device.Android
||
GroupedItems.Any())
{
var items = new List<ISettingsPageListItem>();
foreach (var itemGroup in settingsListGroupItems)
{
items.Add(new SettingsPageHeaderListItem(itemGroup.Name));
items.AddRange(itemGroup);
}
GroupedItems.ReplaceRange(items);
}
else
{
// HACK: we need this on iOS, so that it doesn't crash when adding coming from an empty list
var first = true;
var items = new List<ISettingsPageListItem>();
foreach (var itemGroup in settingsListGroupItems)
{
if (!first)
{
items.Add(new SettingsPageHeaderListItem(itemGroup.Name));
}
else
{
first = false;
}
items.AddRange(itemGroup);
}
if (settingsListGroupItems.Any())
{
GroupedItems.ReplaceRange(new List<ISettingsPageListItem> { new SettingsPageHeaderListItem(settingsListGroupItems[0].Name) });
GroupedItems.AddRange(items);
}
else
{
GroupedItems.Clear();
}
}
}
private async Task PendingLoginRequestsAsync()
{
try
{
var requests = await _authService.GetActivePasswordlessLoginRequestsAsync();
if (requests == null || !requests.Any())
{
_platformUtilsService.ShowToast("info", null, AppResources.NoPendingRequests);
return;
}
Page.Navigation.PushModalAsync(new NavigationPage(new LoginPasswordlessRequestsListPage())).FireAndForget();
}
catch (Exception ex)
{
HandleException(ex);
}
}
private bool IncludeLinksWithSubscriptionInfo()
{
if (Device.RuntimePlatform == Device.iOS)
{
return false;
}
return true;
}
private VaultTimeoutAction GetVaultTimeoutActionFromKey(string key)
{
return _vaultTimeoutActionOptions.FirstOrDefault(o => o.Key == key).Value;
}
private int? GetVaultTimeoutFromKey(string key)
{
return _vaultTimeoutOptions.FirstOrDefault(o => o.Key == key).Value;
}
private string CreateSelectableOption(string option, bool selected) => selected ? $"✓ {option}" : option;
private bool CompareSelection(string selection, string compareTo) => selection == compareTo || selection == $"✓ {compareTo}";
public async Task SetScreenCaptureAllowedAsync()
{
try
{
if (!_screenCaptureAllowed
&&
!await Page.DisplayAlert(AppResources.AllowScreenCapture, AppResources.AreYouSureYouWantToEnableScreenCapture, AppResources.Yes, AppResources.No))
{
return;
}
await _stateService.SetScreenCaptureAllowedAsync(!_screenCaptureAllowed);
_screenCaptureAllowed = !_screenCaptureAllowed;
await _deviceActionService.SetScreenCaptureAllowedAsync();
BuildList();
}
catch (Exception ex)
{
_loggerService.Exception(ex);
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, AppResources.GenericErrorMessage, AppResources.Ok);
}
}
private async Task ToggleWatchConnectionAsync()
{
_shouldConnectToWatch = !_shouldConnectToWatch;
await _watchDeviceService.SetShouldConnectToWatchAsync(_shouldConnectToWatch);
BuildList();
}
}
}

View File

@@ -1,51 +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.SyncPage"
xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="pages:SyncPageViewModel"
Title="{Binding PageTitle}">
<ContentPage.BindingContext>
<pages:SyncPageViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
</ContentPage.ToolbarItems>
<ScrollView Padding="0, 0, 0, 20">
<StackLayout Padding="0" Spacing="20">
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row, box-row-switch">
<Label
Text="{u:I18n EnableSyncOnRefresh}"
StyleClass="box-label-regular"
HorizontalOptions="StartAndExpand" />
<Switch
IsToggled="{Binding EnableSyncOnRefresh}"
StyleClass="box-value"
HorizontalOptions="End" />
</StackLayout>
<Label
Text="{u:I18n EnableSyncOnRefreshDescription}"
StyleClass="box-footer-label, box-footer-label-switch" />
</StackLayout>
<StackLayout StyleClass="box">
<Button Text="{u:I18n SyncVaultNow}" Clicked="Sync_Clicked"></Button>
<Label StyleClass="text-muted, text-sm" HorizontalTextAlignment="Center" Margin="0,10">
<Label.FormattedText>
<FormattedString>
<Span Text="{u:I18n LastSync}" />
<Span Text=" " />
<Span Text="{Binding LastSync}" />
</FormattedString>
</Label.FormattedText>
</Label>
</StackLayout>
</StackLayout>
</ScrollView>
</pages:BaseContentPage>

View File

@@ -1,43 +0,0 @@
using System;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public partial class SyncPage : BaseContentPage
{
private readonly SyncPageViewModel _vm;
public SyncPage()
{
InitializeComponent();
_vm = BindingContext as SyncPageViewModel;
_vm.Page = this;
if (Device.RuntimePlatform == Device.Android)
{
ToolbarItems.RemoveAt(0);
}
}
protected async override void OnAppearing()
{
base.OnAppearing();
await _vm.InitAsync();
}
private async void Sync_Clicked(object sender, EventArgs e)
{
if (DoOnce())
{
await _vm.SyncAsync();
}
}
private async void Close_Clicked(object sender, System.EventArgs e)
{
if (DoOnce())
{
await Navigation.PopModalAsync();
}
}
}
}

View File

@@ -1,117 +0,0 @@
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Exceptions;
using Bit.Core.Utilities;
namespace Bit.App.Pages
{
public class SyncPageViewModel : BaseViewModel
{
private readonly IDeviceActionService _deviceActionService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IStateService _stateService;
private readonly ISyncService _syncService;
private readonly ILocalizeService _localizeService;
private string _lastSync = "--";
private bool _inited;
private bool _syncOnRefresh;
public SyncPageViewModel()
{
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
PageTitle = AppResources.Sync;
}
public bool EnableSyncOnRefresh
{
get => _syncOnRefresh;
set
{
if (SetProperty(ref _syncOnRefresh, value))
{
var task = UpdateSyncOnRefreshAsync();
}
}
}
public string LastSync
{
get => _lastSync;
set => SetProperty(ref _lastSync, value);
}
public async Task InitAsync()
{
await SetLastSyncAsync();
EnableSyncOnRefresh = await _stateService.GetSyncOnRefreshAsync();
_inited = true;
}
public async Task UpdateSyncOnRefreshAsync()
{
if (_inited)
{
await _stateService.SetSyncOnRefreshAsync(_syncOnRefresh);
}
}
public async Task SetLastSyncAsync()
{
var last = await _syncService.GetLastSyncAsync();
if (last != null)
{
var localDate = last.Value.ToLocalTime();
LastSync = string.Format("{0} {1}",
_localizeService.GetLocaleShortDate(localDate),
_localizeService.GetLocaleShortTime(localDate));
}
else
{
LastSync = AppResources.Never;
}
}
public async Task SyncAsync()
{
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
{
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
AppResources.InternetConnectionRequiredTitle);
return;
}
try
{
await _deviceActionService.ShowLoadingAsync(AppResources.Syncing);
await _syncService.SyncPasswordlessLoginRequestsAsync();
var success = await _syncService.FullSyncAsync(true);
await _deviceActionService.HideLoadingAsync();
if (success)
{
await SetLastSyncAsync();
_platformUtilsService.ShowToast("success", null, AppResources.SyncingComplete);
}
else
{
await Page.DisplayAlert(null, AppResources.SyncingFailed, AppResources.Ok);
}
}
catch (ApiException e)
{
await _deviceActionService.HideLoadingAsync();
if (e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
}
}
}

View File

@@ -1,178 +0,0 @@
using System;
using System.Threading.Tasks;
using Bit.App.Effects;
using Bit.App.Models;
using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class TabsPage : TabbedPage
{
private readonly IBroadcasterService _broadcasterService;
private readonly IMessagingService _messagingService;
private readonly IKeyConnectorService _keyConnectorService;
private readonly IStateService _stateService;
private readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
private NavigationPage _groupingsPage;
private NavigationPage _sendGroupingsPage;
private NavigationPage _generatorPage;
public TabsPage(AppOptions appOptions = null, PreviousPageInfo previousPage = null)
{
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
_stateService = ServiceContainer.Resolve<IStateService>();
_groupingsPage = new NavigationPage(new GroupingsPage(true, previousPage: previousPage))
{
Title = AppResources.MyVault,
IconImageSource = "lock.png"
};
Children.Add(_groupingsPage);
_sendGroupingsPage = new NavigationPage(new SendGroupingsPage(true, null, null, appOptions))
{
Title = AppResources.Send,
IconImageSource = "send.png",
};
Children.Add(_sendGroupingsPage);
_generatorPage = new NavigationPage(new GeneratorPage(true, null, this))
{
Title = AppResources.Generator,
IconImageSource = "generate.png"
};
Children.Add(_generatorPage);
var settingsPage = new NavigationPage(new SettingsPage(this))
{
Title = AppResources.Settings,
IconImageSource = "cog_settings.png"
};
Children.Add(settingsPage);
if (Device.RuntimePlatform == Device.Android)
{
Effects.Add(new TabBarEffect());
Xamarin.Forms.PlatformConfiguration.AndroidSpecific.TabbedPage.SetToolbarPlacement(this,
Xamarin.Forms.PlatformConfiguration.AndroidSpecific.ToolbarPlacement.Bottom);
Xamarin.Forms.PlatformConfiguration.AndroidSpecific.TabbedPage.SetIsSwipePagingEnabled(this, false);
Xamarin.Forms.PlatformConfiguration.AndroidSpecific.TabbedPage.SetIsSmoothScrollEnabled(this, false);
}
if (appOptions?.GeneratorTile ?? false)
{
appOptions.GeneratorTile = false;
ResetToGeneratorPage();
}
else if (appOptions?.MyVaultTile ?? false)
{
appOptions.MyVaultTile = false;
}
else if (appOptions?.CreateSend != null)
{
ResetToSendPage();
}
}
protected override async void OnAppearing()
{
base.OnAppearing();
_broadcasterService.Subscribe(nameof(TabsPage), async (message) =>
{
if (message.Command == "syncCompleted")
{
Device.BeginInvokeOnMainThread(async () => await UpdateVaultButtonTitleAsync());
}
});
await UpdateVaultButtonTitleAsync();
if (await _keyConnectorService.UserNeedsMigration())
{
_messagingService.Send("convertAccountToKeyConnector");
}
var forcePasswordResetReason = await _stateService.GetForcePasswordResetReasonAsync();
if (forcePasswordResetReason.HasValue)
{
_messagingService.Send(Constants.ForceUpdatePassword);
}
}
protected override void OnDisappearing()
{
base.OnDisappearing();
_broadcasterService.Unsubscribe(nameof(TabsPage));
}
public void ResetToVaultPage()
{
CurrentPage = _groupingsPage;
}
public void ResetToGeneratorPage()
{
CurrentPage = _generatorPage;
}
public void ResetToSendPage()
{
CurrentPage = _sendGroupingsPage;
}
protected async override void OnCurrentPageChanged()
{
if (CurrentPage is NavigationPage navPage)
{
if (_groupingsPage?.RootPage is GroupingsPage groupingsPage)
{
await groupingsPage.HideAccountSwitchingOverlayAsync();
}
_messagingService.Send("updatedTheme");
if (navPage.RootPage is GroupingsPage)
{
// Load something?
}
else if (navPage.RootPage is GeneratorPage genPage)
{
await genPage.InitAsync();
}
else if (navPage.RootPage is SettingsPage settingsPage)
{
await settingsPage.InitAsync();
}
}
}
public void OnPageReselected()
{
if (_groupingsPage?.RootPage is GroupingsPage groupingsPage)
{
groupingsPage.HideAccountSwitchingOverlayAsync().FireAndForget();
}
}
private async Task UpdateVaultButtonTitleAsync()
{
try
{
var policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
var isShowingVaultFilter = await policyService.ShouldShowVaultFilterAsync();
_groupingsPage.Title = isShowingVaultFilter ? AppResources.Vaults : AppResources.MyVault;
}
catch (Exception ex)
{
_logger.Value.Exception(ex);
}
}
}
}

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
{
@@ -107,6 +112,7 @@ namespace Bit.Droid.Accessibility
new Browser("org.bromite.chromium", "url_bar"),
new Browser("org.chromium.chrome", "url_bar"),
new Browser("org.codeaurora.swe.browser", "url_bar"),
new Browser("org.cromite.cromite", "url_bar"),
new Browser("org.gnu.icecat", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
new Browser("org.mozilla.fenix", "mozac_browser_toolbar_url_view"),
new Browser("org.mozilla.fenix.nightly", "mozac_browser_toolbar_url_view"), // [DEPRECATED ENTRY]
@@ -704,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
{

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