refactor message center use to services

This commit is contained in:
Kyle Spearrin 2017-11-21 23:08:45 -05:00
parent b48e8eeb0e
commit 3b44ede67e
24 changed files with 439 additions and 379 deletions

View file

@ -13,16 +13,10 @@ using System.Reflection;
using Xamarin.Forms.Platform.Android; using Xamarin.Forms.Platform.Android;
using Xamarin.Forms; using Xamarin.Forms;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Models.Page;
using Bit.App; using Bit.App;
using Android.Nfc; using Android.Nfc;
using Android.Views.InputMethods;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Android.Views.Autofill;
using Android.App.Assist;
using Bit.Android.Autofill;
using System.Collections.Generic;
using Bit.App.Models; using Bit.App.Models;
using Bit.App.Enums; using Bit.App.Enums;
@ -35,7 +29,6 @@ namespace Bit.Android
public class MainActivity : FormsAppCompatActivity public class MainActivity : FormsAppCompatActivity
{ {
private const string HockeyAppId = "d3834185b4a643479047b86c65293d42"; private const string HockeyAppId = "d3834185b4a643479047b86c65293d42";
private DateTime? _lastAction;
private Java.Util.Regex.Pattern _otpPattern = Java.Util.Regex.Pattern.Compile("^.*?([cbdefghijklnrtuv]{32,64})$"); private Java.Util.Regex.Pattern _otpPattern = Java.Util.Regex.Pattern.Compile("^.*?([cbdefghijklnrtuv]{32,64})$");
private IDeviceActionService _deviceActionService; private IDeviceActionService _deviceActionService;
private ISettings _settings; private ISettings _settings;
@ -97,105 +90,11 @@ namespace Bit.Android
if(_appOptions?.Uri == null) if(_appOptions?.Uri == null)
{ {
MessagingCenter.Subscribe<Xamarin.Forms.Application>(Xamarin.Forms.Application.Current,
"DismissKeyboard", (sender) => DismissKeyboard());
MessagingCenter.Subscribe<Xamarin.Forms.Application>(Xamarin.Forms.Application.Current,
"RateApp", (sender) => RateApp());
MessagingCenter.Subscribe<Xamarin.Forms.Application>(Xamarin.Forms.Application.Current,
"Accessibility", (sender) => OpenAccessibilitySettings());
MessagingCenter.Subscribe<Xamarin.Forms.Application, string>(Xamarin.Forms.Application.Current,
"LaunchApp", (sender, args) => LaunchApp(args));
MessagingCenter.Subscribe<Xamarin.Forms.Application, bool>(Xamarin.Forms.Application.Current, MessagingCenter.Subscribe<Xamarin.Forms.Application, bool>(Xamarin.Forms.Application.Current,
"ListenYubiKeyOTP", (sender, listen) => ListenYubiKey(listen)); "ListenYubiKeyOTP", (sender, listen) => ListenYubiKey(listen));
}
MessagingCenter.Subscribe<Xamarin.Forms.Application, VaultListPageModel.Cipher>( MessagingCenter.Subscribe<Xamarin.Forms.Application>(Xamarin.Forms.Application.Current,
Xamarin.Forms.Application.Current, "Autofill", (sender, args) => ReturnCredentials(args)); "FinishMainActivity", (sender) => Finish());
MessagingCenter.Subscribe<Xamarin.Forms.Application>(Xamarin.Forms.Application.Current,
"BackgroundApp", (sender) =>
{
if(Intent.GetBooleanExtra("autofillFramework", false))
{
SetResult(Result.Canceled);
Finish();
}
else
{
MoveTaskToBack(true);
}
});
}
private void ReturnCredentials(VaultListPageModel.Cipher cipher)
{
if(Intent.GetBooleanExtra("autofillFramework", false))
{
if(cipher == null)
{
SetResult(Result.Canceled);
Finish();
return;
}
var structure = Intent.GetParcelableExtra(AutofillManager.ExtraAssistStructure) as AssistStructure;
if(structure == null)
{
SetResult(Result.Canceled);
Finish();
return;
}
var parser = new Parser(structure);
parser.Parse();
if(!parser.FieldCollection.Fields.Any() || string.IsNullOrWhiteSpace(parser.Uri))
{
SetResult(Result.Canceled);
Finish();
return;
}
var dataset = AutofillHelpers.BuildDataset(this, parser.FieldCollection, new FilledItem(cipher.CipherModel));
var replyIntent = new Intent();
replyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, dataset);
SetResult(Result.Ok, replyIntent);
Finish();
}
else
{
var data = new Intent();
if(cipher == null)
{
data.PutExtra("canceled", "true");
}
else
{
var isPremium = Resolver.Resolve<ITokenService>()?.TokenPremium ?? false;
var autoCopyEnabled = !_settings.GetValueOrDefault(Constants.SettingDisableTotpCopy, false);
if(isPremium && autoCopyEnabled && _deviceActionService != null && cipher.LoginTotp?.Value != null)
{
_deviceActionService.CopyToClipboard(App.Utilities.Crypto.Totp(cipher.LoginTotp.Value));
}
data.PutExtra("uri", cipher.LoginUri);
data.PutExtra("username", cipher.LoginUsername);
data.PutExtra("password", cipher.LoginPassword?.Value ?? null);
}
if(Parent == null)
{
SetResult(Result.Ok, data);
}
else
{
Parent.SetResult(Result.Ok, data);
}
Finish();
} }
} }
@ -302,65 +201,6 @@ namespace Bit.Android
} }
} }
public void RateApp()
{
try
{
var rateIntent = RateIntentForUrl("market://details");
StartActivity(rateIntent);
}
catch(ActivityNotFoundException)
{
var rateIntent = RateIntentForUrl("https://play.google.com/store/apps/details");
StartActivity(rateIntent);
}
}
private Intent RateIntentForUrl(string url)
{
var intent = new Intent(Intent.ActionView, global::Android.Net.Uri.Parse($"{url}?id={PackageName}"));
var flags = ActivityFlags.NoHistory | ActivityFlags.MultipleTask;
if((int)Build.VERSION.SdkInt >= 21)
{
flags |= ActivityFlags.NewDocument;
}
else
{
// noinspection deprecation
flags |= ActivityFlags.ClearWhenTaskReset;
}
intent.AddFlags(flags);
return intent;
}
private void OpenAccessibilitySettings()
{
var intent = new Intent(global::Android.Provider.Settings.ActionAccessibilitySettings);
StartActivity(intent);
}
private void LaunchApp(string packageName)
{
if(_lastAction.LastActionWasRecent())
{
return;
}
_lastAction = DateTime.UtcNow;
packageName = packageName.Replace("androidapp://", string.Empty);
var launchIntent = PackageManager.GetLaunchIntentForPackage(packageName);
if(launchIntent == null)
{
var dialog = Resolver.Resolve<IUserDialogs>();
dialog.Alert(string.Format(App.Resources.AppResources.CannotOpenApp, packageName));
}
else
{
StartActivity(launchIntent);
}
}
private void ListenYubiKey(bool listen) private void ListenYubiKey(bool listen)
{ {
if(!Utilities.NfcEnabled()) if(!Utilities.NfcEnabled())
@ -405,16 +245,6 @@ namespace Bit.Android
} }
} }
private void DismissKeyboard()
{
try
{
var imm = (InputMethodManager)GetSystemService(InputMethodService);
imm.HideSoftInputFromWindow(CurrentFocus.WindowToken, 0);
}
catch { }
}
private AppOptions GetOptions() private AppOptions GetOptions()
{ {
var options = new AppOptions var options = new AppOptions

View file

@ -15,18 +15,32 @@ using System.Collections.Generic;
using Android; using Android;
using Android.Content.PM; using Android.Content.PM;
using Android.Support.V4.App; using Android.Support.V4.App;
using Bit.App.Models.Page;
using XLabs.Ioc;
using Android.App;
using Android.Views.Autofill;
using Android.App.Assist;
using Bit.Android.Autofill;
using System.Linq;
using Plugin.Settings.Abstractions;
using Acr.UserDialogs;
using Android.Views.InputMethods;
namespace Bit.Android.Services namespace Bit.Android.Services
{ {
public class DeviceActionService : IDeviceActionService public class DeviceActionService : IDeviceActionService
{ {
private readonly IAppSettingsService _appSettingsService; private readonly IAppSettingsService _appSettingsService;
private readonly IUserDialogs _userDialogs;
private bool _cameraPermissionsDenied; private bool _cameraPermissionsDenied;
private DateTime? _lastAction;
public DeviceActionService( public DeviceActionService(
IAppSettingsService appSettingsService) IAppSettingsService appSettingsService,
IUserDialogs userDialogs)
{ {
_appSettingsService = appSettingsService; _appSettingsService = appSettingsService;
_userDialogs = userDialogs;
} }
public void CopyToClipboard(string text) public void CopyToClipboard(string text)
@ -109,34 +123,10 @@ namespace Bit.Android.Services
catch(Exception) { } catch(Exception) { }
} }
private bool DeleteDir(Java.IO.File dir)
{
if(dir != null && dir.IsDirectory)
{
var children = dir.List();
for(int i = 0; i < children.Length; i++)
{
var success = DeleteDir(new Java.IO.File(dir, children[i]));
if(!success)
{
return false;
}
}
return dir.Delete();
}
else if(dir != null && dir.IsFile)
{
return dir.Delete();
}
else
{
return false;
}
}
public Task SelectFileAsync() public Task SelectFileAsync()
{ {
MessagingCenter.Unsubscribe<Application>(Application.Current, "SelectFileCameraPermissionDenied"); MessagingCenter.Unsubscribe<Xamarin.Forms.Application>(Xamarin.Forms.Application.Current,
"SelectFileCameraPermissionDenied");
var hasStorageWritePermission = !_cameraPermissionsDenied && HasPermission(Manifest.Permission.WriteExternalStorage); var hasStorageWritePermission = !_cameraPermissionsDenied && HasPermission(Manifest.Permission.WriteExternalStorage);
@ -189,6 +179,195 @@ namespace Bit.Android.Services
return Task.FromResult(0); return Task.FromResult(0);
} }
public void Autofill(VaultListPageModel.Cipher cipher)
{
var activity = (MainActivity)Forms.Context;
if(activity.Intent.GetBooleanExtra("autofillFramework", false))
{
if(cipher == null)
{
activity.SetResult(Result.Canceled);
activity.Finish();
return;
}
var structure = activity.Intent.GetParcelableExtra(
AutofillManager.ExtraAssistStructure) as AssistStructure;
if(structure == null)
{
activity.SetResult(Result.Canceled);
activity.Finish();
return;
}
var parser = new Parser(structure);
parser.Parse();
if(!parser.FieldCollection.Fields.Any() || string.IsNullOrWhiteSpace(parser.Uri))
{
activity.SetResult(Result.Canceled);
activity.Finish();
return;
}
var dataset = AutofillHelpers.BuildDataset(activity, parser.FieldCollection,
new FilledItem(cipher.CipherModel));
var replyIntent = new Intent();
replyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, dataset);
activity.SetResult(Result.Ok, replyIntent);
activity.Finish();
}
else
{
var data = new Intent();
if(cipher == null)
{
data.PutExtra("canceled", "true");
}
else
{
var isPremium = Resolver.Resolve<ITokenService>()?.TokenPremium ?? false;
var settings = Resolver.Resolve<ISettings>();
var autoCopyEnabled = !settings.GetValueOrDefault(Constants.SettingDisableTotpCopy, false);
if(isPremium && autoCopyEnabled && cipher.LoginTotp?.Value != null)
{
CopyToClipboard(App.Utilities.Crypto.Totp(cipher.LoginTotp.Value));
}
data.PutExtra("uri", cipher.LoginUri);
data.PutExtra("username", cipher.LoginUsername);
data.PutExtra("password", cipher.LoginPassword?.Value ?? null);
}
if(activity.Parent == null)
{
activity.SetResult(Result.Ok, data);
}
else
{
activity.Parent.SetResult(Result.Ok, data);
}
activity.Finish();
MessagingCenter.Send(Xamarin.Forms.Application.Current, "FinishMainActivity");
}
}
public void CloseAutofill()
{
Autofill(null);
}
public void Background()
{
var activity = (MainActivity)Forms.Context;
if(activity.Intent.GetBooleanExtra("autofillFramework", false))
{
activity.SetResult(Result.Canceled);
activity.Finish();
}
else
{
activity.MoveTaskToBack(true);
}
}
public void RateApp()
{
var activity = (MainActivity)Forms.Context;
try
{
var rateIntent = RateIntentForUrl("market://details", activity);
activity.StartActivity(rateIntent);
}
catch(ActivityNotFoundException)
{
var rateIntent = RateIntentForUrl("https://play.google.com/store/apps/details", activity);
activity.StartActivity(rateIntent);
}
}
public void DismissKeyboard()
{
var activity = (MainActivity)Forms.Context;
try
{
var imm = (InputMethodManager)activity.GetSystemService(Context.InputMethodService);
imm.HideSoftInputFromWindow(activity.CurrentFocus.WindowToken, 0);
}
catch { }
}
public void OpenAccessibilitySettings()
{
var activity = (MainActivity)Forms.Context;
var intent = new Intent(Settings.ActionAccessibilitySettings);
activity.StartActivity(intent);
}
public void LaunchApp(string appName)
{
var activity = (MainActivity)Forms.Context;
if(_lastAction.LastActionWasRecent())
{
return;
}
_lastAction = DateTime.UtcNow;
appName = appName.Replace("androidapp://", string.Empty);
var launchIntent = activity.PackageManager.GetLaunchIntentForPackage(appName);
if(launchIntent == null)
{
_userDialogs.Alert(string.Format(AppResources.CannotOpenApp, appName));
}
else
{
activity.StartActivity(launchIntent);
}
}
private Intent RateIntentForUrl(string url, Activity activity)
{
var intent = new Intent(Intent.ActionView, global::Android.Net.Uri.Parse($"{url}?id={activity.PackageName}"));
var flags = ActivityFlags.NoHistory | ActivityFlags.MultipleTask;
if((int)Build.VERSION.SdkInt >= 21)
{
flags |= ActivityFlags.NewDocument;
}
else
{
// noinspection deprecation
flags |= ActivityFlags.ClearWhenTaskReset;
}
intent.AddFlags(flags);
return intent;
}
private bool DeleteDir(Java.IO.File dir)
{
if(dir != null && dir.IsDirectory)
{
var children = dir.List();
for(int i = 0; i < children.Length; i++)
{
var success = DeleteDir(new Java.IO.File(dir, children[i]));
if(!success)
{
return false;
}
}
return dir.Delete();
}
else if(dir != null && dir.IsFile)
{
return dir.Delete();
}
else
{
return false;
}
}
private List<IParcelable> GetCameraIntents(global::Android.Net.Uri outputUri) private List<IParcelable> GetCameraIntents(global::Android.Net.Uri outputUri)
{ {
var intents = new List<IParcelable>(); var intents = new List<IParcelable>();
@ -214,10 +393,11 @@ namespace Bit.Android.Services
private void AskCameraPermission(string permission) private void AskCameraPermission(string permission)
{ {
MessagingCenter.Subscribe<Application>(Application.Current, "SelectFileCameraPermissionDenied", (sender) => MessagingCenter.Subscribe<Xamarin.Forms.Application>(Xamarin.Forms.Application.Current,
{ "SelectFileCameraPermissionDenied", (sender) =>
_cameraPermissionsDenied = true; {
}); _cameraPermissionsDenied = true;
});
AskPermission(permission); AskPermission(permission);
} }

View file

@ -10,17 +10,13 @@ namespace Bit.Android.Services
public class GoogleAnalyticsService : IGoogleAnalyticsService public class GoogleAnalyticsService : IGoogleAnalyticsService
{ {
private readonly GoogleAnalytics _instance; private readonly GoogleAnalytics _instance;
private readonly IAuthService _authService;
private readonly Tracker _tracker; private readonly Tracker _tracker;
public GoogleAnalyticsService( public GoogleAnalyticsService(
Context appContext, Context appContext,
IAppIdService appIdService, IAppIdService appIdService,
IAuthService authService,
ISettings settings) ISettings settings)
{ {
_authService = authService;
_instance = GoogleAnalytics.GetInstance(appContext.ApplicationContext); _instance = GoogleAnalytics.GetInstance(appContext.ApplicationContext);
_instance.SetLocalDispatchPeriod(10); _instance.SetLocalDispatchPeriod(10);

View file

@ -12,9 +12,8 @@ namespace Bit.App.Abstractions
bool UserIdChanged { get; } bool UserIdChanged { get; }
string Email { get; set; } string Email { get; set; }
string PIN { get; set; } string PIN { get; set; }
bool BelongsToOrganization(string orgId); bool BelongsToOrganization(string orgId);
void LogOut(); void LogOut(string logoutMessage = null);
Task<FullLoginResult> TokenPostAsync(string email, string masterPassword); Task<FullLoginResult> TokenPostAsync(string email, string masterPassword);
Task<LoginResult> TokenPostTwoFactorAsync(TwoFactorProviderType type, string token, bool remember, string email, Task<LoginResult> TokenPostTwoFactorAsync(TwoFactorProviderType type, string token, bool remember, string email,
string masterPasswordHash, SymmetricCryptoKey key); string masterPasswordHash, SymmetricCryptoKey key);

View file

@ -10,5 +10,12 @@ namespace Bit.App.Abstractions
bool CanOpenFile(string fileName); bool CanOpenFile(string fileName);
Task SelectFileAsync(); Task SelectFileAsync();
void ClearCache(); void ClearCache();
void Autofill(Models.Page.VaultListPageModel.Cipher cipher);
void CloseAutofill();
void Background();
void RateApp();
void DismissKeyboard();
void OpenAccessibilitySettings();
void LaunchApp(string appName);
} }
} }

View file

@ -8,5 +8,7 @@ namespace Bit.App.Abstractions
{ {
void UpdateLastActivity(DateTime? activityDate = null); void UpdateLastActivity(DateTime? activityDate = null);
Task<LockType> GetLockTypeAsync(bool forceLock); Task<LockType> GetLockTypeAsync(bool forceLock);
Task CheckLockAsync(bool forceLock);
bool TopPageIsLock();
} }
} }

View file

@ -86,27 +86,20 @@ namespace Bit.App
MainPage = new ExtendedNavigationPage(new HomePage()); MainPage = new ExtendedNavigationPage(new HomePage());
} }
MessagingCenter.Subscribe<Application, bool>(Current, "Resumed", async (sender, args) => if(Device.RuntimePlatform == Device.iOS)
{ {
Device.BeginInvokeOnMainThread(async () => await CheckLockAsync(args)); MessagingCenter.Subscribe<Application, bool>(Current, "Resumed", async (sender, args) =>
await Task.Run(() => FullSyncAsync()).ConfigureAwait(false); {
}); Device.BeginInvokeOnMainThread(async () => await _lockService.CheckLockAsync(args));
await Task.Run(() => FullSyncAsync()).ConfigureAwait(false);
MessagingCenter.Subscribe<Application, bool>(Current, "Lock", (sender, args) => });
{ }
Device.BeginInvokeOnMainThread(async () => await CheckLockAsync(args));
});
MessagingCenter.Subscribe<Application, string>(Current, "Logout", (sender, args) =>
{
Logout(args);
});
} }
protected async override void OnStart() protected async override void OnStart()
{ {
// Handle when your app starts // Handle when your app starts
await CheckLockAsync(false); await _lockService.CheckLockAsync(false);
if(string.IsNullOrWhiteSpace(_options.Uri)) if(string.IsNullOrWhiteSpace(_options.Uri))
{ {
@ -132,7 +125,7 @@ namespace Bit.App
SetMainPageFromAutofill(); SetMainPageFromAutofill();
if(Device.RuntimePlatform == Device.Android && !TopPageIsLock()) if(Device.RuntimePlatform == Device.Android && !_lockService.TopPageIsLock())
{ {
_lockService.UpdateLastActivity(); _lockService.UpdateLastActivity();
} }
@ -151,7 +144,7 @@ namespace Bit.App
if(Device.RuntimePlatform == Device.Android) if(Device.RuntimePlatform == Device.Android)
{ {
await CheckLockAsync(false); await _lockService.CheckLockAsync(false);
} }
var lockPinPage = Current.MainPage.Navigation.ModalStack.LastOrDefault() as LockPinPage; var lockPinPage = Current.MainPage.Navigation.ModalStack.LastOrDefault() as LockPinPage;
@ -227,73 +220,6 @@ namespace Bit.App
} }
} }
private void Logout(string logoutMessage)
{
_authService.LogOut();
var deviceApiRepository = Resolver.Resolve<IDeviceApiRepository>();
var appIdService = Resolver.Resolve<IAppIdService>();
Task.Run(async () => await deviceApiRepository.PutClearTokenAsync(appIdService.AppId));
_googleAnalyticsService.TrackAppEvent("LoggedOut");
Device.BeginInvokeOnMainThread(() => Current.MainPage = new ExtendedNavigationPage(new HomePage()));
if(!string.IsNullOrWhiteSpace(logoutMessage))
{
_userDialogs.Toast(logoutMessage);
}
}
private async Task CheckLockAsync(bool forceLock)
{
if(TopPageIsLock())
{
// already locked
return;
}
var lockType = await _lockService.GetLockTypeAsync(forceLock);
if(lockType == Enums.LockType.None)
{
return;
}
_appSettingsService.Locked = true;
switch(lockType)
{
case Enums.LockType.Fingerprint:
await Current.MainPage.Navigation.PushModalAsync(new ExtendedNavigationPage(new LockFingerprintPage(!forceLock)), false);
break;
case Enums.LockType.PIN:
await Current.MainPage.Navigation.PushModalAsync(new ExtendedNavigationPage(new LockPinPage()), false);
break;
case Enums.LockType.Password:
await Current.MainPage.Navigation.PushModalAsync(new ExtendedNavigationPage(new LockPasswordPage()), false);
break;
default:
break;
}
}
private bool TopPageIsLock()
{
var currentPage = Current.MainPage.Navigation.ModalStack.LastOrDefault() as ExtendedNavigationPage;
if((currentPage?.CurrentPage as LockFingerprintPage) != null)
{
return true;
}
if((currentPage?.CurrentPage as LockPinPage) != null)
{
return true;
}
if((currentPage?.CurrentPage as LockPasswordPage) != null)
{
return true;
}
return false;
}
private void SetStyles() private void SetStyles()
{ {
var gray = Color.FromHex("333333"); var gray = Color.FromHex("333333");

View file

@ -1,6 +1,4 @@
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Plugin.Settings.Abstractions;
using System;
using Xamarin.Forms; using Xamarin.Forms;
using XLabs.Ioc; using XLabs.Ioc;
@ -11,6 +9,7 @@ namespace Bit.App.Controls
private ISyncService _syncService; private ISyncService _syncService;
private IGoogleAnalyticsService _googleAnalyticsService; private IGoogleAnalyticsService _googleAnalyticsService;
private ILockService _lockService; private ILockService _lockService;
private IDeviceActionService _deviceActionService;
private bool _syncIndicator; private bool _syncIndicator;
private bool _updateActivity; private bool _updateActivity;
@ -21,25 +20,21 @@ namespace Bit.App.Controls
_syncService = Resolver.Resolve<ISyncService>(); _syncService = Resolver.Resolve<ISyncService>();
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>(); _googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
_lockService = Resolver.Resolve<ILockService>(); _lockService = Resolver.Resolve<ILockService>();
_deviceActionService = Resolver.Resolve<IDeviceActionService>();
BackgroundColor = Color.FromHex("efeff4"); BackgroundColor = Color.FromHex("efeff4");
if(_syncIndicator)
{
MessagingCenter.Subscribe<Application, bool>(Application.Current, "SyncCompleted", (sender, success) =>
{
Device.BeginInvokeOnMainThread(() => IsBusy = _syncService.SyncInProgress);
});
MessagingCenter.Subscribe<Application>(Application.Current, "SyncStarted", (sender) =>
{
Device.BeginInvokeOnMainThread(() => IsBusy = _syncService.SyncInProgress);
});
}
} }
protected override void OnAppearing() protected override void OnAppearing()
{ {
if(_syncIndicator)
{
MessagingCenter.Subscribe<ISyncService, bool>(_syncService, "SyncCompleted",
(sender, success) => Device.BeginInvokeOnMainThread(() => IsBusy = _syncService.SyncInProgress));
MessagingCenter.Subscribe<ISyncService>(_syncService, "SyncStarted",
(sender) => Device.BeginInvokeOnMainThread(() => IsBusy = _syncService.SyncInProgress));
}
if(_syncIndicator) if(_syncIndicator)
{ {
IsBusy = _syncService.SyncInProgress; IsBusy = _syncService.SyncInProgress;
@ -51,6 +46,12 @@ namespace Bit.App.Controls
protected override void OnDisappearing() protected override void OnDisappearing()
{ {
if(_syncIndicator)
{
MessagingCenter.Unsubscribe<ISyncService, bool>(_syncService, "SyncCompleted");
MessagingCenter.Unsubscribe<ISyncService>(_syncService, "SyncStarted");
}
if(_syncIndicator) if(_syncIndicator)
{ {
IsBusy = false; IsBusy = false;
@ -62,7 +63,7 @@ namespace Bit.App.Controls
} }
base.OnDisappearing(); base.OnDisappearing();
MessagingCenter.Send(Application.Current, "DismissKeyboard"); _deviceActionService.DismissKeyboard();
} }
} }
} }

