1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-05 23:53:33 +00:00

Compare commits

...

49 Commits

Author SHA1 Message Date
Kyle Spearrin
59d5314164 New Crowdin translations (#639)
* New translations AppResources.resx (Afrikaans)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Italian)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Korean)

* New translations AppResources.resx (Norwegian Bokmal)

* New translations AppResources.resx (Persian)

* New translations AppResources.resx (Polish)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Romanian)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Slovak)

* New translations AppResources.resx (Spanish)

* New translations AppResources.resx (Swedish)

* New translations AppResources.resx (Thai)

* New translations AppResources.resx (Turkish)

* New translations AppResources.resx (Ukrainian)

* New translations AppResources.resx (Indonesian)

* New translations AppResources.resx (Hindi)

* New translations AppResources.resx (Bulgarian)

* New translations AppResources.resx (Danish)

* New translations AppResources.resx (Catalan)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Chinese Traditional)

* New translations AppResources.resx (Croatian)

* New translations AppResources.resx (Czech)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Hebrew)

* New translations AppResources.resx (English, United Kingdom)

* New translations AppResources.resx (Estonian)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (French)

* New translations AppResources.resx (German)

* New translations AppResources.resx (Vietnamese)
2019-10-23 20:55:51 -04:00
Kyle Spearrin
9c08a37772 UseNativeBiometric only for SDK 29 2019-10-23 11:54:53 -04:00
Kyle Spearrin
b13f5356fe FingerprintManager to detect fingerprints on SDK 28 2019-10-23 11:26:00 -04:00
Kyle Spearrin
5f0c9725ce bump version 2019-10-23 09:27:31 -04:00
Kyle Spearrin
f951fea555 use bio strings for native android bio 2019-10-23 09:24:34 -04:00
Kyle Spearrin
4b989b01e9 use native biomatrics on Android 2019-10-23 09:11:48 -04:00
Kyle Spearrin
aed3ec5474 check authed and unlocked before trying to load 2019-10-22 16:42:05 -04:00
Kyle Spearrin
b354986199 null check apiexception error 2019-10-22 16:37:40 -04:00
Kyle Spearrin
e1983a7d66 fix error when login token expires 2019-10-22 16:30:28 -04:00
Kyle Spearrin
0400d79f43 android 10 and bio permission 2019-10-18 20:41:04 -04:00
Kyle Spearrin
c911484632 upgrade builds to vs 2019 2019-10-18 14:21:07 -04:00
Kyle Spearrin
713e441d2e upgrade to android 10 sdk 2019-10-18 14:19:56 -04:00
Kyle Spearrin
d4b577732b npm audit fix 2019-10-17 08:01:50 -04:00
Kyle Spearrin
440a410d7f skip com.treydev.pns 2019-10-17 08:00:58 -04:00
Kyle Spearrin
37a536b138 catch thrown sync errors from ui 2019-10-15 11:05:56 -04:00
Kyle Spearrin
a0aca3e837 add tf browser 2019-10-11 09:29:33 -04:00
Kyle Spearrin
b58c29111a bump version 2019-10-07 09:42:55 -04:00
Kyle Spearrin
b0f86ea161 focus search bar on appear 2019-10-07 09:23:41 -04:00
Kyle Spearrin
93b59a75a4 remove migration code 2019-10-05 21:36:47 -04:00
Kyle Spearrin
54fcabaea6 shorter delays 2019-10-05 21:31:55 -04:00
Kyle Spearrin
0e966c0304 fix min character assignments for pw gen 2019-10-05 20:39:42 -04:00
Kyle Spearrin
a363712127 use black text for search bar on light theme 2019-10-04 09:32:59 -04:00
Kyle Spearrin
4d8c665917 fix light theme 2019-10-04 09:11:14 -04:00
kspearrin
53bdd92e72 support dark theme default on extensions 2019-09-30 21:40:05 -04:00
Kyle Spearrin
e51aa39ede New Crowdin translations (#614)
* New translations AppResources.resx (Bulgarian)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Portuguese, Brazilian)

* New translations AppResources.resx (Korean)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Czech)

* New translations AppResources.resx (Croatian)

* New translations copy.resx (German)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (Hungarian)

* New translations AppResources.resx (Chinese Simplified)

* New translations AppResources.resx (Dutch)

* New translations AppResources.resx (Bulgarian)

* New translations AppResources.resx (Dutch)

* New translations copy.resx (Dutch)

* New translations copy.resx (Dutch)
2019-09-30 21:35:24 -04:00
Kyle Spearrin
33c82129ff bump version 2019-09-30 21:19:20 -04:00
kspearrin
7c5b8c0e9f modal full screen 2019-09-30 21:17:53 -04:00
kspearrin
9dc01bca1c detect dark mode theme. set modal to full screen 2019-09-30 20:38:22 -04:00
Kyle Spearrin
3c7920b84c XF 3.6 update 2019-09-30 20:33:54 -04:00
Kyle Spearrin
b92f3abbaf support dark theme logos 2019-09-30 16:52:20 -04:00
Kyle Spearrin
b6747a63ed stub out support for dark theme by default 2019-09-30 16:41:31 -04:00
Kyle Spearrin
41a44548d2 use TextColor for search bar text color 2019-09-30 16:33:53 -04:00
Kyle Spearrin
a79d3a0d7c uisearchbar tint color 2019-09-30 16:28:07 -04:00
kspearrin
f3a17709e5 get proper hex string from nsdata token 2019-09-30 16:24:35 -04:00
Kyle Spearrin
ced9d33d2e memory stored pinProtectedKey 2019-09-20 16:43:03 -04:00
Kyle Spearrin
23b1373f80 add tag to entitlement 2019-09-20 08:17:37 -04:00
Kyle Spearrin
a80eb1f533 bump version 2019-09-20 07:56:49 -04:00
Kyle Spearrin
f657edf195 add support for vivaldi browser. resolves #599 2019-09-10 17:21:11 -04:00
Kyle Spearrin
d34279dca5 more null checks on add/edit save 2019-09-06 10:03:11 -04:00
Kyle Spearrin
954aa1112a added locale names dictionary 2019-09-06 09:44:25 -04:00
Kyle Spearrin
b35a3339cb device type should be int 2019-09-06 09:44:25 -04:00
Kyle Spearrin
b59433debd New Crowdin translations (#594)
* New translations copy.resx (Bulgarian)

* New translations copy.resx (Bulgarian)

* New translations AppResources.resx (Finnish)

* New translations AppResources.resx (French)

* New translations AppResources.resx (German)

* New translations AppResources.resx (Japanese)

* New translations AppResources.resx (Norwegian Bokmal)

* New translations AppResources.resx (Portuguese)

* New translations AppResources.resx (Romanian)

* New translations AppResources.resx (Russian)

* New translations AppResources.resx (Turkish)
2019-09-04 13:10:18 -04:00
Kyle Spearrin
fb2db9c652 version bump 2019-09-04 13:02:00 -04:00
Kyle Spearrin
2507f3301b device user agent 2019-09-04 11:52:32 -04:00
Kyle Spearrin
bdad5e4f0a fixes to opening file types on android 2019-08-30 16:43:58 -04:00
Kyle Spearrin
b5dcdc74d7 dark keyboard on dark themes. resolves #588 2019-08-28 20:27:15 -04:00
Kyle Spearrin
e2d1da02d3 more null checking on save 2019-08-27 15:03:33 -04:00
Kyle Spearrin
8253f18312 null check cipher service 2019-08-27 14:55:15 -04:00
kenjirooo
f4a98a2031 Added support for Sleipnir Mobile (https://play.google.com/store/apps/details?id=jp.co.fenrir.android.sleipnir). (#583) 2019-08-22 08:00:03 -04:00
127 changed files with 9013 additions and 9112 deletions

View File

@@ -1,5 +1,5 @@
image:
- Visual Studio 2017
- Visual Studio 2019
- Ubuntu1804
branches:

8
package-lock.json generated
View File

@@ -1,5 +1,5 @@
{
"name": "bitwarden-fdroid",
"name": "bitwarden-mobile",
"version": "0.0.0",
"lockfileVersion": 1,
"requires": true,
@@ -196,9 +196,9 @@
}
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
"dev": true
},
"minimatch": {

View File

@@ -63,6 +63,11 @@ namespace Bit.Droid.Accessibility
new Browser("com.kiwibrowser.browser", "url_bar"),
new Browser("com.ecosia.android", "url_bar"),
new Browser("com.qwant.liberty", "url_bar_title"),
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"),
}.ToDictionary(n => n.PackageName);
// Known packages to skip
@@ -82,13 +87,18 @@ namespace Bit.Droid.Accessibility
"com.teslacoilsw.launcher.prime",
"is.shortcut",
"me.craftsapp.nlauncher",
"com.ss.squarehome2"
"com.ss.squarehome2",
"com.treydev.pns"
};
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)
{
System.Diagnostics.Debug.WriteLine("Node: {0} = {1}", node.id, node.text);
}
}
public static string GetUri(AccessibilityNodeInfo root)

View File

@@ -16,7 +16,7 @@
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v9.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v10.0</TargetFrameworkVersion>
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
@@ -116,7 +116,6 @@
<Compile Include="Effects\FixedSizeEffect.cs" />
<Compile Include="Effects\SelectableLabelEffect.cs" />
<Compile Include="Effects\TabBarEffect.cs" />
<Compile Include="Migration\AndroidKeyStoreStorageService.cs" />
<Compile Include="Push\FirebaseInstanceIdService.cs" />
<Compile Include="Push\FirebaseMessagingService.cs" />
<Compile Include="Receivers\ClearClipboardAlarmReceiver.cs" />

View File

@@ -60,6 +60,7 @@ namespace Bit.Droid.Autofill
"org.mozilla.fenix.nightly",
"org.mozilla.reference.browser",
"org.mozilla.rocket",
"com.vivaldi.browser",
};
// The URLs are blacklisted from autofilling

View File

@@ -40,12 +40,8 @@ namespace Bit.Droid
if(ServiceContainer.RegisteredServices.Count == 0)
{
RegisterLocalServices();
ServiceContainer.Init();
if(App.Migration.MigrationHelpers.NeedsMigration())
{
var task = App.Migration.MigrationHelpers.PerformMigrationAsync();
Task.Delay(2000).Wait();
}
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
ServiceContainer.Init(deviceActionService.DeviceUserAgent);
}
#if !FDROID
if(Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
@@ -73,12 +69,6 @@ namespace Bit.Droid
private void RegisterLocalServices()
{
ServiceContainer.Register<ILogService>("logService", new AndroidLogService());
ServiceContainer.Register("settingsShim", new App.Migration.SettingsShim());
if(App.Migration.MigrationHelpers.NeedsMigration())
{
ServiceContainer.Register<App.Migration.Abstractions.IOldSecureStorageService>(
"oldSecureStorageService", new Migration.AndroidKeyStoreStorageService());
}
Refractored.FabControl.Droid.FloatingActionButtonViewRenderer.Init();
// Note: This might cause a race condition. Investigate more.
@@ -155,4 +145,4 @@ namespace Bit.Droid
await ServiceContainer.Resolve<IEnvironmentService>("environmentService").SetUrlsFromStorageAsync();
}
}
}
}

View File

@@ -1,373 +0,0 @@
using Java.Security;
using Javax.Crypto;
using Android.OS;
using Bit.App.Abstractions;
using System;
using Android.Security;
using Javax.Security.Auth.X500;
using Java.Math;
using Android.Security.Keystore;
using Android.App;
using Java.Util;
using Javax.Crypto.Spec;
using Android.Preferences;
using Bit.App.Migration;
using Bit.Core.Utilities;
using Bit.App.Migration.Abstractions;
namespace Bit.Droid.Migration
{
public class AndroidKeyStoreStorageService : IOldSecureStorageService
{
private const string AndroidKeyStore = "AndroidKeyStore";
private const string AesMode = "AES/GCM/NoPadding";
private const string KeyAlias = "bitwardenKey2";
private const string KeyAliasV1 = "bitwardenKey";
private const string SettingsFormat = "ksSecured2:{0}";
private const string SettingsFormatV1 = "ksSecured:{0}";
private const string AesKey = "ksSecured2:aesKeyForService";
private const string AesKeyV1 = "ksSecured:aesKeyForService";
private readonly string _rsaMode;
private readonly bool _oldAndroid;
private readonly SettingsShim _settings;
private readonly KeyStore _keyStore;
public AndroidKeyStoreStorageService()
{
_oldAndroid = Build.VERSION.SdkInt < BuildVersionCodes.M;
_rsaMode = _oldAndroid ? "RSA/ECB/PKCS1Padding" : "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
_settings = ServiceContainer.Resolve<SettingsShim>("settingsShim");
_keyStore = KeyStore.GetInstance(AndroidKeyStore);
_keyStore.Load(null);
/*
try
{
GenerateStoreKey(true);
}
catch
{
GenerateStoreKey(false);
}
GenerateAesKey();
*/
}
public bool Contains(string key)
{
return _settings.Contains(string.Format(SettingsFormat, key)) ||
_settings.Contains(string.Format(SettingsFormatV1, key));
}
public void Delete(string key)
{
CleanupOld(key);
var formattedKey = string.Format(SettingsFormat, key);
if(_settings.Contains(formattedKey))
{
_settings.Remove(formattedKey);
}
}
public byte[] Retrieve(string key)
{
var formattedKey = string.Format(SettingsFormat, key);
if(!_settings.Contains(formattedKey))
{
return TryGetAndMigrate(key);
}
var cs = _settings.GetValueOrDefault(formattedKey, null);
if(string.IsNullOrWhiteSpace(cs))
{
return null;
}
var aesKey = GetAesKey();
if(aesKey == null)
{
return null;
}
try
{
return App.Migration.Crypto.AesCbcDecrypt(new App.Migration.Models.CipherString(cs), aesKey);
}
catch
{
Console.WriteLine("Failed to decrypt from secure storage.");
_settings.Remove(formattedKey);
//Utilities.SendCrashEmail(e);
//Utilities.SaveCrashFile(e);
return null;
}
}
public void Store(string key, byte[] dataBytes)
{
var formattedKey = string.Format(SettingsFormat, key);
CleanupOld(key);
if(dataBytes == null)
{
_settings.Remove(formattedKey);
return;
}
var aesKey = GetAesKey();
if(aesKey == null)
{
return;
}
try
{
var cipherString = App.Migration.Crypto.AesCbcEncrypt(dataBytes, aesKey);
_settings.AddOrUpdateValue(formattedKey, cipherString.EncryptedString);
}
catch
{
Console.WriteLine("Failed to encrypt to secure storage.");
//Utilities.SendCrashEmail(e);
//Utilities.SaveCrashFile(e);
}
}
private void GenerateStoreKey(bool withDate)
{
if(_keyStore.ContainsAlias(KeyAlias))
{
return;
}
ClearSettings();
var end = Calendar.Instance;
end.Add(CalendarField.Year, 99);
if(_oldAndroid)
{
var subject = new X500Principal($"CN={KeyAlias}");
var builder = new KeyPairGeneratorSpec.Builder(Application.Context)
.SetAlias(KeyAlias)
.SetSubject(subject)
.SetSerialNumber(BigInteger.Ten);
if(withDate)
{
builder.SetStartDate(new Date(0)).SetEndDate(end.Time);
}
var spec = builder.Build();
var gen = KeyPairGenerator.GetInstance(KeyProperties.KeyAlgorithmRsa, AndroidKeyStore);
gen.Initialize(spec);
gen.GenerateKeyPair();
}
else
{
var builder = new KeyGenParameterSpec.Builder(KeyAlias, KeyStorePurpose.Decrypt | KeyStorePurpose.Encrypt)
.SetBlockModes(KeyProperties.BlockModeGcm)
.SetEncryptionPaddings(KeyProperties.EncryptionPaddingNone);
if(withDate)
{
builder.SetKeyValidityStart(new Date(0)).SetKeyValidityEnd(end.Time);
}
var spec = builder.Build();
var gen = KeyGenerator.GetInstance(KeyProperties.KeyAlgorithmAes, AndroidKeyStore);
gen.Init(spec);
gen.GenerateKey();
}
}
private KeyStore.PrivateKeyEntry GetRsaKeyEntry(string alias)
{
return _keyStore.GetEntry(alias, null) as KeyStore.PrivateKeyEntry;
}
private void GenerateAesKey()
{
if(_settings.Contains(AesKey))
{
return;
}
var key = App.Migration.Crypto.RandomBytes(512 / 8);
var encKey = _oldAndroid ? RsaEncrypt(key) : AesEncrypt(key);
_settings.AddOrUpdateValue(AesKey, encKey);
}
private App.Migration.Models.SymmetricCryptoKey GetAesKey(bool v1 = false)
{
try
{
var aesKey = v1 ? AesKeyV1 : AesKey;
if(!_settings.Contains(aesKey))
{
return null;
}
var encKey = _settings.GetValueOrDefault(aesKey, null);
if(string.IsNullOrWhiteSpace(encKey))
{
return null;
}
if(_oldAndroid || v1)
{
var encKeyBytes = Convert.FromBase64String(encKey);
var key = RsaDecrypt(encKeyBytes, v1);
return new App.Migration.Models.SymmetricCryptoKey(key);
}
else
{
var parts = encKey.Split('|');
if(parts.Length < 2)
{
return null;
}
var ivBytes = Convert.FromBase64String(parts[0]);
var encKeyBytes = Convert.FromBase64String(parts[1]);
var key = AesDecrypt(ivBytes, encKeyBytes);
return new App.Migration.Models.SymmetricCryptoKey(key);
}
}
catch
{
Console.WriteLine("Cannot get AesKey.");
_keyStore.DeleteEntry(KeyAlias);
_settings.Remove(AesKey);
if(!v1)
{
//Utilities.SendCrashEmail(e);
//Utilities.SaveCrashFile(e);
}
return null;
}
}
private string AesEncrypt(byte[] input)
{
using(var entry = _keyStore.GetKey(KeyAlias, null))
using(var cipher = Cipher.GetInstance(AesMode))
{
cipher.Init(CipherMode.EncryptMode, entry);
var encBytes = cipher.DoFinal(input);
var ivBytes = cipher.GetIV();
return $"{Convert.ToBase64String(ivBytes)}|{Convert.ToBase64String(encBytes)}";
}
}
private byte[] AesDecrypt(byte[] iv, byte[] encData)
{
using(var entry = _keyStore.GetKey(KeyAlias, null))
using(var cipher = Cipher.GetInstance(AesMode))
{
var spec = new GCMParameterSpec(128, iv);
cipher.Init(CipherMode.DecryptMode, entry, spec);
var decBytes = cipher.DoFinal(encData);
return decBytes;
}
}
private string RsaEncrypt(byte[] data)
{
using(var entry = GetRsaKeyEntry(KeyAlias))
using(var cipher = Cipher.GetInstance(_rsaMode))
{
cipher.Init(CipherMode.EncryptMode, entry.Certificate.PublicKey);
var cipherText = cipher.DoFinal(data);
return Convert.ToBase64String(cipherText);
}
}
private byte[] RsaDecrypt(byte[] encData, bool v1)
{
using(var entry = GetRsaKeyEntry(v1 ? KeyAliasV1 : KeyAlias))
using(var cipher = Cipher.GetInstance(_rsaMode))
{
if(_oldAndroid)
{
cipher.Init(CipherMode.DecryptMode, entry.PrivateKey);
}
else
{
cipher.Init(CipherMode.DecryptMode, entry.PrivateKey, OAEPParameterSpec.Default);
}
var plainText = cipher.DoFinal(encData);
return plainText;
}
}
private byte[] TryGetAndMigrate(string key)
{
var formattedKeyV1 = string.Format(SettingsFormatV1, key);
if(_settings.Contains(formattedKeyV1))
{
var aesKeyV1 = GetAesKey(true);
if(aesKeyV1 != null)
{
try
{
var cs = _settings.GetValueOrDefault(formattedKeyV1, null);
var value = App.Migration.Crypto.AesCbcDecrypt(new App.Migration.Models.CipherString(cs), aesKeyV1);
Store(key, value);
return value;
}
catch
{
Console.WriteLine("Failed to decrypt v1 from secure storage.");
}
}
_settings.Remove(formattedKeyV1);
}
return null;
}
private void CleanupOld(string key)
{
var formattedKeyV1 = string.Format(SettingsFormatV1, key);
if(_settings.Contains(formattedKeyV1))
{
_settings.Remove(formattedKeyV1);
}
}
private void ClearSettings(string format = SettingsFormat)
{
var prefix = string.Format(format, string.Empty);
using(var sharedPreferences = PreferenceManager.GetDefaultSharedPreferences(Application.Context))
using(var sharedPreferencesEditor = sharedPreferences.Edit())
{
var removed = false;
foreach(var pref in sharedPreferences.All)
{
if(pref.Key.StartsWith(prefix))
{
removed = true;
sharedPreferencesEditor.Remove(pref.Key);
}
}
if(removed)
{
sharedPreferencesEditor.Commit();
}
}
}
}
}

View File

@@ -3,16 +3,17 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:versionCode="1"
android:versionName="2.2.1"
android:versionName="2.2.6"
package="com.x8bit.bitwarden">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28" />
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="29" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />

File diff suppressed because it is too large Load Diff

View File

@@ -90,4 +90,7 @@
<compatibility-package
android:name="com.ecosia.android"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.vivaldi.browser"
android:maxLongVersionCode="10000000000"/>
</autofill-service>

View File

