Merge branch 'Branch_1.07'

# Resolved Conflicts:
#	src/keepass2android/Resources/values/strings.xml
This commit is contained in:
Philipp Crocoll
2019-10-10 03:25:02 +02:00
4 changed files with 242 additions and 165 deletions

View File

@@ -294,19 +294,37 @@ namespace keepass2android.Io
get { yield return ProtocolId; } get { yield return ProtocolId; }
} }
Dictionary<String /*userid*/, IGraphServiceClient> mClientByUser = class GraphServiceClientWithState
new Dictionary<String /*userid*/, IGraphServiceClient>(); {
public IGraphServiceClient Client { get; set; }
public DateTime TokenExpiryDate { get; set; }
public bool RequiresUserInteraction { get; set; }
}
private IGraphServiceClient tryGetMsGraphClient(String path) readonly Dictionary<String /*userid*/, GraphServiceClientWithState> mClientByUser =
new Dictionary<String /*userid*/, GraphServiceClientWithState>();
private async Task<IGraphServiceClient> TryGetMsGraphClient(String path, bool tryConnect)
{ {
String userId = OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(path).User.Id; String userId = OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(path).User.Id;
if (mClientByUser.ContainsKey(userId)) if (mClientByUser.ContainsKey(userId))
return mClientByUser[userId]; {
GraphServiceClientWithState clientWithState = mClientByUser[userId];
if (!(clientWithState.RequiresUserInteraction || (clientWithState.TokenExpiryDate < DateTime.Now) || (clientWithState.Client == null)))
return clientWithState.Client;
}
if (tryConnect)
{
if (await TryLoginSilent(path) != null)
{
return mClientByUser[userId].Client;
}
}
return null; return null;
} }
private IGraphServiceClient buildClient(AuthenticationResult authenticationResult) private IGraphServiceClient BuildClient(AuthenticationResult authenticationResult)
{ {
logDebug("buildClient..."); logDebug("buildClient...");
@@ -321,14 +339,20 @@ namespace keepass2android.Io
return Task.FromResult(0); return Task.FromResult(0);
}); });
GraphServiceClient graphClient = new GraphServiceClient(authenticationProvider); GraphServiceClientWithState clientWithState = new GraphServiceClientWithState()
{
Client = new GraphServiceClient(authenticationProvider),
RequiresUserInteraction = false,
TokenExpiryDate = authenticationResult.ExpiresOn.LocalDateTime
};
if (authenticationResult.Account == null) if (authenticationResult.Account == null)
throw new Exception("authenticationResult.Account == null!"); throw new Exception("authenticationResult.Account == null!");
mClientByUser[authenticationResult.Account.HomeAccountId.Identifier] = graphClient; mClientByUser[authenticationResult.Account.HomeAccountId.Identifier] = clientWithState;
return graphClient; return clientWithState.Client;
} }
@@ -341,7 +365,7 @@ namespace keepass2android.Io
protected abstract string SpecialFolder { get; } protected abstract string SpecialFolder { get; }
private PathItemBuilder GetPathItemBuilder(String path) private async Task<PathItemBuilder> GetPathItemBuilder(String path)
{ {
PathItemBuilder result = new PathItemBuilder(SpecialFolder); PathItemBuilder result = new PathItemBuilder(SpecialFolder);
@@ -351,12 +375,13 @@ namespace keepass2android.Io
{ {
throw new Exception("path does not contain user"); throw new Exception("path does not contain user");
} }
result.client = null;
if (!mClientByUser.TryGetValue(result.itemLocation.User.Id, out result.client)) result.client = await TryGetMsGraphClient(path, true);
{
throw new Exception("failed to get client for " + result.itemLocation.User.Id);
}
if (result.client == null)
throw new Exception("Failed to connect or authenticate to OneDrive!");
return result; return result;
} }
@@ -400,10 +425,14 @@ namespace keepass2android.Io
{ {
try try
{ {
PathItemBuilder pathItemBuilder = GetPathItemBuilder(ioc.Path);
Task.Run(async () => await pathItemBuilder.getPathItem() Task.Run(async () =>
.Request() {
.DeleteAsync()).Wait(); PathItemBuilder pathItemBuilder = await GetPathItemBuilder(ioc.Path);
await pathItemBuilder.getPathItem()
.Request()
.DeleteAsync();
}).Wait();
} }
catch (Exception e) catch (Exception e)
{ {
@@ -426,13 +455,17 @@ namespace keepass2android.Io
try try
{ {
string path = ioc.Path; string path = ioc.Path;
PathItemBuilder clientAndpath = GetPathItemBuilder(path);
logDebug("openFileForRead. Path=" + path); logDebug("openFileForRead. Path=" + path);
Stream result = Task.Run(async () => await clientAndpath Stream result = Task.Run(async () =>
.getPathItem() {
.Content PathItemBuilder clientAndpath = await GetPathItemBuilder(path);
.Request() return await clientAndpath
.GetAsync()).Result; .getPathItem()
.Content
.Request()
.GetAsync();
}).Result;
logDebug("ok"); logDebug("ok");
return result; return result;
@@ -479,14 +512,16 @@ namespace keepass2android.Io
{ {
try try
{ {
Task.Run(async () =>
PathItemBuilder pathItemBuilder = GetPathItemBuilder(path); {
Task.Run(async () => await PathItemBuilder pathItemBuilder = await GetPathItemBuilder(path);
pathItemBuilder return await
.getPathItem() pathItemBuilder
.Content .getPathItem()
.Request() .Content
.PutAsync<DriveItem>(stream)).Wait(); .Request()
.PutAsync<DriveItem>(stream);
}).Wait();
} }
catch (Exception e) catch (Exception e)
@@ -530,15 +565,16 @@ namespace keepass2android.Io
driveItem.Name = newDirName; driveItem.Name = newDirName;
driveItem.Folder = new Folder(); driveItem.Folder = new Folder();
PathItemBuilder pathItemBuilder = GetPathItemBuilder(parentIoc.Path); DriveItem res = Task.Run(async () =>
{
PathItemBuilder pathItemBuilder = await GetPathItemBuilder(parentIoc.Path);
logDebug("building request for " + pathItemBuilder.itemLocation);
return await pathItemBuilder.getPathItem()
DriveItem res = Task.Run(async () => await pathItemBuilder.getPathItem() .Children
.Children .Request()
.Request() .AddAsync(driveItem);
.AddAsync(driveItem)).Result; }).Result;
} }
@@ -552,43 +588,8 @@ namespace keepass2android.Io
{ {
try try
{ {
PathItemBuilder pathItemBuilder = GetPathItemBuilder(ioc.Path);
logDebug("listing files for " + ioc.Path); return Task.Run(async () => await ListContentsAsync(ioc)).Result;
if (!pathItemBuilder.hasShare() && !pathItemBuilder.hasOneDrivePath())
{
logDebug("listing shares.");
return ListShares(pathItemBuilder.itemLocation, pathItemBuilder.client);
}
logDebug("listing regular children.");
List<FileDescription> result = new List<FileDescription>();
/*logDebug("parent before:" + parentPath);
parentPath = parentPath.substring(getProtocolPrefix().length());
logDebug("parent after: " + parentPath);*/
IDriveItemChildrenCollectionPage itemsPage = Task.Run(async () => await pathItemBuilder.getPathItem()
.Children
.Request()
.GetAsync()).Result;
while (true)
{
IList<DriveItem> items = itemsPage.CurrentPage;
if (!items.Any())
return result;
foreach (DriveItem i in items)
{
var e = GetFileDescription(pathItemBuilder.itemLocation.BuildLocalChildLocation(i.Name, i.Id, i.ParentReference?.DriveId), i);
result.Add(e);
}
var nextPageReqBuilder = itemsPage.NextPageRequest;
if (nextPageReqBuilder == null)
return result;
itemsPage = Task.Run(async () => await nextPageReqBuilder.GetAsync()).Result;
}
} }
catch (Exception e) catch (Exception e)
{ {
@@ -596,6 +597,46 @@ namespace keepass2android.Io
} }
} }
private async Task<IEnumerable<FileDescription>> ListContentsAsync(IOConnectionInfo ioc)
{
PathItemBuilder pathItemBuilder = await GetPathItemBuilder(ioc.Path);
logDebug("listing files for " + ioc.Path);
if (!pathItemBuilder.hasShare() && !pathItemBuilder.hasOneDrivePath())
{
logDebug("listing shares.");
return await ListShares(pathItemBuilder.itemLocation, pathItemBuilder.client);
}
logDebug("listing regular children.");
List<FileDescription> result = new List<FileDescription>();
/*logDebug("parent before:" + parentPath);
parentPath = parentPath.substring(getProtocolPrefix().length());
logDebug("parent after: " + parentPath);*/
IDriveItemChildrenCollectionPage itemsPage = await pathItemBuilder.getPathItem()
.Children
.Request()
.GetAsync();
while (true)
{
IList<DriveItem> items = itemsPage.CurrentPage;
if (!items.Any())
return result;
foreach (DriveItem i in items)
{
var e = GetFileDescription(pathItemBuilder.itemLocation.BuildLocalChildLocation(i.Name, i.Id, i.ParentReference?.DriveId), i);
result.Add(e);
}
var nextPageReqBuilder = itemsPage.NextPageRequest;
if (nextPageReqBuilder == null)
return result;
itemsPage = Task.Run(async () => await nextPageReqBuilder.GetAsync()).Result;
}
}
private FileDescription GetFileDescription(OneDrive2ItemLocation<OneDrive2PrefixContainerType> path, DriveItem i) private FileDescription GetFileDescription(OneDrive2ItemLocation<OneDrive2PrefixContainerType> path, DriveItem i)
{ {
@@ -620,24 +661,7 @@ namespace keepass2android.Io
{ {
try try
{ {
string filename = ioc.Path; return Task.Run(async() => await GetFileDescriptionAsync(ioc)).Result;
PathItemBuilder pathItemBuilder = GetPathItemBuilder(filename);
if (!pathItemBuilder.itemLocation.LocalPath.Any()
&& !pathItemBuilder.hasShare())
{
FileDescription rootEntry = new FileDescription();
rootEntry.CanRead = rootEntry.CanWrite = true;
rootEntry.Path = filename;
rootEntry.DisplayName = pathItemBuilder.itemLocation.User.Name;
rootEntry.IsDirectory = true;
return rootEntry;
}
IDriveItemRequestBuilder pathItem = pathItemBuilder.getPathItem();
DriveItem item = Task.Run(async () => await pathItem.Request().GetAsync()).Result;
return GetFileDescription(pathItemBuilder.itemLocation, item);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -645,6 +669,28 @@ namespace keepass2android.Io
} }
} }
private async Task<FileDescription> GetFileDescriptionAsync(IOConnectionInfo ioc)
{
string filename = ioc.Path;
PathItemBuilder pathItemBuilder = await GetPathItemBuilder(filename);
if (!pathItemBuilder.itemLocation.LocalPath.Any()
&& !pathItemBuilder.hasShare())
{
FileDescription rootEntry = new FileDescription();
rootEntry.CanRead = rootEntry.CanWrite = true;
rootEntry.Path = filename;
rootEntry.DisplayName = pathItemBuilder.itemLocation.User.Name;
rootEntry.IsDirectory = true;
return rootEntry;
}
IDriveItemRequestBuilder pathItem = pathItemBuilder.getPathItem();
DriveItem item = await pathItem.Request().GetAsync();
return GetFileDescription(pathItemBuilder.itemLocation, item);
}
public bool RequiresSetup(IOConnectionInfo ioConnection) public bool RequiresSetup(IOConnectionInfo ioConnection)
{ {
return false; return false;
@@ -663,13 +709,13 @@ namespace keepass2android.Io
} }
private bool isConnected(String path) private async Task<bool> IsConnectedAsync(string path, bool tryConnect)
{ {
try try
{ {
logDebug("isConnected? " + path); logDebug("isConnected? " + path);
return tryGetMsGraphClient(path) != null; return (await TryGetMsGraphClient(path, tryConnect)) != null;
} }
catch (Exception e) catch (Exception e)
{ {
@@ -679,11 +725,15 @@ namespace keepass2android.Io
} }
public bool IsConnected(string path)
{
return Task.Run(async () => await IsConnectedAsync(path, false)).Result;
}
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode, public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode,
bool alwaysReturnSuccess) bool alwaysReturnSuccess)
{ {
if (isConnected(ioc.Path)) if (IsConnected(ioc.Path))
{ {
Intent intent = new Intent(); Intent intent = new Intent();
intent.PutExtra(FileStorageSetupDefs.ExtraPath, ioc.Path); intent.PutExtra(FileStorageSetupDefs.ExtraPath, ioc.Path);
@@ -698,7 +748,7 @@ namespace keepass2android.Io
public void PrepareFileUsage(Context ctx, IOConnectionInfo ioc) public void PrepareFileUsage(Context ctx, IOConnectionInfo ioc)
{ {
if (!isConnected(ioc.Path)) if (!Task.Run(async() => await IsConnectedAsync(ioc.Path, true)).Result)
{ {
throw new Exception("MsGraph login required"); throw new Exception("MsGraph login required");
} }
@@ -714,7 +764,7 @@ namespace keepass2android.Io
} }
protected void finishActivityWithSuccess( protected void FinishActivityWithSuccess(
IFileStorageSetupActivity setupActivity) IFileStorageSetupActivity setupActivity)
{ {
//Log.d("KP2AJ", "Success with authenticating!"); //Log.d("KP2AJ", "Success with authenticating!");
@@ -751,19 +801,50 @@ namespace keepass2android.Io
if (activity.ProcessName.Equals(FileStorageSetupDefs.ProcessNameFileUsageSetup)) if (activity.ProcessName.Equals(FileStorageSetupDefs.ProcessNameFileUsageSetup))
activity.State.PutString(FileStorageSetupDefs.ExtraPath, activity.Ioc.Path); activity.State.PutString(FileStorageSetupDefs.ExtraPath, activity.Ioc.Path);
string rootPathForUser = await TryLoginSilent(activity.Ioc.Path);
if (rootPathForUser != null)
{
FinishActivityWithSuccess(activity, rootPathForUser);
}
try
{
logDebug("try interactive");
AuthenticationResult res = await _publicClientApp.AcquireTokenInteractive(Scopes)
.WithParentActivityOrWindow((Activity)activity)
.ExecuteAsync();
logDebug("ok interactive");
BuildClient(res);
FinishActivityWithSuccess(activity, BuildRootPathForUser(res));
}
catch (Exception e)
{
logDebug("authenticating not successful: " + e);
Intent data = new Intent();
data.PutExtra(FileStorageSetupDefs.ExtraErrorMessage, "authenticating not successful");
((Activity)activity).SetResult(Result.Canceled, data);
((Activity)activity).Finish();
}
}
private async Task<string> TryLoginSilent(string iocPath)
{
IAccount account = null; IAccount account = null;
try try
{ {
String userId = OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(activity.Ioc.Path).User?.Id;
if (mClientByUser.ContainsKey(userId)) if (IsConnected(iocPath))
{ {
finishActivityWithSuccess(activity); return iocPath;
return;
} }
String userId = OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(iocPath).User?.Id;
logDebug("needs acquire token"); logDebug("needs acquire token");
logDebug("trying silent login " + activity.Ioc.Path); logDebug("trying silent login " + iocPath);
account = Task.Run(async () => await _publicClientApp.GetAccountAsync(userId)).Result; account = Task.Run(async () => await _publicClientApp.GetAccountAsync(userId)).Result;
logDebug("getting user ok."); logDebug("getting user ok.");
@@ -783,54 +864,46 @@ namespace keepass2android.Io
.ExecuteAsync(); .ExecuteAsync();
logDebug("AcquireTokenSilent ok."); logDebug("AcquireTokenSilent ok.");
var graphClient = buildClient(authResult); BuildClient(authResult);
/*User me = await graphClient.Me.Request().WithForceRefresh(true).GetAsync(); /*User me = await graphClient.Me.Request().WithForceRefresh(true).GetAsync();
logDebug("received name " + me.DisplayName);*/ logDebug("received name " + me.DisplayName);*/
finishActivityWithSuccess(activity, authResult); return BuildRootPathForUser(authResult);
return;
} }
catch (MsalUiRequiredException ex) catch (MsalUiRequiredException ex)
{ {
logDebug("ui required");
GraphServiceClientWithState clientWithState = new GraphServiceClientWithState()
{
Client = null,
RequiresUserInteraction = true
};
mClientByUser[account.HomeAccountId.Identifier] = clientWithState;
logDebug("ui required");
return null;
}
catch (Exception ex)
{
logDebug("silent login failed: " + ex.ToString());
return null;
} }
} }
try return null;
{
logDebug("try interactive");
AuthenticationResult res = await _publicClientApp.AcquireTokenInteractive(Scopes)
.WithParentActivityOrWindow((Activity)activity)
.ExecuteAsync();
logDebug("ok interactive");
buildClient(res);
finishActivityWithSuccess(activity, res);
}
catch (Exception e)
{
logDebug("authenticating not successful: " + e);
Intent data = new Intent();
data.PutExtra(FileStorageSetupDefs.ExtraErrorMessage, "authenticating not successful");
((Activity)activity).SetResult(Result.Canceled, data);
((Activity)activity).Finish();
}
} }
string buildRootPathForUser(AuthenticationResult res) string BuildRootPathForUser(AuthenticationResult res)
{ {
return OneDrive2ItemLocation<OneDrive2PrefixContainerType>.RootForUser(res.Account.Username, res.Account.HomeAccountId.Identifier).ToString(); return OneDrive2ItemLocation<OneDrive2PrefixContainerType>.RootForUser(res.Account.Username, res.Account.HomeAccountId.Identifier).ToString();
} }
private void finishActivityWithSuccess(IFileStorageSetupActivity activity, AuthenticationResult authResult) private void FinishActivityWithSuccess(IFileStorageSetupActivity activity, string rootPathForUser)
{ {
activity.State.PutString(FileStorageSetupDefs.ExtraPath, buildRootPathForUser(authResult)); activity.State.PutString(FileStorageSetupDefs.ExtraPath, rootPathForUser);
finishActivityWithSuccess(activity); FinishActivityWithSuccess(activity);
} }
public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data) public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data)
@@ -871,13 +944,13 @@ namespace keepass2android.Io
} }
private List<FileDescription> ListShares(OneDrive2ItemLocation<OneDrive2PrefixContainerType> parentPath, IGraphServiceClient client) private async Task<List<FileDescription>> ListShares(OneDrive2ItemLocation<OneDrive2PrefixContainerType> parentPath, IGraphServiceClient client)
{ {
List<FileDescription> result = new List<FileDescription>(); List<FileDescription> result = new List<FileDescription>();
DriveItem root = Task.Run(async () => await client.Me.Drive.Root.Request().GetAsync()).Result; DriveItem root = await client.Me.Drive.Root.Request().GetAsync();
FileDescription myEntry = GetFileDescription(parentPath.BuildShare("me","me","me", root.ParentReference?.DriveId), root); FileDescription myEntry = GetFileDescription(parentPath.BuildShare("me","me","me", root.ParentReference?.DriveId), root);
myEntry.DisplayName = MyOneDriveDisplayName; myEntry.DisplayName = MyOneDriveDisplayName;
@@ -888,8 +961,7 @@ namespace keepass2android.Io
IDriveSharedWithMeCollectionPage sharedWithMeCollectionPage = IDriveSharedWithMeCollectionPage sharedWithMeCollectionPage = await client.Me.Drive.SharedWithMe().Request().GetAsync();
Task.Run(async () => await client.Me.Drive.SharedWithMe().Request().GetAsync()).Result;
while (true) while (true)
{ {
@@ -905,7 +977,7 @@ namespace keepass2android.Io
} }
var b = sharedWithMeCollectionPage.NextPageRequest; var b = sharedWithMeCollectionPage.NextPageRequest;
if (b == null) break; if (b == null) break;
sharedWithMeCollectionPage = Task.Run(async () => await b.GetAsync()).Result; sharedWithMeCollectionPage = await b.GetAsync();
} }
return result; return result;
} }
@@ -945,29 +1017,7 @@ namespace keepass2android.Io
{ {
try try
{ {
DriveItem driveItem = new DriveItem(); return Task.Run(async() => await CreateFilePathAsync(parent, newFilename)).Result;
driveItem.Name = newFilename;
driveItem.File = new File();
PathItemBuilder pathItemBuilder = GetPathItemBuilder(parent);
//see if such a file exists already:
var item = TryFindFile(pathItemBuilder, newFilename);
if (item != null)
{
return pathItemBuilder.itemLocation.BuildLocalChildLocation(item.Name, item.Id, item.ParentReference?.DriveId).ToString();
}
//doesn't exist. Create:
logDebug("building request for " + pathItemBuilder.itemLocation);
DriveItem res = Task.Run(async () => await pathItemBuilder.getPathItem()
.Children
.Request()
.AddAsync(driveItem)).Result;
return pathItemBuilder.itemLocation.BuildLocalChildLocation(res.Name, res.Id, res.ParentReference?.DriveId).ToString();
} }
catch (Exception e) catch (Exception e)
{ {
@@ -976,6 +1026,32 @@ namespace keepass2android.Io
} }
private async Task<string> CreateFilePathAsync(string parent, string newFilename)
{
DriveItem driveItem = new DriveItem();
driveItem.Name = newFilename;
driveItem.File = new File();
PathItemBuilder pathItemBuilder = await GetPathItemBuilder(parent);
//see if such a file exists already:
var item = TryFindFile(pathItemBuilder, newFilename);
if (item != null)
{
return pathItemBuilder.itemLocation.BuildLocalChildLocation(item.Name, item.Id, item.ParentReference?.DriveId)
.ToString();
}
//doesn't exist. Create:
logDebug("building request for " + pathItemBuilder.itemLocation);
DriveItem res = await pathItemBuilder.getPathItem()
.Children
.Request()
.AddAsync(driveItem);
return pathItemBuilder.itemLocation.BuildLocalChildLocation(res.Name, res.Id, res.ParentReference?.DriveId)
.ToString();
}
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc) public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
{ {
return IOConnectionInfo.FromPath(OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(ioc.Path).Parent.ToString()); return IOConnectionInfo.FromPath(OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(ioc.Path).Parent.ToString());

View File

@@ -635,7 +635,7 @@
<string name="EntryChannel_desc">Értesítés a jelenleg kiválasztott bejegyzés egyszerűsített eléréséhez.</string> <string name="EntryChannel_desc">Értesítés a jelenleg kiválasztott bejegyzés egyszerűsített eléréséhez.</string>
<string name="ShowKeyboardDuringFingerprintAuth">A billentyűzet megjelenítése a jelszóbeíráshoz ujjlenyomat-leolvasás közben.</string> <string name="ShowKeyboardDuringFingerprintAuth">A billentyűzet megjelenítése a jelszóbeíráshoz ujjlenyomat-leolvasás közben.</string>
<string name="ChangeLog_keptDonate">Lehetőségek kiterjesztése, hogy egy sörrel vagy valami mással támogasson</string> <string name="ChangeLog_keptDonate">Lehetőségek kiterjesztése, hogy egy sörrel vagy valami mással támogasson</string>
<string name="ChangeLog"><b>Version 0.6.2</b> <string name="ChangeLog">&lt;b&gt;Version 0.6.2&lt;/&gt;
* Google Drive/Dropbox/... integráció: Ha a hivatalos Google Drive vagy Dropbox applikációból nyit meg egy .kdbx fájlt, akkor az autómatikusan a KP2A-val kerül megnyitásra. * Google Drive/Dropbox/... integráció: Ha a hivatalos Google Drive vagy Dropbox applikációból nyit meg egy .kdbx fájlt, akkor az autómatikusan a KP2A-val kerül megnyitásra.
* Továbbfejlesztett kereső dialógus * Továbbfejlesztett kereső dialógus
* Továbbfejlesztett keresési eredmények ha a megosztott URL aldomaint tartalmaz * Továbbfejlesztett keresési eredmények ha a megosztott URL aldomaint tartalmaz

View File

@@ -543,7 +543,7 @@
<string name="SCOPE_QUERY_CREDENTIALS_title">Запрос учетных данных</string> <string name="SCOPE_QUERY_CREDENTIALS_title">Запрос учетных данных</string>
<string name="SCOPE_QUERY_CREDENTIALS_explanation">Плагину будет разрешено запрашивать учетные данные для веб-сайтов или приложений которые он использует.</string> <string name="SCOPE_QUERY_CREDENTIALS_explanation">Плагину будет разрешено запрашивать учетные данные для веб-сайтов или приложений которые он использует.</string>
<string name="get_regular_version">Больше типов хранилищ</string> <string name="get_regular_version">Больше типов хранилищ</string>
<string name="CertificateWarning">Предупреждение: Ошибка проверки сертификата сервера: %1$ s. Установите соответствующий корневой сертификат на вашем устройстве или проверьте настройки!</string> <string name="CertificateWarning">Предупреждение: Ошибка проверки сертификата сервера: %1$s. Установите соответствующий корневой сертификат на вашем устройстве или проверьте настройки!</string>
<string name="CertificateFailure">Ошибка: Проверка сертификата сервера не удалась! Установите соответствующий корневой сертификат или проверьте настройки!</string> <string name="CertificateFailure">Ошибка: Проверка сертификата сервера не удалась! Установите соответствующий корневой сертификат или проверьте настройки!</string>
<string name="export_fileformats_title">Выберите формат файла</string> <string name="export_fileformats_title">Выберите формат файла</string>
<string name="killed_by_os">Извините! Keepass2Android был убит ОС Android! По соображениям безопасности Keepass2Android не сохраняет выбранные учетные данные на диске, так что вам нужно повторно открыть базу данных. Примечание: Это должно происходить очень редко. Если это так, пожалуйста, напишите мне сообщение на crocoapps@gmail.com.</string> <string name="killed_by_os">Извините! Keepass2Android был убит ОС Android! По соображениям безопасности Keepass2Android не сохраняет выбранные учетные данные на диске, так что вам нужно повторно открыть базу данных. Примечание: Это должно происходить очень редко. Если это так, пожалуйста, напишите мне сообщение на crocoapps@gmail.com.</string>

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net--> <!--Generated by crowdin.net-->
<resources> <resources>
<string name="about_feedback">Feedback</string> <string name="about_feedback">Feedback</string>
<string name="AboutText">Keepass2Android is a password manager providing read/write access to KeePass 2.x databases on Android.</string> <string name="AboutText">Keepass2Android is a password manager providing read/write access to KeePass 2.x databases on Android.</string>
<string name="CreditsText">The User Interface is based on a port of KeepassDroid developed by Brian Pellin. Code for database operations is based on KeePass by Dominik Reichl. The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.</string> <string name="CreditsText">The User Interface is based on a port of KeepassDroid developed by Brian Pellin. Code for database operations is based on KeePass by Dominik Reichl. The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.</string>