more device actions

This commit is contained in:
Kyle Spearrin 2019-04-09 23:33:12 -04:00
parent 9e51c46522
commit 0d417b3eee
6 changed files with 279 additions and 5 deletions

View file

@ -1,10 +1,13 @@
using Bit.App.Abstractions; using System.Threading.Tasks;
using Android.App;
using Bit.App.Abstractions;
using Plugin.CurrentActivity; using Plugin.CurrentActivity;
namespace Bit.Droid.Services namespace Bit.Droid.Services
{ {
public class DeviceActionService : IDeviceActionService public class DeviceActionService : IDeviceActionService
{ {
private ProgressDialog _progressDialog;
private Android.Widget.Toast _toast; private Android.Widget.Toast _toast;
public void Toast(string text, bool longDuration = false) public void Toast(string text, bool longDuration = false)
@ -31,5 +34,29 @@ namespace Bit.Droid.Services
} }
return launchIntent != null; return launchIntent != null;
} }
public async Task ShowLoadingAsync(string text)
{
if(_progressDialog != null)
{
await HideLoadingAsync();
}
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
_progressDialog = new ProgressDialog(activity);
_progressDialog.SetMessage(text);
_progressDialog.SetCancelable(false);
_progressDialog.Show();
}
public Task HideLoadingAsync()
{
if(_progressDialog != null)
{
_progressDialog.Dismiss();
_progressDialog.Dispose();
_progressDialog = null;
}
return Task.FromResult(0);
}
} }
} }

View file

@ -1,8 +1,12 @@
namespace Bit.App.Abstractions using System.Threading.Tasks;
namespace Bit.App.Abstractions
{ {
public interface IDeviceActionService public interface IDeviceActionService
{ {
void Toast(string text, bool longDuration = false); void Toast(string text, bool longDuration = false);
bool LaunchApp(string appName); bool LaunchApp(string appName);
Task ShowLoadingAsync(string text);
Task HideLoadingAsync();
} }
} }

143
src/iOS.Core/Views/Toast.cs Normal file
View file

@ -0,0 +1,143 @@
using Foundation;
using System;
using UIKit;
namespace Bit.iOS.Core.Views
{
public class Toast : UIView
{
private NSTimer _dismissTimer;
private NSLayoutConstraint _heightConstraint;
private NSLayoutConstraint _leftMarginConstraint;
private NSLayoutConstraint _rightMarginConstraint;
private NSLayoutConstraint _bottomMarginConstraint;
public Toast(string text)
: base(CoreGraphics.CGRect.FromLTRB(0, 0, 320, 38))
{
TranslatesAutoresizingMaskIntoConstraints = false;
BackgroundColor = UIColor.DarkGray.ColorWithAlpha(0.9f);
Layer.CornerRadius = 15;
Layer.MasksToBounds = true;
MessageLabel = new UILabel
{
TranslatesAutoresizingMaskIntoConstraints = false,
TextColor = UIColor.White,
Font = UIFont.SystemFontOfSize(14),
BackgroundColor = UIColor.Clear,
LineBreakMode = UILineBreakMode.WordWrap,
TextAlignment = UITextAlignment.Center,
Lines = 0,
Text = text
};
AddSubview(MessageLabel);
var hMessageConstraints = NSLayoutConstraint.FromVisualFormat("H:|-5-[messageLabel]-5-|", 0, new NSDictionary(),
NSDictionary.FromObjectsAndKeys(new NSObject[] { MessageLabel },
new NSObject[] { new NSString("messageLabel") })
);
var vMessageConstraints = NSLayoutConstraint.FromVisualFormat("V:|-0-[messageLabel]-0-|", 0, new NSDictionary(),
NSDictionary.FromObjectsAndKeys(new NSObject[] { MessageLabel },
new NSObject[] { new NSString("messageLabel") })
);
AddConstraints(hMessageConstraints);
AddConstraints(vMessageConstraints);
AddGestureRecognizer(new UITapGestureRecognizer(() => Dismiss(false)));
}
public bool Dismissed { get; set; }
public Action DismissCallback { get; set; }
public TimeSpan Duration { get; set; } = TimeSpan.FromSeconds(3);
public UILabel MessageLabel { get; set; }
public nfloat LeftMargin { get; set; } = 5;
public nfloat RightMargin { get; set; } = 5;
public nfloat BottomMargin { get; set; } = 5;
public nfloat Height { get; set; } = 38;
public void Show()
{
if(Superview != null)
{
return;
}
_dismissTimer = NSTimer.CreateScheduledTimer(Duration, x => Dismiss());
LayoutIfNeeded();
var localSuperView = UIApplication.SharedApplication.KeyWindow;
if(localSuperView != null)
{
localSuperView.AddSubview(this);
_heightConstraint = NSLayoutConstraint.Create(this, NSLayoutAttribute.Height,
NSLayoutRelation.GreaterThanOrEqual, null, NSLayoutAttribute.NoAttribute, 1, Height);
_leftMarginConstraint = NSLayoutConstraint.Create(this, NSLayoutAttribute.Left, NSLayoutRelation.Equal,
localSuperView, NSLayoutAttribute.Left, 1, LeftMargin);
_rightMarginConstraint = NSLayoutConstraint.Create(this, NSLayoutAttribute.Right, NSLayoutRelation.Equal,
localSuperView, NSLayoutAttribute.Right, 1, -RightMargin);
_bottomMarginConstraint = NSLayoutConstraint.Create(this, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal,
localSuperView, NSLayoutAttribute.Bottom, 1, -BottomMargin);
// Avoid the "UIView-Encapsulated-Layout-Height" constraint conflicts
// http://stackoverflow.com/questions/25059443/what-is-nslayoutconstraint-uiview-encapsulated-layout-height-and-how-should-i
_leftMarginConstraint.Priority = 999;
_rightMarginConstraint.Priority = 999;
AddConstraint(_heightConstraint);
localSuperView.AddConstraint(_leftMarginConstraint);
localSuperView.AddConstraint(_rightMarginConstraint);
localSuperView.AddConstraint(_bottomMarginConstraint);
ShowWithAnimation();
}
else
{
Console.WriteLine("Toast needs a keyWindows to display.");
}
}
public void Dismiss(bool animated = true)
{
if(Dismissed)
{
return;
}
Dismissed = true;
_dismissTimer?.Invalidate();
_dismissTimer = null;
if(!animated)
{
RemoveFromSuperview();
DismissCallback?.Invoke();
return;
}
SetNeedsLayout();
Animate(0.3f, 0, UIViewAnimationOptions.CurveEaseIn, () => { Alpha = 0; }, () =>
{
RemoveFromSuperview();
DismissCallback?.Invoke();
});
}
private void ShowWithAnimation()
{
Alpha = 0;
SetNeedsLayout();
_bottomMarginConstraint.Constant = -BottomMargin;
_leftMarginConstraint.Constant = LeftMargin;
_rightMarginConstraint.Constant = -RightMargin;
AnimateNotify(0.3f, 0, 0.7f, 5f, UIViewAnimationOptions.CurveEaseInOut, () => { Alpha = 1; }, null);
}
}
}

