diff --git a/src/Android/MainActivity.cs b/src/Android/MainActivity.cs index 1940b3425..50fe0b678 100644 --- a/src/Android/MainActivity.cs +++ b/src/Android/MainActivity.cs @@ -77,7 +77,8 @@ namespace Bit.Android Resolver.Resolve(), Resolver.Resolve(), Resolver.Resolve(), - Resolver.Resolve())); + Resolver.Resolve(), + Resolver.Resolve())); MessagingCenter.Subscribe( Xamarin.Forms.Application.Current, "DismissKeyboard", (sender) => diff --git a/src/Android/Services/DeviceActionService.cs b/src/Android/Services/DeviceActionService.cs index 87ba79afc..c45c8f7a3 100644 --- a/src/Android/Services/DeviceActionService.cs +++ b/src/Android/Services/DeviceActionService.cs @@ -80,5 +80,39 @@ namespace Bit.Android.Services var activities = pm.QueryIntentActivities(intent, global::Android.Content.PM.PackageInfoFlags.MatchDefaultOnly); return (activities?.Count ?? 0) > 0; } + + public void ClearCache() + { + try + { + DeleteDir(CrossCurrentActivity.Current.Activity.CacheDir); + } + 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; + } + } } } diff --git a/src/App/Abstractions/Services/IDeviceActionService.cs b/src/App/Abstractions/Services/IDeviceActionService.cs index 7d7e06ec5..c24febc9c 100644 --- a/src/App/Abstractions/Services/IDeviceActionService.cs +++ b/src/App/Abstractions/Services/IDeviceActionService.cs @@ -5,5 +5,6 @@ void CopyToClipboard(string text); bool OpenFile(byte[] fileData, string id, string fileName); bool CanOpenFile(string fileName); + void ClearCache(); } } diff --git a/src/App/App.cs b/src/App/App.cs index 811bd7a04..003224a8c 100644 --- a/src/App/App.cs +++ b/src/App/App.cs @@ -32,6 +32,7 @@ namespace Bit.App private readonly ILocalizeService _localizeService; private readonly IAppInfoService _appInfoService; private readonly IAppSettingsService _appSettingsService; + private readonly IDeviceActionService _deviceActionService; public App( string uri, @@ -45,7 +46,8 @@ namespace Bit.App IGoogleAnalyticsService googleAnalyticsService, ILocalizeService localizeService, IAppInfoService appInfoService, - IAppSettingsService appSettingsService) + IAppSettingsService appSettingsService, + IDeviceActionService deviceActionService) { _uri = uri; _databaseService = databaseService; @@ -59,6 +61,7 @@ namespace Bit.App _localizeService = localizeService; _appInfoService = appInfoService; _appSettingsService = appSettingsService; + _deviceActionService = deviceActionService; SetCulture(); SetStyles(); @@ -110,6 +113,7 @@ namespace Bit.App await Task.Run(() => FullSyncAsync()).ConfigureAwait(false); } + await Task.Run(() => _deviceActionService.ClearCache()).ConfigureAwait(false); Debug.WriteLine("OnStart"); } diff --git a/src/App/Controls/LabeledRightDetailCell.cs b/src/App/Controls/LabeledRightDetailCell.cs index 258137f9f..f7e97e62e 100644 --- a/src/App/Controls/LabeledRightDetailCell.cs +++ b/src/App/Controls/LabeledRightDetailCell.cs @@ -17,7 +17,6 @@ namespace Bit.App.Controls Detail = new Label { FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)), - LineBreakMode = LineBreakMode.TailTruncation, Style = (Style)Application.Current.Resources["text-muted"], HorizontalOptions = LayoutOptions.End, VerticalOptions = LayoutOptions.Center diff --git a/src/App/Pages/Vault/VaultViewLoginPage.cs b/src/App/Pages/Vault/VaultViewLoginPage.cs index 33b6fe5c3..a864c525d 100644 --- a/src/App/Pages/Vault/VaultViewLoginPage.cs +++ b/src/App/Pages/Vault/VaultViewLoginPage.cs @@ -8,6 +8,7 @@ using Xamarin.Forms; using XLabs.Ioc; using System.Threading.Tasks; using Bit.App.Utilities; +using System.Collections.Generic; namespace Bit.App.Pages { @@ -38,6 +39,7 @@ namespace Bit.App.Pages public LabeledValueCell UriCell { get; set; } public LabeledValueCell NotesCell { get; set; } private EditLoginToolBarItem EditItem { get; set; } + public List AttachmentCells { get; set; } private void Init() { @@ -108,7 +110,7 @@ namespace Bit.App.Pages Intent = TableIntent.Settings, EnableScrolling = true, HasUnevenRows = true, - EnableSelection = false, + EnableSelection = true, Root = new TableRoot { LoginInformationSection, @@ -185,6 +187,7 @@ namespace Bit.App.Pages Table.Root.Add(NotesSection); } + CleanupAttachmentCells(); if(!Model.ShowAttachments && Table.Root.Contains(AttachmentsSection)) { Table.Root.Remove(AttachmentsSection); @@ -192,12 +195,16 @@ namespace Bit.App.Pages else if(Model.ShowAttachments && !Table.Root.Contains(AttachmentsSection)) { AttachmentsSection = new TableSection(AppResources.Attachments); + AttachmentCells = new List(); foreach(var attachment in Model.Attachments) { - AttachmentsSection.Add(new AttachmentViewCell(attachment, async () => + var attachmentCell = new AttachmentViewCell(attachment, async () => { await OpenAttachmentAsync(attachment); - })); + }); + AttachmentCells.Add(attachmentCell); + AttachmentsSection.Add(attachmentCell); + attachmentCell.InitEvents(); } Table.Root.Add(AttachmentsSection); } @@ -209,6 +216,18 @@ namespace Bit.App.Pages { NotesCell.Tapped -= NotesCell_Tapped; EditItem.Dispose(); + CleanupAttachmentCells(); + } + + private void CleanupAttachmentCells() + { + if(AttachmentCells != null) + { + foreach(var cell in AttachmentCells) + { + cell.Dispose(); + } + } } private async Task OpenAttachmentAsync(VaultViewLoginPageModel.Attachment attachment) @@ -274,7 +293,7 @@ namespace Bit.App.Pages } } - public class AttachmentViewCell : LabeledRightDetailCell + public class AttachmentViewCell : LabeledRightDetailCell, IDisposable { private readonly Action _tapped; @@ -285,9 +304,18 @@ namespace Bit.App.Pages Detail.Text = attachment.SizeName; Icon.Source = "download"; BackgroundColor = Color.White; + } + + public void InitEvents() + { Tapped += AttachmentViewCell_Tapped; } + public void Dispose() + { + Tapped -= AttachmentViewCell_Tapped; + } + private void AttachmentViewCell_Tapped(object sender, EventArgs e) { _tapped?.Invoke(); diff --git a/src/App/Utilities/ApiHttpClient.cs b/src/App/Utilities/ApiHttpClient.cs index cc958a07e..9c0187422 100644 --- a/src/App/Utilities/ApiHttpClient.cs +++ b/src/App/Utilities/ApiHttpClient.cs @@ -20,9 +20,9 @@ namespace Bit.App private void Init() { //BaseAddress = new Uri("http://169.254.80.80:4000"); // Desktop from VS Android Emulator - BaseAddress = new Uri("http://192.168.1.4:4000"); // Desktop + //BaseAddress = new Uri("http://192.168.1.3:4000"); // Desktop //BaseAddress = new Uri("https://preview-api.bitwarden.com"); // Preview - // BaseAddress = new Uri("https://api.bitwarden.com"); // Production + BaseAddress = new Uri("https://api.bitwarden.com"); // Production DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); } } diff --git a/src/App/Utilities/IdentityHttpClient.cs b/src/App/Utilities/IdentityHttpClient.cs index 259c6d16c..24ae7fe7d 100644 --- a/src/App/Utilities/IdentityHttpClient.cs +++ b/src/App/Utilities/IdentityHttpClient.cs @@ -20,9 +20,9 @@ namespace Bit.App private void Init() { //BaseAddress = new Uri("http://169.254.80.80:33656"); // Desktop from VS Android Emulator - BaseAddress = new Uri("http://192.168.1.4:33656"); // Desktop + //BaseAddress = new Uri("http://192.168.1.3:33656"); // Desktop //BaseAddress = new Uri("https://identity-api.bitwarden.com"); // Preview - //BaseAddress = new Uri("https://identity.bitwarden.com"); // Production + BaseAddress = new Uri("https://identity.bitwarden.com"); // Production DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); } } diff --git a/src/iOS/AppDelegate.cs b/src/iOS/AppDelegate.cs index cc290f34d..2553818ae 100644 --- a/src/iOS/AppDelegate.cs +++ b/src/iOS/AppDelegate.cs @@ -69,7 +69,8 @@ namespace Bit.iOS Resolver.Resolve(), Resolver.Resolve(), Resolver.Resolve(), - Resolver.Resolve())); + Resolver.Resolve(), + Resolver.Resolve())); // Appearance stuff diff --git a/src/iOS/Services/DeviceActionService.cs b/src/iOS/Services/DeviceActionService.cs index f9cb9086e..23e1b3d28 100644 --- a/src/iOS/Services/DeviceActionService.cs +++ b/src/iOS/Services/DeviceActionService.cs @@ -1,6 +1,8 @@ using System; using Bit.App.Abstractions; using UIKit; +using Foundation; +using System.IO; namespace Bit.iOS.Services { @@ -14,12 +16,63 @@ namespace Bit.iOS.Services public bool OpenFile(byte[] fileData, string id, string fileName) { - return true; + var filePath = Path.Combine(GetTempPath(), fileName); + File.WriteAllBytes(filePath, fileData); + var url = NSUrl.FromFilename(filePath); + var viewer = UIDocumentInteractionController.FromUrl(url); + var controller = GetVisibleViewController(); + return viewer.PresentOpenInMenu(controller.View.Frame, controller.View, true); } public bool CanOpenFile(string fileName) { + // Not sure of a way to check this ahead of time on iOS return true; } + + public void ClearCache() + { + var url = new NSUrl(GetTempPath()); + NSError error; + var tmpFiles = NSFileManager.DefaultManager.GetDirectoryContent(url, null, + NSDirectoryEnumerationOptions.SkipsHiddenFiles, out error); + if(error == null && tmpFiles.Length > 0) + { + foreach(var item in tmpFiles) + { + NSError itemError; + NSFileManager.DefaultManager.Remove(item, out itemError); + } + } + } + + private UIViewController GetVisibleViewController(UIViewController controller = null) + { + controller = controller ?? UIApplication.SharedApplication.KeyWindow.RootViewController; + if(controller.PresentedViewController == null) + { + return controller; + } + + if(controller.PresentedViewController is UINavigationController) + { + return ((UINavigationController)controller.PresentedViewController).VisibleViewController; + } + + if(controller.PresentedViewController is UITabBarController) + { + return ((UITabBarController)controller.PresentedViewController).SelectedViewController; + } + + return GetVisibleViewController(controller.PresentedViewController); + } + + // ref: //https://developer.xamarin.com/guides/ios/application_fundamentals/working_with_the_file_system/ + public string GetTempPath() + { + var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + var tmp = Path.Combine(documents, "..", "tmp"); + return tmp; + } } }