Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fcf006484 | ||
|
|
48e5b1686f | ||
|
|
608d879c80 | ||
|
|
66055f1d7c | ||
|
|
1120bff34d | ||
|
|
24547e67bf | ||
|
|
f8c7285f56 | ||
|
|
82eb6a4568 | ||
|
|
a3f1f7c78d | ||
|
|
c377c4a52b | ||
|
|
e5a74cf43c | ||
|
|
ccf2bf84da | ||
|
|
fbf3d97d57 | ||
|
|
6da0f82ddd | ||
|
|
4c3df2e1e1 | ||
|
|
39e10ff01c | ||
|
|
0b29c6e5a4 | ||
|
|
cd3585be58 | ||
|
|
4e1f91f4d5 | ||
|
|
272c2e5303 | ||
|
|
2a82b09f7b | ||
|
|
fd26492577 | ||
|
|
528b90b694 | ||
|
|
9c7961ff6b | ||
|
|
954ed6457c | ||
|
|
5a8fc2dabc | ||
|
|
ce965ba5e1 | ||
|
|
4b9a036e5e | ||
|
|
4576f378cc | ||
|
|
4c65daa995 | ||
|
|
9159e14dd9 | ||
|
|
da661c229c | ||
|
|
973f09f98a | ||
|
|
fef370ad88 | ||
|
|
bc46b7172d | ||
|
|
71f546b467 | ||
|
|
a66f66c8ac | ||
|
|
c2c6ca22db | ||
|
|
b29440556a | ||
|
|
4104f6f772 | ||
|
|
01c56dabdf | ||
|
|
fafd8f8ee6 | ||
|
|
e2033eee23 | ||
|
|
780761664d | ||
|
|
0cfa737eff | ||
|
|
6463898c5d | ||
|
|
7d4fffa8b6 | ||
|
|
bcc415ccb3 | ||
|
|
827fead347 | ||
|
|
36cdc7dd1c | ||
|
|
99dceda8ac | ||
|
|
9d27f111bf | ||
|
|
69e0906491 | ||
|
|
1d48171fd5 | ||
|
|
cb0a3e3edf | ||
|
|
2b3915a91f | ||
|
|
9a403ba0ed | ||
|
|
0f35885d1c | ||
|
|
e3e07b6bfe | ||
|
|
84a6d1db71 | ||
|
|
8cee50299f | ||
|
|
5a78cbef02 | ||
|
|
ae66a781d1 | ||
|
|
5ae3b66e06 | ||
|
|
c3f1cee5d6 | ||
|
|
5552c42e37 | ||
|
|
6f146b888b | ||
|
|
56c09eae90 | ||
|
|
6883864e2d | ||
|
|
15bc395454 | ||
|
|
41997d5fe0 | ||
|
|
ed259cd130 | ||
|
|
1dc027cf49 | ||
|
|
b2abcda111 | ||
|
|
d66eaf8855 | ||
|
|
78cfd82fdd | ||
|
|
8ad44b405d | ||
|
|
4ce4288f68 | ||
|
|
d635555576 | ||
|
|
44999557c0 | ||
|
|
5d64bab719 | ||
|
|
4d3d8b643a | ||
|
|
915e8cf072 | ||
|
|
3c18fd7636 | ||
|
|
6c00ac43fc | ||
|
|
5d9a597d8d | ||
|
|
92930955c3 | ||
|
|
145482ea30 | ||
|
|
6fdb1e3356 | ||
|
|
55dff81b9f | ||
|
|
ed37972b99 | ||
|
|
c6b37307b0 | ||
|
|
5d719ba235 | ||
|
|
c19795cce0 |
20
.gitignore
vendored
@@ -1,15 +1,28 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# Visual Studio (>=2015) project-specific, machine local files
|
||||
.vs/
|
||||
|
||||
# JetBrains Rider project-specific, machine local files
|
||||
.idea
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# ignore Xamarin.Android Resource.Designer.cs files
|
||||
**/*.Droid/**/[Rr]esource.[Dd]esigner.cs
|
||||
**/*.Android/**/[Rr]esource.[Dd]esigner.cs
|
||||
**/Android/**/[Rr]esource.[Dd]esigner.cs
|
||||
**/Droid/**/[Rr]esource.[Dd]esigner.cs
|
||||
|
||||
# Xamarin Components
|
||||
Components/
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
@@ -22,9 +35,6 @@ bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# Visual Studo 2015 cache/options directory
|
||||
.vs/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
image:
|
||||
- Visual Studio 2019 Preview
|
||||
- Visual Studio 2019
|
||||
- Ubuntu1804
|
||||
|
||||
branches:
|
||||
@@ -55,7 +55,7 @@ before_build:
|
||||
- ps: |
|
||||
if($isWindows) {
|
||||
nuget restore
|
||||
if($env:KEYSTORE_DEC_SECRET -or $env:GOOGLE_SERVICES_DEC_SECRET -or $env:PLAY_DEC_SECRET) {
|
||||
if($env:UPLOAD_KEYSTORE_DEC_SECRET -or$env:KEYSTORE_DEC_SECRET -or $env:GOOGLE_SERVICES_DEC_SECRET -or $env:PLAY_DEC_SECRET) {
|
||||
nuget install secure-file -ExcludeVersion
|
||||
}
|
||||
if($env:GOOGLE_SERVICES_DEC_SECRET) {
|
||||
@@ -98,6 +98,7 @@ build_script:
|
||||
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
|
||||
.\src\Android\ci-build-apks.ps1
|
||||
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
|
||||
Push-AppveyorArtifact .\com.x8bit.bitwarden.aab
|
||||
Push-AppveyorArtifact .\com.x8bit.bitwarden.apk
|
||||
Push-AppveyorArtifact .\com.x8bit.bitwarden-fdroid.apk
|
||||
}
|
||||
@@ -109,12 +110,12 @@ on_success:
|
||||
npm run deploy
|
||||
fi
|
||||
- ps: |
|
||||
if($isWindows -and $env:PLAY_DEC_SECRET) {
|
||||
if($isWindows -and $env:PLAY_DEC_SECRET -and $env:APPVEYOR_REPO_BRANCH -eq 'master') {
|
||||
secure-file\tools\secure-file -decrypt store\google\Publisher\play_creds.json.enc -secret $env:PLAY_DEC_SECRET
|
||||
cd store\google\Publisher\bin\Release\netcoreapp2.0
|
||||
dotnet Publisher.dll `
|
||||
$env:APPVEYOR_BUILD_FOLDER\store\google\Publisher\play_creds.json `
|
||||
$env:APPVEYOR_BUILD_FOLDER\com.x8bit.bitwarden.apk `
|
||||
$env:APPVEYOR_BUILD_FOLDER\com.x8bit.bitwarden.aab `
|
||||
alpha
|
||||
cd $env:APPVEYOR_BUILD_FOLDER
|
||||
}
|
||||
|
||||
@@ -279,6 +279,7 @@ Global
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|Any CPU.ActiveCfg = Debug|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.Build.0 = Debug|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.Deploy.0 = Debug|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|Any CPU.ActiveCfg = FDroid|iPhone
|
||||
@@ -306,6 +307,7 @@ Global
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.ActiveCfg = Debug|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Build.0 = Debug|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Deploy.0 = Debug|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.ActiveCfg = Release|iPhone
|
||||
@@ -334,6 +336,7 @@ Global
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|Any CPU.ActiveCfg = Debug|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.Build.0 = Debug|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.Deploy.0 = Debug|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|Any CPU.ActiveCfg = Release|iPhone
|
||||
|
||||
@@ -4,6 +4,8 @@ using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using System;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Droid.Accessibility
|
||||
{
|
||||
@@ -16,13 +18,13 @@ namespace Bit.Droid.Accessibility
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
base.OnCreate(bundle);
|
||||
LaunchMainActivity(Intent, 932473);
|
||||
HandleIntent(Intent, 932473);
|
||||
}
|
||||
|
||||
protected override void OnNewIntent(Intent intent)
|
||||
{
|
||||
base.OnNewIntent(intent);
|
||||
LaunchMainActivity(intent, 489729);
|
||||
HandleIntent(intent, 489729);
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
@@ -33,7 +35,7 @@ namespace Bit.Droid.Accessibility
|
||||
protected override void OnResume()
|
||||
{
|
||||
base.OnResume();
|
||||
if(!Intent.HasExtra("uri"))
|
||||
if (!Intent.HasExtra("uri"))
|
||||
{
|
||||
Finish();
|
||||
return;
|
||||
@@ -44,7 +46,7 @@ namespace Bit.Droid.Accessibility
|
||||
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
|
||||
{
|
||||
base.OnActivityResult(requestCode, resultCode, data);
|
||||
if(data == null)
|
||||
if (data == null)
|
||||
{
|
||||
AccessibilityHelpers.LastCredentials = null;
|
||||
}
|
||||
@@ -52,7 +54,7 @@ namespace Bit.Droid.Accessibility
|
||||
{
|
||||
try
|
||||
{
|
||||
if(data.GetStringExtra("canceled") != null)
|
||||
if (data.GetStringExtra("canceled") != null)
|
||||
{
|
||||
AccessibilityHelpers.LastCredentials = null;
|
||||
}
|
||||
@@ -78,23 +80,38 @@ namespace Bit.Droid.Accessibility
|
||||
Finish();
|
||||
}
|
||||
|
||||
private void HandleIntent(Intent callingIntent, int requestCode)
|
||||
{
|
||||
if (callingIntent?.GetBooleanExtra("autofillTileClicked", false) ?? false)
|
||||
{
|
||||
Intent.RemoveExtra("autofillTileClicked");
|
||||
var messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
messagingService.Send("OnAutofillTileClick");
|
||||
Finish();
|
||||
}
|
||||
else
|
||||
{
|
||||
LaunchMainActivity(callingIntent, requestCode);
|
||||
}
|
||||
}
|
||||
|
||||
private void LaunchMainActivity(Intent callingIntent, int requestCode)
|
||||
{
|
||||
_lastQueriedUri = callingIntent?.GetStringExtra("uri");
|
||||
if(_lastQueriedUri == null)
|
||||
if (_lastQueriedUri == null)
|
||||
{
|
||||
Finish();
|
||||
return;
|
||||
}
|
||||
var now = DateTime.UtcNow;
|
||||
if(_lastLaunch.HasValue && (now - _lastLaunch.Value) <= TimeSpan.FromSeconds(2))
|
||||
if (_lastLaunch.HasValue && (now - _lastLaunch.Value) <= TimeSpan.FromSeconds(2))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_lastLaunch = now;
|
||||
var intent = new Intent(this, typeof(MainActivity));
|
||||
if(!callingIntent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory))
|
||||
if (!callingIntent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory))
|
||||
{
|
||||
intent.PutExtra("uri", _lastQueriedUri);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.Res;
|
||||
using Android.Graphics;
|
||||
using Android.OS;
|
||||
using Android.Provider;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Views.Accessibility;
|
||||
using Android.Widget;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Plugin.CurrentActivity;
|
||||
|
||||
namespace Bit.Droid.Accessibility
|
||||
{
|
||||
@@ -20,62 +20,91 @@ namespace Bit.Droid.Accessibility
|
||||
public static Credentials LastCredentials = null;
|
||||
public static string SystemUiPackage = "com.android.systemui";
|
||||
public static string BitwardenTag = "bw_access";
|
||||
public static bool IsAutofillTileAdded = false;
|
||||
public static bool IsAccessibilityBroadcastReady = false;
|
||||
|
||||
// Be sure to keep these two sections sorted alphabetically
|
||||
public static Dictionary<string, Browser> SupportedBrowsers => new List<Browser>
|
||||
{
|
||||
new Browser("com.android.chrome", "url_bar"),
|
||||
new Browser("com.chrome.beta", "url_bar"),
|
||||
new Browser("org.chromium.chrome", "url_bar"),
|
||||
// [Section A] Entries also present in the list of Autofill Framework
|
||||
//
|
||||
// So keep them in sync with:
|
||||
// - AutofillHelpers.{TrustedBrowsers,CompatBrowsers}
|
||||
// - Resources/xml/autofillservice.xml
|
||||
new Browser("com.amazon.cloud9", "url"),
|
||||
new Browser("com.android.browser", "url"),
|
||||
new Browser("com.android.chrome", "url_bar"),
|
||||
new Browser("com.avast.android.secure.browser", "editor"),
|
||||
new Browser("com.avg.android.secure.browser", "editor"),
|
||||
new Browser("com.brave.browser", "url_bar"),
|
||||
new Browser("com.brave.browser_beta", "url_bar"),
|
||||
new Browser("com.brave.browser_default", "url_bar"),
|
||||
new Browser("com.brave.browser_dev", "url_bar"),
|
||||
new Browser("com.brave.browser_nightly", "url_bar"),
|
||||
new Browser("com.chrome.beta", "url_bar"),
|
||||
new Browser("com.chrome.canary", "url_bar"),
|
||||
new Browser("com.chrome.dev", "url_bar"),
|
||||
new Browser("com.duckduckgo.mobile.android", "omnibarTextInput"),
|
||||
new Browser("com.ecosia.android", "url_bar"),
|
||||
new Browser("com.google.android.apps.chrome", "url_bar"),
|
||||
new Browser("com.google.android.apps.chrome_dev", "url_bar"),
|
||||
new Browser("com.kiwibrowser.browser", "url_bar"),
|
||||
new Browser("com.microsoft.emmx", "url_bar"),
|
||||
new Browser("com.naver.whale", "url_bar"),
|
||||
new Browser("com.opera.browser", "url_field"),
|
||||
new Browser("com.opera.browser.beta", "url_field"),
|
||||
new Browser("com.opera.mini.native", "url_field"),
|
||||
new Browser("com.opera.mini.native.beta", "url_field"),
|
||||
new Browser("com.opera.touch", "addressbarEdit"),
|
||||
new Browser("com.chrome.dev", "url_bar"),
|
||||
new Browser("com.chrome.canary", "url_bar"),
|
||||
new Browser("com.google.android.apps.chrome", "url_bar"),
|
||||
new Browser("com.google.android.apps.chrome_dev", "url_bar"),
|
||||
new Browser("org.codeaurora.swe.browser", "url_bar"),
|
||||
new Browser("org.iron.srware", "url_bar"),
|
||||
new Browser("com.qwant.liberty", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
||||
new Browser("com.sec.android.app.sbrowser", "location_bar_edit_text"),
|
||||
new Browser("com.sec.android.app.sbrowser.beta", "location_bar_edit_text"),
|
||||
new Browser("com.stoutner.privacybrowser.free", "url_edittext"),
|
||||
new Browser("com.stoutner.privacybrowser.standard", "url_edittext"),
|
||||
new Browser("com.vivaldi.browser", "url_bar"),
|
||||
new Browser("com.vivaldi.browser.snapshot", "url_bar"),
|
||||
new Browser("com.vivaldi.browser.sopranos", "url_bar"),
|
||||
new Browser("com.yandex.browser", "bro_omnibar_address_title_text,bro_omnibox_collapsed_title",
|
||||
(s) => s.Split(new char[]{' ', ' '}).FirstOrDefault()), // 0 = Regular Space, 1 = No-break space (00A0)
|
||||
new Browser("org.mozilla.firefox", "url_bar_title"),
|
||||
new Browser("org.mozilla.firefox_beta", "url_bar_title"),
|
||||
new Browser("org.mozilla.fennec_aurora", "url_bar_title"),
|
||||
new Browser("org.mozilla.fennec_fdroid", "url_bar_title"),
|
||||
new Browser("org.mozilla.focus", "display_url"),
|
||||
new Browser("org.mozilla.klar", "display_url"),
|
||||
new Browser("mark.via.gp", "aw"),
|
||||
new Browser("org.adblockplus.browser", "url_bar,url_bar_title"), // 2nd = Legacy (before v2)
|
||||
new Browser("org.adblockplus.browser.beta", "url_bar,url_bar_title"), // 2nd = Legacy (before v2)
|
||||
new Browser("org.bromite.bromite", "url_bar"),
|
||||
new Browser("org.chromium.chrome", "url_bar"),
|
||||
new Browser("org.codeaurora.swe.browser", "url_bar"),
|
||||
new Browser("org.gnu.icecat", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
||||
new Browser("org.mozilla.fenix", "mozac_browser_toolbar_url_view"),
|
||||
new Browser("org.mozilla.fenix.nightly", "mozac_browser_toolbar_url_view"),
|
||||
new Browser("org.mozilla.fennec_aurora", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
||||
new Browser("org.mozilla.fennec_fdroid", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
||||
new Browser("org.mozilla.firefox", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
||||
new Browser("org.mozilla.firefox_beta", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
||||
new Browser("org.mozilla.focus", "display_url"),
|
||||
new Browser("org.mozilla.klar", "display_url"),
|
||||
new Browser("org.mozilla.reference.browser", "mozac_browser_toolbar_url_view"),
|
||||
new Browser("org.mozilla.rocket", "display_url"),
|
||||
new Browser("org.torproject.torbrowser", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
||||
new Browser("org.torproject.torbrowser_alpha", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
||||
|
||||
// [Section B] Entries only present here
|
||||
//
|
||||
// FIXME: Test the compatibility of these with Autofill Framework
|
||||
new Browser("acr.browser.barebones", "search"),
|
||||
new Browser("acr.browser.lightning", "search"),
|
||||
new Browser("com.feedback.browser.wjbrowser", "addressbar_url"),
|
||||
new Browser("com.ghostery.android.ghostery", "search_field"),
|
||||
new Browser("org.adblockplus.browser", "url_bar_title"),
|
||||
new Browser("com.htc.sense.browser", "title"),
|
||||
new Browser("com.amazon.cloud9", "url"),
|
||||
new Browser("mobi.mgeek.TunnyBrowser", "title"),
|
||||
new Browser("com.nubelacorp.javelin", "enterUrl"),
|
||||
new Browser("com.jerky.browser2", "enterUrl"),
|
||||
new Browser("com.ksmobile.cb", "address_bar_edit_text"),
|
||||
new Browser("com.linkbubble.playstore", "url_text"),
|
||||
new Browser("com.mx.browser", "address_editor_with_progress"),
|
||||
new Browser("com.mx.browser.tablet", "address_editor_with_progress"),
|
||||
new Browser("com.linkbubble.playstore", "url_text"),
|
||||
new Browser("com.ksmobile.cb", "address_bar_edit_text"),
|
||||
new Browser("acr.browser.lightning", "search"),
|
||||
new Browser("acr.browser.barebones", "search"),
|
||||
new Browser("com.microsoft.emmx", "url_bar"),
|
||||
new Browser("com.duckduckgo.mobile.android", "omnibarTextInput"),
|
||||
new Browser("mark.via.gp", "aw"),
|
||||
new Browser("org.bromite.bromite", "url_bar"),
|
||||
new Browser("com.kiwibrowser.browser", "url_bar"),
|
||||
new Browser("com.ecosia.android", "url_bar"),
|
||||
new Browser("com.qwant.liberty", "url_bar_title"),
|
||||
new Browser("com.nubelacorp.javelin", "enterUrl"),
|
||||
new Browser("jp.co.fenrir.android.sleipnir", "url_text"),
|
||||
new Browser("jp.co.fenrir.android.sleipnir_black", "url_text"),
|
||||
new Browser("jp.co.fenrir.android.sleipnir_test", "url_text"),
|
||||
new Browser("com.vivaldi.browser", "url_bar"),
|
||||
new Browser("com.feedback.browser.wjbrowser", "addressbar_url"),
|
||||
new Browser("mobi.mgeek.TunnyBrowser", "title"),
|
||||
new Browser("org.iron.srware", "url_bar"),
|
||||
}.ToDictionary(n => n.PackageName);
|
||||
|
||||
// Known packages to skip
|
||||
@@ -98,12 +127,23 @@ namespace Bit.Droid.Accessibility
|
||||
"com.ss.squarehome2",
|
||||
"com.treydev.pns"
|
||||
};
|
||||
|
||||
// Be sure to keep these entries sorted alphabetically
|
||||
public static Dictionary<string, KnownUsernameField> KnownUsernameFields => new List<KnownUsernameField>
|
||||
{
|
||||
new KnownUsernameField("accounts.google.com", "ServiceLogin", "Email"),
|
||||
new KnownUsernameField("amazon.com", "signin", "ap_email_login"),
|
||||
new KnownUsernameField("github.com", "", "user[login]-footer"),
|
||||
new KnownUsernameField("paypal.com", "signin", "email"),
|
||||
new KnownUsernameField("signin.aws.amazon.com", "signin", "resolving_input"),
|
||||
new KnownUsernameField("signin.ebay.com", "eBayISAPI.dll", "userid"),
|
||||
}.ToDictionary(n => n.UriAuthority);
|
||||
|
||||
public static void PrintTestData(AccessibilityNodeInfo root, AccessibilityEvent e)
|
||||
{
|
||||
var testNodes = GetWindowNodes(root, e, n => n.ViewIdResourceName != null && n.Text != null, false);
|
||||
var testNodesData = testNodes.Select(n => new { id = n.ViewIdResourceName, text = n.Text });
|
||||
foreach(var node in testNodesData)
|
||||
foreach (var node in testNodesData)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("Node: {0} = {1}", node.id, node.text);
|
||||
}
|
||||
@@ -112,21 +152,21 @@ namespace Bit.Droid.Accessibility
|
||||
public static string GetUri(AccessibilityNodeInfo root)
|
||||
{
|
||||
var uri = string.Concat(Constants.AndroidAppProtocol, root.PackageName);
|
||||
if(SupportedBrowsers.ContainsKey(root.PackageName))
|
||||
if (SupportedBrowsers.ContainsKey(root.PackageName))
|
||||
{
|
||||
var browser = SupportedBrowsers[root.PackageName];
|
||||
AccessibilityNodeInfo addressNode = null;
|
||||
foreach(var uriViewId in browser.UriViewId.Split(","))
|
||||
foreach (var uriViewId in browser.UriViewId.Split(","))
|
||||
{
|
||||
addressNode = root.FindAccessibilityNodeInfosByViewId(
|
||||
$"{root.PackageName}:id/{uriViewId}").FirstOrDefault();
|
||||
if(addressNode != null)
|
||||
if (addressNode != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(addressNode != null)
|
||||
if (addressNode != null)
|
||||
{
|
||||
uri = ExtractUri(uri, addressNode, browser);
|
||||
addressNode.Recycle();
|
||||
@@ -141,35 +181,31 @@ namespace Bit.Droid.Accessibility
|
||||
return uri;
|
||||
}
|
||||
|
||||
public static string ExtractUri(string uri, AccessibilityNodeInfo addressNode, Browser browser)
|
||||
private static string ExtractUri(string uri, AccessibilityNodeInfo addressNode, Browser browser)
|
||||
{
|
||||
if(addressNode?.Text == null)
|
||||
if (addressNode?.Text == null)
|
||||
{
|
||||
return uri;
|
||||
}
|
||||
if(addressNode.Text == null)
|
||||
if (addressNode.Text == null)
|
||||
{
|
||||
return uri;
|
||||
}
|
||||
uri = browser.GetUriFunction(addressNode.Text)?.Trim();
|
||||
if(uri != null && uri.Contains("."))
|
||||
if (uri != null && uri.Contains("."))
|
||||
{
|
||||
if(!uri.Contains("://") && !uri.Contains(" "))
|
||||
var hasHttpProtocol = uri.StartsWith("http://") || uri.StartsWith("https://");
|
||||
if (!hasHttpProtocol && uri.Contains("."))
|
||||
{
|
||||
uri = string.Concat("http://", uri);
|
||||
}
|
||||
else if(Build.VERSION.SdkInt <= BuildVersionCodes.KitkatWatch)
|
||||
{
|
||||
var parts = uri.Split(new string[] { ". " }, StringSplitOptions.None);
|
||||
if(parts.Length > 1)
|
||||
if (Uri.TryCreate("http://" + uri, UriKind.Absolute, out var uri2))
|
||||
{
|
||||
var urlPart = parts.FirstOrDefault(p => p.StartsWith("http"));
|
||||
if(urlPart != null)
|
||||
{
|
||||
uri = urlPart.Trim();
|
||||
}
|
||||
return string.Concat("http://", uri);
|
||||
}
|
||||
}
|
||||
if (Uri.TryCreate(uri, UriKind.Absolute, out var uri3))
|
||||
{
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
@@ -179,11 +215,11 @@ namespace Bit.Droid.Accessibility
|
||||
/// </summary>
|
||||
public static bool NeedToAutofill(Credentials credentials, string currentUriString)
|
||||
{
|
||||
if(credentials == null)
|
||||
if (credentials == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(Uri.TryCreate(credentials.LastUri, UriKind.Absolute, out Uri lastUri) &&
|
||||
if (Uri.TryCreate(credentials.LastUri, UriKind.Absolute, out Uri lastUri) &&
|
||||
Uri.TryCreate(currentUriString, UriKind.Absolute, out Uri currentUri))
|
||||
{
|
||||
return lastUri.Host == currentUri.Host;
|
||||
@@ -200,7 +236,7 @@ namespace Bit.Droid.Accessibility
|
||||
IEnumerable<AccessibilityNodeInfo> passwordNodes)
|
||||
{
|
||||
FillEditText(usernameNode, LastCredentials?.Username);
|
||||
foreach(var n in passwordNodes)
|
||||
foreach (var n in passwordNodes)
|
||||
{
|
||||
FillEditText(n, LastCredentials?.Password);
|
||||
}
|
||||
@@ -208,7 +244,7 @@ namespace Bit.Droid.Accessibility
|
||||
|
||||
public static void FillEditText(AccessibilityNodeInfo editTextNode, string value)
|
||||
{
|
||||
if(editTextNode == null || value == null)
|
||||
if (editTextNode == null || value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -221,35 +257,35 @@ namespace Bit.Droid.Accessibility
|
||||
Func<AccessibilityNodeInfo, bool> condition, bool disposeIfUnused, NodeList nodes = null,
|
||||
int recursionDepth = 0)
|
||||
{
|
||||
if(nodes == null)
|
||||
if (nodes == null)
|
||||
{
|
||||
nodes = new NodeList();
|
||||
}
|
||||
var dispose = disposeIfUnused;
|
||||
if(n != null && recursionDepth < 100)
|
||||
if (n != null && recursionDepth < 100)
|
||||
{
|
||||
var add = n.WindowId == e.WindowId &&
|
||||
!(n.ViewIdResourceName?.StartsWith(SystemUiPackage) ?? false) &&
|
||||
condition(n);
|
||||
if(add)
|
||||
if (add)
|
||||
{
|
||||
dispose = false;
|
||||
nodes.Add(n);
|
||||
}
|
||||
|
||||
for(var i = 0; i < n.ChildCount; i++)
|
||||
for (var i = 0; i < n.ChildCount; i++)
|
||||
{
|
||||
var childNode = n.GetChild(i);
|
||||
if(childNode == null)
|
||||
if (childNode == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if(i > 100)
|
||||
else if (i > 100)
|
||||
{
|
||||
Android.Util.Log.Info(BitwardenTag, "Too many child iterations.");
|
||||
break;
|
||||
}
|
||||
else if(childNode.GetHashCode() == n.GetHashCode())
|
||||
else if (childNode.GetHashCode() == n.GetHashCode())
|
||||
{
|
||||
Android.Util.Log.Info(BitwardenTag, "Child node is the same as parent for some reason.");
|
||||
}
|
||||
@@ -259,7 +295,7 @@ namespace Bit.Droid.Accessibility
|
||||
}
|
||||
}
|
||||
}
|
||||
if(dispose)
|
||||
if (dispose)
|
||||
{
|
||||
n?.Recycle();
|
||||
n?.Dispose();
|
||||
@@ -267,23 +303,56 @@ namespace Bit.Droid.Accessibility
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public static void GetNodesAndFill(AccessibilityNodeInfo root, AccessibilityEvent e,
|
||||
IEnumerable<AccessibilityNodeInfo> passwordNodes)
|
||||
public static AccessibilityNodeInfo GetUsernameEditText(string uriString,
|
||||
IEnumerable<AccessibilityNodeInfo> allEditTexts)
|
||||
{
|
||||
var allEditTexts = GetWindowNodes(root, e, n => EditText(n), false);
|
||||
var usernameEditText = GetUsernameEditTextIfPasswordExists(allEditTexts);
|
||||
FillCredentials(usernameEditText, passwordNodes);
|
||||
allEditTexts.Dispose();
|
||||
usernameEditText = null;
|
||||
}
|
||||
string uriKey = null;
|
||||
string uriLocalPath = null;
|
||||
if (Uri.TryCreate(uriString, UriKind.Absolute, out var uri))
|
||||
{
|
||||
uriKey = uri.Authority;
|
||||
uriLocalPath = uri.LocalPath;
|
||||
}
|
||||
|
||||
public static AccessibilityNodeInfo GetUsernameEditTextIfPasswordExists(
|
||||
if (!string.IsNullOrEmpty(uriKey))
|
||||
{
|
||||
// Uncomment this to log values necessary for username field discovery
|
||||
// foreach (var editText in allEditTexts)
|
||||
// {
|
||||
// System.Diagnostics.Debug.WriteLine(">>> uriKey: {0}, uriLocalPath: {1}, viewId: {2}", uriKey,
|
||||
// uriLocalPath, editText.ViewIdResourceName);
|
||||
// }
|
||||
|
||||
if (KnownUsernameFields.ContainsKey(uriKey))
|
||||
{
|
||||
var usernameField = KnownUsernameFields[uriKey];
|
||||
if (uriLocalPath.EndsWith(usernameField.UriPathEnd))
|
||||
{
|
||||
foreach (var editText in allEditTexts)
|
||||
{
|
||||
foreach (var usernameViewId in usernameField.UsernameViewId.Split(","))
|
||||
{
|
||||
if (usernameViewId == editText.ViewIdResourceName)
|
||||
{
|
||||
return editText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no match found, attempt to establish username field based on password field
|
||||
return GetUsernameEditTextIfPasswordExists(allEditTexts);
|
||||
}
|
||||
|
||||
private static AccessibilityNodeInfo GetUsernameEditTextIfPasswordExists(
|
||||
IEnumerable<AccessibilityNodeInfo> allEditTexts)
|
||||
{
|
||||
AccessibilityNodeInfo previousEditText = null;
|
||||
foreach(var editText in allEditTexts)
|
||||
foreach (var editText in allEditTexts)
|
||||
{
|
||||
if(editText.Password)
|
||||
if (editText.Password)
|
||||
{
|
||||
return previousEditText;
|
||||
}
|
||||
@@ -295,10 +364,11 @@ namespace Bit.Droid.Accessibility
|
||||
public static bool IsUsernameEditText(AccessibilityNodeInfo root, AccessibilityEvent e)
|
||||
{
|
||||
var allEditTexts = GetWindowNodes(root, e, n => EditText(n), false);
|
||||
var usernameEditText = GetUsernameEditTextIfPasswordExists(allEditTexts);
|
||||
var uriString = GetUri(root);
|
||||
var usernameEditText = GetUsernameEditText(uriString, allEditTexts);
|
||||
|
||||
var isUsernameEditText = false;
|
||||
if(usernameEditText != null)
|
||||
if (usernameEditText != null)
|
||||
{
|
||||
isUsernameEditText = IsSameNode(usernameEditText, e.Source);
|
||||
}
|
||||
@@ -309,7 +379,7 @@ namespace Bit.Droid.Accessibility
|
||||
|
||||
public static bool IsSameNode(AccessibilityNodeInfo node1, AccessibilityNodeInfo node2)
|
||||
{
|
||||
if(node1 != null && node2 != null)
|
||||
if (node1 != null && node2 != null)
|
||||
{
|
||||
return node1.Equals(node2) || node1.GetHashCode() == node2.GetHashCode();
|
||||
}
|
||||
@@ -318,15 +388,42 @@ namespace Bit.Droid.Accessibility
|
||||
|
||||
public static bool OverlayPermitted()
|
||||
{
|
||||
if(Build.VERSION.SdkInt >= BuildVersionCodes.M)
|
||||
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
|
||||
{
|
||||
return Settings.CanDrawOverlays(Android.App.Application.Context);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO do older android versions require a check?
|
||||
return true;
|
||||
if (Settings.CanDrawOverlays(Application.Context))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var appOpsMgr = (AppOpsManager)Application.Context.GetSystemService(Context.AppOpsService);
|
||||
var mode = appOpsMgr.CheckOpNoThrow("android:system_alert_window", Process.MyUid(),
|
||||
Application.Context.PackageName);
|
||||
if (mode == AppOpsManagerMode.Allowed || mode == AppOpsManagerMode.Ignored)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var wm = Application.Context.GetSystemService(Context.WindowService)
|
||||
.JavaCast<IWindowManager>();
|
||||
if (wm == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var testView = new View(Application.Context);
|
||||
var layoutParams = GetOverlayLayoutParams();
|
||||
wm.AddView(testView, layoutParams);
|
||||
wm.RemoveView(testView);
|
||||
return true;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// older android versions are always true
|
||||
return true;
|
||||
}
|
||||
|
||||
public static LinearLayout GetOverlayView(Context context)
|
||||
@@ -345,7 +442,7 @@ namespace Bit.Droid.Accessibility
|
||||
public static WindowManagerLayoutParams GetOverlayLayoutParams()
|
||||
{
|
||||
WindowManagerTypes windowManagerType;
|
||||
if(Build.VERSION.SdkInt >= BuildVersionCodes.O)
|
||||
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
|
||||
{
|
||||
windowManagerType = WindowManagerTypes.ApplicationOverlay;
|
||||
}
|
||||
@@ -365,8 +462,8 @@ namespace Bit.Droid.Accessibility
|
||||
return layoutParams;
|
||||
}
|
||||
|
||||
public static Point GetOverlayAnchorPosition(AccessibilityNodeInfo anchorView, int overlayViewHeight,
|
||||
bool isOverlayAboveAnchor)
|
||||
public static Point GetOverlayAnchorPosition(AccessibilityService service, AccessibilityNodeInfo anchorView,
|
||||
int overlayViewHeight, bool isOverlayAboveAnchor)
|
||||
{
|
||||
var anchorViewRect = new Rect();
|
||||
anchorView.GetBoundsInScreen(anchorViewRect);
|
||||
@@ -374,58 +471,57 @@ namespace Bit.Droid.Accessibility
|
||||
var anchorViewY = isOverlayAboveAnchor ? anchorViewRect.Top : anchorViewRect.Bottom;
|
||||
anchorViewRect.Dispose();
|
||||
|
||||
if(isOverlayAboveAnchor)
|
||||
if (isOverlayAboveAnchor)
|
||||
{
|
||||
anchorViewY -= overlayViewHeight;
|
||||
}
|
||||
anchorViewY -= GetStatusBarHeight();
|
||||
anchorViewY -= GetStatusBarHeight(service);
|
||||
|
||||
return new Point(anchorViewX, anchorViewY);
|
||||
}
|
||||
|
||||
public static Point GetOverlayAnchorPosition(AccessibilityNodeInfo anchorNode, AccessibilityNodeInfo root,
|
||||
IEnumerable<AccessibilityWindowInfo> windows, int overlayViewHeight, bool isOverlayAboveAnchor)
|
||||
public static Point GetOverlayAnchorPosition(AccessibilityService service, AccessibilityNodeInfo anchorNode,
|
||||
AccessibilityNodeInfo root, IEnumerable<AccessibilityWindowInfo> windows, int overlayViewHeight,
|
||||
bool isOverlayAboveAnchor)
|
||||
{
|
||||
Point point = null;
|
||||
if(anchorNode != null)
|
||||
if (anchorNode != null)
|
||||
{
|
||||
// Update node's info since this is still a reference from an older event
|
||||
anchorNode.Refresh();
|
||||
if(!anchorNode.VisibleToUser)
|
||||
if (!anchorNode.VisibleToUser)
|
||||
{
|
||||
return new Point(-1, -1);
|
||||
}
|
||||
if(!anchorNode.Focused)
|
||||
if (!anchorNode.Focused)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// node.VisibleToUser doesn't always give us exactly what we want, so attempt to tighten up the range
|
||||
// of visibility
|
||||
var minY = 0;
|
||||
int maxY;
|
||||
if(windows != null)
|
||||
var inputMethodHeight = 0;
|
||||
if (windows != null)
|
||||
{
|
||||
if(IsStatusBarExpanded(windows))
|
||||
if (IsStatusBarExpanded(windows))
|
||||
{
|
||||
return new Point(-1, -1);
|
||||
}
|
||||
maxY = GetApplicationVisibleHeight(windows);
|
||||
inputMethodHeight = GetInputMethodHeight(windows);
|
||||
}
|
||||
else
|
||||
var minY = 0;
|
||||
var rootNodeHeight = GetNodeHeight(root);
|
||||
if (rootNodeHeight == -1)
|
||||
{
|
||||
var rootNodeHeight = GetNodeHeight(root);
|
||||
if(rootNodeHeight == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
maxY = rootNodeHeight - GetNavigationBarHeight();
|
||||
return null;
|
||||
}
|
||||
var maxY = rootNodeHeight - GetNavigationBarHeight(service) - GetStatusBarHeight(service) -
|
||||
inputMethodHeight;
|
||||
|
||||
point = GetOverlayAnchorPosition(anchorNode, overlayViewHeight, isOverlayAboveAnchor);
|
||||
if(point.Y < minY)
|
||||
point = GetOverlayAnchorPosition(service, anchorNode, overlayViewHeight, isOverlayAboveAnchor);
|
||||
if (point.Y < minY)
|
||||
{
|
||||
if(isOverlayAboveAnchor)
|
||||
if (isOverlayAboveAnchor)
|
||||
{
|
||||
// view nearing bounds, anchor to bottom
|
||||
point.X = -1;
|
||||
@@ -438,9 +534,9 @@ namespace Bit.Droid.Accessibility
|
||||
point.Y = -1;
|
||||
}
|
||||
}
|
||||
else if(point.Y > maxY)
|
||||
else if (point.Y > (maxY - overlayViewHeight))
|
||||
{
|
||||
if(isOverlayAboveAnchor)
|
||||
if (isOverlayAboveAnchor)
|
||||
{
|
||||
// view out of bounds, hide overlay
|
||||
point.X = -1;
|
||||
@@ -453,7 +549,7 @@ namespace Bit.Droid.Accessibility
|
||||
point.Y = -1;
|
||||
}
|
||||
}
|
||||
else if(isOverlayAboveAnchor && point.Y < (maxY - overlayViewHeight - GetNodeHeight(anchorNode)))
|
||||
else if (isOverlayAboveAnchor && point.Y < (maxY - (overlayViewHeight * 2) - GetNodeHeight(anchorNode)))
|
||||
{
|
||||
// This else block forces the overlay to return to bottom alignment as soon as space is available
|
||||
// below the anchor view. Removing this will change the behavior to wait until there isn't enough
|
||||
@@ -467,12 +563,12 @@ namespace Bit.Droid.Accessibility
|
||||
|
||||
public static bool IsStatusBarExpanded(IEnumerable<AccessibilityWindowInfo> windows)
|
||||
{
|
||||
if(windows != null && windows.Any())
|
||||
if (windows != null && windows.Any())
|
||||
{
|
||||
var isSystemWindowsOnly = true;
|
||||
foreach(var window in windows)
|
||||
foreach (var window in windows)
|
||||
{
|
||||
if(window.Type != AccessibilityWindowType.System)
|
||||
if (window.Type != AccessibilityWindowType.System)
|
||||
{
|
||||
isSystemWindowsOnly = false;
|
||||
break;
|
||||
@@ -483,32 +579,33 @@ namespace Bit.Droid.Accessibility
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int GetApplicationVisibleHeight(IEnumerable<AccessibilityWindowInfo> windows)
|
||||
public static int GetInputMethodHeight(IEnumerable<AccessibilityWindowInfo> windows)
|
||||
{
|
||||
var appWindowHeight = 0;
|
||||
var nonAppWindowHeight = 0;
|
||||
if(windows != null)
|
||||
var inputMethodWindowHeight = 0;
|
||||
if (windows != null)
|
||||
{
|
||||
foreach(var window in windows)
|
||||
foreach (var window in windows)
|
||||
{
|
||||
var windowRect = new Rect();
|
||||
window.GetBoundsInScreen(windowRect);
|
||||
if(window.Type == AccessibilityWindowType.Application)
|
||||
if (window.Type == AccessibilityWindowType.InputMethod)
|
||||
{
|
||||
appWindowHeight += windowRect.Height();
|
||||
}
|
||||
else
|
||||
{
|
||||
nonAppWindowHeight += windowRect.Height();
|
||||
var windowRect = new Rect();
|
||||
window.GetBoundsInScreen(windowRect);
|
||||
inputMethodWindowHeight = windowRect.Height();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return appWindowHeight - nonAppWindowHeight;
|
||||
return inputMethodWindowHeight;
|
||||
}
|
||||
|
||||
public static bool IsAutofillServicePromptVisible(IEnumerable<AccessibilityWindowInfo> windows)
|
||||
{
|
||||
return windows?.Any(w => w.Title?.ToLower().Contains("autofill") ?? false) ?? false;
|
||||
}
|
||||
|
||||
public static int GetNodeHeight(AccessibilityNodeInfo node)
|
||||
{
|
||||
if(node == null)
|
||||
if (node == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@@ -519,26 +616,24 @@ namespace Bit.Droid.Accessibility
|
||||
return nodeRectHeight;
|
||||
}
|
||||
|
||||
private static int GetStatusBarHeight()
|
||||
private static int GetStatusBarHeight(AccessibilityService service)
|
||||
{
|
||||
return GetSystemResourceDimenPx("status_bar_height");
|
||||
return GetSystemResourceDimenPx(service, "status_bar_height");
|
||||
}
|
||||
|
||||
private static int GetNavigationBarHeight()
|
||||
private static int GetNavigationBarHeight(AccessibilityService service)
|
||||
{
|
||||
return GetSystemResourceDimenPx("navigation_bar_height");
|
||||
return GetSystemResourceDimenPx(service, "navigation_bar_height");
|
||||
}
|
||||
|
||||
private static int GetSystemResourceDimenPx(string resName)
|
||||
private static int GetSystemResourceDimenPx(AccessibilityService service, string resName)
|
||||
{
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
var barHeight = 0;
|
||||
var resourceId = activity.Resources.GetIdentifier(resName, "dimen", "android");
|
||||
if(resourceId > 0)
|
||||
var resourceId = service.Resources.GetIdentifier(resName, "dimen", "android");
|
||||
if (resourceId > 0)
|
||||
{
|
||||
barHeight = activity.Resources.GetDimensionPixelSize(resourceId);
|
||||
return service.Resources.GetDimensionPixelSize(resourceId);
|
||||
}
|
||||
return barHeight;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Graphics;
|
||||
using Android.OS;
|
||||
using Android.Provider;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Views.Accessibility;
|
||||
@@ -15,7 +13,6 @@ using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Java.Util;
|
||||
|
||||
namespace Bit.Droid.Accessibility
|
||||
{
|
||||
@@ -29,6 +26,7 @@ namespace Bit.Droid.Accessibility
|
||||
private const string BitwardenWebsite = "vault.bitwarden.com";
|
||||
|
||||
private IStorageService _storageService;
|
||||
private IBroadcasterService _broadcasterService;
|
||||
private DateTime? _lastSettingsReload = null;
|
||||
private TimeSpan _settingsReloadSpan = TimeSpan.FromMinutes(1);
|
||||
private HashSet<string> _blacklistedUris;
|
||||
@@ -48,23 +46,45 @@ namespace Bit.Droid.Accessibility
|
||||
private DateTime? _lastLauncherSetBuilt = null;
|
||||
private TimeSpan _rebuildLauncherSpan = TimeSpan.FromHours(1);
|
||||
|
||||
public override void OnCreate()
|
||||
{
|
||||
base.OnCreate();
|
||||
LoadServices();
|
||||
var settingsTask = LoadSettingsAsync();
|
||||
_broadcasterService.Subscribe(nameof(AccessibilityService), (message) =>
|
||||
{
|
||||
if (message.Command == "OnAutofillTileClick")
|
||||
{
|
||||
var runnable = new Java.Lang.Runnable(OnAutofillTileClick);
|
||||
_handler.PostDelayed(runnable, 250);
|
||||
}
|
||||
});
|
||||
AccessibilityHelpers.IsAccessibilityBroadcastReady = true;
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
AccessibilityHelpers.IsAccessibilityBroadcastReady = false;
|
||||
_broadcasterService.Unsubscribe(nameof(AccessibilityService));
|
||||
}
|
||||
|
||||
public override void OnAccessibilityEvent(AccessibilityEvent e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var powerManager = GetSystemService(PowerService) as PowerManager;
|
||||
if(Build.VERSION.SdkInt > BuildVersionCodes.KitkatWatch && !powerManager.IsInteractive)
|
||||
if (Build.VERSION.SdkInt > BuildVersionCodes.KitkatWatch && !powerManager.IsInteractive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if(Build.VERSION.SdkInt < BuildVersionCodes.Lollipop && !powerManager.IsScreenOn)
|
||||
else if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop && !powerManager.IsScreenOn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(SkipPackage(e?.PackageName))
|
||||
if (SkipPackage(e?.PackageName))
|
||||
{
|
||||
if(e?.PackageName != "com.android.systemui")
|
||||
if (e?.PackageName != "com.android.systemui")
|
||||
{
|
||||
CancelOverlayPrompt();
|
||||
}
|
||||
@@ -77,28 +97,28 @@ namespace Bit.Droid.Accessibility
|
||||
var settingsTask = LoadSettingsAsync();
|
||||
AccessibilityNodeInfo root = null;
|
||||
|
||||
switch(e.EventType)
|
||||
switch (e.EventType)
|
||||
{
|
||||
case EventTypes.ViewFocused:
|
||||
case EventTypes.ViewClicked:
|
||||
if(e.Source == null || e.PackageName == BitwardenPackage)
|
||||
if (e.Source == null || e.PackageName == BitwardenPackage)
|
||||
{
|
||||
CancelOverlayPrompt();
|
||||
break;
|
||||
}
|
||||
|
||||
root = RootInActiveWindow;
|
||||
if(root == null || root.PackageName != e.PackageName)
|
||||
if (root == null || root.PackageName != e.PackageName)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if(!(e.Source?.Password ?? false) && !AccessibilityHelpers.IsUsernameEditText(root, e))
|
||||
if (!(e.Source?.Password ?? false) && !AccessibilityHelpers.IsUsernameEditText(root, e))
|
||||
{
|
||||
CancelOverlayPrompt();
|
||||
break;
|
||||
}
|
||||
if(ScanAndAutofill(root, e))
|
||||
if (ScanAndAutofill(root, e))
|
||||
{
|
||||
CancelOverlayPrompt();
|
||||
}
|
||||
@@ -109,22 +129,22 @@ namespace Bit.Droid.Accessibility
|
||||
break;
|
||||
case EventTypes.WindowContentChanged:
|
||||
case EventTypes.WindowStateChanged:
|
||||
if(AccessibilityHelpers.LastCredentials == null)
|
||||
if (AccessibilityHelpers.LastCredentials == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(e.PackageName == BitwardenPackage)
|
||||
if (e.PackageName == BitwardenPackage)
|
||||
{
|
||||
CancelOverlayPrompt();
|
||||
break;
|
||||
}
|
||||
|
||||
root = RootInActiveWindow;
|
||||
if(root == null || root.PackageName != e.PackageName)
|
||||
if (root == null || root.PackageName != e.PackageName)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(ScanAndAutofill(root, e))
|
||||
if (ScanAndAutofill(root, e))
|
||||
{
|
||||
CancelOverlayPrompt();
|
||||
}
|
||||
@@ -134,7 +154,7 @@ namespace Bit.Droid.Accessibility
|
||||
}
|
||||
}
|
||||
// Suppress exceptions so that service doesn't crash.
|
||||
catch(Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", ex.GetType(), ex.StackTrace);
|
||||
}
|
||||
@@ -148,22 +168,24 @@ namespace Bit.Droid.Accessibility
|
||||
public bool ScanAndAutofill(AccessibilityNodeInfo root, AccessibilityEvent e)
|
||||
{
|
||||
var filled = false;
|
||||
var passwordNodes = AccessibilityHelpers.GetWindowNodes(root, e, n => n.Password, false);
|
||||
if(passwordNodes.Count > 0)
|
||||
var uri = AccessibilityHelpers.GetUri(root);
|
||||
if (uri != null && !uri.Contains(BitwardenWebsite) &&
|
||||
AccessibilityHelpers.NeedToAutofill(AccessibilityHelpers.LastCredentials, uri))
|
||||
{
|
||||
var uri = AccessibilityHelpers.GetUri(root);
|
||||
if(uri != null && !uri.Contains(BitwardenWebsite))
|
||||
var allEditTexts = AccessibilityHelpers.GetWindowNodes(root, e, n => AccessibilityHelpers.EditText(n), false);
|
||||
var usernameEditText = AccessibilityHelpers.GetUsernameEditText(uri, allEditTexts);
|
||||
var passwordNodes = AccessibilityHelpers.GetWindowNodes(root, e, n => n.Password, false);
|
||||
if (usernameEditText != null || passwordNodes.Count > 0)
|
||||
{
|
||||
if(AccessibilityHelpers.NeedToAutofill(AccessibilityHelpers.LastCredentials, uri))
|
||||
{
|
||||
AccessibilityHelpers.GetNodesAndFill(root, e, passwordNodes);
|
||||
filled = true;
|
||||
_lastAutoFillTime = Java.Lang.JavaSystem.CurrentTimeMillis();
|
||||
}
|
||||
AccessibilityHelpers.FillCredentials(usernameEditText, passwordNodes);
|
||||
filled = true;
|
||||
_lastAutoFillTime = Java.Lang.JavaSystem.CurrentTimeMillis();
|
||||
AccessibilityHelpers.LastCredentials = null;
|
||||
}
|
||||
AccessibilityHelpers.LastCredentials = null;
|
||||
allEditTexts.Dispose();
|
||||
passwordNodes.Dispose();
|
||||
}
|
||||
else if(AccessibilityHelpers.LastCredentials != null)
|
||||
if (AccessibilityHelpers.LastCredentials != null)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
@@ -171,18 +193,44 @@ namespace Bit.Droid.Accessibility
|
||||
AccessibilityHelpers.LastCredentials = null;
|
||||
});
|
||||
}
|
||||
passwordNodes.Dispose();
|
||||
return filled;
|
||||
}
|
||||
|
||||
private void OnAutofillTileClick()
|
||||
{
|
||||
CancelOverlayPrompt();
|
||||
|
||||
var root = RootInActiveWindow;
|
||||
if (root != null && root.PackageName != BitwardenPackage &&
|
||||
root.PackageName != AccessibilityHelpers.SystemUiPackage &&
|
||||
!SkipPackage(root.PackageName))
|
||||
{
|
||||
var uri = AccessibilityHelpers.GetUri(root);
|
||||
if (!string.IsNullOrWhiteSpace(uri))
|
||||
{
|
||||
var intent = new Intent(this, typeof(AccessibilityActivity));
|
||||
intent.PutExtra("uri", uri);
|
||||
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
|
||||
StartActivity(intent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Toast.MakeText(this, AppResources.AutofillTileUriNotFound, ToastLength.Long).Show();
|
||||
}
|
||||
|
||||
private void CancelOverlayPrompt()
|
||||
{
|
||||
_overlayAnchorObserverRunning = false;
|
||||
|
||||
if(_windowManager != null && _overlayView != null)
|
||||
if (_windowManager != null && _overlayView != null)
|
||||
{
|
||||
_windowManager.RemoveViewImmediate(_overlayView);
|
||||
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Removed");
|
||||
try
|
||||
{
|
||||
_windowManager.RemoveViewImmediate(_overlayView);
|
||||
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Removed");
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
_overlayView = null;
|
||||
@@ -190,7 +238,7 @@ namespace Bit.Droid.Accessibility
|
||||
_lastAnchorY = 0;
|
||||
_isOverlayAboveAnchor = false;
|
||||
|
||||
if(_anchorNode != null)
|
||||
if (_anchorNode != null)
|
||||
{
|
||||
_anchorNode.Recycle();
|
||||
_anchorNode = null;
|
||||
@@ -199,30 +247,37 @@ namespace Bit.Droid.Accessibility
|
||||
|
||||
private void OverlayPromptToAutofill(AccessibilityNodeInfo root, AccessibilityEvent e)
|
||||
{
|
||||
if(!AccessibilityHelpers.OverlayPermitted())
|
||||
if (Java.Lang.JavaSystem.CurrentTimeMillis() - _lastAutoFillTime < 1000 ||
|
||||
AccessibilityHelpers.IsAutofillServicePromptVisible(Windows))
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(">>> Overlay Permission not granted");
|
||||
Toast.MakeText(this, AppResources.AccessibilityOverlayPermissionAlert, ToastLength.Long).Show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AccessibilityHelpers.OverlayPermitted())
|
||||
{
|
||||
if (!AccessibilityHelpers.IsAutofillTileAdded)
|
||||
{
|
||||
// The user has the option of only using the autofill tile and leaving the overlay permission
|
||||
// disabled, so only show this toast if they're using accessibility without overlay permission and
|
||||
// have _not_ added the autofill tile
|
||||
System.Diagnostics.Debug.WriteLine(">>> Overlay Permission not granted");
|
||||
Toast.MakeText(this, AppResources.AccessibilityOverlayPermissionAlert, ToastLength.Long).Show();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(_overlayView != null || _anchorNode != null || _overlayAnchorObserverRunning)
|
||||
if (_overlayView != null || _anchorNode != null || _overlayAnchorObserverRunning)
|
||||
{
|
||||
CancelOverlayPrompt();
|
||||
}
|
||||
|
||||
if(Java.Lang.JavaSystem.CurrentTimeMillis() - _lastAutoFillTime < 1000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var uri = AccessibilityHelpers.GetUri(root);
|
||||
var fillable = !string.IsNullOrWhiteSpace(uri);
|
||||
if(fillable)
|
||||
if (fillable)
|
||||
{
|
||||
if(_blacklistedUris != null && _blacklistedUris.Any())
|
||||
if (_blacklistedUris != null && _blacklistedUris.Any())
|
||||
{
|
||||
if(Uri.TryCreate(uri, UriKind.Absolute, out var parsedUri) && parsedUri.Scheme.StartsWith("http"))
|
||||
if (Uri.TryCreate(uri, UriKind.Absolute, out var parsedUri) && parsedUri.Scheme.StartsWith("http"))
|
||||
{
|
||||
fillable = !_blacklistedUris.Contains(
|
||||
string.Format("{0}://{1}", parsedUri.Scheme, parsedUri.Host));
|
||||
@@ -233,7 +288,7 @@ namespace Bit.Droid.Accessibility
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!fillable)
|
||||
if (!fillable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -253,12 +308,12 @@ namespace Bit.Droid.Accessibility
|
||||
};
|
||||
|
||||
var layoutParams = AccessibilityHelpers.GetOverlayLayoutParams();
|
||||
var anchorPosition = AccessibilityHelpers.GetOverlayAnchorPosition(e.Source, _overlayViewHeight,
|
||||
_isOverlayAboveAnchor);
|
||||
var anchorPosition = AccessibilityHelpers.GetOverlayAnchorPosition(this, e.Source,
|
||||
_overlayViewHeight, _isOverlayAboveAnchor);
|
||||
layoutParams.X = anchorPosition.X;
|
||||
layoutParams.Y = anchorPosition.Y;
|
||||
|
||||
if(_windowManager == null)
|
||||
if (_windowManager == null)
|
||||
{
|
||||
_windowManager = GetSystemService(WindowService).JavaCast<IWindowManager>();
|
||||
}
|
||||
@@ -277,7 +332,7 @@ namespace Bit.Droid.Accessibility
|
||||
|
||||
private void StartOverlayAnchorObserver()
|
||||
{
|
||||
if(_overlayAnchorObserverRunning)
|
||||
if (_overlayAnchorObserverRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -285,7 +340,7 @@ namespace Bit.Droid.Accessibility
|
||||
_overlayAnchorObserverRunning = true;
|
||||
_overlayAnchorObserverRunnable = new Java.Lang.Runnable(() =>
|
||||
{
|
||||
if(_overlayAnchorObserverRunning)
|
||||
if (_overlayAnchorObserverRunning)
|
||||
{
|
||||
AdjustOverlayForScroll();
|
||||
_handler.PostDelayed(_overlayAnchorObserverRunnable, 250);
|
||||
@@ -297,7 +352,8 @@ namespace Bit.Droid.Accessibility
|
||||
|
||||
private void AdjustOverlayForScroll()
|
||||
{
|
||||
if(_overlayView == null || _anchorNode == null)
|
||||
if (_overlayView == null || _anchorNode == null ||
|
||||
AccessibilityHelpers.IsAutofillServicePromptVisible(Windows))
|
||||
{
|
||||
CancelOverlayPrompt();
|
||||
return;
|
||||
@@ -305,42 +361,42 @@ namespace Bit.Droid.Accessibility
|
||||
|
||||
var root = RootInActiveWindow;
|
||||
IEnumerable<AccessibilityWindowInfo> windows = null;
|
||||
if(Build.VERSION.SdkInt > BuildVersionCodes.Kitkat)
|
||||
if (Build.VERSION.SdkInt > BuildVersionCodes.Kitkat)
|
||||
{
|
||||
windows = Windows;
|
||||
}
|
||||
|
||||
var anchorPosition = AccessibilityHelpers.GetOverlayAnchorPosition(_anchorNode, root, windows,
|
||||
_overlayViewHeight, _isOverlayAboveAnchor);
|
||||
if(anchorPosition == null)
|
||||
var anchorPosition = AccessibilityHelpers.GetOverlayAnchorPosition(this, _anchorNode, root,
|
||||
windows, _overlayViewHeight, _isOverlayAboveAnchor);
|
||||
if (anchorPosition == null)
|
||||
{
|
||||
CancelOverlayPrompt();
|
||||
return;
|
||||
}
|
||||
else if(anchorPosition.X == -1 && anchorPosition.Y == -1)
|
||||
else if (anchorPosition.X == -1 && anchorPosition.Y == -1)
|
||||
{
|
||||
if(_overlayView.Visibility != ViewStates.Gone)
|
||||
if (_overlayView.Visibility != ViewStates.Gone)
|
||||
{
|
||||
_overlayView.Visibility = ViewStates.Gone;
|
||||
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Hidden");
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if(anchorPosition.X == -1)
|
||||
else if (anchorPosition.X == -1)
|
||||
{
|
||||
_isOverlayAboveAnchor = false;
|
||||
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Below Anchor");
|
||||
return;
|
||||
}
|
||||
else if(anchorPosition.Y == -1)
|
||||
else if (anchorPosition.Y == -1)
|
||||
{
|
||||
_isOverlayAboveAnchor = true;
|
||||
System.Diagnostics.Debug.WriteLine(">>> Accessibility Overlay View Above Anchor");
|
||||
return;
|
||||
}
|
||||
else if(anchorPosition.X == _lastAnchorX && anchorPosition.Y == _lastAnchorY)
|
||||
else if (anchorPosition.X == _lastAnchorX && anchorPosition.Y == _lastAnchorY)
|
||||
{
|
||||
if(_overlayView.Visibility != ViewStates.Visible)
|
||||
if (_overlayView.Visibility != ViewStates.Visible)
|
||||
{
|
||||
_overlayView.Visibility = ViewStates.Visible;
|
||||
}
|
||||
@@ -356,7 +412,7 @@ namespace Bit.Droid.Accessibility
|
||||
|
||||
_windowManager.UpdateViewLayout(_overlayView, layoutParams);
|
||||
|
||||
if(_overlayView.Visibility != ViewStates.Visible)
|
||||
if (_overlayView.Visibility != ViewStates.Visible)
|
||||
{
|
||||
_overlayView.Visibility = ViewStates.Visible;
|
||||
}
|
||||
@@ -367,13 +423,13 @@ namespace Bit.Droid.Accessibility
|
||||
|
||||
private bool SkipPackage(string eventPackageName)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(eventPackageName) ||
|
||||
if (string.IsNullOrWhiteSpace(eventPackageName) ||
|
||||
AccessibilityHelpers.FilteredPackageNames.Contains(eventPackageName) ||
|
||||
eventPackageName.Contains("launcher"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if(_launcherPackageNames == null || _lastLauncherSetBuilt == null ||
|
||||
if (_launcherPackageNames == null || _lastLauncherSetBuilt == null ||
|
||||
(DateTime.Now - _lastLauncherSetBuilt.Value) > _rebuildLauncherSpan)
|
||||
{
|
||||
// refresh launcher list every now and then
|
||||
@@ -388,23 +444,29 @@ namespace Bit.Droid.Accessibility
|
||||
|
||||
private void LoadServices()
|
||||
{
|
||||
if(_storageService == null)
|
||||
if (_storageService == null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
}
|
||||
if (_broadcasterService == null)
|
||||
{
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadSettingsAsync()
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
if(_lastSettingsReload == null || (now - _lastSettingsReload.Value) > _settingsReloadSpan)
|
||||
if (_lastSettingsReload == null || (now - _lastSettingsReload.Value) > _settingsReloadSpan)
|
||||
{
|
||||
_lastSettingsReload = now;
|
||||
var uris = await _storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
|
||||
if(uris != null)
|
||||
if (uris != null)
|
||||
{
|
||||
_blacklistedUris = new HashSet<string>(uris);
|
||||
}
|
||||
var isAutoFillTileAdded = await _storageService.GetAsync<bool?>(Constants.AutofillTileAdded);
|
||||
AccessibilityHelpers.IsAutofillTileAdded = isAutoFillTileAdded.GetValueOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
16
src/Android/Accessibility/KnownUsernameField.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace Bit.Droid.Accessibility
|
||||
{
|
||||
public class KnownUsernameField
|
||||
{
|
||||
public KnownUsernameField(string uriAuthority, string uriPathEnd, string usernameViewId)
|
||||
{
|
||||
UriAuthority = uriAuthority;
|
||||
UriPathEnd = uriPathEnd;
|
||||
UsernameViewId = usernameViewId;
|
||||
}
|
||||
|
||||
public string UriAuthority { get; set; }
|
||||
public string UriPathEnd { get; set; }
|
||||
public string UsernameViewId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace Bit.Droid.Accessibility
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
foreach(var item in this)
|
||||
foreach (var item in this)
|
||||
{
|
||||
item.Recycle();
|
||||
item.Dispose();
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
|
||||
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
|
||||
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
|
||||
<TargetFrameworkVersion>v10.0</TargetFrameworkVersion>
|
||||
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
|
||||
<NuGetPackageImportStamp>
|
||||
@@ -30,10 +29,8 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>3</WarningLevel>
|
||||
<AndroidLinkMode>None</AndroidLinkMode>
|
||||
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
|
||||
<AndroidSupportedAbis />
|
||||
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
||||
<AndroidLinkSkip>Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;BitwardenCore;Xamarin.Android.Net;Xamarin.Android.Net.OldAndroidSSLSocketFactory</AndroidLinkSkip>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
@@ -46,10 +43,8 @@
|
||||
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
|
||||
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
||||
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
|
||||
<AndroidLinkSkip>Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;BitwardenCore;Xamarin.Android.Net;Xamarin.Android.Net.OldAndroidSSLSocketFactory</AndroidLinkSkip>
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
<AndroidLinkMode>Full</AndroidLinkMode>
|
||||
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'FDroid|AnyCPU'">
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
@@ -63,11 +58,9 @@
|
||||
<DefineConstants>FDROID</DefineConstants>
|
||||
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
|
||||
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
||||
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
|
||||
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
<AndroidLinkSkip>Xamarin.GooglePlayServices.Base;Xamarin.GooglePlayServices.Basement;Xamarin.GooglePlayServices.Measurement;Xamarin.GooglePlayServices.Gcm;BitwardenAndroid;BitwardenApp;BitwardenCore;Xamarin.Android.Net;Xamarin.Android.Net.OldAndroidSSLSocketFactory</AndroidLinkSkip>
|
||||
<AndroidLinkMode>Full</AndroidLinkMode>
|
||||
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Mono.Android" />
|
||||
@@ -82,21 +75,22 @@
|
||||
<Version>2.1.0.4</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Portable.BouncyCastle">
|
||||
<Version>1.8.5.2</Version>
|
||||
<Version>1.8.6.7</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Essentials">
|
||||
<Version>1.3.1</Version>
|
||||
<Version>1.5.3.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Firebase.Messaging">
|
||||
<Version>60.1142.1</Version>
|
||||
<Version>71.1740.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Android.Support.Design" Version="28.0.0.3" />
|
||||
<PackageReference Include="Xamarin.Android.Support.v7.AppCompat" Version="28.0.0.3" />
|
||||
<PackageReference Include="Xamarin.Android.Support.v4" Version="28.0.0.3" />
|
||||
<PackageReference Include="Xamarin.Android.Support.v7.CardView" Version="28.0.0.3" />
|
||||
<PackageReference Include="Xamarin.Android.Support.v7.MediaRouter" Version="28.0.0.3" />
|
||||
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.0.0" />
|
||||
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.1.0" />
|
||||
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0" />
|
||||
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0" />
|
||||
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.1.0" />
|
||||
<PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.6" />
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
|
||||
<Version>60.1142.1</Version>
|
||||
<Version>71.1600.0</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -106,6 +100,7 @@
|
||||
<Compile Include="Accessibility\AccessibilityService.cs" />
|
||||
<Compile Include="Accessibility\Browser.cs" />
|
||||
<Compile Include="Accessibility\NodeList.cs" />
|
||||
<Compile Include="Accessibility\KnownUsernameField.cs" />
|
||||
<Compile Include="Autofill\AutofillHelpers.cs" />
|
||||
<Compile Include="Autofill\AutofillService.cs" />
|
||||
<Compile Include="Autofill\Field.cs" />
|
||||
@@ -113,6 +108,7 @@
|
||||
<Compile Include="Autofill\FilledItem.cs" />
|
||||
<Compile Include="Autofill\Parser.cs" />
|
||||
<Compile Include="Autofill\SavedItem.cs" />
|
||||
<Compile Include="Effects\FabShadowEffect.cs" />
|
||||
<Compile Include="Effects\FixedSizeEffect.cs" />
|
||||
<Compile Include="Effects\SelectableLabelEffect.cs" />
|
||||
<Compile Include="Effects\TabBarEffect.cs" />
|
||||
@@ -140,12 +136,11 @@
|
||||
<Compile Include="Services\CryptoPrimitiveService.cs" />
|
||||
<Compile Include="Services\DeviceActionService.cs" />
|
||||
<Compile Include="Services\LocalizeService.cs" />
|
||||
<Compile Include="Tiles\AutofillTileService.cs" />
|
||||
<Compile Include="Tiles\GeneratorTileService.cs" />
|
||||
<Compile Include="Tiles\MyVaultTileService.cs" />
|
||||
<Compile Include="Utilities\AndroidHelpers.cs" />
|
||||
<Compile Include="Utilities\CustomFingerprintDialogFragment.cs" />
|
||||
<Compile Include="Utilities\HockeyAppCrashManagerListener.cs" />
|
||||
<Compile Include="Utilities\StaticStore.cs" />
|
||||
<Compile Include="Utilities\AppCenterHelper.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidAsset Include="Assets\FontAwesome.ttf" />
|
||||
@@ -156,10 +151,49 @@
|
||||
<GoogleServicesJson Include="google-services.json" />
|
||||
<GoogleServicesJson Include="google-services.json.enc" />
|
||||
<None Include="Properties\AndroidManifest.xml" />
|
||||
<None Include="upload-keystore.jks.enc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-hdpi\accessibility_overlay.png" />
|
||||
<AndroidResource Include="Resources\drawable-hdpi\accessibility_permission.png" />
|
||||
<AndroidResource Include="Resources\drawable-hdpi\accessibility_step1.png" />
|
||||
<AndroidResource Include="Resources\drawable-hdpi\accessibility_step2.png" />
|
||||
<AndroidResource Include="Resources\drawable-hdpi\autofill_enable.png" />
|
||||
<AndroidResource Include="Resources\drawable-hdpi\autofill_use.png" />
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_overlay.png" />
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_permission.png" />
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_step1.png" />
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_step2.png" />
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\autofill_enable.png" />
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\autofill_use.png" />
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_overlay.png" />
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_permission.png" />
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_step1.png" />
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_step2.png" />
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\autofill_enable.png" />
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\autofill_use.png" />
|
||||
<AndroidResource Include="Resources\drawable\accessibility_overlay.png" />
|
||||
<AndroidResource Include="Resources\drawable\accessibility_permission.png" />
|
||||
<AndroidResource Include="Resources\drawable\accessibility_step1.png" />
|
||||
<AndroidResource Include="Resources\drawable\accessibility_step2.png" />
|
||||
<AndroidResource Include="Resources\drawable\autofill_enable.png" />
|
||||
<AndroidResource Include="Resources\drawable\autofill_use.png" />
|
||||
<AndroidResource Include="Resources\drawable\icon.xml" />
|
||||
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
|
||||
<AndroidResource Include="Resources\layout\Tabbar.axml" />
|
||||
<AndroidResource Include="Resources\layout\Toolbar.axml" />
|
||||
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
|
||||
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher_round.xml" />
|
||||
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher.png" />
|
||||
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_round.png" />
|
||||
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher.png" />
|
||||
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_round.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_round.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_round.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_round.png" />
|
||||
<AndroidResource Include="Resources\values\styles.xml" />
|
||||
<AndroidResource Include="Resources\values\colors.xml" />
|
||||
</ItemGroup>
|
||||
@@ -194,57 +228,6 @@
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\values\ic_launcher_background.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_foreground.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_round.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher_round.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_foreground.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_round.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_foreground.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_round.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_foreground.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_round.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_foreground.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_round.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\layout\autofill_listitem.xml" />
|
||||
</ItemGroup>
|
||||
@@ -287,69 +270,15 @@
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\shield.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\autofill_use.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\accessibility_notification.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\accessibility_notification_icon.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\accessibility_step1.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\accessibility_step2.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\autofill_enable.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-hdpi\autofill_use.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-hdpi\accessibility_step1.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-hdpi\accessibility_step2.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-hdpi\autofill_enable.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\yubikey.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_step1.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_step2.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\autofill_enable.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\autofill_use.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\autofill_use.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\logo.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\yubikey.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_step1.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_step2.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\autofill_enable.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-hdpi\lock.png" />
|
||||
</ItemGroup>
|
||||
@@ -404,21 +333,6 @@
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xxxhdpi\id.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\icon.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-hdpi\icon.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\icon.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\icon.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xxxhdpi\icon.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\pencil.png" />
|
||||
</ItemGroup>
|
||||
@@ -536,24 +450,6 @@
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-hdpi\accessibility_overlay.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_overlay.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_overlay.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-hdpi\accessibility_permission.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_permission.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_permission.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\xml\app_restrictions.xml">
|
||||
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
||||
|
||||
@@ -16,52 +16,78 @@ namespace Bit.Droid.Autofill
|
||||
{
|
||||
private static int _pendingIntentId = 0;
|
||||
|
||||
// These browser work natively with the autofill framework
|
||||
// These browsers work natively with the Autofill Framework
|
||||
//
|
||||
// Be sure:
|
||||
// - to keep these entries sorted alphabetically and
|
||||
//
|
||||
// - ... to keep this list in sync with values in AccessibilityHelpers.SupportedBrowsers [Section A], too.
|
||||
public static HashSet<string> TrustedBrowsers = new HashSet<string>
|
||||
{
|
||||
"com.duckduckgo.mobile.android",
|
||||
"org.mozilla.focus",
|
||||
"org.mozilla.klar",
|
||||
"com.duckduckgo.mobile.android",
|
||||
};
|
||||
|
||||
// These browsers work using the compatibility shim for the autofill framework
|
||||
// These browsers work using the compatibility shim for the Autofill Framework
|
||||
//
|
||||
// Be sure:
|
||||
// - to keep these entries sorted alphabetically,
|
||||
// - to keep this list in sync with values in Resources/xml/autofillservice.xml, and
|
||||
//
|
||||
// - ... to keep this list in sync with values in AccessibilityHelpers.SupportedBrowsers [Section A], too.
|
||||
public static HashSet<string> CompatBrowsers = new HashSet<string>
|
||||
{
|
||||
"org.mozilla.firefox",
|
||||
"org.mozilla.firefox_beta",
|
||||
"com.microsoft.emmx",
|
||||
"com.android.chrome",
|
||||
"com.chrome.beta",
|
||||
"com.amazon.cloud9",
|
||||
"com.android.browser",
|
||||
"com.android.chrome",
|
||||
"com.avast.android.secure.browser",
|
||||
"com.avg.android.secure.browser",
|
||||
"com.brave.browser",
|
||||
"com.brave.browser_beta",
|
||||
"com.brave.browser_default",
|
||||
"com.brave.browser_dev",
|
||||
"com.brave.browser_nightly",
|
||||
"com.chrome.beta",
|
||||
"com.chrome.canary",
|
||||
"com.chrome.dev",
|
||||
"com.ecosia.android",
|
||||
"com.google.android.apps.chrome",
|
||||
"com.google.android.apps.chrome_dev",
|
||||
"com.kiwibrowser.browser",
|
||||
"com.microsoft.emmx",
|
||||
"com.naver.whale",
|
||||
"com.opera.browser",
|
||||
"com.opera.browser.beta",
|
||||
"com.opera.mini.native",
|
||||
"com.chrome.dev",
|
||||
"com.chrome.canary",
|
||||
"com.google.android.apps.chrome",
|
||||
"com.google.android.apps.chrome_dev",
|
||||
"com.yandex.browser",
|
||||
"com.opera.mini.native.beta",
|
||||
"com.opera.touch",
|
||||
"com.qwant.liberty",
|
||||
"com.sec.android.app.sbrowser",
|
||||
"com.sec.android.app.sbrowser.beta",
|
||||
"org.codeaurora.swe.browser",
|
||||
"com.amazon.cloud9",
|
||||
"com.stoutner.privacybrowser.free",
|
||||
"com.stoutner.privacybrowser.standard",
|
||||
"com.vivaldi.browser",
|
||||
"com.vivaldi.browser.snapshot",
|
||||
"com.vivaldi.browser.sopranos",
|
||||
"com.yandex.browser",
|
||||
"mark.via.gp",
|
||||
"org.adblockplus.browser",
|
||||
"org.adblockplus.browser.beta",
|
||||
"org.bromite.bromite",
|
||||
"org.chromium.chrome",
|
||||
"com.kiwibrowser.browser",
|
||||
"com.ecosia.android",
|
||||
"com.opera.mini.native.beta",
|
||||
"org.mozilla.fennec_aurora",
|
||||
"org.mozilla.fennec_fdroid",
|
||||
"com.qwant.liberty",
|
||||
"com.opera.touch",
|
||||
"org.codeaurora.swe.browser",
|
||||
"org.gnu.icecat",
|
||||
"org.mozilla.fenix",
|
||||
"org.mozilla.fenix.nightly",
|
||||
"org.mozilla.fennec_aurora",
|
||||
"org.mozilla.fennec_fdroid",
|
||||
"org.mozilla.firefox",
|
||||
"org.mozilla.firefox_beta",
|
||||
"org.mozilla.reference.browser",
|
||||
"org.mozilla.rocket",
|
||||
"org.torproject.torbrowser",
|
||||
"com.vivaldi.browser",
|
||||
"org.torproject.torbrowser_alpha",
|
||||
};
|
||||
|
||||
// The URLs are blacklisted from autofilling
|
||||
@@ -75,17 +101,17 @@ namespace Bit.Droid.Autofill
|
||||
|
||||
public static async Task<List<FilledItem>> GetFillItemsAsync(Parser parser, ICipherService cipherService)
|
||||
{
|
||||
if(parser.FieldCollection.FillableForLogin)
|
||||
if (parser.FieldCollection.FillableForLogin)
|
||||
{
|
||||
var ciphers = await cipherService.GetAllDecryptedByUrlAsync(parser.Uri);
|
||||
if(ciphers.Item1.Any() || ciphers.Item2.Any())
|
||||
if (ciphers.Item1.Any() || ciphers.Item2.Any())
|
||||
{
|
||||
var allCiphers = ciphers.Item1.ToList();
|
||||
allCiphers.AddRange(ciphers.Item2.ToList());
|
||||
return allCiphers.Select(c => new FilledItem(c)).ToList();
|
||||
}
|
||||
}
|
||||
else if(parser.FieldCollection.FillableForCard)
|
||||
else if (parser.FieldCollection.FillableForCard)
|
||||
{
|
||||
var ciphers = await cipherService.GetAllDecryptedAsync();
|
||||
return ciphers.Where(c => c.Type == CipherType.Card).Select(c => new FilledItem(c)).ToList();
|
||||
@@ -96,12 +122,12 @@ namespace Bit.Droid.Autofill
|
||||
public static FillResponse BuildFillResponse(Parser parser, List<FilledItem> items, bool locked)
|
||||
{
|
||||
var responseBuilder = new FillResponse.Builder();
|
||||
if(items != null && items.Count > 0)
|
||||
if (items != null && items.Count > 0)
|
||||
{
|
||||
foreach(var item in items)
|
||||
foreach (var item in items)
|
||||
{
|
||||
var dataset = BuildDataset(parser.ApplicationContext, parser.FieldCollection, item);
|
||||
if(dataset != null)
|
||||
if (dataset != null)
|
||||
{
|
||||
responseBuilder.AddDataset(dataset);
|
||||
}
|
||||
@@ -118,7 +144,7 @@ namespace Bit.Droid.Autofill
|
||||
{
|
||||
var datasetBuilder = new Dataset.Builder(
|
||||
BuildListView(filledItem.Name, filledItem.Subtitle, filledItem.Icon, context));
|
||||
if(filledItem.ApplyToFields(fields, datasetBuilder))
|
||||
if (filledItem.ApplyToFields(fields, datasetBuilder))
|
||||
{
|
||||
return datasetBuilder.Build();
|
||||
}
|
||||
@@ -129,15 +155,15 @@ namespace Bit.Droid.Autofill
|
||||
{
|
||||
var intent = new Intent(context, typeof(MainActivity));
|
||||
intent.PutExtra("autofillFramework", true);
|
||||
if(fields.FillableForLogin)
|
||||
if (fields.FillableForLogin)
|
||||
{
|
||||
intent.PutExtra("autofillFrameworkFillType", (int)CipherType.Login);
|
||||
}
|
||||
else if(fields.FillableForCard)
|
||||
else if (fields.FillableForCard)
|
||||
{
|
||||
intent.PutExtra("autofillFrameworkFillType", (int)CipherType.Card);
|
||||
}
|
||||
else if(fields.FillableForIdentity)
|
||||
else if (fields.FillableForIdentity)
|
||||
{
|
||||
intent.PutExtra("autofillFrameworkFillType", (int)CipherType.Identity);
|
||||
}
|
||||
@@ -159,7 +185,7 @@ namespace Bit.Droid.Autofill
|
||||
datasetBuilder.SetAuthentication(pendingIntent.IntentSender);
|
||||
|
||||
// Dataset must have a value set. We will reset this in the main activity when the real item is chosen.
|
||||
foreach(var autofillId in fields.AutofillIds)
|
||||
foreach (var autofillId in fields.AutofillIds)
|
||||
{
|
||||
datasetBuilder.SetValue(autofillId, AutofillValue.ForText("PLACEHOLDER"));
|
||||
}
|
||||
@@ -181,24 +207,24 @@ namespace Bit.Droid.Autofill
|
||||
// Docs state that password fields cannot be reliably saved in Compat mode since they will show as
|
||||
// masked values.
|
||||
var compatBrowser = CompatBrowsers.Contains(parser.PackageName);
|
||||
if(compatBrowser && fields.SaveType == SaveDataType.Password)
|
||||
if (compatBrowser && fields.SaveType == SaveDataType.Password)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var requiredIds = fields.GetRequiredSaveFields();
|
||||
if(fields.SaveType == SaveDataType.Generic || requiredIds.Length == 0)
|
||||
if (fields.SaveType == SaveDataType.Generic || requiredIds.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var saveBuilder = new SaveInfo.Builder(fields.SaveType, requiredIds);
|
||||
var optionalIds = fields.GetOptionalSaveIds();
|
||||
if(optionalIds.Length > 0)
|
||||
if (optionalIds.Length > 0)
|
||||
{
|
||||
saveBuilder.SetOptionalIds(optionalIds);
|
||||
}
|
||||
if(compatBrowser)
|
||||
if (compatBrowser)
|
||||
{
|
||||
saveBuilder.SetFlags(SaveFlags.SaveOnAllViewsInvisible);
|
||||
}
|
||||
|
||||
@@ -21,14 +21,14 @@ namespace Bit.Droid.Autofill
|
||||
public class AutofillService : Android.Service.Autofill.AutofillService
|
||||
{
|
||||
private ICipherService _cipherService;
|
||||
private ILockService _lockService;
|
||||
private IVaultTimeoutService _vaultTimeoutService;
|
||||
private IStorageService _storageService;
|
||||
|
||||
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
|
||||
FillCallback callback)
|
||||
{
|
||||
var structure = request.FillContexts?.LastOrDefault()?.Structure;
|
||||
if(structure == null)
|
||||
if (structure == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -36,27 +36,27 @@ namespace Bit.Droid.Autofill
|
||||
var parser = new Parser(structure, ApplicationContext);
|
||||
parser.Parse();
|
||||
|
||||
if(_storageService == null)
|
||||
if (_storageService == null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
}
|
||||
|
||||
var shouldAutofill = await parser.ShouldAutofillAsync(_storageService);
|
||||
if(!shouldAutofill)
|
||||
if (!shouldAutofill)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(_lockService == null)
|
||||
if (_vaultTimeoutService == null)
|
||||
{
|
||||
_lockService = ServiceContainer.Resolve<ILockService>("lockService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
}
|
||||
|
||||
List<FilledItem> items = null;
|
||||
var locked = await _lockService.IsLockedAsync();
|
||||
if(!locked)
|
||||
var locked = await _vaultTimeoutService.IsLockedAsync();
|
||||
if (!locked)
|
||||
{
|
||||
if(_cipherService == null)
|
||||
if (_cipherService == null)
|
||||
{
|
||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
}
|
||||
@@ -71,18 +71,18 @@ namespace Bit.Droid.Autofill
|
||||
public async override void OnSaveRequest(SaveRequest request, SaveCallback callback)
|
||||
{
|
||||
var structure = request.FillContexts?.LastOrDefault()?.Structure;
|
||||
if(structure == null)
|
||||
if (structure == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(_storageService == null)
|
||||
if (_storageService == null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
}
|
||||
|
||||
var disableSavePrompt = await _storageService.GetAsync<bool?>(Constants.AutofillDisableSavePromptKey);
|
||||
if(disableSavePrompt.GetValueOrDefault())
|
||||
if (disableSavePrompt.GetValueOrDefault())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -91,7 +91,7 @@ namespace Bit.Droid.Autofill
|
||||
parser.Parse();
|
||||
|
||||
var savedItem = parser.FieldCollection.GetSavedItem();
|
||||
if(savedItem == null)
|
||||
if (savedItem == null)
|
||||
{
|
||||
Toast.MakeText(this, "Unable to save this form.", ToastLength.Short).Show();
|
||||
return;
|
||||
@@ -102,7 +102,7 @@ namespace Bit.Droid.Autofill
|
||||
intent.PutExtra("autofillFramework", true);
|
||||
intent.PutExtra("autofillFrameworkSave", true);
|
||||
intent.PutExtra("autofillFrameworkType", (int)savedItem.Type);
|
||||
switch(savedItem.Type)
|
||||
switch (savedItem.Type)
|
||||
{
|
||||
case CipherType.Login:
|
||||
intent.PutExtra("autofillFrameworkName", parser.Uri
|
||||
|
||||
@@ -31,26 +31,26 @@ namespace Bit.Droid.Autofill
|
||||
HtmlInfo = node.HtmlInfo;
|
||||
Node = node;
|
||||
|
||||
if(node.AutofillValue != null)
|
||||
if (node.AutofillValue != null)
|
||||
{
|
||||
if(node.AutofillValue.IsList)
|
||||
if (node.AutofillValue.IsList)
|
||||
{
|
||||
var autofillOptions = node.GetAutofillOptions();
|
||||
if(autofillOptions != null && autofillOptions.Length > 0)
|
||||
if (autofillOptions != null && autofillOptions.Length > 0)
|
||||
{
|
||||
ListValue = node.AutofillValue.ListValue;
|
||||
TextValue = autofillOptions[node.AutofillValue.ListValue];
|
||||
}
|
||||
}
|
||||
else if(node.AutofillValue.IsDate)
|
||||
else if (node.AutofillValue.IsDate)
|
||||
{
|
||||
DateValue = node.AutofillValue.DateValue;
|
||||
}
|
||||
else if(node.AutofillValue.IsText)
|
||||
else if (node.AutofillValue.IsText)
|
||||
{
|
||||
TextValue = node.AutofillValue.TextValue;
|
||||
}
|
||||
else if(node.AutofillValue.IsToggle)
|
||||
else if (node.AutofillValue.IsToggle)
|
||||
{
|
||||
ToggleValue = node.AutofillValue.ToggleValue;
|
||||
}
|
||||
@@ -93,20 +93,20 @@ namespace Bit.Droid.Autofill
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if(this == obj)
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if(obj == null || GetType() != obj.GetType())
|
||||
if (obj == null || GetType() != obj.GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var field = obj as Field;
|
||||
if(TextValue != null ? !TextValue.Equals(field.TextValue) : field.TextValue != null)
|
||||
if (TextValue != null ? !TextValue.Equals(field.TextValue) : field.TextValue != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(DateValue != null ? !DateValue.Equals(field.DateValue) : field.DateValue != null)
|
||||
if (DateValue != null ? !DateValue.Equals(field.DateValue) : field.DateValue != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -128,7 +128,7 @@ namespace Bit.Droid.Autofill
|
||||
|
||||
private static bool IsValidHint(string hint)
|
||||
{
|
||||
switch(hint)
|
||||
switch (hint)
|
||||
{
|
||||
case View.AutofillHintCreditCardExpirationDate:
|
||||
case View.AutofillHintCreditCardExpirationDay:
|
||||
@@ -152,14 +152,14 @@ namespace Bit.Droid.Autofill
|
||||
private void UpdateSaveTypeFromHints()
|
||||
{
|
||||
SaveType = SaveDataType.Generic;
|
||||
if(_hints == null)
|
||||
if (_hints == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach(var hint in _hints)
|
||||
foreach (var hint in _hints)
|
||||
{
|
||||
switch(hint)
|
||||
switch (hint)
|
||||
{
|
||||
case View.AutofillHintCreditCardExpirationDate:
|
||||
case View.AutofillHintCreditCardExpirationDay:
|
||||
|
||||
@@ -19,11 +19,11 @@ namespace Bit.Droid.Autofill
|
||||
{
|
||||
get
|
||||
{
|
||||
if(FillableForLogin)
|
||||
if (FillableForLogin)
|
||||
{
|
||||
return SaveDataType.Password;
|
||||
}
|
||||
else if(FillableForCard)
|
||||
else if (FillableForCard)
|
||||
{
|
||||
return SaveDataType.CreditCard;
|
||||
}
|
||||
@@ -43,14 +43,14 @@ namespace Bit.Droid.Autofill
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_passwordFields != null)
|
||||
if (_passwordFields != null)
|
||||
{
|
||||
return _passwordFields;
|
||||
}
|
||||
if(Hints.Any())
|
||||
if (Hints.Any())
|
||||
{
|
||||
_passwordFields = new List<Field>();
|
||||
if(HintToFieldsMap.ContainsKey(View.AutofillHintPassword))
|
||||
if (HintToFieldsMap.ContainsKey(View.AutofillHintPassword))
|
||||
{
|
||||
_passwordFields.AddRange(HintToFieldsMap[View.AutofillHintPassword]);
|
||||
}
|
||||
@@ -58,7 +58,7 @@ namespace Bit.Droid.Autofill
|
||||
else
|
||||
{
|
||||
_passwordFields = Fields.Where(f => FieldIsPassword(f)).ToList();
|
||||
if(!_passwordFields.Any())
|
||||
if (!_passwordFields.Any())
|
||||
{
|
||||
_passwordFields = Fields.Where(f => FieldHasPasswordTerms(f)).ToList();
|
||||
}
|
||||
@@ -71,29 +71,29 @@ namespace Bit.Droid.Autofill
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_usernameFields != null)
|
||||
if (_usernameFields != null)
|
||||
{
|
||||
return _usernameFields;
|
||||
}
|
||||
_usernameFields = new List<Field>();
|
||||
if(Hints.Any())
|
||||
if (Hints.Any())
|
||||
{
|
||||
if(HintToFieldsMap.ContainsKey(View.AutofillHintEmailAddress))
|
||||
if (HintToFieldsMap.ContainsKey(View.AutofillHintEmailAddress))
|
||||
{
|
||||
_usernameFields.AddRange(HintToFieldsMap[View.AutofillHintEmailAddress]);
|
||||
}
|
||||
if(HintToFieldsMap.ContainsKey(View.AutofillHintUsername))
|
||||
if (HintToFieldsMap.ContainsKey(View.AutofillHintUsername))
|
||||
{
|
||||
_usernameFields.AddRange(HintToFieldsMap[View.AutofillHintUsername]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach(var passwordField in PasswordFields)
|
||||
foreach (var passwordField in PasswordFields)
|
||||
{
|
||||
var usernameField = Fields.TakeWhile(f => f.AutofillId != passwordField.AutofillId)
|
||||
.LastOrDefault();
|
||||
if(usernameField != null)
|
||||
if (usernameField != null)
|
||||
{
|
||||
_usernameFields.Add(usernameField);
|
||||
}
|
||||
@@ -127,7 +127,7 @@ namespace Bit.Droid.Autofill
|
||||
|
||||
public void Add(Field field)
|
||||
{
|
||||
if(field == null || FieldTrackingIds.Contains(field.TrackingId))
|
||||
if (field == null || FieldTrackingIds.Contains(field.TrackingId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -137,16 +137,16 @@ namespace Bit.Droid.Autofill
|
||||
Fields.Add(field);
|
||||
AutofillIds.Add(field.AutofillId);
|
||||
|
||||
if(field.Hints != null)
|
||||
if (field.Hints != null)
|
||||
{
|
||||
foreach(var hint in field.Hints)
|
||||
foreach (var hint in field.Hints)
|
||||
{
|
||||
Hints.Add(hint);
|
||||
if(field.Focused)
|
||||
if (field.Focused)
|
||||
{
|
||||
FocusedHints.Add(hint);
|
||||
}
|
||||
if(!HintToFieldsMap.ContainsKey(hint))
|
||||
if (!HintToFieldsMap.ContainsKey(hint))
|
||||
{
|
||||
HintToFieldsMap.Add(hint, new List<Field>());
|
||||
}
|
||||
@@ -157,10 +157,10 @@ namespace Bit.Droid.Autofill
|
||||
|
||||
public SavedItem GetSavedItem()
|
||||
{
|
||||
if(SaveType == SaveDataType.Password)
|
||||
if (SaveType == SaveDataType.Password)
|
||||
{
|
||||
var passwordField = PasswordFields.FirstOrDefault(f => !string.IsNullOrWhiteSpace(f.TextValue));
|
||||
if(passwordField == null)
|
||||
if (passwordField == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -178,7 +178,7 @@ namespace Bit.Droid.Autofill
|
||||
savedItem.Login.Username = GetFieldValue(usernameField);
|
||||
return savedItem;
|
||||
}
|
||||
else if(SaveType == SaveDataType.CreditCard)
|
||||
else if (SaveType == SaveDataType.CreditCard)
|
||||
{
|
||||
var savedItem = new SavedItem
|
||||
{
|
||||
@@ -199,26 +199,26 @@ namespace Bit.Droid.Autofill
|
||||
|
||||
public AutofillId[] GetOptionalSaveIds()
|
||||
{
|
||||
if(SaveType == SaveDataType.Password)
|
||||
if (SaveType == SaveDataType.Password)
|
||||
{
|
||||
return UsernameFields.Select(f => f.AutofillId).ToArray();
|
||||
}
|
||||
else if(SaveType == SaveDataType.CreditCard)
|
||||
else if (SaveType == SaveDataType.CreditCard)
|
||||
{
|
||||
var fieldList = new List<Field>();
|
||||
if(HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardSecurityCode))
|
||||
if (HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardSecurityCode))
|
||||
{
|
||||
fieldList.AddRange(HintToFieldsMap[View.AutofillHintCreditCardSecurityCode]);
|
||||
}
|
||||
if(HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardExpirationYear))
|
||||
if (HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardExpirationYear))
|
||||
{
|
||||
fieldList.AddRange(HintToFieldsMap[View.AutofillHintCreditCardExpirationYear]);
|
||||
}
|
||||
if(HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardExpirationMonth))
|
||||
if (HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardExpirationMonth))
|
||||
{
|
||||
fieldList.AddRange(HintToFieldsMap[View.AutofillHintCreditCardExpirationMonth]);
|
||||
}
|
||||
if(HintToFieldsMap.ContainsKey(View.AutofillHintName))
|
||||
if (HintToFieldsMap.ContainsKey(View.AutofillHintName))
|
||||
{
|
||||
fieldList.AddRange(HintToFieldsMap[View.AutofillHintName]);
|
||||
}
|
||||
@@ -229,11 +229,11 @@ namespace Bit.Droid.Autofill
|
||||
|
||||
public AutofillId[] GetRequiredSaveFields()
|
||||
{
|
||||
if(SaveType == SaveDataType.Password)
|
||||
if (SaveType == SaveDataType.Password)
|
||||
{
|
||||
return PasswordFields.Select(f => f.AutofillId).ToArray();
|
||||
}
|
||||
else if(SaveType == SaveDataType.CreditCard && HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardNumber))
|
||||
else if (SaveType == SaveDataType.CreditCard && HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardNumber))
|
||||
{
|
||||
return HintToFieldsMap[View.AutofillHintCreditCardNumber].Select(f => f.AutofillId).ToArray();
|
||||
}
|
||||
@@ -247,12 +247,12 @@ namespace Bit.Droid.Autofill
|
||||
|
||||
private string GetFieldValue(string hint, bool monthValue = false)
|
||||
{
|
||||
if(HintToFieldsMap.ContainsKey(hint))
|
||||
if (HintToFieldsMap.ContainsKey(hint))
|
||||
{
|
||||
foreach(var field in HintToFieldsMap[hint])
|
||||
foreach (var field in HintToFieldsMap[hint])
|
||||
{
|
||||
var val = GetFieldValue(field, monthValue);
|
||||
if(!string.IsNullOrWhiteSpace(val))
|
||||
if (!string.IsNullOrWhiteSpace(val))
|
||||
{
|
||||
return val;
|
||||
}
|
||||
@@ -263,30 +263,30 @@ namespace Bit.Droid.Autofill
|
||||
|
||||
private string GetFieldValue(Field field, bool monthValue = false)
|
||||
{
|
||||
if(field == null)
|
||||
if (field == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if(!string.IsNullOrWhiteSpace(field.TextValue))
|
||||
if (!string.IsNullOrWhiteSpace(field.TextValue))
|
||||
{
|
||||
if(field.AutofillType == AutofillType.List && field.ListValue.HasValue && monthValue)
|
||||
if (field.AutofillType == AutofillType.List && field.ListValue.HasValue && monthValue)
|
||||
{
|
||||
if(field.AutofillOptions.Count == 13)
|
||||
if (field.AutofillOptions.Count == 13)
|
||||
{
|
||||
return field.ListValue.ToString();
|
||||
}
|
||||
else if(field.AutofillOptions.Count == 12)
|
||||
else if (field.AutofillOptions.Count == 12)
|
||||
{
|
||||
return (field.ListValue + 1).ToString();
|
||||
}
|
||||
}
|
||||
return field.TextValue;
|
||||
}
|
||||
else if(field.DateValue.HasValue)
|
||||
else if (field.DateValue.HasValue)
|
||||
{
|
||||
return field.DateValue.Value.ToString();
|
||||
}
|
||||
else if(field.ToggleValue.HasValue)
|
||||
else if (field.ToggleValue.HasValue)
|
||||
{
|
||||
return field.ToggleValue.Value.ToString();
|
||||
}
|
||||
@@ -300,20 +300,20 @@ namespace Bit.Droid.Autofill
|
||||
f.InputType.HasFlag(InputTypes.TextVariationWebPassword);
|
||||
|
||||
// For whatever reason, multi-line input types are coming through with TextVariationPassword flags
|
||||
if(inputTypePassword && f.InputType.HasFlag(InputTypes.TextVariationPassword) &&
|
||||
if (inputTypePassword && f.InputType.HasFlag(InputTypes.TextVariationPassword) &&
|
||||
f.InputType.HasFlag(InputTypes.TextFlagMultiLine))
|
||||
{
|
||||
inputTypePassword = false;
|
||||
}
|
||||
|
||||
if(!inputTypePassword && f.HtmlInfo != null && f.HtmlInfo.Tag == "input" &&
|
||||
if (!inputTypePassword && f.HtmlInfo != null && f.HtmlInfo.Tag == "input" &&
|
||||
(f.HtmlInfo.Attributes?.Any() ?? false))
|
||||
{
|
||||
foreach(var a in f.HtmlInfo.Attributes)
|
||||
foreach (var a in f.HtmlInfo.Attributes)
|
||||
{
|
||||
var key = a.First as Java.Lang.String;
|
||||
var val = a.Second as Java.Lang.String;
|
||||
if(key != null && val != null && key.ToString() == "type" && val.ToString() == "password")
|
||||
if (key != null && val != null && key.ToString() == "type" && val.ToString() == "password")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -331,7 +331,7 @@ namespace Bit.Droid.Autofill
|
||||
|
||||
private bool ValueContainsAnyTerms(string value, HashSet<string> terms)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(value))
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Bit.Droid.Autofill
|
||||
Type = cipher.Type;
|
||||
Subtitle = cipher.SubTitle;
|
||||
|
||||
switch(Type)
|
||||
switch (Type)
|
||||
{
|
||||
case CipherType.Login:
|
||||
Icon = Resource.Drawable.login;
|
||||
@@ -62,32 +62,32 @@ namespace Bit.Droid.Autofill
|
||||
|
||||
public bool ApplyToFields(FieldCollection fieldCollection, Dataset.Builder datasetBuilder)
|
||||
{
|
||||
if(!fieldCollection?.Fields.Any() ?? true)
|
||||
if (!fieldCollection?.Fields.Any() ?? true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var setValues = false;
|
||||
if(Type == CipherType.Login)
|
||||
if (Type == CipherType.Login)
|
||||
{
|
||||
if(fieldCollection.PasswordFields.Any() && !string.IsNullOrWhiteSpace(_password))
|
||||
if (fieldCollection.PasswordFields.Any() && !string.IsNullOrWhiteSpace(_password))
|
||||
{
|
||||
foreach(var f in fieldCollection.PasswordFields)
|
||||
foreach (var f in fieldCollection.PasswordFields)
|
||||
{
|
||||
var val = ApplyValue(f, _password);
|
||||
if(val != null)
|
||||
if (val != null)
|
||||
{
|
||||
setValues = true;
|
||||
datasetBuilder.SetValue(f.AutofillId, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(fieldCollection.UsernameFields.Any() && !string.IsNullOrWhiteSpace(Subtitle))
|
||||
if (fieldCollection.UsernameFields.Any() && !string.IsNullOrWhiteSpace(Subtitle))
|
||||
{
|
||||
foreach(var f in fieldCollection.UsernameFields)
|
||||
foreach (var f in fieldCollection.UsernameFields)
|
||||
{
|
||||
var val = ApplyValue(f, Subtitle);
|
||||
if(val != null)
|
||||
if (val != null)
|
||||
{
|
||||
setValues = true;
|
||||
datasetBuilder.SetValue(f.AutofillId, val);
|
||||
@@ -95,59 +95,59 @@ namespace Bit.Droid.Autofill
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(Type == CipherType.Card)
|
||||
else if (Type == CipherType.Card)
|
||||
{
|
||||
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintCreditCardNumber,
|
||||
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintCreditCardNumber,
|
||||
_cardNumber))
|
||||
{
|
||||
setValues = true;
|
||||
}
|
||||
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintCreditCardSecurityCode,
|
||||
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintCreditCardSecurityCode,
|
||||
_cardCode))
|
||||
{
|
||||
setValues = true;
|
||||
}
|
||||
if(ApplyValue(datasetBuilder, fieldCollection,
|
||||
if (ApplyValue(datasetBuilder, fieldCollection,
|
||||
Android.Views.View.AutofillHintCreditCardExpirationMonth, _cardExpMonth, true))
|
||||
{
|
||||
setValues = true;
|
||||
}
|
||||
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintCreditCardExpirationYear,
|
||||
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintCreditCardExpirationYear,
|
||||
_cardExpYear))
|
||||
{
|
||||
setValues = true;
|
||||
}
|
||||
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintName, _cardName))
|
||||
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintName, _cardName))
|
||||
{
|
||||
setValues = true;
|
||||
}
|
||||
}
|
||||
else if(Type == CipherType.Identity)
|
||||
else if (Type == CipherType.Identity)
|
||||
{
|
||||
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintPhone, _idPhone))
|
||||
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintPhone, _idPhone))
|
||||
{
|
||||
setValues = true;
|
||||
}
|
||||
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintEmailAddress, _idEmail))
|
||||
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintEmailAddress, _idEmail))
|
||||
{
|
||||
setValues = true;
|
||||
}
|
||||
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintUsername,
|
||||
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintUsername,
|
||||
_idUsername))
|
||||
{
|
||||
setValues = true;
|
||||
}
|
||||
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintPostalAddress,
|
||||
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintPostalAddress,
|
||||
_idAddress))
|
||||
{
|
||||
setValues = true;
|
||||
}
|
||||
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintPostalCode,
|
||||
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintPostalCode,
|
||||
_idPostalCode))
|
||||
{
|
||||
setValues = true;
|
||||
}
|
||||
if(ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintName, Subtitle))
|
||||
if (ApplyValue(datasetBuilder, fieldCollection, Android.Views.View.AutofillHintName, Subtitle))
|
||||
{
|
||||
setValues = true;
|
||||
}
|
||||
@@ -159,12 +159,12 @@ namespace Bit.Droid.Autofill
|
||||
string hint, string value, bool monthValue = false)
|
||||
{
|
||||
bool setValues = false;
|
||||
if(fieldCollection.HintToFieldsMap.ContainsKey(hint) && !string.IsNullOrWhiteSpace(value))
|
||||
if (fieldCollection.HintToFieldsMap.ContainsKey(hint) && !string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
foreach(var f in fieldCollection.HintToFieldsMap[hint])
|
||||
foreach (var f in fieldCollection.HintToFieldsMap[hint])
|
||||
{
|
||||
var val = ApplyValue(f, value, monthValue);
|
||||
if(val != null)
|
||||
if (val != null)
|
||||
{
|
||||
setValues = true;
|
||||
builder.SetValue(f.AutofillId, val);
|
||||
@@ -176,31 +176,31 @@ namespace Bit.Droid.Autofill
|
||||
|
||||
private static AutofillValue ApplyValue(Field field, string value, bool monthValue = false)
|
||||
{
|
||||
switch(field.AutofillType)
|
||||
switch (field.AutofillType)
|
||||
{
|
||||
case AutofillType.Date:
|
||||
if(long.TryParse(value, out long dateValue))
|
||||
if (long.TryParse(value, out long dateValue))
|
||||
{
|
||||
return AutofillValue.ForDate(dateValue);
|
||||
}
|
||||
break;
|
||||
case AutofillType.List:
|
||||
if(field.AutofillOptions != null)
|
||||
if (field.AutofillOptions != null)
|
||||
{
|
||||
if(monthValue && int.TryParse(value, out int monthIndex))
|
||||
if (monthValue && int.TryParse(value, out int monthIndex))
|
||||
{
|
||||
if(field.AutofillOptions.Count == 13)
|
||||
if (field.AutofillOptions.Count == 13)
|
||||
{
|
||||
return AutofillValue.ForList(monthIndex);
|
||||
}
|
||||
else if(field.AutofillOptions.Count >= monthIndex)
|
||||
else if (field.AutofillOptions.Count >= monthIndex)
|
||||
{
|
||||
return AutofillValue.ForList(monthIndex - 1);
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < field.AutofillOptions.Count; i++)
|
||||
for (var i = 0; i < field.AutofillOptions.Count; i++)
|
||||
{
|
||||
if(field.AutofillOptions[i].Equals(value))
|
||||
if (field.AutofillOptions[i].Equals(value))
|
||||
{
|
||||
return AutofillValue.ForList(i);
|
||||
}
|
||||
@@ -210,7 +210,7 @@ namespace Bit.Droid.Autofill
|
||||
case AutofillType.Text:
|
||||
return AutofillValue.ForText(value);
|
||||
case AutofillType.Toggle:
|
||||
if(bool.TryParse(value, out bool toggleValue))
|
||||
if (bool.TryParse(value, out bool toggleValue))
|
||||
{
|
||||
return AutofillValue.ForToggle(toggleValue);
|
||||
}
|
||||
|
||||
@@ -33,16 +33,16 @@ namespace Bit.Droid.Autofill
|
||||
{
|
||||
get
|
||||
{
|
||||
if(!string.IsNullOrWhiteSpace(_uri))
|
||||
if (!string.IsNullOrWhiteSpace(_uri))
|
||||
{
|
||||
return _uri;
|
||||
}
|
||||
var websiteNull = string.IsNullOrWhiteSpace(Website);
|
||||
if(websiteNull && string.IsNullOrWhiteSpace(PackageName))
|
||||
if (websiteNull && string.IsNullOrWhiteSpace(PackageName))
|
||||
{
|
||||
_uri = null;
|
||||
}
|
||||
else if(!websiteNull)
|
||||
else if (!websiteNull)
|
||||
{
|
||||
_uri = Website;
|
||||
}
|
||||
@@ -59,7 +59,7 @@ namespace Bit.Droid.Autofill
|
||||
get => _packageName;
|
||||
set
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(value))
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
_packageName = _uri = null;
|
||||
}
|
||||
@@ -72,7 +72,7 @@ namespace Bit.Droid.Autofill
|
||||
get => _website;
|
||||
set
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(value))
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
_website = _uri = null;
|
||||
}
|
||||
@@ -84,10 +84,10 @@ namespace Bit.Droid.Autofill
|
||||
{
|
||||
var fillable = !string.IsNullOrWhiteSpace(Uri) && !AutofillHelpers.BlacklistedUris.Contains(Uri) &&
|
||||
FieldCollection != null && FieldCollection.Fillable;
|
||||
if(fillable)
|
||||
if (fillable)
|
||||
{
|
||||
var blacklistedUris = await storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
|
||||
if(blacklistedUris != null && blacklistedUris.Count > 0)
|
||||
if (blacklistedUris != null && blacklistedUris.Count > 0)
|
||||
{
|
||||
fillable = !new HashSet<string>(blacklistedUris).Contains(Uri);
|
||||
}
|
||||
@@ -98,20 +98,20 @@ namespace Bit.Droid.Autofill
|
||||
public void Parse()
|
||||
{
|
||||
string titlePackageId = null;
|
||||
for(var i = 0; i < _structure.WindowNodeCount; i++)
|
||||
for (var i = 0; i < _structure.WindowNodeCount; i++)
|
||||
{
|
||||
var node = _structure.GetWindowNodeAt(i);
|
||||
if(i == 0)
|
||||
if (i == 0)
|
||||
{
|
||||
titlePackageId = GetTitlePackageId(node);
|
||||
}
|
||||
ParseNode(node.RootViewNode);
|
||||
}
|
||||
if(string.IsNullOrWhiteSpace(PackageName) && string.IsNullOrWhiteSpace(Website))
|
||||
if (string.IsNullOrWhiteSpace(PackageName) && string.IsNullOrWhiteSpace(Website))
|
||||
{
|
||||
PackageName = titlePackageId;
|
||||
}
|
||||
if(!AutofillHelpers.TrustedBrowsers.Contains(PackageName) &&
|
||||
if (!AutofillHelpers.TrustedBrowsers.Contains(PackageName) &&
|
||||
!AutofillHelpers.CompatBrowsers.Contains(PackageName))
|
||||
{
|
||||
Website = null;
|
||||
@@ -123,7 +123,7 @@ namespace Bit.Droid.Autofill
|
||||
SetPackageAndDomain(node);
|
||||
var hints = node.GetAutofillHints();
|
||||
var isEditText = node.ClassName == "android.widget.EditText" || node?.HtmlInfo?.Tag == "input";
|
||||
if(isEditText || (hints?.Length ?? 0) > 0)
|
||||
if (isEditText || (hints?.Length ?? 0) > 0)
|
||||
{
|
||||
FieldCollection.Add(new Field(node));
|
||||
}
|
||||
@@ -132,7 +132,7 @@ namespace Bit.Droid.Autofill
|
||||
FieldCollection.IgnoreAutofillIds.Add(node.AutofillId);
|
||||
}
|
||||
|
||||
for(var i = 0; i < node.ChildCount; i++)
|
||||
for (var i = 0; i < node.ChildCount; i++)
|
||||
{
|
||||
ParseNode(node.GetChildAt(i));
|
||||
}
|
||||
@@ -140,15 +140,15 @@ namespace Bit.Droid.Autofill
|
||||
|
||||
private void SetPackageAndDomain(ViewNode node)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(PackageName) && !string.IsNullOrWhiteSpace(node.IdPackage) &&
|
||||
if (string.IsNullOrWhiteSpace(PackageName) && !string.IsNullOrWhiteSpace(node.IdPackage) &&
|
||||
!_excludedPackageIds.Contains(node.IdPackage))
|
||||
{
|
||||
PackageName = node.IdPackage;
|
||||
}
|
||||
if(string.IsNullOrWhiteSpace(Website) && !string.IsNullOrWhiteSpace(node.WebDomain))
|
||||
if (string.IsNullOrWhiteSpace(Website) && !string.IsNullOrWhiteSpace(node.WebDomain))
|
||||
{
|
||||
var scheme = "http";
|
||||
if((int)Build.VERSION.SdkInt >= 28)
|
||||
if ((int)Build.VERSION.SdkInt >= 28)
|
||||
{
|
||||
scheme = node.WebScheme;
|
||||
}
|
||||
@@ -158,13 +158,13 @@ namespace Bit.Droid.Autofill
|
||||
|
||||
private string GetTitlePackageId(WindowNode node)
|
||||
{
|
||||
if(node != null && !string.IsNullOrWhiteSpace(node.Title))
|
||||
if (node != null && !string.IsNullOrWhiteSpace(node.Title))
|
||||
{
|
||||
var slashPosition = node.Title.IndexOf('/');
|
||||
if(slashPosition > -1)
|
||||
if (slashPosition > -1)
|
||||
{
|
||||
var packageId = node.Title.Substring(0, slashPosition);
|
||||
if(packageId.Contains("."))
|
||||
if (packageId.Contains("."))
|
||||
{
|
||||
return packageId;
|
||||
}
|
||||
|
||||
30
src/Android/Effects/FabShadowEffect.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Android.Graphics.Drawables;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Droid.Effects;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.Android;
|
||||
|
||||
[assembly: ExportEffect(typeof(FabShadowEffect), "FabShadowEffect")]
|
||||
namespace Bit.Droid.Effects
|
||||
{
|
||||
public class FabShadowEffect : PlatformEffect
|
||||
{
|
||||
protected override void OnAttached ()
|
||||
{
|
||||
if (Control is Android.Widget.Button button)
|
||||
{
|
||||
var gd = new GradientDrawable();
|
||||
gd.SetColor(ThemeManager.GetResourceColor("FabColor").ToAndroid());
|
||||
gd.SetCornerRadius(100);
|
||||
|
||||
button.SetBackground(gd);
|
||||
button.Elevation = 6;
|
||||
button.TranslationZ = 20;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDetached ()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
using Android.Support.Design.BottomNavigation;
|
||||
using Android.Support.Design.Widget;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using Android.Widget;
|
||||
using Bit.Droid.Effects;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.Android;
|
||||
@@ -13,7 +10,7 @@ namespace Bit.Droid.Effects
|
||||
{
|
||||
protected override void OnAttached()
|
||||
{
|
||||
if(Element is Label label && Control is TextView textView)
|
||||
if (Element is Label label && Control is TextView textView)
|
||||
{
|
||||
textView.SetTextSize(Android.Util.ComplexUnitType.Pt, (float)label.FontSize);
|
||||
}
|
||||
@@ -23,4 +20,4 @@ namespace Bit.Droid.Effects
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using Android.Support.Design.BottomNavigation;
|
||||
using Android.Support.Design.Widget;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using Android.Widget;
|
||||
using Bit.Droid.Effects;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.Android;
|
||||
@@ -13,7 +10,7 @@ namespace Bit.Droid.Effects
|
||||
{
|
||||
protected override void OnAttached()
|
||||
{
|
||||
if(Control is TextView textView)
|
||||
if (Control is TextView textView)
|
||||
{
|
||||
textView.SetTextIsSelectable(true);
|
||||
}
|
||||
@@ -23,4 +20,4 @@ namespace Bit.Droid.Effects
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Android.Support.Design.BottomNavigation;
|
||||
using Android.Support.Design.Widget;
|
||||
using Android.Views;
|
||||
using Android.Views;
|
||||
using Bit.Droid.Effects;
|
||||
using Google.Android.Material.BottomNavigation;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.Android;
|
||||
|
||||
@@ -13,11 +12,11 @@ namespace Bit.Droid.Effects
|
||||
{
|
||||
protected override void OnAttached()
|
||||
{
|
||||
if(!(Container.GetChildAt(0) is ViewGroup layout))
|
||||
if (!(Container.GetChildAt(0) is ViewGroup layout))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(!(layout.GetChildAt(1) is BottomNavigationView bottomNavigationView))
|
||||
if (!(layout.GetChildAt(1) is BottomNavigationView bottomNavigationView))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -28,4 +27,4 @@ namespace Bit.Droid.Effects
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ using Bit.Core.Enums;
|
||||
using Android.Nfc;
|
||||
using Bit.App.Utilities;
|
||||
using System.Threading.Tasks;
|
||||
using Android.Support.V4.Content;
|
||||
using AndroidX.Core.Content;
|
||||
|
||||
namespace Bit.Droid
|
||||
{
|
||||
@@ -37,7 +37,7 @@ namespace Bit.Droid
|
||||
private IAppIdService _appIdService;
|
||||
private IStorageService _storageService;
|
||||
private IEventService _eventService;
|
||||
private PendingIntent _lockAlarmPendingIntent;
|
||||
private PendingIntent _vaultTimeoutAlarmPendingIntent;
|
||||
private PendingIntent _clearClipboardPendingIntent;
|
||||
private PendingIntent _eventUploadPendingIntent;
|
||||
private AppOptions _appOptions;
|
||||
@@ -51,7 +51,7 @@ namespace Bit.Droid
|
||||
_eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent,
|
||||
PendingIntentFlags.UpdateCurrent);
|
||||
var alarmIntent = new Intent(this, typeof(LockAlarmReceiver));
|
||||
_lockAlarmPendingIntent = PendingIntent.GetBroadcast(this, 0, alarmIntent,
|
||||
_vaultTimeoutAlarmPendingIntent = PendingIntent.GetBroadcast(this, 0, alarmIntent,
|
||||
PendingIntentFlags.UpdateCurrent);
|
||||
var clearClipboardIntent = new Intent(this, typeof(ClearClipboardAlarmReceiver));
|
||||
_clearClipboardPendingIntent = PendingIntent.GetBroadcast(this, 0, clearClipboardIntent,
|
||||
@@ -73,14 +73,14 @@ namespace Bit.Droid
|
||||
|
||||
UpdateTheme(ThemeManager.GetTheme(true));
|
||||
base.OnCreate(savedInstanceState);
|
||||
if(!CoreHelpers.InDebugMode())
|
||||
if (!CoreHelpers.InDebugMode())
|
||||
{
|
||||
Window.AddFlags(Android.Views.WindowManagerFlags.Secure);
|
||||
}
|
||||
|
||||
#if !FDROID
|
||||
var hockeyAppListener = new HockeyAppCrashManagerListener(_appIdService, _userService);
|
||||
var hockeyAppTask = hockeyAppListener.InitAsync(this);
|
||||
var appCenterHelper = new AppCenterHelper(_appIdService, _userService);
|
||||
var appCenterTask = appCenterHelper.InitAsync();
|
||||
#endif
|
||||
|
||||
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
|
||||
@@ -90,44 +90,44 @@ namespace Bit.Droid
|
||||
|
||||
_broadcasterService.Subscribe(_activityKey, (message) =>
|
||||
{
|
||||
if(message.Command == "scheduleLockTimer")
|
||||
if (message.Command == "scheduleVaultTimeoutTimer")
|
||||
{
|
||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
||||
var lockOptionMinutes = (int)message.Data;
|
||||
var lockOptionMs = lockOptionMinutes * 60000;
|
||||
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + lockOptionMs + 10;
|
||||
alarmManager.Set(AlarmType.RtcWakeup, triggerMs, _lockAlarmPendingIntent);
|
||||
var vaultTimeoutMinutes = (int)message.Data;
|
||||
var vaultTimeoutMs = vaultTimeoutMinutes * 60000;
|
||||
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + vaultTimeoutMs + 10;
|
||||
alarmManager.Set(AlarmType.RtcWakeup, triggerMs, _vaultTimeoutAlarmPendingIntent);
|
||||
}
|
||||
else if(message.Command == "cancelLockTimer")
|
||||
else if (message.Command == "cancelVaultTimeoutTimer")
|
||||
{
|
||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
||||
alarmManager.Cancel(_lockAlarmPendingIntent);
|
||||
alarmManager.Cancel(_vaultTimeoutAlarmPendingIntent);
|
||||
}
|
||||
else if(message.Command == "startEventTimer")
|
||||
else if (message.Command == "startEventTimer")
|
||||
{
|
||||
StartEventAlarm();
|
||||
}
|
||||
else if(message.Command == "stopEventTimer")
|
||||
else if (message.Command == "stopEventTimer")
|
||||
{
|
||||
var task = StopEventAlarmAsync();
|
||||
}
|
||||
else if(message.Command == "finishMainActivity")
|
||||
else if (message.Command == "finishMainActivity")
|
||||
{
|
||||
Xamarin.Forms.Device.BeginInvokeOnMainThread(() => Finish());
|
||||
}
|
||||
else if(message.Command == "listenYubiKeyOTP")
|
||||
else if (message.Command == "listenYubiKeyOTP")
|
||||
{
|
||||
ListenYubiKey((bool)message.Data);
|
||||
}
|
||||
else if(message.Command == "updatedTheme")
|
||||
else if (message.Command == "updatedTheme")
|
||||
{
|
||||
RestartApp();
|
||||
}
|
||||
else if(message.Command == "exit")
|
||||
else if (message.Command == "exit")
|
||||
{
|
||||
ExitApp();
|
||||
}
|
||||
else if(message.Command == "copiedToClipboard")
|
||||
else if (message.Command == "copiedToClipboard")
|
||||
{
|
||||
var task = ClearClipboardAlarmAsync(message.Data as Tuple<string, int?, bool>);
|
||||
}
|
||||
@@ -143,7 +143,7 @@ namespace Bit.Droid
|
||||
protected override void OnResume()
|
||||
{
|
||||
base.OnResume();
|
||||
if(_deviceActionService.SupportsNfc())
|
||||
if (_deviceActionService.SupportsNfc())
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -157,18 +157,18 @@ namespace Bit.Droid
|
||||
protected override void OnNewIntent(Intent intent)
|
||||
{
|
||||
base.OnNewIntent(intent);
|
||||
if(intent.GetBooleanExtra("generatorTile", false))
|
||||
if (intent.GetBooleanExtra("generatorTile", false))
|
||||
{
|
||||
_messagingService.Send("popAllAndGoToTabGenerator");
|
||||
if(_appOptions != null)
|
||||
if (_appOptions != null)
|
||||
{
|
||||
_appOptions.GeneratorTile = true;
|
||||
}
|
||||
}
|
||||
if(intent.GetBooleanExtra("myVaultTile", false))
|
||||
if (intent.GetBooleanExtra("myVaultTile", false))
|
||||
{
|
||||
_messagingService.Send("popAllAndGoToTabMyVault");
|
||||
if(_appOptions != null)
|
||||
if (_appOptions != null)
|
||||
{
|
||||
_appOptions.MyVaultTile = true;
|
||||
}
|
||||
@@ -182,9 +182,9 @@ namespace Bit.Droid
|
||||
public async override void OnRequestPermissionsResult(int requestCode, string[] permissions,
|
||||
[GeneratedEnum] Permission[] grantResults)
|
||||
{
|
||||
if(requestCode == Constants.SelectFilePermissionRequestCode)
|
||||
if (requestCode == Constants.SelectFilePermissionRequestCode)
|
||||
{
|
||||
if(grantResults.Any(r => r != Permission.Granted))
|
||||
if (grantResults.Any(r => r != Permission.Granted))
|
||||
{
|
||||
_messagingService.Send("selectFileCameraPermissionDenied");
|
||||
}
|
||||
@@ -201,12 +201,12 @@ namespace Bit.Droid
|
||||
|
||||
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
|
||||
{
|
||||
if(resultCode == Result.Ok &&
|
||||
if (resultCode == Result.Ok &&
|
||||
(requestCode == Constants.SelectFileRequestCode || requestCode == Constants.SaveFileRequestCode))
|
||||
{
|
||||
Android.Net.Uri uri = null;
|
||||
string fileName = null;
|
||||
if(data != null && data.Data != null)
|
||||
if (data != null && data.Data != null)
|
||||
{
|
||||
uri = data.Data;
|
||||
fileName = AndroidHelpers.GetFileName(ApplicationContext, uri);
|
||||
@@ -219,12 +219,12 @@ namespace Bit.Droid
|
||||
fileName = $"photo_{DateTime.UtcNow.ToString("yyyyMMddHHmmss")}.jpg";
|
||||
}
|
||||
|
||||
if(uri == null)
|
||||
if (uri == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(requestCode == Constants.SaveFileRequestCode)
|
||||
if (requestCode == Constants.SaveFileRequestCode)
|
||||
{
|
||||
_messagingService.Send("selectSaveFileResult",
|
||||
new Tuple<string, string>(uri.ToString(), fileName));
|
||||
@@ -233,15 +233,15 @@ namespace Bit.Droid
|
||||
|
||||
try
|
||||
{
|
||||
using(var stream = ContentResolver.OpenInputStream(uri))
|
||||
using(var memoryStream = new MemoryStream())
|
||||
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)
|
||||
catch (Java.IO.FileNotFoundException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -256,12 +256,12 @@ namespace Bit.Droid
|
||||
|
||||
private void ListenYubiKey(bool listen)
|
||||
{
|
||||
if(!_deviceActionService.SupportsNfc())
|
||||
if (!_deviceActionService.SupportsNfc())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var adapter = NfcAdapter.GetDefaultAdapter(this);
|
||||
if(listen)
|
||||
if (listen)
|
||||
{
|
||||
var intent = new Intent(this, Class);
|
||||
intent.AddFlags(ActivityFlags.SingleTop);
|
||||
@@ -298,11 +298,11 @@ namespace Bit.Droid
|
||||
FromAutofillFramework = Intent.GetBooleanExtra("autofillFramework", false)
|
||||
};
|
||||
var fillType = Intent.GetIntExtra("autofillFrameworkFillType", 0);
|
||||
if(fillType > 0)
|
||||
if (fillType > 0)
|
||||
{
|
||||
options.FillType = (CipherType)fillType;
|
||||
}
|
||||
if(Intent.GetBooleanExtra("autofillFrameworkSave", false))
|
||||
if (Intent.GetBooleanExtra("autofillFrameworkSave", false))
|
||||
{
|
||||
options.SaveType = (CipherType)Intent.GetIntExtra("autofillFrameworkType", 0);
|
||||
options.SaveName = Intent.GetStringExtra("autofillFrameworkName");
|
||||
@@ -319,12 +319,12 @@ namespace Bit.Droid
|
||||
|
||||
private void ParseYubiKey(string data)
|
||||
{
|
||||
if(data == null)
|
||||
if (data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var otpMatch = _otpPattern.Matcher(data);
|
||||
if(otpMatch.Matches())
|
||||
if (otpMatch.Matches())
|
||||
{
|
||||
var otp = otpMatch.Group(1);
|
||||
_messagingService.Send("gotYubiKeyOTP", otp);
|
||||
@@ -333,15 +333,15 @@ namespace Bit.Droid
|
||||
|
||||
private void UpdateTheme(string theme)
|
||||
{
|
||||
if(theme == "dark")
|
||||
if (theme == "dark")
|
||||
{
|
||||
SetTheme(Resource.Style.DarkTheme);
|
||||
}
|
||||
else if(theme == "black")
|
||||
else if (theme == "black")
|
||||
{
|
||||
SetTheme(Resource.Style.BlackTheme);
|
||||
}
|
||||
else if(theme == "nord")
|
||||
else if (theme == "nord")
|
||||
{
|
||||
SetTheme(Resource.Style.NordTheme);
|
||||
}
|
||||
@@ -369,24 +369,23 @@ namespace Bit.Droid
|
||||
|
||||
private async Task ClearClipboardAlarmAsync(Tuple<string, int?, bool> data)
|
||||
{
|
||||
if(data.Item3)
|
||||
if (data.Item3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var clearMs = data.Item2;
|
||||
if(clearMs == null)
|
||||
if (clearMs == null)
|
||||
{
|
||||
var clearSeconds = await _storageService.GetAsync<int?>(Constants.ClearClipboardKey);
|
||||
if(clearSeconds != null)
|
||||
if (clearSeconds != null)
|
||||
{
|
||||
clearMs = clearSeconds.Value * 1000;
|
||||
}
|
||||
}
|
||||
if(clearMs == null)
|
||||
if (clearMs == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
StaticStore.LastClipboardValue = data.Item1;
|
||||
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + clearMs.Value;
|
||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
||||
alarmManager.Set(AlarmType.Rtc, triggerMs, _clearClipboardPendingIntent);
|
||||
|
||||
@@ -37,14 +37,14 @@ namespace Bit.Droid
|
||||
public MainApplication(IntPtr handle, JniHandleOwnership transer)
|
||||
: base(handle, transer)
|
||||
{
|
||||
if(ServiceContainer.RegisteredServices.Count == 0)
|
||||
if (ServiceContainer.RegisteredServices.Count == 0)
|
||||
{
|
||||
RegisterLocalServices();
|
||||
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
ServiceContainer.Init(deviceActionService.DeviceUserAgent);
|
||||
}
|
||||
#if !FDROID
|
||||
if(Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
|
||||
if (Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
|
||||
{
|
||||
ProviderInstaller.InstallIfNeededAsync(ApplicationContext, this);
|
||||
}
|
||||
@@ -70,7 +70,6 @@ namespace Bit.Droid
|
||||
{
|
||||
ServiceContainer.Register<ILogService>("logService", new AndroidLogService());
|
||||
|
||||
Refractored.FabControl.Droid.FloatingActionButtonViewRenderer.Init();
|
||||
// Note: This might cause a race condition. Investigate more.
|
||||
Task.Run(() =>
|
||||
{
|
||||
@@ -83,7 +82,6 @@ namespace Bit.Droid
|
||||
ZXing.Net.Mobile.Forms.Android.Platform.Init();
|
||||
});
|
||||
CrossFingerprint.SetCurrentActivityResolver(() => CrossCurrentActivity.Current.Activity);
|
||||
CrossFingerprint.SetDialogFragmentType<CustomFingerprintDialogFragment>();
|
||||
|
||||
var preferencesStorage = new PreferencesStorageService(null);
|
||||
var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:versionCode="1"
|
||||
android:versionName="2.3.0"
|
||||
android:versionName="2.4.1"
|
||||
package="com.x8bit.bitwarden">
|
||||
|
||||
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="29" />
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
@@ -30,7 +30,7 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:networkSecurityConfig="@xml/network_security_config">
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="com.x8bit.bitwarden.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
@@ -55,5 +55,12 @@
|
||||
|
||||
<meta-data android:name="android.max_aspect" android:value="2.1" />
|
||||
<meta-data android:name="android.content.APP_RESTRICTIONS" android:resource="@xml/app_restrictions" />
|
||||
|
||||
<!-- Support for Samsung "Multi Window" mode (for Android < 7.0 users) -->
|
||||
<meta-data android:name="com.samsung.android.sdk.multiwindow.enable" android:value="true" />
|
||||
<meta-data android:name="com.samsung.android.sdk.multiwindow.penwindow.enable" android:value="true" />
|
||||
|
||||
<!-- Support for LG "Dual Window" mode (for Android < 7.0 users) -->
|
||||
<meta-data android:name="com.lge.support.SPLIT_WINDOW" android:value="true" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -16,12 +16,12 @@ namespace Bit.Droid.Push
|
||||
{
|
||||
public async override void OnMessageReceived(RemoteMessage message)
|
||||
{
|
||||
if(message?.Data == null)
|
||||
if (message?.Data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var data = message.Data.ContainsKey("data") ? message.Data["data"] : null;
|
||||
if(data == null)
|
||||
if (data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -32,7 +32,7 @@ namespace Bit.Droid.Push
|
||||
"pushNotificationListenerService");
|
||||
await listener.OnMessageAsync(obj, Device.Android);
|
||||
}
|
||||
catch(JsonReaderException ex)
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(ex.ToString());
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using Android.Content;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Droid.Utilities;
|
||||
|
||||
namespace Bit.Droid.Receivers
|
||||
{
|
||||
@@ -12,11 +8,7 @@ namespace Bit.Droid.Receivers
|
||||
public override void OnReceive(Context context, Intent intent)
|
||||
{
|
||||
var clipboardManager = context.GetSystemService(Context.ClipboardService) as ClipboardManager;
|
||||
if(StaticStore.LastClipboardValue != null && StaticStore.LastClipboardValue == clipboardManager.Text)
|
||||
{
|
||||
clipboardManager.Text = string.Empty;
|
||||
}
|
||||
StaticStore.LastClipboardValue = null;
|
||||
clipboardManager.PrimaryClip = ClipData.NewPlainText("bitwarden", string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace Bit.Droid.Receivers
|
||||
{
|
||||
public async override void OnReceive(Context context, Intent intent)
|
||||
{
|
||||
var lockService = ServiceContainer.Resolve<ILockService>("lockService");
|
||||
await lockService.CheckLockAsync();
|
||||
var vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
await vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Bit.Droid.Receivers
|
||||
{
|
||||
public async override void OnReceive(Context context, Intent intent)
|
||||
{
|
||||
if(intent.Action == Intent.ActionApplicationRestrictionsChanged)
|
||||
if (intent.Action == Intent.ActionApplicationRestrictionsChanged)
|
||||
{
|
||||
await AndroidHelpers.SetPreconfiguredRestrictionSettingsAsync(context);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using Android.Views;
|
||||
using Android.Views.InputMethods;
|
||||
using Android.Widget;
|
||||
using Bit.App.Controls;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Droid.Renderers;
|
||||
using FFImageLoading;
|
||||
using FFImageLoading.Views;
|
||||
@@ -32,34 +33,30 @@ namespace Bit.Droid.Renderers
|
||||
protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView,
|
||||
ViewGroup parent, Context context)
|
||||
{
|
||||
if(_faTypeface == null)
|
||||
if (_faTypeface == null)
|
||||
{
|
||||
_faTypeface = Typeface.CreateFromAsset(context.Assets, "FontAwesome.ttf");
|
||||
}
|
||||
if(_miTypeface == null)
|
||||
if (_miTypeface == null)
|
||||
{
|
||||
_miTypeface = Typeface.CreateFromAsset(context.Assets, "MaterialIcons_Regular.ttf");
|
||||
}
|
||||
if(_textColor == default(Android.Graphics.Color))
|
||||
if (_textColor == default(Android.Graphics.Color))
|
||||
{
|
||||
_textColor = ((Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["TextColor"])
|
||||
.ToAndroid();
|
||||
_textColor = ThemeManager.GetResourceColor("TextColor").ToAndroid();
|
||||
}
|
||||
if(_mutedColor == default(Android.Graphics.Color))
|
||||
if (_mutedColor == default(Android.Graphics.Color))
|
||||
{
|
||||
_mutedColor = ((Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["MutedColor"])
|
||||
.ToAndroid();
|
||||
_mutedColor = ThemeManager.GetResourceColor("MutedColor").ToAndroid();
|
||||
}
|
||||
if(_disabledIconColor == default(Android.Graphics.Color))
|
||||
if (_disabledIconColor == default(Android.Graphics.Color))
|
||||
{
|
||||
_disabledIconColor =
|
||||
((Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["DisabledIconColor"])
|
||||
.ToAndroid();
|
||||
_disabledIconColor = ThemeManager.GetResourceColor("DisabledIconColor").ToAndroid();
|
||||
}
|
||||
|
||||
var cipherCell = item as CipherViewCell;
|
||||
_cell = convertView as AndroidCipherCell;
|
||||
if(_cell == null)
|
||||
if (_cell == null)
|
||||
{
|
||||
_cell = new AndroidCipherCell(context, cipherCell, _faTypeface, _miTypeface);
|
||||
}
|
||||
@@ -77,11 +74,11 @@ namespace Bit.Droid.Renderers
|
||||
{
|
||||
var cipherCell = sender as CipherViewCell;
|
||||
_cell.CipherViewCell = cipherCell;
|
||||
if(e.PropertyName == CipherViewCell.CipherProperty.PropertyName)
|
||||
if (e.PropertyName == CipherViewCell.CipherProperty.PropertyName)
|
||||
{
|
||||
_cell.UpdateCell(cipherCell);
|
||||
}
|
||||
else if(e.PropertyName == CipherViewCell.WebsiteIconsEnabledProperty.PropertyName)
|
||||
else if (e.PropertyName == CipherViewCell.WebsiteIconsEnabledProperty.PropertyName)
|
||||
{
|
||||
_cell.UpdateIconImage(cipherCell);
|
||||
}
|
||||
@@ -145,7 +142,7 @@ namespace Bit.Droid.Renderers
|
||||
|
||||
var cipher = cipherCell.Cipher;
|
||||
Name.Text = cipher.Name;
|
||||
if(!string.IsNullOrWhiteSpace(cipher.SubTitle))
|
||||
if (!string.IsNullOrWhiteSpace(cipher.SubTitle))
|
||||
{
|
||||
SubTitle.Text = cipher.SubTitle;
|
||||
SubTitle.Visibility = ViewStates.Visible;
|
||||
@@ -160,7 +157,7 @@ namespace Bit.Droid.Renderers
|
||||
|
||||
public void UpdateIconImage(CipherViewCell cipherCell)
|
||||
{
|
||||
if(_currentTask != null && !_currentTask.IsCancelled && !_currentTask.IsCompleted)
|
||||
if (_currentTask != null && !_currentTask.IsCancelled && !_currentTask.IsCompleted)
|
||||
{
|
||||
_currentTask.Cancel();
|
||||
}
|
||||
@@ -168,7 +165,7 @@ namespace Bit.Droid.Renderers
|
||||
var cipher = cipherCell.Cipher;
|
||||
|
||||
var iconImage = cipherCell.GetIconImage(cipher);
|
||||
if(iconImage.Item2 != null)
|
||||
if (iconImage.Item2 != null)
|
||||
{
|
||||
IconImage.SetImageResource(Resource.Drawable.login);
|
||||
IconImage.Visibility = ViewStates.Visible;
|
||||
@@ -197,7 +194,7 @@ namespace Bit.Droid.Renderers
|
||||
|
||||
private void MoreButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if(CipherViewCell.ButtonCommand?.CanExecute(CipherViewCell.Cipher) ?? false)
|
||||
if (CipherViewCell.ButtonCommand?.CanExecute(CipherViewCell.Cipher) ?? false)
|
||||
{
|
||||
CipherViewCell.ButtonCommand.Execute(CipherViewCell.Cipher);
|
||||
}
|
||||
@@ -205,7 +202,7 @@ namespace Bit.Droid.Renderers
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if(disposing)
|
||||
if (disposing)
|
||||
{
|
||||
MoreButton.Click -= MoreButton_Click;
|
||||
}
|
||||
|
||||
@@ -12,11 +12,20 @@ namespace Bit.Droid.Renderers
|
||||
public CustomEditorRenderer(Context context)
|
||||
: base(context)
|
||||
{ }
|
||||
|
||||
// Workaround for issue described here:
|
||||
// https://github.com/xamarin/Xamarin.Forms/issues/8291#issuecomment-617456651
|
||||
protected override void OnAttachedToWindow()
|
||||
{
|
||||
base.OnAttachedToWindow();
|
||||
EditText.Enabled = false;
|
||||
EditText.Enabled = true;
|
||||
}
|
||||
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
if(Control != null && e.NewElement != null)
|
||||
if (Control != null && e.NewElement != null)
|
||||
{
|
||||
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
|
||||
Control.PaddingBottom + 20);
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Bit.Droid.Renderers
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
if(Control != null && e.NewElement != null)
|
||||
if (Control != null && e.NewElement != null)
|
||||
{
|
||||
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
|
||||
Control.PaddingBottom + 20);
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Bit.Droid.Renderers
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
if(Control != null && e.NewElement != null)
|
||||
if (Control != null && e.NewElement != null)
|
||||
{
|
||||
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
|
||||
Control.PaddingBottom + 20);
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Bit.Droid.Renderers
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
if(Control != null && e.NewElement != null)
|
||||
if (Control != null && e.NewElement != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Bit.Droid.Renderers
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
if(Control != null && e.NewElement != null && e.NewElement is ExtendedListView listView)
|
||||
if (Control != null && e.NewElement != null && e.NewElement is ExtendedListView listView)
|
||||
{
|
||||
// Pad for FAB
|
||||
Control.SetPadding(0, 0, 0, 170);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Android.Content;
|
||||
using Android.Graphics.Drawables;
|
||||
using Android.Support.V4.Content.Res;
|
||||
using AndroidX.Core.Content.Resources;
|
||||
using Bit.App.Controls;
|
||||
using Bit.Droid.Renderers;
|
||||
using Xamarin.Forms;
|
||||
@@ -18,12 +18,12 @@ namespace Bit.Droid.Renderers
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
if(Control != null && Element is ExtendedSlider view)
|
||||
if (Control != null && Element is ExtendedSlider view)
|
||||
{
|
||||
var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.slider_thumb, null);
|
||||
if(t is GradientDrawable thumb)
|
||||
if (t is GradientDrawable thumb)
|
||||
{
|
||||
if(view.ThumbColor == Color.Default)
|
||||
if (view.ThumbColor == Color.Default)
|
||||
{
|
||||
thumb.SetColor(Color.White.ToAndroid());
|
||||
}
|
||||
|
||||
@@ -28,20 +28,20 @@ namespace Bit.Droid.Renderers
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
|
||||
if(Control == null)
|
||||
if (Control == null)
|
||||
{
|
||||
var webView = new AWebkit.WebView(_context);
|
||||
webView.Settings.JavaScriptEnabled = true;
|
||||
webView.SetWebViewClient(new JSWebViewClient(string.Format("javascript: {0}", JSFunction)));
|
||||
SetNativeControl(webView);
|
||||
}
|
||||
if(e.OldElement != null)
|
||||
if (e.OldElement != null)
|
||||
{
|
||||
Control.RemoveJavascriptInterface("jsBridge");
|
||||
var hybridWebView = e.OldElement as HybridWebView;
|
||||
hybridWebView.Cleanup();
|
||||
}
|
||||
if(e.NewElement != null)
|
||||
if (e.NewElement != null)
|
||||
{
|
||||
Control.AddJavascriptInterface(new JSBridge(this), "jsBridge");
|
||||
Control.LoadUrl(Element.Uri);
|
||||
@@ -51,7 +51,7 @@ namespace Bit.Droid.Renderers
|
||||
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnElementPropertyChanged(sender, e);
|
||||
if(e.PropertyName == HybridWebView.UriProperty.PropertyName)
|
||||
if (e.PropertyName == HybridWebView.UriProperty.PropertyName)
|
||||
{
|
||||
Control.LoadUrl(Element.Uri);
|
||||
}
|
||||
@@ -70,7 +70,7 @@ namespace Bit.Droid.Renderers
|
||||
[Export("invokeAction")]
|
||||
public void InvokeAction(string data)
|
||||
{
|
||||
if(_hybridWebViewRenderer != null &&
|
||||
if (_hybridWebViewRenderer != null &&
|
||||
_hybridWebViewRenderer.TryGetTarget(out HybridWebViewRenderer hybridRenderer))
|
||||
{
|
||||
hybridRenderer.Element.InvokeAction(data);
|
||||
|
||||
15693
src/Android/Resources/Resource.designer.cs
generated
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
BIN
src/Android/Resources/drawable/accessibility_overlay.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/Android/Resources/drawable/accessibility_permission.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 13 KiB |
13
src/Android/Resources/drawable/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="1024.4897"
|
||||
android:viewportHeight="1024.4897">
|
||||
<group android:translateX="300.2449"
|
||||
android:translateY="261.2449">
|
||||
<path
|
||||
android:pathData="M393.4,52.2C389.7,48.5 385.3,46.6 380.3,46.6L43.7,46.6C38.6,46.6 34.3,48.5 30.6,52.2C26.9,55.9 25,60.2 25,65.3L25,289.7C25,306.4 28.3,323.1 34.8,339.5C41.3,356 49.4,370.6 59.1,383.3C68.7,396.1 80.2,408.5 93.6,420.5C106.9,432.6 119.3,442.6 130.6,450.6C141.9,458.6 153.7,466.1 166,473.2C178.3,480.3 187,485.1 192.2,487.7C197.4,490.2 201.5,492.2 204.6,493.5C206.9,494.7 209.5,495.3 212.2,495.3C214.9,495.3 217.5,494.7 219.8,493.5C222.9,492.1 227.1,490.2 232.2,487.7C237.4,485.2 246.1,480.3 258.4,473.2C270.7,466.1 282.5,458.5 293.8,450.6C305.1,442.6 317.4,432.6 330.8,420.5C344.1,408.4 355.6,396 365.3,383.3C374.9,370.5 383,355.9 389.5,339.5C396,323 399.3,306.4 399.3,289.7L399.3,65.3C399,60.2 397.1,55.9 393.4,52.2ZM350,291.8C350,373 212,443 212,443L212,94.6L350,94.6L350,291.8Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillType="nonZero"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
Before Width: | Height: | Size: 941 B |
12
src/Android/Resources/drawable/icon.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="1024dp"
|
||||
android:height="1024dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M1024,864c0,88.4 -71.6,160 -160,160H160C71.6,1024 0,952.4 0,864V160C0,71.6 71.6,0 160,0h704c88.4,0 160,71.6 160,160V864z"
|
||||
android:fillColor="#175DDC"/>
|
||||
<path
|
||||
android:pathData="M829.8,128.6c-6.5,-6.5 -14.2,-9.7 -23,-9.7H217.2c-8.9,0 -16.5,3.2 -23,9.7c-6.5,6.5 -9.7,14.2 -9.7,23v393.1c0,29.3 5.7,58.4 17.1,87.3c11.4,28.8 25.6,54.4 42.5,76.8c16.9,22.3 37,44.1 60.4,65.3c23.4,21.2 45,38.7 64.7,52.7c19.8,14 40.4,27.2 61.9,39.7c21.5,12.5 36.8,20.9 45.8,25.3c9,4.4 16.3,7.9 21.7,10.2c4.1,2 8.5,3.1 13.3,3.1c4.8,0 9.2,-1 13.3,-3.1c5.5,-2.4 12.7,-5.8 21.8,-10.2c9,-4.4 24.3,-12.9 45.8,-25.3c21.5,-12.5 42.1,-25.7 61.9,-39.7c19.8,-14 41.4,-31.6 64.8,-52.7c23.4,-21.2 43.5,-42.9 60.4,-65.3c16.9,-22.4 31,-47.9 42.5,-76.8c11.4,-28.8 17.1,-57.9 17.1,-87.3V151.7C839.6,142.8 836.3,135.1 829.8,128.6zM753.8,548.4c0,142.3 -241.8,264.9 -241.8,264.9V203.1h241.8C753.8,203.1 753.8,406.1 753.8,548.4z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
</vector>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.TabLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<com.google.android.material.tabs.TabLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/sliding_tabs"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<android.support.v7.widget.Toolbar
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 941 B |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 896 B After Width: | Height: | Size: 968 B |
|
Before Width: | Height: | Size: 608 B |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.8 KiB |
@@ -1,10 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Light theme -->
|
||||
<color name="colorPrimary">#3c8dbc</color>
|
||||
<color name="colorPrimaryDark">#222d32</color>
|
||||
<color name="primary">#3c8dbc</color>
|
||||
<color name="notificationBar">#3883af</color>
|
||||
<color name="colorPrimary">#175DDC</color>
|
||||
<color name="colorPrimaryDark">#1A3B66</color>
|
||||
<color name="primary">#175DDC</color>
|
||||
<color name="notificationBar">#1452BC</color>
|
||||
<color name="border">#dddddd</color>
|
||||
|
||||
<!-- Dark theme -->
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#3C8DBC</color>
|
||||
<color name="ic_launcher_background">#175DDC</color>
|
||||
</resources>
|
||||
@@ -16,6 +16,9 @@
|
||||
<string name="PasswordGenerator">
|
||||
Password Generator
|
||||
</string>
|
||||
<string name="AutoFillTile">
|
||||
Auto-fill
|
||||
</string>
|
||||
<string name="SelfHostedServerUrl">
|
||||
Self-hosted server URL
|
||||
</string>
|
||||
|
||||
@@ -1,20 +1,72 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
These browsers work using the compatibility shim for the Autofill Framework
|
||||
(and need to be listed here as well)
|
||||
|
||||
Be sure:
|
||||
- to keep these entries sorted alphabetically,
|
||||
- to keep this list in sync with values in AutofillHelpers.CompatBrowsers, and
|
||||
|
||||
- ... to keep this list in sync with values in AccessibilityHelpers.SupportedBrowsers [Section A], too.
|
||||
-->
|
||||
<autofill-service xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<compatibility-package
|
||||
android:name="com.amazon.cloud9"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.android.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.android.chrome"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.avast.android.secure.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.avg.android.secure.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.brave.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.brave.browser_beta"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.brave.browser_default"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.brave.browser_dev"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.brave.browser_nightly"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.chrome.beta"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.chrome.dev"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.chrome.canary"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.chrome.dev"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.ecosia.android"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.google.android.apps.chrome"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.google.android.apps.chrome_dev"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.kiwibrowser.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.microsoft.emmx"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.naver.whale"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.opera.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
@@ -27,12 +79,63 @@
|
||||
<compatibility-package
|
||||
android:name="com.opera.mini.native.beta"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.opera.touch"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.qwant.liberty"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.sec.android.app.sbrowser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.sec.android.app.sbrowser.beta"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.stoutner.privacybrowser.free"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.stoutner.privacybrowser.standard"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.vivaldi.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.vivaldi.browser.snapshot"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.vivaldi.browser.sopranos"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.yandex.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="mark.via.gp"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.adblockplus.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.adblockplus.browser.beta"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.bromite.bromite"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.chromium.chrome"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.codeaurora.swe.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.gnu.icecat"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.mozilla.fenix"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.mozilla.fenix.nightly"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.mozilla.fennec_aurora"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
@@ -45,12 +148,6 @@
|
||||
<compatibility-package
|
||||
android:name="org.mozilla.firefox_beta"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.mozilla.fenix"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.mozilla.fenix.nightly"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.mozilla.reference.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
@@ -58,39 +155,9 @@
|
||||
android:name="org.mozilla.rocket"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.brave.browser"
|
||||
android:name="org.torproject.torbrowser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.google.android.apps.chrome"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.google.android.apps.chrome_dev"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.yandex.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.codeaurora.swe.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.amazon.cloud9"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="mark.via.gp"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.bromite.bromite"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.chromium.chrome"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.kiwibrowser.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.ecosia.android"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.vivaldi.browser"
|
||||
android:name="org.torproject.torbrowser_alpha"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
</autofill-service>
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
var registeredToken = await _storageService.GetAsync<string>(Constants.PushRegisteredTokenKey);
|
||||
var currentToken = await GetTokenAsync();
|
||||
if(!string.IsNullOrWhiteSpace(registeredToken) && registeredToken != currentToken)
|
||||
if (!string.IsNullOrWhiteSpace(registeredToken) && registeredToken != currentToken)
|
||||
{
|
||||
await _pushNotificationListenerService.OnRegisteredAsync(registeredToken, Device.Android);
|
||||
}
|
||||
|
||||
@@ -14,12 +14,12 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
int keySize = 256;
|
||||
IDigest digest = null;
|
||||
if(algorithm == CryptoHashAlgorithm.Sha256)
|
||||
if (algorithm == CryptoHashAlgorithm.Sha256)
|
||||
{
|
||||
keySize = 256;
|
||||
digest = new Sha256Digest();
|
||||
}
|
||||
else if(algorithm == CryptoHashAlgorithm.Sha512)
|
||||
else if (algorithm == CryptoHashAlgorithm.Sha512)
|
||||
{
|
||||
keySize = 512;
|
||||
digest = new Sha512Digest();
|
||||
|
||||
@@ -14,14 +14,14 @@ using Android.Nfc;
|
||||
using Android.OS;
|
||||
using Android.Provider;
|
||||
using Android.Runtime;
|
||||
using Android.Support.V4.App;
|
||||
using Android.Support.V4.Content;
|
||||
using Android.Text;
|
||||
using Android.Text.Method;
|
||||
using Android.Views.Autofill;
|
||||
using Android.Views.InputMethods;
|
||||
using Android.Webkit;
|
||||
using Android.Widget;
|
||||
using AndroidX.Core.App;
|
||||
using AndroidX.Core.Content;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
@@ -59,7 +59,7 @@ namespace Bit.Droid.Services
|
||||
|
||||
_broadcasterService.Subscribe(nameof(DeviceActionService), (message) =>
|
||||
{
|
||||
if(message.Command == "selectFileCameraPermissionDenied")
|
||||
if (message.Command == "selectFileCameraPermissionDenied")
|
||||
{
|
||||
_cameraPermissionsDenied = true;
|
||||
}
|
||||
@@ -70,7 +70,7 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
get
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(_userAgent))
|
||||
if (string.IsNullOrWhiteSpace(_userAgent))
|
||||
{
|
||||
_userAgent = $"Bitwarden_Mobile/{Xamarin.Essentials.AppInfo.VersionString} " +
|
||||
$"(Android {Build.VERSION.Release}; SDK {Build.VERSION.Sdk}; Model {Build.Model})";
|
||||
@@ -83,7 +83,7 @@ namespace Bit.Droid.Services
|
||||
|
||||
public void Toast(string text, bool longDuration = false)
|
||||
{
|
||||
if(_toast != null)
|
||||
if (_toast != null)
|
||||
{
|
||||
_toast.Cancel();
|
||||
_toast.Dispose();
|
||||
@@ -99,7 +99,7 @@ namespace Bit.Droid.Services
|
||||
var activity = CrossCurrentActivity.Current.Activity;
|
||||
appName = appName.Replace("androidapp://", string.Empty);
|
||||
var launchIntent = activity.PackageManager.GetLaunchIntentForPackage(appName);
|
||||
if(launchIntent != null)
|
||||
if (launchIntent != null)
|
||||
{
|
||||
activity.StartActivity(launchIntent);
|
||||
}
|
||||
@@ -108,7 +108,7 @@ namespace Bit.Droid.Services
|
||||
|
||||
public async Task ShowLoadingAsync(string text)
|
||||
{
|
||||
if(_progressDialog != null)
|
||||
if (_progressDialog != null)
|
||||
{
|
||||
await HideLoadingAsync();
|
||||
}
|
||||
@@ -121,7 +121,7 @@ namespace Bit.Droid.Services
|
||||
|
||||
public Task HideLoadingAsync()
|
||||
{
|
||||
if(_progressDialog != null)
|
||||
if (_progressDialog != null)
|
||||
{
|
||||
_progressDialog.Dismiss();
|
||||
_progressDialog.Dispose();
|
||||
@@ -136,7 +136,7 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
var intent = BuildOpenFileIntent(fileData, fileName);
|
||||
if(intent == null)
|
||||
if (intent == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -153,7 +153,7 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
var intent = BuildOpenFileIntent(new byte[0], string.Concat("opentest_", fileName));
|
||||
if(intent == null)
|
||||
if (intent == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -168,12 +168,12 @@ namespace Bit.Droid.Services
|
||||
private Intent BuildOpenFileIntent(byte[] fileData, string fileName)
|
||||
{
|
||||
var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName.Replace(' ', '_').ToLower());
|
||||
if(extension == null)
|
||||
if (extension == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
|
||||
if(mimeType == null)
|
||||
if (mimeType == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -183,7 +183,7 @@ namespace Bit.Droid.Services
|
||||
var filePath = Path.Combine(cachePath.Path, fileName);
|
||||
File.WriteAllBytes(filePath, fileData);
|
||||
var file = new Java.IO.File(cachePath, fileName);
|
||||
if(!file.IsFile)
|
||||
if (!file.IsFile)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -200,14 +200,14 @@ namespace Bit.Droid.Services
|
||||
catch { }
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public bool SaveFile(byte[] fileData, string id, string fileName, string contentUri)
|
||||
{
|
||||
try
|
||||
{
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
|
||||
if(contentUri != null)
|
||||
|
||||
if (contentUri != null)
|
||||
{
|
||||
var uri = Android.Net.Uri.Parse(contentUri);
|
||||
var stream = activity.ContentResolver.OpenOutputStream(uri);
|
||||
@@ -219,16 +219,16 @@ namespace Bit.Droid.Services
|
||||
javaStream.Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Prompt for location to save file
|
||||
var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName.Replace(' ', '_').ToLower());
|
||||
if(extension == null)
|
||||
if (extension == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
|
||||
if(mimeType == null)
|
||||
if (mimeType == null)
|
||||
{
|
||||
// Unable to identify so fall back to generic "any" type
|
||||
mimeType = "*/*";
|
||||
@@ -238,11 +238,11 @@ namespace Bit.Droid.Services
|
||||
intent.SetType(mimeType);
|
||||
intent.AddCategory(Intent.CategoryOpenable);
|
||||
intent.PutExtra(Intent.ExtraTitle, fileName);
|
||||
|
||||
|
||||
activity.StartActivityForResult(intent, Constants.SaveFileRequestCode);
|
||||
return true;
|
||||
}
|
||||
catch(Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", ex.GetType(), ex.StackTrace);
|
||||
}
|
||||
@@ -256,7 +256,7 @@ namespace Bit.Droid.Services
|
||||
DeleteDir(CrossCurrentActivity.Current.Activity.CacheDir);
|
||||
await _storageService.SaveAsync(Constants.LastFileCacheClearKey, DateTime.UtcNow);
|
||||
}
|
||||
catch(Exception) { }
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
public Task SelectFileAsync()
|
||||
@@ -265,25 +265,25 @@ namespace Bit.Droid.Services
|
||||
var hasStorageWritePermission = !_cameraPermissionsDenied &&
|
||||
HasPermission(Manifest.Permission.WriteExternalStorage);
|
||||
var additionalIntents = new List<IParcelable>();
|
||||
if(activity.PackageManager.HasSystemFeature(PackageManager.FeatureCamera))
|
||||
if (activity.PackageManager.HasSystemFeature(PackageManager.FeatureCamera))
|
||||
{
|
||||
var hasCameraPermission = !_cameraPermissionsDenied && HasPermission(Manifest.Permission.Camera);
|
||||
if(!_cameraPermissionsDenied && !hasStorageWritePermission)
|
||||
if (!_cameraPermissionsDenied && !hasStorageWritePermission)
|
||||
{
|
||||
AskPermission(Manifest.Permission.WriteExternalStorage);
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
if(!_cameraPermissionsDenied && !hasCameraPermission)
|
||||
if (!_cameraPermissionsDenied && !hasCameraPermission)
|
||||
{
|
||||
AskPermission(Manifest.Permission.Camera);
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
if(!_cameraPermissionsDenied && hasCameraPermission && hasStorageWritePermission)
|
||||
if (!_cameraPermissionsDenied && hasCameraPermission && hasStorageWritePermission)
|
||||
{
|
||||
try
|
||||
{
|
||||
var file = new Java.IO.File(activity.FilesDir, "temp_camera_photo.jpg");
|
||||
if(!file.Exists())
|
||||
if (!file.Exists())
|
||||
{
|
||||
file.ParentFile.Mkdirs();
|
||||
file.CreateNewFile();
|
||||
@@ -292,7 +292,7 @@ namespace Bit.Droid.Services
|
||||
"com.x8bit.bitwarden.fileprovider", file);
|
||||
additionalIntents.AddRange(GetCameraIntents(outputFileUri));
|
||||
}
|
||||
catch(Java.IO.IOException) { }
|
||||
catch (Java.IO.IOException) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,7 +300,7 @@ namespace Bit.Droid.Services
|
||||
docIntent.AddCategory(Intent.CategoryOpenable);
|
||||
docIntent.SetType("*/*");
|
||||
var chooserIntent = Intent.CreateChooser(docIntent, AppResources.FileSource);
|
||||
if(additionalIntents.Count > 0)
|
||||
if (additionalIntents.Count > 0)
|
||||
{
|
||||
chooserIntent.PutExtra(Intent.ExtraInitialIntents, additionalIntents.ToArray());
|
||||
}
|
||||
@@ -313,7 +313,7 @@ namespace Bit.Droid.Services
|
||||
bool numericKeyboard = false, bool autofocus = true)
|
||||
{
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
if(activity == null)
|
||||
if (activity == null)
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
}
|
||||
@@ -325,11 +325,11 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
InputType = InputTypes.ClassText
|
||||
};
|
||||
if(text == null)
|
||||
if (text == null)
|
||||
{
|
||||
text = string.Empty;
|
||||
}
|
||||
if(numericKeyboard)
|
||||
if (numericKeyboard)
|
||||
{
|
||||
input.InputType = InputTypes.ClassNumber | InputTypes.NumberFlagDecimal | InputTypes.NumberFlagSigned;
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
@@ -359,7 +359,7 @@ namespace Bit.Droid.Services
|
||||
var alert = alertBuilder.Create();
|
||||
alert.Window.SetSoftInputMode(Android.Views.SoftInput.StateVisible);
|
||||
alert.Show();
|
||||
if(autofocus)
|
||||
if (autofocus)
|
||||
{
|
||||
input.RequestFocus();
|
||||
}
|
||||
@@ -374,7 +374,7 @@ namespace Bit.Droid.Services
|
||||
var rateIntent = RateIntentForUrl("market://details", activity);
|
||||
activity.StartActivity(rateIntent);
|
||||
}
|
||||
catch(ActivityNotFoundException)
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
var rateIntent = RateIntentForUrl("https://play.google.com/store/apps/details", activity);
|
||||
activity.StartActivity(rateIntent);
|
||||
@@ -399,7 +399,7 @@ namespace Bit.Droid.Services
|
||||
|
||||
public async Task<bool> BiometricAvailableAsync()
|
||||
{
|
||||
if(UseNativeBiometric())
|
||||
if (UseNativeBiometric())
|
||||
{
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
var manager = activity.GetSystemService(Context.BiometricService) as BiometricManager;
|
||||
@@ -425,13 +425,13 @@ namespace Bit.Droid.Services
|
||||
|
||||
public Task<bool> AuthenticateBiometricAsync(string text = null)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(text))
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
text = AppResources.BiometricsDirection;
|
||||
}
|
||||
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
using(var builder = new BiometricPrompt.Builder(activity))
|
||||
using (var builder = new BiometricPrompt.Builder(activity))
|
||||
{
|
||||
builder.SetTitle(text);
|
||||
builder.SetConfirmationRequired(false);
|
||||
@@ -446,7 +446,8 @@ namespace Bit.Droid.Services
|
||||
new BiometricAuthenticationCallback
|
||||
{
|
||||
Success = authResult => result.TrySetResult(true),
|
||||
Failed = () => result.TrySetResult(false),
|
||||
Error = () => result.TrySetResult(false),
|
||||
Failed = () => { },
|
||||
Help = (helpCode, helpString) => { }
|
||||
});
|
||||
return result.Task;
|
||||
@@ -468,7 +469,7 @@ namespace Bit.Droid.Services
|
||||
|
||||
public bool SupportsAutofillService()
|
||||
{
|
||||
if(Build.VERSION.SdkInt < BuildVersionCodes.O)
|
||||
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -498,7 +499,7 @@ namespace Bit.Droid.Services
|
||||
public Task<string> DisplayAlertAsync(string title, string message, string cancel, params string[] buttons)
|
||||
{
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
if(activity == null)
|
||||
if (activity == null)
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
}
|
||||
@@ -507,11 +508,11 @@ namespace Bit.Droid.Services
|
||||
var alertBuilder = new AlertDialog.Builder(activity);
|
||||
alertBuilder.SetTitle(title);
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(message))
|
||||
if (!string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
if(buttons != null && buttons.Length > 2)
|
||||
if (buttons != null && buttons.Length > 2)
|
||||
{
|
||||
if(!string.IsNullOrWhiteSpace(title))
|
||||
if (!string.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
alertBuilder.SetTitle($"{title}: {message}");
|
||||
}
|
||||
@@ -526,9 +527,9 @@ namespace Bit.Droid.Services
|
||||
}
|
||||
}
|
||||
|
||||
if(buttons != null)
|
||||
if (buttons != null)
|
||||
{
|
||||
if(buttons.Length > 2)
|
||||
if (buttons.Length > 2)
|
||||
{
|
||||
alertBuilder.SetItems(buttons, (sender, args) =>
|
||||
{
|
||||
@@ -537,14 +538,14 @@ namespace Bit.Droid.Services
|
||||
}
|
||||
else
|
||||
{
|
||||
if(buttons.Length > 0)
|
||||
if (buttons.Length > 0)
|
||||
{
|
||||
alertBuilder.SetPositiveButton(buttons[0], (sender, args) =>
|
||||
{
|
||||
result.TrySetResult(buttons[0]);
|
||||
});
|
||||
}
|
||||
if(buttons.Length > 1)
|
||||
if (buttons.Length > 1)
|
||||
{
|
||||
alertBuilder.SetNeutralButton(buttons[1], (sender, args) =>
|
||||
{
|
||||
@@ -554,7 +555,7 @@ namespace Bit.Droid.Services
|
||||
}
|
||||
}
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(cancel))
|
||||
if (!string.IsNullOrWhiteSpace(cancel))
|
||||
{
|
||||
alertBuilder.SetNegativeButton(cancel, (sender, args) =>
|
||||
{
|
||||
@@ -568,16 +569,23 @@ namespace Bit.Droid.Services
|
||||
return result.Task;
|
||||
}
|
||||
|
||||
public async Task<string> DisplayActionSheetAsync(string title, string cancel, string destruction,
|
||||
params string[] buttons)
|
||||
{
|
||||
return await Xamarin.Forms.Application.Current.MainPage.DisplayActionSheet(
|
||||
title, cancel, destruction, buttons);
|
||||
}
|
||||
|
||||
public void Autofill(CipherView cipher)
|
||||
{
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
if(activity == null)
|
||||
if (activity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(activity.Intent.GetBooleanExtra("autofillFramework", false))
|
||||
if (activity.Intent.GetBooleanExtra("autofillFramework", false))
|
||||
{
|
||||
if(cipher == null)
|
||||
if (cipher == null)
|
||||
{
|
||||
activity.SetResult(Result.Canceled);
|
||||
activity.Finish();
|
||||
@@ -585,7 +593,7 @@ namespace Bit.Droid.Services
|
||||
}
|
||||
var structure = activity.Intent.GetParcelableExtra(
|
||||
AutofillManager.ExtraAssistStructure) as AssistStructure;
|
||||
if(structure == null)
|
||||
if (structure == null)
|
||||
{
|
||||
activity.SetResult(Result.Canceled);
|
||||
activity.Finish();
|
||||
@@ -593,7 +601,7 @@ namespace Bit.Droid.Services
|
||||
}
|
||||
var parser = new Parser(structure, activity.ApplicationContext);
|
||||
parser.Parse();
|
||||
if((!parser.FieldCollection?.Fields?.Any() ?? true) || string.IsNullOrWhiteSpace(parser.Uri))
|
||||
if ((!parser.FieldCollection?.Fields?.Any() ?? true) || string.IsNullOrWhiteSpace(parser.Uri))
|
||||
{
|
||||
activity.SetResult(Result.Canceled);
|
||||
activity.Finish();
|
||||
@@ -610,7 +618,7 @@ namespace Bit.Droid.Services
|
||||
else
|
||||
{
|
||||
var data = new Intent();
|
||||
if(cipher == null)
|
||||
if (cipher == null)
|
||||
{
|
||||
data.PutExtra("canceled", "true");
|
||||
}
|
||||
@@ -621,7 +629,7 @@ namespace Bit.Droid.Services
|
||||
data.PutExtra("username", cipher.Login.Username);
|
||||
data.PutExtra("password", cipher.Login.Password);
|
||||
}
|
||||
if(activity.Parent == null)
|
||||
if (activity.Parent == null)
|
||||
{
|
||||
activity.SetResult(Result.Ok, data);
|
||||
}
|
||||
@@ -631,7 +639,7 @@ namespace Bit.Droid.Services
|
||||
}
|
||||
activity.Finish();
|
||||
_messagingService.Send("finishMainActivity");
|
||||
if(cipher != null)
|
||||
if (cipher != null)
|
||||
{
|
||||
var eventTask = _eventServiceFunc().CollectAsync(EventType.Cipher_ClientAutofilled, cipher.Id);
|
||||
}
|
||||
@@ -646,7 +654,7 @@ namespace Bit.Droid.Services
|
||||
public void Background()
|
||||
{
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
if(activity.Intent.GetBooleanExtra("autofillFramework", false))
|
||||
if (activity.Intent.GetBooleanExtra("autofillFramework", false))
|
||||
{
|
||||
activity.SetResult(Result.Canceled);
|
||||
activity.Finish();
|
||||
@@ -687,7 +695,7 @@ namespace Bit.Droid.Services
|
||||
intent.SetData(Android.Net.Uri.Parse("package:com.x8bit.bitwarden"));
|
||||
activity.StartActivity(intent);
|
||||
}
|
||||
catch(ActivityNotFoundException)
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
// can't open overlay permission management, fall back to app settings
|
||||
var intent = new Intent(Settings.ActionApplicationDetailsSettings);
|
||||
@@ -709,7 +717,7 @@ namespace Bit.Droid.Services
|
||||
|
||||
public bool AutofillServiceEnabled()
|
||||
{
|
||||
if(Build.VERSION.SdkInt < BuildVersionCodes.O)
|
||||
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -746,7 +754,7 @@ namespace Bit.Droid.Services
|
||||
intent.SetData(Android.Net.Uri.Parse("package:com.x8bit.bitwarden"));
|
||||
activity.StartActivity(intent);
|
||||
}
|
||||
catch(ActivityNotFoundException)
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
var alertBuilder = new AlertDialog.Builder(activity);
|
||||
alertBuilder.SetMessage(AppResources.BitwardenAutofillGoToSettings);
|
||||
@@ -766,20 +774,20 @@ namespace Bit.Droid.Services
|
||||
|
||||
private bool DeleteDir(Java.IO.File dir)
|
||||
{
|
||||
if(dir != null && dir.IsDirectory)
|
||||
if (dir != null && dir.IsDirectory)
|
||||
{
|
||||
var children = dir.List();
|
||||
for(int i = 0; i < children.Length; i++)
|
||||
for (int i = 0; i < children.Length; i++)
|
||||
{
|
||||
var success = DeleteDir(new Java.IO.File(dir, children[i]));
|
||||
if(!success)
|
||||
if (!success)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return dir.Delete();
|
||||
}
|
||||
else if(dir != null && dir.IsFile)
|
||||
else if (dir != null && dir.IsFile)
|
||||
{
|
||||
return dir.Delete();
|
||||
}
|
||||
@@ -807,7 +815,7 @@ namespace Bit.Droid.Services
|
||||
var pm = CrossCurrentActivity.Current.Activity.PackageManager;
|
||||
var captureIntent = new Intent(MediaStore.ActionImageCapture);
|
||||
var listCam = pm.QueryIntentActivities(captureIntent, 0);
|
||||
foreach(var res in listCam)
|
||||
foreach (var res in listCam)
|
||||
{
|
||||
var packageName = res.ActivityInfo.PackageName;
|
||||
var intent = new Intent(captureIntent);
|
||||
@@ -823,7 +831,7 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
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)
|
||||
if ((int)Build.VERSION.SdkInt >= 21)
|
||||
{
|
||||
flags |= ActivityFlags.NewDocument;
|
||||
}
|
||||
@@ -838,16 +846,16 @@ namespace Bit.Droid.Services
|
||||
|
||||
private async Task CopyTotpAsync(CipherView cipher)
|
||||
{
|
||||
if(!string.IsNullOrWhiteSpace(cipher?.Login?.Totp))
|
||||
if (!string.IsNullOrWhiteSpace(cipher?.Login?.Totp))
|
||||
{
|
||||
var userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
var autoCopyDisabled = await _storageService.GetAsync<bool?>(Constants.DisableAutoTotpCopyKey);
|
||||
var canAccessPremium = await userService.CanAccessPremiumAsync();
|
||||
if((canAccessPremium || cipher.OrganizationUseTotp) && !autoCopyDisabled.GetValueOrDefault())
|
||||
if ((canAccessPremium || cipher.OrganizationUseTotp) && !autoCopyDisabled.GetValueOrDefault())
|
||||
{
|
||||
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||
var totp = await totpService.GetCodeAsync(cipher.Login.Totp);
|
||||
if(totp != null)
|
||||
if (totp != null)
|
||||
{
|
||||
CopyToClipboard(totp);
|
||||
}
|
||||
@@ -860,12 +868,13 @@ namespace Bit.Droid.Services
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
var clipboardManager = activity.GetSystemService(
|
||||
Context.ClipboardService) as Android.Content.ClipboardManager;
|
||||
clipboardManager.Text = text;
|
||||
clipboardManager.PrimaryClip = ClipData.NewPlainText("bitwarden", text);
|
||||
}
|
||||
|
||||
private class BiometricAuthenticationCallback : BiometricPrompt.AuthenticationCallback
|
||||
{
|
||||
public Action<BiometricPrompt.AuthenticationResult> Success { get; set; }
|
||||
public Action Error { get; set; }
|
||||
public Action Failed { get; set; }
|
||||
public Action<BiometricAcquiredStatus, Java.Lang.ICharSequence> Help { get; set; }
|
||||
|
||||
@@ -875,6 +884,12 @@ namespace Bit.Droid.Services
|
||||
Success?.Invoke(authResult);
|
||||
}
|
||||
|
||||
public override void OnAuthenticationError([GeneratedEnum] BiometricErrorCode errorCode, Java.Lang.ICharSequence errString)
|
||||
{
|
||||
base.OnAuthenticationError(errorCode, errString);
|
||||
Error?.Invoke();
|
||||
}
|
||||
|
||||
public override void OnAuthenticationFailed()
|
||||
{
|
||||
base.OnAuthenticationFailed();
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
ci = new CultureInfo(netLanguage);
|
||||
}
|
||||
catch(CultureNotFoundException e1)
|
||||
catch (CultureNotFoundException e1)
|
||||
{
|
||||
// iOS locale not valid .NET culture (eg. "en-ES" : English in Spain)
|
||||
// fallback to first characters, in this case "en"
|
||||
@@ -28,7 +28,7 @@ namespace Bit.Droid.Services
|
||||
Console.WriteLine(netLanguage + " failed, trying " + fallback + " (" + e1.Message + ")");
|
||||
ci = new CultureInfo(fallback);
|
||||
}
|
||||
catch(CultureNotFoundException e2)
|
||||
catch (CultureNotFoundException e2)
|
||||
{
|
||||
// iOS language not valid .NET culture, falling back to English
|
||||
Console.WriteLine(netLanguage + " couldn't be set, using 'en' (" + e2.Message + ")");
|
||||
@@ -42,9 +42,9 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
Console.WriteLine("Android Language:" + androidLanguage);
|
||||
var netLanguage = androidLanguage;
|
||||
if(androidLanguage.StartsWith("zh"))
|
||||
if (androidLanguage.StartsWith("zh"))
|
||||
{
|
||||
if(androidLanguage.Contains("Hant") || androidLanguage.Contains("TW") ||
|
||||
if (androidLanguage.Contains("Hant") || androidLanguage.Contains("TW") ||
|
||||
androidLanguage.Contains("HK") || androidLanguage.Contains("MO"))
|
||||
{
|
||||
netLanguage = "zh-Hant";
|
||||
@@ -54,7 +54,7 @@ namespace Bit.Droid.Services
|
||||
netLanguage = "zh-Hans";
|
||||
}
|
||||
}
|
||||
else if(androidLanguage.StartsWith("iw"))
|
||||
else if (androidLanguage.StartsWith("iw"))
|
||||
{
|
||||
// Uncomment when we support RTL
|
||||
// netLanguage = "he";
|
||||
@@ -62,7 +62,7 @@ namespace Bit.Droid.Services
|
||||
else
|
||||
{
|
||||
// Certain languages need to be converted to CultureInfo equivalent
|
||||
switch(androidLanguage)
|
||||
switch (androidLanguage)
|
||||
{
|
||||
case "ms-BN": // "Malaysian (Brunei)" not supported .NET culture
|
||||
case "ms-MY": // "Malaysian (Malaysia)" not supported .NET culture
|
||||
@@ -87,7 +87,7 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
Console.WriteLine(".NET Fallback Language:" + platCulture.LanguageCode);
|
||||
var netLanguage = platCulture.LanguageCode; // use the first part of the identifier (two chars, usually);
|
||||
switch(platCulture.LanguageCode)
|
||||
switch (platCulture.LanguageCode)
|
||||
{
|
||||
case "gsw":
|
||||
netLanguage = "de-CH"; // equivalent to German (Switzerland) for this app
|
||||
|
||||
95
src/Android/Tiles/AutofillTileService.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using Android;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Runtime;
|
||||
using Android.Service.QuickSettings;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Droid.Accessibility;
|
||||
using Java.Lang;
|
||||
|
||||
namespace Bit.Droid.Tile
|
||||
{
|
||||
[Service(Permission = Manifest.Permission.BindQuickSettingsTile, Label = "@string/AutoFillTile",
|
||||
Icon = "@drawable/shield")]
|
||||
[IntentFilter(new string[] { ActionQsTile })]
|
||||
[Register("com.x8bit.bitwarden.AutofillTileService")]
|
||||
public class AutofillTileService : TileService
|
||||
{
|
||||
private IStorageService _storageService;
|
||||
|
||||
public override void OnTileAdded()
|
||||
{
|
||||
base.OnTileAdded();
|
||||
SetTileAdded(true);
|
||||
}
|
||||
|
||||
public override void OnStartListening()
|
||||
{
|
||||
base.OnStartListening();
|
||||
}
|
||||
|
||||
public override void OnStopListening()
|
||||
{
|
||||
base.OnStopListening();
|
||||
}
|
||||
|
||||
public override void OnTileRemoved()
|
||||
{
|
||||
base.OnTileRemoved();
|
||||
SetTileAdded(false);
|
||||
}
|
||||
|
||||
public override void OnClick()
|
||||
{
|
||||
base.OnClick();
|
||||
|
||||
if (IsLocked)
|
||||
{
|
||||
UnlockAndRun(new Runnable(ScanAndFill));
|
||||
}
|
||||
else
|
||||
{
|
||||
ScanAndFill();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetTileAdded(bool isAdded)
|
||||
{
|
||||
AccessibilityHelpers.IsAutofillTileAdded = isAdded;
|
||||
if (_storageService == null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
}
|
||||
_storageService.SaveAsync(Constants.AutofillTileAdded, isAdded);
|
||||
}
|
||||
|
||||
private void ScanAndFill()
|
||||
{
|
||||
if (!AccessibilityHelpers.IsAccessibilityBroadcastReady)
|
||||
{
|
||||
ShowConfigErrorDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
var intent = new Intent(this, typeof(AccessibilityActivity));
|
||||
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
|
||||
intent.PutExtra("autofillTileClicked", true);
|
||||
StartActivityAndCollapse(intent);
|
||||
}
|
||||
|
||||
private void ShowConfigErrorDialog()
|
||||
{
|
||||
var alertBuilder = new AlertDialog.Builder(this);
|
||||
alertBuilder.SetMessage(AppResources.AutofillTileAccessibilityRequired);
|
||||
alertBuilder.SetCancelable(true);
|
||||
alertBuilder.SetPositiveButton(AppResources.Ok, (sender, args) =>
|
||||
{
|
||||
(sender as AlertDialog)?.Cancel();
|
||||
});
|
||||
ShowDialog(alertBuilder.Create());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ namespace Bit.Droid.Tile
|
||||
{
|
||||
base.OnClick();
|
||||
|
||||
if(IsLocked)
|
||||
if (IsLocked)
|
||||
{
|
||||
UnlockAndRun(new Runnable(() =>
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Bit.Droid.Tile
|
||||
{
|
||||
base.OnClick();
|
||||
|
||||
if(IsLocked)
|
||||
if (IsLocked)
|
||||
{
|
||||
UnlockAndRun(new Runnable(() =>
|
||||
{
|
||||
|
||||
@@ -15,11 +15,11 @@ namespace Bit.Droid.Utilities
|
||||
string name = null;
|
||||
string[] projection = { MediaStore.MediaColumns.DisplayName };
|
||||
var metaCursor = context.ContentResolver.Query(uri, projection, null, null, null);
|
||||
if(metaCursor != null)
|
||||
if (metaCursor != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(metaCursor.MoveToFirst())
|
||||
if (metaCursor.MoveToFirst())
|
||||
{
|
||||
name = metaCursor.GetString(0);
|
||||
}
|
||||
@@ -37,12 +37,12 @@ namespace Bit.Droid.Utilities
|
||||
var restrictionsManager = (RestrictionsManager)context.GetSystemService(Context.RestrictionsService);
|
||||
var restrictions = restrictionsManager.ApplicationRestrictions;
|
||||
var dict = new Dictionary<string, string>();
|
||||
if(restrictions.ContainsKey(BaseEnvironmentUrlRestrictionKey))
|
||||
if (restrictions.ContainsKey(BaseEnvironmentUrlRestrictionKey))
|
||||
{
|
||||
dict.Add(BaseEnvironmentUrlRestrictionKey, restrictions.GetString(BaseEnvironmentUrlRestrictionKey));
|
||||
}
|
||||
|
||||
if(dict.Count > 0)
|
||||
if (dict.Count > 0)
|
||||
{
|
||||
await AppHelpers.SetPreconfiguredSettingsAsync(dict);
|
||||
}
|
||||
|
||||
58
src/Android/Utilities/AppCenterHelper.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
#if !FDROID
|
||||
using Bit.Core.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AppCenter;
|
||||
using Microsoft.AppCenter.Crashes;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Droid.Utilities
|
||||
{
|
||||
public class AppCenterHelper
|
||||
{
|
||||
private const string AppSecret = "d3834185-b4a6-4347-9047-b86c65293d42";
|
||||
|
||||
private readonly IAppIdService _appIdService;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
private string _userId;
|
||||
private string _appId;
|
||||
|
||||
public AppCenterHelper(
|
||||
IAppIdService appIdService,
|
||||
IUserService userService)
|
||||
{
|
||||
_appIdService = appIdService;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
_userId = await _userService.GetUserIdAsync();
|
||||
_appId = await _appIdService.GetAppIdAsync();
|
||||
|
||||
AppCenter.Start(AppSecret, typeof(Crashes));
|
||||
AppCenter.SetUserId(_userId);
|
||||
|
||||
Crashes.GetErrorAttachments = (ErrorReport report) =>
|
||||
{
|
||||
return new ErrorAttachmentLog[]
|
||||
{
|
||||
ErrorAttachmentLog.AttachmentWithText(Description, "crshdesc.txt"),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
public string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
return JsonConvert.SerializeObject(new
|
||||
{
|
||||
AppId = _appId,
|
||||
UserId = _userId
|
||||
}, Formatting.Indented);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||