@@ -8,9 +8,12 @@ using Android.App;
using Android.App.Assist;
using Android.Content;
using Android.Content.PM;
using Android.Hardware.Biometrics;
using Android.Hardware.Fingerprints;
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;
@@ -28,6 +31,7 @@ using Bit.Core.Models.View;
using Bit.Core.Utilities;
using Bit.Droid.Autofill;
using Plugin.CurrentActivity;
using Plugin.Fingerprint;
namespace Bit.Droid.Services
{
@@ -40,6 +44,7 @@ namespace Bit.Droid.Services
private ProgressDialog _progressDialog;
private bool _cameraPermissionsDenied;
private Toast _toast;
private string _userAgent;
public DeviceActionService(
IStorageService storageService,
@@ -61,6 +66,19 @@ namespace Bit.Droid.Services
});
}
public string DeviceUserAgent
{
get
{
if(string.IsNullOrWhiteSpace(_userAgent))
{
_userAgent = $"Bitwarden_Mobile/{Xamarin.Essentials.AppInfo.VersionString} " +
$"(Android {Build.VERSION.Release}; SDK {Build.VERSION.Sdk}; Model {Build.Model})";
}
return _userAgent;
}
}
public DeviceType DeviceType => DeviceType.Android;
public void Toast(string text, bool longDuration = false)
@@ -114,38 +132,14 @@ namespace Bit.Droid.Services
public bool OpenFile(byte[] fileData, string id, string fileName)
{
if(!CanOpenFile(fileName))
{
return false;
}
var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName.Replace(' ', '_').ToLower());
if(extension == null)
{
return false;
}
var mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
if(mimeType == null)
{
return false;
}
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
var cachePath = activity.CacheDir;
var filePath = Path.Combine(cachePath.Path, fileName);
File.WriteAllBytes(filePath, fileData);
var file = new Java.IO.File(cachePath, fileName);
if(!file.IsFile)
{
return false;
}
try
{
var intent = new Intent(Intent.ActionView);
var uri = FileProvider.GetUriForFile(activity.ApplicationContext,
"com.x8bit.bitwarden.fileprovider", file);
intent.SetDataAndType(uri, mimeType);
intent.SetFlags(ActivityFlags.GrantReadUriPermission);
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
var intent = BuildOpenFileIntent(fileData, fileName);
if(intent == null)
{
return false;
}
activity.StartActivity(intent);
return true;
}
@@ -154,22 +148,57 @@ namespace Bit.Droid.Services
}
public bool CanOpenFile(string fileName)
{
try
{
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
var intent = BuildOpenFileIntent(new byte[0], string.Concat("opentest_", fileName));
if(intent == null)
{
return false;
}
var activities = activity.PackageManager.QueryIntentActivities(intent,
PackageInfoFlags.MatchDefaultOnly);
return (activities?.Count ?? 0) > 0;
}
catch { }
return false;
}
private Intent BuildOpenFileIntent(byte[] fileData, string fileName)
{
var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName.Replace(' ', '_').ToLower());
if(extension == null)
{
return false;
return null;
}
var mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
if(mimeType == null)
{
return false;
return null;
}
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
var intent = new Intent(Intent.ActionView);
intent.SetType(mimeType);
var activities = activity.PackageManager.QueryIntentActivities(intent, PackageInfoFlags.MatchDefaultOnly);
return (activities?.Count ?? 0) > 0;
var cachePath = activity.CacheDir;
var filePath = Path.Combine(cachePath.Path, fileName);
File.WriteAllBytes(filePath, fileData);
var file = new Java.IO.File(cachePath, fileName);
if(!file.IsFile)
{
return null;
}
try
{
var intent = new Intent(Intent.ActionView);
var uri = FileProvider.GetUriForFile(activity.ApplicationContext,
"com.x8bit.bitwarden.fileprovider", file);
intent.SetDataAndType(uri, mimeType);
intent.SetFlags(ActivityFlags.GrantReadUriPermission);
return intent;
}
catch { }
return null;
}
public async Task ClearCacheAsync()
@@ -185,7 +214,8 @@ namespace Bit.Droid.Services
public Task SelectFileAsync()
{
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
var hasStorageWritePermission = !_cameraPermissionsDenied && HasPermission(Manifest.Permission.WriteExternalStorage);
var hasStorageWritePermission = !_cameraPermissionsDenied &&
HasPermission(Manifest.Permission.WriteExternalStorage);
var additionalIntents = new List<IParcelable>();
if(activity.PackageManager.HasSystemFeature(PackageManager.FeatureCamera))
{
@@ -309,11 +339,72 @@ namespace Bit.Droid.Services
Application.Context.PackageName, 0).VersionCode.ToString();
}
public bool SupportsFaceId()
public bool SupportsFaceBiometric()
{
return false;
}
public Task<bool> SupportsFaceBiometricAsync()
{
return Task.FromResult(SupportsFaceBiometric());
}
public async Task<bool> BiometricAvailableAsync()
{
if(UseNativeBiometric())
{
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
var manager = activity.GetSystemService(Context.BiometricService) as BiometricManager;
return manager.CanAuthenticate() == BiometricCode.Success;
}
else
{
try
{
return await CrossFingerprint.Current.IsAvailableAsync();
}
catch
{
return false;
}
}
}
public bool UseNativeBiometric()
{
return (int)Build.VERSION.SdkInt >= 29;
}
public Task<bool> AuthenticateBiometricAsync(string text = null)
{
if(string.IsNullOrWhiteSpace(text))
{
text = AppResources.BiometricsDirection;
}
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
using(var builder = new BiometricPrompt.Builder(activity))
{
builder.SetTitle(text);
builder.SetConfirmationRequired(false);
builder.SetNegativeButton(AppResources.Cancel, activity.MainExecutor,
new DialogInterfaceOnClickListener
{
Clicked = () => { }
});
var prompt = builder.Build();
var result = new TaskCompletionSource<bool>();
prompt.Authenticate(new CancellationSignal(), activity.MainExecutor,
new BiometricAuthenticationCallback
{
Success = authResult => result.TrySetResult(true),
Failed = () => result.TrySetResult(false),
Help = (helpCode, helpString) => { }
});
return result.Task;
}
}
public bool SupportsNfc()
{
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
@@ -543,7 +634,8 @@ namespace Bit.Droid.Services
try
{
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
var afm = (AutofillManager)activity.GetSystemService(Java.Lang.Class.FromType(typeof(AutofillManager)));
var afm = (AutofillManager)activity.GetSystemService(
Java.Lang.Class.FromType(typeof(AutofillManager)));
return afm.IsEnabled && afm.HasEnabledAutofillServices;
}
catch
@@ -585,6 +677,11 @@ namespace Bit.Droid.Services
}
}
public bool UsingDarkTheme()
{
return false;
}
private bool DeleteDir(Java.IO.File dir)
{
if(dir != null && dir.IsDirectory)
@@ -683,5 +780,41 @@ namespace Bit.Droid.Services
Context.ClipboardService) as Android.Content.ClipboardManager;
clipboardManager.Text = text;
}
private class BiometricAuthenticationCallback : BiometricPrompt.AuthenticationCallback
{
public Action<BiometricPrompt.AuthenticationResult> Success { get; set; }
public Action Failed { get; set; }
public Action<BiometricAcquiredStatus, Java.Lang.ICharSequence> Help { get; set; }
public override void OnAuthenticationSucceeded(BiometricPrompt.AuthenticationResult authResult)
{
base.OnAuthenticationSucceeded(authResult);
Success?.Invoke(authResult);
}
public override void OnAuthenticationFailed()
{
base.OnAuthenticationFailed();
Failed?.Invoke();
}
public override void OnAuthenticationHelp([GeneratedEnum] BiometricAcquiredStatus helpCode,
Java.Lang.ICharSequence helpString)
{
base.OnAuthenticationHelp(helpCode, helpString);
Help?.Invoke(helpCode, helpString);
}
}
private class DialogInterfaceOnClickListener : Java.Lang.Object, IDialogInterfaceOnClickListener
{
public Action Clicked { get; set; }
public void OnClick(IDialogInterface dialog, int which)
{
Clicked?.Invoke();
}
}
}
}
}

View File

@@ -6,6 +6,7 @@ namespace Bit.App.Abstractions
{
public interface IDeviceActionService
{
string DeviceUserAgent { get; }
DeviceType DeviceType { get; }
void Toast(string text, bool longDuration = false);
bool LaunchApp(string appName);
@@ -19,7 +20,11 @@ namespace Bit.App.Abstractions
string okButtonText = null, string cancelButtonText = null, bool numericKeyboard = false,
bool autofocus = true);
void RateApp();
bool SupportsFaceId();
bool SupportsFaceBiometric();
Task<bool> SupportsFaceBiometricAsync();
Task<bool> BiometricAvailableAsync();
bool UseNativeBiometric();
Task<bool> AuthenticateBiometricAsync(string text = null);
bool SupportsNfc();
bool SupportsCamera();
bool SupportsAutofillService();
@@ -34,5 +39,6 @@ namespace Bit.App.Abstractions
string GetBuildNumber();
void OpenAccessibilitySettings();
void OpenAutofillSettings();
bool UsingDarkTheme();
}
}

View File

@@ -18,7 +18,7 @@
<PackageReference Include="Refractored.FloatingActionButtonForms" Version="2.1.0" />
<PackageReference Include="Xamarin.Essentials" Version="1.1.0" />
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
<PackageReference Include="Xamarin.Forms" Version="3.6.0.344457" />
<PackageReference Include="Xamarin.Forms" Version="3.6.0.709228" />
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.1.47" />
</ItemGroup>

View File

@@ -97,11 +97,8 @@ namespace Bit.App
}
else if(message.Command == "logout")
{
if(Migration.MigrationHelpers.Migrating)
{
return;
}
Device.BeginInvokeOnMainThread(async () => await LogOutAsync(false));
Device.BeginInvokeOnMainThread(async () =>
await LogOutAsync((message.Data as bool?).GetValueOrDefault()));
}
else if(message.Command == "loggedOut")
{
@@ -239,12 +236,15 @@ namespace Bit.App
_passwordGenerationService.ClearAsync(),
_lockService.ClearAsync(),
_stateService.PurgeAsync());
_lockService.PinLocked = false;
_lockService.FingerprintLocked = true;
_searchService.ClearIndex();
_authService.LogOut(() =>
{
Current.MainPage = new HomePage();
if(expired)
{
_platformUtilsService.ShowToast("warning", null, AppResources.LoginExpired);
}
});
}
@@ -356,10 +356,6 @@ namespace Bit.App
private void SyncIfNeeded()
{
if(Migration.MigrationHelpers.Migrating)
{
return;
}
if(Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
{
return;

View File

@@ -0,0 +1,21 @@
using Bit.App.Abstractions;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedSearchBar : SearchBar
{
public ExtendedSearchBar()
{
if(Device.RuntimePlatform == Device.iOS)
{
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService", true);
if(!deviceActionService?.UsingDarkTheme() ?? false)
{
TextColor = Color.Black;
}
}
}
}
}

View File

@@ -1,10 +0,0 @@
namespace Bit.App.Migration.Abstractions
{
public interface IOldSecureStorageService
{
bool Contains(string key);
void Delete(string key);
byte[] Retrieve(string key);
void Store(string key, byte[] dataBytes);
}
}

View File

@@ -1,199 +0,0 @@
using Bit.App.Migration.Models;
using Bit.Core.Enums;
using PCLCrypto;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Bit.App.Migration
{
public static class Crypto
{
public static CipherString AesCbcEncrypt(byte[] plainBytes, SymmetricCryptoKey key)
{
var parts = AesCbcEncryptToParts(plainBytes, key);
return new CipherString(parts.Item1, Convert.ToBase64String(parts.Item2),
Convert.ToBase64String(parts.Item4), parts.Item3 != null ? Convert.ToBase64String(parts.Item3) : null);
}
public static byte[] AesCbcEncryptToBytes(byte[] plainBytes, SymmetricCryptoKey key)
{
var parts = AesCbcEncryptToParts(plainBytes, key);
var macLength = parts.Item3?.Length ?? 0;
var encBytes = new byte[1 + parts.Item2.Length + macLength + parts.Item4.Length];
encBytes[0] = (byte)parts.Item1;
parts.Item2.CopyTo(encBytes, 1);
if(parts.Item3 != null)
{
parts.Item3.CopyTo(encBytes, 1 + parts.Item2.Length);
}
parts.Item4.CopyTo(encBytes, 1 + parts.Item2.Length + macLength);
return encBytes;
}
private static Tuple<EncryptionType, byte[], byte[], byte[]> AesCbcEncryptToParts(byte[] plainBytes,
SymmetricCryptoKey key)
{
if(key == null)
{
throw new ArgumentNullException(nameof(key));
}
if(plainBytes == null)
{
throw new ArgumentNullException(nameof(plainBytes));
}
var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
var cryptoKey = provider.CreateSymmetricKey(key.EncKey);
var iv = RandomBytes(provider.BlockLength);
var ct = WinRTCrypto.CryptographicEngine.Encrypt(cryptoKey, plainBytes, iv);
var mac = key.MacKey != null ? ComputeMac(ct, iv, key.MacKey) : null;
return new Tuple<EncryptionType, byte[], byte[], byte[]>(key.EncryptionType, iv, mac, ct);
}
public static byte[] AesCbcDecrypt(CipherString encyptedValue, SymmetricCryptoKey key)
{
if(encyptedValue == null)
{
throw new ArgumentNullException(nameof(encyptedValue));
}
return AesCbcDecrypt(encyptedValue.EncryptionType, encyptedValue.CipherTextBytes,
encyptedValue.InitializationVectorBytes, encyptedValue.MacBytes, key);
}
public static byte[] AesCbcDecrypt(EncryptionType type, byte[] ct, byte[] iv, byte[] mac,
SymmetricCryptoKey key)
{
if(key == null)
{
throw new ArgumentNullException(nameof(key));
}
if(ct == null)
{
throw new ArgumentNullException(nameof(ct));
}
if(iv == null)
{
throw new ArgumentNullException(nameof(iv));
}
if(key.MacKey != null && mac == null)
{
throw new ArgumentNullException(nameof(mac));
}
if(key.EncryptionType != type)
{
throw new InvalidOperationException(nameof(type));
}
if(key.MacKey != null && mac != null)
{
var computedMacBytes = ComputeMac(ct, iv, key.MacKey);
if(!MacsEqual(computedMacBytes, mac))
{
throw new InvalidOperationException("MAC failed.");
}
}
var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
var cryptoKey = provider.CreateSymmetricKey(key.EncKey);
var decryptedBytes = WinRTCrypto.CryptographicEngine.Decrypt(cryptoKey, ct, iv);
return decryptedBytes;
}
public static byte[] RandomBytes(int length)
{
return WinRTCrypto.CryptographicBuffer.GenerateRandom(length);
}
public static byte[] ComputeMac(byte[] ctBytes, byte[] ivBytes, byte[] macKey)
{
if(ctBytes == null)
{
throw new ArgumentNullException(nameof(ctBytes));
}
if(ivBytes == null)
{
throw new ArgumentNullException(nameof(ivBytes));
}
return ComputeMac(ivBytes.Concat(ctBytes), macKey);
}
public static byte[] ComputeMac(IEnumerable<byte> dataBytes, byte[] macKey)
{
if(macKey == null)
{
throw new ArgumentNullException(nameof(macKey));
}
if(dataBytes == null)
{
throw new ArgumentNullException(nameof(dataBytes));
}
var algorithm = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha256);
var hasher = algorithm.CreateHash(macKey);
hasher.Append(dataBytes.ToArray());
var mac = hasher.GetValueAndReset();
return mac;
}
// Safely compare two MACs in a way that protects against timing attacks (Double HMAC Verification).
// ref: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/
// ref: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy
public static bool MacsEqual(byte[] mac1, byte[] mac2)
{
var algorithm = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha256);
var hasher = algorithm.CreateHash(RandomBytes(32));
hasher.Append(mac1);
mac1 = hasher.GetValueAndReset();
hasher.Append(mac2);
mac2 = hasher.GetValueAndReset();
if(mac1.Length != mac2.Length)
{
return false;
}
for(int i = 0; i < mac2.Length; i++)
{
if(mac1[i] != mac2[i])
{
return false;
}
}
return true;
}
// ref: https://tools.ietf.org/html/rfc5869
public static byte[] HkdfExpand(byte[] prk, byte[] info, int size)
{
var hashLen = 32; // sha256
var okm = new byte[size];
var previousT = new byte[0];
var n = (int)Math.Ceiling((double)size / hashLen);
for(int i = 0; i < n; i++)
{
var t = new byte[previousT.Length + info.Length + 1];
previousT.CopyTo(t, 0);
info.CopyTo(t, previousT.Length);
t[t.Length - 1] = (byte)(i + 1);
previousT = ComputeMac(t, prk);
previousT.CopyTo(okm, i * hashLen);
}
return okm;
}
}
}

View File

@@ -1,201 +0,0 @@
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Utilities;
using System;
using System.Text;
using System.Threading.Tasks;
namespace Bit.App.Migration
{
public static class MigrationHelpers
{
public static bool Migrating = false;
public static bool NeedsMigration()
{
return ServiceContainer.Resolve<SettingsShim>("settingsShim")
.GetValueOrDefault(Constants.OldUserIdKey, null) != null; ;
}
public static async Task<bool> PerformMigrationAsync()
{
if(!NeedsMigration() || Migrating)
{
return false;
}
Migrating = true;
var settingsShim = ServiceContainer.Resolve<SettingsShim>("settingsShim");
var oldSecureStorageService = ServiceContainer.Resolve<Abstractions.IOldSecureStorageService>(
"oldSecureStorageService");
var messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
var secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
var cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
var tokenService = ServiceContainer.Resolve<ITokenService>("tokenService");
var userService = ServiceContainer.Resolve<IUserService>("userService");
var environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
var passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
"passwordGenerationService");
var syncService = ServiceContainer.Resolve<ISyncService>("syncService");
var lockService = ServiceContainer.Resolve<ILockService>("lockService");
// Get old data
var oldTokenBytes = oldSecureStorageService.Retrieve("accessToken");
var oldToken = oldTokenBytes == null ? null : Encoding.UTF8.GetString(
oldTokenBytes, 0, oldTokenBytes.Length);
var oldKeyBytes = oldSecureStorageService.Retrieve("key");
var oldKey = oldKeyBytes == null ? null : new Models.SymmetricCryptoKey(oldKeyBytes);
var oldUserId = settingsShim.GetValueOrDefault("userId", null);
var isAuthenticated = oldKey != null && !string.IsNullOrWhiteSpace(oldToken) &&
!string.IsNullOrWhiteSpace(oldUserId);
if(!isAuthenticated)
{
Migrating = false;
return false;
}
var oldRefreshTokenBytes = oldSecureStorageService.Retrieve("refreshToken");
var oldRefreshToken = oldRefreshTokenBytes == null ? null : Encoding.UTF8.GetString(
oldRefreshTokenBytes, 0, oldRefreshTokenBytes.Length);
var oldPinBytes = oldSecureStorageService.Retrieve("pin");
var oldPin = oldPinBytes == null ? null : Encoding.UTF8.GetString(
oldPinBytes, 0, oldPinBytes.Length);
var oldEncKey = settingsShim.GetValueOrDefault("encKey", null);
var oldEncPrivateKey = settingsShim.GetValueOrDefault("encPrivateKey", null);
var oldEmail = settingsShim.GetValueOrDefault("email", null);
var oldKdf = (KdfType)settingsShim.GetValueOrDefault("kdf", (int)KdfType.PBKDF2_SHA256);
var oldKdfIterations = settingsShim.GetValueOrDefault("kdfIterations", 5000);
var oldTwoFactorTokenBytes = oldSecureStorageService.Retrieve(
string.Format("twoFactorToken_{0}", Convert.ToBase64String(Encoding.UTF8.GetBytes(oldEmail))));
var oldTwoFactorToken = oldTwoFactorTokenBytes == null ? null : Encoding.UTF8.GetString(
oldTwoFactorTokenBytes, 0, oldTwoFactorTokenBytes.Length);
var oldAppIdBytes = oldSecureStorageService.Retrieve("appId");
var oldAppId = oldAppIdBytes == null ? null : new Guid(oldAppIdBytes).ToString();
var oldAnonAppIdBytes = oldSecureStorageService.Retrieve("anonymousAppId");
var oldAnonAppId = oldAnonAppIdBytes == null ? null : new Guid(oldAnonAppIdBytes).ToString();
var oldFingerprint = settingsShim.GetValueOrDefault("setting:fingerprintUnlockOn", false);
// Save settings
await storageService.SaveAsync(Constants.AccessibilityAutofillPersistNotificationKey,
settingsShim.GetValueOrDefault("setting:persistNotification", false));
await storageService.SaveAsync(Constants.AccessibilityAutofillPasswordFieldKey,
settingsShim.GetValueOrDefault("setting:autofillPasswordField", false));
await storageService.SaveAsync(Constants.DisableAutoTotpCopyKey,
settingsShim.GetValueOrDefault("setting:disableAutoCopyTotp", false));
await storageService.SaveAsync(Constants.DisableFaviconKey,
settingsShim.GetValueOrDefault("setting:disableWebsiteIcons", false));
await storageService.SaveAsync(Constants.AddSitePromptShownKey,
settingsShim.GetValueOrDefault("addedSiteAlert", false));
await storageService.SaveAsync(Constants.PushInitialPromptShownKey,
settingsShim.GetValueOrDefault("push:initialPromptShown", false));
await storageService.SaveAsync(Constants.PushCurrentTokenKey,
settingsShim.GetValueOrDefault("push:currentToken", null));
await storageService.SaveAsync(Constants.PushRegisteredTokenKey,
settingsShim.GetValueOrDefault("push:registeredToken", null));
// For some reason "push:lastRegistrationDate" isn't getting pulled from settingsShim correctly.
// We don't really need it anyways.
// var lastReg = settingsShim.GetValueOrDefault("push:lastRegistrationDate", DateTime.MinValue);
// await storageService.SaveAsync(Constants.PushLastRegistrationDateKey, lastReg);
await storageService.SaveAsync("rememberedEmail",
settingsShim.GetValueOrDefault("other:lastLoginEmail", null));
await storageService.SaveAsync("appExtensionStarted",
settingsShim.GetValueOrDefault("extension:started", false));
await storageService.SaveAsync("appExtensionActivated",
settingsShim.GetValueOrDefault("extension:activated", false));
await environmentService.SetUrlsAsync(new Core.Models.Data.EnvironmentUrlData
{
Base = settingsShim.GetValueOrDefault("other:baseUrl", null),
Api = settingsShim.GetValueOrDefault("other:apiUrl", null),
WebVault = settingsShim.GetValueOrDefault("other:webVaultUrl", null),
Identity = settingsShim.GetValueOrDefault("other:identityUrl", null),
Icons = settingsShim.GetValueOrDefault("other:iconsUrl", null)
});
await passwordGenerationService.SaveOptionsAsync(new Core.Models.Domain.PasswordGenerationOptions
{
Ambiguous = settingsShim.GetValueOrDefault("pwGenerator:ambiguous", false),
Length = settingsShim.GetValueOrDefault("pwGenerator:length", 15),
Uppercase = settingsShim.GetValueOrDefault("pwGenerator:uppercase", true),
Lowercase = settingsShim.GetValueOrDefault("pwGenerator:lowercase", true),
Number = settingsShim.GetValueOrDefault("pwGenerator:numbers", true),
MinNumber = settingsShim.GetValueOrDefault("pwGenerator:minNumbers", 0),
Special = settingsShim.GetValueOrDefault("pwGenerator:special", true),
MinSpecial = settingsShim.GetValueOrDefault("pwGenerator:minSpecial", 0),
WordSeparator = "-",
NumWords = 3
});
// Save lock options
int? lockOptionsSeconds = settingsShim.GetValueOrDefault("setting:lockSeconds", -10);
if(lockOptionsSeconds == -10)
{
lockOptionsSeconds = 60 * 15;
}
else if(lockOptionsSeconds == -1)
{
lockOptionsSeconds = null;
}
await storageService.SaveAsync(Constants.LockOptionKey,
lockOptionsSeconds == null ? (int?)null : lockOptionsSeconds.Value / 60);
// Save app ids
await storageService.SaveAsync("appId", oldAppId);
await storageService.SaveAsync("anonymousAppId", oldAnonAppId);
// Save new authed data
await tokenService.SetTwoFactorTokenAsync(oldTwoFactorToken, oldEmail);
await tokenService.SetTokensAsync(oldToken, oldRefreshToken);
await userService.SetInformationAsync(oldUserId, oldEmail, oldKdf, oldKdfIterations);
// Save fingerprint
if(oldFingerprint)
{
await storageService.SaveAsync(Constants.FingerprintUnlockKey, true);
}
var newKey = new Core.Models.Domain.SymmetricCryptoKey(oldKey.Key);
await cryptoService.SetKeyAsync(newKey);
// Key hash is unavailable in old version, store old key until we can move it to key hash
await secureStorageService.SaveAsync("oldKey", newKey.KeyB64);
await cryptoService.SetEncKeyAsync(oldEncKey);
await cryptoService.SetEncPrivateKeyAsync(oldEncPrivateKey);
// Save pin
if(!oldFingerprint && !string.IsNullOrWhiteSpace(oldPin))
{
var pinKey = await cryptoService.MakePinKeyAysnc(oldPin, oldEmail, oldKdf, oldKdfIterations);
var pinProtectedKey = await cryptoService.EncryptAsync(oldKeyBytes, pinKey);
await storageService.SaveAsync(Constants.PinProtectedKey, pinProtectedKey.EncryptedString);
}
// Post migration tasks
await cryptoService.ToggleKeyAsync();
await storageService.SaveAsync(Constants.LastActiveKey, DateTime.UtcNow.AddYears(-1));
await lockService.CheckLockAsync();
// Remove "needs migration" flag
settingsShim.Remove(Constants.OldUserIdKey);
await storageService.SaveAsync(Constants.MigratedFromV1, true);
Migrating = false;
messagingService.Send("migrated");
if(Xamarin.Essentials.Connectivity.NetworkAccess != Xamarin.Essentials.NetworkAccess.None)
{
var task = Task.Run(() => syncService.FullSyncAsync(true));
}
return true;
}
}
}

View File