View file

@ -4,27 +4,28 @@ using Bit.App.Controls;
using Bit.App.Resources; using Bit.App.Resources;
using Xamarin.Forms; using Xamarin.Forms;
using XLabs.Ioc; using XLabs.Ioc;
using Bit.App.Abstractions;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
public class BaseLockPage : ExtendedContentPage public class BaseLockPage : ExtendedContentPage
{ {
private readonly IDeviceActionService _deviceActionService;
public BaseLockPage() public BaseLockPage()
: base(false, false) : base(false, false)
{ {
UserDialogs = Resolver.Resolve<IUserDialogs>(); UserDialogs = Resolver.Resolve<IUserDialogs>();
AuthService = Resolver.Resolve<IAuthService>();
_deviceActionService = Resolver.Resolve<IDeviceActionService>();
} }
protected IUserDialogs UserDialogs { get; set; } protected IUserDialogs UserDialogs { get; set; }
protected IAuthService AuthService { get; set; }
protected override bool OnBackButtonPressed() protected override bool OnBackButtonPressed()
{ {
if(Device.RuntimePlatform == Device.Android) _deviceActionService.Background();
{
MessagingCenter.Send(Application.Current, "BackgroundApp");
}
return true; return true;
} }
@ -34,8 +35,7 @@ namespace Bit.App.Pages
{ {
return; return;
} }
AuthService.LogOut();
MessagingCenter.Send(Application.Current, "Logout", (string)null);
} }
} }
} }