View file

@ -49,6 +49,7 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\CryptoPrimitiveService.cs" /> <Compile Include="Services\CryptoPrimitiveService.cs" />
<Compile Include="Services\KeyChainStorageService.cs" /> <Compile Include="Services\KeyChainStorageService.cs" />
<Compile Include="Views\Toast.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Core\Core.csproj"> <ProjectReference Include="..\Core\Core.csproj">

View file

@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.iOS.Core.Views;
using CoreGraphics;
using Foundation;
using UIKit;
namespace Bit.iOS.Services
{
public class DeviceActionService : IDeviceActionService
{
private Toast _toast;
private UIAlertController _progressAlert;
public bool LaunchApp(string appName)
{
throw new NotImplementedException();
}
public void Toast(string text, bool longDuration = false)
{
if(!_toast?.Dismissed ?? false)
{
_toast.Dismiss(false);
}
_toast = new Toast(text)
{
Duration = TimeSpan.FromSeconds(longDuration ? 5 : 3)
};
if(TabBarVisible())
{
_toast.BottomMargin = 55;
}
_toast.Show();
_toast.DismissCallback = () =>
{
_toast?.Dispose();
_toast = null;
};
}
public Task ShowLoadingAsync(string text)
{
if(_progressAlert != null)
{
HideLoadingAsync().GetAwaiter().GetResult();
}
var result = new TaskCompletionSource<int>();
var loadingIndicator = new UIActivityIndicatorView(new CGRect(10, 5, 50, 50));
loadingIndicator.HidesWhenStopped = true;
loadingIndicator.ActivityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray;
loadingIndicator.StartAnimating();
_progressAlert = UIAlertController.Create(null, text, UIAlertControllerStyle.Alert);
_progressAlert.View.TintColor = UIColor.Black;
_progressAlert.View.Add(loadingIndicator);
var vc = GetPresentedViewController();
vc?.PresentViewController(_progressAlert, false, () => result.TrySetResult(0));
return result.Task;
}
public Task HideLoadingAsync()
{
var result = new TaskCompletionSource<int>();
if(_progressAlert == null)
{
result.TrySetResult(0);
}
_progressAlert.DismissViewController(false, () => result.TrySetResult(0));
_progressAlert.Dispose();
_progressAlert = null;
return result.Task;
}
private UIViewController GetPresentedViewController()
{
var window = UIApplication.SharedApplication.KeyWindow;
var vc = window.RootViewController;
while(vc.PresentedViewController != null)
{
vc = vc.PresentedViewController;
}
return vc;
}
private bool TabBarVisible()
{
var vc = GetPresentedViewController();
return vc != null && (vc is UITabBarController ||
(vc.ChildViewControllers?.Any(c => c is UITabBarController) ?? false));
}
}
}

View file

@ -92,6 +92,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="Main.cs" /> <Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" /> <Compile Include="AppDelegate.cs" />
<Compile Include="Services\DeviceActionService.cs" />
<None Include="Entitlements.plist" /> <None Include="Entitlements.plist" />
<None Include="Info.plist" /> <None Include="Info.plist" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
@ -171,7 +172,5 @@
<IsWatchApp>false</IsWatchApp> <IsWatchApp>false</IsWatchApp>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup />
<Folder Include="Services\" />
</ItemGroup>
</Project> </Project>