@@ -1,117 +0,0 @@
using System;
using Bit.Core.Enums;
namespace Bit.App.Migration.Models
{
public class CipherString
{
private string _decryptedValue;
public CipherString(string encryptedString)
{
if(string.IsNullOrWhiteSpace(encryptedString))
{
throw new ArgumentException(nameof(encryptedString));
}
var headerPieces = encryptedString.Split('.');
string[] encPieces;
EncryptionType encType;
if(headerPieces.Length == 2 && Enum.TryParse(headerPieces[0], out encType))
{
EncryptionType = encType;
encPieces = headerPieces[1].Split('|');
}
else if(headerPieces.Length == 1)
{
encPieces = headerPieces[0].Split('|');
EncryptionType = encPieces.Length == 3 ? EncryptionType.AesCbc128_HmacSha256_B64 :
EncryptionType.AesCbc256_B64;
}
else
{
throw new ArgumentException("Malformed header.");
}
switch(EncryptionType)
{
case EncryptionType.AesCbc256_B64:
if(encPieces.Length != 2)
{
throw new ArgumentException("Malformed encPieces.");
}
InitializationVector = encPieces[0];
CipherText = encPieces[1];
break;
case EncryptionType.AesCbc128_HmacSha256_B64:
case EncryptionType.AesCbc256_HmacSha256_B64:
if(encPieces.Length != 3)
{
throw new ArgumentException("Malformed encPieces.");
}
InitializationVector = encPieces[0];
CipherText = encPieces[1];
Mac = encPieces[2];
break;
case EncryptionType.Rsa2048_OaepSha256_B64:
case EncryptionType.Rsa2048_OaepSha1_B64:
if(encPieces.Length != 1)
{
throw new ArgumentException("Malformed encPieces.");
}
CipherText = encPieces[0];
break;
case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64:
case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64:
if(encPieces.Length != 2)
{
throw new ArgumentException("Malformed encPieces.");
}
CipherText = encPieces[0];
Mac = encPieces[1];
break;
default:
throw new ArgumentException("Unknown encType.");
}
EncryptedString = encryptedString;
}
public CipherString(EncryptionType encryptionType, string initializationVector, string cipherText,
string mac = null)
{
if(string.IsNullOrWhiteSpace(initializationVector))
{
throw new ArgumentNullException(nameof(initializationVector));
}
if(string.IsNullOrWhiteSpace(cipherText))
{
throw new ArgumentNullException(nameof(cipherText));
}
EncryptionType = encryptionType;
EncryptedString = string.Format("{0}.{1}|{2}", (byte)EncryptionType, initializationVector, cipherText);
if(!string.IsNullOrWhiteSpace(mac))
{
EncryptedString = string.Format("{0}|{1}", EncryptedString, mac);
}
CipherText = cipherText;
InitializationVector = initializationVector;
Mac = mac;
}
public EncryptionType EncryptionType { get; private set; }
public string EncryptedString { get; private set; }
public string InitializationVector { get; private set; }
public string CipherText { get; private set; }
public string Mac { get; private set; }
public byte[] InitializationVectorBytes => string.IsNullOrWhiteSpace(InitializationVector) ?
null : Convert.FromBase64String(InitializationVector);
public byte[] CipherTextBytes => Convert.FromBase64String(CipherText);
public byte[] MacBytes => Mac == null ? null : Convert.FromBase64String(Mac);
}
}

View File

@@ -1,62 +0,0 @@
using Bit.Core.Enums;
using System;
using System.Linq;
namespace Bit.App.Migration.Models
{
public class SymmetricCryptoKey
{
public SymmetricCryptoKey(byte[] rawBytes, EncryptionType? encType = null)
{
if(rawBytes == null || rawBytes.Length == 0)
{
throw new Exception("Must provide keyBytes.");
}
if(encType == null)
{
if(rawBytes.Length == 32)
{
encType = EncryptionType.AesCbc256_B64;
}
else if(rawBytes.Length == 64)
{
encType = EncryptionType.AesCbc256_HmacSha256_B64;
}
else
{
throw new Exception("Unable to determine encType.");
}
}
EncryptionType = encType.Value;
Key = rawBytes;
if(EncryptionType == EncryptionType.AesCbc256_B64 && Key.Length == 32)
{
EncKey = Key;
MacKey = null;
}
else if(EncryptionType == EncryptionType.AesCbc128_HmacSha256_B64 && Key.Length == 32)
{
EncKey = Key.Take(16).ToArray();
MacKey = Key.Skip(16).Take(16).ToArray();
}
else if(EncryptionType == EncryptionType.AesCbc256_HmacSha256_B64 && Key.Length == 64)
{
EncKey = Key.Take(32).ToArray();
MacKey = Key.Skip(32).Take(32).ToArray();
}
else
{
throw new Exception("Unsupported encType/key length.");
}
}
public byte[] Key { get; set; }
public string B64Key => Convert.ToBase64String(Key);
public byte[] EncKey { get; set; }
public byte[] MacKey { get; set; }
public EncryptionType EncryptionType { get; set; }
}
}

View File

@@ -1,122 +0,0 @@
using System;
namespace Bit.App.Migration
{
public class SettingsShim
{
private readonly string _sharedName;
public SettingsShim(string sharedName = null)
{
_sharedName = sharedName;
}
public bool Contains(string key)
{
return _sharedName != null ? Xamarin.Essentials.Preferences.ContainsKey(key, _sharedName) :
Xamarin.Essentials.Preferences.ContainsKey(key);
}
public string GetValueOrDefault(string key, string defaultValue)
{
return _sharedName != null ? Xamarin.Essentials.Preferences.Get(key, defaultValue, _sharedName) :
Xamarin.Essentials.Preferences.Get(key, defaultValue);
}
public DateTime GetValueOrDefault(string key, DateTime defaultValue)
{
return _sharedName != null ? Xamarin.Essentials.Preferences.Get(key, defaultValue, _sharedName) :
Xamarin.Essentials.Preferences.Get(key, defaultValue);
}
public bool GetValueOrDefault(string key, bool defaultValue)
{
return _sharedName != null ? Xamarin.Essentials.Preferences.Get(key, defaultValue, _sharedName) :
Xamarin.Essentials.Preferences.Get(key, defaultValue);
}
public int GetValueOrDefault(string key, int defaultValue)
{
return _sharedName != null ? Xamarin.Essentials.Preferences.Get(key, defaultValue, _sharedName) :
Xamarin.Essentials.Preferences.Get(key, defaultValue);
}
public long GetValueOrDefault(string key, long defaultValue)
{
return _sharedName != null ? Xamarin.Essentials.Preferences.Get(key, defaultValue, _sharedName) :
Xamarin.Essentials.Preferences.Get(key, defaultValue);
}
public void AddOrUpdateValue(string key, string value)
{
if(_sharedName != null)
{
Xamarin.Essentials.Preferences.Set(key, value, _sharedName);
}
else
{
Xamarin.Essentials.Preferences.Set(key, value);
}
}
public void AddOrUpdateValue(string key, DateTime value)
{
if(_sharedName != null)
{
Xamarin.Essentials.Preferences.Set(key, value, _sharedName);
}
else
{
Xamarin.Essentials.Preferences.Set(key, value);
}
}
public void AddOrUpdateValue(string key, bool value)
{
if(_sharedName != null)
{
Xamarin.Essentials.Preferences.Set(key, value, _sharedName);
}
else
{
Xamarin.Essentials.Preferences.Set(key, value);
}
}
public void AddOrUpdateValue(string key, long value)
{
if(_sharedName != null)
{
Xamarin.Essentials.Preferences.Set(key, value, _sharedName);
}
else
{
Xamarin.Essentials.Preferences.Set(key, value);
}
}
public void AddOrUpdateValue(string key, int value)
{
if(_sharedName != null)
{
Xamarin.Essentials.Preferences.Set(key, value, _sharedName);
}
else
{
Xamarin.Essentials.Preferences.Set(key, value);
}
}
public void Remove(string key)
{
if(_sharedName != null)
{
Xamarin.Essentials.Preferences.Remove(key, _sharedName);
}
else
{
Xamarin.Essentials.Preferences.Remove(key);
}
}
}
}

View File

@@ -60,7 +60,11 @@ namespace Bit.App.Pages
catch(ApiException e)
{
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(), AppResources.Ok);
if(e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
}
}

View File

@@ -16,9 +16,7 @@ namespace Bit.App.Pages
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_messagingService.Send("showStatusBar", false);
InitializeComponent();
var theme = ThemeManager.GetTheme(Device.RuntimePlatform == Device.Android);
var darkbasedTheme = theme == "dark" || theme == "black" || theme == "nord";
_logo.Source = darkbasedTheme ? "logo_white.png" : "logo.png";
_logo.Source = !ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png";
}
public async Task DismissRegisterPageAndLogInAsync(string email)

View File

@@ -25,7 +25,6 @@ namespace Bit.App.Pages
private readonly IEnvironmentService _environmentService;
private readonly IStateService _stateService;
private bool _hasKey;
private string _email;
private bool _showPassword;
private bool _pinLock;
@@ -104,8 +103,7 @@ namespace Bit.App.Pages
public async Task InitAsync(bool autoPromptFingerprint)
{
_pinSet = await _lockService.IsPinLockSetAsync();
_hasKey = await _cryptoService.HasKeyAsync();
PinLock = (_pinSet.Item1 && _hasKey) || _pinSet.Item2;
PinLock = (_pinSet.Item1 && _lockService.PinProtectedKey != null) || _pinSet.Item2;
FingerprintLock = await _lockService.IsFingerprintLockSetAsync();
_email = await _userService.GetEmailAsync();
var webVault = _environmentService.GetWebVaultUrl();
@@ -128,8 +126,19 @@ namespace Bit.App.Pages
if(FingerprintLock)
{
FingerprintButtonText = _deviceActionService.SupportsFaceId() ? AppResources.UseFaceIDToUnlock :
AppResources.UseFingerprintToUnlock;
var supportsFace = await _deviceActionService.SupportsFaceBiometricAsync();
if(Device.RuntimePlatform == Device.iOS && supportsFace)
{
FingerprintButtonText = AppResources.UseFaceIDToUnlock;
}
else if(Device.RuntimePlatform == Device.Android && _deviceActionService.UseNativeBiometric())
{
FingerprintButtonText = AppResources.UseBiometricsToUnlock;
}
else
{
FingerprintButtonText = AppResources.UseFingerprintToUnlock;
}
if(autoPromptFingerprint)
{
var tasks = Task.Run(async () =>
@@ -169,14 +178,17 @@ namespace Bit.App.Pages
{
if(_pinSet.Item1)
{
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000),
_lockService.PinProtectedKey);
var encKey = await _cryptoService.GetEncKeyAsync(key);
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
var decPin = await _cryptoService.DecryptToUtf8Async(new CipherString(protectedPin));
var decPin = await _cryptoService.DecryptToUtf8Async(new CipherString(protectedPin), encKey);
failed = decPin != Pin;
_lockService.PinLocked = failed;
if(!failed)
{
Pin = string.Empty;
await DoContinueAsync();
await SetKeyAndContinueAsync(key);
}
}
else
@@ -221,6 +233,15 @@ namespace Bit.App.Pages
}
if(storedKeyHash != null && keyHash != null && storedKeyHash == keyHash)
{
if(_pinSet.Item1)
{
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
var encKey = await _cryptoService.GetEncKeyAsync(key);
var decPin = await _cryptoService.DecryptToUtf8Async(new CipherString(protectedPin), encKey);
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email,
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
_lockService.PinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
}
MasterPassword = string.Empty;
await SetKeyAndContinueAsync(key);
}
@@ -256,7 +277,7 @@ namespace Bit.App.Pages
{
return;
}
var success = await _platformUtilsService.AuthenticateFingerprintAsync(null,
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
PinLock ? AppResources.PIN : AppResources.MasterPassword, () =>
{
var page = Page as LockPage;
@@ -278,7 +299,8 @@ namespace Bit.App.Pages
private async Task SetKeyAndContinueAsync(SymmetricCryptoKey key)
{
if(!_hasKey)
var hasKey = await _cryptoService.HasKeyAsync();
if(!hasKey)
{
await _cryptoService.SetKeyAsync(key);
}
@@ -287,7 +309,6 @@ namespace Bit.App.Pages
private async Task DoContinueAsync()
{
_lockService.PinLocked = false;
_lockService.FingerprintLocked = false;
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());

View File

@@ -135,7 +135,11 @@ namespace Bit.App.Pages
catch(ApiException e)
{
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(), AppResources.Ok);
if(e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
}

View File

@@ -135,7 +135,11 @@ namespace Bit.App.Pages
catch(ApiException e)
{
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(), AppResources.Ok);
if(e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
}

View File

@@ -213,8 +213,11 @@ namespace Bit.App.Pages
catch(ApiException e)
{
await _deviceActionService.HideLoadingAsync();
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
if(e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
}

View File

@@ -4,6 +4,8 @@ using Bit.Core.Utilities;
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
namespace Bit.App.Pages
{
@@ -14,6 +16,14 @@ namespace Bit.App.Pages
protected int ShowModalAnimationDelay = 400;
protected int ShowPageAnimationDelay = 100;
public BaseContentPage()
{
if (Device.RuntimePlatform == Device.iOS)
{
On<iOS>().SetModalPresentationStyle(UIModalPresentationStyle.FullScreen);
}
}
public DateTime? LastPageAction { get; set; }
protected override void OnAppearing()

View File

@@ -93,7 +93,11 @@ namespace Bit.App.Pages
catch(ApiException e)
{
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(), AppResources.Ok);
if(e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
return false;
}
@@ -128,7 +132,11 @@ namespace Bit.App.Pages
catch(ApiException e)
{
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(), AppResources.Ok);
if(e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
return false;
}

View File

@@ -142,8 +142,12 @@ namespace Bit.App.Pages
var fingerprintName = AppResources.Fingerprint;
if(Device.RuntimePlatform == Device.iOS)
{
fingerprintName = _deviceActionService.SupportsFaceId() ?
AppResources.FaceID : AppResources.TouchID;
var supportsFace = await _deviceActionService.SupportsFaceBiometricAsync();
fingerprintName = supportsFace ? AppResources.FaceID : AppResources.TouchID;
}
else if(Device.RuntimePlatform == Device.Android && _deviceActionService.UseNativeBiometric())
{
fingerprintName = AppResources.Biometrics;
}
if(item.Name == string.Format(AppResources.UnlockWith, fingerprintName))
{

View File

@@ -62,7 +62,7 @@ namespace Bit.App.Pages
public async Task InitAsync()
{
_supportsFingerprint = await _platformUtilsService.SupportsFingerprintAsync();
_supportsFingerprint = await _platformUtilsService.SupportsBiometricAsync();
var lastSync = await _syncService.GetLastSyncAsync();
if(lastSync != null)
{
@@ -214,21 +214,24 @@ namespace Bit.App.Pages
var masterPassOnRestart = await _platformUtilsService.ShowDialogAsync(
AppResources.PINRequireMasterPasswordRestart, AppResources.UnlockWithPIN,
AppResources.Yes, AppResources.No);
var kdf = await _userService.GetKdfAsync();
var kdfIterations = await _userService.GetKdfIterationsAsync();
var email = await _userService.GetEmailAsync();
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email,
kdf.GetValueOrDefault(Core.Enums.KdfType.PBKDF2_SHA256),
kdfIterations.GetValueOrDefault(5000));
var key = await _cryptoService.GetKeyAsync();
var pinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
if(masterPassOnRestart)
{
var encPin = await _cryptoService.EncryptAsync(pin);
await _storageService.SaveAsync(Constants.ProtectedPin, encPin.EncryptedString);
_lockService.PinProtectedKey = pinProtectedKey;
}
else
{
var kdf = await _userService.GetKdfAsync();
var kdfIterations = await _userService.GetKdfIterationsAsync();
var email = await _userService.GetEmailAsync();
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email,
kdf.GetValueOrDefault(Core.Enums.KdfType.PBKDF2_SHA256),
kdfIterations.GetValueOrDefault(5000));
var key = await _cryptoService.GetKeyAsync();
var pinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
await _storageService.SaveAsync(Constants.PinProtectedKey, pinProtectedKey.EncryptedString);
}
}
@@ -239,8 +242,8 @@ namespace Bit.App.Pages
}
if(!_pin)
{
await _storageService.RemoveAsync(Constants.PinProtectedKey);
await _storageService.RemoveAsync(Constants.ProtectedPin);
await _cryptoService.ClearPinProtectedKeyAsync();
await _lockService.ClearAsync();
}
BuildList();
}
@@ -252,9 +255,9 @@ namespace Bit.App.Pages
{
_fingerprint = false;
}
else if(await _platformUtilsService.SupportsFingerprintAsync())
else if(await _platformUtilsService.SupportsBiometricAsync())
{
_fingerprint = await _platformUtilsService.AuthenticateFingerprintAsync(null,
_fingerprint = await _platformUtilsService.AuthenticateBiometricAsync(null,
_deviceActionService.DeviceType == Core.Enums.DeviceType.Android ? "." : null);
}
if(_fingerprint == current)
@@ -325,8 +328,12 @@ namespace Bit.App.Pages
var fingerprintName = AppResources.Fingerprint;
if(Device.RuntimePlatform == Device.iOS)
{
fingerprintName = _deviceActionService.SupportsFaceId() ?
AppResources.FaceID : AppResources.TouchID;
fingerprintName = _deviceActionService.SupportsFaceBiometric() ? AppResources.FaceID :
AppResources.TouchID;
}
else if(Device.RuntimePlatform == Device.Android && _deviceActionService.UseNativeBiometric())
{
fingerprintName = AppResources.Biometrics;
}
var item = new SettingsPageListItem
{

View File

@@ -70,7 +70,11 @@ namespace Bit.App.Pages
catch(ApiException e)
{
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(), AppResources.Ok);
if(e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
}
}

View File

@@ -401,12 +401,13 @@ namespace Bit.App.Pages
return false;
}
Cipher.Fields = Fields != null && Fields.Any() ? Fields.Select(f => f.Field).ToList() : null;
Cipher.Fields = Fields != null && Fields.Any() ?
Fields.Where(f => f != null).Select(f => f.Field).ToList() : null;
if(Cipher.Login != null)
{
Cipher.Login.Uris = Uris?.ToList();
if(!EditMode && Cipher.Type == CipherType.Login && (Cipher.Login.Uris?.Count ?? 0) == 1 &&
string.IsNullOrWhiteSpace(Cipher.Login.Uris.First().Uri))
if(!EditMode && Cipher.Type == CipherType.Login && Cipher.Login.Uris != null &&
Cipher.Login.Uris.Count == 1 && string.IsNullOrWhiteSpace(Cipher.Login.Uris[0].Uri))
{
Cipher.Login.Uris = null;
}
@@ -414,7 +415,7 @@ namespace Bit.App.Pages
if(!EditMode && Cipher.OrganizationId != null)
{
if(!Collections?.Any(c => c.Checked) ?? true)
if(Collections == null || !Collections.Any(c => c != null && c.Checked))
{
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, AppResources.SelectOneCollection,
AppResources.Ok);
@@ -422,7 +423,8 @@ namespace Bit.App.Pages
}
Cipher.CollectionIds = Collections.Any() ?
new HashSet<string>(Collections.Where(c => c.Checked).Select(c => c.Collection.Id)) : null;
new HashSet<string>(Collections.Where(c => c != null && c.Checked && c.Collection?.Id != null)
.Select(c => c.Collection.Id)) : null;
}
var cipher = await _cipherService.EncryptAsync(Cipher);
@@ -454,7 +456,11 @@ namespace Bit.App.Pages
catch(ApiException e)
{
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(), AppResources.Ok);
if(e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
return false;
}
@@ -485,7 +491,11 @@ namespace Bit.App.Pages
catch(ApiException e)
{
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(), AppResources.Ok);
if(e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
return false;
}

View File

@@ -124,7 +124,11 @@ namespace Bit.App.Pages
catch(ApiException e)
{
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(), AppResources.Ok);
if(e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
return false;
}
@@ -164,7 +168,11 @@ namespace Bit.App.Pages
catch(ApiException e)
{
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(), AppResources.Ok);
if(e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
}

View File

@@ -39,7 +39,7 @@ namespace Bit.App.Pages
}
catch(Exception e) when(e.Message.Contains("No key."))
{
await Task.Delay(5000);
await Task.Delay(1000);
await _vm.LoadAsync();
}
}, _mainContent);

View File

@@ -157,8 +157,11 @@ namespace Bit.App.Pages
catch(ApiException e)
{
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(),
AppResources.Ok);
if(e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
}
if(autofillResponse == AppResources.Yes || autofillResponse == AppResources.YesAndSave)

View File

@@ -34,7 +34,7 @@
VerticalOptions="CenterAndExpand"
Clicked="BackButton_Clicked"
x:Name="_backButton" />
<SearchBar
<controls:ExtendedSearchBar
x:Name="_searchBar"
HorizontalOptions="FillAndExpand"
TextChanged="SearchBar_TextChanged"

View File

@@ -168,8 +168,11 @@ namespace Bit.App.Pages
catch(ApiException e)
{
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(),
AppResources.Ok);
if(e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
}
if(_deviceActionService.SystemMajorVersion() < 21)

View File

@@ -85,7 +85,11 @@ namespace Bit.App.Pages
catch(ApiException e)
{
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(), AppResources.Ok);
if(e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
return false;
}

View File

@@ -105,7 +105,7 @@ namespace Bit.App.Pages
}
catch(Exception e) when(e.Message.Contains("No key."))
{
await Task.Delay(5000);
await Task.Delay(1000);
await _vm.LoadAsync();
}
}

View File

@@ -37,6 +37,8 @@ namespace Bit.App.Pages
private readonly IFolderService _folderService;
private readonly ICollectionService _collectionService;
private readonly ISyncService _syncService;
private readonly IUserService _userService;
private readonly ILockService _lockService;
private readonly IDeviceActionService _deviceActionService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IMessagingService _messagingService;
@@ -48,6 +50,8 @@ namespace Bit.App.Pages
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_userService = ServiceContainer.Resolve<IUserService>("userService");
_lockService = ServiceContainer.Resolve<ILockService>("lockService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
@@ -134,6 +138,15 @@ namespace Bit.App.Pages
{
return;
}
var authed = await _userService.IsAuthenticatedAsync();
if(!authed)
{
return;
}
if(await _lockService.IsLockedAsync())
{
return;
}
_doingLoad = true;
LoadedOnce = true;
ShowNoData = false;
@@ -305,9 +318,17 @@ namespace Bit.App.Pages
return;
}
await _deviceActionService.ShowLoadingAsync(AppResources.Syncing);
await _syncService.FullSyncAsync(false);
await _deviceActionService.HideLoadingAsync();
_platformUtilsService.ShowToast("success", null, AppResources.SyncingComplete);
try
{
await _syncService.FullSyncAsync(false, true);
await _deviceActionService.HideLoadingAsync();
_platformUtilsService.ShowToast("success", null, AppResources.SyncingComplete);
}
catch
{
await _deviceActionService.HideLoadingAsync();
_platformUtilsService.ShowToast("error", null, AppResources.SyncingFailed);
}
}
private async Task LoadDataAsync()

View File

@@ -116,12 +116,19 @@ namespace Bit.App.Pages
catch(ApiException e)
{
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(), AppResources.Ok);
if(e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
catch(System.Exception e)
{
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Message, AppResources.Ok);
if(e.Message != null)
{
await _platformUtilsService.ShowDialogAsync(e.Message, AppResources.AnErrorHasOccurred);
}
}
return false;
}

View File

@@ -298,7 +298,11 @@ namespace Bit.App.Pages
catch(ApiException e)
{
await _deviceActionService.HideLoadingAsync();
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(), AppResources.Ok);
if(e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
return false;
}

View File

