mirror of
https://github.com/bitwarden/android.git
synced 2024-12-25 10:28:28 +03:00
[EC-325] Settings option to allow screen capture on Android (#1914)
* settings option to allow screen capture on Android * Improved code on Screen Capture and added prompt to the user to allow screen capture * EC-325 Removed async on OnCreate of MainActivity given that's not necessary anymore Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
This commit is contained in:
parent
cb0c52fb26
commit
cf222bd0c3
13 changed files with 5991 additions and 3843 deletions
|
@ -64,10 +64,11 @@ namespace Bit.Droid
|
||||||
Intent?.Validate();
|
Intent?.Validate();
|
||||||
|
|
||||||
base.OnCreate(savedInstanceState);
|
base.OnCreate(savedInstanceState);
|
||||||
if (!CoreHelpers.InDebugMode())
|
|
||||||
|
_deviceActionService.SetScreenCaptureAllowedAsync().FireAndForget(_ =>
|
||||||
{
|
{
|
||||||
Window.AddFlags(Android.Views.WindowManagerFlags.Secure);
|
Window.AddFlags(Android.Views.WindowManagerFlags.Secure);
|
||||||
}
|
});
|
||||||
|
|
||||||
ServiceContainer.Resolve<ILogger>("logger").InitAsync();
|
ServiceContainer.Resolve<ILogger>("logger").InitAsync();
|
||||||
|
|
||||||
|
|
|
@ -948,5 +948,21 @@ namespace Bit.Droid.Services
|
||||||
{
|
{
|
||||||
// for any Android-specific cleanup required after switching accounts
|
// for any Android-specific cleanup required after switching accounts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task SetScreenCaptureAllowedAsync()
|
||||||
|
{
|
||||||
|
if (CoreHelpers.ForceScreenCaptureEnabled())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var activity = CrossCurrentActivity.Current?.Activity;
|
||||||
|
if (await _stateService.GetScreenCaptureAllowedAsync())
|
||||||
|
{
|
||||||
|
activity.RunOnUiThread(() => activity.Window.ClearFlags(WindowManagerFlags.Secure));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
activity.RunOnUiThread(() => activity.Window.AddFlags(WindowManagerFlags.Secure));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,5 +48,6 @@ namespace Bit.App.Abstractions
|
||||||
bool SupportsFido2();
|
bool SupportsFido2();
|
||||||
float GetSystemFontSizeScale();
|
float GetSystemFontSizeScale();
|
||||||
Task OnAccountSwitchCompleteAsync();
|
Task OnAccountSwitchCompleteAsync();
|
||||||
|
Task SetScreenCaptureAllowedAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Services;
|
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.CommunityToolkit.ObjectModel;
|
using Xamarin.CommunityToolkit.ObjectModel;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
@ -31,11 +30,13 @@ namespace Bit.App.Pages
|
||||||
private readonly IKeyConnectorService _keyConnectorService;
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
private readonly IClipboardService _clipboardService;
|
private readonly IClipboardService _clipboardService;
|
||||||
private readonly ILogger _loggerService;
|
private readonly ILogger _loggerService;
|
||||||
|
|
||||||
private const int CustomVaultTimeoutValue = -100;
|
private const int CustomVaultTimeoutValue = -100;
|
||||||
|
|
||||||
private bool _supportsBiometric;
|
private bool _supportsBiometric;
|
||||||
private bool _pin;
|
private bool _pin;
|
||||||
private bool _biometric;
|
private bool _biometric;
|
||||||
|
private bool _screenCaptureAllowed;
|
||||||
private string _lastSyncDate;
|
private string _lastSyncDate;
|
||||||
private string _vaultTimeoutDisplayValue;
|
private string _vaultTimeoutDisplayValue;
|
||||||
private string _vaultTimeoutActionDisplayValue;
|
private string _vaultTimeoutActionDisplayValue;
|
||||||
|
@ -122,6 +123,7 @@ namespace Bit.App.Pages
|
||||||
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||||
_pin = pinSet.Item1 || pinSet.Item2;
|
_pin = pinSet.Item1 || pinSet.Item2;
|
||||||
_biometric = await _vaultTimeoutService.IsBiometricLockSetAsync();
|
_biometric = await _vaultTimeoutService.IsBiometricLockSetAsync();
|
||||||
|
_screenCaptureAllowed = await _stateService.GetScreenCaptureAllowedAsync();
|
||||||
|
|
||||||
if (_vaultTimeoutDisplayValue == null)
|
if (_vaultTimeoutDisplayValue == null)
|
||||||
{
|
{
|
||||||
|
@ -547,6 +549,15 @@ namespace Bit.App.Pages
|
||||||
UseFrame = true,
|
UseFrame = true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (Device.RuntimePlatform == Device.Android)
|
||||||
|
{
|
||||||
|
securityItems.Add(new SettingsPageListItem
|
||||||
|
{
|
||||||
|
Name = AppResources.AllowScreenCapture,
|
||||||
|
SubLabel = _screenCaptureAllowed ? AppResources.Enabled : AppResources.Disabled,
|
||||||
|
ExecuteAsync = () => SetScreenCaptureAllowedAsync()
|
||||||
|
});
|
||||||
|
}
|
||||||
var accountItems = new List<SettingsPageListItem>
|
var accountItems = new List<SettingsPageListItem>
|
||||||
{
|
{
|
||||||
new SettingsPageListItem
|
new SettingsPageListItem
|
||||||
|
@ -709,5 +720,33 @@ namespace Bit.App.Pages
|
||||||
private string CreateSelectableOption(string option, bool selected) => selected ? $"✓ {option}" : option;
|
private string CreateSelectableOption(string option, bool selected) => selected ? $"✓ {option}" : option;
|
||||||
|
|
||||||
private bool CompareSelection(string selection, string compareTo) => selection == compareTo || selection == $"✓ {compareTo}";
|
private bool CompareSelection(string selection, string compareTo) => selection == compareTo || selection == $"✓ {compareTo}";
|
||||||
|
|
||||||
|
public async Task SetScreenCaptureAllowedAsync()
|
||||||
|
{
|
||||||
|
if (CoreHelpers.ForceScreenCaptureEnabled())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,6 +181,8 @@ namespace Bit.App.Pages
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_deviceActionService.SetScreenCaptureAllowedAsync().FireAndForget();
|
||||||
|
|
||||||
await InitVaultFilterAsync(MainPage);
|
await InitVaultFilterAsync(MainPage);
|
||||||
if (MainPage)
|
if (MainPage)
|
||||||
{
|
{
|
||||||
|
@ -296,7 +298,7 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
}, AppResources.Trash, _deletedCount, uppercaseGroupNames, false));
|
}, AppResources.Trash, _deletedCount, uppercaseGroupNames, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: refactor this
|
// TODO: refactor this
|
||||||
if (Device.RuntimePlatform == Device.Android
|
if (Device.RuntimePlatform == Device.Android
|
||||||
||
|
||
|
||||||
|
|
9707
src/App/Resources/AppResources.Designer.cs
generated
9707
src/App/Resources/AppResources.Designer.cs
generated
File diff suppressed because it is too large
Load diff
|
@ -2263,4 +2263,10 @@
|
||||||
<data name="GenericErrorMessage" xml:space="preserve">
|
<data name="GenericErrorMessage" xml:space="preserve">
|
||||||
<value>We were unable to process your request. Please try again or contact us.</value>
|
<value>We were unable to process your request. Please try again or contact us.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="AllowScreenCapture" xml:space="preserve">
|
||||||
|
<value>Allow Screen Capture</value>
|
||||||
|
</data>
|
||||||
|
<data name="AreYouSureYouWantToEnableScreenCapture" xml:space="preserve">
|
||||||
|
<value>Are you sure you want to enable Screen Capture?</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
|
@ -148,6 +148,8 @@ namespace Bit.Core.Abstractions
|
||||||
Task SetRefreshTokenAsync(string value, bool skipTokenStorage, string userId = null);
|
Task SetRefreshTokenAsync(string value, bool skipTokenStorage, string userId = null);
|
||||||
Task<string> GetTwoFactorTokenAsync(string email = null);
|
Task<string> GetTwoFactorTokenAsync(string email = null);
|
||||||
Task SetTwoFactorTokenAsync(string value, string email = null);
|
Task SetTwoFactorTokenAsync(string value, string email = null);
|
||||||
|
Task<bool> GetScreenCaptureAllowedAsync(string userId = null);
|
||||||
|
Task SetScreenCaptureAllowedAsync(bool value, string userId = null);
|
||||||
Task SaveExtensionActiveUserIdToStorageAsync(string userId);
|
Task SaveExtensionActiveUserIdToStorageAsync(string userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,11 +94,13 @@ namespace Bit.Core.Models.Domain
|
||||||
EnvironmentUrls = copy.EnvironmentUrls;
|
EnvironmentUrls = copy.EnvironmentUrls;
|
||||||
VaultTimeout = copy.VaultTimeout;
|
VaultTimeout = copy.VaultTimeout;
|
||||||
VaultTimeoutAction = copy.VaultTimeoutAction;
|
VaultTimeoutAction = copy.VaultTimeoutAction;
|
||||||
|
ScreenCaptureAllowed = copy.ScreenCaptureAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EnvironmentUrlData EnvironmentUrls;
|
public EnvironmentUrlData EnvironmentUrls;
|
||||||
public int? VaultTimeout;
|
public int? VaultTimeout;
|
||||||
public VaultTimeoutAction? VaultTimeoutAction;
|
public VaultTimeoutAction? VaultTimeoutAction;
|
||||||
|
public bool ScreenCaptureAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AccountVolatileData
|
public class AccountVolatileData
|
||||||
|
|
|
@ -191,7 +191,7 @@ namespace Bit.Core.Services
|
||||||
EnvironmentUrls = environmentUrls,
|
EnvironmentUrls = environmentUrls,
|
||||||
VaultTimeout = vaultTimeout,
|
VaultTimeout = vaultTimeout,
|
||||||
VaultTimeoutAction =
|
VaultTimeoutAction =
|
||||||
vaultTimeoutAction == "logout" ? VaultTimeoutAction.Logout : VaultTimeoutAction.Lock,
|
vaultTimeoutAction == "logout" ? VaultTimeoutAction.Logout : VaultTimeoutAction.Lock
|
||||||
};
|
};
|
||||||
var state = new State { Accounts = new Dictionary<string, Account> { [userId] = account } };
|
var state = new State { Accounts = new Dictionary<string, Account> { [userId] = account } };
|
||||||
state.ActiveUserId = userId;
|
state.ActiveUserId = userId;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
|
@ -558,6 +559,27 @@ namespace Bit.Core.Services
|
||||||
await SaveAccountAsync(account, reconciledOptions);
|
await SaveAccountAsync(account, reconciledOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> GetScreenCaptureAllowedAsync(string userId = null)
|
||||||
|
{
|
||||||
|
if (CoreHelpers.ForceScreenCaptureEnabled())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (await GetAccountAsync(
|
||||||
|
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
|
||||||
|
))?.Settings?.ScreenCaptureAllowed ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetScreenCaptureAllowedAsync(bool value, string userId = null)
|
||||||
|
{
|
||||||
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
|
await GetDefaultStorageOptionsAsync());
|
||||||
|
var account = await GetAccountAsync(reconciledOptions);
|
||||||
|
account.Settings.ScreenCaptureAllowed = value;
|
||||||
|
await SaveAccountAsync(account, reconciledOptions);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<DateTime?> GetLastFileCacheClearAsync()
|
public async Task<DateTime?> GetLastFileCacheClearAsync()
|
||||||
{
|
{
|
||||||
var options = await GetDefaultStorageOptionsAsync();
|
var options = await GetDefaultStorageOptionsAsync();
|
||||||
|
@ -1461,6 +1483,7 @@ namespace Bit.Core.Services
|
||||||
var existingAccount = state.Accounts[account.Profile.UserId];
|
var existingAccount = state.Accounts[account.Profile.UserId];
|
||||||
account.Settings.VaultTimeout = existingAccount.Settings.VaultTimeout;
|
account.Settings.VaultTimeout = existingAccount.Settings.VaultTimeout;
|
||||||
account.Settings.VaultTimeoutAction = existingAccount.Settings.VaultTimeoutAction;
|
account.Settings.VaultTimeoutAction = existingAccount.Settings.VaultTimeoutAction;
|
||||||
|
account.Settings.ScreenCaptureAllowed = existingAccount.Settings.ScreenCaptureAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// New account defaults
|
// New account defaults
|
||||||
|
|
|
@ -34,6 +34,25 @@ namespace Bit.Core.Utilities
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether to force enabling the screen capture.
|
||||||
|
/// On Debug it will allow screen capture by default but this method
|
||||||
|
/// makes it easier to test the change on enabling/disabling the feature
|
||||||
|
/// on debug.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// To test enabling/disabling in DEBUG, just return <c>false</c> in the #if condition
|
||||||
|
/// and that's it.
|
||||||
|
/// </remarks>
|
||||||
|
public static bool ForceScreenCaptureEnabled()
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
public static string GetHostname(string uriString)
|
public static string GetHostname(string uriString)
|
||||||
{
|
{
|
||||||
var uri = GetUri(uriString);
|
var uri = GetUri(uriString);
|
||||||
|
|
|
@ -604,6 +604,12 @@ namespace Bit.iOS.Core.Services
|
||||||
await ASHelpers.ReplaceAllIdentities();
|
await ASHelpers.ReplaceAllIdentities();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task SetScreenCaptureAllowedAsync()
|
||||||
|
{
|
||||||
|
// only used by Android. Not possible in iOS
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
public class PickerDelegate : UIDocumentPickerDelegate
|
public class PickerDelegate : UIDocumentPickerDelegate
|
||||||
{
|
{
|
||||||
private readonly DeviceActionService _deviceActionService;
|
private readonly DeviceActionService _deviceActionService;
|
||||||
|
|
Loading…
Reference in a new issue