View file

@ -100,7 +100,7 @@ namespace Bit.App.Pages
} }
else if(result.Status == FingerprintAuthenticationResultStatus.FallbackRequested) else if(result.Status == FingerprintAuthenticationResultStatus.FallbackRequested)
{ {
MessagingCenter.Send(Application.Current, "Logout", (string)null); AuthService.LogOut();
} }
} }
} }

View file

@ -22,6 +22,7 @@ namespace Bit.App.Pages
private IUserDialogs _userDialogs; private IUserDialogs _userDialogs;
private ISyncService _syncService; private ISyncService _syncService;
private IDeviceInfoService _deviceInfoService; private IDeviceInfoService _deviceInfoService;
private IDeviceActionService _deviceActionService;
private IGoogleAnalyticsService _googleAnalyticsService; private IGoogleAnalyticsService _googleAnalyticsService;
private ITwoFactorApiRepository _twoFactorApiRepository; private ITwoFactorApiRepository _twoFactorApiRepository;
private IPushNotificationService _pushNotification; private IPushNotificationService _pushNotification;
@ -45,6 +46,7 @@ namespace Bit.App.Pages
_providers = result.TwoFactorProviders; _providers = result.TwoFactorProviders;
_providerType = type ?? GetDefaultProvider(); _providerType = type ?? GetDefaultProvider();
_deviceActionService = Resolver.Resolve<IDeviceActionService>();
_authService = Resolver.Resolve<IAuthService>(); _authService = Resolver.Resolve<IAuthService>();
_userDialogs = Resolver.Resolve<IUserDialogs>(); _userDialogs = Resolver.Resolve<IUserDialogs>();
_syncService = Resolver.Resolve<ISyncService>(); _syncService = Resolver.Resolve<ISyncService>();
@ -266,7 +268,7 @@ namespace Bit.App.Pages
InitEvents(); InitEvents();
if(TokenCell == null && Device.RuntimePlatform == Device.Android) if(TokenCell == null && Device.RuntimePlatform == Device.Android)
{ {
MessagingCenter.Send(Application.Current, "DismissKeyboard"); _deviceActionService.DismissKeyboard();
} }
} }

