2019-04-29 23:09:27 +03:00
|
|
|
|
using System;
|
2019-05-11 06:43:35 +03:00
|
|
|
|
using System.Collections.Generic;
|
2019-04-29 23:09:27 +03:00
|
|
|
|
using System.IO;
|
2019-05-17 21:34:00 +03:00
|
|
|
|
using System.Linq;
|
2019-04-29 23:09:27 +03:00
|
|
|
|
using System.Threading.Tasks;
|
2019-05-11 06:43:35 +03:00
|
|
|
|
using Android;
|
2019-04-10 06:33:12 +03:00
|
|
|
|
using Android.App;
|
2019-05-17 21:34:00 +03:00
|
|
|
|
using Android.App.Assist;
|
2019-04-29 23:09:27 +03:00
|
|
|
|
using Android.Content;
|
|
|
|
|
using Android.Content.PM;
|
2019-05-17 19:03:35 +03:00
|
|
|
|
using Android.Nfc;
|
2019-05-11 06:43:35 +03:00
|
|
|
|
using Android.OS;
|
|
|
|
|
using Android.Provider;
|
|
|
|
|
using Android.Support.V4.App;
|
2019-04-29 23:09:27 +03:00
|
|
|
|
using Android.Support.V4.Content;
|
2019-05-16 22:54:21 +03:00
|
|
|
|
using Android.Text;
|
|
|
|
|
using Android.Text.Method;
|
2019-05-17 19:03:35 +03:00
|
|
|
|
using Android.Views.Autofill;
|
2019-04-29 23:09:27 +03:00
|
|
|
|
using Android.Webkit;
|
2019-05-09 18:44:27 +03:00
|
|
|
|
using Android.Widget;
|
2019-04-10 06:33:12 +03:00
|
|
|
|
using Bit.App.Abstractions;
|
2019-05-09 18:44:27 +03:00
|
|
|
|
using Bit.App.Resources;
|
2019-04-29 23:09:27 +03:00
|
|
|
|
using Bit.Core;
|
|
|
|
|
using Bit.Core.Abstractions;
|
2019-04-19 16:11:17 +03:00
|
|
|
|
using Bit.Core.Enums;
|
2019-05-17 21:34:00 +03:00
|
|
|
|
using Bit.Core.Models.View;
|
|
|
|
|
using Bit.Core.Utilities;
|
|
|
|
|
using Bit.Droid.Autofill;
|
2019-04-10 06:24:03 +03:00
|
|
|
|
using Plugin.CurrentActivity;
|
|
|
|
|
|
|
|
|
|
namespace Bit.Droid.Services
|
|
|
|
|
{
|
|
|
|
|
public class DeviceActionService : IDeviceActionService
|
|
|
|
|
{
|
2019-04-29 23:09:27 +03:00
|
|
|
|
private readonly IStorageService _storageService;
|
2019-05-11 06:43:35 +03:00
|
|
|
|
private readonly IMessagingService _messagingService;
|
|
|
|
|
private readonly IBroadcasterService _broadcasterService;
|
2019-04-10 06:33:12 +03:00
|
|
|
|
private ProgressDialog _progressDialog;
|
2019-05-11 06:43:35 +03:00
|
|
|
|
private bool _cameraPermissionsDenied;
|
|
|
|
|
private Toast _toast;
|
2019-04-10 06:24:03 +03:00
|
|
|
|
|
2019-05-11 06:43:35 +03:00
|
|
|
|
public DeviceActionService(
|
|
|
|
|
IStorageService storageService,
|
|
|
|
|
IMessagingService messagingService,
|
|
|
|
|
IBroadcasterService broadcasterService)
|
2019-04-29 23:09:27 +03:00
|
|
|
|
{
|
|
|
|
|
_storageService = storageService;
|
2019-05-11 06:43:35 +03:00
|
|
|
|
_messagingService = messagingService;
|
|
|
|
|
_broadcasterService = broadcasterService;
|
|
|
|
|
|
|
|
|
|
_broadcasterService.Subscribe(nameof(DeviceActionService), (message) =>
|
|
|
|
|
{
|
|
|
|
|
if(message.Command == "selectFileCameraPermissionDenied")
|
|
|
|
|
{
|
|
|
|
|
_cameraPermissionsDenied = true;
|
|
|
|
|
}
|
|
|
|
|
});
|
2019-04-29 23:09:27 +03:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-19 16:11:17 +03:00
|
|
|
|
public DeviceType DeviceType => DeviceType.Android;
|
|
|
|
|
|
2019-04-10 06:24:03 +03:00
|
|
|
|
public void Toast(string text, bool longDuration = false)
|
|
|
|
|
{
|
|
|
|
|
if(_toast != null)
|
|
|
|
|
{
|
|
|
|
|
_toast.Cancel();
|
|
|
|
|
_toast.Dispose();
|
|
|
|
|
_toast = null;
|
|
|
|
|
}
|
|
|
|
|
_toast = Android.Widget.Toast.MakeText(CrossCurrentActivity.Current.Activity, text,
|
2019-05-11 06:43:35 +03:00
|
|
|
|
longDuration ? ToastLength.Long : ToastLength.Short);
|
2019-04-10 06:24:03 +03:00
|
|
|
|
_toast.Show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool LaunchApp(string appName)
|
|
|
|
|
{
|
|
|
|
|
var activity = CrossCurrentActivity.Current.Activity;
|
|
|
|
|
appName = appName.Replace("androidapp://", string.Empty);
|
|
|
|
|
var launchIntent = activity.PackageManager.GetLaunchIntentForPackage(appName);
|
|
|
|
|
if(launchIntent != null)
|
|
|
|
|
{
|
|
|
|
|
activity.StartActivity(launchIntent);
|
|
|
|
|
}
|
|
|
|
|
return launchIntent != null;
|
|
|
|
|
}
|
2019-04-10 06:33:12 +03:00
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
2019-04-29 23:09:27 +03:00
|
|
|
|
|
|
|
|
|
public bool OpenFile(byte[] fileData, string id, string fileName)
|
|
|
|
|
{
|
|
|
|
|
if(!CanOpenFile(fileName))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName.Replace(' ', '_').ToLower());
|
|
|
|
|
if(extension == null)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
var mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
|
|
|
|
|
if(mimeType == null)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
|
|
|
var cachePath = activity.CacheDir;
|
|
|
|
|
var filePath = Path.Combine(cachePath.Path, fileName);
|
|
|
|
|
File.WriteAllBytes(filePath, fileData);
|
|
|
|
|
var file = new Java.IO.File(cachePath, fileName);
|
|
|
|
|
if(!file.IsFile)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var intent = new Intent(Intent.ActionView);
|
|
|
|
|
var uri = FileProvider.GetUriForFile(activity.ApplicationContext,
|
|
|
|
|
"com.x8bit.bitwarden.fileprovider", file);
|
|
|
|
|
intent.SetDataAndType(uri, mimeType);
|
|
|
|
|
intent.SetFlags(ActivityFlags.GrantReadUriPermission);
|
|
|
|
|
activity.StartActivity(intent);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool CanOpenFile(string fileName)
|
|
|
|
|
{
|
|
|
|
|
var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName.Replace(' ', '_').ToLower());
|
|
|
|
|
if(extension == null)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
var mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
|
|
|
|
|
if(mimeType == null)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
|
|
|
var intent = new Intent(Intent.ActionView);
|
|
|
|
|
intent.SetType(mimeType);
|
|
|
|
|
var activities = activity.PackageManager.QueryIntentActivities(intent, PackageInfoFlags.MatchDefaultOnly);
|
|
|
|
|
return (activities?.Count ?? 0) > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task ClearCacheAsync()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
DeleteDir(CrossCurrentActivity.Current.Activity.CacheDir);
|
|
|
|
|
await _storageService.SaveAsync(Constants.LastFileCacheClearKey, DateTime.UtcNow);
|
|
|
|
|
}
|
|
|
|
|
catch(Exception) { }
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 06:43:35 +03:00
|
|
|
|
public Task SelectFileAsync()
|
|
|
|
|
{
|
|
|
|
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
|
|
|
var hasStorageWritePermission = !_cameraPermissionsDenied && HasPermission(Manifest.Permission.WriteExternalStorage);
|
|
|
|
|
var additionalIntents = new List<IParcelable>();
|
|
|
|
|
if(activity.PackageManager.HasSystemFeature(PackageManager.FeatureCamera))
|
|
|
|
|
{
|
|
|
|
|
var hasCameraPermission = !_cameraPermissionsDenied && HasPermission(Manifest.Permission.Camera);
|
|
|
|
|
if(!_cameraPermissionsDenied && !hasStorageWritePermission)
|
|
|
|
|
{
|
|
|
|
|
AskPermission(Manifest.Permission.WriteExternalStorage);
|
|
|
|
|
return Task.FromResult(0);
|
|
|
|
|
}
|
|
|
|
|
if(!_cameraPermissionsDenied && !hasCameraPermission)
|
|
|
|
|
{
|
|
|
|
|
AskPermission(Manifest.Permission.Camera);
|
|
|
|
|
return Task.FromResult(0);
|
|
|
|
|
}
|
|
|
|
|
if(!_cameraPermissionsDenied && hasCameraPermission && hasStorageWritePermission)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var root = new Java.IO.File(Android.OS.Environment.ExternalStorageDirectory, "bitwarden");
|
|
|
|
|
var file = new Java.IO.File(root, "temp_camera_photo.jpg");
|
|
|
|
|
if(!file.Exists())
|
|
|
|
|
{
|
|
|
|
|
file.ParentFile.Mkdirs();
|
|
|
|
|
file.CreateNewFile();
|
|
|
|
|
}
|
|
|
|
|
var outputFileUri = Android.Net.Uri.FromFile(file);
|
|
|
|
|
additionalIntents.AddRange(GetCameraIntents(outputFileUri));
|
|
|
|
|
}
|
|
|
|
|
catch(Java.IO.IOException) { }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var docIntent = new Intent(Intent.ActionOpenDocument);
|
|
|
|
|
docIntent.AddCategory(Intent.CategoryOpenable);
|
|
|
|
|
docIntent.SetType("*/*");
|
|
|
|
|
var chooserIntent = Intent.CreateChooser(docIntent, AppResources.FileSource);
|
|
|
|
|
if(additionalIntents.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
chooserIntent.PutExtra(Intent.ExtraInitialIntents, additionalIntents.ToArray());
|
|
|
|
|
}
|
|
|
|
|
activity.StartActivityForResult(chooserIntent, Constants.SelectFileRequestCode);
|
|
|
|
|
return Task.FromResult(0);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-09 18:44:27 +03:00
|
|
|
|
public Task<string> DisplayPromptAync(string title = null, string description = null,
|
2019-05-16 22:54:21 +03:00
|
|
|
|
string text = null, string okButtonText = null, string cancelButtonText = null,
|
|
|
|
|
bool numericKeyboard = false)
|
2019-05-09 18:44:27 +03:00
|
|
|
|
{
|
|
|
|
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
|
|
|
if(activity == null)
|
|
|
|
|
{
|
|
|
|
|
return Task.FromResult<string>(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var alertBuilder = new AlertDialog.Builder(activity);
|
|
|
|
|
alertBuilder.SetTitle(title);
|
|
|
|
|
alertBuilder.SetMessage(description);
|
|
|
|
|
var input = new EditText(activity)
|
|
|
|
|
{
|
2019-05-16 22:54:21 +03:00
|
|
|
|
InputType = InputTypes.ClassText
|
2019-05-09 18:44:27 +03:00
|
|
|
|
};
|
|
|
|
|
if(text == null)
|
|
|
|
|
{
|
|
|
|
|
text = string.Empty;
|
|
|
|
|
}
|
2019-05-16 22:54:21 +03:00
|
|
|
|
if(numericKeyboard)
|
|
|
|
|
{
|
|
|
|
|
input.InputType = InputTypes.ClassNumber | InputTypes.NumberFlagDecimal | InputTypes.NumberFlagSigned;
|
|
|
|
|
#pragma warning disable CS0618 // Type or member is obsolete
|
|
|
|
|
input.KeyListener = DigitsKeyListener.GetInstance(false, false);
|
|
|
|
|
#pragma warning restore CS0618 // Type or member is obsolete
|
|
|
|
|
}
|
2019-05-09 18:44:27 +03:00
|
|
|
|
|
|
|
|
|
input.Text = text;
|
|
|
|
|
input.SetSelection(text.Length);
|
|
|
|
|
var container = new FrameLayout(activity);
|
|
|
|
|
var lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MatchParent,
|
|
|
|
|
LinearLayout.LayoutParams.MatchParent);
|
|
|
|
|
lp.SetMargins(25, 0, 25, 0);
|
|
|
|
|
input.LayoutParameters = lp;
|
|
|
|
|
container.AddView(input);
|
|
|
|
|
alertBuilder.SetView(container);
|
|
|
|
|
|
|
|
|
|
okButtonText = okButtonText ?? AppResources.Ok;
|
|
|
|
|
cancelButtonText = cancelButtonText ?? AppResources.Cancel;
|
|
|
|
|
var result = new TaskCompletionSource<string>();
|
|
|
|
|
alertBuilder.SetPositiveButton(okButtonText,
|
|
|
|
|
(sender, args) => result.TrySetResult(input.Text ?? string.Empty));
|
|
|
|
|
alertBuilder.SetNegativeButton(cancelButtonText, (sender, args) => result.TrySetResult(null));
|
|
|
|
|
|
|
|
|
|
var alert = alertBuilder.Create();
|
|
|
|
|
alert.Window.SetSoftInputMode(Android.Views.SoftInput.StateVisible);
|
|
|
|
|
alert.Show();
|
|
|
|
|
return result.Task;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-15 20:09:49 +03:00
|
|
|
|
public void RateApp()
|
|
|
|
|
{
|
|
|
|
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 21:13:02 +03:00
|
|
|
|
public string GetBuildNumber()
|
|
|
|
|
{
|
|
|
|
|
return Application.Context.ApplicationContext.PackageManager.GetPackageInfo(
|
|
|
|
|
Application.Context.PackageName, 0).VersionCode.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-17 16:45:07 +03:00
|
|
|
|
public bool SupportsFaceId()
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-17 19:03:35 +03:00
|
|
|
|
public bool SupportsNfc()
|
|
|
|
|
{
|
|
|
|
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
|
|
|
var manager = activity.GetSystemService(Context.NfcService) as NfcManager;
|
|
|
|
|
return manager.DefaultAdapter?.IsEnabled ?? false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool SupportsCamera()
|
|
|
|
|
{
|
|
|
|
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
|
|
|
return activity.PackageManager.HasSystemFeature(PackageManager.FeatureCamera);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool SupportsAutofillService()
|
|
|
|
|
{
|
|
|
|
|
if(Build.VERSION.SdkInt < BuildVersionCodes.O)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
|
|
|
var type = Java.Lang.Class.FromType(typeof(AutofillManager));
|
|
|
|
|
var manager = activity.GetSystemService(type) as AutofillManager;
|
|
|
|
|
return manager.IsAutofillSupported;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-17 20:46:32 +03:00
|
|
|
|
public int SystemMajorVersion()
|
|
|
|
|
{
|
|
|
|
|
return (int)Build.VERSION.SdkInt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string SystemModel()
|
|
|
|
|
{
|
|
|
|
|
return Build.Model;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-17 21:04:16 +03:00
|
|
|
|
public Task<string> DisplayAlertAsync(string title, string message, string cancel, params string[] buttons)
|
|
|
|
|
{
|
|
|
|
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
|
|
|
if(activity == null)
|
|
|
|
|
{
|
|
|
|
|
return Task.FromResult<string>(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var result = new TaskCompletionSource<string>();
|
|
|
|
|
var alertBuilder = new AlertDialog.Builder(activity);
|
|
|
|
|
alertBuilder.SetTitle(title);
|
|
|
|
|
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(message))
|
|
|
|
|
{
|
|
|
|
|
if(buttons != null && buttons.Length > 2)
|
|
|
|
|
{
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(title))
|
|
|
|
|
{
|
|
|
|
|
alertBuilder.SetTitle($"{title}: {message}");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
alertBuilder.SetTitle(message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
alertBuilder.SetMessage(message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(buttons != null)
|
|
|
|
|
{
|
|
|
|
|
if(buttons.Length > 2)
|
|
|
|
|
{
|
|
|
|
|
alertBuilder.SetItems(buttons, (sender, args) =>
|
|
|
|
|
{
|
|
|
|
|
result.TrySetResult(buttons[args.Which]);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(buttons.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
alertBuilder.SetPositiveButton(buttons[0], (sender, args) =>
|
|
|
|
|
{
|
|
|
|
|
result.TrySetResult(buttons[0]);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if(buttons.Length > 1)
|
|
|
|
|
{
|
|
|
|
|
alertBuilder.SetNeutralButton(buttons[1], (sender, args) =>
|
|
|
|
|
{
|
|
|
|
|
result.TrySetResult(buttons[1]);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(cancel))
|
|
|
|
|
{
|
|
|
|
|
alertBuilder.SetNegativeButton(cancel, (sender, args) =>
|
|
|
|
|
{
|
|
|
|
|
result.TrySetResult(cancel);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var alert = alertBuilder.Create();
|
|
|
|
|
alert.CancelEvent += (o, args) => { result.TrySetResult(null); };
|
|
|
|
|
alert.Show();
|
|
|
|
|
return result.Task;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-17 21:34:00 +03:00
|
|
|
|
public void Autofill(CipherView cipher)
|
|
|
|
|
{
|
|
|
|
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
|
|
|
if(activity.Intent.GetBooleanExtra("autofillFramework", false))
|
|
|
|
|
{
|
|
|
|
|
if(cipher == null)
|
|
|
|
|
{
|
|
|
|
|
activity.SetResult(Result.Canceled);
|
|
|
|
|
activity.Finish();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-17 22:43:14 +03:00
|
|
|
|
var structure = activity.Intent.GetParcelableExtra(
|
|
|
|
|
AutofillManager.ExtraAssistStructure) as AssistStructure;
|
2019-05-17 21:34:00 +03:00
|
|
|
|
if(structure == null)
|
|
|
|
|
{
|
|
|
|
|
activity.SetResult(Result.Canceled);
|
|
|
|
|
activity.Finish();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
var parser = new Parser(structure, activity.ApplicationContext);
|
|
|
|
|
parser.Parse();
|
|
|
|
|
if(!parser.FieldCollection.Fields.Any() || string.IsNullOrWhiteSpace(parser.Uri))
|
|
|
|
|
{
|
|
|
|
|
activity.SetResult(Result.Canceled);
|
|
|
|
|
activity.Finish();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-17 22:43:14 +03:00
|
|
|
|
var task = CopyTotpAsync(cipher);
|
2019-05-17 21:34:00 +03:00
|
|
|
|
var dataset = AutofillHelpers.BuildDataset(activity, parser.FieldCollection, new FilledItem(cipher));
|
|
|
|
|
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 task = CopyTotpAsync(cipher);
|
|
|
|
|
data.PutExtra("uri", cipher.Login.Uri);
|
|
|
|
|
data.PutExtra("username", cipher.Login.Username);
|
|
|
|
|
data.PutExtra("password", cipher.Login.Password);
|
|
|
|
|
}
|
|
|
|
|
if(activity.Parent == null)
|
|
|
|
|
{
|
|
|
|
|
activity.SetResult(Result.Ok, data);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
activity.Parent.SetResult(Result.Ok, data);
|
|
|
|
|
}
|
|
|
|
|
activity.Finish();
|
|
|
|
|
_messagingService.Send("finishMainActivity");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void CloseAutofill()
|
|
|
|
|
{
|
|
|
|
|
Autofill(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Background()
|
|
|
|
|
{
|
|
|
|
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
|
|
|
if(activity.Intent.GetBooleanExtra("autofillFramework", false))
|
|
|
|
|
{
|
|
|
|
|
activity.SetResult(Result.Canceled);
|
|
|
|
|
activity.Finish();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
activity.MoveTaskToBack(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 21:13:02 +03:00
|
|
|
|
public bool AutofillAccessibilityServiceRunning()
|
|
|
|
|
{
|
|
|
|
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
|
|
|
var manager = activity.GetSystemService(Context.ActivityService) as ActivityManager;
|
|
|
|
|
var services = manager.GetRunningServices(int.MaxValue);
|
|
|
|
|
return services.Any(s => s.Process.ToLowerInvariant().Contains("bitwarden") &&
|
2019-05-31 05:45:48 +03:00
|
|
|
|
s.Service.ClassName.ToLowerInvariant().Contains("accessibilityservice"));
|
2019-05-30 21:13:02 +03:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 21:21:15 +03:00
|
|
|
|
public bool AutofillServiceEnabled()
|
2019-05-30 21:13:02 +03:00
|
|
|
|
{
|
|
|
|
|
if(Build.VERSION.SdkInt < BuildVersionCodes.O)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
|
|
|
var afm = (AutofillManager)activity.GetSystemService(Java.Lang.Class.FromType(typeof(AutofillManager)));
|
|
|
|
|
return afm.IsEnabled && afm.HasEnabledAutofillServices;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-29 23:09:27 +03:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-11 06:43:35 +03:00
|
|
|
|
|
|
|
|
|
private bool HasPermission(string permission)
|
|
|
|
|
{
|
|
|
|
|
return ContextCompat.CheckSelfPermission(
|
|
|
|
|
CrossCurrentActivity.Current.Activity, permission) == Permission.Granted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void AskPermission(string permission)
|
|
|
|
|
{
|
|
|
|
|
ActivityCompat.RequestPermissions(CrossCurrentActivity.Current.Activity, new string[] { permission },
|
|
|
|
|
Constants.SelectFilePermissionRequestCode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<IParcelable> GetCameraIntents(Android.Net.Uri outputUri)
|
|
|
|
|
{
|
|
|
|
|
var intents = new List<IParcelable>();
|
|
|
|
|
var pm = CrossCurrentActivity.Current.Activity.PackageManager;
|
|
|
|
|
var captureIntent = new Intent(MediaStore.ActionImageCapture);
|
|
|
|
|
var listCam = pm.QueryIntentActivities(captureIntent, 0);
|
|
|
|
|
foreach(var res in listCam)
|
|
|
|
|
{
|
|
|
|
|
var packageName = res.ActivityInfo.PackageName;
|
|
|
|
|
var intent = new Intent(captureIntent);
|
|
|
|
|
intent.SetComponent(new ComponentName(packageName, res.ActivityInfo.Name));
|
|
|
|
|
intent.SetPackage(packageName);
|
|
|
|
|
intent.PutExtra(MediaStore.ExtraOutput, outputUri);
|
|
|
|
|
intents.Add(intent);
|
|
|
|
|
}
|
|
|
|
|
return intents;
|
|
|
|
|
}
|
2019-05-15 20:09:49 +03:00
|
|
|
|
|
|
|
|
|
private Intent RateIntentForUrl(string url, Activity activity)
|
|
|
|
|
{
|
|
|
|
|
var intent = new Intent(Intent.ActionView, 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;
|
|
|
|
|
}
|
2019-05-17 21:34:00 +03:00
|
|
|
|
|
|
|
|
|
private async Task CopyTotpAsync(CipherView cipher)
|
|
|
|
|
{
|
|
|
|
|
var autoCopyDisabled = await _storageService.GetAsync<bool?>(Constants.DisableAutoTotpCopyKey);
|
|
|
|
|
var canAccessPremium = await ServiceContainer.Resolve<IUserService>("userService").CanAccessPremiumAsync();
|
2019-05-29 16:55:48 +03:00
|
|
|
|
if((canAccessPremium || cipher.OrganizationUseTotp) && !autoCopyDisabled.GetValueOrDefault() &&
|
2019-05-17 21:34:00 +03:00
|
|
|
|
!string.IsNullOrWhiteSpace(cipher?.Login?.Totp))
|
|
|
|
|
{
|
|
|
|
|
var totp = await ServiceContainer.Resolve<ITotpService>("totpService").GetCodeAsync(cipher.Login.Totp);
|
|
|
|
|
if(totp != null)
|
|
|
|
|
{
|
|
|
|
|
CopyToClipboard(totp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CopyToClipboard(string text)
|
|
|
|
|
{
|
|
|
|
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
|
|
|
var clipboardManager = activity.GetSystemService(
|
|
|
|
|
Context.ClipboardService) as Android.Content.ClipboardManager;
|
|
|
|
|
clipboardManager.Text = text;
|
|
|
|
|
}
|
2019-04-10 06:24:03 +03:00
|
|
|
|
}
|
|
|
|
|
}
|