@@ -537,6 +537,24 @@ namespace Bit.App.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Biometrics.
/// </summary>
public static string Biometrics {
get {
return ResourceManager.GetString("Biometrics", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Use biometrics to verify..
/// </summary>
public static string BiometricsDirection {
get {
return ResourceManager.GetString("BiometricsDirection", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Bitwarden.
/// </summary>
@@ -2274,6 +2292,15 @@ namespace Bit.App.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Your login session has expired..
/// </summary>
public static string LoginExpired {
get {
return ResourceManager.GetString("LoginExpired", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Login.
/// </summary>
@@ -3813,6 +3840,15 @@ namespace Bit.App.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Use Biometrics To Unlock.
/// </summary>
public static string UseBiometricsToUnlock {
get {
return ResourceManager.GetString("UseBiometricsToUnlock", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Use Face ID To Unlock.
/// </summary>

View File

@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

File diff suppressed because it is too large Load Diff

View File

@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Commuta la visibilitat</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -997,7 +997,7 @@
<value>Bezpečnostní kód</value>
</data>
<data name="TypeCard" xml:space="preserve">
<value>Kreditní karta</value>
<value>Karta</value>
</data>
<data name="TypeIdentity" xml:space="preserve">
<value>Identita</value>
@@ -1239,7 +1239,7 @@
<value>Ano a uložit</value>
</data>
<data name="AutofillAndSave" xml:space="preserve">
<value>Automaticky vyplněno a uloženo.</value>
<value>Automaticky vyplnit a uložit</value>
</data>
<data name="Organization" xml:space="preserve">
<value>Organizace</value>
@@ -1458,7 +1458,7 @@
<value>Nastavte svůj PIN kód pro odemknutí trezoru. Pokud se zcela odhlásíte z aplikace bude váš současný PIN bude resetován.</value>
</data>
<data name="LoggedInAsOn" xml:space="preserve">
<value>Přihlášen jako {0} na {1}.</value>
<value>Přihlášen jako {0} na {1}.</value>
<comment>ex: Logged in as user@example.com on bitwarden.com.</comment>
</data>
<data name="VaultLockedMasterPassword" xml:space="preserve">
@@ -1542,13 +1542,13 @@
<value>URI na černé listině</value>
</data>
<data name="BlacklistedUrisDescription" xml:space="preserve">
<value>URIs that are blacklisted will not offer auto-fill. The list of apps should be comma separated. Ex: "https://twitter.com, androidapp://com.twitter.android".</value>
<value>Pro URI na černé listině nebude nabízeno automatické vyplnění. Záznamy oddělujte čárkou. Např.: https://twitter.com, androidapp://com.twitter.android.</value>
</data>
<data name="DisableSavePrompt" xml:space="preserve">
<value>Zakázat výzvu o uložení</value>
</data>
<data name="DisableSavePromptDescription" xml:space="preserve">
<value>"Výzva k uložení" vás automaticky vyzve k uložení nových položek do trezoru, kdykoli je zadáte poprvé.</value>
<value>Výzva k uložení vás automaticky vyzve k uložení nových položek do trezoru, kdykoli je poprvé zadáte.</value>
</data>
<data name="LockOptionOnRestart" xml:space="preserve">
<value>Při restartu aplikace</value>
@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Přepnout viditelnost</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Slå synlighed til/fra</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -753,7 +753,7 @@
<value>Status</value>
</data>
<data name="BitwardenAutofillServiceAlert2" xml:space="preserve">
<value>Die einfachste Möglichkeit, neue Anmeldedaten zu Ihrem Tresor hinzuzufügen, ist der automatische Fülldienst von Bitwarden. Erfahren Sie mehr über den automatische Fülldienst von Bitwarden, indem Sie zu dem "Einstellungen"-Bildschirm navigieren.</value>
<value>Die einfachste Möglichkeit, neue Anmeldedaten zu Ihrem Tresor hinzuzufügen, ist der automatische Fülldienst von Bitwarden. Erfahren Sie mehr über den automatischen Fülldienst von Bitwarden, indem Sie zu dem "Einstellungen"-Bildschirm navigieren.</value>
</data>
<data name="Autofill" xml:space="preserve">
<value>Auto-Ausfüllen</value>
@@ -1302,7 +1302,7 @@
<value>Passwort AutoFill</value>
</data>
<data name="BitwardenAutofillAlert2" xml:space="preserve">
<value>Die einfachste Möglichkeit, neue Anmeldedaten zu Ihrem Tresor hinzuzufügen, ist die Bitwardenerweiterung zum Automatische Füllen. Erfahren Sie mehr über die Bitwardenerweiterung zum Automatische Füllen, indem Sie zu dem "Einstellungen"-Bildschirm navigieren.</value>
<value>Die einfachste Möglichkeit, neue Anmeldedaten zu Ihrem Tresor hinzuzufügen, ist der automatische Fülldienst von Bitwarden. Erfahren Sie mehr über den automatischen Fülldienst von Bitwarden, indem Sie zu dem "Einstellungen"-Bildschirm navigieren.</value>
</data>
<data name="InvalidEmail" xml:space="preserve">
<value>Ungültige E-Mail-Adresse.</value>
@@ -1376,16 +1376,16 @@
<value>Nach oben bewegen</value>
</data>
<data name="Miscellaneous" xml:space="preserve">
<value>Verschiedenes</value>
<value>Sonstiges</value>
</data>
<data name="Ownership" xml:space="preserve">
<value>Besitzer</value>
<value>Besitz</value>
</data>
<data name="WhoOwnsThisItem" xml:space="preserve">
<value>Wer besitzt diesen Eintrag?</value>
</data>
<data name="NoCollectionsToList" xml:space="preserve">
<value>Keine Sammlungen zum Auflisten.</value>
<value>Keine Sammlungen vorhanden.</value>
</data>
<data name="ItemShared" xml:space="preserve">
<value>Eintrag wurde geteilt.</value>
@@ -1400,7 +1400,7 @@
<value>Eintrag teilen</value>
</data>
<data name="NoOrgsToList" xml:space="preserve">
<value>Keine Organisationen zum Auflisten.</value>
<value>Keine Organisationen vorhanden.</value>
</data>
<data name="ShareDesc" xml:space="preserve">
<value>Wählen Sie eine Organisation, mit der Sie dieses Eintrag teilen möchten. Teilen überträgt den Besitz des Eintrages an die Organisation. Sie werden nicht mehr der direkte Bestitzer dieses Eintrages sein, sobald es geteilt wurde.</value>
@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Sichtbarkeit umschalten</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle visibility</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Alternar visibilidad</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>تغییر نمایشگر</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1275,19 +1275,19 @@
<value>Your logins are now easily accessable right from your keyboard while logging into apps and websites.</value>
</data>
<data name="AutofillSetup2" xml:space="preserve">
<value>We recommend disabling any other AutoFill apps under Settings if you do not plan to use them.</value>
<value>Suosittelemme poistamaan muut AutoFill-sovellukset käytöstä asetuksista, jos et aio käyttää niitä.</value>
</data>
<data name="BitwardenAutofillDescription" xml:space="preserve">
<value>Access your vault directly from your keyboard to quickly autofill passwords.</value>
<value>Käytä holviasi suoraan näppäimistöltä täyttääksesi salasanat automaattisesti.</value>
</data>
<data name="AutofillTurnOn" xml:space="preserve">
<value>To enable password autofill on your device, follow these instructions:</value>
<value>Ota salasanojen automattinen täyttäminen käyttöön seuraavasti:</value>
</data>
<data name="AutofillTurnOn1" xml:space="preserve">
<value>1. Siirry iOS:n Asetukset-sovellukseen</value>
</data>
<data name="AutofillTurnOn2" xml:space="preserve">
<value>2. Tap "Passwords &amp; Accounts"</value>
<value>2. Napauta "Salasanat ja tilit"</value>
</data>
<data name="AutofillTurnOn3" xml:space="preserve">
<value>3. Tap "AutoFill Passwords"</value>
@@ -1557,22 +1557,34 @@
<value>Automaattinen täyttö tekee Bitwarden-holviiisi pääsystä muista ohjelmista ja sivustoista helppoa. Et näytä kytkeneen automaattista täyttöä päälle. Voit tehdä sen Asetukset-näytöstä.</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Your theme changes will apply when the app is restarted.</value>
<value>Teemamuutokset tulevat voimaan, kun sovellus käynnistetään uudelleen.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Capitalize</value>
<value>Jokainen sana isolla alkukirjaimella</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>Include Number</value>
<value>Sisällytä numero</value>
</data>
<data name="Download" xml:space="preserve">
<value>Download</value>
<value>Lataa</value>
</data>
<data name="Shared" xml:space="preserve">
<value>Shared</value>
<value>Jaettu</value>
</data>
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -356,7 +356,7 @@
<comment>Validation message for when a form field is left blank and is required to be entered.</comment>
</data>
<data name="ValueHasBeenCopied" xml:space="preserve">
<value>{0} a été copié.</value>
<value>{0} a été copié(e).</value>
<comment>Confirmation message after suceessfully copying a value to the clipboard.</comment>
</data>
<data name="VerifyFingerprint" xml:space="preserve">
@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Afficher/Masquer</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1576,4 +1576,16 @@ Bitwarden בעזרת פתיחת חלון "הגדרות".</value>
<data name="ToggleVisibility" xml:space="preserve">
<value>הצג או הסתר</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -412,7 +412,7 @@
<value>Bitwarden Proširenje aplikacije</value>
</data>
<data name="BitwardenAppExtensionAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is from the Bitwarden App Extension. Learn more about using the Bitwarden App Extension by navigating to the "Settings" screen.</value>
<value>Najlakši način za dodavanje novih prijava u Vaš trezor je iz proširenja aplikacije Bitwarden. Saznajte više o upotrebi proširenja aplikacije Bitwarden odlaskom na zaslon "Postavke".</value>
</data>
<data name="BitwardenAppExtensionDescription" xml:space="preserve">
<value>Upotrijebite Bitwarden u Safariju i drugim aplikacijama da biste automatski ispunili prijave.</value>
@@ -477,7 +477,7 @@
<value>Spremni ste se prijaviti!</value>
</data>
<data name="ExtensionSetup" xml:space="preserve">
<value>Vašim prijava se sada lako može pristupiti iz Safaria, Chromea i drugih podržanih aplikacija.</value>
<value>Vašim prijavama se sada lako može pristupiti iz Safaria, Chromea i drugih podržanih aplikacija.</value>
</data>
<data name="ExtensionSetup2" xml:space="preserve">
<value>U pregledniku Safari i Chrome pronađite Bitwarden upotrebom ikone za dijeljenje (savjet: pomaknite se desno u donjem retku izbornika za dijeljenje).</value>
@@ -753,7 +753,7 @@
<value>Status</value>
</data>
<data name="BitwardenAutofillServiceAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is from the Bitwarden Auto-fill Service. Learn more about using the Bitwarden Auto-fill Service by navigating to the "Settings" screen.</value>
<value>Najlakši način za dodavanje novih prijava na Vaš trezor je iz usluge s automatskim popunjavanjem Bitwarden. Saznajte više o upotrebi usluge automatskog popunjavanja usluge Bitwarden odlaskom na zaslon "Postavke".</value>
</data>
<data name="Autofill" xml:space="preserve">
<value>Automatsko popunjavanje</value>
@@ -1272,7 +1272,7 @@
<value>Morate se prijaviti u glavnu Bitwarden aplikaciju prije nego možete upotrijebljavati Automatsko popunjavanje.</value>
</data>
<data name="AutofillSetup" xml:space="preserve">
<value>Your logins are now easily accessable right from your keyboard while logging into apps and websites.</value>
<value>Vašim prijavama sada je lako pristupiti izravno s tipkovnice dok se prijavljujete u aplikacije i web stranice.</value>
</data>
<data name="AutofillSetup2" xml:space="preserve">
<value>Preporučujemo da onemogućite bilo koje druge aplikacije za automatsko popunjavanje u odjeljku Postavke ako ih ne namjeravate koristiti.</value>
@@ -1302,120 +1302,120 @@
<value>Automatsko popunjavanje zaporki</value>
</data>
<data name="BitwardenAutofillAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is by using the Bitwarden Password AutoFill extension. Learn more about using the Bitwarden Password AutoFill extension by navigating to the "Settings" screen.</value>
<value>Najlakši način za dodavanje novih prijava u Vaš trezor je korištenjem Bitwarden proširenja za automatsko popunjavanje lozinki. Saznajte više o korištenju Bitwarden proširenja za automatsko popunjavanje lozinki odlaskom na zaslon "Postavke".</value>
</data>
<data name="InvalidEmail" xml:space="preserve">
<value>Invalid email address.</value>
<value>Nevažeća adresa e-pošte.</value>
</data>
<data name="Cards" xml:space="preserve">
<value>Cards</value>
<value>Kartice</value>
</data>
<data name="Identities" xml:space="preserve">
<value>Identities</value>
<value>Identiteti</value>
</data>
<data name="Logins" xml:space="preserve">
<value>Logins</value>
<value>Prijave</value>
</data>
<data name="SecureNotes" xml:space="preserve">
<value>Secure Notes</value>
<value>Sigurne bilješke</value>
</data>
<data name="AllItems" xml:space="preserve">
<value>All Items</value>
<value>Sve stavke</value>
</data>
<data name="URIs" xml:space="preserve">
<value>URIs</value>
<value>URI</value>
<comment>Plural form of a URI</comment>
</data>
<data name="CheckingPassword" xml:space="preserve">
<value>Checking password...</value>
<value>Provjera lozinke...</value>
<comment>A loading message when doing an exposed password check.</comment>
</data>
<data name="CheckPassword" xml:space="preserve">
<value>Check if password has been exposed.</value>
<value>Provjerite je li lozinka kompromitirana.</value>
</data>
<data name="PasswordExposed" xml:space="preserve">
<value>This password has been exposed {0} time(s) in data breaches. You should change it.</value>
<value>Ova lozinka je kompromitirana {0} puta u krađi podataka. Trebali bi ste je promijeniti.</value>
</data>
<data name="PasswordSafe" xml:space="preserve">
<value>This password was not found in any known data breaches. It should be safe to use.</value>
<value>Lozinka nije pronađena niti u jednoj krađi podataka. Sigurna je za korištenje.</value>
</data>
<data name="IdentityName" xml:space="preserve">
<value>Identity Name</value>
<value>Ime identiteta</value>
</data>
<data name="Value" xml:space="preserve">
<value>Value</value>
<value>Vrijednost</value>
</data>
<data name="PasswordHistory" xml:space="preserve">
<value>Password History</value>
<value>Povijest lozinke</value>
</data>
<data name="Types" xml:space="preserve">
<value>Types</value>
<value>Vrste</value>
</data>
<data name="NoPasswordsToList" xml:space="preserve">
<value>No passwords to list.</value>
<value>Nema lozinki za popis.</value>
</data>
<data name="NoItemsToList" xml:space="preserve">
<value>There are no items to list.</value>
<value>Nema stavki za prikaz.</value>
</data>
<data name="SearchCollection" xml:space="preserve">
<value>Search collection</value>
<value>Zbirka pretraživanja</value>
</data>
<data name="SearchFolder" xml:space="preserve">
<value>Search folder</value>
<value>Mapa pretraživanja</value>
</data>
<data name="SearchType" xml:space="preserve">
<value>Search type</value>
<value>Tip pretrage</value>
</data>
<data name="Type" xml:space="preserve">
<value>Type</value>
<value>Vrsta</value>
</data>
<data name="MoveDown" xml:space="preserve">
<value>Move Down</value>
<value>Pomakni dolje</value>
</data>
<data name="MoveUp" xml:space="preserve">
<value>Move Up</value>
<value>Pomakni gore</value>
</data>
<data name="Miscellaneous" xml:space="preserve">
<value>Miscellaneous</value>
<value>Razno</value>
</data>
<data name="Ownership" xml:space="preserve">
<value>Ownership</value>
<value>Vlasništvo</value>
</data>
<data name="WhoOwnsThisItem" xml:space="preserve">
<value>Who owns this item?</value>
<value>Tko je vlasnik ove stavke?</value>
</data>
<data name="NoCollectionsToList" xml:space="preserve">
<value>There are no collections to list.</value>
<value>Nema zbirki za prikaz.</value>
</data>
<data name="ItemShared" xml:space="preserve">
<value>Item has been shared.</value>
<value>Stavka je podijeljena.</value>
</data>
<data name="SelectOneCollection" xml:space="preserve">
<value>You must select at least one collection.</value>
<value>Morate odabrati barem jednu zbirku.</value>
</data>
<data name="Share" xml:space="preserve">
<value>Share</value>
<value>Podijeli</value>
</data>
<data name="ShareItem" xml:space="preserve">
<value>Share Item</value>
<value>Podijeli stavku</value>
</data>
<data name="NoOrgsToList" xml:space="preserve">
<value>No organizations to list.</value>
<value>Nema organizacija za popis.</value>
</data>
<data name="ShareDesc" xml:space="preserve">
<value>Choose an organization that you wish to share this item with. Sharing transfers ownership of the item to the organization. You will no longer be the direct owner of this item once it has been shared.</value>
<value>Odaberite organizaciju s kojom želite podijeliti ovu stavku. Dijeljenje prenosi vlasništvo stavke na organizaciju. Više nećete biti izravni vlasnik ove stavke jednom kada je podijelite.</value>
</data>
<data name="NumberOfWords" xml:space="preserve">
<value>Number of Words</value>
<value>Broj riječi</value>
</data>
<data name="Passphrase" xml:space="preserve">
<value>Passphrase</value>
<value>Lozinka</value>
</data>
<data name="WordSeparator" xml:space="preserve">
<value>Word Separator</value>
<value>Razdjelitelj riječi</value>
</data>
<data name="Clear" xml:space="preserve">
<value>Clear</value>
<value>Očisti</value>
<comment>To clear something out. example: To clear browser history.</comment>
</data>
<data name="Generator" xml:space="preserve">
@@ -1423,156 +1423,168 @@
<comment>Short for "Password Generator"</comment>
</data>
<data name="NoFoldersToList" xml:space="preserve">
<value>There are no folders to list.</value>
<value>Nema mapa na popisu.</value>
</data>
<data name="FingerprintPhrase" xml:space="preserve">
<value>Fingerprint Phrase</value>
<value>Jedinstveni izraz</value>
<comment>A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing.</comment>
</data>
<data name="YourAccountsFingerprint" xml:space="preserve">
<value>Your account's fingerprint phrase</value>
<value>Jedinstveni izraz Vašeg računa</value>
<comment>A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing.</comment>
</data>
<data name="ShareVaultConfirmation" xml:space="preserve">
<value>Bitwarden allows you to share your vault with others by using an organization account. Would you like to visit the bitwarden.com website to learn more?</value>
<value>Bitwarden omogućuje dijeljenje trezora s drugima pomoću računa organizacije. Želite li posjetiti web stranicu bitwarden.com da biste saznali više?</value>
</data>
<data name="ExportVault" xml:space="preserve">
<value>Export Vault</value>
<value>Izvoz trezora</value>
</data>
<data name="LockNow" xml:space="preserve">
<value>Lock Now</value>
<value>Zaključaj sada</value>
</data>
<data name="PIN" xml:space="preserve">
<value>PIN</value>
</data>
<data name="Unlock" xml:space="preserve">
<value>Unlock</value>
<value>Otključaj</value>
</data>
<data name="LockOption30Minutes" xml:space="preserve">
<value>30 minutes</value>
<value>30 minuta</value>
</data>
<data name="LockOption5Minutes" xml:space="preserve">
<value>5 minutes</value>
<value>5 minuta</value>
</data>
<data name="SetPINDescription" xml:space="preserve">
<value>Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application.</value>
<value>Postavite Vaš PIN kod za otključavanje Bitwardena. Vaše postavke PIN-a će biti resetirane ako se ikada potpuno odjavite iz aplikacije.</value>
</data>
<data name="LoggedInAsOn" xml:space="preserve">
<value>Logged in as {0} on {1}.</value>
<value>Prijavljen kao {0} na {1}.</value>
<comment>ex: Logged in as user@example.com on bitwarden.com.</comment>
</data>
<data name="VaultLockedMasterPassword" xml:space="preserve">
<value>Your vault is locked. Verify your master password to continue.</value>
<value>Vaš trezor je zaključan. Potvrdite glavnu lozinku da biste nastavili.</value>
</data>
<data name="VaultLockedPIN" xml:space="preserve">
<value>Your vault is locked. Verify your PIN code to continue.</value>
<value>Vaš trezor je zaključan. Potvrdite Vaš PIN da biste nastavili.</value>
</data>
<data name="Dark" xml:space="preserve">
<value>Dark</value>
<value>Tamno</value>
<comment>A dark color</comment>
</data>
<data name="Light" xml:space="preserve">
<value>Light</value>
<value>Svjetlo</value>
<comment>A light color</comment>
</data>
<data name="FiveMinutes" xml:space="preserve">
<value>5 minutes</value>
<value>5 minuta</value>
</data>
<data name="OneMinute" xml:space="preserve">
<value>1 minute</value>
<value>1 minuta</value>
</data>
<data name="TenSeconds" xml:space="preserve">
<value>10 seconds</value>
<value>10 sekundi</value>
</data>
<data name="ThirtySeconds" xml:space="preserve">
<value>30 seconds</value>
<value>30 sekundi</value>
</data>
<data name="TwentySeconds" xml:space="preserve">
<value>20 seconds</value>
<value>20 sekundi</value>
</data>
<data name="TwoMinutes" xml:space="preserve">
<value>2 minutes</value>
<value>2 minute</value>
</data>
<data name="ClearClipboard" xml:space="preserve">
<value>Clear Clipboard</value>
<value>Očisti međuspremnik</value>
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data>
<data name="ClearClipboardDescription" xml:space="preserve">
<value>Automatically clear copied values from your clipboard.</value>
<value>Automatski očisti kopirane vrijednosti iz Vašeg međuspremnika.</value>
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data>
<data name="DefaultUriMatchDetection" xml:space="preserve">
<value>Default URI Match Detection</value>
<value>Zadano otkrivanje URL podudaranja</value>
<comment>Default URI match detection for auto-fill.</comment>
</data>
<data name="DefaultUriMatchDetectionDescription" xml:space="preserve">
<value>Choose the default way that URI match detection is handled for logins when performing actions such as auto-fill.</value>
<value>Odaberite zadani način na koji se otkriva URI podudaranje za prijave prilikom izvođenja radnji poput automatskog ispunjavanja.</value>
</data>
<data name="Theme" xml:space="preserve">
<value>Theme</value>
<value>Tema</value>
<comment>Color theme</comment>
</data>
<data name="ThemeDescription" xml:space="preserve">
<value>Change the application's color theme.</value>
<value>Promijenite temu boja aplikacije.</value>
</data>
<data name="RestartIsRequired" xml:space="preserve">
<value>Restart is required.</value>
<value>Potrebno je ponovo pokretanje.</value>
<comment>Referring to restarting the application.</comment>
</data>
<data name="Restarting" xml:space="preserve">
<value>Restarting...</value>
<value>Ponovno pokretanje...</value>
</data>
<data name="CopyNotes" xml:space="preserve">
<value>Copy Notes</value>
<value>Kopiraj bilješke</value>
</data>
<data name="Exit" xml:space="preserve">
<value>Exit</value>
<value>Izlaz</value>
</data>
<data name="ExitConfirmation" xml:space="preserve">
<value>Are you sure you want to exit Bitwarden?</value>
<value>Jeste li sigurni da želite izaći iz Bitwardena?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Želite li zahtijevati otključavanje s glavnom lozinkom prilikom ponovnog pokretanja aplikacije?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Black</value>
<value>Crno</value>
<comment>The color black</comment>
</data>
<data name="BlacklistedUris" xml:space="preserve">
<value>Blacklisted URIs</value>
<value>URI-i na crnom popisu</value>
</data>
<data name="BlacklistedUrisDescription" xml:space="preserve">
<value>URIs that are blacklisted will not offer auto-fill. The list of apps should be comma separated. Ex: "https://twitter.com, androidapp://com.twitter.android".</value>
<value>URI-ovi koji su na crnoj listi neće ponuditi automatsko popunjavanje. Popis aplikacija treba odvojiti zarezima. Na primjer: "https://twitter.com, androidapp://com.twitter.android".</value>
</data>
<data name="DisableSavePrompt" xml:space="preserve">
<value>Disable Save Prompt</value>
<value>Onemogući upit za spremanje</value>
</data>
<data name="DisableSavePromptDescription" xml:space="preserve">
<value>The "Save Prompt" automatically prompts you to save new items to your vault whenever you enter them for the first time.</value>
<value>"Upit za spremanje" automatski traži da spremite nove unose u trezor prvi put kad u njih uđete.</value>
</data>
<data name="LockOptionOnRestart" xml:space="preserve">
<value>On App Restart</value>
<value>Kada se aplikacija ponovno pokrene</value>
</data>
<data name="AutofillServiceNotEnabled" xml:space="preserve">
<value>Auto-fill makes it easy to securely access your Bitwarden vault from other websites and apps. It looks like you have not enabled an auto-fill service for Bitwarden. Enable auto-fill for Bitwarden from the "Settings" screen.</value>
<value>Automatsko punjenje olakšava siguran pristup Vašem Bitwarden trezoru s drugih web stranica i aplikacija. Izgleda da niste omogućili uslugu automatskog popunjavanja Bitwardena. Omogućite automatsko popunjavanje Bitwardena na zaslonu "Postavke".</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Your theme changes will apply when the app is restarted.</value>
<value>Promjene teme primijenit će se kad se aplikacija ponovo pokrene.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Capitalize</value>
<value>Pretvori u velika početna slova</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>Include Number</value>
<value>Uključi broj</value>
</data>
<data name="Download" xml:space="preserve">
<value>Download</value>
<value>Preuzmi</value>
</data>
<data name="Shared" xml:space="preserve">
<value>Shared</value>
<value>Dijeljeno</value>
</data>
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
<value>Promijeni vidljivost</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -323,7 +323,7 @@
<comment>Reveal a hidden value (password).</comment>
</data>
<data name="ItemDeleted" xml:space="preserve">
<value>Elem törölve.</value>
<value>Az elem törlésre került.</value>
<comment>Confirmation message after successfully deleting a login.</comment>
</data>
<data name="Submit" xml:space="preserve">
@@ -378,7 +378,7 @@
<value>Látogasd meg a weboldalunkat</value>
</data>
<data name="VisitOurWebsiteDescription" xml:space="preserve">
<value>Látogasd meg a weboldalunkat segítség kéréshez, hírekért, küldj nekünk e-mailt és/vagy tanuld meg hogyan használd a bitwarden-t.</value>
<value>A fejlesztői webhely felkeresése segítségért, hírekért, email küldése a fejlesztőknek és/vagy további információk a Bitwarden használatáról.</value>
</data>
<data name="Website" xml:space="preserve">
<value>Weboldal</value>
@@ -400,7 +400,7 @@
<value>App kiterjesztés</value>
</data>
<data name="AutofillAccessibilityDescription" xml:space="preserve">
<value>A bitwarden kisegítő szolgáltatás használata, mely automatikusan kitölti a bejelentkezési adataidat akár egy app-ban, akár webes felületeken.</value>
<value>A Bitwarden kisegítő szolgáltatás használata bejelentkezések automatikus kitöltéséhez alkalmazásból és weben.</value>
</data>
<data name="AutofillService" xml:space="preserve">
<value>Automatikus kitöltő szolgáltatás</value>
@@ -409,19 +409,19 @@
<value>Félreérthető karakterek mellőzése</value>
</data>
<data name="BitwardenAppExtension" xml:space="preserve">
<value>bitwarden alkalmazás</value>
<value>Bitwarden alkalmazás bővítmény</value>
</data>
<data name="BitwardenAppExtensionAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is from the Bitwarden App Extension. Learn more about using the Bitwarden App Extension by navigating to the "Settings" screen.</value>
<value>A legkönnyebb módja új bejelentkezések széfbe helyezéséhez a Bitwarden alkalmazás kiegészítőből. További információ olvasható a Bitwarden alkalmazás kiegészítő használatáról a "Beállítások" képernyőn.</value>
</data>
<data name="BitwardenAppExtensionDescription" xml:space="preserve">
<value>Használd a bitwarden automatikus kitöltő szolgáltatását a Safari-ban és más alkalmazásokban.</value>
<value>A Bitwarden használata Safariban és más alkalmazásokban a bejelentkezések automatikus kitöltéséhez.</value>
</data>
<data name="BitwardenAutofillService" xml:space="preserve">
<value>Bitwarden automatikus kitöltő szolgáltatás</value>
</data>
<data name="BitwardenAutofillAccessibilityServiceDescription" xml:space="preserve">
<value>A bitwarden kisegítő szolgáltatás használata bejelentkezések automatikus kitöltéséhez.</value>
<value>A Bitwarden kisegítő szolgáltatás használata bejelentkezések automatikus kitöltéséhez.</value>
</data>
<data name="ChangeEmail" xml:space="preserve">
<value>E-mail cím módosítása</value>
@@ -467,7 +467,7 @@
<value>Bővítmény engedélyezése</value>
</data>
<data name="ExtensionInSafari" xml:space="preserve">
<value>A Safari-ban a bitwarden-t a megosztás ikon alatt találod (Tipp: a menü alsó sorában gördítsd jobbra).</value>
<value>A Safariban keressük meg a Bitwardent a megosztás ikon használatával (tipp: gördítés a menü alsó sorának jobb oldalára).</value>
<comment>Safari is the name of apple's web browser</comment>
</data>
<data name="ExtensionInstantAccess" xml:space="preserve">
@@ -477,16 +477,16 @@
<value>Készen állsz a bejelentkezéshez!</value>
</data>
<data name="ExtensionSetup" xml:space="preserve">
<value>A bejelentkezéseid mostantól könnyen hozzáférhetők a Safari-ból, Chrome-ból és más támogatott alkalmazásból.</value>
<value>A bejelentkezések mostantól könnyen hozzáférhetők a Safariból, a Chrome-ból és más támogatott alkalmazásból.</value>
</data>
<data name="ExtensionSetup2" xml:space="preserve">
<value>A Safari-ban és a Chrome-ban a bitwarden-t a megosztás ikon alatt találod (Tipp: a menü alsó sorában gördítsd jobbra).</value>
<value>A Safariban és a Chrome-ban keressük meg a Bitwardent a megosztás ikon használatával (tipp: gördítés a menü alsó sorának jobb oldalára).</value>
</data>
<data name="ExtensionTapIcon" xml:space="preserve">
<value>A bővítmény elindításához bökj a bitwarden ikonra a menüben.</value>
<value>Koppintás a Bitwarden ikonra a menüben a bővítmény elindításához.</value>
</data>
<data name="ExtensionTurnOn" xml:space="preserve">
<value>A bitwarden Safari-ban és más alkalmazásokban való bekapcsolásához bökj az "Egyéb" ikonra a menü alsó sorában.</value>
<value>A Safariban és más alkalmazásban a Bitwarden bekapcsolásához koppintsunk az "egyéb" ikonra a menü alsó sorában.</value>
</data>
<data name="Favorite" xml:space="preserve">
<value>Kedvenc</value>
@@ -753,7 +753,7 @@
<value>Állapot</value>
</data>
<data name="BitwardenAutofillServiceAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is from the Bitwarden Auto-fill Service. Learn more about using the Bitwarden Auto-fill Service by navigating to the "Settings" screen.</value>
<value>A legkönnyebb módja új bejelentkezések széfbe helyezéséhez a Bitwarden automatikus kitöltés szolgáltatásból. További információ olvasható a Bitwarden automatikus kitöltés szolgáltatás használatáról a "Beállítások" képernyőn.</value>
</data>
<data name="Autofill" xml:space="preserve">
<value>Automatikus kitöltés</value>
@@ -1302,7 +1302,7 @@
<value>Jelszó Automatikus kitöltése</value>
</data>
<data name="BitwardenAutofillAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is by using the Bitwarden Password AutoFill extension. Learn more about using the Bitwarden Password AutoFill extension by navigating to the "Settings" screen.</value>
<value>A legkönnyebb módja új bejelentkezések széfbe helyezéséhez a Bitwarden automatikus jelszó kitöltés bővítmény. További információ olvasható a Bitwarden automatikus jelszó kitöltés bővítmény használatáról a "Beállítások" képernyőn.</value>
</data>
<data name="InvalidEmail" xml:space="preserve">
<value>Érvénytelen e-mail cím.</value>
@@ -1403,7 +1403,7 @@
<value>Nincs megjeleníthető szervezet.</value>
</data>
<data name="ShareDesc" xml:space="preserve">
<value>Choose an organization that you wish to share this item with. Sharing transfers ownership of the item to the organization. You will no longer be the direct owner of this item once it has been shared.</value>
<value>Szervezet választása az elem megosztásához. Az elemtulajdon átadása a szervezetnek. Innentől nem közvetlen tulajdonosa leszünk az elemnek a megosztás után.</value>
</data>
<data name="NumberOfWords" xml:space="preserve">
<value>Szavak száma</value>
@@ -1455,7 +1455,7 @@
<value>5 perc</value>
</data>
<data name="SetPINDescription" xml:space="preserve">
<value>Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application.</value>
<value>A pinkód beállítása a Bitwarden feloldásához. A pinkód beállítás alaphelyzetbe kerül, ha teljesen kijelentkezünk az alkalmazásból.</value>
</data>
<data name="LoggedInAsOn" xml:space="preserve">
<value>Bejelentkezve, mint {0} - {1}.</value>
@@ -1506,7 +1506,7 @@
<comment>Default URI match detection for auto-fill.</comment>
</data>
<data name="DefaultUriMatchDetectionDescription" xml:space="preserve">
<value>Choose the default way that URI match detection is handled for logins when performing actions such as auto-fill.</value>
<value>Az URI egyezés érzékelés alapértelmezett módjának kiválasztása a bejelentkezéseknél olyan műveletek esetében mint az automatikus kitöltés.</value>
</data>
<data name="Theme" xml:space="preserve">
<value>Téma</value>
@@ -1539,25 +1539,25 @@
<comment>The color black</comment>
</data>
<data name="BlacklistedUris" xml:space="preserve">
<value>Blacklisted URIs</value>
<value>Feketelistán levő URI elemek</value>
</data>
<data name="BlacklistedUrisDescription" xml:space="preserve">
<value>URIs that are blacklisted will not offer auto-fill. The list of apps should be comma separated. Ex: "https://twitter.com, androidapp://com.twitter.android".</value>
</data>
<data name="DisableSavePrompt" xml:space="preserve">
<value>Disable Save Prompt</value>
<value>Mentés felkérés letiltása</value>
</data>
<data name="DisableSavePromptDescription" xml:space="preserve">
<value>The "Save Prompt" automatically prompts you to save new items to your vault whenever you enter them for the first time.</value>
<value>A "Bejelentkezés felkérés hozzáadása" automatikusan felajánlja a bejelentkezés széfbe mentését az első bejelentkezéskor.</value>
</data>
<data name="LockOptionOnRestart" xml:space="preserve">
<value>Újraindításkor</value>
</data>
<data name="AutofillServiceNotEnabled" xml:space="preserve">
<value>Auto-fill makes it easy to securely access your Bitwarden vault from other websites and apps. It looks like you have not enabled an auto-fill service for Bitwarden. Enable auto-fill for Bitwarden from the "Settings" screen.</value>
<value>Az automatikus kitöltés megkönnyíti a Bitwarden széf biztonságos elérését más webhelyekről és alkalmazásokból. Úgy tűnik, hogy nem engedélyezett az automatikus kitöltés a Bitwardennél. Az automatikus kitöltés a Bitwardennél a "Beállítások" képernyőn engedélyezhető.</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Your theme changes will apply when the app is restarted.</value>
<value>A témaváltás az alkalmazás újraindítása után lép életbe.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Nagy kezdőbetű</value>
@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1305,33 +1305,33 @@
<value>The easiest way to add new logins to your vault is by using the Bitwarden Password AutoFill extension. Learn more about using the Bitwarden Password AutoFill extension by navigating to the "Settings" screen.</value>
</data>
<data name="InvalidEmail" xml:space="preserve">
<value>Invalid email address.</value>
<value>Alamat email tidak valid.</value>
</data>
<data name="Cards" xml:space="preserve">
<value>Cards</value>
<value>Kartu</value>
</data>
<data name="Identities" xml:space="preserve">
<value>Identities</value>
<value>Identitas</value>
</data>
<data name="Logins" xml:space="preserve">
<value>Logins</value>
<value>Info Masuk</value>
</data>
<data name="SecureNotes" xml:space="preserve">
<value>Secure Notes</value>
<value>Catatan Aman</value>
</data>
<data name="AllItems" xml:space="preserve">
<value>All Items</value>
<value>Semua Item</value>
</data>
<data name="URIs" xml:space="preserve">
<value>URIs</value>
<value>URI</value>
<comment>Plural form of a URI</comment>
</data>
<data name="CheckingPassword" xml:space="preserve">
<value>Checking password...</value>
<value>Memeriksa kata sandi...</value>
<comment>A loading message when doing an exposed password check.</comment>
</data>
<data name="CheckPassword" xml:space="preserve">
<value>Check if password has been exposed.</value>
<value>Periksalah jika kata sandi telah terpapar.</value>
</data>
<data name="PasswordExposed" xml:space="preserve">
<value>This password has been exposed {0} time(s) in data breaches. You should change it.</value>
@@ -1449,10 +1449,10 @@
<value>Unlock</value>
</data>
<data name="LockOption30Minutes" xml:space="preserve">
<value>30 minutes</value>
<value>30 menit</value>
</data>
<data name="LockOption5Minutes" xml:space="preserve">
<value>5 minutes</value>
<value>5 menit</value>
</data>
<data name="SetPINDescription" xml:space="preserve">
<value>Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application.</value>
@@ -1476,22 +1476,22 @@
<comment>A light color</comment>
</data>
<data name="FiveMinutes" xml:space="preserve">
<value>5 minutes</value>
<value>5 menit</value>
</data>
<data name="OneMinute" xml:space="preserve">
<value>1 minute</value>
<value>1 menit</value>
</data>
<data name="TenSeconds" xml:space="preserve">
<value>10 seconds</value>
<value>10 detik</value>
</data>
<data name="ThirtySeconds" xml:space="preserve">
<value>30 seconds</value>
<value>30 detik</value>
</data>
<data name="TwentySeconds" xml:space="preserve">
<value>20 seconds</value>
<value>20 detik</value>
</data>
<data name="TwoMinutes" xml:space="preserve">
<value>2 minutes</value>
<value>2 menit</value>
</data>
<data name="ClearClipboard" xml:space="preserve">
<value>Clear Clipboard</value>
@@ -1509,70 +1509,82 @@
<value>Choose the default way that URI match detection is handled for logins when performing actions such as auto-fill.</value>
</data>
<data name="Theme" xml:space="preserve">
<value>Theme</value>
<value>Tema</value>
<comment>Color theme</comment>
</data>
<data name="ThemeDescription" xml:space="preserve">
<value>Change the application's color theme.</value>
<value>Ubah warna tema aplikasi.</value>
</data>
<data name="RestartIsRequired" xml:space="preserve">
<value>Restart is required.</value>
<value>Dibutuhkan pemulaian ulang.</value>
<comment>Referring to restarting the application.</comment>
</data>
<data name="Restarting" xml:space="preserve">
<value>Restarting...</value>
<value>Memulai ulang...</value>
</data>
<data name="CopyNotes" xml:space="preserve">
<value>Copy Notes</value>
<value>Catatan Salinan</value>
</data>
<data name="Exit" xml:space="preserve">
<value>Exit</value>
<value>Keluar</value>
</data>
<data name="ExitConfirmation" xml:space="preserve">
<value>Are you sure you want to exit Bitwarden?</value>
<value>Apakah anda yakin ingin keluar dari Bitwarden?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Black</value>
<value>Hitam</value>
<comment>The color black</comment>
</data>
<data name="BlacklistedUris" xml:space="preserve">
<value>Blacklisted URIs</value>
<value>Daftar hitam URI</value>
</data>
<data name="BlacklistedUrisDescription" xml:space="preserve">
<value>URIs that are blacklisted will not offer auto-fill. The list of apps should be comma separated. Ex: "https://twitter.com, androidapp://com.twitter.android".</value>
</data>
<data name="DisableSavePrompt" xml:space="preserve">
<value>Disable Save Prompt</value>
<value>Nonaktifkan Saran Simpan</value>
</data>
<data name="DisableSavePromptDescription" xml:space="preserve">
<value>The "Save Prompt" automatically prompts you to save new items to your vault whenever you enter them for the first time.</value>
<value>"Saran Simpan" secara otomatis meminta anda untuk menyimpan item baru ke brankas anda setiap kali anda memasukkannya untuk pertama kali.</value>
</data>
<data name="LockOptionOnRestart" xml:space="preserve">
<value>On App Restart</value>
<value>Saat Aplikasi Memulai Ulang</value>
</data>
<data name="AutofillServiceNotEnabled" xml:space="preserve">
<value>Auto-fill makes it easy to securely access your Bitwarden vault from other websites and apps. It looks like you have not enabled an auto-fill service for Bitwarden. Enable auto-fill for Bitwarden from the "Settings" screen.</value>
<value>Isi-otomatis memudahkan untuk mengakses brankas Bitwarden anda dengan aman dari situs web dan aplikasi lain. Sepertinya anda belum mengaktifkan layanan pengisian-otomatis untuk Bitwarden. Aktifkan pengisian-otomatis untuk Bitwarden dari layar "Pengaturan".</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Your theme changes will apply when the app is restarted.</value>
<value>Perubahan tema anda akan diterapkan setelah aplikasi dimulai ulang.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Capitalize</value>
<value>Jadikan Huruf Besar</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>Include Number</value>
<value>Sertakan Digit</value>
</data>
<data name="Download" xml:space="preserve">
<value>Download</value>
<value>Unduh</value>
</data>
<data name="Shared" xml:space="preserve">
<value>Shared</value>
<value>Dibagikan</value>
</data>
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
<value>Alihkan Visibilitas</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Mostra/Nascondi</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -753,7 +753,7 @@
<value>状態</value>
</data>
<data name="BitwardenAutofillServiceAlert2" xml:space="preserve">
<value>Bitwarden 自動入力サービスから新しいログイン情報を保管庫に追加するのが一番簡単です。設定画面で自動入力サービスの詳細を確認できます。</value>
<value>ログイン情報を保管庫に追加する一番簡単な方法はBitwardenの自動入力機能を使うことです。詳しくは「設定画面に進んでください。</value>
</data>
<data name="Autofill" xml:space="preserve">
<value>自動入力</value>
@@ -1272,7 +1272,7 @@
<value>自動入力を使うには、まず Bitwarden アプリでログインしてください。</value>
</data>
<data name="AutofillSetup" xml:space="preserve">
<value>Your logins are now easily accessable right from your keyboard while logging into apps and websites.</value>
<value>アプリやサイトにログインするときに、ログイン情報に簡単にキーボードでアクセス可能となります。</value>
</data>
<data name="AutofillSetup2" xml:space="preserve">
<value>使う予定が無い場合は、設定で他の自動入力アプリを無効化することをおすすめします。</value>
@@ -1302,7 +1302,7 @@
<value>パスワード自動入力</value>
</data>
<data name="BitwardenAutofillAlert2" xml:space="preserve">
<value>Bitwarden パスワード自動入力拡張機能を使うと、簡単に新しいログイン情報を保管庫に追加できます。設定画面で詳しい使い方をご覧ください。</value>
<value>ログイン情報を保管庫に加える一番簡単な方法は、Bitwardenパスワード自動入力拡張機能を使うことです。詳しい情報は「設定画面でご覧ください。</value>
</data>
<data name="InvalidEmail" xml:space="preserve">
<value>無効なメールアドレスです。</value>
@@ -1317,7 +1317,7 @@
<value>ログイン</value>
</data>
<data name="SecureNotes" xml:space="preserve">
<value>セキュアメモ</value>
<value>秘密のメモ</value>
</data>
<data name="AllItems" xml:space="preserve">
<value>すべてのアイテム</value>
@@ -1334,10 +1334,10 @@
<value>パスワードが漏洩していないか確認する</value>
</data>
<data name="PasswordExposed" xml:space="preserve">
<value>このパスワードは過去に {0} 回漏洩したことがあるため、変更するべきです。</value>
<value>このパスワードは過去に {0} 回漏洩しています。変更するべきです。</value>
</data>
<data name="PasswordSafe" xml:space="preserve">
<value>このパスワードは過去に漏洩したデータにはないため、安全であると思われます。</value>
<value>このパスワードは過去に漏洩したデータにはありません。安全だと思われます。</value>
</data>
<data name="IdentityName" xml:space="preserve">
<value>固有名</value>
@@ -1364,7 +1364,7 @@
<value>フォルダーの検索</value>
</data>
<data name="SearchType" xml:space="preserve">
<value>検索の種類</value>
<value>タイプの検索</value>
</data>
<data name="Type" xml:space="preserve">
<value>タイプ</value>
@@ -1391,7 +1391,7 @@
<value>アイテムを共有しました。</value>
</data>
<data name="SelectOneCollection" xml:space="preserve">
<value>最低でも一つのコレクションを選んでください。</value>
<value>コレクションを最低1つは選んでください。</value>
</data>
<data name="Share" xml:space="preserve">
<value>共有</value>
@@ -1458,7 +1458,7 @@
<value>Bitwarden のロックを解除するための PIN コードを設定します。アプリから完全にログアウトすると、PIN 設定はリセットされます。</value>
</data>
<data name="LoggedInAsOn" xml:space="preserve">
<value>{1} で {0} としてログインしました。</value>
<value>{0} として {1} にログインしました。</value>
<comment>ex: Logged in as user@example.com on bitwarden.com.</comment>
</data>
<data name="VaultLockedMasterPassword" xml:space="preserve">
@@ -1498,7 +1498,7 @@
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data>
<data name="ClearClipboardDescription" xml:space="preserve">
<value>選択した時間が経過した後、自動的にクリップボード消去します。</value>
<value>コピー内容をクリップボードから自動的に消去します。</value>
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data>
<data name="DefaultUriMatchDetection" xml:space="preserve">
@@ -1506,7 +1506,7 @@
<comment>Default URI match detection for auto-fill.</comment>
</data>
<data name="DefaultUriMatchDetectionDescription" xml:space="preserve">
<value>自動入力などのアクションをする時に、デフォルトでどの方法で URI一致検出するか選択します。</value>
<value>自動入力などを行うときにデフォルトで使うURI一致検出方法を選択します。</value>
</data>
<data name="Theme" xml:space="preserve">
<value>テーマ</value>
@@ -1532,7 +1532,7 @@
<value>Bitwarden を終了しますか?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>アプリを再起動したときにマスターパスワードでのロック解除を必須としますか?</value>
</data>
<data name="Black" xml:space="preserve">
<value>ブラック</value>
@@ -1542,13 +1542,13 @@
<value>URI ブラックリスト</value>
</data>
<data name="BlacklistedUrisDescription" xml:space="preserve">
<value>URIs that are blacklisted will not offer auto-fill. The list of apps should be comma separated. Ex: "https://twitter.com, androidapp://com.twitter.android".</value>
<value>ブラックリストに入れたURIでは自動入力を行いません。リストはコンマで区切ってください。"https://twitter.com, androidapp://com.twitter.android"</value>
</data>
<data name="DisableSavePrompt" xml:space="preserve">
<value>保存プロンプトを無効化</value>
</data>
<data name="DisableSavePromptDescription" xml:space="preserve">
<value>保存プロンプトは、新しいアイテムを入力したとき保管庫に保存するかどうか尋ねるため自動的に表示されます。</value>
<value>新しいアイテムを入力したとき保管庫に保存するかどうか尋ねるためのプロンプトを自動的に表示ます。</value>
</data>
<data name="LockOptionOnRestart" xml:space="preserve">
<value>アプリ再起動時</value>
@@ -1557,14 +1557,14 @@
<value>自動入力を使うと、他のウェブサイトやアプリから Bitwarden 保管庫に、より安全・簡単にアクセスできます。現在 Bitwarden の自動入力サービスを有効にしていないようです。設定画面で自動入力を有効化できます。</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>アプリを再起動するとテーマの変更が反映されます。</value>
<value>テーマの変更はアプリを再起動すると反映されます。</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>先頭を大文字</value>
<value>英単語の先頭を大文字にする</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>数字を含む</value>
<value>数字を付ける</value>
</data>
<data name="Download" xml:space="preserve">
<value>ダウンロード</value>
@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>表示切り替え</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1308,22 +1308,22 @@
<value>잘못된 이메일 주소입니다.</value>
</data>
<data name="Cards" xml:space="preserve">
<value>Cards</value>
<value>카드</value>
</data>
<data name="Identities" xml:space="preserve">
<value>Identities</value>
<value>신원</value>
</data>
<data name="Logins" xml:space="preserve">
<value>Logins</value>
<value>로그인</value>
</data>
<data name="SecureNotes" xml:space="preserve">
<value>Secure Notes</value>
<value>보안 메모</value>
</data>
<data name="AllItems" xml:space="preserve">
<value>All Items</value>
<value>모든 항목</value>
</data>
<data name="URIs" xml:space="preserve">
<value>URIs</value>
<value>URI</value>
<comment>Plural form of a URI</comment>
</data>
<data name="CheckingPassword" xml:space="preserve">
@@ -1334,25 +1334,25 @@
<value>비밀번호가 노출되었는지 확인합니다.</value>
</data>
<data name="PasswordExposed" xml:space="preserve">
<value>This password has been exposed {0} time(s) in data breaches. You should change it.</value>
<value>이 비밀번호는 데이터 유출에 {0}회 노출되었습니다. 비밀번호를 변경하는 것이 좋습니다.</value>
</data>
<data name="PasswordSafe" xml:space="preserve">
<value>This password was not found in any known data breaches. It should be safe to use.</value>
<value>이 비밀번호는 데이터 유출 목록에 없습니다. 사용하기에 안전한 비밀번호입니다.</value>
</data>
<data name="IdentityName" xml:space="preserve">
<value>Identity Name</value>
<value>ID 이름</value>
</data>
<data name="Value" xml:space="preserve">
<value>값</value>
</data>
<data name="PasswordHistory" xml:space="preserve">
<value>Password History</value>
<value>비밀번호 변경 기록</value>
</data>
<data name="Types" xml:space="preserve">
<value>유형</value>
</data>
<data name="NoPasswordsToList" xml:space="preserve">
<value>No passwords to list.</value>
<value>비밀번호가 없습니다.</value>
</data>
<data name="NoItemsToList" xml:space="preserve">
<value>항목이 없습니다.</value>
@@ -1364,66 +1364,66 @@
<value>폴더 검색</value>
</data>
<data name="SearchType" xml:space="preserve">
<value>Search type</value>
<value>검색 유형</value>
</data>
<data name="Type" xml:space="preserve">
<value>유형</value>
</data>
<data name="MoveDown" xml:space="preserve">
<value>Move Down</value>
<value>아래로 옮기기</value>
</data>
<data name="MoveUp" xml:space="preserve">
<value>Move Up</value>
<value>위로 옮기기</value>
</data>
<data name="Miscellaneous" xml:space="preserve">
<value>Miscellaneous</value>
<value>기타</value>
</data>
<data name="Ownership" xml:space="preserve">
<value>Ownership</value>
<value>소유자</value>
</data>
<data name="WhoOwnsThisItem" xml:space="preserve">
<value>Who owns this item?</value>
<value>이 항목의 소유자는 누구입니까?</value>
</data>
<data name="NoCollectionsToList" xml:space="preserve">
<value>There are no collections to list.</value>
<value>컬렉션이 없습니다.</value>
</data>
<data name="ItemShared" xml:space="preserve">
<value>Item has been shared.</value>
<value>항목이 공유되었습니다.</value>
</data>
<data name="SelectOneCollection" xml:space="preserve">
<value>You must select at least one collection.</value>
</data>
<data name="Share" xml:space="preserve">
<value>Share</value>
<value>공유</value>
</data>
<data name="ShareItem" xml:space="preserve">
<value>Share Item</value>
<value>항목 공유</value>
</data>
<data name="NoOrgsToList" xml:space="preserve">
<value>No organizations to list.</value>
<value>조직이 없습니다.</value>
</data>
<data name="ShareDesc" xml:space="preserve">
<value>Choose an organization that you wish to share this item with. Sharing transfers ownership of the item to the organization. You will no longer be the direct owner of this item once it has been shared.</value>
<value>이 항목을 공유할 조직을 선택하세요. 항목을 공유하면 소유권이 조직으로 이전됩니다. 일단 항목을 공유하면, 항목의 직접적인 소유자가 아니게 됩니다.</value>
</data>
<data name="NumberOfWords" xml:space="preserve">
<value>Number of Words</value>
<value>단어 수</value>
</data>
<data name="Passphrase" xml:space="preserve">
<value>Passphrase</value>
<value>패스프레이즈</value>
</data>
<data name="WordSeparator" xml:space="preserve">
<value>Word Separator</value>
<value>구분 기호</value>
</data>
<data name="Clear" xml:space="preserve">
<value>Clear</value>
<value>삭제</value>
<comment>To clear something out. example: To clear browser history.</comment>
</data>
<data name="Generator" xml:space="preserve">
<value>Generator</value>
<value>생성기</value>
<comment>Short for "Password Generator"</comment>
</data>
<data name="NoFoldersToList" xml:space="preserve">
<value>There are no folders to list.</value>
<value>폴더가 없습니다.</value>
</data>
<data name="FingerprintPhrase" xml:space="preserve">
<value>Fingerprint Phrase</value>
@@ -1437,22 +1437,22 @@
<value>Bitwarden allows you to share your vault with others by using an organization account. Would you like to visit the bitwarden.com website to learn more?</value>
</data>
<data name="ExportVault" xml:space="preserve">
<value>Export Vault</value>
<value>보관함 내보내기</value>
</data>
<data name="LockNow" xml:space="preserve">
<value>Lock Now</value>
<value>지금 잠그기</value>
</data>
<data name="PIN" xml:space="preserve">
<value>PIN</value>
</data>
<data name="Unlock" xml:space="preserve">
<value>Unlock</value>
<value>잠금 해제</value>
</data>
<data name="LockOption30Minutes" xml:space="preserve">
<value>30 minutes</value>
<value>30</value>
</data>
<data name="LockOption5Minutes" xml:space="preserve">
<value>5 minutes</value>
<value>5</value>
</data>
<data name="SetPINDescription" xml:space="preserve">
<value>Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application.</value>
@@ -1468,52 +1468,52 @@
<value>Your vault is locked. Verify your PIN code to continue.</value>
</data>
<data name="Dark" xml:space="preserve">
<value>Dark</value>
<value>어두운 테마</value>
<comment>A dark color</comment>
</data>
<data name="Light" xml:space="preserve">
<value>Light</value>
<value>밝은 테마</value>
<comment>A light color</comment>
</data>
<data name="FiveMinutes" xml:space="preserve">
<value>5 minutes</value>
<value>5</value>
</data>
<data name="OneMinute" xml:space="preserve">
<value>1 minute</value>
<value>1</value>
</data>
<data name="TenSeconds" xml:space="preserve">
<value>10 seconds</value>
<value>10</value>
</data>
<data name="ThirtySeconds" xml:space="preserve">
<value>30 seconds</value>
<value>30</value>
</data>
<data name="TwentySeconds" xml:space="preserve">
<value>20 seconds</value>
<value>20</value>
</data>
<data name="TwoMinutes" xml:space="preserve">
<value>2 minutes</value>
<value>2</value>
</data>
<data name="ClearClipboard" xml:space="preserve">
<value>Clear Clipboard</value>
<value>클립보드 비우기</value>
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data>
<data name="ClearClipboardDescription" xml:space="preserve">
<value>Automatically clear copied values from your clipboard.</value>
<value>클립보드에 복사된 값을 자동으로 제거합니다.</value>
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data>
<data name="DefaultUriMatchDetection" xml:space="preserve">
<value>Default URI Match Detection</value>
<value>기본 URI 일치 인식</value>
<comment>Default URI match detection for auto-fill.</comment>
</data>
<data name="DefaultUriMatchDetectionDescription" xml:space="preserve">
<value>Choose the default way that URI match detection is handled for logins when performing actions such as auto-fill.</value>
</data>
<data name="Theme" xml:space="preserve">
<value>Theme</value>
<value>테마</value>
<comment>Color theme</comment>
</data>
<data name="ThemeDescription" xml:space="preserve">
<value>Change the application's color theme.</value>
<value>애플리케이션의 색상 테마를 변경합니다.</value>
</data>
<data name="RestartIsRequired" xml:space="preserve">
<value>Restart is required.</value>
@@ -1523,10 +1523,10 @@
<value>Restarting...</value>
</data>
<data name="CopyNotes" xml:space="preserve">
<value>Copy Notes</value>
<value>메모 복사</value>
</data>
<data name="Exit" xml:space="preserve">
<value>Exit</value>
<value>끝내기</value>
</data>
<data name="ExitConfirmation" xml:space="preserve">
<value>Are you sure you want to exit Bitwarden?</value>
@@ -1535,7 +1535,7 @@
<value>You you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Black</value>
<value>검은 테마</value>
<comment>The color black</comment>
</data>
<data name="BlacklistedUris" xml:space="preserve">
@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1557,22 +1557,34 @@
<value>Auto-utfylling gjør det lett å få sikker tilgang til Bitwarden-hvelvet ditt fra andre nettsteder og apper. Det ser ut til at du ikke har skrudd på noen auto-utfyllingstjenester for Bitwarden. Du kan skru på auto-utfylling for Bitwarden i «Innstillinger»-menyen.</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Your theme changes will apply when the app is restarted.</value>
<value>Temaendringene dine vil gjelde når appen startes på nytt.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Capitalize</value>
<value>Stor forbokstav</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>Include Number</value>
<value>Inkluder nummer</value>
</data>
<data name="Download" xml:space="preserve">
<value>Download</value>
<value>Last ned</value>
</data>
<data name="Shared" xml:space="preserve">
<value>Shared</value>
<value>Delt</value>
</data>
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

File diff suppressed because it is too large Load Diff

View File

@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Pokaż / Ukryj</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1560,7 +1560,7 @@
<value>As alterações do seu tema serão aplicadas quando o aplicativo for reiniciado.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Letras Iniciais em Maiúsculas</value>
<value>Iniciais em Maiúsculas</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Alterar Visiblidade</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -356,7 +356,7 @@
<comment>Validation message for when a form field is left blank and is required to be entered.</comment>
</data>
<data name="ValueHasBeenCopied" xml:space="preserve">
<value>{0} foi copiado.</value>
<value>{0} foi copiado(a).</value>
<comment>Confirmation message after suceessfully copying a value to the clipboard.</comment>
</data>
<data name="VerifyFingerprint" xml:space="preserve">
@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Alternar visibilidade</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visibility</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -412,7 +412,7 @@
<value>Extensia aplicaţiei Bitwarden</value>
</data>
<data name="BitwardenAppExtensionAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is from the Bitwarden App Extension. Learn more about using the Bitwarden App Extension by navigating to the "Settings" screen.</value>
<value>Cea mai ușoară modalitate de a adăuga noi autentificări în seif este din extensia aplicaţiei Bitwarden. Aflați mai multe despre utilizarea extensiei aplicaţiei Bitwarden accesând ecranul "Setări".</value>
</data>
<data name="BitwardenAppExtensionDescription" xml:space="preserve">
<value>Utilizați Bitwarden în Safari și în alte aplicații pentru a completa automat datele de autentificare.</value>
@@ -753,7 +753,7 @@
<value>Stare</value>
</data>
<data name="BitwardenAutofillServiceAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is from the Bitwarden Auto-fill Service. Learn more about using the Bitwarden Auto-fill Service by navigating to the "Settings" screen.</value>
<value>Cea mai ușoară modalitate de a adăuga noi autentificări în seif este din Serviciul de completare automată Bitwarden. Aflați mai multe despre utilizarea Serviciului de completare automată Bitwarden accesând ecranul "Setări".</value>
</data>
<data name="Autofill" xml:space="preserve">
<value>Completare automată</value>
@@ -1302,120 +1302,120 @@
<value>Auto Completare Parolă</value>
</data>
<data name="BitwardenAutofillAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is by using the Bitwarden Password AutoFill extension. Learn more about using the Bitwarden Password AutoFill extension by navigating to the "Settings" screen.</value>
<value>Cea mai ușoară modalitate de a adăuga noi autentificări în seif este din extensia aplicaţiei Bitwarden. Aflați mai multe despre utilizarea extensiei aplicaţiei Bitwarden accesând ecranul "Setări".</value>
</data>
<data name="InvalidEmail" xml:space="preserve">
<value>Invalid email address.</value>
<value>Adresă de email greșită.</value>
</data>
<data name="Cards" xml:space="preserve">
<value>Cards</value>
<value>Carduri</value>
</data>
<data name="Identities" xml:space="preserve">
<value>Identities</value>
<value>Identități</value>
</data>
<data name="Logins" xml:space="preserve">
<value>Logins</value>
<value>Autentificări</value>
</data>
<data name="SecureNotes" xml:space="preserve">
<value>Secure Notes</value>
<value>Notițe securizate</value>
</data>
<data name="AllItems" xml:space="preserve">
<value>All Items</value>
<value>Toate elementele</value>
</data>
<data name="URIs" xml:space="preserve">
<value>URIs</value>
<value>URI-uri</value>
<comment>Plural form of a URI</comment>
</data>
<data name="CheckingPassword" xml:space="preserve">
<value>Checking password...</value>
<value>Verificare parolă...</value>
<comment>A loading message when doing an exposed password check.</comment>
</data>
<data name="CheckPassword" xml:space="preserve">
<value>Check if password has been exposed.</value>
<value>Verificare dacă parola a fost dezvăluită.</value>
</data>
<data name="PasswordExposed" xml:space="preserve">
<value>This password has been exposed {0} time(s) in data breaches. You should change it.</value>
<value>Această parolă a fost dezvăluită de {0} ori într-o breșă de date. Ar trebui să o schimbați.</value>
</data>
<data name="PasswordSafe" xml:space="preserve">
<value>This password was not found in any known data breaches. It should be safe to use.</value>
<value>Aceasta parola nu a fost găsită în nicio breșă de date cunoscută. Ar trebui să fie sigură pentru a fi utilizată.</value>
</data>
<data name="IdentityName" xml:space="preserve">
<value>Identity Name</value>
<value>Numele Identității</value>
</data>
<data name="Value" xml:space="preserve">
<value>Value</value>
<value>Valoare</value>
</data>
<data name="PasswordHistory" xml:space="preserve">
<value>Password History</value>
<value>Istoric parole</value>
</data>
<data name="Types" xml:space="preserve">
<value>Types</value>
<value>Tipuri</value>
</data>
<data name="NoPasswordsToList" xml:space="preserve">
<value>No passwords to list.</value>
<value>Nicio parolă.</value>
</data>
<data name="NoItemsToList" xml:space="preserve">
<value>There are no items to list.</value>
<value>Nu există niciun articol de afișat.</value>
</data>
<data name="SearchCollection" xml:space="preserve">
<value>Search collection</value>
<value>Căutare colecție</value>
</data>
<data name="SearchFolder" xml:space="preserve">
<value>Search folder</value>
<value>Căutare dosar</value>
</data>
<data name="SearchType" xml:space="preserve">
<value>Search type</value>
<value>Tipul de căutare</value>
</data>
<data name="Type" xml:space="preserve">
<value>Type</value>
<value>Tip</value>
</data>
<data name="MoveDown" xml:space="preserve">
<value>Move Down</value>
<value>Mută în jos</value>
</data>
<data name="MoveUp" xml:space="preserve">
<value>Move Up</value>
<value>Mută în sus</value>
</data>
<data name="Miscellaneous" xml:space="preserve">
<value>Miscellaneous</value>
<value>Diverse</value>
</data>
<data name="Ownership" xml:space="preserve">
<value>Ownership</value>
<value>Proprietate</value>
</data>
<data name="WhoOwnsThisItem" xml:space="preserve">
<value>Who owns this item?</value>
<value>Cine deține acest element?</value>
</data>
<data name="NoCollectionsToList" xml:space="preserve">
<value>There are no collections to list.</value>
<value>Nu există colecții în listă.</value>
</data>
<data name="ItemShared" xml:space="preserve">
<value>Item has been shared.</value>
<value>Articolul a fost distribuit.</value>
</data>
<data name="SelectOneCollection" xml:space="preserve">
<value>You must select at least one collection.</value>
<value>Este nevoie să alegeți măcar o colecție.</value>
</data>
<data name="Share" xml:space="preserve">
<value>Share</value>
<value>Distribuire</value>
</data>
<data name="ShareItem" xml:space="preserve">
<value>Share Item</value>
<value>Distribuie element</value>
</data>
<data name="NoOrgsToList" xml:space="preserve">
<value>No organizations to list.</value>
<value>Nu există organizații care să fie afișate.</value>
</data>
<data name="ShareDesc" xml:space="preserve">
<value>Choose an organization that you wish to share this item with. Sharing transfers ownership of the item to the organization. You will no longer be the direct owner of this item once it has been shared.</value>
<value>Alegeți o organizație căreia doriți să îi distribuiți acest element. Distribuirea transferă proprietatea asupra obiectului către organizație. Nu veți mai fi proprietarul direct al acestui element odată ce a fost distribuit.</value>
</data>
<data name="NumberOfWords" xml:space="preserve">
<value>Number of Words</value>
<value>Număr de cuvinte</value>
</data>
<data name="Passphrase" xml:space="preserve">
<value>Passphrase</value>
<value>Frază de acces</value>
</data>
<data name="WordSeparator" xml:space="preserve">
<value>Word Separator</value>
<value>Separator cuvânt</value>
</data>
<data name="Clear" xml:space="preserve">
<value>Clear</value>
<value>Ştergere</value>
<comment>To clear something out. example: To clear browser history.</comment>
</data>
<data name="Generator" xml:space="preserve">
@@ -1423,156 +1423,168 @@
<comment>Short for "Password Generator"</comment>
</data>
<data name="NoFoldersToList" xml:space="preserve">
<value>There are no folders to list.</value>
<value>Nu există dosare de afișat.</value>
</data>
<data name="FingerprintPhrase" xml:space="preserve">
<value>Fingerprint Phrase</value>
<value>Frază amprentă</value>
<comment>A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing.</comment>
</data>
<data name="YourAccountsFingerprint" xml:space="preserve">
<value>Your account's fingerprint phrase</value>
<value>Fraza amprentă a contului</value>
<comment>A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing.</comment>
</data>
<data name="ShareVaultConfirmation" xml:space="preserve">
<value>Bitwarden allows you to share your vault with others by using an organization account. Would you like to visit the bitwarden.com website to learn more?</value>
<value>Bitwarden vă permite să vă împărtășiți seiful cu alte persoane utilizând un cont pentru organizații. Doriți să vizitați site-ul bitwarden.com pentru a afla mai multe?</value>
</data>
<data name="ExportVault" xml:space="preserve">
<value>Export Vault</value>
<value>Export seif</value>
</data>
<data name="LockNow" xml:space="preserve">
<value>Lock Now</value>
<value>Blocați acum</value>
</data>
<data name="PIN" xml:space="preserve">
<value>PIN</value>
</data>
<data name="Unlock" xml:space="preserve">
<value>Unlock</value>
<value>Deblocare</value>
</data>
<data name="LockOption30Minutes" xml:space="preserve">
<value>30 minutes</value>
<value>30 de minute</value>
</data>
<data name="LockOption5Minutes" xml:space="preserve">
<value>5 minutes</value>
<value>5 minute</value>
</data>
<data name="SetPINDescription" xml:space="preserve">
<value>Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application.</value>
<value>Setaţi codul PIN de deblocare Bitwarden. Setările codului PIN vor fi resetate dacă vă deconectaţi vreodată din aplicație.</value>
</data>
<data name="LoggedInAsOn" xml:space="preserve">
<value>Logged in as {0} on {1}.</value>
<value>Autentificat ca {0} în {1}.</value>
<comment>ex: Logged in as user@example.com on bitwarden.com.</comment>
</data>
<data name="VaultLockedMasterPassword" xml:space="preserve">
<value>Your vault is locked. Verify your master password to continue.</value>
<value>Seiful dvs. este blocat. Verificaţi parola principală pentru a continua.</value>
</data>
<data name="VaultLockedPIN" xml:space="preserve">
<value>Your vault is locked. Verify your PIN code to continue.</value>
<value>Seiful dvs. este blocat. Verificaţi codul PIN pentru a continua.</value>
</data>
<data name="Dark" xml:space="preserve">
<value>Dark</value>
<value>Întunecată</value>
<comment>A dark color</comment>
</data>
<data name="Light" xml:space="preserve">
<value>Light</value>
<value>Luminoasă</value>
<comment>A light color</comment>
</data>
<data name="FiveMinutes" xml:space="preserve">
<value>5 minutes</value>
<value>5 minute</value>
</data>
<data name="OneMinute" xml:space="preserve">
<value>1 minute</value>
<value>1 minut</value>
</data>
<data name="TenSeconds" xml:space="preserve">
<value>10 seconds</value>
<value>10 secunde</value>
</data>
<data name="ThirtySeconds" xml:space="preserve">
<value>30 seconds</value>
<value>30 de secunde</value>
</data>
<data name="TwentySeconds" xml:space="preserve">
<value>20 seconds</value>
<value>20 de secunde</value>
</data>
<data name="TwoMinutes" xml:space="preserve">
<value>2 minutes</value>
<value>2 minute</value>
</data>
<data name="ClearClipboard" xml:space="preserve">
<value>Clear Clipboard</value>
<value>Goliţi Clipboardul</value>
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data>
<data name="ClearClipboardDescription" xml:space="preserve">
<value>Automatically clear copied values from your clipboard.</value>
<value>Şterge automat valorile copiate din clipboard.</value>
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data>
<data name="DefaultUriMatchDetection" xml:space="preserve">
<value>Default URI Match Detection</value>
<value>Detectare de potrivire URI implicită</value>
<comment>Default URI match detection for auto-fill.</comment>
</data>
<data name="DefaultUriMatchDetectionDescription" xml:space="preserve">
<value>Choose the default way that URI match detection is handled for logins when performing actions such as auto-fill.</value>
<value>Alege modul în care se gestionează detectarea de potrivire de URI pentru autentificări la acțiuni precum cea de auto-completare.</value>
</data>
<data name="Theme" xml:space="preserve">
<value>Theme</value>
<value>Temă</value>
<comment>Color theme</comment>
</data>
<data name="ThemeDescription" xml:space="preserve">
<value>Change the application's color theme.</value>
<value>Schimbă tema de culori a aplicației.</value>
</data>
<data name="RestartIsRequired" xml:space="preserve">
<value>Restart is required.</value>
<value>Repornire necesară.</value>
<comment>Referring to restarting the application.</comment>
</data>
<data name="Restarting" xml:space="preserve">
<value>Restarting...</value>
<value>Repornire...</value>
</data>
<data name="CopyNotes" xml:space="preserve">
<value>Copy Notes</value>
<value>Copiere note</value>
</data>
<data name="Exit" xml:space="preserve">
<value>Exit</value>
<value>Ieșire</value>
</data>
<data name="ExitConfirmation" xml:space="preserve">
<value>Are you sure you want to exit Bitwarden?</value>
<value>Sunteți sigur că doriți să ieșiți?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Black</value>
<value>Neagră</value>
<comment>The color black</comment>
</data>
<data name="BlacklistedUris" xml:space="preserve">
<value>Blacklisted URIs</value>
<value>URI-uri în lista neagră</value>
</data>
<data name="BlacklistedUrisDescription" xml:space="preserve">
<value>URIs that are blacklisted will not offer auto-fill. The list of apps should be comma separated. Ex: "https://twitter.com, androidapp://com.twitter.android".</value>
</data>
<data name="DisableSavePrompt" xml:space="preserve">
<value>Disable Save Prompt</value>
<value>Dezactivați solicitarea de salvare</value>
</data>
<data name="DisableSavePromptDescription" xml:space="preserve">
<value>The "Save Prompt" automatically prompts you to save new items to your vault whenever you enter them for the first time.</value>
<value>"Notificarea de salvare" solicită automat salvarea de noi elemente în seiful dvs. de fiecare dată când le introduceți pentru prima dată.</value>
</data>
<data name="LockOptionOnRestart" xml:space="preserve">
<value>On App Restart</value>
<value>La repornirea aplicației</value>
</data>
<data name="AutofillServiceNotEnabled" xml:space="preserve">
<value>Auto-fill makes it easy to securely access your Bitwarden vault from other websites and apps. It looks like you have not enabled an auto-fill service for Bitwarden. Enable auto-fill for Bitwarden from the "Settings" screen.</value>
<value>Completarea automată simplifică accesarea securizată a seifului Bitwarden de pe alte site-uri web și aplicații. Se pare că nu ați activat un serviciu de completare automată pentru Bitwarden. Activați completarea automată pentru Bitwarden din ecranul „Setări”.</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Your theme changes will apply when the app is restarted.</value>
<value>Modificările temei se vor aplica după repornirea aplicației.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Capitalize</value>
<value>Majuscule</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>Include Number</value>
<value>Include număr</value>
</data>
<data name="Download" xml:space="preserve">
<value>Download</value>
<value>Descărcare</value>
</data>
<data name="Shared" xml:space="preserve">
<value>Shared</value>
<value>Distribuit</value>
</data>
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -534,7 +534,7 @@
<value>Немедленно</value>
</data>
<data name="LockOptions" xml:space="preserve">
<value>Настройки блокировки</value>
<value>Параметры блокировки</value>
</data>
<data name="LoggingIn" xml:space="preserve">
<value>Вход...</value>
@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Изменить видимость</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -412,7 +412,7 @@
<value>bitwarden rozšírenie aplikácie</value>
</data>
<data name="BitwardenAppExtensionAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is from the Bitwarden App Extension. Learn more about using the Bitwarden App Extension by navigating to the "Settings" screen.</value>
<value>Najjednoduchší spôsob, ako pridať nové prihlasovacie údaje do trezora, je z Bitwarden rozšírenia aplikácie. Ďalšie informácie o používaní Bitwarden rozšírenia aplikácie získate prechodom do obrazovky Nastavení.</value>
</data>
<data name="BitwardenAppExtensionDescription" xml:space="preserve">
<value>Použiť bitwarden v Safari a iných aplikáciach pre automatické vypĺňanie prihlasovacích údajov.</value>
@@ -753,7 +753,7 @@
<value>Stav</value>
</data>
<data name="BitwardenAutofillServiceAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is from the Bitwarden Auto-fill Service. Learn more about using the Bitwarden Auto-fill Service by navigating to the "Settings" screen.</value>
<value>Najjednoduchší spôsob, ako pridať nové prihlasovacie údaje do trezora, je z bitwarden služby automatického vypĺňania. Ďalšie informácie o používaní bitwarden automatického vypĺňania získate prechodom do obrazovky Nastavení.</value>
</data>
<data name="Autofill" xml:space="preserve">
<value>Automatické dopĺňanie</value>
@@ -1302,7 +1302,7 @@
<value>Automatické vypĺňanie hesiel</value>
</data>
<data name="BitwardenAutofillAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is by using the Bitwarden Password AutoFill extension. Learn more about using the Bitwarden Password AutoFill extension by navigating to the "Settings" screen.</value>
<value>Najjednoduchší spôsob, ako pridať nové prihlasovacie údaje do trezora, je z Bitwarden rozšírenia pre automatické vypĺňanie. Ďalšie informácie o používaní rozšírenia pre automatické dopĺňanie získate prechodom na obrazovku Nastavení.</value>
</data>
<data name="InvalidEmail" xml:space="preserve">
<value>Neplatná emailová adresa.</value>
@@ -1352,7 +1352,7 @@
<value>Typy</value>
</data>
<data name="NoPasswordsToList" xml:space="preserve">
<value>No passwords to list.</value>
<value>Nenašli sa žiadne heslá.</value>
</data>
<data name="NoItemsToList" xml:space="preserve">
<value>Neexistujú žiadne položky na zobrazenie.</value>
@@ -1388,7 +1388,7 @@
<value>Neexistujú žiadne zbierky na zobrazenie.</value>
</data>
<data name="ItemShared" xml:space="preserve">
<value>Item has been shared.</value>
<value>Položka bola zdieľaná.</value>
</data>
<data name="SelectOneCollection" xml:space="preserve">
<value>Musíte vybrať aspoň jednu zbierku.</value>
@@ -1400,7 +1400,7 @@
<value>Zdieľať položku</value>
</data>
<data name="NoOrgsToList" xml:space="preserve">
<value>No organizations to list.</value>
<value>Nenašli sa žiadne organizácie.</value>
</data>
<data name="ShareDesc" xml:space="preserve">
<value>Vyberte organizáciu s ktorou chcete zdieľať túto položku. Zdieľanie presunie vlastníctvo položky na organizáciu. Po zdieľaní už nebudete priamy vlastník položky.</value>
@@ -1458,7 +1458,7 @@
<value>Nastaviť kód PIN na odomknutie Bitwarden. Nastavenie PIN sa vynuluje, ak úplne odhlásite z aplikácie.</value>
</data>
<data name="LoggedInAsOn" xml:space="preserve">
<value>Logged in as {0} on {1}.</value>
<value>Prihlásený ako {0} na {1}.</value>
<comment>ex: Logged in as user@example.com on bitwarden.com.</comment>
</data>
<data name="VaultLockedMasterPassword" xml:space="preserve">
@@ -1539,40 +1539,52 @@
<comment>The color black</comment>
</data>
<data name="BlacklistedUris" xml:space="preserve">
<value>Blacklisted URIs</value>
<value>Zakázané URI</value>
</data>
<data name="BlacklistedUrisDescription" xml:space="preserve">
<value>URIs that are blacklisted will not offer auto-fill. The list of apps should be comma separated. Ex: "https://twitter.com, androidapp://com.twitter.android".</value>
</data>
<data name="DisableSavePrompt" xml:space="preserve">
<value>Disable Save Prompt</value>
<value>Nepýtať sa na ukládanie</value>
</data>
<data name="DisableSavePromptDescription" xml:space="preserve">
<value>The "Save Prompt" automatically prompts you to save new items to your vault whenever you enter them for the first time.</value>
<value>"Notifikácia Pridať prihlasovacie údaje" vás automaticky upozorní na uloženie nových údajov do vášho trezoru vždy, keď sa s nimi prihlasujete po prvý krát.</value>
</data>
<data name="LockOptionOnRestart" xml:space="preserve">
<value>On App Restart</value>
<value>Po reštarte aplikácie</value>
</data>
<data name="AutofillServiceNotEnabled" xml:space="preserve">
<value>Auto-fill makes it easy to securely access your Bitwarden vault from other websites and apps. It looks like you have not enabled an auto-fill service for Bitwarden. Enable auto-fill for Bitwarden from the "Settings" screen.</value>
<value>Automatické dopĺňanie umožňuje jednoducho pristupovať k vašemu trezoru z iných stránok a plaikácii. Vyzerá že ste službu automatického dopĺňania pomocou Bitwarden nezapli. Zapnúť ju môžete na obrazove "Nastavenia".</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Your theme changes will apply when the app is restarted.</value>
<value>Zmena nastavení vzhľadu sa prejaví po opätovnom štarte aplikácie.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Capitalize</value>
<value>Prvé písmeno veľkým</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>Include Number</value>
<value>Zahrnúť číslo</value>
</data>
<data name="Download" xml:space="preserve">
<value>Download</value>
<value>Stiahnuť</value>
</data>
<data name="Shared" xml:space="preserve">
<value>Shared</value>
<value>Zdieľané</value>
</data>
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -412,7 +412,7 @@
<value>Bitwarden Uygulama Uzantısı</value>
</data>
<data name="BitwardenAppExtensionAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is from the Bitwarden App Extension. Learn more about using the Bitwarden App Extension by navigating to the "Settings" screen.</value>
<value>Kasanıza yeni hesaplar eklemenin en kolay yolu Bitwarden Uygulama Uzantısıdır. Bitwarden Uygulama Uzantısının kullanımı hakkında daha fazla bilgi edinmek için "Ayarlar" ekranına gidin.</value>
</data>
<data name="BitwardenAppExtensionDescription" xml:space="preserve">
<value>Giriş bilgilerinizi otomatik doldurmak için Safari ve diğer uygulamalarda Bitwarden kullanın.</value>
@@ -753,7 +753,7 @@
<value>Durum</value>
</data>
<data name="BitwardenAutofillServiceAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is from the Bitwarden Auto-fill Service. Learn more about using the Bitwarden Auto-fill Service by navigating to the "Settings" screen.</value>
<value>Kasanıza yeni hesaplar eklemenin en kolay yolu Bitwarden Otomatik Doldurma Hizmetidir. Bitwarden Otomatik Doldurma Hizmetinin kullanımı hakkında daha fazla bilgi edinmek için "Ayarlar" ekranına gidin.</value>
</data>
<data name="Autofill" xml:space="preserve">
<value>Otomatik doldur</value>
@@ -1302,7 +1302,7 @@
<value>Parola Otomatik Doldurma</value>
</data>
<data name="BitwardenAutofillAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is by using the Bitwarden Password AutoFill extension. Learn more about using the Bitwarden Password AutoFill extension by navigating to the "Settings" screen.</value>
<value>Kasanıza yeni hesaplar eklemenin en kolay yolu Bitwarden Parola Otomatik Doldurma uzantısını kullanmaktır. Bitwarden Parola Otomatik Doldurma uzantısının kullanımı hakkında daha fazla bilgi edinmek için "Ayarlar" ekranına gidin.</value>
</data>
<data name="InvalidEmail" xml:space="preserve">
<value>Geçersiz e-posta adresi.</value>
@@ -1334,245 +1334,257 @@
<value>Parolanız herhangi bir saldırı sonucu açığa çıkmış mı kontrol edin.</value>
</data>
<data name="PasswordExposed" xml:space="preserve">
<value>This password has been exposed {0} time(s) in data breaches. You should change it.</value>
<value>Bu parola {0} defa saldırı sonucu veri ele geçmesiyle açığa çıkmış. Değiştirmeniz gerekiyor.</value>
</data>
<data name="PasswordSafe" xml:space="preserve">
<value>This password was not found in any known data breaches. It should be safe to use.</value>
<value>Parolanız, herhangi bir saldırı sonucu ele geçirilen veride bulunmadı. Kullanılması güvenli görünüyor.</value>
</data>
<data name="IdentityName" xml:space="preserve">
<value>Identity Name</value>
<value>Kimlik Adı</value>
</data>
<data name="Value" xml:space="preserve">
<value>Değer</value>
</data>
<data name="PasswordHistory" xml:space="preserve">
<value>Password History</value>
<value>Parola Geçmişi</value>
</data>
<data name="Types" xml:space="preserve">
<value>Types</value>
<value>Türler</value>
</data>
<data name="NoPasswordsToList" xml:space="preserve">
<value>No passwords to list.</value>
<value>Listelenecek parola yok.</value>
</data>
<data name="NoItemsToList" xml:space="preserve">
<value>There are no items to list.</value>
<value>Listelenecek hesap yok.</value>
</data>
<data name="SearchCollection" xml:space="preserve">
<value>Search collection</value>
<value>Koleksiyon ara</value>
</data>
<data name="SearchFolder" xml:space="preserve">
<value>Search folder</value>
<value>Klasörü ara</value>
</data>
<data name="SearchType" xml:space="preserve">
<value>Search type</value>
<value>Arama türü</value>
</data>
<data name="Type" xml:space="preserve">
<value>Type</value>
<value>Tür</value>
</data>
<data name="MoveDown" xml:space="preserve">
<value>Move Down</value>
<value>Aşağı Taşı</value>
</data>
<data name="MoveUp" xml:space="preserve">
<value>Move Up</value>
<value>Yukarı Taşı</value>
</data>
<data name="Miscellaneous" xml:space="preserve">
<value>Miscellaneous</value>
<value>Çeşitli</value>
</data>
<data name="Ownership" xml:space="preserve">
<value>Ownership</value>
<value>Sahiplik</value>
</data>
<data name="WhoOwnsThisItem" xml:space="preserve">
<value>Who owns this item?</value>
<value>Bu ögenin sahibi kim?</value>
</data>
<data name="NoCollectionsToList" xml:space="preserve">
<value>There are no collections to list.</value>
<value>Listelenecek koleksiyon yok.</value>
</data>
<data name="ItemShared" xml:space="preserve">
<value>Item has been shared.</value>
<value>Öge paylaşıldı.</value>
</data>
<data name="SelectOneCollection" xml:space="preserve">
<value>You must select at least one collection.</value>
<value>En az bir koleksiyon seçmelisiniz.</value>
</data>
<data name="Share" xml:space="preserve">
<value>Share</value>
<value>Paylaş</value>
</data>
<data name="ShareItem" xml:space="preserve">
<value>Share Item</value>
<value>Ögeyi Paylaş</value>
</data>
<data name="NoOrgsToList" xml:space="preserve">
<value>No organizations to list.</value>
<value>Listelenecek organizasyon yok.</value>
</data>
<data name="ShareDesc" xml:space="preserve">
<value>Choose an organization that you wish to share this item with. Sharing transfers ownership of the item to the organization. You will no longer be the direct owner of this item once it has been shared.</value>
<value>Bu ögeyi paylaşmak istediğiniz organizasyonu seçin. Paylaşım, ögenin sahipliğini kuruluşa aktarır. Paylaşıldıktan sonra artık bu ögenin doğrudan sahibi olmayacaksınız.</value>
</data>
<data name="NumberOfWords" xml:space="preserve">
<value>Number of Words</value>
<value>Kelime Sayısı</value>
</data>
<data name="Passphrase" xml:space="preserve">
<value>Passphrase</value>
<value>Parola</value>
</data>
<data name="WordSeparator" xml:space="preserve">
<value>Word Separator</value>
<value>Kelime Ayracı</value>
</data>
<data name="Clear" xml:space="preserve">
<value>Clear</value>
<value>Temizle</value>
<comment>To clear something out. example: To clear browser history.</comment>
</data>
<data name="Generator" xml:space="preserve">
<value>Generator</value>
<value>Oluşturucu</value>
<comment>Short for "Password Generator"</comment>
</data>
<data name="NoFoldersToList" xml:space="preserve">
<value>There are no folders to list.</value>
<value>Listelenecek klasör yok.</value>
</data>
<data name="FingerprintPhrase" xml:space="preserve">
<value>Fingerprint Phrase</value>
<value>Parmak İzi Şifresi</value>
<comment>A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing.</comment>
</data>
<data name="YourAccountsFingerprint" xml:space="preserve">
<value>Your account's fingerprint phrase</value>
<value>Hesabınızın parmak izi şifresi</value>
<comment>A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing.</comment>
</data>
<data name="ShareVaultConfirmation" xml:space="preserve">
<value>Bitwarden allows you to share your vault with others by using an organization account. Would you like to visit the bitwarden.com website to learn more?</value>
<value>Bitwarden bir kuruluş hesabı kullanarak başkalarıyla kasanızı paylaşmanızı sağlar. Daha fazla bilgi için bitwarden.com sitesini ziyaret etmek ister misiniz?</value>
</data>
<data name="ExportVault" xml:space="preserve">
<value>Export Vault</value>
<value>Kasayı Dışa Aktar</value>
</data>
<data name="LockNow" xml:space="preserve">
<value>Lock Now</value>
<value>Şimdi Kilitle</value>
</data>
<data name="PIN" xml:space="preserve">
<value>PIN</value>
</data>
<data name="Unlock" xml:space="preserve">
<value>Unlock</value>
<value>Kilidi Aç</value>
</data>
<data name="LockOption30Minutes" xml:space="preserve">
<value>30 minutes</value>
<value>30 dakika</value>
</data>
<data name="LockOption5Minutes" xml:space="preserve">
<value>5 minutes</value>
<value>5 dakika</value>
</data>
<data name="SetPINDescription" xml:space="preserve">
<value>Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application.</value>
<value>Bitwarden'ı açmak için bir PIN kodu belirleyin. Uygulamadan tamamen çıkış yaparsanız PIN ayarlarınız sıfırlanacaktır.</value>
</data>
<data name="LoggedInAsOn" xml:space="preserve">
<value>Logged in as {0} on {1}.</value>
<value>{1} üzerinde {0} olarak oturum açıldı.</value>
<comment>ex: Logged in as user@example.com on bitwarden.com.</comment>
</data>
<data name="VaultLockedMasterPassword" xml:space="preserve">
<value>Your vault is locked. Verify your master password to continue.</value>
<value>Kasanız kilitlendi. Devam etmek için ana parolanızı doğrulayın.</value>
</data>
<data name="VaultLockedPIN" xml:space="preserve">
<value>Your vault is locked. Verify your PIN code to continue.</value>
<value>Kasanız kilitlendi. Devam etmek için PIN kodunuzu doğrulayın.</value>
</data>
<data name="Dark" xml:space="preserve">
<value>Dark</value>
<value>Koyu</value>
<comment>A dark color</comment>
</data>
<data name="Light" xml:space="preserve">
<value>Light</value>
<value>ık</value>
<comment>A light color</comment>
</data>
<data name="FiveMinutes" xml:space="preserve">
<value>5 minutes</value>
<value>5 dakika</value>
</data>
<data name="OneMinute" xml:space="preserve">
<value>1 minute</value>
<value>1 dakika</value>
</data>
<data name="TenSeconds" xml:space="preserve">
<value>10 seconds</value>
<value>10 saniye</value>
</data>
<data name="ThirtySeconds" xml:space="preserve">
<value>30 seconds</value>
<value>30 saniye</value>
</data>
<data name="TwentySeconds" xml:space="preserve">
<value>20 seconds</value>
<value>20 saniye</value>
</data>
<data name="TwoMinutes" xml:space="preserve">
<value>2 minutes</value>
<value>2 dakika</value>
</data>
<data name="ClearClipboard" xml:space="preserve">
<value>Clear Clipboard</value>
<value>Panoyu Temizle</value>
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data>
<data name="ClearClipboardDescription" xml:space="preserve">
<value>Automatically clear copied values from your clipboard.</value>
<value>Kopyalanan değerleri panodan otomatik olarak temizle.</value>
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data>
<data name="DefaultUriMatchDetection" xml:space="preserve">
<value>Default URI Match Detection</value>
<value>Varsayılan Eşleşen URI Tespiti</value>
<comment>Default URI match detection for auto-fill.</comment>
</data>
<data name="DefaultUriMatchDetectionDescription" xml:space="preserve">
<value>Choose the default way that URI match detection is handled for logins when performing actions such as auto-fill.</value>
<value>Otomatik doldurma gibi eylemler gerçekleştirilirken oturum açma işlemleri için URI eşleşme tespitinin varsayılan biçimini seçiniz.</value>
</data>
<data name="Theme" xml:space="preserve">
<value>Theme</value>
<value>Tema</value>
<comment>Color theme</comment>
</data>
<data name="ThemeDescription" xml:space="preserve">
<value>Change the application's color theme.</value>
<value>Uygulamanın renk temasını değiştir.</value>
</data>
<data name="RestartIsRequired" xml:space="preserve">
<value>Restart is required.</value>
<value>Yeniden başlatmak gerekli.</value>
<comment>Referring to restarting the application.</comment>
</data>
<data name="Restarting" xml:space="preserve">
<value>Restarting...</value>
<value>Yeniden başlatılıyor...</value>
</data>
<data name="CopyNotes" xml:space="preserve">
<value>Copy Notes</value>
<value>Notları Kopyala</value>
</data>
<data name="Exit" xml:space="preserve">
<value>Exit</value>
<value>Çıkış</value>
</data>
<data name="ExitConfirmation" xml:space="preserve">
<value>Are you sure you want to exit Bitwarden?</value>
<value>Bitwarden'den çıkmak istediğinize emin misiniz?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Black</value>
<value>Siyah</value>
<comment>The color black</comment>
</data>
<data name="BlacklistedUris" xml:space="preserve">
<value>Blacklisted URIs</value>
<value>Kara listeye alınmış URI'ler</value>
</data>
<data name="BlacklistedUrisDescription" xml:space="preserve">
<value>URIs that are blacklisted will not offer auto-fill. The list of apps should be comma separated. Ex: "https://twitter.com, androidapp://com.twitter.android".</value>
</data>
<data name="DisableSavePrompt" xml:space="preserve">
<value>Disable Save Prompt</value>
<value>Kaydet Uyarısını Devre Dışı Bırak</value>
</data>
<data name="DisableSavePromptDescription" xml:space="preserve">
<value>The "Save Prompt" automatically prompts you to save new items to your vault whenever you enter them for the first time.</value>
<value>"Kaydet Uyarısı", ilk kez girdiğinizde yeni ögeleri kasanıza kaydetmeniz için otomatik olarak size sorar.</value>
</data>
<data name="LockOptionOnRestart" xml:space="preserve">
<value>On App Restart</value>
<value>Uygulama Yeniden Başlatılırken</value>
</data>
<data name="AutofillServiceNotEnabled" xml:space="preserve">
<value>Auto-fill makes it easy to securely access your Bitwarden vault from other websites and apps. It looks like you have not enabled an auto-fill service for Bitwarden. Enable auto-fill for Bitwarden from the "Settings" screen.</value>
<value>Otomatik doldurma, Bitwarden kasanıza diğer web sitelerinden ve uygulamalardan güvenli bir şekilde erişmenizi kolaylaştırır. Bitwarden için otomatik doldurma hizmetini etkinleştirmemiş gibi görünüyorsunuz. "Ayarlar" ekranından Bitwarden için otomatik doldurmayı etkinleştirin.</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Your theme changes will apply when the app is restarted.</value>
<value>Tema değişiklikleriniz uygulama yeniden başlatıldığında geçerli olacaktır.</value>
</data>
<data name="Capitalize" xml:space="preserve">
<value>Capitalize</value>
<value>Baş Harfleri Büyük Yap</value>
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>Include Number</value>
<value>Sayı Ekle</value>
</data>
<data name="Download" xml:space="preserve">
<value>Download</value>
<value>İndir</value>
</data>
<data name="Shared" xml:space="preserve">
<value>Shared</value>
<value>Paylaşılan</value>
</data>
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Перемкнути видимість</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>Toggle Visiblity</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -403,7 +403,7 @@
<value>使用 Bitwarden 无障碍服务在应用和网站自动填写您的登录项目。</value>
</data>
<data name="AutofillService" xml:space="preserve">
<value>自动填</value>
<value>自动填写服务</value>
</data>
<data name="AvoidAmbiguousCharacters" xml:space="preserve">
<value>避免模棱两可</value>
@@ -756,10 +756,10 @@
<value>向您的密码库中添加新登录项目的最简单方法,就是 Bitwarden 自动填写服务。转到 “设置” 屏幕了解更多有关使用 Bitwarden 自动填写服务的方法。</value>
</data>
<data name="Autofill" xml:space="preserve">
<value>自动填</value>
<value>自动填</value>
</data>
<data name="AutofillOrView" xml:space="preserve">
<value>你想要自动填还是查看此登录项目?</value>
<value>要自动填还是查看此登录项目?</value>
</data>
<data name="BitwardenAutofillServiceMatchConfirm" xml:space="preserve">
<value>确定要自动登录?并不完全匹配 "{0}" 。</value>
@@ -783,13 +783,13 @@
<value>当焦点在密码字段时扫描</value>
</data>
<data name="AutofillPasswordFieldDescription" xml:space="preserve">
<value>当你选择一个密码字段时,只扫描屏幕字段并提供自动填充通知。此设置可帮助延长电池寿命。</value>
<value>当你选择一个密码字段时,只扫描屏幕字段并提供自动填充通知。此设置可帮助延长电池寿命。 </value>
</data>
<data name="AutofillPersistNotification" xml:space="preserve">
<value>始终通知</value>
</data>
<data name="AutofillPersistNotificationDescription" xml:space="preserve">
<value>始终提供自动填通知,并且在试图自动填充后只扫描字段。此设置可帮助延长电池寿命。</value>
<value>始终提供自动填通知,并且只在尝试自动填写后扫描字段。此设置可帮助延长电池寿命。</value>
</data>
<data name="AutofillAlways" xml:space="preserve">
<value>始终扫描</value>
@@ -1542,7 +1542,7 @@
<value>黑名单 URI</value>
</data>
<data name="BlacklistedUrisDescription" xml:space="preserve">
<value>URIs that are blacklisted will not offer auto-fill. The list of apps should be comma separated. Ex: "https://twitter.com, androidapp://com.twitter.android".</value>
<value>列入黑名单的URI将不提供自动填充。列表应以逗号分隔。例如"https://twitter.com, androidapp://com.twitter.android"</value>
</data>
<data name="DisableSavePrompt" xml:space="preserve">
<value>禁用保存提示</value>
@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>切换可见性</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1575,4 +1575,16 @@
<data name="ToggleVisibility" xml:space="preserve">
<value>切換顯示</value>
</data>
<data name="LoginExpired" xml:space="preserve">
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Use Biometrics To Unlock</value>
</data>
</root>

View File

@@ -1,6 +1,7 @@
using Bit.App.Resources;
using Bit.Core.Abstractions;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Resources;
@@ -18,6 +19,7 @@ namespace Bit.App.Services
private readonly CultureInfo _defaultCulture = new CultureInfo("en-US");
private bool _inited;
private StringComparer _stringComparer;
private Dictionary<string, string> _localeNames;
public MobileI18nService(CultureInfo systemCulture)
{
@@ -36,6 +38,57 @@ namespace Bit.App.Services
return _stringComparer;
}
}
public Dictionary<string, string> LocaleNames
{
get
{
if(_localeNames == null)
{
_localeNames = new Dictionary<string, string>
{
["af"] = "Afrikaans",
["bg"] = "български",
["ca"] = "català",
["cs"] = "čeština",
["da"] = "dansk",
["de"] = "Deutsch",
["el"] = "Ελληνικά",
["en"] = "English",
["en-GB"] = "English (British)",
["eo"] = "Esperanto",
["es"] = "español",
["et"] = "eesti",
["fa"] = "فارسی",
["fi"] = "suomi",
["fr"] = "français",
["he"] = "עברית",
["hi"] = "हिन्दी",
["hr"] = "hrvatski",
["hu"] = "magyar",
["id"] = "Bahasa Indonesia",
["it"] = "italiano",
["ja"] = "日本語",
["ko"] = "한국어",
["nb"] = "norsk (bokmål)",
["nl"] = "Nederlands",
["pl"] = "polski",
["pt-BR"] = "português do Brasil",
["pt-PT"] = "português",
["ro"] = "română",
["ru"] = "русский",
["sk"] = "slovenčina",
["sv"] = "svenska",
["th"] = "ไทย",
["tr"] = "Türkçe",
["uk"] = "українська",
["vi"] = "Tiếng Việt",
["zh-CN"] = "中文(中国大陆)",
["zh-TW"] = "中文(台灣)"
};
}
return _localeNames;
}
}
public void Init(CultureInfo culture = null)
{

View File

@@ -197,45 +197,45 @@ namespace Bit.App.Services
return await Clipboard.GetTextAsync();
}
public async Task<bool> SupportsFingerprintAsync()
public async Task<bool> SupportsBiometricAsync()
{
try
return await _deviceActionService.BiometricAvailableAsync();
}
public async Task<bool> AuthenticateBiometricAsync(string text = null, string fallbackText = null,
Action fallback = null)
{
if(_deviceActionService.UseNativeBiometric())
{
return await CrossFingerprint.Current.IsAvailableAsync();
return await _deviceActionService.AuthenticateBiometricAsync(text);
}
catch
else
{
try
{
if(text == null)
{
var supportsFace = await _deviceActionService.SupportsFaceBiometricAsync();
text = supportsFace ? AppResources.FaceIDDirection : AppResources.FingerprintDirection;
}
var fingerprintRequest = new AuthenticationRequestConfiguration(text)
{
CancelTitle = AppResources.Cancel,
FallbackTitle = fallbackText
};
var result = await CrossFingerprint.Current.AuthenticateAsync(fingerprintRequest);
if(result.Authenticated)
{
return true;
}
else if(result.Status == FingerprintAuthenticationResultStatus.FallbackRequested)
{
fallback?.Invoke();
}
}
catch { }
return false;
}
}
public async Task<bool> AuthenticateFingerprintAsync(string text = null, string fallbackText = null,
Action fallback = null)
{
try
{
if(text == null)
{
text = _deviceActionService.SupportsFaceId() ? AppResources.FaceIDDirection :
AppResources.FingerprintDirection;
}
var fingerprintRequest = new AuthenticationRequestConfiguration(text)
{
CancelTitle = AppResources.Cancel,
FallbackTitle = fallbackText
};
var result = await CrossFingerprint.Current.AuthenticateAsync(fingerprintRequest);
if(result.Authenticated)
{
return true;
}
else if(result.Status == FingerprintAuthenticationResultStatus.FallbackRequested)
{
fallback?.Invoke();
}
}
catch { }
return false;
}
}
}

View File

@@ -22,7 +22,8 @@
<Setter Property="Margin"
Value="-4, 0, -4, -4" />
</Style>
<Style TargetType="SearchBar">
<Style TargetType="SearchBar"
ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor"
Value="Transparent" />
<Setter Property="TextColor"

View File

@@ -35,11 +35,12 @@
<Setter Property="OnColor"
Value="{StaticResource SwitchOnColor}" />
</Style>
<Style TargetType="SearchBar">
<Style TargetType="SearchBar"
ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor"
Value="{StaticResource ListHeaderBackgroundColor}" />
<Setter Property="TextColor"
Value="Black" />
Value="{StaticResource TextColor}" />
<Setter Property="CancelButtonColor"
Value="{StaticResource PrimaryColor}" />
<Setter Property="PlaceholderColor"

View File

@@ -124,23 +124,20 @@ namespace Bit.App.Utilities
{
var currentBuild = deviceActionService.GetBuildNumber();
var lastBuild = await storageService.GetAsync<string>(Constants.LastBuildKey);
if(!Migration.MigrationHelpers.NeedsMigration())
if(lastBuild == null)
{
if(lastBuild == null)
// Installed
var currentLock = await storageService.GetAsync<int?>(Constants.LockOptionKey);
if(currentLock == null)
{
// Installed
var currentLock = await storageService.GetAsync<int?>(Constants.LockOptionKey);
if(currentLock == null)
{
await storageService.SaveAsync(Constants.LockOptionKey, 15);
}
}
else if(lastBuild != currentBuild)
{
// Updated
var tasks = Task.Run(() => syncService.FullSyncAsync(true));
await storageService.SaveAsync(Constants.LockOptionKey, 15);
}
}
else if(lastBuild != currentBuild)
{
// Updated
var tasks = Task.Run(() => syncService.FullSyncAsync(true));
}
if(lastBuild != currentBuild)
{
await storageService.SaveAsync(Constants.LastBuildKey, currentBuild);

View File

@@ -1,12 +1,16 @@
using Bit.App.Services;
using Bit.App.Abstractions;
using Bit.App.Services;
using Bit.App.Styles;
using Bit.Core;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Utilities
{
public static class ThemeManager
{
public static bool UsingLightTheme = true;
public static void SetThemeStyle(string name)
{
// Reset styles
@@ -20,18 +24,36 @@ namespace Bit.App.Utilities
if(name == "dark")
{
Application.Current.Resources.MergedDictionaries.Add(new Dark());
UsingLightTheme = false;
}
else if(name == "black")
{
Application.Current.Resources.MergedDictionaries.Add(new Black());
UsingLightTheme = false;
}
else if(name == "nord")
{
Application.Current.Resources.MergedDictionaries.Add(new Nord());
UsingLightTheme = false;
}
else if(name == "light")
{
Application.Current.Resources.MergedDictionaries.Add(new Light());
UsingLightTheme = true;
}
else
{
Application.Current.Resources.MergedDictionaries.Add(new Light());
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService", true);
if(deviceActionService?.UsingDarkTheme() ?? false)
{
Application.Current.Resources.MergedDictionaries.Add(new Dark());
UsingLightTheme = false;
}
else
{
Application.Current.Resources.MergedDictionaries.Add(new Light());
UsingLightTheme = true;
}
}
// Base styles

View File

@@ -22,7 +22,7 @@ namespace Bit.Core.Abstractions
Task<CipherString> EncryptAsync(byte[] plainValue, SymmetricCryptoKey key = null);
Task<CipherString> EncryptAsync(string plainValue, SymmetricCryptoKey key = null);
Task<byte[]> EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null);
Task<SymmetricCryptoKey> GetEncKeyAsync();
Task<SymmetricCryptoKey> GetEncKeyAsync(SymmetricCryptoKey key = null);
Task<List<string>> GetFingerprintAsync(string userId, byte[] publicKey = null);
Task<SymmetricCryptoKey> GetKeyAsync();
Task<string> GetKeyHashAsync();
@@ -35,7 +35,8 @@ namespace Bit.Core.Abstractions
Task<bool> HasKeyAsync();
Task<Tuple<SymmetricCryptoKey, CipherString>> MakeEncKeyAsync(SymmetricCryptoKey key);
Task<SymmetricCryptoKey> MakeKeyAsync(string password, string salt, KdfType? kdf, int? kdfIterations);
Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt, KdfType kdf, int kdfIterations);
Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt, KdfType kdf, int kdfIterations,
CipherString protectedKeyCs = null);
Task<Tuple<string, CipherString>> MakeKeyPairAsync(SymmetricCryptoKey key = null);
Task<SymmetricCryptoKey> MakePinKeyAysnc(string pin, string salt, KdfType kdf, int kdfIterations);
Task<Tuple<CipherString, SymmetricCryptoKey>> MakeShareKeyAsync();
@@ -49,4 +50,4 @@ namespace Bit.Core.Abstractions
Task SetOrgKeysAsync(IEnumerable<ProfileOrganizationResponse> orgs);
Task ToggleKeyAsync();
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
namespace Bit.Core.Abstractions
@@ -7,7 +8,8 @@ namespace Bit.Core.Abstractions
{
CultureInfo Culture { get; set; }
StringComparer StringComparer { get; }
Dictionary<string, string> LocaleNames { get; }
string T(string id, string p1 = null, string p2 = null, string p3 = null);
string Translate(string id, string p1 = null, string p2 = null, string p3 = null);
}
}
}

View File

@@ -1,11 +1,12 @@
using System;
using System.Threading.Tasks;
using Bit.Core.Models.Domain;
namespace Bit.Core.Abstractions
{
public interface ILockService
{
bool PinLocked { get; set; }
CipherString PinProtectedKey { get; set; }
bool FingerprintLocked { get; set; }
Task CheckLockAsync();
@@ -16,4 +17,4 @@ namespace Bit.Core.Abstractions
Task LockAsync(bool allowSoftLock = false, bool userInitiated = false);
Task SetLockOptionAsync(int? lockOption);
}
}
}

View File

@@ -26,7 +26,7 @@ namespace Bit.Core.Abstractions
void ShowToast(string type, string title, string[] text, Dictionary<string, object> options = null);
bool SupportsU2f();
bool SupportsDuo();
Task<bool> SupportsFingerprintAsync();
Task<bool> AuthenticateFingerprintAsync(string text = null, string fallbackText = null, Action fallback = null);
Task<bool> SupportsBiometricAsync();
Task<bool> AuthenticateBiometricAsync(string text = null, string fallbackText = null, Action fallback = null);
}
}

View File

@@ -8,7 +8,7 @@ namespace Bit.Core.Abstractions
{
bool SyncInProgress { get; set; }
Task<bool> FullSyncAsync(bool forceSync);
Task<bool> FullSyncAsync(bool forceSync, bool allowThrowOnError = false);
Task<DateTime?> GetLastSyncAsync();
Task SetLastSyncAsync(DateTime date);
Task<bool> SyncDeleteCipherAsync(SyncCipherNotification notification);
@@ -16,4 +16,4 @@ namespace Bit.Core.Abstractions
Task<bool> SyncUpsertCipherAsync(SyncCipherNotification notification, bool isEdit);
Task<bool> SyncUpsertFolderAsync(SyncFolderNotification notification, bool isEdit);
}
}
}

View File

@@ -25,18 +25,22 @@ namespace Bit.Core.Services
private readonly ITokenService _tokenService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly Func<bool, Task> _logoutCallbackAsync;
private string _deviceType;
public ApiService(
ITokenService tokenService,
IPlatformUtilsService platformUtilsService,
Func<bool, Task> logoutCallbackAsync)
Func<bool, Task> logoutCallbackAsync,
string customUserAgent = null)
{
_tokenService = tokenService;
_platformUtilsService = platformUtilsService;
_logoutCallbackAsync = logoutCallbackAsync;
var device = _platformUtilsService.GetDevice();
_deviceType = device.ToString();
var device = (int)_platformUtilsService.GetDevice();
_httpClient.DefaultRequestHeaders.Add("Device-Type", device.ToString());
if(!string.IsNullOrWhiteSpace(customUserAgent))
{
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(customUserAgent);
}
}
public bool UrlsSet { get; private set; }
@@ -86,7 +90,6 @@ namespace Bit.Core.Services
Content = new FormUrlEncodedContent(request.ToIdentityToken(_platformUtilsService.IdentityClientId))
};
requestMessage.Headers.Add("Accept", "application/json");
requestMessage.Headers.Add("Device-Type", _deviceType);
HttpResponseMessage response;
try
@@ -304,7 +307,6 @@ namespace Bit.Core.Services
{
requestMessage.Method = HttpMethod.Post;
requestMessage.RequestUri = new Uri(string.Concat(EventsBaseUrl, "/collect"));
requestMessage.Headers.Add("Device-Type", _deviceType);
var authHeader = await GetActiveBearerTokenAsync();
requestMessage.Headers.Add("Authorization", string.Concat("Bearer ", authHeader));
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(request, _jsonSettings),
@@ -377,7 +379,6 @@ namespace Bit.Core.Services
}
}
requestMessage.Headers.Add("Device-Type", _deviceType);
if(authed)
{
var authHeader = await GetActiveBearerTokenAsync();
@@ -432,7 +433,6 @@ namespace Bit.Core.Services
})
};
requestMessage.Headers.Add("Accept", "application/json");
requestMessage.Headers.Add("Device-Type", _deviceType);
HttpResponseMessage response;
try