View file

@ -19,7 +19,8 @@ namespace Bit.App.Pages
private readonly IFingerprint _fingerprint; private readonly IFingerprint _fingerprint;
private readonly IPushNotificationService _pushNotification; private readonly IPushNotificationService _pushNotification;
private readonly IGoogleAnalyticsService _googleAnalyticsService; private readonly IGoogleAnalyticsService _googleAnalyticsService;
private readonly IDeviceInfoService _deviceInfoService; private readonly IDeviceActionService _deviceActionService;
private readonly ILockService _lockService;
// TODO: Model binding context? // TODO: Model binding context?
@ -31,7 +32,8 @@ namespace Bit.App.Pages
_fingerprint = Resolver.Resolve<IFingerprint>(); _fingerprint = Resolver.Resolve<IFingerprint>();
_pushNotification = Resolver.Resolve<IPushNotificationService>(); _pushNotification = Resolver.Resolve<IPushNotificationService>();
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>(); _googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
_deviceInfoService = Resolver.Resolve<IDeviceInfoService>(); _deviceActionService = Resolver.Resolve<IDeviceActionService>();
_lockService = Resolver.Resolve<ILockService>();
Init(); Init();
} }
@ -327,22 +329,7 @@ namespace Bit.App.Pages
private void RateCell_Tapped(object sender, EventArgs e) private void RateCell_Tapped(object sender, EventArgs e)
{ {
_googleAnalyticsService.TrackAppEvent("OpenedSetting", "RateApp"); _googleAnalyticsService.TrackAppEvent("OpenedSetting", "RateApp");
if(Device.RuntimePlatform == Device.iOS) _deviceActionService.RateApp();
{
if(_deviceInfoService.Version < 11)
{
Device.OpenUri(new Uri("itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews" +
"?id=1137397744&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software"));
}
else
{
Device.OpenUri(new Uri("itms-apps://itunes.apple.com/us/app/id1137397744?action=write-review"));
}
}
else if(Device.RuntimePlatform == Device.Android)
{
MessagingCenter.Send(Application.Current, "RateApp");
}
} }
private void HelpCell_Tapped(object sender, EventArgs e) private void HelpCell_Tapped(object sender, EventArgs e)
@ -353,7 +340,7 @@ namespace Bit.App.Pages
private void LockCell_Tapped(object sender, EventArgs e) private void LockCell_Tapped(object sender, EventArgs e)
{ {
_googleAnalyticsService.TrackAppEvent("Locked"); _googleAnalyticsService.TrackAppEvent("Locked");
MessagingCenter.Send(Application.Current, "Lock", true); Device.BeginInvokeOnMainThread(async () => await _lockService.CheckLockAsync(true));
} }
private async void LogOutCell_Tapped(object sender, EventArgs e) private async void LogOutCell_Tapped(object sender, EventArgs e)
@ -363,7 +350,7 @@ namespace Bit.App.Pages
return; return;
} }
MessagingCenter.Send(Application.Current, "Logout", (string)null); _authService.LogOut();
} }
private async void ChangeMasterPasswordCell_Tapped(object sender, EventArgs e) private async void ChangeMasterPasswordCell_Tapped(object sender, EventArgs e)

