2019-03-28 06:44:54 +03:00
|
|
|
|
using Android.App;
|
2019-03-28 03:12:44 +03:00
|
|
|
|
using Android.Content.PM;
|
|
|
|
|
using Android.Runtime;
|
|
|
|
|
using Android.OS;
|
2019-05-11 06:43:35 +03:00
|
|
|
|
using Bit.Core;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using Bit.App.Abstractions;
|
|
|
|
|
using Bit.Core.Utilities;
|
|
|
|
|
using Bit.Core.Abstractions;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System;
|
|
|
|
|
using Android.Content;
|
|
|
|
|
using Bit.Droid.Utilities;
|
2019-05-16 19:30:20 +03:00
|
|
|
|
using Bit.Droid.Receivers;
|
2019-05-17 19:03:35 +03:00
|
|
|
|
using Bit.App.Models;
|
|
|
|
|
using Bit.Core.Enums;
|
|
|
|
|
using Android.Nfc;
|
2019-05-29 22:50:20 +03:00
|
|
|
|
using Bit.App.Utilities;
|
2019-05-30 18:22:35 +03:00
|
|
|
|
using System.Threading.Tasks;
|
2019-03-28 03:12:44 +03:00
|
|
|
|
|
|
|
|
|
namespace Bit.Droid
|
|
|
|
|
{
|
|
|
|
|
[Activity(
|
|
|
|
|
Label = "Bitwarden",
|
|
|
|
|
Icon = "@mipmap/ic_launcher",
|
2019-06-05 22:09:13 +03:00
|
|
|
|
Theme = "@style/LightTheme.Splash",
|
|
|
|
|
MainLauncher = true,
|
2019-03-28 03:12:44 +03:00
|
|
|
|
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
|
|
|
|
|
[Register("com.x8bit.bitwarden.MainActivity")]
|
2019-03-28 06:44:54 +03:00
|
|
|
|
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
|
2019-03-28 03:12:44 +03:00
|
|
|
|
{
|
2019-05-11 06:43:35 +03:00
|
|
|
|
private IDeviceActionService _deviceActionService;
|
|
|
|
|
private IMessagingService _messagingService;
|
2019-05-16 19:30:20 +03:00
|
|
|
|
private IBroadcasterService _broadcasterService;
|
2019-05-29 06:14:02 +03:00
|
|
|
|
private IUserService _userService;
|
|
|
|
|
private IAppIdService _appIdService;
|
2019-05-30 18:22:35 +03:00
|
|
|
|
private IStorageService _storageService;
|
|
|
|
|
private IStateService _stateService;
|
2019-05-16 19:30:20 +03:00
|
|
|
|
private PendingIntent _lockAlarmPendingIntent;
|
2019-05-30 18:22:35 +03:00
|
|
|
|
private PendingIntent _clearClipboardPendingIntent;
|
2019-05-17 19:03:35 +03:00
|
|
|
|
private AppOptions _appOptions;
|
2019-05-29 06:14:02 +03:00
|
|
|
|
private const string HockeyAppId = "d3834185b4a643479047b86c65293d42";
|
2019-05-28 16:54:08 +03:00
|
|
|
|
private Java.Util.Regex.Pattern _otpPattern =
|
|
|
|
|
Java.Util.Regex.Pattern.Compile("^.*?([cbdefghijklnrtuv]{32,64})$");
|
2019-05-11 06:43:35 +03:00
|
|
|
|
|
2019-03-28 03:12:44 +03:00
|
|
|
|
protected override void OnCreate(Bundle savedInstanceState)
|
|
|
|
|
{
|
2019-05-16 19:30:20 +03:00
|
|
|
|
var alarmIntent = new Intent(this, typeof(LockAlarmReceiver));
|
|
|
|
|
_lockAlarmPendingIntent = PendingIntent.GetBroadcast(this, 0, alarmIntent,
|
|
|
|
|
PendingIntentFlags.UpdateCurrent);
|
2019-05-30 18:22:35 +03:00
|
|
|
|
var clearClipboardIntent = new Intent(this, typeof(ClearClipboardAlarmReceiver));
|
|
|
|
|
_clearClipboardPendingIntent = PendingIntent.GetBroadcast(this, 0, clearClipboardIntent,
|
|
|
|
|
PendingIntentFlags.UpdateCurrent);
|
2019-05-16 19:30:20 +03:00
|
|
|
|
|
2019-05-29 06:03:03 +03:00
|
|
|
|
var policy = new StrictMode.ThreadPolicy.Builder().PermitAll().Build();
|
|
|
|
|
StrictMode.SetThreadPolicy(policy);
|
|
|
|
|
|
2019-05-11 06:43:35 +03:00
|
|
|
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
|
|
|
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
2019-05-16 19:30:20 +03:00
|
|
|
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
2019-05-29 06:14:02 +03:00
|
|
|
|
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
|
|
|
|
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
2019-05-30 18:22:35 +03:00
|
|
|
|
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
|
|
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
2019-05-11 06:43:35 +03:00
|
|
|
|
|
2019-03-28 03:12:44 +03:00
|
|
|
|
TabLayoutResource = Resource.Layout.Tabbar;
|
|
|
|
|
ToolbarResource = Resource.Layout.Toolbar;
|
|
|
|
|
|
2019-05-29 22:50:20 +03:00
|
|
|
|
UpdateTheme(ThemeManager.GetTheme());
|
2019-03-28 03:12:44 +03:00
|
|
|
|
base.OnCreate(savedInstanceState);
|
2019-05-29 06:04:01 +03:00
|
|
|
|
if(!CoreHelpers.InDebugMode())
|
|
|
|
|
{
|
|
|
|
|
Window.AddFlags(Android.Views.WindowManagerFlags.Secure);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-29 06:14:02 +03:00
|
|
|
|
#if !FDROID
|
|
|
|
|
var hockeyAppListener = new HockeyAppCrashManagerListener(_appIdService, _userService);
|
|
|
|
|
var hockeyAppTask = hockeyAppListener.InitAsync();
|
|
|
|
|
HockeyApp.Android.CrashManager.Register(this, HockeyAppId, hockeyAppListener);
|
|
|
|
|
#endif
|
|
|
|
|
|
2019-03-28 06:44:54 +03:00
|
|
|
|
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
|
|
|
|
|
Xamarin.Forms.Forms.Init(this, savedInstanceState);
|
2019-05-17 19:03:35 +03:00
|
|
|
|
_appOptions = GetOptions();
|
|
|
|
|
LoadApplication(new App.App(_appOptions));
|
2019-05-16 19:30:20 +03:00
|
|
|
|
|
|
|
|
|
_broadcasterService.Subscribe(nameof(MainActivity), (message) =>
|
|
|
|
|
{
|
|
|
|
|
if(message.Command == "scheduleLockTimer")
|
|
|
|
|
{
|
|
|
|
|
var lockOptionMs = (int)message.Data * 1000;
|
|
|
|
|
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + lockOptionMs + 10;
|
|
|
|
|
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
|
|
|
|
alarmManager.Set(AlarmType.RtcWakeup, triggerMs, _lockAlarmPendingIntent);
|
|
|
|
|
}
|
|
|
|
|
else if(message.Command == "cancelLockTimer")
|
|
|
|
|
{
|
|
|
|
|
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
|
|
|
|
alarmManager.Cancel(_lockAlarmPendingIntent);
|
|
|
|
|
}
|
2019-05-17 21:46:31 +03:00
|
|
|
|
else if(message.Command == "finishMainActivity")
|
|
|
|
|
{
|
2019-05-31 03:40:50 +03:00
|
|
|
|
Xamarin.Forms.Device.BeginInvokeOnMainThread(() => Finish());
|
2019-05-17 21:46:31 +03:00
|
|
|
|
}
|
2019-05-28 16:54:08 +03:00
|
|
|
|
else if(message.Command == "listenYubiKeyOTP")
|
|
|
|
|
{
|
|
|
|
|
ListenYubiKey((bool)message.Data);
|
|
|
|
|
}
|
2019-05-29 22:50:20 +03:00
|
|
|
|
else if(message.Command == "updatedTheme")
|
|
|
|
|
{
|
2019-05-30 06:02:30 +03:00
|
|
|
|
RestartApp();
|
2019-05-29 22:50:20 +03:00
|
|
|
|
}
|
2019-05-30 07:29:00 +03:00
|
|
|
|
else if(message.Command == "exit")
|
|
|
|
|
{
|
|
|
|
|
ExitApp();
|
|
|
|
|
}
|
2019-05-30 18:22:35 +03:00
|
|
|
|
else if(message.Command == "copiedToClipboard")
|
|
|
|
|
{
|
|
|
|
|
var task = ClearClipboardAlarmAsync(message.Data as Tuple<string, int?, bool>);
|
|
|
|
|
}
|
2019-05-16 19:30:20 +03:00
|
|
|
|
});
|
2019-03-28 03:12:44 +03:00
|
|
|
|
}
|
2019-03-28 06:44:54 +03:00
|
|
|
|
|
2019-05-28 16:54:08 +03:00
|
|
|
|
protected override void OnPause()
|
|
|
|
|
{
|
|
|
|
|
base.OnPause();
|
|
|
|
|
ListenYubiKey(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnResume()
|
|
|
|
|
{
|
|
|
|
|
base.OnResume();
|
|
|
|
|
if(_deviceActionService.SupportsNfc())
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
_messagingService.Send("resumeYubiKey");
|
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnNewIntent(Intent intent)
|
|
|
|
|
{
|
|
|
|
|
base.OnNewIntent(intent);
|
|
|
|
|
ParseYubiKey(intent.DataString);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 06:43:35 +03:00
|
|
|
|
public async override void OnRequestPermissionsResult(int requestCode, string[] permissions,
|
2019-03-28 06:44:54 +03:00
|
|
|
|
[GeneratedEnum] Permission[] grantResults)
|
|
|
|
|
{
|
2019-05-11 06:43:35 +03:00
|
|
|
|
if(requestCode == Constants.SelectFilePermissionRequestCode)
|
|
|
|
|
{
|
|
|
|
|
if(grantResults.Any(r => r != Permission.Granted))
|
|
|
|
|
{
|
|
|
|
|
_messagingService.Send("selectFileCameraPermissionDenied");
|
|
|
|
|
}
|
|
|
|
|
await _deviceActionService.SelectFileAsync();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
2019-05-23 03:28:31 +03:00
|
|
|
|
ZXing.Net.Mobile.Forms.Android.PermissionsHandler.OnRequestPermissionsResult(
|
|
|
|
|
requestCode, permissions, grantResults);
|
2019-05-11 06:43:35 +03:00
|
|
|
|
}
|
2019-03-28 06:44:54 +03:00
|
|
|
|
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
|
|
|
}
|
2019-05-11 06:43:35 +03:00
|
|
|
|
|
|
|
|
|
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
|
|
|
|
|
{
|
|
|
|
|
if(requestCode == Constants.SelectFileRequestCode && resultCode == Result.Ok)
|
|
|
|
|
{
|
|
|
|
|
Android.Net.Uri uri = null;
|
|
|
|
|
string fileName = null;
|
|
|
|
|
if(data != null && data.Data != null)
|
|
|
|
|
{
|
|
|
|
|
uri = data.Data;
|
|
|
|
|
fileName = AndroidHelpers.GetFileName(ApplicationContext, uri);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// camera
|
|
|
|
|
var root = new Java.IO.File(Android.OS.Environment.ExternalStorageDirectory, "bitwarden");
|
|
|
|
|
var file = new Java.IO.File(root, "temp_camera_photo.jpg");
|
|
|
|
|
uri = Android.Net.Uri.FromFile(file);
|
|
|
|
|
fileName = $"photo_{DateTime.UtcNow.ToString("yyyyMMddHHmmss")}.jpg";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(uri == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
using(var stream = ContentResolver.OpenInputStream(uri))
|
|
|
|
|
using(var memoryStream = new MemoryStream())
|
|
|
|
|
{
|
|
|
|
|
stream.CopyTo(memoryStream);
|
|
|
|
|
_messagingService.Send("selectFileResult",
|
|
|
|
|
new Tuple<byte[], string>(memoryStream.ToArray(), fileName ?? "unknown_file_name"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch(Java.IO.FileNotFoundException)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-17 19:03:35 +03:00
|
|
|
|
|
|
|
|
|
private void ListenYubiKey(bool listen)
|
|
|
|
|
{
|
|
|
|
|
if(!_deviceActionService.SupportsNfc())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
var adapter = NfcAdapter.GetDefaultAdapter(this);
|
|
|
|
|
if(listen)
|
|
|
|
|
{
|
|
|
|
|
var intent = new Intent(this, Class);
|
|
|
|
|
intent.AddFlags(ActivityFlags.SingleTop);
|
|
|
|
|
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, 0);
|
|
|
|
|
// register for all NDEF tags starting with http och https
|
|
|
|
|
var ndef = new IntentFilter(NfcAdapter.ActionNdefDiscovered);
|
|
|
|
|
ndef.AddDataScheme("http");
|
|
|
|
|
ndef.AddDataScheme("https");
|
|
|
|
|
var filters = new IntentFilter[] { ndef };
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// register for foreground dispatch so we'll receive tags according to our intent filters
|
|
|
|
|
adapter.EnableForegroundDispatch(this, pendingIntent, filters, null);
|
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
adapter.DisableForegroundDispatch(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private AppOptions GetOptions()
|
|
|
|
|
{
|
|
|
|
|
var options = new AppOptions
|
|
|
|
|
{
|
|
|
|
|
Uri = Intent.GetStringExtra("uri") ?? Intent.GetStringExtra("autofillFrameworkUri"),
|
|
|
|
|
MyVaultTile = Intent.GetBooleanExtra("myVaultTile", false),
|
|
|
|
|
FromAutofillFramework = Intent.GetBooleanExtra("autofillFramework", false)
|
|
|
|
|
};
|
|
|
|
|
var fillType = Intent.GetIntExtra("autofillFrameworkFillType", 0);
|
|
|
|
|
if(fillType > 0)
|
|
|
|
|
{
|
|
|
|
|
options.FillType = (CipherType)fillType;
|
|
|
|
|
}
|
|
|
|
|
if(Intent.GetBooleanExtra("autofillFrameworkSave", false))
|
|
|
|
|
{
|
|
|
|
|
options.SaveType = (CipherType)Intent.GetIntExtra("autofillFrameworkType", 0);
|
|
|
|
|
options.SaveName = Intent.GetStringExtra("autofillFrameworkName");
|
|
|
|
|
options.SaveUsername = Intent.GetStringExtra("autofillFrameworkUsername");
|
|
|
|
|
options.SavePassword = Intent.GetStringExtra("autofillFrameworkPassword");
|
|
|
|
|
options.SaveCardName = Intent.GetStringExtra("autofillFrameworkCardName");
|
|
|
|
|
options.SaveCardNumber = Intent.GetStringExtra("autofillFrameworkCardNumber");
|
|
|
|
|
options.SaveCardExpMonth = Intent.GetStringExtra("autofillFrameworkCardExpMonth");
|
|
|
|
|
options.SaveCardExpYear = Intent.GetStringExtra("autofillFrameworkCardExpYear");
|
|
|
|
|
options.SaveCardCode = Intent.GetStringExtra("autofillFrameworkCardCode");
|
|
|
|
|
}
|
|
|
|
|
return options;
|
|
|
|
|
}
|
2019-05-28 16:54:08 +03:00
|
|
|
|
|
|
|
|
|
private void ParseYubiKey(string data)
|
|
|
|
|
{
|
|
|
|
|
if(data == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
var otpMatch = _otpPattern.Matcher(data);
|
|
|
|
|
if(otpMatch.Matches())
|
|
|
|
|
{
|
|
|
|
|
var otp = otpMatch.Group(1);
|
|
|
|
|
_messagingService.Send("gotYubiKeyOTP", otp);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-29 22:50:20 +03:00
|
|
|
|
|
|
|
|
|
private void UpdateTheme(string theme)
|
|
|
|
|
{
|
|
|
|
|
if(theme == "dark")
|
|
|
|
|
{
|
|
|
|
|
SetTheme(Resource.Style.DarkTheme);
|
|
|
|
|
}
|
2019-06-02 06:59:17 +03:00
|
|
|
|
else if(theme == "black")
|
|
|
|
|
{
|
|
|
|
|
SetTheme(Resource.Style.BlackTheme);
|
|
|
|
|
}
|
2019-06-05 18:36:58 +03:00
|
|
|
|
else if(theme == "nord")
|
|
|
|
|
{
|
|
|
|
|
SetTheme(Resource.Style.NordTheme);
|
|
|
|
|
}
|
2019-05-29 22:50:20 +03:00
|
|
|
|
else
|
|
|
|
|
{
|
2019-06-04 16:36:45 +03:00
|
|
|
|
SetTheme(Resource.Style.LightTheme);
|
2019-05-29 22:50:20 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-30 06:02:30 +03:00
|
|
|
|
|
|
|
|
|
private void RestartApp()
|
|
|
|
|
{
|
2019-06-05 22:09:13 +03:00
|
|
|
|
var intent = new Intent(this, typeof(MainActivity));
|
2019-05-30 06:02:30 +03:00
|
|
|
|
var pendingIntent = PendingIntent.GetActivity(this, 5923650, intent, PendingIntentFlags.CancelCurrent);
|
|
|
|
|
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
|
|
|
|
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + 500;
|
|
|
|
|
alarmManager.Set(AlarmType.Rtc, triggerMs, pendingIntent);
|
|
|
|
|
Java.Lang.JavaSystem.Exit(0);
|
|
|
|
|
}
|
2019-05-30 07:29:00 +03:00
|
|
|
|
|
|
|
|
|
private void ExitApp()
|
|
|
|
|
{
|
|
|
|
|
FinishAffinity();
|
|
|
|
|
Java.Lang.JavaSystem.Exit(0);
|
|
|
|
|
}
|
2019-05-30 18:22:35 +03:00
|
|
|
|
|
|
|
|
|
private async Task ClearClipboardAlarmAsync(Tuple<string, int?, bool> data)
|
|
|
|
|
{
|
|
|
|
|
if(data.Item3)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
var clearMs = data.Item2;
|
|
|
|
|
if(clearMs == null)
|
|
|
|
|
{
|
|
|
|
|
var clearSeconds = await _storageService.GetAsync<int?>(Constants.ClearClipboardKey);
|
|
|
|
|
if(clearSeconds != null)
|
|
|
|
|
{
|
|
|
|
|
clearMs = clearSeconds.Value * 1000;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(clearMs == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
await _stateService.SaveAsync(Constants.LastClipboardValueKey, data.Item1);
|
|
|
|
|
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + clearMs.Value;
|
|
|
|
|
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
|
|
|
|
alarmManager.Set(AlarmType.Rtc, triggerMs, _clearClipboardPendingIntent);
|
|
|
|
|
}
|
2019-03-28 03:12:44 +03:00
|
|
|
|
}
|
2019-03-28 06:44:54 +03:00
|
|
|
|
}
|