View File

@@ -118,7 +118,7 @@ namespace Bit.Core.Services
return _keyHash;
}
public Task<SymmetricCryptoKey> GetEncKeyAsync()
public Task<SymmetricCryptoKey> GetEncKeyAsync(SymmetricCryptoKey key = null)
{
if(_encKey != null)
{
@@ -138,7 +138,10 @@ namespace Bit.Core.Services
return null;
}
var key = await GetKeyAsync();
if(key == null)
{
key = await GetKeyAsync();
}
if(key == null)
{
return null;
@@ -386,14 +389,17 @@ namespace Bit.Core.Services
}
public async Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt,
KdfType kdf, int kdfIterations)
KdfType kdf, int kdfIterations, CipherString protectedKeyCs = null)
{
var pinProtectedKey = await _storageService.GetAsync<string>(Constants.PinProtectedKey);
if(pinProtectedKey == null)
if(protectedKeyCs == null)
{
throw new Exception("No PIN protected key found.");
var pinProtectedKey = await _storageService.GetAsync<string>(Constants.PinProtectedKey);
if(pinProtectedKey == null)
{
throw new Exception("No PIN protected key found.");
}
protectedKeyCs = new CipherString(pinProtectedKey);
}
var protectedKeyCs = new CipherString(pinProtectedKey);
var pinKey = await MakePinKeyAysnc(pin, salt, kdf, kdfIterations);
var decKey = await DecryptToBytesAsync(protectedKeyCs, pinKey);
return new SymmetricCryptoKey(decKey);

View File

@@ -1,4 +1,5 @@
using Bit.Core.Abstractions;
using Bit.Core.Models.Domain;
using System;
using System.Threading.Tasks;
@@ -41,7 +42,7 @@ namespace Bit.Core.Services
_lockedCallback = lockedCallback;
}
public bool PinLocked { get; set; }
public CipherString PinProtectedKey { get; set; } = null;
public bool FingerprintLocked { get; set; } = true;
public async Task<bool> IsLockedAsync()
@@ -49,18 +50,11 @@ namespace Bit.Core.Services
var hasKey = await _cryptoService.HasKeyAsync();
if(hasKey)
{
if(PinLocked)
var fingerprintSet = await IsFingerprintLockSetAsync();
if(fingerprintSet && FingerprintLocked)
{
return true;
}
else
{
var fingerprintSet = await IsFingerprintLockSetAsync();
if(fingerprintSet && FingerprintLocked)
{
return true;
}
}
}
return !hasKey;
}
@@ -111,13 +105,8 @@ namespace Bit.Core.Services
}
if(allowSoftLock)
{
var pinSet = await IsPinLockSetAsync();
if(pinSet.Item1)
{
PinLocked = true;
}
FingerprintLocked = await IsFingerprintLockSetAsync();
if(FingerprintLocked || PinLocked)
if(FingerprintLocked)
{
_messagingService.Send("locked", userInitiated);
_lockedCallback?.Invoke(userInitiated);
@@ -159,6 +148,7 @@ namespace Bit.Core.Services
public async Task ClearAsync()
{
PinProtectedKey = null;
await _storageService.RemoveAsync(Constants.ProtectedPin);
}
}

View File

@@ -51,25 +51,44 @@ namespace Bit.Core.Services
{
options.MinUppercase = 1;
}
else if(!options.Uppercase.GetValueOrDefault())
{
options.MinUppercase = 0;
}
if(options.Lowercase.GetValueOrDefault() && options.MinLowercase.GetValueOrDefault() <= 0)
{
options.MinLowercase = 1;
}
else if(!options.Lowercase.GetValueOrDefault())
{
options.MinLowercase = 0;
}
if(options.Number.GetValueOrDefault() && options.MinNumber.GetValueOrDefault() <= 0)
{
options.MinNumber = 1;
}
else if(!options.Number.GetValueOrDefault())
{
options.MinNumber = 0;
}
if(options.Special.GetValueOrDefault() && options.MinSpecial.GetValueOrDefault() <= 0)
{
options.MinSpecial = 1;
}
else if(!options.Special.GetValueOrDefault())
{
options.MinSpecial = 0;
}
if(options.Length.GetValueOrDefault() < 1)
{
options.Length = 10;
}
var minLength = options.MinSpecial.GetValueOrDefault() + options.MinLowercase.GetValueOrDefault() +
options.MinNumber.GetValueOrDefault() + options.MinSpecial.GetValueOrDefault();
options.MinNumber.GetValueOrDefault() + options.MinUppercase.GetValueOrDefault();
if(options.Length < minLength)
{
options.Length = minLength;

View File

@@ -23,7 +23,7 @@ namespace Bit.Core.Services
private readonly ICollectionService _collectionService;
private readonly IStorageService _storageService;
private readonly IMessagingService _messagingService;
private readonly Action _logoutCallback;
private readonly Func<bool, Task> _logoutCallbackAsync;
public SyncService(
IUserService userService,
@@ -35,7 +35,7 @@ namespace Bit.Core.Services
ICollectionService collectionService,
IStorageService storageService,
IMessagingService messagingService,
Action logoutCallback)
Func<bool, Task> logoutCallbackAsync)
{
_userService = userService;
_apiService = apiService;
@@ -46,7 +46,7 @@ namespace Bit.Core.Services
_collectionService = collectionService;
_storageService = storageService;
_messagingService = messagingService;
_logoutCallback = logoutCallback;
_logoutCallbackAsync = logoutCallbackAsync;
}
public bool SyncInProgress { get; set; }
@@ -71,7 +71,7 @@ namespace Bit.Core.Services
await _storageService.SaveAsync(string.Format(Keys_LastSyncFormat, userId), date);
}
public async Task<bool> FullSyncAsync(bool forceSync)
public async Task<bool> FullSyncAsync(bool forceSync, bool allowThrowOnError = false)
{
SyncStarted();
var isAuthenticated = await _userService.IsAuthenticatedAsync();
@@ -106,7 +106,14 @@ namespace Bit.Core.Services
}
catch
{
return SyncCompleted(false);
if(allowThrowOnError)
{
throw;
}
else
{
return SyncCompleted(false);
}
}
}
@@ -300,7 +307,10 @@ namespace Bit.Core.Services
var stamp = await _userService.GetSecurityStampAsync();
if(stamp != null && stamp != response.SecurityStamp)
{
_logoutCallback?.Invoke();
if(_logoutCallbackAsync != null)
{
await _logoutCallbackAsync(true);
}
return;
}
await _cryptoService.SetEncKeyAsync(response.Key);