View file

@ -12,12 +12,14 @@ namespace Bit.App.Pages
{ {
private readonly IGoogleAnalyticsService _googleAnalyticsService; private readonly IGoogleAnalyticsService _googleAnalyticsService;
private readonly IAppInfoService _appInfoService; private readonly IAppInfoService _appInfoService;
private readonly IDeviceActionService _deviceActionService;
private bool _pageDisappeared = false; private bool _pageDisappeared = false;
public ToolsAutofillServicePage() public ToolsAutofillServicePage()
{ {
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>(); _googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
_appInfoService = Resolver.Resolve<IAppInfoService>(); _appInfoService = Resolver.Resolve<IAppInfoService>();
_deviceActionService = Resolver.Resolve<IDeviceActionService>();
Init(); Init();
} }
@ -221,7 +223,7 @@ namespace Bit.App.Pages
Command = new Command(() => Command = new Command(() =>
{ {
_googleAnalyticsService.TrackAppEvent("OpenAccessibilitySettings"); _googleAnalyticsService.TrackAppEvent("OpenAccessibilitySettings");
MessagingCenter.Send(Application.Current, "Accessibility"); _deviceActionService.OpenAccessibilitySettings();
}), }),
VerticalOptions = LayoutOptions.End, VerticalOptions = LayoutOptions.End,
HorizontalOptions = LayoutOptions.Fill, HorizontalOptions = LayoutOptions.Fill,

View file

@ -29,6 +29,7 @@ namespace Bit.App.Pages
private readonly ISettings _settings; private readonly ISettings _settings;
private readonly IAppInfoService _appInfoService; private readonly IAppInfoService _appInfoService;
private readonly IDeviceInfoService _deviceInfo; private readonly IDeviceInfoService _deviceInfo;
private readonly IDeviceActionService _deviceActionService;
private readonly string _defaultUri; private readonly string _defaultUri;
private readonly string _defaultName; private readonly string _defaultName;
private readonly string _defaultUsername; private readonly string _defaultUsername;
@ -75,6 +76,7 @@ namespace Bit.App.Pages
_settings = Resolver.Resolve<ISettings>(); _settings = Resolver.Resolve<ISettings>();
_appInfoService = Resolver.Resolve<IAppInfoService>(); _appInfoService = Resolver.Resolve<IAppInfoService>();
_deviceInfo = Resolver.Resolve<IDeviceInfoService>(); _deviceInfo = Resolver.Resolve<IDeviceInfoService>();
_deviceActionService = Resolver.Resolve<IDeviceActionService>();
if(doInit) if(doInit)
{ {
@ -751,7 +753,7 @@ namespace Bit.App.Pages
if(_fromAutofillFramework) if(_fromAutofillFramework)
{ {
// close and go back to app // close and go back to app
MessagingCenter.Send(Application.Current, "Autofill", (VaultListPageModel.Cipher)null); _deviceActionService.CloseAutofill();
} }
else else
{ {

View file

@ -20,7 +20,7 @@ namespace Bit.App.Pages
{ {
private readonly ICipherService _cipherService; private readonly ICipherService _cipherService;
private readonly IDeviceInfoService _deviceInfoService; private readonly IDeviceInfoService _deviceInfoService;
private readonly IDeviceActionService _clipboardService; private readonly IDeviceActionService _deviceActionService;
private readonly ISettingsService _settingsService; private readonly ISettingsService _settingsService;
private readonly IAppSettingsService _appSettingsService; private readonly IAppSettingsService _appSettingsService;
private CancellationTokenSource _filterResultsCancellationTokenSource; private CancellationTokenSource _filterResultsCancellationTokenSource;
@ -44,7 +44,7 @@ namespace Bit.App.Pages
_cipherService = Resolver.Resolve<ICipherService>(); _cipherService = Resolver.Resolve<ICipherService>();
_deviceInfoService = Resolver.Resolve<IDeviceInfoService>(); _deviceInfoService = Resolver.Resolve<IDeviceInfoService>();
_clipboardService = Resolver.Resolve<IDeviceActionService>(); _deviceActionService = Resolver.Resolve<IDeviceActionService>();
_settingsService = Resolver.Resolve<ISettingsService>(); _settingsService = Resolver.Resolve<ISettingsService>();
UserDialogs = Resolver.Resolve<IUserDialogs>(); UserDialogs = Resolver.Resolve<IUserDialogs>();
_appSettingsService = Resolver.Resolve<IAppSettingsService>(); _appSettingsService = Resolver.Resolve<IAppSettingsService>();
@ -141,7 +141,7 @@ namespace Bit.App.Pages
protected override bool OnBackButtonPressed() protected override bool OnBackButtonPressed()
{ {
GoogleAnalyticsService.TrackExtensionEvent("BackClosed", Uri.StartsWith("http") ? "Website" : "App"); GoogleAnalyticsService.TrackExtensionEvent("BackClosed", Uri.StartsWith("http") ? "Website" : "App");
MessagingCenter.Send(Application.Current, "Autofill", (VaultListPageModel.Cipher)null); _deviceActionService.CloseAutofill();
return true; return true;
} }
@ -243,7 +243,7 @@ namespace Bit.App.Pages
if(doAutofill) if(doAutofill)
{ {
GoogleAnalyticsService.TrackExtensionEvent("AutoFilled", Uri.StartsWith("http") ? "Website" : "App"); GoogleAnalyticsService.TrackExtensionEvent("AutoFilled", Uri.StartsWith("http") ? "Website" : "App");
MessagingCenter.Send(Application.Current, "Autofill", cipher as VaultListPageModel.Cipher); _deviceActionService.Autofill(cipher);
} }
} }
@ -322,7 +322,7 @@ namespace Bit.App.Pages
private void Copy(string copyText, string alertLabel) private void Copy(string copyText, string alertLabel)
{ {
_clipboardService.CopyToClipboard(copyText); _deviceActionService.CopyToClipboard(copyText);
UserDialogs.Toast(string.Format(AppResources.ValueHasBeenCopied, alertLabel)); UserDialogs.Toast(string.Format(AppResources.ValueHasBeenCopied, alertLabel));
} }

View file

@ -24,7 +24,7 @@ namespace Bit.App.Pages
private readonly ICipherService _cipherService; private readonly ICipherService _cipherService;
private readonly IUserDialogs _userDialogs; private readonly IUserDialogs _userDialogs;
private readonly IConnectivity _connectivity; private readonly IConnectivity _connectivity;
private readonly IDeviceActionService _clipboardService; private readonly IDeviceActionService _deviceActionService;
private readonly ISyncService _syncService; private readonly ISyncService _syncService;
private readonly IPushNotificationService _pushNotification; private readonly IPushNotificationService _pushNotification;
private readonly IDeviceInfoService _deviceInfoService; private readonly IDeviceInfoService _deviceInfoService;
@ -42,7 +42,7 @@ namespace Bit.App.Pages
_cipherService = Resolver.Resolve<ICipherService>(); _cipherService = Resolver.Resolve<ICipherService>();
_connectivity = Resolver.Resolve<IConnectivity>(); _connectivity = Resolver.Resolve<IConnectivity>();
_userDialogs = Resolver.Resolve<IUserDialogs>(); _userDialogs = Resolver.Resolve<IUserDialogs>();
_clipboardService = Resolver.Resolve<IDeviceActionService>(); _deviceActionService = Resolver.Resolve<IDeviceActionService>();
_syncService = Resolver.Resolve<ISyncService>(); _syncService = Resolver.Resolve<ISyncService>();
_pushNotification = Resolver.Resolve<IPushNotificationService>(); _pushNotification = Resolver.Resolve<IPushNotificationService>();
_deviceInfoService = Resolver.Resolve<IDeviceInfoService>(); _deviceInfoService = Resolver.Resolve<IDeviceInfoService>();
@ -71,14 +71,6 @@ namespace Bit.App.Pages
private void Init() private void Init()
{ {
MessagingCenter.Subscribe<Application, bool>(Application.Current, "SyncCompleted", (sender, success) =>
{
if(success)
{
_filterResultsCancellationTokenSource = FetchAndLoadVault();
}
});
if(!_favorites) if(!_favorites)
{ {
AddCipherItem = new AddCipherToolBarItem(this); AddCipherItem = new AddCipherToolBarItem(this);
@ -232,6 +224,14 @@ namespace Bit.App.Pages
protected override void OnAppearing() protected override void OnAppearing()
{ {
base.OnAppearing(); base.OnAppearing();
MessagingCenter.Subscribe<ISyncService, bool>(_syncService, "SyncCompleted", (sender, success) =>
{
if(success)
{
_filterResultsCancellationTokenSource = FetchAndLoadVault();
}
});
ListView.ItemSelected += CipherSelected; ListView.ItemSelected += CipherSelected;
Search.TextChanged += SearchBar_TextChanged; Search.TextChanged += SearchBar_TextChanged;
Search.SearchButtonPressed += SearchBar_SearchButtonPressed; Search.SearchButtonPressed += SearchBar_SearchButtonPressed;
@ -274,6 +274,8 @@ namespace Bit.App.Pages
protected override void OnDisappearing() protected override void OnDisappearing()
{ {
base.OnDisappearing(); base.OnDisappearing();
MessagingCenter.Unsubscribe<ISyncService, bool>(_syncService, "SyncCompleted");
ListView.ItemSelected -= CipherSelected; ListView.ItemSelected -= CipherSelected;
Search.TextChanged -= SearchBar_TextChanged; Search.TextChanged -= SearchBar_TextChanged;
Search.SearchButtonPressed -= SearchBar_SearchButtonPressed; Search.SearchButtonPressed -= SearchBar_SearchButtonPressed;
@ -288,7 +290,7 @@ namespace Bit.App.Pages
} }
_googleAnalyticsService.TrackExtensionEvent("BackClosed", Uri.StartsWith("http") ? "Website" : "App"); _googleAnalyticsService.TrackExtensionEvent("BackClosed", Uri.StartsWith("http") ? "Website" : "App");
MessagingCenter.Send(Application.Current, "Autofill", (VaultListPageModel.Cipher)null); _deviceActionService.CloseAutofill();
return true; return true;
} }
@ -417,7 +419,7 @@ namespace Bit.App.Pages
{ {
_googleAnalyticsService.TrackExtensionEvent("AutoFilled", _googleAnalyticsService.TrackExtensionEvent("AutoFilled",
Uri.StartsWith("http") ? "Website" : "App"); Uri.StartsWith("http") ? "Website" : "App");
MessagingCenter.Send(Application.Current, "Autofill", cipher); _deviceActionService.Autofill(cipher);
} }
} }
@ -492,7 +494,7 @@ namespace Bit.App.Pages
private void Copy(string copyText, string alertLabel) private void Copy(string copyText, string alertLabel)
{ {
_clipboardService.CopyToClipboard(copyText); _deviceActionService.CopyToClipboard(copyText);
_userDialogs.Toast(string.Format(AppResources.ValueHasBeenCopied, alertLabel)); _userDialogs.Toast(string.Format(AppResources.ValueHasBeenCopied, alertLabel));
} }

View file

@ -136,7 +136,7 @@ namespace Bit.App.Pages
{ {
if(Device.RuntimePlatform == Device.Android && Model.LoginUri.StartsWith("androidapp://")) if(Device.RuntimePlatform == Device.Android && Model.LoginUri.StartsWith("androidapp://"))
{ {
MessagingCenter.Send(Application.Current, "LaunchApp", Model.LoginUri); _deviceActionService.LaunchApp(Model.LoginUri);
} }
else if(Model.LoginUri.StartsWith("http://") || Model.LoginUri.StartsWith("https://")) else if(Model.LoginUri.StartsWith("http://") || Model.LoginUri.StartsWith("https://"))
{ {

View file

@ -7,6 +7,10 @@ using Plugin.Settings.Abstractions;
using Bit.App.Models; using Bit.App.Models;
using System.Linq; using System.Linq;
using Bit.App.Enums; using Bit.App.Enums;
using Xamarin.Forms;
using Bit.App.Pages;
using Bit.App.Controls;
using Acr.UserDialogs;
namespace Bit.App.Services namespace Bit.App.Services
{ {
@ -25,6 +29,9 @@ namespace Bit.App.Services
private readonly IAccountsApiRepository _accountsApiRepository; private readonly IAccountsApiRepository _accountsApiRepository;
private readonly IAppIdService _appIdService; private readonly IAppIdService _appIdService;
private readonly IDeviceInfoService _deviceInfoService; private readonly IDeviceInfoService _deviceInfoService;
private readonly IDeviceApiRepository _deviceApiRepository;
private readonly IGoogleAnalyticsService _googleAnalyticsService;
private readonly IUserDialogs _userDialogs;
private string _email; private string _email;
private string _userId; private string _userId;
@ -39,7 +46,10 @@ namespace Bit.App.Services
IConnectApiRepository connectApiRepository, IConnectApiRepository connectApiRepository,
IAccountsApiRepository accountsApiRepository, IAccountsApiRepository accountsApiRepository,
IAppIdService appIdService, IAppIdService appIdService,
IDeviceInfoService deviceInfoService) IDeviceInfoService deviceInfoService,
IDeviceApiRepository deviceApiRepository,
IGoogleAnalyticsService googleAnalyticsService,
IUserDialogs userDialogs)
{ {
_secureStorage = secureStorage; _secureStorage = secureStorage;
_tokenService = tokenService; _tokenService = tokenService;
@ -49,6 +59,9 @@ namespace Bit.App.Services
_accountsApiRepository = accountsApiRepository; _accountsApiRepository = accountsApiRepository;
_appIdService = appIdService; _appIdService = appIdService;
_deviceInfoService = deviceInfoService; _deviceInfoService = deviceInfoService;
_deviceApiRepository = deviceApiRepository;
_googleAnalyticsService = googleAnalyticsService;
_userDialogs = userDialogs;
} }
public string UserId public string UserId
@ -194,7 +207,7 @@ namespace Bit.App.Services
return !string.IsNullOrWhiteSpace(orgId) && (_cryptoService.OrgKeys?.ContainsKey(orgId) ?? false); return !string.IsNullOrWhiteSpace(orgId) && (_cryptoService.OrgKeys?.ContainsKey(orgId) ?? false);
} }
public void LogOut() public void LogOut(string logoutMessage = null)
{ {
_tokenService.Token = null; _tokenService.Token = null;
_tokenService.RefreshToken = null; _tokenService.RefreshToken = null;
@ -204,6 +217,16 @@ namespace Bit.App.Services
_settings.Remove(Constants.SecurityStamp); _settings.Remove(Constants.SecurityStamp);
_settings.Remove(Constants.PushLastRegistrationDate); _settings.Remove(Constants.PushLastRegistrationDate);
_settings.Remove(Constants.Locked); _settings.Remove(Constants.Locked);
Task.Run(async () => await _deviceApiRepository.PutClearTokenAsync(_appIdService.AppId));
_googleAnalyticsService.TrackAppEvent("LoggedOut");
Device.BeginInvokeOnMainThread(() => Application.Current.MainPage = new ExtendedNavigationPage(new HomePage()));
if(!string.IsNullOrWhiteSpace(logoutMessage))
{
_userDialogs.Toast(logoutMessage);
}
} }
public async Task<FullLoginResult> TokenPostAsync(string email, string masterPassword) public async Task<FullLoginResult> TokenPostAsync(string email, string masterPassword)
@ -249,10 +272,10 @@ namespace Bit.App.Services
return result; return result;
} }
public async Task<LoginResult> TokenPostTwoFactorAsync(TwoFactorProviderType type, string token, bool remember, public async Task<Models.LoginResult> TokenPostTwoFactorAsync(TwoFactorProviderType type, string token, bool remember,
string email, string masterPasswordHash, SymmetricCryptoKey key) string email, string masterPasswordHash, SymmetricCryptoKey key)
{ {
var result = new LoginResult(); var result = new Models.LoginResult();
var request = new TokenRequest var request = new TokenRequest
{ {

View file

@ -250,7 +250,7 @@ namespace Bit.App.Services
else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden
|| response.StatusCode == System.Net.HttpStatusCode.Unauthorized) || response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{ {
MessagingCenter.Send(Application.Current, "Logout", (string)null); _authService.LogOut();
} }
return response; return response;
@ -272,7 +272,7 @@ namespace Bit.App.Services
else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden
|| response.StatusCode == System.Net.HttpStatusCode.Unauthorized) || response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{ {
MessagingCenter.Send(Application.Current, "Logout", (string)null); _authService.LogOut();
} }
return response; return response;
@ -334,7 +334,7 @@ namespace Bit.App.Services
else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden
|| response.StatusCode == System.Net.HttpStatusCode.Unauthorized) || response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{ {
MessagingCenter.Send(Application.Current, "Logout", (string)null); _authService.LogOut();
} }
return response; return response;
@ -359,7 +359,7 @@ namespace Bit.App.Services
else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden
|| response.StatusCode == System.Net.HttpStatusCode.Unauthorized) || response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{ {
MessagingCenter.Send(Application.Current, "Logout", (string)null); _authService.LogOut();
} }
return response; return response;

View file

@ -75,7 +75,7 @@ namespace Bit.App.Services
else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden
|| response.StatusCode == System.Net.HttpStatusCode.Unauthorized) || response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{ {
MessagingCenter.Send(Application.Current, "Logout", (string)null); _authService.LogOut();
} }
return response; return response;
@ -91,7 +91,7 @@ namespace Bit.App.Services
else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden
|| response.StatusCode == System.Net.HttpStatusCode.Unauthorized) || response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{ {
MessagingCenter.Send(Application.Current, "Logout", (string)null); _authService.LogOut();
} }
return response; return response;

View file

@ -4,6 +4,10 @@ using Plugin.Settings.Abstractions;
using Plugin.Fingerprint.Abstractions; using Plugin.Fingerprint.Abstractions;
using Bit.App.Enums; using Bit.App.Enums;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Controls;
using Bit.App.Pages;
using Xamarin.Forms;
using System.Linq;
namespace Bit.App.Services namespace Bit.App.Services
{ {
@ -79,5 +83,58 @@ namespace Bit.App.Services
return LockType.Password; return LockType.Password;
} }
} }
public async Task CheckLockAsync(bool forceLock)
{
if(TopPageIsLock())
{
// already locked
return;
}
var lockType = await GetLockTypeAsync(forceLock);
if(lockType == LockType.None)
{
return;
}
_appSettings.Locked = true;
switch(lockType)
{
case LockType.Fingerprint:
await Application.Current.MainPage.Navigation.PushModalAsync(
new ExtendedNavigationPage(new LockFingerprintPage(!forceLock)), false);
break;
case LockType.PIN:
await Application.Current.MainPage.Navigation.PushModalAsync(
new ExtendedNavigationPage(new LockPinPage()), false);
break;
case LockType.Password:
await Application.Current.MainPage.Navigation.PushModalAsync(
new ExtendedNavigationPage(new LockPasswordPage()), false);
break;
default:
break;
}
}
public bool TopPageIsLock()
{
var currentPage = Application.Current.MainPage.Navigation.ModalStack.LastOrDefault() as ExtendedNavigationPage;
if((currentPage?.CurrentPage as LockFingerprintPage) != null)
{
return true;
}
if((currentPage?.CurrentPage as LockPinPage) != null)
{
return true;
}
if((currentPage?.CurrentPage as LockPasswordPage) != null)
{
return true;
}
return false;
}
} }
} }

