diff --git a/src/App/Abstractions/Services/IAppSettingsService.cs b/src/App/Abstractions/Services/IAppSettingsService.cs
index 77b5736bc..f10fe8a5f 100644
--- a/src/App/Abstractions/Services/IAppSettingsService.cs
+++ b/src/App/Abstractions/Services/IAppSettingsService.cs
@@ -15,5 +15,6 @@ namespace Bit.App.Abstractions
string WebVaultUrl { get; set; }
string ApiUrl { get; set; }
string IdentityUrl { get; set; }
+ string IconsUrl { get; set; }
}
}
\ No newline at end of file
diff --git a/src/App/Constants.cs b/src/App/Constants.cs
index 2cbbe8886..315920f05 100644
--- a/src/App/Constants.cs
+++ b/src/App/Constants.cs
@@ -40,6 +40,7 @@
public const string WebVaultUrl = "other:webVaultUrl";
public const string ApiUrl = "other:apiUrl";
public const string IdentityUrl = "other:identityUrl";
+ public const string IconsUrl = "other:iconsUrl";
public const int SelectFileRequestCode = 42;
public const int SelectFilePermissionRequestCode = 43;
diff --git a/src/App/Models/Page/VaultListPageModel.cs b/src/App/Models/Page/VaultListPageModel.cs
index f221a43f3..122d54bf7 100644
--- a/src/App/Models/Page/VaultListPageModel.cs
+++ b/src/App/Models/Page/VaultListPageModel.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using Bit.App.Resources;
using System.Linq;
using Bit.App.Enums;
+using Bit.App.Abstractions;
namespace Bit.App.Models.Page
{
@@ -10,7 +11,7 @@ namespace Bit.App.Models.Page
{
public class Cipher
{
- public Cipher(Models.Cipher cipher, bool imageEnabled)
+ public Cipher(Models.Cipher cipher, IAppSettingsService appSettings)
{
Id = cipher.Id;
Shared = !string.IsNullOrWhiteSpace(cipher.OrganizationId);
@@ -30,6 +31,7 @@ namespace Bit.App.Models.Page
Icon = "login.png";
var hostnameUri = LoginUri;
var isWebsite = false;
+ var imageEnabled = !appSettings.DisableWebsiteIcons;
if(hostnameUri.StartsWith("androidapp://"))
{
Icon = "android.png";
@@ -50,7 +52,20 @@ namespace Bit.App.Models.Page
if(imageEnabled && isWebsite && Uri.TryCreate(hostnameUri, UriKind.Absolute, out Uri u))
{
- Icon = "https://icons.bitwarden.com/" + u.Host + "/icon.png";
+ var iconsUrl = appSettings.IconsUrl;
+ if(string.IsNullOrWhiteSpace(iconsUrl))
+ {
+ if(!string.IsNullOrWhiteSpace(appSettings.BaseUrl))
+ {
+ iconsUrl = $"{appSettings.BaseUrl}/icons";
+ }
+ else
+ {
+ iconsUrl = "https://icons.bitwarden.com";
+ }
+ }
+
+ Icon = $"{iconsUrl}/{u.Host}/icon.png";
}
Subtitle = LoginUsername;
@@ -122,8 +137,8 @@ namespace Bit.App.Models.Page
public class AutofillCipher : Cipher
{
- public AutofillCipher(Models.Cipher cipher, bool imageEnabled, bool fuzzy = false)
- : base(cipher, imageEnabled)
+ public AutofillCipher(Models.Cipher cipher, IAppSettingsService appSettings, bool fuzzy = false)
+ : base(cipher, appSettings)
{
Fuzzy = fuzzy;
}
diff --git a/src/App/Pages/EnvironmentPage.cs b/src/App/Pages/EnvironmentPage.cs
index bd39de2a8..d7780e699 100644
--- a/src/App/Pages/EnvironmentPage.cs
+++ b/src/App/Pages/EnvironmentPage.cs
@@ -29,6 +29,7 @@ namespace Bit.App.Pages
public FormEntryCell WebVaultUrlCell { get; set; }
public FormEntryCell ApiUrlCell { get; set; }
public FormEntryCell IdentityUrlCell { get; set; }
+ public FormEntryCell IconsUrlCell { get; set; }
public StackLayout StackLayout { get; set; }
public Label SelfHostLabel { get; set; }
public Label CustomLabel { get; set; }
@@ -37,13 +38,23 @@ namespace Bit.App.Pages
{
MessagingCenter.Send(Application.Current, "ShowStatusBar", true);
- IdentityUrlCell = new FormEntryCell(AppResources.IdentityUrl, entryKeyboard: Keyboard.Url);
+ IconsUrlCell = new FormEntryCell(AppResources.IconsUrl, entryKeyboard: Keyboard.Url);
+ IconsUrlCell.Entry.Text = _appSettings.IconsUrl;
+
+ IdentityUrlCell = new FormEntryCell(AppResources.IdentityUrl, nextElement: IconsUrlCell.Entry,
+ entryKeyboard: Keyboard.Url);
IdentityUrlCell.Entry.Text = _appSettings.IdentityUrl;
- ApiUrlCell = new FormEntryCell(AppResources.ApiUrl, nextElement: IdentityUrlCell.Entry, entryKeyboard: Keyboard.Url);
+
+ ApiUrlCell = new FormEntryCell(AppResources.ApiUrl, nextElement: IdentityUrlCell.Entry,
+ entryKeyboard: Keyboard.Url);
ApiUrlCell.Entry.Text = _appSettings.ApiUrl;
- WebVaultUrlCell = new FormEntryCell(AppResources.WebVaultUrl, nextElement: ApiUrlCell.Entry, entryKeyboard: Keyboard.Url);
+
+ WebVaultUrlCell = new FormEntryCell(AppResources.WebVaultUrl, nextElement: ApiUrlCell.Entry,
+ entryKeyboard: Keyboard.Url);
WebVaultUrlCell.Entry.Text = _appSettings.WebVaultUrl;
- BaseUrlCell = new FormEntryCell(AppResources.ServerUrl, nextElement: WebVaultUrlCell.Entry, entryKeyboard: Keyboard.Url);
+
+ BaseUrlCell = new FormEntryCell(AppResources.ServerUrl, nextElement: WebVaultUrlCell.Entry,
+ entryKeyboard: Keyboard.Url);
BaseUrlCell.Entry.Text = _appSettings.BaseUrl;
var table = new FormTableView
@@ -74,7 +85,8 @@ namespace Bit.App.Pages
{
WebVaultUrlCell,
ApiUrlCell,
- IdentityUrlCell
+ IdentityUrlCell,
+ IconsUrlCell
}
}
};
@@ -122,6 +134,7 @@ namespace Bit.App.Pages
base.OnAppearing();
MessagingCenter.Send(Application.Current, "ShowStatusBar", true);
BaseUrlCell.InitEvents();
+ IconsUrlCell.InitEvents();
IdentityUrlCell.InitEvents();
ApiUrlCell.InitEvents();
WebVaultUrlCell.InitEvents();
@@ -132,6 +145,7 @@ namespace Bit.App.Pages
{
base.OnDisappearing();
BaseUrlCell.Dispose();
+ IconsUrlCell.Dispose();
IdentityUrlCell.Dispose();
ApiUrlCell.Dispose();
WebVaultUrlCell.Dispose();
@@ -204,7 +218,22 @@ namespace Bit.App.Pages
IdentityUrlCell.Entry.Text = null;
}
+ if(!string.IsNullOrWhiteSpace(IconsUrlCell.Entry.Text))
+ {
+ IconsUrlCell.Entry.Text = FixUrl(IconsUrlCell.Entry.Text);
+ if(!Uri.TryCreate(IconsUrlCell.Entry.Text, UriKind.Absolute, out result))
+ {
+ _userDialogs.Alert(string.Format(AppResources.FormattedIncorrectly, AppResources.IconsUrl));
+ return;
+ }
+ }
+ else
+ {
+ IconsUrlCell.Entry.Text = null;
+ }
+
_appSettings.BaseUrl = BaseUrlCell.Entry.Text;
+ _appSettings.IconsUrl = IconsUrlCell.Entry.Text;
_appSettings.IdentityUrl = IdentityUrlCell.Entry.Text;
_appSettings.ApiUrl = ApiUrlCell.Entry.Text;
_appSettings.WebVaultUrl = WebVaultUrlCell.Entry.Text;
diff --git a/src/App/Pages/Vault/VaultAutofillListCiphersPage.cs b/src/App/Pages/Vault/VaultAutofillListCiphersPage.cs
index c9c211247..abb8e98a3 100644
--- a/src/App/Pages/Vault/VaultAutofillListCiphersPage.cs
+++ b/src/App/Pages/Vault/VaultAutofillListCiphersPage.cs
@@ -161,7 +161,6 @@ namespace Bit.App.Pages
{
var cts = new CancellationTokenSource();
_filterResultsCancellationTokenSource?.Cancel();
- var websiteIconsEnabled = !_appSettingsService.DisableWebsiteIcons;
Task.Run(async () =>
{
@@ -169,7 +168,7 @@ namespace Bit.App.Pages
var ciphers = await _cipherService.GetAllAsync(Uri);
var normalLogins = ciphers?.Item1.Select(l => new VaultListPageModel.AutofillCipher(
- l, websiteIconsEnabled, false))
+ l, _appSettingsService, false))
.OrderBy(s => s.Name)
.ThenBy(s => s.Subtitle)
.ToList();
@@ -179,7 +178,7 @@ namespace Bit.App.Pages
}
var fuzzyLogins = ciphers?.Item2.Select(l => new VaultListPageModel.AutofillCipher(
- l, websiteIconsEnabled, true))
+ l, _appSettingsService, true))
.OrderBy(s => s.Name)
.ThenBy(s => s.LoginUsername)
.ToList();
diff --git a/src/App/Pages/Vault/VaultListCiphersPage.cs b/src/App/Pages/Vault/VaultListCiphersPage.cs
index f38f39ff4..1e045dbfa 100644
--- a/src/App/Pages/Vault/VaultListCiphersPage.cs
+++ b/src/App/Pages/Vault/VaultListCiphersPage.cs
@@ -311,7 +311,6 @@ namespace Bit.App.Pages
}
_filterResultsCancellationTokenSource?.Cancel();
- var websiteIconsEnabled = !_appSettingsService.DisableWebsiteIcons;
Task.Run(async () =>
{
@@ -328,7 +327,7 @@ namespace Bit.App.Pages
.ToArray();
Ciphers = ciphers
- .Select(s => new VaultListPageModel.Cipher(s, websiteIconsEnabled))
+ .Select(s => new VaultListPageModel.Cipher(s, _appSettingsService))
.OrderBy(s => s.Name)
.ThenBy(s => s.Subtitle)
.ToArray();
diff --git a/src/App/Resources/AppResources.Designer.cs b/src/App/Resources/AppResources.Designer.cs
index 158fa30e0..180d3cf2d 100644
--- a/src/App/Resources/AppResources.Designer.cs
+++ b/src/App/Resources/AppResources.Designer.cs
@@ -1456,6 +1456,15 @@ namespace Bit.App.Resources {
}
}
+ ///
+ /// Looks up a localized string similar to Icons Server URL.
+ ///
+ public static string IconsUrl {
+ get {
+ return ResourceManager.GetString("IconsUrl", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Identity Server URL.
///
diff --git a/src/App/Resources/AppResources.resx b/src/App/Resources/AppResources.resx
index 3bb41be62..c3bbb9d9e 100644
--- a/src/App/Resources/AppResources.resx
+++ b/src/App/Resources/AppResources.resx
@@ -1182,4 +1182,7 @@
Website Icons provide a recognizable image next to each login item in your vault.
+
+ Icons Server URL
+
\ No newline at end of file
diff --git a/src/App/Services/AppSettingsService.cs b/src/App/Services/AppSettingsService.cs
index ec686c3a8..be2bc2f01 100644
--- a/src/App/Services/AppSettingsService.cs
+++ b/src/App/Services/AppSettingsService.cs
@@ -169,5 +169,20 @@ namespace Bit.App.Services
_settings.AddOrUpdateValue(Constants.IdentityUrl, value);
}
}
+
+ public string IconsUrl
+ {
+ get => _settings.GetValueOrDefault(Constants.IconsUrl, null);
+ set
+ {
+ if(value == null)
+ {
+ _settings.Remove(Constants.IconsUrl);
+ return;
+ }
+
+ _settings.AddOrUpdateValue(Constants.IconsUrl, value);
+ }
+ }
}
}