View File

@@ -11,7 +11,7 @@ namespace Bit.Core.Utilities
public static Dictionary<string, object> RegisteredServices { get; set; } = new Dictionary<string, object>();
public static bool Inited { get; set; }
public static void Init()
public static void Init(string customUserAgent = null)
{
if(Inited)
{
@@ -31,7 +31,11 @@ namespace Bit.Core.Utilities
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
var cryptoService = new CryptoService(storageService, secureStorageService, cryptoFunctionService);
var tokenService = new TokenService(storageService);
var apiService = new ApiService(tokenService, platformUtilsService, (bool expired) => Task.FromResult(0));
var apiService = new ApiService(tokenService, platformUtilsService, (bool expired) =>
{
messagingService.Send("logout", expired);
return Task.FromResult(0);
}, customUserAgent);
var appIdService = new AppIdService(storageService);
var userService = new UserService(storageService, tokenService);
var settingsService = new SettingsService(userService, storageService);
@@ -44,8 +48,11 @@ namespace Bit.Core.Utilities
var lockService = new LockService(cryptoService, userService, platformUtilsService, storageService,
folderService, cipherService, collectionService, searchService, messagingService, null);
var syncService = new SyncService(userService, apiService, settingsService, folderService,
cipherService, cryptoService, collectionService, storageService, messagingService,
() => messagingService.Send("logout"));
cipherService, cryptoService, collectionService, storageService, messagingService, (bool expired) =>
{
messagingService.Send("logout", expired);
return Task.FromResult(0);
});
var passwordGenerationService = new PasswordGenerationService(cryptoService, storageService,
cryptoFunctionService);
var totpService = new TotpService(storageService, cryptoFunctionService);

View File

@@ -1,4 +1,5 @@
using AuthenticationServices;
using AuthenticationServices;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
@@ -18,7 +19,9 @@ namespace Bit.iOS.Autofill
public CredentialProviderViewController(IntPtr handle)
: base(handle)
{ }
{
ModalPresentationStyle = UIModalPresentationStyle.FullScreen;
}
public override void ViewDidLoad()
{
@@ -191,8 +194,8 @@ namespace Bit.iOS.Autofill
private async Task ProvideCredentialAsync()
{
var cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
var cipher = await cipherService.GetAsync(_context.CredentialIdentity.RecordIdentifier);
var cipherService = ServiceContainer.Resolve<ICipherService>("cipherService", true);
var cipher = await cipherService?.GetAsync(_context.CredentialIdentity.RecordIdentifier);
if(cipher == null || cipher.Type != Bit.Core.Enums.CipherType.Login)
{
var err = new NSError(new NSString("ASExtensionErrorDomain"),
@@ -265,14 +268,15 @@ namespace Bit.iOS.Autofill
ServiceContainer.Reset();
}
iOSCoreHelpers.RegisterLocalServices();
ServiceContainer.Init();
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
ServiceContainer.Init(deviceActionService.DeviceUserAgent);
if(!_initedHockeyApp)
{
iOSCoreHelpers.RegisterHockeyApp();
_initedHockeyApp = true;
}
iOSCoreHelpers.Bootstrap();
iOSCoreHelpers.AppearanceAdjustments();
iOSCoreHelpers.AppearanceAdjustments(deviceActionService);
}
private void InitAppIfNeeded()
@@ -283,4 +287,4 @@ namespace Bit.iOS.Autofill
}
}
}
}
}