View file

@ -305,7 +305,7 @@ namespace Bit.App.Services
if(Application.Current != null && (accountRevisionDate.StatusCode == System.Net.HttpStatusCode.Forbidden if(Application.Current != null && (accountRevisionDate.StatusCode == System.Net.HttpStatusCode.Forbidden
|| accountRevisionDate.StatusCode == System.Net.HttpStatusCode.Unauthorized)) || accountRevisionDate.StatusCode == System.Net.HttpStatusCode.Unauthorized))
{ {
MessagingCenter.Send(Application.Current, "Logout", (string)null); _authService.LogOut();
} }
return false; return false;
@ -477,7 +477,7 @@ namespace Bit.App.Services
} }
SyncInProgress = true; SyncInProgress = true;
MessagingCenter.Send(Application.Current, "SyncStarted"); MessagingCenter.Send(this, "SyncStarted");
} }
private void SyncCompleted(bool successfully) private void SyncCompleted(bool successfully)
@ -488,7 +488,7 @@ namespace Bit.App.Services
} }
SyncInProgress = false; SyncInProgress = false;
MessagingCenter.Send(Application.Current, "SyncCompleted", successfully); MessagingCenter.Send(this, "SyncCompleted", successfully);
} }
private bool CheckSuccess<T>(ApiResult<T> result, bool logout = false) private bool CheckSuccess<T>(ApiResult<T> result, bool logout = false)
@ -501,7 +501,7 @@ namespace Bit.App.Services
result.StatusCode == System.Net.HttpStatusCode.Forbidden || result.StatusCode == System.Net.HttpStatusCode.Forbidden ||
result.StatusCode == System.Net.HttpStatusCode.Unauthorized)) result.StatusCode == System.Net.HttpStatusCode.Unauthorized))
{ {
MessagingCenter.Send(Application.Current, "Logout", (string)null); _authService.LogOut();
} }
return false; return false;

View file

@ -8,15 +8,11 @@ namespace Bit.iOS.Core.Services
public class GoogleAnalyticsService : IGoogleAnalyticsService public class GoogleAnalyticsService : IGoogleAnalyticsService
{ {
private readonly ITracker _tracker; private readonly ITracker _tracker;
private readonly IAuthService _authService;
public GoogleAnalyticsService( public GoogleAnalyticsService(
IAppIdService appIdService, IAppIdService appIdService,
IAuthService authService,
ISettings settings) ISettings settings)
{ {
_authService = authService;
Gai.SharedInstance.DispatchInterval = 10; Gai.SharedInstance.DispatchInterval = 10;
Gai.SharedInstance.TrackUncaughtExceptions = false; Gai.SharedInstance.TrackUncaughtExceptions = false;
_tracker = Gai.SharedInstance.GetTracker("UA-81915606-1"); _tracker = Gai.SharedInstance.GetTracker("UA-81915606-1");

View file

@ -9,16 +9,21 @@ using Xamarin.Forms;
using Photos; using Photos;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Models.Page;
namespace Bit.iOS.Services namespace Bit.iOS.Services
{ {
public class DeviceActionService : IDeviceActionService public class DeviceActionService : IDeviceActionService
{ {
private readonly IAppSettingsService _appSettingsService; private readonly IAppSettingsService _appSettingsService;
private readonly IDeviceInfoService _deviceInfoService;
public DeviceActionService(IAppSettingsService appSettingsService) public DeviceActionService(
IAppSettingsService appSettingsService,
IDeviceInfoService deviceInfoService)
{ {
_appSettingsService = appSettingsService; _appSettingsService = appSettingsService;
_deviceInfoService = deviceInfoService;
} }
public void CopyToClipboard(string text) public void CopyToClipboard(string text)
@ -202,5 +207,48 @@ namespace Bit.iOS.Services
MessagingCenter.Send(Xamarin.Forms.Application.Current, "SelectFileResult", MessagingCenter.Send(Xamarin.Forms.Application.Current, "SelectFileResult",
new Tuple<byte[], string>(data, fileName)); new Tuple<byte[], string>(data, fileName));
} }
public void Autofill(VaultListPageModel.Cipher cipher)
{
throw new NotImplementedException();
}
public void CloseAutofill()
{
throw new NotImplementedException();
}
public void Background()
{
// do nothing
}
public void RateApp()
{
if(_deviceInfoService.Version < 11)
{
Device.OpenUri(new Uri("itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews" +
"?id=1137397744&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software"));
}
else
{
Device.OpenUri(new Uri("itms-apps://itunes.apple.com/us/app/id1137397744?action=write-review"));
}
}
public void DismissKeyboard()
{
// do nothing
}
public void OpenAccessibilitySettings()
{
throw new NotImplementedException();
}
public void LaunchApp(string appName)
{
throw new NotImplementedException();
}
} }
} }