View File

@@ -11,9 +11,9 @@
<key>CFBundleIdentifier</key>
<string>com.8bit.bitwarden.autofill</string>
<key>CFBundleShortVersionString</key>
<string>2.2.1</string>
<string>2.2.6</string>
<key>CFBundleVersion</key>
<string>55</string>
<string>59</string>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using Bit.iOS.Autofill.Models;
using Foundation;
using UIKit;
@@ -27,6 +27,10 @@ namespace Bit.iOS.Autofill
CancelBarButton.Title = AppResources.Cancel;
SearchBar.Placeholder = AppResources.Search;
SearchBar.BackgroundColor = SearchBar.BarTintColor = ThemeHelpers.ListHeaderBackgroundColor;
if(!ThemeHelpers.LightTheme)
{
SearchBar.KeyboardAppearance = UIKeyboardAppearance.Dark;
}
TableView.RowHeight = UITableView.AutomaticDimension;
TableView.EstimatedRowHeight = 44;
@@ -35,6 +39,12 @@ namespace Bit.iOS.Autofill
await ((TableSource)TableView.Source).LoadItemsAsync(false, SearchBar.Text);
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
SearchBar?.BecomeFirstResponder();
}
partial void CancelBarButton_Activated(UIBarButtonItem sender)
{
if(FromList)

Some files were not shown because too many files have changed in this diff Show More