Compare commits

...

78 Commits

Author SHA1 Message Date
Philipp Crocoll
c3c9c8610d remove debug logging 2025-06-03 16:00:24 +02:00
Philipp Crocoll
9c2d50c7b0 remove debugging code, resolve some TODOs 2025-06-03 15:54:34 +02:00
Philipp Crocoll
0cd9df7415 fix issues with background sync and multiple databases (especially autoopen) 2025-06-03 15:37:21 +02:00
Philipp Crocoll
a4a3112dc6 fix issues with background operations implementation, added more logging 2025-06-03 14:44:01 +02:00
Philipp Crocoll
6351e1f3d0 towards unifying Background and Blocking Operation Runners. Not working yet, does it run more in non-ui thread? 2025-06-03 11:12:37 +02:00
Philipp Crocoll
2959d8cbcc improve intent when clicking the background-operation notification 2025-06-03 10:16:48 +02:00
Philipp Crocoll
c98680347a fix implementation for BackgroundSyncService for blocking operations 2025-05-27 16:29:42 +02:00
Philipp Crocoll
35367bb28b use ActionInContextInstanceOnOperationFinished where appropriate 2025-05-27 15:54:01 +02:00
Philipp Crocoll
8a1890bc10 refactor and improve EntryEditActivity: Ensure that configuration changes are handled properly 2025-05-27 15:26:09 +02:00
Philipp Crocoll
bf4035fcfe refactor EntryEditActivity, extracting views for entry-edit sections 2025-05-27 12:36:37 +02:00
Philipp Crocoll
d3dfbaab4b start using Pending Actions in EntryEditActivity. Extra fields still causing trouble on config change. 2025-05-27 11:59:26 +02:00
Philipp Crocoll
227074efb6 remove superfluous character from layout 2025-05-27 11:58:47 +02:00
Philipp Crocoll
69f79c1b20 change blocking operations to use BackgroundSyncService also. Introduce pending actions for context instances. 2025-05-27 11:57:58 +02:00
Philipp Crocoll
000d1254ec make parameter naming more consistent 2025-05-27 11:54:55 +02:00
Philipp Crocoll
324fc1f2ee allow to cancel background operations manually or when another operation starts; block database updates in certain activities, e.g. EntryEdit; 2025-05-20 17:05:13 +02:00
Philipp Crocoll
52121c6a85 split file into one file per class 2025-05-13 21:46:24 +02:00
Philipp Crocoll
c6f494ac33 remove no longer needed lock 2025-05-13 21:37:45 +02:00
Philipp Crocoll
400e171bc5 implement UI updates after background sync for Group activity and Entry activity 2025-05-13 21:34:06 +02:00
Philipp Crocoll
41e6e67e87 add support for yes/no/cancel question during background sync 2025-05-13 16:42:50 +02:00
Philipp Crocoll
8277283ebc only enable sync-in-background mode when file is already cached 2025-05-13 14:52:13 +02:00
Philipp Crocoll
71806178d0 tasks no longer store references to activities. These can "expire" and are hard to update (e.g. if one task creates another). Instead, the app can provide the currently active context. 2025-05-13 14:51:44 +02:00
Philipp Crocoll
aa2e4b856d introduce BackgroundSyncService to keep app alive while sync is going on. 2025-05-13 12:38:21 +02:00
Philipp Crocoll
cfb185b53d LoadDb and SaveDb respect the SyncInBackground preference 2025-05-13 11:10:26 +02:00
Philipp Crocoll
c3b6612591 let database sync run in the background. not handling all cases yet. 2025-05-13 09:23:57 +02:00
Philipp Crocoll
fefcf8f30e rename SynchronizeDatabase => StartSynchronizeDatabase; add experimental UI for displaying sync-in-progress 2025-05-06 13:29:47 +02:00
Philipp Crocoll
e95cc84a15 rename ProgressTask => BlockingOperationRunner 2025-04-29 15:10:54 +02:00
Philipp Crocoll
c0ed185612 rename RunnableOnFinish => OperationWithFinishHandler, introduce IKp2aStatusLogger 2025-04-29 15:08:09 +02:00
Philipp Crocoll
61c871f782 rename OnFinish => OnOperationFinishedHandler 2025-04-29 13:45:24 +02:00
Philipp Crocoll
e5d28f0979 only try to activate the keyboard once in password activity and entry edit activity; explain why the prompt shows up in SwitchImeActivity. closes #1400 2025-04-22 17:48:56 +02:00
Philipp Crocoll
0e581a66c5 treat server certificate failures as error by default, closes #1078 2025-04-22 16:02:02 +02:00
PhilippC
ceb31c54b1 Merge pull request #2847 from PhilippC/2430-remove-plain-storage-uri-from-logs
remove potential plain-text credentials for file storage from logs
2025-04-15 14:27:02 +02:00
Philipp Crocoll
42d8be593e remove potential plain-text credentials for file storage from logs 2025-04-15 14:00:31 +02:00
Philipp Crocoll
313adb6c3e manifest for 1.12-r5 2025-04-15 13:27:47 +02:00
PhilippC
668ba4cdee Merge pull request #2845 from PhilippC/2816-bug-yubikey-not-working-in-112
fix implementation of yubikey secret reading
2025-04-15 13:18:25 +02:00
PhilippC
a36bfa7ff5 Merge pull request #2833 from PhilippC/l10n_master3
New Crowdin updates
2025-04-15 12:45:48 +02:00
Philipp Crocoll
26c37bcd2a fix implementation of yubikey secret reading 2025-04-15 12:44:43 +02:00
PhilippC
1980f05a7c Merge pull request #2844 from PhilippC/2837-improve-error-reporting
Improve error reporting and fix crash
2025-04-15 12:17:18 +02:00
Philipp Crocoll
dbf10ba9fb Improve error reporting: Extract original message when showing "Java exception messages" consistently. Improve error handling in deprecated OneDrive API stub and avoid crash by no-longer throwing in Main-thread methods. 2025-04-15 11:45:16 +02:00
PhilippC
4be18d8373 Merge pull request #2830 from PhilippC/754-crash-when-using-fingerprint-before-otp
Fix crash when combining biometric unlock and KeeChallenge/OTP.
2025-04-15 11:42:59 +02:00
PhilippC
831b290d81 New translations strings.xml (Greek) 2025-04-13 20:35:49 +02:00
PhilippC
9d4c15f7bc New translations strings.xml (Greek) 2025-04-13 19:35:57 +02:00
PhilippC
4c4afa792d New translations strings.xml (Romanian) 2025-04-09 10:41:02 +02:00
Philipp Crocoll
8e256ac94d fix crash when combining biometric unlock and KeeChallenge/OTP. 2025-04-08 16:27:52 +02:00
Philipp Crocoll
65ff09f866 Manifest for 1.12-r4 2025-04-08 15:31:56 +02:00
Philipp Crocoll
8e9c2824cf add some files for building on Linux 2025-04-08 14:16:39 +02:00
PhilippC
92b8ff5c8d Merge pull request #2827 from PhilippC/2816-bug-yubikey-not-working-in-112
fix reading of cryptostream. Fixes Yubikey/KeeChallenge regression in 1.12-r1 .. 1.12-r3
2025-04-08 12:57:36 +02:00
PhilippC
223c3bfb8e Merge pull request #2826 from PhilippC/feature/snackbar-messages
use snackbars instead of toasts
2025-04-08 12:56:23 +02:00
PhilippC
b4e03a8374 Merge pull request #2797 from PhilippC/l10n_master3
New Crowdin updates
2025-04-08 12:41:50 +02:00
PhilippC
fb2df35d37 Merge pull request #2828 from PhilippC/2818-feat-new-color-scheme-too-beige-green
slightly adjust background color to become true gray.
2025-04-08 12:34:55 +02:00
Philipp Crocoll
50d6598b02 fix reading of cryptostream. The implementation issue became a bug in .net8. Closes #2816 2025-04-08 12:25:29 +02:00
Philipp Crocoll
90f04b76f4 fix issue with MessagePresenter potentially crashing 2025-04-08 12:20:24 +02:00
Philipp Crocoll
8b4314c394 fix bug with messages not always showing as they should. 2025-04-08 11:25:43 +02:00
Philipp Crocoll
17241bc422 use snackbars instead of toasts (in most cases) 2025-04-08 10:37:40 +02:00
PhilippC
c4a73bf107 New translations strings.xml (Czech) 2025-04-04 15:32:59 +02:00
PhilippC
4ba40ba24f New translations strings.xml (Slovenian) 2025-03-27 12:19:23 +01:00
PhilippC
e2711b709d New translations strings.xml (Portuguese, Brazilian) 2025-03-26 12:13:50 +01:00
PhilippC
4764b15e75 New translations strings.xml (Portuguese, Brazilian) 2025-03-26 09:06:54 +01:00
PhilippC
1b389ef12e New translations strings.xml (Japanese) 2025-03-26 09:06:43 +01:00
PhilippC
b32c2dbc7e New translations strings.xml (Czech) 2025-03-26 09:06:37 +01:00
PhilippC
f06937dab3 New translations strings.xml (French) 2025-03-26 09:06:32 +01:00
PhilippC
14efce62ff New translations strings.xml (Slovenian) 2025-03-26 09:06:29 +01:00
PhilippC
3c8b530e2e New translations strings.xml (Slovak) 2025-03-26 09:06:28 +01:00
PhilippC
9939e07b7d New translations strings.xml (Italian) 2025-03-26 09:06:27 +01:00
PhilippC
ecf416febc New translations strings.xml (German) 2025-03-26 09:06:25 +01:00
PhilippC
1cb036941e New translations strings.xml (Vietnamese) 2025-03-23 09:12:18 +01:00
PhilippC
a53ff37e89 New translations strings.xml (Vietnamese) 2025-03-23 07:30:07 +01:00
PhilippC
dc3ee35c8b New translations strings.xml (Italian) 2025-03-21 14:54:31 +01:00
PhilippC
e05fe94650 New translations strings.xml (Italian) 2025-03-20 21:28:17 +01:00
PhilippC
b0cb0b06a2 New translations strings.xml (German) 2025-03-20 19:11:11 +01:00
PhilippC
6d7b4810da New translations strings.xml (Hungarian) 2025-03-18 13:16:38 +01:00
PhilippC
585b747612 New translations strings.xml (Hungarian) 2025-03-18 11:35:13 +01:00
PhilippC
55887e1a89 New translations strings.xml (Slovak) 2025-03-17 18:31:07 +01:00
PhilippC
39a7a1298a New translations strings.xml (Romanian) 2025-03-14 09:08:28 +01:00
PhilippC
90059c5ae6 New translations strings.xml (Romanian) 2025-03-14 09:08:27 +01:00
PhilippC
ad63179484 New translations strings.xml (Romanian) 2025-03-14 07:51:48 +01:00
PhilippC
6eaba9d3a8 New translations strings.xml (Chinese Simplified) 2025-03-13 08:11:39 +01:00
PhilippC
11ce68902c New translations strings.xml (Slovenian) 2025-03-13 08:11:38 +01:00
PhilippC
70ca059e0f New translations strings.xml (Portuguese, Brazilian) 2025-03-12 00:48:40 +01:00
130 changed files with 5066 additions and 2668 deletions

View File

@@ -86,12 +86,17 @@ namespace KeePassLib.Interfaces
/// the current work.</returns>
bool SetText(string strNewText, LogStatusType lsType);
/// <summary>
/// Check if the user cancelled the current work.
/// </summary>
/// <returns>Returns <c>true</c> if the caller should continue
/// the current work.</returns>
bool ContinueWork();
void UpdateMessage(String message);
void UpdateSubMessage(String submessage);
/// <summary>
/// Check if the user cancelled the current work.
/// </summary>
/// <returns>Returns <c>true</c> if the caller should continue
/// the current work.</returns>
bool ContinueWork();
}
public sealed class NullStatusLogger : IStatusLogger
@@ -100,6 +105,12 @@ namespace KeePassLib.Interfaces
public void EndLogging() { }
public bool SetProgress(uint uPercent) { return true; }
public bool SetText(string strNewText, LogStatusType lsType) { return true; }
public bool ContinueWork() { return true; }
public void UpdateMessage(string message){
}
public void UpdateSubMessage(string submessage){
}
public bool ContinueWork() { return true; }
}
}

View File

@@ -21,6 +21,7 @@ using System.IO;
using Android;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Preferences;
using KeePassLib.Serialization;
@@ -34,9 +35,17 @@ namespace keepass2android
public static void Log(string message)
{
if (message != null)
Android.Util.Log.Debug("KP2A", message);
if (LogToFile)
if (message != null)
{
message += Thread.CurrentThread.ManagedThreadId != 0
? " (Thread ID: " + Thread.CurrentThread.ManagedThreadId + ")"
: "";
if (Looper.MainLooper == Looper.MyLooper())
message += " (Main Looper)";
Android.Util.Log.Debug("KP2A", message);
}
if (LogToFile)
{
lock (_fileLocker)
{

View File

@@ -208,7 +208,7 @@ namespace KeePassLib.Serialization
if (!string.IsNullOrEmpty(strHash) && (m_pbHashOfHeader != null) &&
!m_bRepairMode)
{
Debug.Assert(m_uFileVersion < FileVersion32_4);
// Debug.Assert(m_uFileVersion < FileVersion32_4);
byte[] pbHash = Convert.FromBase64String(strHash);
if (!MemUtil.ArraysEqual(pbHash, m_pbHashOfHeader))

View File

@@ -488,7 +488,7 @@ namespace KeePassLib.Serialization
ProtectedBinary pb = new ProtectedBinary(bProt, pbData,
1, pbData.Length - 1);
Debug.Assert(m_pbsBinaries.Find(pb) < 0); // No deduplication?
//Debug.Assert(m_pbsBinaries.Find(pb) < 0); // No deduplication?
m_pbsBinaries.Add(pb);
if (bProt) MemUtil.ZeroByteArray(pbData);

View File

@@ -0,0 +1,53 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using Android.App;
using Android.Content;
using Android.OS;
using Java.Lang;
using Java.Security;
using System.Threading.Tasks;
namespace keepass2android
{
/// <summary>
/// Class to run a task while a progress dialog is shown
/// </summary>
public class BlockingOperationStarter
{
private readonly OperationWithFinishHandler _task;
private readonly IKp2aApp _app;
public BlockingOperationStarter(IKp2aApp app, OperationWithFinishHandler task)
{
_task = task;
_app = app;
}
public void Run()
{
_app.CancelBackgroundOperations();
OperationRunner.Instance.Run(_app, _task, true);
}
}
}

View File

@@ -29,11 +29,19 @@ namespace keepass2android
}
public enum MessageSeverity
{
Info,
Warning,
Error
}
/// <summary>
/// Interface through which Activities and the logic layer can access some app specific functionalities and Application static data
/// </summary>
/// This also contains methods which are UI specific and should be replacable for testing.
public interface IKp2aApp : ICertificateValidationHandler
public interface IKp2aApp : ICertificateValidationHandler, IActiveContextProvider
{
/// <summary>
/// Locks all currently open databases, quicklocking if available (unless false is passed for allowQuickUnlock)
@@ -44,7 +52,9 @@ namespace keepass2android
/// <summary>
/// Loads the specified data as the currently open database, as unlocked.
/// </summary>
Database LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey, ProgressDialogStatusLogger statusLogger, IDatabaseFormat databaseFormat, bool makeCurrent);
Database LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey,
IKp2aStatusLogger statusLogger, IDatabaseFormat databaseFormat, bool makeCurrent,
IDatabaseModificationWatcher modificationWatcher);
HashSet<PwGroup> DirtyGroups { get; }
@@ -88,7 +98,6 @@ namespace keepass2android
EventHandler<DialogClickEventArgs> yesHandler,
EventHandler<DialogClickEventArgs> noHandler,
EventHandler<DialogClickEventArgs> cancelHandler,
Context ctx,
string messageSuffix = "");
/// <summary>
@@ -99,13 +108,15 @@ namespace keepass2android
EventHandler<DialogClickEventArgs> yesHandler,
EventHandler<DialogClickEventArgs> noHandler,
EventHandler<DialogClickEventArgs> cancelHandler,
Context ctx,
string messageSuffix = "");
/// <summary>
/// Returns a Handler object which can run tasks on the UI thread
/// </summary>
Handler UiThreadHandler { get; }
void ShowMessage(Context ctx, int resourceId, MessageSeverity severity);
void ShowMessage(Context ctx, string text, MessageSeverity severity);
/// <summary>
/// Returns a Handler object which can run tasks on the UI thread
/// </summary>
Handler UiThreadHandler { get; }
IProgressDialog CreateProgressDialog(Context ctx);
@@ -125,10 +136,17 @@ namespace keepass2android
bool CheckForDuplicateUuids { get; }
#if !NoNet && !EXCLUDE_JAVAFILESTORAGE
ICertificateErrorHandler CertificateErrorHandler { get; }
#endif
}
bool SyncInBackgroundPreference { get; set; }
void StartBackgroundSyncService();
ReaderWriterLockSlim DatabasesBackgroundModificationLock { get; }
bool CancelBackgroundOperations();
/// <summary>
/// Registers an action that should be executed when the context instance with the given id has been resumed.
/// </summary>
void RegisterPendingActionForContextInstance(int contextInstanceId, ActionOnOperationFinished actionToPerformWhenContextIsResumed);
}
}

View File

@@ -13,7 +13,7 @@ using Android.Content.PM;
using Android.OS;
using Android.Preferences;
using Java.IO;
using KeePass.Util;
using KeePassLib.Serialization;
using KeePassLib.Utility;
using File = System.IO.File;
@@ -121,7 +121,7 @@ namespace keepass2android.Io
var response = ex.Response as HttpWebResponse;
if ((response != null) && (response.StatusCode == HttpStatusCode.NotFound))
{
throw new FileNotFoundException(ex.Message, ioc.Path, ex);
throw new FileNotFoundException(ExceptionUtil.GetErrorMessage(ex), ioc.Path, ex);
}
if (ex.Status == WebExceptionStatus.TrustFailure)
{

View File

@@ -186,8 +186,11 @@ namespace keepass2android.Io
Kp2aLog.Log("couldn't open from remote " + ioc.Path);
#endif
Kp2aLog.Log(ex.ToString());
_cacheSupervisor.CouldntOpenFromRemote(ioc, ex);
if (TriggerWarningWhenFallingBackToCache)
{
_cacheSupervisor.CouldntOpenFromRemote(ioc, ex);
}
return File.OpenRead(cachedFilePath);
}
}
@@ -327,7 +330,10 @@ namespace keepass2android.Io
Kp2aLog.Log("couldn't save to remote " + ioc.Path);
Kp2aLog.Log(e.ToString());
//notify the supervisor so it might display a warning or schedule a retry
_cacheSupervisor.CouldntSaveToRemote(ioc, e);
if (TriggerWarningWhenFallingBackToCache)
{
_cacheSupervisor.CouldntSaveToRemote(ioc, e); }
return false;
}
}
@@ -632,7 +638,9 @@ namespace keepass2android.Io
set { _cachedStorage.IsOffline = value; }
}
public void OnRequestPermissionsResult(IFileStorageSetupActivity fileStorageSetupActivity, int requestCode,
public bool TriggerWarningWhenFallingBackToCache { get; set; }
public void OnRequestPermissionsResult(IFileStorageSetupActivity fileStorageSetupActivity, int requestCode,
string[] permissions, Permission[] grantResults)
{
_cachedStorage.OnRequestPermissionsResult(fileStorageSetupActivity, requestCode, permissions, grantResults);

View File

@@ -13,6 +13,7 @@ using Keepass2android.Javafilestorage;
#endif
using Exception = System.Exception;
using FileNotFoundException = Java.IO.FileNotFoundException;
using KeePass.Util;
namespace keepass2android.Io
{
@@ -42,7 +43,7 @@ namespace keepass2android.Io
}
catch (FileNotFoundException e)
{
throw new System.IO.FileNotFoundException(e.Message, e);
throw new System.IO.FileNotFoundException(ExceptionUtil.GetErrorMessage(e), e);
}
catch (Java.Lang.Exception e)
{
@@ -110,6 +111,11 @@ namespace keepass2android.Io
}
Java.Lang.Exception exception = e as Java.Lang.Exception;
if ((exception is Java.Lang.InterruptedException) || (exception is Java.IO.InterruptedIOException))
{
throw new Java.Lang.InterruptedException(exception.Message);
}
if (exception != null)
{
var ex = new Exception(exception.LocalizedMessage ??
@@ -195,7 +201,7 @@ namespace keepass2android.Io
}
catch (FileNotFoundException e)
{
throw new System.IO.FileNotFoundException(e.Message, e);
throw new System.IO.FileNotFoundException(ExceptionUtil.GetErrorMessage(e), e);
}
catch (Java.Lang.Exception e)
{
@@ -214,7 +220,7 @@ namespace keepass2android.Io
}
catch (FileNotFoundException e)
{
throw new System.IO.FileNotFoundException(e.Message, e);
throw new System.IO.FileNotFoundException(ExceptionUtil.GetErrorMessage(e), e);
}
catch (Java.Lang.Exception e)
{
@@ -244,7 +250,7 @@ namespace keepass2android.Io
}
catch (FileNotFoundException e)
{
throw new System.IO.FileNotFoundException(e.Message, e);
throw new System.IO.FileNotFoundException(ExceptionUtil.GetErrorMessage(e), e);
}
catch (Java.Lang.Exception e)
{

View File

@@ -8,6 +8,7 @@ using Android.Content;
using Android.OS;
using FluentFTP;
using FluentFTP.Exceptions;
using KeePass.Util;
using KeePassLib;
using KeePassLib.Serialization;
using KeePassLib.Utility;
@@ -127,7 +128,7 @@ namespace keepass2android.Io
var ftpEx = (FtpCommandException) exception;
if (ftpEx.CompletionCode == "550")
throw new FileNotFoundException(exception.Message, exception);
throw new FileNotFoundException(ExceptionUtil.GetErrorMessage(exception), exception);
}
return exception;

View File

@@ -11,7 +11,8 @@ namespace keepass2android.Io
public interface IOfflineSwitchable
{
bool IsOffline { get; set; }
}
bool TriggerWarningWhenFallingBackToCache { get; set; }
}
/// <summary>
/// Encapsulates another IFileStorage. Allows to switch to offline mode by throwing
@@ -21,8 +22,9 @@ namespace keepass2android.Io
{
private readonly IFileStorage _baseStorage;
public bool IsOffline { get; set; }
public bool TriggerWarningWhenFallingBackToCache { get; set; }
public OfflineSwitchableFileStorage(IFileStorage baseStorage)
public OfflineSwitchableFileStorage(IFileStorage baseStorage)
{
_baseStorage = baseStorage;
}

View File

@@ -3,6 +3,7 @@ using System.Reflection;
using System.Text;
using Android.Content;
using Android.Util;
using KeePass.Util;
using keepass2android.Io.ItemLocation;
using KeePassLib.Serialization;
using KeePassLib.Utility;
@@ -522,10 +523,10 @@ namespace keepass2android.Io
{
if (e.IsMatch(GraphErrorCode.ItemNotFound.ToString()))
return new FileNotFoundException(e.Message);
return new FileNotFoundException(ExceptionUtil.GetErrorMessage(e));
if (e.Message.Contains("\n\n404 : ")
) //hacky solution to check for not found. errorCode was null in my tests so I had to find a workaround.
return new FileNotFoundException(e.Message);
return new FileNotFoundException(ExceptionUtil.GetErrorMessage(e));
return e;
}

View File

@@ -16,20 +16,32 @@ namespace keepass2android.Io
/// </summary>
public class OneDriveFileStorage: IFileStorage
{
public IEnumerable<string> SupportedProtocols
public OneDriveFileStorage(IKp2aApp app)
{
_app = app;
}
private readonly IKp2aApp _app;
public IEnumerable<string> SupportedProtocols
{
get
{
yield return "skydrive";
yield return "onedrive";
}
}
}
private Exception GetDeprecatedMessage()
string GetDeprecatedMessage()
{
return
"You have opened your file through a deprecated Microsoft API. Please select Change database, Open Database and then select OneDrive again.";
}
private Exception GetDeprecatedException()
{
return new Exception(
"You have opened your file through a deprecated Microsoft API. Please select Change database, Open Database and then select One Drive again.");
GetDeprecatedMessage());
}
public bool UserShouldBackup
@@ -39,133 +51,132 @@ namespace keepass2android.Io
public void Delete(IOConnectionInfo ioc)
{
throw GetDeprecatedMessage();
throw GetDeprecatedException();
}
public bool CheckForFileChangeFast(IOConnectionInfo ioc, string previousFileVersion)
{
throw GetDeprecatedMessage();
throw GetDeprecatedException();
}
public string GetCurrentFileVersionFast(IOConnectionInfo ioc)
{
throw GetDeprecatedMessage();
throw GetDeprecatedException();
}
public Stream OpenFileForRead(IOConnectionInfo ioc)
{
throw GetDeprecatedMessage();
throw GetDeprecatedException();
}
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
{
throw GetDeprecatedMessage();
throw GetDeprecatedException();
}
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
{
throw GetDeprecatedMessage();
throw GetDeprecatedException();
}
public string GetFileExtension(IOConnectionInfo ioc)
{
throw GetDeprecatedMessage();
throw GetDeprecatedException();
}
public bool RequiresCredentials(IOConnectionInfo ioc)
{
throw GetDeprecatedMessage();
return false;
}
public void CreateDirectory(IOConnectionInfo ioc, string newDirName)
{
throw GetDeprecatedMessage();
throw GetDeprecatedException();
}
public IEnumerable<FileDescription> ListContents(IOConnectionInfo ioc)
{
throw GetDeprecatedMessage();
throw GetDeprecatedException();
}
public FileDescription GetFileDescription(IOConnectionInfo ioc)
{
throw GetDeprecatedMessage();
throw GetDeprecatedException();
}
public bool RequiresSetup(IOConnectionInfo ioConnection)
{
throw GetDeprecatedMessage();
return false;
}
public string IocToPath(IOConnectionInfo ioc)
{
throw GetDeprecatedMessage();
throw GetDeprecatedException();
}
public void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId)
{
throw GetDeprecatedMessage();
}
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode,
bool alwaysReturnSuccess)
{
throw GetDeprecatedMessage();
_app.ShowMessage(activity.Activity, GetDeprecatedMessage(), MessageSeverity.Error);
}
public void PrepareFileUsage(Context ctx, IOConnectionInfo ioc)
{
throw GetDeprecatedMessage();
}
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
{
throw GetDeprecatedMessage();
}
public void OnResume(IFileStorageSetupActivity activity)
{
throw GetDeprecatedMessage();
}
public void OnStart(IFileStorageSetupActivity activity)
{
throw GetDeprecatedMessage();
}
public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data)
{
throw GetDeprecatedMessage();
}
public string GetDisplayName(IOConnectionInfo ioc)
{
throw GetDeprecatedMessage();
return "File using deprecated Microsoft API. Please update.";
}
public string CreateFilePath(string parent, string newFilename)
{
throw GetDeprecatedMessage();
throw GetDeprecatedException();
}
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
{
throw GetDeprecatedMessage();
throw GetDeprecatedException();
}
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
{
throw GetDeprecatedMessage();
throw GetDeprecatedException();
}
public bool IsPermanentLocation(IOConnectionInfo ioc)
{
throw GetDeprecatedMessage();
throw GetDeprecatedException();
}
public bool IsReadOnly(IOConnectionInfo ioc, OptionalOut<UiStringKey> reason = null)
{
throw GetDeprecatedMessage();
throw GetDeprecatedException();
}
}
}

View File

@@ -0,0 +1,239 @@
using Android.App;
using Android.Content;
using Android.OS;
using System.Threading.Tasks;
using Thread = Java.Lang.Thread;
namespace keepass2android;
/// <summary>
/// Allows to run tasks in the background. The UI is not blocked by the task. Tasks continue to run in the BackgroundSyncService if the app goes to background while tasks are active.
/// </summary>
public class OperationRunner
{
//singleton instance
private static OperationRunner _instance = null;
public static OperationRunner Instance
{
get
{
if (_instance == null)
{
_instance = new OperationRunner();
}
return _instance;
}
}
void Initialize(IKp2aApp app)
{
}
public struct OperationWithMetadata
{
public OperationWithMetadata()
{
Operation = null;
}
public OperationWithFinishHandler Operation { get; set; }
public bool RunBlocking { get; set; } = false;
}
public ProgressUiAsStatusLoggerAdapter StatusLogger => _statusLogger;
private OperationRunner()
{
//private constructor
}
private readonly Queue<OperationWithMetadata> _taskQueue = new Queue<OperationWithMetadata>();
private readonly object _taskQueueLock = new object();
private Java.Lang.Thread? _thread = null;
private OperationWithMetadata? _currentlyRunningTask = null;
private ProgressUiAsStatusLoggerAdapter _statusLogger = null;
private IProgressDialog _progressDialog;
private IKp2aApp _app;
public void Run(IKp2aApp app, OperationWithFinishHandler operation, bool runBlocking = false)
{
lock (Instance._taskQueueLock)
{
_taskQueue.Enqueue(new OperationWithMetadata(){ Operation = operation, RunBlocking = runBlocking});
operation.SetStatusLogger(_statusLogger);
// Start thread to run the task (unless it's already running)
if (_thread == null)
{
_statusLogger.StartLogging("", false);
_thread = new Java.Lang.Thread(() =>
{
while (true)
{
lock (_taskQueueLock)
{
if (!_taskQueue.Any())
{
_thread = null;
_statusLogger.EndLogging();
break;
}
else
{
_currentlyRunningTask = _taskQueue.Dequeue();
}
}
if (_currentlyRunningTask.Value.RunBlocking)
{
app.UiThreadHandler.Post(
() =>
{
TrySetupProgressDialog();
});
}
var originalFinishedHandler = _currentlyRunningTask.Value.Operation.operationFinishedHandler;
_currentlyRunningTask.Value.Operation.operationFinishedHandler = new ActionOnOperationFinished(app, (
(success, message, context) =>
{
if (_currentlyRunningTask?.RunBlocking == true)
{
_app.UiThreadHandler.Post(() =>
{
_progressDialog?.Dismiss();
}
);
}
_currentlyRunningTask = null;
}), originalFinishedHandler);
_currentlyRunningTask.Value.Operation.Run();
while (_currentlyRunningTask != null)
{
try
{
Thread.Sleep(100);
}
catch (Exception e)
{
Kp2aLog.Log("Thread interrupted.");
}
}
}
});
_thread.Start();
}
}
}
private bool TrySetupProgressDialog()
{
string currentMessage = "Initializing...";
string currentSubmessage = "";
if (_statusLogger != null)
{
currentMessage = _statusLogger.LastMessage;
currentSubmessage = _statusLogger.LastSubMessage;
}
if (_progressDialog != null)
{
var pd = _progressDialog;
_app.UiThreadHandler.Post(() =>
{
pd.Dismiss();
});
}
// Show process dialog
_progressDialog = _app.CreateProgressDialog(_app.ActiveContext);
if (_progressDialog == null)
{
return false;
}
var progressUi = new ProgressDialogUi(_app, _app.UiThreadHandler, _progressDialog);
_statusLogger.SetNewProgressUi(progressUi);
_statusLogger.StartLogging("", false);
_statusLogger.UpdateMessage(currentMessage);
_statusLogger.UpdateSubMessage(currentSubmessage);
return true;
}
public void SetNewActiveContext(IKp2aApp app)
{
_app = app;
Context? context = app.ActiveContext;
bool isAppContext = context == null || (context.ApplicationContext == context);
lock (_taskQueueLock)
{
if (isAppContext && _thread != null)
{
//this will register the service as new active context (see BackgroundSyncService.OnStartCommand())
app.StartBackgroundSyncService();
return;
}
if (_currentlyRunningTask?.RunBlocking == true && (context is Activity { IsFinishing: false, IsDestroyed:false}))
{
app.UiThreadHandler.Post(() =>
{
TrySetupProgressDialog();
});
}
else
{
var progressUi = (context as IProgressUiProvider)?.ProgressUi;
if (_statusLogger == null)
{
_statusLogger = new ProgressUiAsStatusLoggerAdapter(progressUi, app);
}
else
{
_statusLogger.SetNewProgressUi(progressUi);
}
}
foreach (var task in _taskQueue.Concat(_currentlyRunningTask == null ?
new List<OperationWithMetadata>() : [_currentlyRunningTask.Value])
)
{
task.Operation.SetStatusLogger(_statusLogger);
}
}
}
public void CancelAll()
{
lock (_taskQueueLock)
{
if (_thread != null)
{
_thread.Interrupt();
_thread = null;
_statusLogger?.EndLogging();
}
_taskQueue.Clear();
_currentlyRunningTask = null;
}
}
}

View File

@@ -22,116 +22,152 @@ using KeePassLib.Interfaces;
namespace keepass2android
{
/// <summary>
/// StatusLogger implementation which shows the progress in a progress dialog
/// </summary>
public class ProgressDialogStatusLogger: IStatusLogger {
public interface IKp2aStatusLogger : IStatusLogger
{
void UpdateMessage(UiStringKey stringKey);
string LastMessage { get; }
string LastSubMessage { get; }
}
public interface IProgressUi
{
void Show();
void Hide();
void UpdateMessage(String message);
void UpdateSubMessage(String submessage);
}
public interface IProgressUiProvider
{
IProgressUi? ProgressUi { get; }
}
public class Kp2aNullStatusLogger : IKp2aStatusLogger
{
public void StartLogging(string strOperation, bool bWriteOperationToLog)
{
}
public void EndLogging()
{
}
public bool SetProgress(uint uPercent)
{
return true;
}
public bool SetText(string strNewText, LogStatusType lsType)
{
return true;
}
private string _lastMessage;
private string _lastSubMessage;
public void UpdateMessage(string message)
{
_lastMessage = message;
}
public void UpdateSubMessage(string submessage)
{
_lastSubMessage = submessage;
}
public bool ContinueWork()
{
return true;
}
public void UpdateMessage(UiStringKey stringKey)
{
}
public string LastMessage { get { return _lastMessage; } }
public string LastSubMessage { get { return _lastSubMessage; } }
}
/// <summary>
/// StatusLogger implementation which shows the progress in a progress dialog
/// </summary>
public class ProgressDialogUi: IProgressUi
{
private readonly IProgressDialog _progressDialog;
readonly IKp2aApp _app;
private readonly Handler _handler;
private string _message = "";
private string _submessage;
private readonly IKp2aApp _app;
public String SubMessage => _submessage;
public String Message => _message;
public String LastSubMessage => _submessage;
public String LastMessage => _message;
public ProgressDialogStatusLogger() {
}
public ProgressDialogStatusLogger(IKp2aApp app, Handler handler, IProgressDialog pd) {
_app = app;
public ProgressDialogUi(IKp2aApp app, Handler handler, IProgressDialog pd)
{
_app = app;
_progressDialog = pd;
_handler = handler;
}
public void UpdateMessage(UiStringKey stringKey) {
if (_app != null)
UpdateMessage(_app.GetResourceString(stringKey));
}
public void UpdateMessage (String message)
{
Kp2aLog.Log("status message: " + message);
public void UpdateSubMessage(String submessage)
{
Kp2aLog.Log("status submessage: " + submessage);
_submessage = submessage;
if (_app != null && _progressDialog != null && _handler != null)
{
_handler.Post(() =>
{
if (!String.IsNullOrEmpty(submessage))
{
_progressDialog.SetMessage(_message + " (" + submessage + ")");
}
else
{
_progressDialog.SetMessage(_message);
}
}
);
}
}
public void Show()
{
_handler.Post(() =>
{
_progressDialog?.Show();
});
}
public void Hide()
{
_handler.Post(() =>
{
_progressDialog?.Dismiss();
});
}
public void UpdateMessage(string message)
{
Kp2aLog.Log("status message: " + message);
_message = message;
if ( _app!= null && _progressDialog != null && _handler != null ) {
_handler.Post(() => {_progressDialog.SetMessage(message); } );
}
}
if (_app != null && _progressDialog != null && _handler != null)
{
_handler.Post(() =>
{
_progressDialog.SetMessage(message);
});
}
}
public void UpdateSubMessage(String submessage)
{
Kp2aLog.Log("status submessage: " + submessage);
_submessage = submessage;
if (_app != null && _progressDialog != null && _handler != null)
{
_handler.Post(() =>
{
if (!String.IsNullOrEmpty(submessage))
{
_progressDialog.SetMessage(_message + " (" + submessage + ")");
}
else
{
_progressDialog.SetMessage(_message);
}
}
);
}
}
}
#region IStatusLogger implementation
public void StartLogging (string strOperation, bool bWriteOperationToLog)
{
}
public void EndLogging ()
{
}
public bool SetProgress (uint uPercent)
{
return true;
}
public bool SetText (string strNewText, LogStatusType lsType)
{
if (strNewText.StartsWith("KP2AKEY_"))
{
UiStringKey key;
if (Enum.TryParse(strNewText.Substring("KP2AKEY_".Length), true, out key))
{
UpdateMessage(_app.GetResourceString(key), lsType);
return true;
}
}
UpdateMessage(strNewText, lsType);
return true;
}
private void UpdateMessage(string message, LogStatusType lsType)
{
if (lsType == LogStatusType.AdditionalInfo)
{
UpdateSubMessage(message);
}
else
{
UpdateMessage(message);
}
}
public bool ContinueWork ()
{
return true;
}
#endregion
}
}

View File

@@ -1,179 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using Android.App;
using Android.Content;
using Android.OS;
using Java.Lang;
namespace keepass2android
{
/// <summary>
/// Class to run a task while a progress dialog is shown
/// </summary>
public class ProgressTask
{
//for handling Activity recreation situations, we need access to the currently active task. It must hold that there is no more than one active task.
private static ProgressTask _currentTask = null;
public static void SetNewActiveActivity(Activity activeActivity)
{
if (_currentTask != null)
{
_currentTask.ActiveActivity = activeActivity;
}
}
public static void RemoveActiveActivity(Activity activity)
{
if ((_currentTask != null) && (_currentTask._activeActivity == activity))
_currentTask.ActiveActivity = null;
}
public Activity ActiveActivity
{
get { return _activeActivity; }
private set
{
if (_activeActivity != null && _activeActivity != _previouslyActiveActivity)
{
_previouslyActiveActivity = _activeActivity;
}
_activeActivity = value;
if (_task != null)
_task.ActiveActivity = _activeActivity;
if (_activeActivity != null)
{
SetupProgressDialog(_app);
_progressDialog.Show();
}
}
}
public Activity PreviouslyActiveActivity
{
get { return _previouslyActiveActivity; }
}
private readonly Handler _handler;
private readonly RunnableOnFinish _task;
private IProgressDialog _progressDialog;
private readonly IKp2aApp _app;
private Java.Lang.Thread _thread;
private Activity _activeActivity, _previouslyActiveActivity;
private ProgressDialogStatusLogger _progressDialogStatusLogger;
public ProgressTask(IKp2aApp app, Activity activity, RunnableOnFinish task)
{
_activeActivity = activity;
_task = task;
_handler = app.UiThreadHandler;
_app = app;
SetupProgressDialog(app);
// Set code to run when this is finished
_task.OnFinishToRun = new AfterTask(activity, task.OnFinishToRun, _handler, this);
_task.SetStatusLogger(_progressDialogStatusLogger);
}
private void SetupProgressDialog(IKp2aApp app)
{
string currentMessage = "Initializing...";
string currentSubmessage = "";
if (_progressDialogStatusLogger != null)
{
currentMessage = _progressDialogStatusLogger.Message;
currentSubmessage = _progressDialogStatusLogger.SubMessage;
}
if (_progressDialog != null)
{
var pd = _progressDialog;
app.UiThreadHandler.Post(() =>
{
pd.Dismiss();
});
}
// Show process dialog
_progressDialog = app.CreateProgressDialog(_activeActivity);
_progressDialog.SetTitle(_app.GetResourceString(UiStringKey.progress_title));
_progressDialogStatusLogger = new ProgressDialogStatusLogger(_app, _handler, _progressDialog);
_progressDialogStatusLogger.UpdateMessage(currentMessage);
_progressDialogStatusLogger.UpdateSubMessage(currentSubmessage);
}
public void Run(bool allowOverwriteCurrentTask = false)
{
if ((!allowOverwriteCurrentTask) && (_currentTask != null))
throw new System.Exception("Cannot start another ProgressTask while ProgressTask is already running! " + _task.GetType().Name + "/" + _currentTask._task.GetType().Name);
_currentTask = this;
// Show process dialog
_progressDialog.Show();
// Start Thread to Run task
_thread = new Java.Lang.Thread(_task.Run);
_thread.Start();
}
public void JoinWorkerThread()
{
_thread.Join();
}
private class AfterTask : OnFinish {
readonly ProgressTask _progressTask;
public AfterTask (Activity activity, OnFinish finish, Handler handler, ProgressTask pt): base(activity, finish, handler)
{
_progressTask = pt;
}
public override void Run() {
base.Run();
if (Handler != null) //can be null in tests
{
// Remove the progress dialog
Handler.Post(delegate
{
_progressTask._progressDialog.Dismiss();
});
}
else
{
_progressTask._progressDialog.Dismiss();
}
_currentTask = null;
}
}
}
}

View File

@@ -0,0 +1,94 @@
using KeePassLib.Interfaces;
namespace keepass2android;
public class ProgressUiAsStatusLoggerAdapter : IKp2aStatusLogger
{
private IProgressUi? _progressUi;
private readonly IKp2aApp _app;
private string _lastMessage = "";
private string _lastSubMessage = "";
private bool _isVisible = false;
public ProgressUiAsStatusLoggerAdapter(IProgressUi progressUi, IKp2aApp app)
{
_progressUi = progressUi;
_app = app;
}
public void SetNewProgressUi(IProgressUi progressUi)
{
_progressUi?.Hide();
_progressUi = progressUi;
if (_isVisible)
{
progressUi?.Show();
progressUi?.UpdateMessage(_lastMessage);
progressUi?.UpdateSubMessage(_lastSubMessage);
}
else
{
progressUi?.Hide();
}
}
public void StartLogging(string strOperation, bool bWriteOperationToLog)
{
_progressUi?.Show();
_isVisible = true;
}
public void EndLogging()
{
_progressUi?.Hide();
_isVisible = false;
}
public bool SetProgress(uint uPercent)
{
return true;
}
public bool SetText(string strNewText, LogStatusType lsType)
{
if (strNewText.StartsWith("KP2AKEY_"))
{
UiStringKey key;
if (Enum.TryParse(strNewText.Substring("KP2AKEY_".Length), true, out key))
{
UpdateMessage(_app.GetResourceString(key));
return true;
}
}
UpdateMessage(strNewText);
return true;
}
public void UpdateMessage(string message)
{
_progressUi?.UpdateMessage(message);
_lastMessage = message;
}
public void UpdateSubMessage(string submessage)
{
_progressUi?.UpdateSubMessage(submessage);
_lastSubMessage = submessage;
}
public bool ContinueWork()
{
return !Java.Lang.Thread.Interrupted();
}
public void UpdateMessage(UiStringKey stringKey)
{
if (_app != null)
UpdateMessage(_app.GetResourceString(stringKey));
}
public string LastMessage { get { return _lastMessage; } }
public string LastSubMessage { get { return _lastSubMessage; } }
}

View File

@@ -4,6 +4,7 @@ using Android.Content;
using Android.OS;
using Android.Widget;
using Java.Net;
using KeePass.Util;
using KeePassLib.Serialization;
using keepass2android.Io;
@@ -94,15 +95,12 @@ namespace keepass2android
}
if ((resultCode == Result.Canceled) && (data != null) && (data.HasExtra("EXTRA_ERROR_MESSAGE")))
{
ShowToast(data.GetStringExtra("EXTRA_ERROR_MESSAGE"));
ShowErrorToast(data.GetStringExtra("EXTRA_ERROR_MESSAGE"));
}
if (resultCode == Result.Ok)
{
Kp2aLog.Log("FileSelection returned "+data.DataString);
//TODO: don't try to extract filename if content URI
string filename = IntentToFilename(data);
Kp2aLog.Log("FileSelection returned filename " + filename);
if (filename != null)
{
if (filename.StartsWith("file://"))
@@ -150,7 +148,7 @@ namespace keepass2android
protected abstract void StartFileChooser(string path, int requestCode, bool isForSave);
protected abstract void ShowToast(string text);
protected abstract void ShowErrorToast(string text);
protected abstract void ShowInvalidSchemeMessage(string dataString);
@@ -208,7 +206,7 @@ namespace keepass2android
{
return () =>
{
ShowToast(_app.GetResourceString(UiStringKey.ErrorOcurred) + " " + e.Message);
ShowErrorToast(_app.GetResourceString(UiStringKey.ErrorOcurred) + " " + ExceptionUtil.GetErrorMessage(e));
ReturnCancel();
};
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace KeePass.Util
{
public class ExceptionUtil
{
public static string GetErrorMessage(Exception e)
{
string errorMessage = e.Message;
if (e is Java.Lang.Exception javaException)
{
errorMessage = javaException.Message ?? errorMessage;
}
return errorMessage;
}
}
}

View File

@@ -5,6 +5,7 @@ using System.Security.Cryptography;
using System.Text;
using Android.App;
using Android.Content;
using KeePass.Util;
using KeePassLib.Cryptography;
using KeePassLib.Serialization;
using KeePassLib.Utility;
@@ -12,16 +13,15 @@ using keepass2android.Io;
namespace keepass2android
{
public class CheckDatabaseForChanges: RunnableOnFinish
public class CheckDatabaseForChanges: OperationWithFinishHandler
{
private readonly Context _context;
private readonly IKp2aApp _app;
public CheckDatabaseForChanges(Activity context, IKp2aApp app, OnFinish finish)
: base(context, finish)
public CheckDatabaseForChanges(IKp2aApp app, OnOperationFinishedHandler operationFinishedHandler)
: base(app, operationFinishedHandler)
{
_context = context;
_app = app;
}
@@ -65,7 +65,7 @@ namespace keepass2android
}
catch (Exception e)
{
Finish(false, e.Message);
Finish(false, ExceptionUtil.GetErrorMessage(e));
}
}

View File

@@ -85,7 +85,7 @@ namespace keepass2android
/// <summary>
/// Do not call this method directly. Call App.Kp2a.LoadDatabase instead.
/// </summary>
public void LoadData(IKp2aApp app, IOConnectionInfo iocInfo, MemoryStream databaseData, CompositeKey compositeKey, ProgressDialogStatusLogger status, IDatabaseFormat databaseFormat)
public void LoadData(IKp2aApp app, IOConnectionInfo iocInfo, MemoryStream databaseData, CompositeKey compositeKey, IKp2aStatusLogger status, IDatabaseFormat databaseFormat)
{
PwDatabase pwDatabase = new PwDatabase();
@@ -149,7 +149,7 @@ namespace keepass2android
get { return GetFingerprintModePrefKey(Ioc); }
}
protected virtual void PopulateDatabaseFromStream(PwDatabase pwDatabase, Stream s, IOConnectionInfo iocInfo, CompositeKey compositeKey, ProgressDialogStatusLogger status, IDatabaseFormat databaseFormat)
protected virtual void PopulateDatabaseFromStream(PwDatabase pwDatabase, Stream s, IOConnectionInfo iocInfo, CompositeKey compositeKey, IKp2aStatusLogger status, IDatabaseFormat databaseFormat)
{
IFileStorage fileStorage = _app.GetFileStorage(iocInfo);
var filename = fileStorage.GetFilenameWithoutPathAndExt(iocInfo);
@@ -194,9 +194,9 @@ namespace keepass2android
}
public void SaveData() {
public void SaveData(IFileStorage fileStorage) {
using (IWriteTransaction trans = _app.GetFileStorage(Ioc).OpenWriteTransaction(Ioc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions)))
using (IWriteTransaction trans = fileStorage.OpenWriteTransaction(Ioc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions)))
{
DatabaseFormat.Save(KpDatabase, trans.OpenFile());

View File

@@ -10,6 +10,7 @@ using Com.Keepassdroid.Database.Exception;
#endif
using Com.Keepassdroid.Database.Save;
using Java.Util;
using KeePass.Util;
using KeePassLib;
using KeePassLib.Cryptography;
using KeePassLib.Cryptography.Cipher;
@@ -82,15 +83,14 @@ namespace keepass2android
catch (Java.IO.FileNotFoundException e)
{
throw new FileNotFoundException(
e.Message, e);
ExceptionUtil.GetErrorMessage(e), e);
}
catch (Java.Lang.Exception e)
{
if (e.Message == "Invalid key!")
throw new InvalidCompositeKeyException();
throw new Exception(e.LocalizedMessage ??
e.Message ??
e.GetType().Name, e);
throw new Exception(ExceptionUtil.GetErrorMessage(e) ??
e.GetType().Name, e);
}
HashOfLastStream = hashingStream.Hash;
@@ -396,8 +396,6 @@ namespace keepass2android
{
PwGroupV3 toGroup = new PwGroupV3();
toGroup.Name = fromGroup.Name;
//todo remove
Android.Util.Log.Debug("KP2A", "save kdb: group " + fromGroup.Name);
toGroup.TCreation = new PwDate(ConvertTime(fromGroup.CreationTime));
toGroup.TLastAccess= new PwDate(ConvertTime(fromGroup.LastAccessTime));

View File

@@ -4,120 +4,159 @@ using System.IO;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using KeePassLib.Serialization;
using keepass2android.Io;
using KeePass.Util;
using Group.Pals.Android.Lib.UI.Filechooser.Utils;
using KeePassLib;
namespace keepass2android
{
public class SynchronizeCachedDatabase: RunnableOnFinish
public class SynchronizeCachedDatabase: OperationWithFinishHandler
{
private readonly Activity _context;
private readonly IKp2aApp _app;
private SaveDb _saveDb;
private IDatabaseModificationWatcher _modificationWatcher;
private readonly Database _database;
public SynchronizeCachedDatabase(Activity context, IKp2aApp app, OnFinish finish)
: base(context, finish)
{
_context = context;
_app = app;
}
public SynchronizeCachedDatabase(IKp2aApp app, Database database, OnOperationFinishedHandler operationFinishedHandler, IDatabaseModificationWatcher modificationWatcher)
: base(app, operationFinishedHandler)
{
_app = app;
_database = database;
_modificationWatcher = modificationWatcher;
}
public override void Run()
{
try
{
IOConnectionInfo ioc = _app.CurrentDb.Ioc;
IFileStorage fileStorage = _app.GetFileStorage(ioc);
if (!(fileStorage is CachingFileStorage))
{
throw new Exception("Cannot sync a non-cached database!");
}
StatusLogger.UpdateMessage(UiStringKey.SynchronizingCachedDatabase);
CachingFileStorage cachingFileStorage = (CachingFileStorage) fileStorage;
try
{
IOConnectionInfo ioc = _database.Ioc;
IFileStorage fileStorage = _app.GetFileStorage(ioc);
if (!(fileStorage is CachingFileStorage))
{
throw new Exception("Cannot sync a non-cached database!");
}
//download file from remote location and calculate hash:
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.DownloadingRemoteFile));
string hash;
MemoryStream remoteData;
try
{
remoteData = cachingFileStorage.GetRemoteDataAndHash(ioc, out hash);
Kp2aLog.Log("Checking for file change. Current hash = " + hash);
}
catch (FileNotFoundException)
{
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.RestoringRemoteFile));
cachingFileStorage.UpdateRemoteFile(ioc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions));
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
StatusLogger.UpdateMessage(UiStringKey.SynchronizingCachedDatabase);
CachingFileStorage cachingFileStorage = (CachingFileStorage)fileStorage;
//download file from remote location and calculate hash:
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.DownloadingRemoteFile));
string hash;
MemoryStream remoteData;
try
{
remoteData = cachingFileStorage.GetRemoteDataAndHash(ioc, out hash);
Kp2aLog.Log("Checking for file change. Current hash = " + hash);
}
catch (FileNotFoundException)
{
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.RestoringRemoteFile));
cachingFileStorage.UpdateRemoteFile(ioc,
_app.GetBooleanPreference(PreferenceKey.UseFileTransactions));
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
Kp2aLog.Log("Checking for file change: file not found");
return;
}
return;
}
//check if remote file was modified:
//check if remote file was modified:
var baseVersionHash = cachingFileStorage.GetBaseVersionHash(ioc);
Kp2aLog.Log("Checking for file change. baseVersionHash = " + baseVersionHash);
if (baseVersionHash != hash)
{
//remote file is modified
if (cachingFileStorage.HasLocalChanges(ioc))
{
//conflict! need to merge
_saveDb = new SaveDb(_context, _app, new ActionOnFinish(ActiveActivity, (success, result, activity) =>
{
if (!success)
{
Finish(false, result);
}
else
{
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
}
_saveDb = null;
}), _app.CurrentDb, false, remoteData);
_saveDb.Run();
if (baseVersionHash != hash)
{
//remote file is modified
if (cachingFileStorage.HasLocalChanges(ioc))
{
//conflict! need to merge
var _saveDb = new SaveDb(_app, new ActionOnOperationFinished(_app,
(success, result, activity) =>
{
if (!success)
{
Finish(false, result);
}
else
{
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
}
}), _database, false, remoteData, _modificationWatcher);
_saveDb.SetStatusLogger(StatusLogger);
_saveDb.DoNotSetStatusLoggerMessage = true; //Keep "sync db" as main message
_saveDb.SyncInBackground = false;
_saveDb.Run();
_app.CurrentDb.UpdateGlobals();
_database.UpdateGlobals();
_app.MarkAllGroupsAsDirty();
}
else
{
//only the remote file was modified -> reload database.
//note: it's best to lock the database and do a complete reload here (also better for UI consistency in case something goes wrong etc.)
_app.TriggerReload(_context, (bool result) => Finish(result));
}
}
else
{
//remote file is unmodified
if (cachingFileStorage.HasLocalChanges(ioc))
{
//but we have local changes -> upload:
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.UploadingFile));
cachingFileStorage.UpdateRemoteFile(ioc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions));
StatusLogger.UpdateSubMessage("");
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
}
else
{
//files are in sync: just set the result
Finish(true, _app.GetResourceString(UiStringKey.FilesInSync));
}
}
}
_app.MarkAllGroupsAsDirty();
}
else
{
//only the remote file was modified -> reload database.
var onFinished = new ActionOnOperationFinished(_app, (success, result, activity) =>
{
if (!success)
{
Finish(false, result);
}
else
{
new Handler(Looper.MainLooper).Post(() =>
{
_database.UpdateGlobals();
_app.MarkAllGroupsAsDirty();
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
});
}
});
var _loadDb = new LoadDb(_app, ioc, Task.FromResult(remoteData),
_database.KpDatabase.MasterKey, null, onFinished, true, false, _modificationWatcher);
_loadDb.SetStatusLogger(StatusLogger);
_loadDb.DoNotSetStatusLoggerMessage = true; //Keep "sync db" as main message
_loadDb.Run();
}
}
else
{
//remote file is unmodified
if (cachingFileStorage.HasLocalChanges(ioc))
{
//but we have local changes -> upload:
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.UploadingFile));
cachingFileStorage.UpdateRemoteFile(ioc,
_app.GetBooleanPreference(PreferenceKey.UseFileTransactions));
StatusLogger.UpdateSubMessage("");
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
}
else
{
//files are in sync: just set the result
Finish(true, _app.GetResourceString(UiStringKey.FilesInSync));
}
}
}
catch (Java.Lang.InterruptedException e)
{
Kp2aLog.LogUnexpectedError(e);
//no Finish()
}
catch (Java.IO.InterruptedIOException e)
{
Kp2aLog.LogUnexpectedError(e);
//no Finish()
}
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
Finish(false, e.Message);
Finish(false, ExceptionUtil.GetErrorMessage(e));
}
}
public void JoinWorkerThread()
{
if (_saveDb != null)
_saveDb.JoinWorkerThread();
}
}
}

View File

@@ -1,57 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using Android.App;
using Android.OS;
namespace keepass2android
{
public class ActionOnFinish: OnFinish
{
public delegate void ActionToPerformOnFinsh(bool success, String message, Activity activeActivity);
readonly ActionToPerformOnFinsh _actionToPerform;
public ActionOnFinish(Activity activity, ActionToPerformOnFinsh actionToPerform) : base(activity, null, null)
{
_actionToPerform = actionToPerform;
}
public ActionOnFinish(Activity activity, ActionToPerformOnFinsh actionToPerform, OnFinish finish) : base(activity, finish)
{
_actionToPerform = actionToPerform;
}
//if set to true, the previously active active will be passed to ActionToPerformOnFinish instead null if no activity is on foreground
public bool AllowInactiveActivity { get; set; }
public override void Run()
{
if (Message == null)
Message = "";
if (Handler != null)
{
Handler.Post(() => {_actionToPerform(Success, Message, ActiveActivity);});
}
else
_actionToPerform(Success, Message, AllowInactiveActivity ? (ActiveActivity ?? PreviouslyActiveActivity) : ActiveActivity);
base.Run();
}
}
}

View File

@@ -0,0 +1,92 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using Android.App;
using Android.Content;
using Android.OS;
using keepass2android;
namespace keepass2android
{
public class ActionOnOperationFinished: OnOperationFinishedHandler
{
public delegate void ActionToPerformOnFinsh(bool success, String message, Context activeContext);
readonly ActionToPerformOnFinsh _actionToPerform;
public ActionOnOperationFinished(IKp2aApp app, ActionToPerformOnFinsh actionToPerform) : base(app, null, null)
{
_actionToPerform = actionToPerform;
}
public ActionOnOperationFinished(IKp2aApp app, ActionToPerformOnFinsh actionToPerform, OnOperationFinishedHandler operationFinishedHandler) : base(app, operationFinishedHandler)
{
_actionToPerform = actionToPerform;
}
public override void Run()
{
if (Message == null)
Message = "";
if (Handler != null)
{
Handler.Post(() =>
{
_actionToPerform(Success, Message, ActiveContext);
});
}
else
{
_actionToPerform(Success, Message, ActiveContext);
}
base.Run();
}
}
}
//Action which runs when the contextInstanceId is the active context
// otherwise it is registered as pending action for the context instance.
public class ActionInContextInstanceOnOperationFinished : ActionOnOperationFinished
{
private readonly int _contextInstanceId;
private IKp2aApp _app;
public ActionInContextInstanceOnOperationFinished(int contextInstanceId, IKp2aApp app, ActionToPerformOnFinsh actionToPerform) : base(app, actionToPerform)
{
_contextInstanceId = contextInstanceId;
_app = app;
}
public ActionInContextInstanceOnOperationFinished(int contextInstanceId, IKp2aApp app, ActionToPerformOnFinsh actionToPerform, OnOperationFinishedHandler operationFinishedHandler) : base(app, actionToPerform, operationFinishedHandler)
{
_contextInstanceId = contextInstanceId;
_app = app;
}
public override void Run()
{
if ((ActiveContext as IContextInstanceIdProvider)?.ContextInstanceId != _contextInstanceId)
{
_app.RegisterPendingActionForContextInstance(_contextInstanceId, this);
}
else _app.UiThreadHandler.Post(() => base.Run());
}
}

View File

@@ -21,7 +21,7 @@ using KeePassLib;
namespace keepass2android
{
public class AddEntry : RunnableOnFinish {
public class AddEntry : OperationWithFinishHandler {
protected Database Db
{
get { return _app.CurrentDb; }
@@ -30,22 +30,20 @@ namespace keepass2android
private readonly IKp2aApp _app;
private readonly PwEntry _entry;
private readonly PwGroup _parentGroup;
private readonly Activity _ctx;
private readonly Database _db;
public static AddEntry GetInstance(Activity ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish, Database db) {
public static AddEntry GetInstance(IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnOperationFinishedHandler operationFinishedHandler, Database db) {
return new AddEntry(ctx, db, app, entry, parentGroup, finish);
return new AddEntry(db, app, entry, parentGroup, operationFinishedHandler);
}
public AddEntry(Activity ctx, Database db, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish):base(ctx, finish) {
_ctx = ctx;
public AddEntry(Database db, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnOperationFinishedHandler operationFinishedHandler):base(app, operationFinishedHandler) {
_db = db;
_parentGroup = parentGroup;
_app = app;
_entry = entry;
_onFinishToRun = new AfterAdd(ctx, app.CurrentDb, entry, app,OnFinishToRun);
_operationFinishedHandler = new AfterAdd(app.CurrentDb, entry, app,operationFinishedHandler);
}
@@ -65,17 +63,17 @@ namespace keepass2android
_db.Elements.Add(_entry);
// Commit to disk
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
SaveDb save = new SaveDb(_app, _app.CurrentDb, operationFinishedHandler);
save.SetStatusLogger(StatusLogger);
save.Run();
}
private class AfterAdd : OnFinish {
private class AfterAdd : OnOperationFinishedHandler {
private readonly Database _db;
private readonly PwEntry _entry;
private readonly IKp2aApp _app;
public AfterAdd(Activity activity, Database db, PwEntry entry, IKp2aApp app, OnFinish finish):base(activity, finish) {
public AfterAdd( Database db, PwEntry entry, IKp2aApp app, OnOperationFinishedHandler operationFinishedHandler):base(app, operationFinishedHandler) {
_db = db;
_entry = entry;
_app = app;

View File

@@ -23,7 +23,7 @@ using KeePassLib;
namespace keepass2android
{
public class AddGroup : RunnableOnFinish {
public class AddGroup : OperationWithFinishHandler {
internal Database Db
{
get { return _app.CurrentDb; }
@@ -38,18 +38,16 @@ namespace keepass2android
public PwGroup Group;
internal PwGroup Parent;
protected bool DontSave;
readonly Activity _ctx;
public static AddGroup GetInstance(Activity ctx, IKp2aApp app, string name, int iconid, PwUuid groupCustomIconId, PwGroup parent, OnFinish finish, bool dontSave) {
return new AddGroup(ctx, app, name, iconid, groupCustomIconId, parent, finish, dontSave);
public static AddGroup GetInstance(IKp2aApp app, string name, int iconid, PwUuid groupCustomIconId, PwGroup parent, OnOperationFinishedHandler operationFinishedHandler, bool dontSave) {
return new AddGroup(app, name, iconid, groupCustomIconId, parent, operationFinishedHandler, dontSave);
}
private AddGroup(Activity ctx, IKp2aApp app, String name, int iconid, PwUuid groupCustomIconId, PwGroup parent, OnFinish finish, bool dontSave)
: base(ctx, finish)
private AddGroup(IKp2aApp app, String name, int iconid, PwUuid groupCustomIconId, PwGroup parent, OnOperationFinishedHandler operationFinishedHandler, bool dontSave)
: base(app, operationFinishedHandler)
{
_ctx = ctx;
_name = name;
_iconId = iconid;
_groupCustomIconId = groupCustomIconId;
@@ -57,7 +55,7 @@ namespace keepass2android
DontSave = dontSave;
_app = app;
_onFinishToRun = new AfterAdd(ctx, this, OnFinishToRun);
_operationFinishedHandler = new AfterAdd(_app, this, operationFinishedHandler);
}
@@ -74,15 +72,15 @@ namespace keepass2android
_app.CurrentDb.Elements.Add(Group);
// Commit to disk
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun, DontSave);
SaveDb save = new SaveDb(_app, _app.CurrentDb, operationFinishedHandler, DontSave, null);
save.SetStatusLogger(StatusLogger);
save.Run();
}
private class AfterAdd : OnFinish {
private class AfterAdd : OnOperationFinishedHandler {
readonly AddGroup _addGroup;
public AfterAdd(Activity activity, AddGroup addGroup,OnFinish finish): base(activity, finish) {
public AfterAdd(IKp2aApp app, AddGroup addGroup,OnOperationFinishedHandler operationFinishedHandler): base(app, operationFinishedHandler) {
_addGroup = addGroup;
}

View File

@@ -26,7 +26,7 @@ using KeePassLib.Utility;
namespace keepass2android
{
public class AddTemplateEntries : RunnableOnFinish {
public class AddTemplateEntries : OperationWithFinishHandler {
public class TemplateEntry
{
@@ -130,15 +130,13 @@ namespace keepass2android
}
private readonly IKp2aApp _app;
private readonly Activity _ctx;
public AddTemplateEntries(Activity ctx, IKp2aApp app, OnFinish finish)
: base(ctx, finish)
public AddTemplateEntries(IKp2aApp app, OnOperationFinishedHandler operationFinishedHandler)
: base(app, operationFinishedHandler)
{
_ctx = ctx;
_app = app;
_app = app;
//_onFinishToRun = new AfterAdd(this, OnFinishToRun);
//_operationFinishedHandler = new AfterAdd(this, operationFinishedHandler);
}
public static readonly List<TemplateEntry> TemplateEntries = new List<TemplateEntry>()
@@ -313,7 +311,7 @@ namespace keepass2android
_app.DirtyGroups.Add(templateGroup);
// Commit to disk
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
SaveDb save = new SaveDb(_app, _app.CurrentDb, operationFinishedHandler);
save.SetStatusLogger(StatusLogger);
save.Run();
}
@@ -337,7 +335,6 @@ namespace keepass2android
_app.DirtyGroups.Add(_app.CurrentDb.KpDatabase.RootGroup);
_app.CurrentDb.GroupsById[templateGroup.Uuid] = templateGroup;
_app.CurrentDb.Elements.Add(templateGroup);
}
addedEntries = new List<PwEntry>();
@@ -369,11 +366,11 @@ namespace keepass2android
return entry;
}
private class AfterAdd : OnFinish {
private class AfterAdd : OnOperationFinishedHandler {
private readonly Database _db;
private readonly List<PwEntry> _entries;
public AfterAdd(Activity activity, Database db, List<PwEntry> entries, OnFinish finish):base(activity, finish) {
public AfterAdd(IKp2aApp app, Database db, List<PwEntry> entries, OnOperationFinishedHandler operationFinishedHandler):base(app, operationFinishedHandler) {
_db = db;
_entries = entries;

View File

@@ -16,8 +16,8 @@ namespace keepass2android.database.edit
{
public class CopyEntry: AddEntry
{
public CopyEntry(Activity ctx, IKp2aApp app, PwEntry entry, OnFinish finish, Database db)
: base(ctx, db, app, CreateCopy(entry, app), entry.ParentGroup, finish)
public CopyEntry(IKp2aApp app, PwEntry entry, OnOperationFinishedHandler operationFinishedHandler, Database db)
: base(db, app, CreateCopy(entry, app), entry.ParentGroup, operationFinishedHandler)
{
}

View File

@@ -26,27 +26,24 @@ using KeePassLib.Keys;
namespace keepass2android
{
public class CreateDb : RunnableOnFinish {
public class CreateDb : OperationWithFinishHandler {
private readonly IOConnectionInfo _ioc;
private readonly bool _dontSave;
private readonly Activity _ctx;
private readonly IKp2aApp _app;
private CompositeKey _key;
private readonly bool _makeCurrent;
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave, bool makeCurrent): base(ctx, finish) {
_ctx = ctx;
_ioc = ioc;
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnOperationFinishedHandler operationFinishedHandler, bool dontSave, bool makeCurrent): base(app, operationFinishedHandler) {
_ioc = ioc;
_dontSave = dontSave;
_makeCurrent = makeCurrent;
_app = app;
}
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave, CompositeKey key, bool makeCurrent)
: base(ctx, finish)
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnOperationFinishedHandler operationFinishedHandler, bool dontSave, CompositeKey key, bool makeCurrent)
: base(app, operationFinishedHandler)
{
_ctx = ctx;
_ioc = ioc;
_ioc = ioc;
_dontSave = dontSave;
_app = app;
_key = key;
@@ -77,19 +74,19 @@ namespace keepass2android
db.SearchHelper = new SearchDbHelper(_app);
// Add a couple default groups
AddGroup internet = AddGroup.GetInstance(_ctx, _app, "Internet", 1, null, db.KpDatabase.RootGroup, null, true);
AddGroup internet = AddGroup.GetInstance(_app, "Internet", 1, null, db.KpDatabase.RootGroup, null, true);
internet.Run();
AddGroup email = AddGroup.GetInstance(_ctx, _app, "eMail", 19, null, db.KpDatabase.RootGroup, null, true);
AddGroup email = AddGroup.GetInstance(_app, "eMail", 19, null, db.KpDatabase.RootGroup, null, true);
email.Run();
List<PwEntry> addedEntries;
AddTemplateEntries addTemplates = new AddTemplateEntries(_ctx, _app, null);
AddTemplateEntries addTemplates = new AddTemplateEntries(_app, null);
addTemplates.AddTemplates(out addedEntries);
// Commit changes
SaveDb save = new SaveDb(_ctx, _app, db, OnFinishToRun, _dontSave);
SaveDb save = new SaveDb(_app, db, operationFinishedHandler, _dontSave, null);
save.SetStatusLogger(StatusLogger);
_onFinishToRun = null;
_operationFinishedHandler = null;
save.Run();
db.UpdateGlobals();

View File

@@ -0,0 +1,39 @@
using Java.Lang;
namespace keepass2android;
public interface IDatabaseModificationWatcher
{
void BeforeModifyDatabases();
void AfterModifyDatabases();
}
public class NullDatabaseModificationWatcher : IDatabaseModificationWatcher
{
public void BeforeModifyDatabases() { }
public void AfterModifyDatabases() { }
}
public class BackgroundDatabaseModificationLocker(IKp2aApp app) : IDatabaseModificationWatcher
{
public void BeforeModifyDatabases()
{
while (true)
{
if (app.DatabasesBackgroundModificationLock.TryEnterWriteLock(TimeSpan.FromSeconds(0.1)))
{
break;
}
if (Java.Lang.Thread.Interrupted())
{
throw new InterruptedException();
}
}
}
public void AfterModifyDatabases()
{
app.DatabasesBackgroundModificationLock.ExitWriteLock();
}
}

View File

@@ -29,8 +29,8 @@ namespace keepass2android
private readonly PwEntry _entry;
private UiStringKey _statusMessage;
public DeleteEntry(Activity activiy, IKp2aApp app, PwEntry entry, OnFinish finish):base(activiy, finish, app) {
Ctx = activiy;
public DeleteEntry(IKp2aApp app, PwEntry entry, OnOperationFinishedHandler operationFinishedHandler):base(operationFinishedHandler, app) {
Db = app.FindDatabaseForElement(entry);
_entry = entry;

View File

@@ -29,25 +29,25 @@ namespace keepass2android
private PwGroup _group;
protected bool DontSave;
public DeleteGroup(Activity activity, IKp2aApp app, PwGroup group, OnFinish finish)
: base(activity, finish, app)
public DeleteGroup(Activity activity, IKp2aApp app, PwGroup group, OnOperationFinishedHandler operationFinishedHandler)
: base(operationFinishedHandler, app)
{
SetMembers(activity, app, group, false);
SetMembers(app, group, false);
}
/*
public DeleteGroup(Context ctx, Database db, PwGroup group, Activity act, OnFinish finish, bool dontSave)
: base(finish)
public DeleteGroup(Context ctx, Database db, PwGroup group, Activity act, OnOperationFinishedHandler operationFinishedHandler, bool dontSave)
: base(operationFinishedHandler)
{
SetMembers(ctx, db, group, act, dontSave);
}
public DeleteGroup(Context ctx, Database db, PwGroup group, OnFinish finish, bool dontSave):base(finish) {
public DeleteGroup(Context ctx, Database db, PwGroup group, OnOperationFinishedHandler operationFinishedHandler, bool dontSave):base(operationFinishedHandler) {
SetMembers(ctx, db, group, null, dontSave);
}
*/
private void SetMembers(Activity activity, IKp2aApp app, PwGroup group, bool dontSave)
private void SetMembers(IKp2aApp app, PwGroup group, bool dontSave)
{
base.SetMembers(activity, app.FindDatabaseForElement(group));
base.SetMembers(app.FindDatabaseForElement(group));
_group = group;
DontSave = dontSave;

View File

@@ -12,11 +12,11 @@ namespace keepass2android
private readonly List<IStructureItem> _elementsToDelete;
private readonly bool _canRecycle;
public DeleteMultipleItemsFromOneDatabase(Activity activity, Database db, List<IStructureItem> elementsToDelete, OnFinish finish, IKp2aApp app)
: base(activity, finish, app)
public DeleteMultipleItemsFromOneDatabase(Activity activity, Database db, List<IStructureItem> elementsToDelete, OnOperationFinishedHandler operationFinishedHandler, IKp2aApp app)
: base(operationFinishedHandler, app)
{
_elementsToDelete = elementsToDelete;
SetMembers(activity, db);
SetMembers(db);
//determine once. The property is queried for each delete operation, but might return false
//after one entry/group is deleted (and thus in recycle bin and thus can't be recycled anymore)

View File

@@ -6,10 +6,10 @@ using KeePassLib;
namespace keepass2android
{
public abstract class DeleteRunnable : RunnableOnFinish
public abstract class DeleteRunnable : OperationWithFinishHandler
{
protected DeleteRunnable(Activity activity, OnFinish finish, IKp2aApp app)
: base(activity, finish)
protected DeleteRunnable(OnOperationFinishedHandler operationFinishedHandler, IKp2aApp app)
: base(app, operationFinishedHandler)
{
App = app;
}
@@ -18,11 +18,10 @@ namespace keepass2android
protected Database Db;
protected Activity Ctx;
protected void SetMembers(Activity activity, Database db)
protected void SetMembers( Database db)
{
Ctx = activity;
Db = db;
}
@@ -131,18 +130,18 @@ namespace keepass2android
(dlgSender, dlgEvt) =>
{
DeletePermanently = true;
ProgressTask pt = new ProgressTask(App, Ctx, this);
BlockingOperationStarter pt = new BlockingOperationStarter(App, this);
pt.Run();
},
(dlgSender, dlgEvt) =>
{
DeletePermanently = false;
ProgressTask pt = new ProgressTask(App, Ctx, this);
BlockingOperationStarter pt = new BlockingOperationStarter(App, this);
pt.Run();
},
(dlgSender, dlgEvt) => { },
Ctx, messageSuffix);
messageSuffix);
@@ -153,12 +152,12 @@ namespace keepass2android
QuestionNoRecycleResourceId,
(dlgSender, dlgEvt) =>
{
ProgressTask pt = new ProgressTask(App, Ctx, this);
BlockingOperationStarter pt = new BlockingOperationStarter(App, this);
pt.Run();
},
null,
(dlgSender, dlgEvt) => { },
Ctx, messageSuffix);
messageSuffix);
}
@@ -215,7 +214,7 @@ namespace keepass2android
Android.Util.Log.Debug("KP2A", "Calling PerformDelete..");
PerformDelete(touchedGroups, permanentlyDeletedGroups);
_onFinishToRun = new ActionOnFinish(ActiveActivity,(success, message, activity) =>
_operationFinishedHandler = new ActionOnOperationFinished(App,(success, message, context) =>
{
if (success)
{
@@ -236,10 +235,10 @@ namespace keepass2android
// Let's not bother recovering from a failure to save. It is too much work.
App.Lock(false, false);
}
}, OnFinishToRun);
}, operationFinishedHandler);
// Commit database
SaveDb save = new SaveDb(Ctx, App, Db, OnFinishToRun, false);
SaveDb save = new SaveDb( App, Db, operationFinishedHandler, false, null);
save.ShowDatabaseIocInStatus = ShowDatabaseIocInStatus;
save.SetStatusLogger(StatusLogger);

View File

@@ -23,7 +23,7 @@ using KeePassLib;
namespace keepass2android
{
public class EditGroup : RunnableOnFinish {
public class EditGroup : OperationWithFinishHandler {
internal Database Db
{
get { return _app.FindDatabaseForElement(Group); }
@@ -36,19 +36,17 @@ namespace keepass2android
private readonly PwIcon _iconId;
private readonly PwUuid _customIconId;
internal PwGroup Group;
readonly Activity _ctx;
public EditGroup(Activity ctx, IKp2aApp app, String name, PwIcon iconid, PwUuid customIconId, PwGroup group, OnFinish finish)
: base(ctx, finish)
public EditGroup(IKp2aApp app, String name, PwIcon iconid, PwUuid customIconId, PwGroup group, OnOperationFinishedHandler operationFinishedHandler)
: base(app, operationFinishedHandler)
{
_ctx = ctx;
_name = name;
_iconId = iconid;
Group = group;
_customIconId = customIconId;
_app = app;
_onFinishToRun = new AfterEdit(ctx, this, OnFinishToRun);
_operationFinishedHandler = new AfterEdit(app, this, operationFinishedHandler);
}
@@ -60,16 +58,16 @@ namespace keepass2android
Group.Touch(true);
// Commit to disk
SaveDb save = new SaveDb(_ctx, _app, Db, OnFinishToRun);
SaveDb save = new SaveDb(_app, Db, operationFinishedHandler);
save.SetStatusLogger(StatusLogger);
save.Run();
}
private class AfterEdit : OnFinish {
private class AfterEdit : OnOperationFinishedHandler {
readonly EditGroup _editGroup;
public AfterEdit(Activity ctx, EditGroup editGroup, OnFinish finish)
: base(ctx, finish)
public AfterEdit(IKp2aApp app, EditGroup editGroup, OnOperationFinishedHandler operationFinishedHandler)
: base(app, operationFinishedHandler)
{
_editGroup = editGroup;
}

View File

@@ -21,10 +21,10 @@ using Android.App;
namespace keepass2android
{
public abstract class FileOnFinish : OnFinish {
public abstract class FileOnFinish : OnOperationFinishedHandler {
private String _filename = "";
protected FileOnFinish(Activity activity, FileOnFinish finish):base(activity, finish) {
protected FileOnFinish(IKp2aApp app, FileOnFinish operationFinishedHandler):base(app, operationFinishedHandler) {
}
public string Filename

View File

@@ -0,0 +1,13 @@
namespace keepass2android;
// A context instance can be the instance of an Activity. Even if the activity is recreated (due to a configuration change, for example), the instance id must remain the same
// but it must be different for other activities/services or if the activity is finished and then starts again.
// We want to be able to perform actions on a context instance, even though that instance might not live at the time when we want to perform the action.
// In that case, we want to be able to register the action such that it is performed when the activity is recreated.
public interface IContextInstanceIdProvider
{
int ContextInstanceId { get; }
}

View File

@@ -21,59 +21,88 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Android.App;
using Android.OS;
using KeePass.Util;
using keepass2android.database.edit;
using keepass2android.Io;
using KeePassLib;
using KeePassLib.Keys;
using KeePassLib.Serialization;
namespace keepass2android
{
public class LoadDb : RunnableOnFinish {
public class LoadDb : OperationWithFinishHandler {
private readonly IOConnectionInfo _ioc;
private readonly Task<MemoryStream> _databaseData;
private readonly CompositeKey _compositeKey;
private readonly string _keyfileOrProvider;
private readonly string? _keyfileOrProvider;
private readonly IKp2aApp _app;
private readonly bool _rememberKeyfile;
IDatabaseFormat _format;
public LoadDb(Activity activity, IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey, String keyfileOrProvider, OnFinish finish, bool updateLastUsageTimestamp, bool makeCurrent): base(activity, finish)
public bool DoNotSetStatusLoggerMessage = false;
public LoadDb(IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey,
string keyfileOrProvider, OnOperationFinishedHandler operationFinishedHandler,
bool updateLastUsageTimestamp, bool makeCurrent, IDatabaseModificationWatcher modificationWatcher = null): base(app, operationFinishedHandler)
{
_app = app;
_modificationWatcher = modificationWatcher ?? new NullDatabaseModificationWatcher();
_app = app;
_ioc = ioc;
_databaseData = databaseData;
_compositeKey = compositeKey;
_keyfileOrProvider = keyfileOrProvider;
_updateLastUsageTimestamp = updateLastUsageTimestamp;
_makeCurrent = makeCurrent;
_rememberKeyfile = app.GetBooleanPreference(PreferenceKey.remember_keyfile);
}
_rememberKeyfile = app.GetBooleanPreference(PreferenceKey.remember_keyfile);
}
protected bool success = false;
private bool _updateLastUsageTimestamp;
private readonly bool _makeCurrent;
private readonly IDatabaseModificationWatcher _modificationWatcher;
public override void Run()
public override void Run()
{
try
{
try
{
//make sure the file data is stored in the recent files list even if loading fails
SaveFileData(_ioc, _keyfileOrProvider);
SaveFileData(_ioc, _keyfileOrProvider);
var fileStorage = _app.GetFileStorage(_ioc);
RequiresSubsequentSync = false;
StatusLogger.UpdateMessage(UiStringKey.loading_database);
if (!DoNotSetStatusLoggerMessage)
{
StatusLogger.UpdateMessage(UiStringKey.loading_database);
}
//get the stream data into a single stream variable (databaseStream) regardless whether its preloaded or not:
MemoryStream preloadedMemoryStream = _databaseData == null ? null : _databaseData.Result;
MemoryStream databaseStream;
if (preloadedMemoryStream != null)
databaseStream = preloadedMemoryStream;
else
if (preloadedMemoryStream != null)
{
//note: if the stream has been loaded already, we don't need to trigger another sync later on
databaseStream = preloadedMemoryStream;
}
else
{
using (Stream s = _app.GetFileStorage(_ioc).OpenFileForRead(_ioc))
if (_app.SyncInBackgroundPreference && fileStorage is CachingFileStorage cachingFileStorage &&
cachingFileStorage.IsCached(_ioc))
{
cachingFileStorage.IsOffline = true;
//no warning. We'll trigger a sync later.
cachingFileStorage.TriggerWarningWhenFallingBackToCache = false;
RequiresSubsequentSync = true;
}
using (Stream s = fileStorage.OpenFileForRead(_ioc))
{
databaseStream = new MemoryStream();
s.CopyTo(databaseStream);
@@ -81,8 +110,13 @@ namespace keepass2android
}
}
if (!StatusLogger.ContinueWork())
{
return;
}
//ok, try to load the database. Let's start with Kdbx format and retry later if that is the wrong guess:
_format = new KdbxDatabaseFormat(KdbxDatabaseFormat.GetFormatToUse(_app.GetFileStorage(_ioc).GetFileExtension(_ioc)));
_format = new KdbxDatabaseFormat(KdbxDatabaseFormat.GetFormatToUse(fileStorage.GetFileExtension(_ioc)));
TryLoad(databaseStream);
@@ -103,10 +137,10 @@ namespace keepass2android
}
catch (AggregateException e)
{
string message = e.Message;
string message = ExceptionUtil.GetErrorMessage(e);
foreach (var innerException in e.InnerExceptions)
{
message = innerException.Message;
message = ExceptionUtil.GetErrorMessage(innerException);
// Override the message shown with the last (hopefully most recent) inner exception
Kp2aLog.LogUnexpectedError(innerException);
}
@@ -116,21 +150,29 @@ namespace keepass2android
catch (DuplicateUuidsException e)
{
Kp2aLog.Log(e.ToString());
Finish(false, _app.GetResourceString(UiStringKey.DuplicateUuidsError) + " " + e.Message + _app.GetResourceString(UiStringKey.DuplicateUuidsErrorAdditional), false, Exception);
Finish(false, _app.GetResourceString(UiStringKey.DuplicateUuidsError) + " " + ExceptionUtil.GetErrorMessage(e) + _app.GetResourceString(UiStringKey.DuplicateUuidsErrorAdditional), false, Exception);
return;
}
catch (Exception e)
catch (Java.Lang.InterruptedException)
{
Kp2aLog.Log("Load interrupted");
//close without Finish()
return;
}
catch (Exception e)
{
if (!(e is InvalidCompositeKeyException))
Kp2aLog.LogUnexpectedError(e);
Finish(false, _app.GetResourceString(UiStringKey.ErrorOcurred) + " " + (e.Message ?? (e is FileNotFoundException ? _app.GetResourceString(UiStringKey.FileNotFound) : "")), false, Exception);
Finish(false, _app.GetResourceString(UiStringKey.ErrorOcurred) + " " + (ExceptionUtil.GetErrorMessage(e) ?? (e is FileNotFoundException ? _app.GetResourceString(UiStringKey.FileNotFound) : "")), false, Exception);
return;
}
}
/// <summary>
public bool RequiresSubsequentSync { get; set; } = false;
/// <summary>
/// Holds the exception which was thrown during execution (if any)
/// </summary>
public Exception Exception { get; set; }
@@ -145,15 +187,21 @@ namespace keepass2android
workingCopy.Seek(0, SeekOrigin.Begin);
//reset stream if we need to reuse it later:
databaseStream.Seek(0, SeekOrigin.Begin);
if (!StatusLogger.ContinueWork())
{
throw new Java.Lang.InterruptedException();
}
//now let's go:
try
{
Database newDb = _app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _format, _makeCurrent);
Kp2aLog.Log("LoadDB OK");
try
{
Database newDb =
_app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _format, _makeCurrent, _modificationWatcher);
Kp2aLog.Log("LoadDB OK");
Finish(true, _format.SuccessMessage);
return newDb;
}
return newDb;
}
catch (OldFormatException)
{
_format = new KdbDatabaseFormat(_app);

View File

@@ -10,18 +10,16 @@ using KeePassLib.Interfaces;
namespace keepass2android.database.edit
{
public class MoveElements: RunnableOnFinish
public class MoveElements: OperationWithFinishHandler
{
private readonly List<IStructureItem> _elementsToMove;
private readonly PwGroup _targetGroup;
private readonly Activity _ctx;
private readonly IKp2aApp _app;
private readonly IKp2aApp _app;
public MoveElements(List<IStructureItem> elementsToMove, PwGroup targetGroup, Activity ctx, IKp2aApp app, OnFinish finish) : base(ctx, finish)
public MoveElements(List<IStructureItem> elementsToMove, PwGroup targetGroup,IKp2aApp app, OnOperationFinishedHandler operationFinishedHandler) : base(app, operationFinishedHandler)
{
_elementsToMove = elementsToMove;
_targetGroup = targetGroup;
_ctx = ctx;
_app = app;
}
@@ -123,24 +121,24 @@ namespace keepass2android.database.edit
int indexToSave = 0;
bool allSavesSuccess = true;
void ContinueSave(bool success, string message, Activity activeActivity)
void ContinueSave(bool success, string message, Context activeActivity)
{
allSavesSuccess &= success;
indexToSave++;
if (indexToSave == allDatabasesToSave.Count)
{
OnFinishToRun.SetResult(allSavesSuccess);
OnFinishToRun.Run();
operationFinishedHandler.SetResult(allSavesSuccess);
operationFinishedHandler.Run();
return;
}
SaveDb saveDb = new SaveDb(_ctx, _app, allDatabasesToSave[indexToSave], new ActionOnFinish(activeActivity, ContinueSave), false);
SaveDb saveDb = new SaveDb( _app, allDatabasesToSave[indexToSave], new ActionOnOperationFinished(_app, ContinueSave), false, null);
saveDb.SetStatusLogger(StatusLogger);
saveDb.ShowDatabaseIocInStatus = allDatabasesToSave.Count > 1;
saveDb.Run();
}
SaveDb save = new SaveDb(_ctx, _app, allDatabasesToSave[0], new ActionOnFinish(ActiveActivity, ContinueSave), false);
SaveDb save = new SaveDb(_app, allDatabasesToSave[0], new ActionOnOperationFinished(_app, ContinueSave), false, null);
save.SetStatusLogger(StatusLogger);
save.ShowDatabaseIocInStatus = allDatabasesToSave.Count > 1;
save.Run();

View File

@@ -1,154 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using Android;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Widget;
using Google.Android.Material.Dialog;
namespace keepass2android
{
public abstract class OnFinish
{
protected bool Success;
protected String Message;
protected Exception Exception;
protected bool ImportantMessage
{
get;
set;
}
protected OnFinish BaseOnFinish;
protected Handler Handler;
private ProgressDialogStatusLogger _statusLogger = new ProgressDialogStatusLogger(); //default: no logging but not null -> can be used whenever desired
private Activity _activeActivity, _previouslyActiveActivity;
public ProgressDialogStatusLogger StatusLogger
{
get { return _statusLogger; }
set { _statusLogger = value; }
}
public Activity ActiveActivity
{
get { return _activeActivity; }
set
{
if (_activeActivity != null && _activeActivity != _previouslyActiveActivity)
{
_previouslyActiveActivity = _activeActivity;
}
_activeActivity = value;
if (BaseOnFinish != null)
{
BaseOnFinish.ActiveActivity = value;
}
}
}
public Activity PreviouslyActiveActivity
{
get { return _previouslyActiveActivity; }
}
protected OnFinish(Activity activeActivity, Handler handler)
{
ActiveActivity = activeActivity;
BaseOnFinish = null;
Handler = handler;
}
protected OnFinish(Activity activeActivity, OnFinish finish, Handler handler)
{
ActiveActivity = activeActivity;
BaseOnFinish = finish;
Handler = handler;
}
protected OnFinish(Activity activeActivity, OnFinish finish)
{
ActiveActivity = activeActivity;
BaseOnFinish = finish;
Handler = null;
}
public void SetResult(bool success, string message, bool importantMessage, Exception exception) {
Success = success;
Message = message;
ImportantMessage = importantMessage;
Exception = exception;
}
public void SetResult(bool success) {
Success = success;
}
public virtual void Run() {
if (BaseOnFinish == null) return;
// Pass on result on call finish
BaseOnFinish.SetResult(Success, Message, ImportantMessage, Exception);
if ( Handler != null ) {
Handler.Post(BaseOnFinish.Run);
} else {
BaseOnFinish.Run();
}
}
protected void DisplayMessage(Context ctx) {
DisplayMessage(ctx, Message, ImportantMessage);
}
public static void DisplayMessage(Context ctx, string message, bool makeDialog)
{
if ( !String.IsNullOrEmpty(message) ) {
Kp2aLog.Log("OnFinish message: " + message);
if (makeDialog && ctx != null)
{
try
{
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(ctx);
builder.SetMessage(message)
.SetPositiveButton(Android.Resource.String.Ok, (sender, args) => ((Dialog)sender).Dismiss())
.Show();
}
catch (Exception)
{
Toast.MakeText(ctx, message, ToastLength.Long).Show();
}
}
else
Toast.MakeText(ctx ?? Application.Context, message, ToastLength.Long).Show();
}
}
}
}

View File

@@ -0,0 +1,144 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using Android;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Widget;
using Google.Android.Material.Dialog;
using KeePassLib.Interfaces;
namespace keepass2android
{
public interface IActiveContextProvider
{
Context ActiveContext { get; }
}
public abstract class OnOperationFinishedHandler
{
protected bool Success;
protected String Message;
protected Exception Exception;
protected bool ImportantMessage
{
get;
set;
}
protected Context ActiveContext
{
get
{
return _activeContextProvider?.ActiveContext;
}
}
protected OnOperationFinishedHandler NextOnOperationFinishedHandler;
protected Handler Handler;
private IKp2aStatusLogger _statusLogger = new Kp2aNullStatusLogger(); //default: no logging but not null -> can be used whenever desired
private readonly IActiveContextProvider _activeContextProvider;
public IKp2aStatusLogger StatusLogger
{
get { return _statusLogger; }
set { _statusLogger = value; }
} protected OnOperationFinishedHandler(IActiveContextProvider activeContextProvider, Handler handler)
{
_activeContextProvider = activeContextProvider;
NextOnOperationFinishedHandler = null;
Handler = handler;
}
protected OnOperationFinishedHandler(IActiveContextProvider activeContextProvider, OnOperationFinishedHandler operationFinishedHandler, Handler handler)
{
_activeContextProvider = activeContextProvider;
NextOnOperationFinishedHandler = operationFinishedHandler;
Handler = handler;
}
protected OnOperationFinishedHandler(IActiveContextProvider activeContextProvider, OnOperationFinishedHandler operationFinishedHandler)
{
_activeContextProvider = activeContextProvider;
NextOnOperationFinishedHandler = operationFinishedHandler;
Handler = null;
}
public void SetResult(bool success, string message, bool importantMessage, Exception exception) {
Success = success;
Message = message;
ImportantMessage = importantMessage;
Exception = exception;
}
public void SetResult(bool success) {
Success = success;
}
public virtual void Run() {
if (NextOnOperationFinishedHandler == null) return;
// Pass on result on call finish
NextOnOperationFinishedHandler.SetResult(Success, Message, ImportantMessage, Exception);
var handler = Handler ?? NextOnOperationFinishedHandler.Handler ?? null;
if (handler != null ) {
handler.Post(() =>
{
NextOnOperationFinishedHandler.Run();
});
} else {
NextOnOperationFinishedHandler.Run();
}
}
protected void DisplayMessage(Context ctx) {
DisplayMessage(ctx, Message, ImportantMessage);
}
public static void DisplayMessage(Context ctx, string message, bool makeDialog)
{
if ( !String.IsNullOrEmpty(message) ) {
Kp2aLog.Log("OnOperationFinishedHandler message: " + message);
if (makeDialog && ctx != null)
{
try
{
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(ctx);
builder.SetMessage(message)
.SetPositiveButton(Android.Resource.String.Ok, (sender, args) => ((Dialog)sender).Dismiss())
.Show();
}
catch (Exception)
{
Toast.MakeText(ctx, message, ToastLength.Long).Show();
}
}
else
Toast.MakeText(ctx ?? Application.Context, message, ToastLength.Long).Show();
}
}
}
}

View File

@@ -0,0 +1,69 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using Android.App;
using Android.Content;
using KeePassLib.Interfaces;
namespace keepass2android
{
public abstract class OperationWithFinishHandler {
protected OnOperationFinishedHandler _operationFinishedHandler;
public IKp2aStatusLogger StatusLogger = new Kp2aNullStatusLogger(); //default: empty but not null
private IActiveContextProvider _activeContextProvider;
protected OperationWithFinishHandler(IActiveContextProvider activeContextProvider, OnOperationFinishedHandler operationFinishedHandler)
{
_activeContextProvider = activeContextProvider;
_operationFinishedHandler = operationFinishedHandler;
}
public OnOperationFinishedHandler operationFinishedHandler
{
get { return _operationFinishedHandler; }
set { _operationFinishedHandler = value; }
}
protected void Finish(bool result, String message, bool importantMessage = false, Exception exception = null) {
if ( operationFinishedHandler != null ) {
operationFinishedHandler.SetResult(result, message, importantMessage, exception);
operationFinishedHandler.Run();
}
}
protected void Finish(bool result) {
if ( operationFinishedHandler != null ) {
operationFinishedHandler.SetResult(result);
operationFinishedHandler.Run();
}
}
public void SetStatusLogger(IKp2aStatusLogger statusLogger) {
if (operationFinishedHandler != null)
{
operationFinishedHandler.StatusLogger = statusLogger;
}
StatusLogger = statusLogger;
}
public abstract void Run();
}
}

View File

@@ -1,78 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using Android.App;
using Android.Content;
namespace keepass2android
{
public abstract class RunnableOnFinish {
protected OnFinish _onFinishToRun;
public ProgressDialogStatusLogger StatusLogger = new ProgressDialogStatusLogger(); //default: empty but not null
private Activity _activeActivity;
protected RunnableOnFinish(Activity activeActivity, OnFinish finish)
{
_activeActivity = activeActivity;
_onFinishToRun = finish;
}
public OnFinish OnFinishToRun
{
get { return _onFinishToRun; }
set { _onFinishToRun = value; }
}
public Activity ActiveActivity
{
get { return _activeActivity; }
set
{
_activeActivity = value;
if (_onFinishToRun != null)
_onFinishToRun.ActiveActivity = _activeActivity;
}
}
protected void Finish(bool result, String message, bool importantMessage = false, Exception exception = null) {
if ( OnFinishToRun != null ) {
OnFinishToRun.SetResult(result, message, importantMessage, exception);
OnFinishToRun.Run();
}
}
protected void Finish(bool result) {
if ( OnFinishToRun != null ) {
OnFinishToRun.SetResult(result);
OnFinishToRun.Run();
}
}
public void SetStatusLogger(ProgressDialogStatusLogger status) {
if (OnFinishToRun != null)
{
OnFinishToRun.StatusLogger = status;
}
StatusLogger = status;
}
public abstract void Run();
}
}

View File

@@ -29,57 +29,70 @@ using KeePassLib.Utility;
using keepass2android.Io;
using Debug = System.Diagnostics.Debug;
using Exception = System.Exception;
using KeePass.Util;
using Thread = System.Threading.Thread;
namespace keepass2android
{
/// <summary>
/// Save the database. If the file has changed, ask the user if he wants to overwrite or sync.
/// </summary>
public class SaveDb : RunnableOnFinish {
public class SaveDb : OperationWithFinishHandler {
private readonly IKp2aApp _app;
private readonly Database _db;
private readonly bool _dontSave;
private readonly IDatabaseModificationWatcher _modificationWatcher;
private bool requiresSubsequentSync = false; //if true, we need to sync the file after saving.
public bool DoNotSetStatusLoggerMessage = false;
/// <summary>
/// stream for reading the data from the original file. If this is set to a non-null value, we know we need to sync
/// </summary>
private readonly Stream _streamForOrigFile;
private readonly Context _ctx;
private Java.Lang.Thread _workerThread;
public SaveDb(Activity ctx, IKp2aApp app, Database db, OnFinish finish, bool dontSave)
: base(ctx, finish)
public SaveDb(IKp2aApp app, Database db, OnOperationFinishedHandler operationFinishedHandler, bool dontSave, IDatabaseModificationWatcher modificationWatcher)
: base(app, operationFinishedHandler)
{
_db = db;
_ctx = ctx;
_app = app;
_dontSave = dontSave;
_modificationWatcher = modificationWatcher ?? new NullDatabaseModificationWatcher();
}
/// <summary>
/// Constructor for sync
/// </summary>
/// <param name="ctx"></param>
/// <param name="app"></param>
/// <param name="finish"></param>
/// <param name="operationFinishedHandler"></param>
/// <param name="dontSave"></param>
/// <param name="streamForOrigFile">Stream for reading the data from the (changed) original location</param>
public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish, Database db, bool dontSave, Stream streamForOrigFile)
: base(ctx, finish)
public SaveDb(IKp2aApp app, OnOperationFinishedHandler operationFinishedHandler, Database db, bool dontSave, Stream streamForOrigFile, IDatabaseModificationWatcher modificationWatcher = null)
: base(app, operationFinishedHandler)
{
_db = db;
_ctx = ctx;
_modificationWatcher = modificationWatcher ?? new NullDatabaseModificationWatcher();
_db = db;
_app = app;
_dontSave = dontSave;
_streamForOrigFile = streamForOrigFile;
}
SyncInBackground = _app.SyncInBackgroundPreference;
public SaveDb(Activity ctx, IKp2aApp app, Database db, OnFinish finish)
: base(ctx, finish)
}
public SaveDb(IKp2aApp app, Database db, OnOperationFinishedHandler operationFinishedHandler, IDatabaseModificationWatcher modificationWatcher = null)
: base(app, operationFinishedHandler)
{
_ctx = ctx;
_app = app;
_modificationWatcher = modificationWatcher ?? new NullDatabaseModificationWatcher();
_app = app;
_db = db;
_dontSave = false;
}
SyncInBackground = _app.SyncInBackgroundPreference;
}
public bool ShowDatabaseIocInStatus { get; set; }
@@ -102,29 +115,42 @@ namespace keepass2android
if (ShowDatabaseIocInStatus)
message += " (" + _app.GetFileStorage(_db.Ioc).GetDisplayName(_db.Ioc) + ")";
StatusLogger.UpdateMessage(message);
IOConnectionInfo ioc = _db.Ioc;
if (!DoNotSetStatusLoggerMessage)
{
StatusLogger.UpdateMessage(message);
}
IOConnectionInfo ioc = _db.Ioc;
IFileStorage fileStorage = _app.GetFileStorage(ioc);
if (_streamForOrigFile == null)
if (SyncInBackground && fileStorage is IOfflineSwitchable offlineSwitchable)
{
offlineSwitchable.IsOffline = true;
//no warning. We'll trigger a sync later.
offlineSwitchable.TriggerWarningWhenFallingBackToCache = false;
requiresSubsequentSync = true;
}
if (_streamForOrigFile == null)
{
if ((!_app.GetBooleanPreference(PreferenceKey.CheckForFileChangesOnSave))
|| (_db.KpDatabase.HashOfFileOnDisk == null)) //first time saving
{
PerformSaveWithoutCheck(fileStorage, ioc);
Finish(true);
FinishWithSuccess();
return;
}
}
bool hasStreamForOrigFile = (_streamForOrigFile != null);
bool hasChangeFast = hasStreamForOrigFile ||
fileStorage.CheckForFileChangeFast(ioc, _db.LastFileVersion); //first try to use the fast change detection;
bool hasHashChanged = hasChangeFast ||
bool hasHashChanged = !requiresSubsequentSync && (
hasChangeFast ||
(FileHashChanged(ioc, _db.KpDatabase.HashOfFileOnDisk) ==
FileHashChange.Changed); //if that fails, hash the file and compare:
FileHashChange.Changed)); //if that fails, hash the file and compare:
if (hasHashChanged)
{
@@ -157,15 +183,14 @@ namespace keepass2android
RunInWorkerThread(() =>
{
PerformSaveWithoutCheck(fileStorage, ioc);
Finish(true);
FinishWithSuccess();
});
},
//cancel
(sender, args) =>
{
RunInWorkerThread(() => Finish(false));
},
_ctx
}
);
}
@@ -173,7 +198,7 @@ namespace keepass2android
else
{
PerformSaveWithoutCheck(fileStorage, ioc);
Finish(true);
FinishWithSuccess();
}
}
@@ -187,28 +212,73 @@ namespace keepass2android
}
*/
Kp2aLog.LogUnexpectedError(e);
Finish(false, e.Message);
Finish(false, ExceptionUtil.GetErrorMessage(e));
return;
}
}
else
{
Finish(true);
FinishWithSuccess();
}
}
public bool SyncInBackground { get; set; }
private void FinishWithSuccess()
{
if (requiresSubsequentSync)
{
var syncTask = new SynchronizeCachedDatabase(_app, _db, new ActionOnOperationFinished(_app,
(success, message, context) =>
{
if (!System.String.IsNullOrEmpty(message))
_app.ShowMessage(context, message, success ? MessageSeverity.Info : MessageSeverity.Error);
}), new BackgroundDatabaseModificationLocker(_app)
);
OperationRunner.Instance.Run(_app, syncTask);
}
Finish(true);
}
private void MergeAndFinish(IFileStorage fileStorage, IOConnectionInfo ioc)
{
//note: when synced, the file might be downloaded once again from the server. Caching the data
//in the hashing function would solve this but increases complexity. I currently assume the files are
//small.
MergeIn(fileStorage, ioc);
try
{
_modificationWatcher.BeforeModifyDatabases();
}
catch (Java.Lang.InterruptedException)
{
// leave without Finish()
return;
}
try
{
MergeIn(fileStorage, ioc);
}
finally
{
_modificationWatcher.AfterModifyDatabases();
}
PerformSaveWithoutCheck(fileStorage, ioc);
_db.UpdateGlobals();
Finish(true);
new Handler(Looper.MainLooper).Post(() =>
{
_db.UpdateGlobals();
});
FinishWithSuccess();
}
private void RunInWorkerThread(Action runHandler)
{
try
@@ -222,8 +292,8 @@ namespace keepass2android
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
Kp2aLog.Log("Error in worker thread of SaveDb: " + e);
Finish(false, e.Message);
Kp2aLog.Log("Error in worker thread of SaveDb: " + ExceptionUtil.GetErrorMessage(e));
Finish(false, ExceptionUtil.GetErrorMessage(e));
}
});
@@ -233,7 +303,7 @@ namespace keepass2android
{
Kp2aLog.LogUnexpectedError(e);
Kp2aLog.Log("Error starting worker thread of SaveDb: "+e);
Finish(false, e.Message);
Finish(false, ExceptionUtil.GetErrorMessage(e));
}
}
@@ -281,7 +351,7 @@ namespace keepass2android
private void PerformSaveWithoutCheck(IFileStorage fileStorage, IOConnectionInfo ioc)
{
StatusLogger.UpdateSubMessage("");
_db.SaveData();
_db.SaveData(fileStorage);
_db.LastFileVersion = fileStorage.GetCurrentFileVersionFast(ioc);
}

View File

@@ -22,26 +22,24 @@ using KeePassLib.Keys;
namespace keepass2android
{
public class SetPassword : RunnableOnFinish {
public class SetPassword : OperationWithFinishHandler {
private readonly String _password;
private readonly String _keyfile;
private readonly IKp2aApp _app;
private readonly bool _dontSave;
private readonly Activity _ctx;
public SetPassword(Activity ctx, IKp2aApp app, String password, String keyfile, OnFinish finish): base(ctx, finish) {
_ctx = ctx;
public SetPassword(IKp2aApp app, String password, String keyfile, OnOperationFinishedHandler operationFinishedHandler): base(app, operationFinishedHandler) {
_app = app;
_password = password;
_keyfile = keyfile;
_dontSave = false;
}
public SetPassword(Activity ctx, IKp2aApp app, String password, String keyfile, OnFinish finish, bool dontSave)
: base(ctx, finish)
public SetPassword(IKp2aApp app, String password, String keyfile, OnOperationFinishedHandler operationFinishedHandler, bool dontSave)
: base(app, operationFinishedHandler)
{
_ctx = ctx;
_app = app;
_password = password;
_keyfile = keyfile;
@@ -73,18 +71,18 @@ namespace keepass2android
pm.MasterKey = newKey;
// Save Database
_onFinishToRun = new AfterSave(ActiveActivity, previousKey, previousMasterKeyChanged, pm, OnFinishToRun);
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun, _dontSave);
_operationFinishedHandler = new AfterSave(_app, previousKey, previousMasterKeyChanged, pm, operationFinishedHandler);
SaveDb save = new SaveDb(_app, _app.CurrentDb, operationFinishedHandler, _dontSave, null);
save.SetStatusLogger(StatusLogger);
save.Run();
}
private class AfterSave : OnFinish {
private class AfterSave : OnOperationFinishedHandler {
private readonly CompositeKey _backup;
private readonly DateTime _previousKeyChanged;
private readonly PwDatabase _db;
public AfterSave(Activity activity, CompositeKey backup, DateTime previousKeyChanged, PwDatabase db, OnFinish finish): base(activity, finish) {
public AfterSave(IActiveContextProvider activeContextProvider, CompositeKey backup, DateTime previousKeyChanged, PwDatabase db, OnOperationFinishedHandler operationFinishedHandler): base(activeContextProvider, operationFinishedHandler) {
_previousKeyChanged = previousKeyChanged;
_backup = backup;
_db = db;

View File

@@ -22,31 +22,29 @@ using KeePassLib;
namespace keepass2android
{
public class UpdateEntry : RunnableOnFinish {
public class UpdateEntry : OperationWithFinishHandler {
private readonly IKp2aApp _app;
private readonly Activity _ctx;
public UpdateEntry(Activity ctx, IKp2aApp app, PwEntry oldE, PwEntry newE, OnFinish finish):base(ctx, finish) {
_ctx = ctx;
_app = app;
_onFinishToRun = new AfterUpdate(ctx, oldE, newE, app, finish);
public UpdateEntry(IKp2aApp app, PwEntry oldE, PwEntry newE, OnOperationFinishedHandler operationFinishedHandler):base(app, operationFinishedHandler) {
_app = app;
_operationFinishedHandler = new AfterUpdate( oldE, newE, app, operationFinishedHandler);
}
public override void Run() {
// Commit to disk
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
SaveDb save = new SaveDb(_app, _app.CurrentDb, operationFinishedHandler);
save.SetStatusLogger(StatusLogger);
save.Run();
}
private class AfterUpdate : OnFinish {
private class AfterUpdate : OnOperationFinishedHandler {
private readonly PwEntry _backup;
private readonly PwEntry _updatedEntry;
private readonly IKp2aApp _app;
public AfterUpdate(Activity activity, PwEntry backup, PwEntry updatedEntry, IKp2aApp app, OnFinish finish):base(activity, finish) {
public AfterUpdate(PwEntry backup, PwEntry updatedEntry, IKp2aApp app, OnOperationFinishedHandler operationFinishedHandler):base(app, operationFinishedHandler) {
_backup = backup;
_updatedEntry = updatedEntry;
_app = app;

25
src/build-scripts/build-java.sh Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/bash
set -e
#unset ANDROID_NDK_HOME ANDROID_NDK
pushd ../java/
pushd JavaFileStorageTest-AS
./gradlew assemble
popd
pushd KP2ASoftkeyboard_AS
./gradlew assemble
popd
pushd Keepass2AndroidPluginSDK2
./gradlew assemble
popd
pushd KP2AKdbLibrary
./gradlew assemble
popd
popd

View File

@@ -0,0 +1,6 @@
#!/bin/bash
set -e
pushd ../java/argon2
ndk-build
popd

View File

@@ -0,0 +1,29 @@
## Setup build environment
* install Android SDK
* install Android NDK
* install dotnet8
```
#from https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/sdk-8.0.407-linux-x64-binaries
wget https://download.visualstudio.microsoft.com/download/pr/9d07577e-f7bc-4d60-838d-f79c50b5c11a/459ef339396783db369e0432d6dc3d7e/dotnet-sdk-8.0.407-linux-x64.tar.gz
mkdir -p $HOME/dotnet && tar zxf dotnet-sdk-8.0.407-linux-x64.tar.gz -C $HOME/dotnet
export DOTNET_ROOT=$HOME/dotnet
export PATH=$PATH:$HOME/dotnet
```
## Build Keepass2Android
```
git clone --recurse-submodules https://github.com/PhilippC/keepass2android.git
cd keepass2android/src/build-scripts
./build-java.sh && ./build-native.sh
cd ..
cp Kp2aBusinessLogic/Io/DropboxFileStorageKeysDummy.cs Kp2aBusinessLogic/Io/DropboxFileStorageKeys.cs
cd keepass2android-app
ln -s Manifests/AndroidManifest_debug.xml AndroidManifest.xml
dotnet workload restore
dotnet restore
dotnet build
```

View File

@@ -55,4 +55,19 @@
<string name="afc_title_size">Dimensiune</string>
<string name="afc_title_sort_by">Sortează după…</string>
<string name="afc_yesterday">Ieri</string>
<plurals name="afc_title_choose_directories">
<item quantity="one">Alege dosarul…</item>
<item quantity="few">Alege dosarele…</item>
<item quantity="other">Alege dosarele…</item>
</plurals>
<plurals name="afc_title_choose_files">
<item quantity="one">Alege fișierul…</item>
<item quantity="few">Alege fișierele…</item>
<item quantity="other">Alege fișierele…</item>
</plurals>
<plurals name="afc_title_choose_files_directories">
<item quantity="one">Alege fișierul/dosarul…</item>
<item quantity="few">Alege fișierele/dosarele…</item>
<item quantity="other">Alege fișierele/dosarele…</item>
</plurals>
</resources>

View File

@@ -59,7 +59,7 @@ namespace keepass2android
}
catch (ActivityNotFoundException)
{
Toast.MakeText(Context, Resource.String.no_url_handler, ToastLength.Long).Show();
App.Kp2a.ShowMessage(Context, Resource.String.no_url_handler, MessageSeverity.Error);
}
};
@@ -71,7 +71,7 @@ namespace keepass2android
}
catch (ActivityNotFoundException)
{
Toast.MakeText(Context, Resource.String.no_url_handler, ToastLength.Long).Show();
App.Kp2a.ShowMessage(Context, Resource.String.no_url_handler, MessageSeverity.Error);
}
};
FindViewById(Resource.Id.translate).Click += delegate
@@ -82,7 +82,7 @@ namespace keepass2android
}
catch (ActivityNotFoundException)
{
Toast.MakeText(Context, Resource.String.no_url_handler, ToastLength.Long).Show();
App.Kp2a.ShowMessage(Context, Resource.String.no_url_handler, MessageSeverity.Error);
}
}; FindViewById(Resource.Id.donate).Click += delegate
{

View File

@@ -228,9 +228,9 @@ namespace keepass2android
newEntry.SetUuid(new PwUuid(true), true); // Create new UUID
string strTitle = newEntry.Strings.ReadSafe(PwDefs.TitleField);
newEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, strTitle + " (" + Android.OS.Build.Model + ")"));
var addTask = new AddEntry(this, App.Kp2a.CurrentDb, App.Kp2a, newEntry,item.Entry.ParentGroup,new ActionOnFinish(this, (success, message, activity) => ((ConfigureChildDatabasesActivity)activity).Update()));
var addTask = new AddEntry( App.Kp2a.CurrentDb, App.Kp2a, newEntry,item.Entry.ParentGroup,new ActionInContextInstanceOnOperationFinished(ContextInstanceId, App.Kp2a, (success, message, context) => (context as ConfigureChildDatabasesActivity)?.Update()));
ProgressTask pt = new ProgressTask(App.Kp2a, this, addTask);
BlockingOperationStarter pt = new BlockingOperationStarter(App.Kp2a, addTask);
pt.Run();
}
@@ -260,9 +260,9 @@ namespace keepass2android
private void Save(AutoExecItem item)
{
var addTask = new SaveDb(this, App.Kp2a, App.Kp2a.FindDatabaseForElement(item.Entry), new ActionOnFinish(this, (success, message, activity) => ((ConfigureChildDatabasesActivity)activity).Update()));
var addTask = new SaveDb(App.Kp2a, App.Kp2a.FindDatabaseForElement(item.Entry), new ActionInContextInstanceOnOperationFinished(ContextInstanceId, App.Kp2a, (success, message, context) => (context as ConfigureChildDatabasesActivity)?.Update()));
ProgressTask pt = new ProgressTask(App.Kp2a, this, addTask);
BlockingOperationStarter pt = new BlockingOperationStarter(App.Kp2a, addTask);
pt.Run();
}
@@ -343,7 +343,7 @@ namespace keepass2android
}
if (autoOpenGroup == null)
{
AddGroup addGroupTask = AddGroup.GetInstance(this, App.Kp2a, "AutoOpen", 1, null, rootGroup, null, true);
AddGroup addGroupTask = AddGroup.GetInstance(App.Kp2a, "AutoOpen", 1, null, rootGroup, null, true);
addGroupTask.Run();
autoOpenGroup = addGroupTask.Group;
}
@@ -367,9 +367,9 @@ namespace keepass2android
{KeeAutoExecExt.ThisDeviceId, true}
})));
var addTask = new AddEntry(this, db, App.Kp2a, newEntry, autoOpenGroup, new ActionOnFinish(this, (success, message, activity) => (activity as ConfigureChildDatabasesActivity)?.Update()));
var addTask = new AddEntry( db, App.Kp2a, newEntry, autoOpenGroup, new ActionInContextInstanceOnOperationFinished(ContextInstanceId, App.Kp2a, (success, message, context) => (context as ConfigureChildDatabasesActivity)?.Update()));
ProgressTask pt = new ProgressTask(App.Kp2a, this, addTask);
BlockingOperationStarter pt = new BlockingOperationStarter(App.Kp2a, addTask);
pt.Run();
}

View File

@@ -183,7 +183,7 @@ namespace keepass2android
// Verify that a password or keyfile is set
if (password.Length == 0 && !keyfileCheckbox.Checked)
{
Toast.MakeText(this, Resource.String.error_nopass, ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, Resource.String.error_nopass, MessageSeverity.Error);
return;
}
@@ -207,16 +207,15 @@ namespace keepass2android
}
catch (Exception)
{
Toast.MakeText(this, Resource.String.error_adding_keyfile, ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, Resource.String.error_adding_keyfile, MessageSeverity.Error);
return;
}
}
// Create the new database
CreateDb create = new CreateDb(App.Kp2a, this, _ioc, new LaunchGroupActivity(_ioc, this), false, newKey, makeCurrent);
ProgressTask createTask = new ProgressTask(
App.Kp2a,
this, create);
CreateDb create = new CreateDb(App.Kp2a, this, _ioc, new LaunchGroupActivity(_ioc, App.Kp2a, this), false, newKey, makeCurrent);
BlockingOperationStarter createTask = new BlockingOperationStarter(
App.Kp2a, create);
createTask.Run();
}
@@ -235,7 +234,7 @@ namespace keepass2android
if (! pass.Equals(confpass))
{
// Passwords do not match
Toast.MakeText(this, Resource.String.error_pass_match, ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, Resource.String.error_pass_match, MessageSeverity.Error);
return false;
}
return true;
@@ -317,7 +316,7 @@ namespace keepass2android
if (resultCode == KeePass.ResultOkPasswordGenerator)
{
String generatedPassword = data.GetStringExtra("keepass2android.password.generated_password");
String generatedPassword = data.GetStringExtra(GeneratePasswordActivity.GeneratedPasswordKey);
FindViewById<TextView>(Resource.Id.entry_password).Text = generatedPassword;
FindViewById<TextView>(Resource.Id.entry_confpassword).Text = generatedPassword;
}
@@ -403,14 +402,14 @@ namespace keepass2android
private class LaunchGroupActivity : FileOnFinish
{
readonly CreateDatabaseActivity _activity;
private readonly IOConnectionInfo _ioc;
private readonly CreateDatabaseActivity _activity;
public LaunchGroupActivity(IOConnectionInfo ioc, CreateDatabaseActivity activity)
: base(activity, null)
public LaunchGroupActivity(IOConnectionInfo ioc, IKp2aApp app, CreateDatabaseActivity activity)
: base(app, null)
{
_activity = activity;
_ioc = ioc;
_activity = activity;
_ioc = ioc;
}
public override void Run()
@@ -420,7 +419,7 @@ namespace keepass2android
// Update the ongoing notification
App.Kp2a.UpdateOngoingNotification();
if (PreferenceManager.GetDefaultSharedPreferences(_activity).GetBoolean(_activity.GetString(Resource.String.RememberRecentFiles_key), _activity.Resources.GetBoolean(Resource.Boolean.RememberRecentFiles_default)))
if (PreferenceManager.GetDefaultSharedPreferences(App.Context).GetBoolean(App.Context.GetString(Resource.String.RememberRecentFiles_key), App.Context.Resources.GetBoolean(Resource.Boolean.RememberRecentFiles_default)))
{
// Add to recent files
FileDbHelper dbHelper = App.Kp2a.FileDbHelper;

View File

@@ -4,12 +4,12 @@ using KeePassLib.Serialization;
namespace keepass2android
{
class CreateNewFilename : RunnableOnFinish
class CreateNewFilename : OperationWithFinishHandler
{
private readonly string _filename;
public CreateNewFilename(Activity activity, OnFinish finish, string filename)
: base(activity,finish)
public CreateNewFilename(IKp2aApp app, OnOperationFinishedHandler operationFinishedHandler, string filename)
: base(app,operationFinishedHandler)
{
_filename = filename;
}
@@ -28,7 +28,7 @@ namespace keepass2android
}
catch (Exception e)
{
Finish(false, e.Message);
Finish(false, Util.GetErrorMessage(e));
}
}

View File

@@ -41,7 +41,7 @@ namespace keepass2android
string requestedUrl = Intent.GetStringExtra(ChooseForAutofillActivityBase.ExtraQueryString);
if (requestedUrl == null)
{
Toast.MakeText(this, "Cannot execute query for null.", ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, "Cannot execute query for null.", MessageSeverity.Error);
RestartApp();
return;
}

View File

@@ -56,6 +56,7 @@ using Android.Util;
using AndroidX.Core.Content;
using Google.Android.Material.Dialog;
using keepass2android;
using keepass2android.views;
namespace keepass2android
{
@@ -63,7 +64,7 @@ namespace keepass2android
{
private readonly string _binaryToSave;
public ExportBinaryProcessManager(int requestCode, Activity activity, string key) : base(requestCode, activity)
public ExportBinaryProcessManager(int requestCode, LifecycleAwareActivity activity, string key) : base(requestCode, activity)
{
_binaryToSave = key;
}
@@ -75,13 +76,13 @@ namespace keepass2android
protected override void SaveFile(IOConnectionInfo ioc)
{
var task = new EntryActivity.WriteBinaryTask(_activity, App.Kp2a, new ActionOnFinish(_activity, (success, message, activity) =>
var task = new EntryActivity.WriteBinaryTask(App.Kp2a, new ActionOnOperationFinished(App.Kp2a, (success, message, context) =>
{
if (!success)
Toast.MakeText(activity, message, ToastLength.Long).Show();
App.Kp2a.ShowMessage(context, message, MessageSeverity.Error);
}
), ((EntryActivity)_activity).Entry.Binaries.Get(_binaryToSave), ioc);
ProgressTask pt = new ProgressTask(App.Kp2a, _activity, task);
BlockingOperationStarter pt = new BlockingOperationStarter(App.Kp2a, task);
pt.Run();
}
@@ -89,6 +90,7 @@ namespace keepass2android
public override void OnSaveInstanceState(Bundle outState)
{
outState.PutString("BinaryToSave", _binaryToSave);
base.OnSaveInstanceState(outState);
}
@@ -97,7 +99,7 @@ namespace keepass2android
[Activity (Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden,
Theme = "@style/Kp2aTheme_ActionBar")]
public class EntryActivity : LockCloseActivity
public class EntryActivity : LockCloseActivity, IProgressUiProvider
{
public const String KeyEntry = "entry";
public const String KeyRefreshPos = "refresh_pos";
@@ -107,8 +109,47 @@ namespace keepass2android
public const int requestCodeBinaryFilename = 42376;
public const int requestCodeSelFileStorageForWriteAttachment = 42377;
protected override View? SnackbarAnchorView => FindViewById(Resource.Id.main_content);
public class UpdateEntryActivityBroadcastReceiver : BroadcastReceiver
{
private readonly EntryActivity _activity;
public UpdateEntryActivityBroadcastReceiver(EntryActivity activity)
{
_activity = activity;
}
public override void OnReceive(Context? context, Intent? intent)
{
if (intent?.Action == Intents.DataUpdated)
{
_activity.OnDataUpdated();
}
}
}
private void OnDataUpdated()
{
if (Entry == null)
{
return;
}
var entryUId = Entry.Uuid;
if (!App.Kp2a.CurrentDb.EntriesById.ContainsKey(entryUId))
{
Finish();
return;
}
var newEntry = App.Kp2a.CurrentDb.EntriesById[entryUId];
if (!newEntry.EqualsEntry(Entry, PwCompareOptions.None, MemProtCmpMode.Full))
{
Recreate();
}
}
public static void Launch(Activity act, PwEntry pw, int pos, AppTask appTask, ActivityFlags? flags = null, int historyIndex=-1)
{
@@ -481,8 +522,8 @@ namespace keepass2android
Entry.Expires = true;
Entry.Touch(true);
RequiresRefresh();
UpdateEntry update = new UpdateEntry(this, App.Kp2a, backupEntry, Entry, null);
ProgressTask pt = new ProgressTask(App.Kp2a, this, update);
UpdateEntry update = new UpdateEntry(App.Kp2a, backupEntry, Entry, null);
BlockingOperationStarter pt = new BlockingOperationStarter(App.Kp2a, update);
pt.Run();
}
FillData();
@@ -501,7 +542,13 @@ namespace keepass2android
//the rest of the things to do depends on the current app task:
AppTask.CompleteOnCreateEntryActivity(this, notifyPluginsOnOpenThread);
}
_dataUpdatedIntentReceiver = new UpdateEntryActivityBroadcastReceiver(this);
IntentFilter filter = new IntentFilter();
filter.AddAction(Intents.DataUpdated);
ContextCompat.RegisterReceiver(this, _dataUpdatedIntentReceiver, filter, (int)ReceiverFlags.Exported);
}
private void RemoveFromHistory()
{
@@ -525,13 +572,17 @@ namespace keepass2android
App.Kp2a.DirtyGroups.Add(parent);
}
var saveTask = new SaveDb(this, App.Kp2a, App.Kp2a.FindDatabaseForElement(Entry), new ActionOnFinish(this, (success, message, activity) =>
var saveTask = new SaveDb( App.Kp2a, App.Kp2a.FindDatabaseForElement(Entry), new ActionInContextInstanceOnOperationFinished(ContextInstanceId, App.Kp2a, (success, message, context) =>
{
activity.SetResult(KeePass.ExitRefresh);
activity.Finish();
if (context is Activity activity)
{
activity.SetResult(KeePass.ExitRefresh);
activity.Finish();
}
}));
ProgressTask pt = new ProgressTask(App.Kp2a, this, saveTask);
BlockingOperationStarter pt = new BlockingOperationStarter(App.Kp2a, saveTask);
pt.Run();
}
@@ -767,9 +818,9 @@ namespace keepass2android
if (parent == null || (parent.Exists() && !parent.IsDirectory))
{
Toast.MakeText(this,
App.Kp2a.ShowMessage(this,
Resource.String.error_invalid_path,
ToastLength.Long).Show();
MessageSeverity.Error);
return null;
}
@@ -778,9 +829,9 @@ namespace keepass2android
// Create parent directory
if (!parent.Mkdirs())
{
Toast.MakeText(this,
App.Kp2a.ShowMessage(this,
Resource.String.error_could_not_create_parent,
ToastLength.Long).Show();
MessageSeverity.Error);
return null;
}
@@ -794,18 +845,18 @@ namespace keepass2android
}
catch (Exception exWrite)
{
Toast.MakeText(this,
App.Kp2a.ShowMessage(this,
GetString(Resource.String.SaveAttachment_Failed, new Java.Lang.Object[] {filename})
+ exWrite.Message, ToastLength.Long).Show();
+ Util.GetErrorMessage(exWrite), MessageSeverity.Error);
return null;
}
finally
{
MemUtil.ZeroByteArray(pbData);
}
Toast.MakeText(this,
App.Kp2a.ShowMessage(this,
GetString(Resource.String.SaveAttachment_doneMessage, new Java.Lang.Object[] {filename}),
ToastLength.Short).Show();
MessageSeverity.Info);
return Uri.Parse("content://" + AttachmentContentProvider.Authority + "/"
+ filename);
}
@@ -838,7 +889,7 @@ namespace keepass2android
catch (ActivityNotFoundException)
{
//ignore
Toast.MakeText(this, "Couldn't open file", ToastLength.Short).Show();
App.Kp2a.ShowMessage(this, "Couldn't open file", MessageSeverity.Error);
}
}
@@ -1078,7 +1129,9 @@ namespace keepass2android
UnregisterReceiver(_pluginActionReceiver);
if (_pluginFieldReceiver != null)
UnregisterReceiver(_pluginFieldReceiver);
base.OnDestroy();
if (_dataUpdatedIntentReceiver != null)
UnregisterReceiver(_dataUpdatedIntentReceiver);
base.OnDestroy();
}
private void NotifyPluginsOnClose()
@@ -1260,13 +1313,13 @@ namespace keepass2android
}
public class WriteBinaryTask : RunnableOnFinish
public class WriteBinaryTask : OperationWithFinishHandler
{
private readonly IKp2aApp _app;
private readonly ProtectedBinary _data;
private IOConnectionInfo _targetIoc;
public WriteBinaryTask(Activity activity, IKp2aApp app, OnFinish onFinish, ProtectedBinary data, IOConnectionInfo targetIoc) : base(activity, onFinish)
public WriteBinaryTask(IKp2aApp app, OnOperationFinishedHandler onOperationFinishedHandler, ProtectedBinary data, IOConnectionInfo targetIoc) : base(app, onOperationFinishedHandler)
{
_app = app;
_data = data;
@@ -1305,7 +1358,7 @@ namespace keepass2android
}
catch (Exception ex)
{
Finish(false, ex.Message);
Finish(false, Util.GetErrorMessage(ex));
}
@@ -1354,6 +1407,7 @@ namespace keepass2android
}
bool isPaused = false;
private UpdateEntryActivityBroadcastReceiver _dataUpdatedIntentReceiver;
protected override void OnPause()
{
@@ -1440,8 +1494,8 @@ namespace keepass2android
Finish();
return true;
case Resource.Id.menu_delete:
DeleteEntry task = new DeleteEntry(this, App.Kp2a, Entry,
new ActionOnFinish(this, (success, message, activity) => { if (success) { RequiresRefresh(); Finish();}}));
DeleteEntry task = new DeleteEntry(App.Kp2a, Entry,
new ActionOnOperationFinished(App.Kp2a, (success, message, context) => { if (success) { RequiresRefresh(); Finish();}}));
task.Start();
break;
case Resource.Id.menu_toggle_pass:
@@ -1504,16 +1558,16 @@ namespace keepass2android
//save the entry:
ActionOnFinish closeOrShowError = new ActionOnFinish(this, (success, message, activity) =>
ActionOnOperationFinished closeOrShowError = new ActionInContextInstanceOnOperationFinished(ContextInstanceId, App.Kp2a, (success, message, context) =>
{
OnFinish.DisplayMessage(this, message, true);
finishAction((EntryActivity)activity);
OnOperationFinishedHandler.DisplayMessage(this, message, true);
finishAction(context as EntryActivity);
});
RunnableOnFinish runnable = new UpdateEntry(this, App.Kp2a, initialEntry, newEntry, closeOrShowError);
OperationWithFinishHandler runnable = new UpdateEntry(App.Kp2a, initialEntry, newEntry, closeOrShowError);
ProgressTask pt = new ProgressTask(App.Kp2a, this, runnable);
BlockingOperationStarter pt = new BlockingOperationStarter(App.Kp2a, runnable);
pt.Run();
}
@@ -1558,7 +1612,7 @@ namespace keepass2android
}
catch (ActivityNotFoundException)
{
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, Resource.String.no_url_handler, MessageSeverity.Error);
}
return true;
}
@@ -1603,5 +1657,7 @@ namespace keepass2android
imageViewerIntent.PutExtra("EntryKey", key);
StartActivity(imageViewerIntent);
}
}
public IProgressUi? ProgressUi => FindViewById<BackgroundOperationContainer>(Resource.Id.background_ops_container);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -62,7 +62,7 @@ namespace keepass2android
}
/// <summary>
/// Holds the state of the EntrryEditActivity. This is required to be able to keep a partially modified entry in memory
/// Holds the state of the EntryEditActivity. This is required to be able to keep a partially modified entry in memory
/// through the App variable. Serializing this state (especially the Entry/EntryInDatabase) can be a performance problem
/// when there are big attachements.
/// </summary>

View File

@@ -19,23 +19,23 @@ namespace keepass2android
{
private readonly FileFormatProvider _ffp;
public ExportDbProcessManager(int requestCode, Activity activity, FileFormatProvider ffp) : base(requestCode, activity)
public ExportDbProcessManager(int requestCode, LifecycleAwareActivity activity, FileFormatProvider ffp) : base(requestCode, activity)
{
_ffp = ffp;
}
protected override void SaveFile(IOConnectionInfo ioc)
{
var exportDb = new ExportDatabaseActivity.ExportDb(_activity, App.Kp2a, new ActionOnFinish(_activity, (success, message, activity) =>
var exportDb = new ExportDatabaseActivity.ExportDb(App.Kp2a, new ActionInContextInstanceOnOperationFinished(_activity.ContextInstanceId, App.Kp2a, (success, message, context) =>
{
if (!success)
Toast.MakeText(activity, message, ToastLength.Long).Show();
App.Kp2a.ShowMessage(context, message, MessageSeverity.Error);
else
Toast.MakeText(activity, _activity.GetString(Resource.String.export_database_successful), ToastLength.Long).Show();
activity.Finish();
App.Kp2a.ShowMessage(context, _activity.GetString(Resource.String.export_database_successful), MessageSeverity.Info);
(context as Activity)?.Finish();
}
), _ffp, ioc);
ProgressTask pt = new ProgressTask(App.Kp2a, _activity, exportDb);
BlockingOperationStarter pt = new BlockingOperationStarter(App.Kp2a, exportDb);
pt.Run();
}
@@ -93,13 +93,13 @@ namespace keepass2android
get { return 0; }
}
public class ExportDb : RunnableOnFinish
public class ExportDb : OperationWithFinishHandler
{
private readonly IKp2aApp _app;
private readonly FileFormatProvider _fileFormat;
private IOConnectionInfo _targetIoc;
public ExportDb(Activity activity, IKp2aApp app, OnFinish onFinish, FileFormatProvider fileFormat, IOConnectionInfo targetIoc) : base(activity, onFinish)
public ExportDb(IKp2aApp app, OnOperationFinishedHandler onOperationFinishedHandler, FileFormatProvider fileFormat, IOConnectionInfo targetIoc) : base(app, onOperationFinishedHandler)
{
_app = app;
this._fileFormat = fileFormat;
@@ -140,7 +140,7 @@ namespace keepass2android
}
catch (Exception ex)
{
Finish(false, ex.Message);
Finish(false, Util.GetErrorMessage(ex));
}

View File

@@ -12,9 +12,9 @@ namespace keepass2android
{
protected readonly int _requestCode;
protected readonly Activity _activity;
protected readonly LifecycleAwareActivity _activity;
public FileSaveProcessManager(int requestCode, Activity activity)
public FileSaveProcessManager(int requestCode, LifecycleAwareActivity activity)
{
_requestCode = requestCode;
_activity = activity;
@@ -103,11 +103,11 @@ namespace keepass2android
}
else
{
var task = new CreateNewFilename(_activity, new ActionOnFinish(_activity, (success, messageOrFilename, activity) =>
var task = new CreateNewFilename(App.Kp2a, new ActionOnOperationFinished(App.Kp2a, (success, messageOrFilename, activity) =>
{
if (!success)
{
Toast.MakeText(activity, messageOrFilename, ToastLength.Long).Show();
App.Kp2a.ShowMessage(activity, messageOrFilename, MessageSeverity.Error);
return;
}
SaveFile(new IOConnectionInfo { Path = FileSelectHelper.ConvertFilenameToIocPath(messageOrFilename) });
@@ -115,7 +115,7 @@ namespace keepass2android
}), filename);
new ProgressTask(App.Kp2a, _activity, task).Run();
new BlockingOperationStarter(App.Kp2a, task).Run();
}
return true;

View File

@@ -115,6 +115,7 @@ namespace keepass2android
string keyContent = keyContentTxt.Text;
string toastMsg = null;
MessageSeverity severity = MessageSeverity.Info;
if (!string.IsNullOrEmpty(keyName) && !string.IsNullOrEmpty(keyContent))
{
try
@@ -127,8 +128,10 @@ namespace keepass2android
catch (Exception e)
{
toastMsg = ctx.GetString(Resource.String.private_key_save_failed,
new Java.Lang.Object[] { e.Message });
}
new Java.Lang.Object[] { Util.GetErrorMessage(e)});
severity = MessageSeverity.Error;
}
}
else
{
@@ -136,7 +139,7 @@ namespace keepass2android
}
if (toastMsg!= null) {
Toast.MakeText(_activity, toastMsg, ToastLength.Long).Show();
App.Kp2a.ShowMessage(_activity, toastMsg, severity);
}
UpdatePrivateKeyNames(keyNamesAdapter, fileStorage, ctx);
@@ -153,7 +156,7 @@ namespace keepass2android
int msgId = deleted ? Resource.String.private_key_delete : Resource.String.private_key_delete_failed;
string msg = ctx.GetString(msgId, new Java.Lang.Object[] { keyName });
Toast.MakeText(_activity, msg, ToastLength.Long).Show();
App.Kp2a.ShowMessage(_activity, msg, deleted ? MessageSeverity.Info :MessageSeverity.Error);
UpdatePrivateKeyNames(keyNamesAdapter, fileStorage, ctx);
keySpinner.SetSelection(SftpKeySpinnerCreateNewIdx);
@@ -581,9 +584,9 @@ namespace keepass2android
// Make sure file name exists
if (filename.Length == 0)
{
Toast.MakeText(_activity,
App.Kp2a.ShowMessage(_activity,
Resource.String.error_filename_required,
ToastLength.Long).Show();
MessageSeverity.Error);
return false;
}
@@ -604,9 +607,9 @@ namespace keepass2android
}
catch (NoFileStorageFoundException)
{
Toast.MakeText(_activity,
App.Kp2a.ShowMessage(_activity,
"Unexpected scheme in "+filename,
ToastLength.Long).Show();
MessageSeverity.Error);
return false;
}
@@ -620,9 +623,9 @@ namespace keepass2android
if (parent == null || (parent.Exists() && !parent.IsDirectory))
{
Toast.MakeText(_activity,
App.Kp2a.ShowMessage(_activity,
Resource.String.error_invalid_path,
ToastLength.Long).Show();
MessageSeverity.Error);
return false;
}
@@ -631,9 +634,9 @@ namespace keepass2android
// Create parent dircetory
if (!parent.Mkdirs())
{
Toast.MakeText(_activity,
App.Kp2a.ShowMessage(_activity,
Resource.String.error_could_not_create_parent,
ToastLength.Long).Show();
MessageSeverity.Error);
return false;
}
@@ -643,11 +646,11 @@ namespace keepass2android
}
catch (Java.IO.IOException ex)
{
Toast.MakeText(
App.Kp2a.ShowMessage(
_activity,
_activity.GetText(Resource.String.error_file_not_create) + " "
+ ex.LocalizedMessage,
ToastLength.Long).Show();
MessageSeverity.Error);
return false;
}
@@ -664,10 +667,10 @@ namespace keepass2android
return true;
}
private void IocSelected(Activity activity, IOConnectionInfo ioc)
private void IocSelected(Context context, IOConnectionInfo ioc)
{
if (OnOpen != null)
OnOpen(activity, ioc);
OnOpen(context, ioc);
}
public bool StartFileChooser(string defaultPath)
@@ -700,7 +703,7 @@ namespace keepass2android
_activity.StartActivityForResult(i, _requestCode);
#else
Toast.MakeText(LocaleManager.LocalizedAppContext, "File chooser is excluded!", ToastLength.Long).Show();
App.Kp2a.ShowMessage(LocaleManager.LocalizedAppContext, "File chooser is excluded!", MessageSeverity.Error);
#endif
return true;
}
@@ -778,11 +781,11 @@ namespace keepass2android
}
else
{
var task = new CreateNewFilename(activity, new ActionOnFinish(activity, (success, messageOrFilename, newActivity) =>
var task = new CreateNewFilename(App.Kp2a, new ActionOnOperationFinished(App.Kp2a, (success, messageOrFilename, newActivity) =>
{
if (!success)
{
Toast.MakeText(newActivity, messageOrFilename, ToastLength.Long).Show();
App.Kp2a.ShowMessage(newActivity, messageOrFilename, MessageSeverity.Error);
return;
}
var ioc = new IOConnectionInfo { Path = ConvertFilenameToIocPath(messageOrFilename) };
@@ -790,7 +793,7 @@ namespace keepass2android
}), filename);
new ProgressTask(App.Kp2a, activity, task).Run();
new BlockingOperationStarter(App.Kp2a, task).Run();
}
}

View File

@@ -251,7 +251,7 @@ namespace keepass2android
catch (Exception e)
{
CheckCurrentRadioButton();
Toast.MakeText(this, e.ToString(), ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, e.ToString(), MessageSeverity.Error);
FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Visible;
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;
}

View File

@@ -46,7 +46,9 @@ namespace keepass2android
#endif
{
private readonly int[] _buttonLengthButtonIds = new[] {Resource.Id.btn_length6,
public const string GeneratedPasswordKey = "keepass2android.password.generated_password";
private readonly int[] _buttonLengthButtonIds = new[] {Resource.Id.btn_length6,
Resource.Id.btn_length8,
Resource.Id.btn_length12,
Resource.Id.btn_length16,
@@ -259,7 +261,7 @@ namespace keepass2android
EditText password = (EditText) FindViewById(Resource.Id.password_edit);
Intent intent = new Intent();
intent.PutExtra("keepass2android.password.generated_password", password.Text);
intent.PutExtra(GeneratedPasswordKey, password.Text);
SetResult(KeePass.ResultOkPasswordGenerator, intent);
@@ -543,7 +545,7 @@ namespace keepass2android
}
catch (Exception e)
{
Toast.MakeText(this, e.Message, ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, Util.GetErrorMessage(e), MessageSeverity.Error);
}
return password;

View File

@@ -223,9 +223,9 @@ namespace keepass2android
(o, args) =>
{
//yes
ProgressTask pt = new ProgressTask(App.Kp2a, this,
new AddTemplateEntries(this, App.Kp2a, new ActionOnFinish(this,
(success, message, activity) => ((GroupActivity)activity)?.StartAddEntry())));
BlockingOperationStarter pt = new BlockingOperationStarter(App.Kp2a,
new AddTemplateEntries(App.Kp2a, new ActionInContextInstanceOnOperationFinished(ContextInstanceId, App.Kp2a,
(success, message, context) => (context as GroupActivity)?.StartAddEntry())));
pt.Run();
},
(o, args) =>
@@ -235,7 +235,7 @@ namespace keepass2android
edit.Commit();
//no
StartAddEntry();
},null, this);
},null);
}
else

View File

@@ -43,11 +43,13 @@ using keepass2android;
using KeeTrayTOTP.Libraries;
using AndroidX.AppCompat.Widget;
using Google.Android.Material.Dialog;
using keepass2android.views;
using SearchView = AndroidX.AppCompat.Widget.SearchView;
using AndroidX.Core.Content;
namespace keepass2android
{
public abstract class GroupBaseActivity : LockCloseActivity
public abstract class GroupBaseActivity : LockCloseActivity, IProgressUiProvider
{
public const String KeyEntry = "entry";
public const String KeyMode = "mode";
@@ -56,6 +58,8 @@ namespace keepass2android
public const int RequestCodeActivateRealSearch = 12366;
protected override View? SnackbarAnchorView => FindViewById(Resource.Id.main_content);
static readonly Dictionary<int /*resource id*/, int /*prio*/> bottomBarElementsPriority = new Dictionary<int, int>()
{
{ Resource.Id.cancel_insert_element, 20 },
@@ -199,19 +203,18 @@ namespace keepass2android
new PwUuid(MemUtil.HexStringToByteArray(data.Extras.GetString(GroupEditActivity.KeyCustomIconId)));
String strGroupUuid = data.Extras.GetString(GroupEditActivity.KeyGroupUuid);
GroupBaseActivity act = this;
Handler handler = new Handler();
RunnableOnFinish task;
OperationWithFinishHandler task;
if (strGroupUuid == null)
{
task = AddGroup.GetInstance(this, App.Kp2a, groupName, groupIconId, groupCustomIconId, Group, new RefreshTask(handler, this), false);
task = AddGroup.GetInstance(App.Kp2a, groupName, groupIconId, groupCustomIconId, Group, CreateRefreshAction(), false);
}
else
{
PwUuid groupUuid = new PwUuid(MemUtil.HexStringToByteArray(strGroupUuid));
task = new EditGroup(this, App.Kp2a, groupName, (PwIcon)groupIconId, groupCustomIconId, App.Kp2a.FindGroup(groupUuid),
new RefreshTask(handler, this));
task = new EditGroup(App.Kp2a, groupName, (PwIcon)groupIconId, groupCustomIconId, App.Kp2a.FindGroup(groupUuid),
CreateRefreshAction());
}
ProgressTask pt = new ProgressTask(App.Kp2a, act, task);
BlockingOperationStarter pt = new BlockingOperationStarter(App.Kp2a, task);
pt.Run();
}
@@ -272,6 +275,7 @@ namespace keepass2android
private IMenuItem searchItem;
private IMenuItem searchItemDummy;
private bool isPaused;
private UpdateGroupBaseActivityBroadcastReceiver _dataUpdatedIntentReceiver;
protected override void OnResume()
{
@@ -743,9 +747,10 @@ namespace keepass2android
_dataUpdatedIntentReceiver = new UpdateGroupBaseActivityBroadcastReceiver(this);
IntentFilter filter = new IntentFilter();
filter.AddAction(Intents.DataUpdated);
ContextCompat.RegisterReceiver(this, _dataUpdatedIntentReceiver, filter, (int)ReceiverFlags.Exported);
SetResult(KeePass.ExitNormal);
@@ -922,14 +927,14 @@ namespace keepass2android
var moveElement = new MoveElements(elementsToMove.ToList(), Group, this, App.Kp2a, new ActionOnFinish(this,
(success, message, activity) =>
var moveElement = new MoveElements(elementsToMove.ToList(), Group, App.Kp2a, new ActionInContextInstanceOnOperationFinished(ContextInstanceId, App.Kp2a,
(success, message, context) =>
{
((GroupBaseActivity)activity)?.StopMovingElements();
(context as GroupBaseActivity)?.StopMovingElements();
if (!String.IsNullOrEmpty(message))
Toast.MakeText(activity, message, ToastLength.Long).Show();
App.Kp2a.ShowMessage(context, message, MessageSeverity.Error);
}));
var progressTask = new ProgressTask(App.Kp2a, this, moveElement);
var progressTask = new BlockingOperationStarter(App.Kp2a, moveElement);
progressTask.Run();
}
@@ -1031,6 +1036,13 @@ namespace keepass2android
}
}
protected override void OnDestroy()
{
UnregisterReceiver(_dataUpdatedIntentReceiver);
base.OnDestroy();
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
@@ -1210,7 +1222,7 @@ namespace keepass2android
return true;
case Resource.Id.menu_sync:
new SyncUtil(this).SynchronizeDatabase(() => { });
new SyncUtil(this).StartSynchronizeDatabase(App.Kp2a.CurrentDb.Ioc);
return true;
case Resource.Id.menu_work_offline:
@@ -1221,7 +1233,7 @@ namespace keepass2android
case Resource.Id.menu_work_online:
App.Kp2a.OfflineMode = App.Kp2a.OfflineModePreference = false;
UpdateOfflineModeMenu();
new SyncUtil(this).SynchronizeDatabase(() => { });
new SyncUtil(this).StartSynchronizeDatabase(App.Kp2a.CurrentDb.Ioc);
return true;
case Resource.Id.menu_open_other_db:
AppTask.SetActivityResult(this, KeePass.ExitLoadAnotherDb);
@@ -1291,51 +1303,7 @@ namespace keepass2android
}
public class RefreshTask : OnFinish
{
public RefreshTask(Handler handler, GroupBaseActivity act)
: base(act, handler)
{
}
public override void Run()
{
if (Success)
{
((GroupBaseActivity)ActiveActivity)?.RefreshIfDirty();
}
else
{
DisplayMessage(ActiveActivity);
}
}
}
public class AfterDeleteGroup : OnFinish
{
public AfterDeleteGroup(Handler handler, GroupBaseActivity act)
: base(act, handler)
{
}
public override void Run()
{
if (Success)
{
((GroupBaseActivity)ActiveActivity)?.RefreshIfDirty();
}
else
{
Handler.Post(() =>
{
Toast.MakeText(ActiveActivity ?? LocaleManager.LocalizedAppContext, "Unrecoverable error: " + Message, ToastLength.Long).Show();
});
App.Kp2a.Lock(false);
}
}
}
public bool IsBeingMoved(PwUuid uuid)
{
@@ -1406,6 +1374,79 @@ namespace keepass2android
{
GroupEditActivity.Launch(this, pwGroup.ParentGroup, pwGroup);
}
public IProgressUi ProgressUi
{
get
{
return FindViewById<BackgroundOperationContainer>(Resource.Id.background_ops_container);
}
}
public void OnDataUpdated()
{
if (Group == null || FragmentManager.IsDestroyed)
{
return;
}
var groupId = Group.Uuid;
if (!App.Kp2a.CurrentDb.GroupsById.ContainsKey(groupId))
{
Finish();
return;
}
Group = App.Kp2a.CurrentDb.GroupsById[groupId];
var fragment = FragmentManager.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment);
if (fragment == null)
{
throw new Exception("did not find fragment");
}
fragment.ListAdapter = new PwGroupListAdapter(this, Group);
SetGroupIcon();
SetGroupTitle();
ListAdapter?.NotifyDataSetChanged();
}
public OnOperationFinishedHandler CreateRefreshAction()
{
return new ActionInContextInstanceOnOperationFinished(
ContextInstanceId, App.Kp2a,
(success, message, context) =>
{
if (success)
{
RunOnUiThread(() =>
{
(context as GroupBaseActivity)?.RefreshIfDirty();
});
}
else
{
App.Kp2a.ShowMessage(context, message, MessageSeverity.Error);
}
}
);
}
}
public class UpdateGroupBaseActivityBroadcastReceiver : BroadcastReceiver
{
private readonly GroupBaseActivity _groupBaseActivity;
public UpdateGroupBaseActivityBroadcastReceiver(GroupBaseActivity groupBaseActivity)
{
_groupBaseActivity = groupBaseActivity;
}
public override void OnReceive(Context? context, Intent? intent)
{
if (intent?.Action == Intents.DataUpdated)
{
_groupBaseActivity.OnDataUpdated();
}
}
}
public class GroupListFragment : ListFragment, AbsListView.IMultiChoiceModeListener
@@ -1468,12 +1509,12 @@ namespace keepass2android
{
return false;
}
Handler handler = new Handler();
switch (item.ItemId)
{
case Resource.Id.menu_delete:
DeleteMultipleItems((GroupBaseActivity)Activity, checkedItems, new GroupBaseActivity.RefreshTask(handler, ((GroupBaseActivity)Activity)), App.Kp2a);
DeleteMultipleItems((GroupBaseActivity)Activity, checkedItems, ((GroupBaseActivity)Activity).CreateRefreshAction(), App.Kp2a);
break;
case Resource.Id.menu_move:
var navMove = new NavigateToFolderAndLaunchMoveElementTask(App.Kp2a.CurrentDb, checkedItems.First().ParentGroup, checkedItems.Select(i => i.Uuid).ToList(), ((GroupBaseActivity)Activity).IsSearchResult);
@@ -1481,10 +1522,10 @@ namespace keepass2android
break;
case Resource.Id.menu_copy:
var copyTask = new CopyEntry((GroupBaseActivity)Activity, App.Kp2a, (PwEntry)checkedItems.First(),
new GroupBaseActivity.RefreshTask(handler, ((GroupBaseActivity)Activity)), App.Kp2a.CurrentDb);
var copyTask = new CopyEntry(App.Kp2a, (PwEntry)checkedItems.First(),
((GroupBaseActivity)Activity).CreateRefreshAction(), App.Kp2a.CurrentDb);
ProgressTask pt = new ProgressTask(App.Kp2a, Activity, copyTask);
BlockingOperationStarter pt = new BlockingOperationStarter(App.Kp2a, copyTask);
pt.Run();
break;
@@ -1629,7 +1670,7 @@ namespace keepass2android
}
public void DeleteMultipleItems(GroupBaseActivity activity, List<IStructureItem> checkedItems, OnFinish onFinish, Kp2aApp app)
public void DeleteMultipleItems(GroupBaseActivity activity, List<IStructureItem> checkedItems, OnOperationFinishedHandler onOperationFinishedHandler, Kp2aApp app)
{
if (checkedItems.Any() == false)
return;
@@ -1660,30 +1701,30 @@ namespace keepass2android
}
int dbIndex = 0;
Action<bool, string, Activity> action = null;
action = (success, message, activeActivity) =>
Action<bool, string, Context> action = null;
action = (success, message, context) =>
{
if (success)
{
dbIndex++;
if (dbIndex == itemsForDatabases.Count)
{
onFinish.SetResult(true);
onFinish.Run();
onOperationFinishedHandler.SetResult(true);
onOperationFinishedHandler.Run();
return;
}
new DeleteMultipleItemsFromOneDatabase(activity, itemsForDatabases[dbIndex].Key,
itemsForDatabases[dbIndex].Value, new ActionOnFinish(activeActivity, (b, s, activity1) => action(b, s, activity1)), app)
itemsForDatabases[dbIndex].Value, new ActionOnOperationFinished(App.Kp2a, (b, s, activity1) => action(b, s, activity1)), app)
.Start();
}
else
{
onFinish.SetResult(false, message, true, null);
onOperationFinishedHandler.SetResult(false, message, true, null);
}
};
new DeleteMultipleItemsFromOneDatabase(activity, itemsForDatabases[dbIndex].Key,
itemsForDatabases[dbIndex].Value, new ActionOnFinish(activity, (b, s, activity1) => action(b, s, activity1)), app)
itemsForDatabases[dbIndex].Value, new ActionOnOperationFinished(App.Kp2a, (b, s, activity1) => action(b, s, activity1)), app)
.Start();
}

View File

@@ -111,9 +111,10 @@ namespace keepass2android
SetResult (Result.Ok, intent);
Finish ();
} else {
Toast.MakeText (this, Resource.String.error_no_name, ToastLength.Long).Show ();
}
} else
{
App.Kp2a.ShowMessage(this, Resource.String.error_no_name, MessageSeverity.Error);
}
};
if (Intent.HasExtra(KeyGroupUuid))

View File

@@ -316,7 +316,7 @@ namespace keepass2android
try { ck.AddUserKey(new KcpKeyFile(strAbs)); }
catch (InvalidOperationException)
{
Toast.MakeText(LocaleManager.LocalizedAppContext,Resource.String.error_adding_keyfile,ToastLength.Long).Show();
App.Kp2a.ShowMessage(LocaleManager.LocalizedAppContext,Resource.String.error_adding_keyfile, MessageSeverity.Error);
return false;
}
catch (Exception) { throw; }

View File

@@ -146,7 +146,16 @@ namespace KeeChallenge
{
using (CryptoStream csDecrypt = (CryptoStream)aes.DecryptStream(msDecrypt, key, inf.IV))
{
csDecrypt.Read(secret, 0, secret.Length);
//read the secret from the stream
int totalBytesRead = 0;
int bytesRead = csDecrypt.Read(secret, totalBytesRead, secret.Length - totalBytesRead);
while (bytesRead > 0 && totalBytesRead < secret.Length)
{
totalBytesRead += bytesRead;
bytesRead = csDecrypt.Read(secret, totalBytesRead, secret.Length - totalBytesRead);
}
csDecrypt.Close();
}
msDecrypt.Close();

View File

@@ -25,7 +25,7 @@ using Android.Runtime;
namespace keepass2android
{
public abstract class LifecycleAwareActivity : AndroidX.AppCompat.App.AppCompatActivity
public abstract class LifecycleAwareActivity : AndroidX.AppCompat.App.AppCompatActivity, IContextInstanceIdProvider
{
protected override void AttachBaseContext(Context baseContext)
{
@@ -84,12 +84,11 @@ namespace keepass2android
return baseRes;
}
public Action? OnResumeListener { get; set; }
protected override void OnResume()
{
base.OnResume();
OnResumeListener?.Invoke();
App.Kp2a.PerformPendingActions(_instanceId);
Kp2aLog.Log(ClassName + ".OnResume " + ID);
if (App.Kp2a.CurrentDb == null)
@@ -104,26 +103,38 @@ namespace keepass2android
protected override void OnStart()
{
ProgressTask.SetNewActiveActivity(this);
App.Kp2a.ActiveContext = this;
base.OnStart();
Kp2aLog.Log(ClassName + ".OnStart" + " " + ID);
}
const string ID_KEY = "kp2a_context_instance_id";
const int InvalidId = -1;
private int _instanceId;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
_instanceId = bundle?.GetInt(ID_KEY, InvalidId) ?? InvalidId;
if (_instanceId == InvalidId)
{
_instanceId = _nextContextInstanceId++;
}
OnCreateListener?.Invoke(bundle);
Kp2aLog.Log(ClassName + ".OnCreate" + " " + ID);
Kp2aLog.Log(ClassName + ".OnCreate" + " " + ID + " (instance=" + _instanceId +")");
Kp2aLog.Log(ClassName + ":apptask=" + Intent.GetStringExtra("KP2A_APP_TASK_TYPE") + " " + ID);
}
protected override void OnDestroy()
{
base.OnDestroy();
Kp2aLog.Log(ClassName + ".OnDestroy" + IsFinishing.ToString() + " " + ID);
Kp2aLog.Log(ClassName + ".OnDestroy " + IsFinishing.ToString() + " " + ID);
}
protected override void OnPause()
@@ -136,14 +147,18 @@ namespace keepass2android
{
base.OnStop();
Kp2aLog.Log(ClassName + ".OnStop" + " " + ID);
ProgressTask.RemoveActiveActivity(this);
}
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
outState.PutInt(ID_KEY, _instanceId);
OnSaveInstanceStateListener?.Invoke(outState);
}
static int _nextContextInstanceId = 0;
public int ContextInstanceId => _instanceId;
}
}

View File

@@ -38,7 +38,7 @@ namespace keepass2android
protected const string NoLockCheck = "NO_LOCK_CHECK";
protected IOConnectionInfo _ioc;
private BroadcastReceiver _intentReceiver;
private BroadcastReceiver _lockCloseIntentReceiver;
private ActivityDesign _design;
public LockCloseActivity()
@@ -66,11 +66,11 @@ namespace keepass2android
if (Intent.GetBooleanExtra(NoLockCheck, false))
return;
_intentReceiver = new LockCloseActivityBroadcastReceiver(this);
_lockCloseIntentReceiver = new LockCloseActivityBroadcastReceiver(this);
IntentFilter filter = new IntentFilter();
filter.AddAction(Intents.DatabaseLocked);
filter.AddAction(Intent.ActionScreenOff);
ContextCompat.RegisterReceiver(this, _intentReceiver, filter, (int)ReceiverFlags.Exported);
ContextCompat.RegisterReceiver(this, _lockCloseIntentReceiver, filter, (int)ReceiverFlags.Exported);
}
protected override void OnDestroy()
@@ -79,7 +79,7 @@ namespace keepass2android
{
try
{
UnregisterReceiver(_intentReceiver);
UnregisterReceiver(_lockCloseIntentReceiver);
}
catch (Exception ex)
{

View File

@@ -21,8 +21,10 @@ using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Google.Android.Material.Dialog;
using keepass2android;
using keepass2android.Utils;
namespace keepass2android
{
@@ -76,19 +78,34 @@ namespace keepass2android
base.OnPause();
TimeoutHelper.Pause(this);
}
App.Kp2a.MessagePresenter = new NonePresenter();
}
protected override void OnDestroy()
{
base.OnDestroy();
GC.Collect();
}
protected override void OnResume() {
base.OnResume();
TimeoutHelper.Resume(this);
}
protected override void OnResume()
{
base.OnResume();
TimeoutHelper.Resume(this);
var snackbarAnchorView = SnackbarAnchorView;
if (snackbarAnchorView != null)
{
App.Kp2a.MessagePresenter = new ChainedSnackbarPresenter(snackbarAnchorView);
}
else
{
App.Kp2a.MessagePresenter = new ToastPresenter();
}
}
protected virtual View? SnackbarAnchorView => null;
public const int RequestCodeChallengeYubikey = 793;

View File

@@ -266,6 +266,7 @@ The scheme=file is still there for old OS devices. It's also queried by apps lik
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" android:maxSdkVersion="22" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" android:maxSdkVersion="22" />
<uses-permission android:name="keepass2android.keepass2android_debug.permission.KP2aInternalFileBrowsing" />

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="204"
android:versionName="1.12-r3"
android:versionCode="206"
android:versionName="1.12-r5"
package="keepass2android.keepass2android"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="auto">
@@ -278,6 +278,7 @@ The scheme=file is still there for old OS devices. It's also queried by apps lik
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" android:maxSdkVersion="22" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" android:maxSdkVersion="22" />
<uses-permission android:name="keepass2android.keepass2android.permission.KP2aInternalFileBrowsing" />

View File

@@ -257,6 +257,9 @@ The scheme=file is still there for old OS devices. It's also queried by apps lik
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-feature android:name="android.hardware.camera" android:required="false" />

View File

@@ -145,7 +145,7 @@ namespace keepass2android
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
Toast.MakeText(this, "No Yubikey OTP found!", ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, "No Yubikey OTP found!", MessageSeverity.Error);
Finish();
return;
}

View File

@@ -66,6 +66,7 @@ using Exception = System.Exception;
using String = System.String;
using Toolbar = AndroidX.AppCompat.Widget.Toolbar;
using AndroidX.Core.Content;
using Google.Android.Material.Snackbar;
namespace keepass2android
{
@@ -221,6 +222,7 @@ namespace keepass2android
//StackBaseActivity will launch the next activity
Intent data = new Intent();
data.PutExtra("ioc", IOConnectionInfo.SerializeToString(_ioConnection));
data.PutExtra("requiresSubsequentSync", _lastLoadOperation?.RequiresSubsequentSync == true);
SetResult(Result.Ok, data);
@@ -309,7 +311,7 @@ namespace keepass2android
catch (Exception e)
{
Kp2aLog.Log(e.ToString());
Toast.MakeText(this, "Error: " + e.Message, ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, "Error: " + Util.GetErrorMessage(e), MessageSeverity.Error);
return;
}
@@ -328,8 +330,7 @@ namespace keepass2android
ChallengeInfo temp = _challengeProv.Encrypt(_challengeSecret);
if (!temp.Save(_otpAuxIoc))
{
Toast.MakeText(this, Resource.String.ErrorUpdatingChalAuxFile, ToastLength.Long)
.Show();
App.Kp2a.ShowMessage(this, Resource.String.ErrorUpdatingChalAuxFile, MessageSeverity.Error);
return false;
}
@@ -348,7 +349,7 @@ namespace keepass2android
}
else
{
Toast.MakeText(this, Resource.String.bad_resp, ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, Resource.String.bad_resp, MessageSeverity.Error);
}
}
}
@@ -458,7 +459,7 @@ namespace keepass2android
}
else
{
Toast.MakeText(Activity,GetErrorMessage(), ToastLength.Long).Show();
App.Kp2a.ShowMessage(Activity,GetErrorMessage(), MessageSeverity.Error);
}
return;
@@ -957,7 +958,7 @@ namespace keepass2android
{
btn.SetImageResource(Resource.Drawable.baseline_fingerprint_24);
}, 1300);
Toast.MakeText(this, message, ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, message, MessageSeverity.Error);
}
public void OnBiometricAttemptFailed(string message)
@@ -996,8 +997,12 @@ namespace keepass2android
btn.PostDelayed(() =>
{
//fire
OnOk(true);
//fire if everything else is ready
if (FindViewById(Resource.Id.pass_ok).Enabled)
{
OnOk(true);
}
FindViewById<EditText>(Resource.Id.password_edit).Enabled = true;
}, 500);
@@ -1036,7 +1041,7 @@ namespace keepass2android
if (_appnameclickCount == 6)
{
Kp2aLog.LogUnexpectedError(new Exception("some blabla"));
Toast.MakeText(this, "Once again and the app will crash.", ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, "Once again and the app will crash.", MessageSeverity.Warning);
}
if (_appnameclickCount == 7)
@@ -1123,7 +1128,7 @@ namespace keepass2android
//For security reasons: discard the OTP (otherwise the user might not select a database now and forget
//about the OTP, but it would still be stored in the Intents and later be passed to PasswordActivity again.
Toast.MakeText(this, GetString(Resource.String.otp_discarded_because_no_db), ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, GetString(Resource.String.otp_discarded_because_no_db), MessageSeverity.Warning);
GoToFileSelectActivity();
return false;
}
@@ -1400,7 +1405,7 @@ namespace keepass2android
string errorMessage;
if (!CreateCompositeKey(out compositeKey, out errorMessage)) return (() =>
{
Toast.MakeText(this, errorMessage, ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, errorMessage, MessageSeverity.Warning);
_performingLoad = false;
});
return () => { PerformLoadDatabaseWithCompositeKey(compositeKey); };
@@ -1436,15 +1441,22 @@ namespace keepass2android
MakePasswordMaskedOrVisible();
Handler handler = new Handler();
OnFinish onFinish = new AfterLoad(handler, this, _ioConnection);
LoadDb task = (KeyProviderTypes.Contains(KeyProviders.Otp))
OnOperationFinishedHandler onOperationFinishedHandler = new AfterLoad(handler, this, _ioConnection);
LoadDb loadOperation = (KeyProviderTypes.Contains(KeyProviders.Otp))
? new SaveOtpAuxFileAndLoadDb(App.Kp2a, _ioConnection, _loadDbFileTask, compositeKey, GetKeyProviderString(),
onFinish, this, true, _makeCurrent)
: new LoadDb(this, App.Kp2a, _ioConnection, _loadDbFileTask, compositeKey, GetKeyProviderString(), onFinish,true, _makeCurrent);
onOperationFinishedHandler, this, true, _makeCurrent)
: new LoadDb(App.Kp2a, _ioConnection, _loadDbFileTask, compositeKey, GetKeyProviderString(), onOperationFinishedHandler,true, _makeCurrent);
_loadDbFileTask = null; // prevent accidental re-use
new ProgressTask(App.Kp2a, this, task).Run();
}
_lastLoadOperation = loadOperation;
//Don't use BlockingOperationStarter as that would cancel running operations.
//This is bad when used with AutoOpen: we might get here when one database has loaded and is now synced in the background.
//We don't want to cancel this.
OperationRunner.Instance.Run(App.Kp2a, loadOperation, true);
}
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(new Exception("cannot load database: "+e + ", c: " + (compositeKey != null) + (_ioConnection != null) + (_keyFile != null), e));
@@ -1481,7 +1493,7 @@ namespace keepass2android
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
errorMessage = e.Message;
errorMessage = Util.GetErrorMessage(e);
return false;
}
}
@@ -1566,15 +1578,21 @@ namespace keepass2android
base.OnPause();
}
protected override void OnStart()
private bool hasRequestedKeyboardActivation = false;
private LoadDb _lastLoadOperation;
protected override void OnStart()
{
base.OnStart();
_starting = true;
if (PreferenceManager.GetDefaultSharedPreferences(this)
.GetBoolean(GetString(Resource.String.UseKp2aKeyboardInKp2a_key), false))
{
CopyToClipboardService.ActivateKeyboard(this);
.GetBoolean(GetString(Resource.String.UseKp2aKeyboardInKp2a_key), false)
&& !hasRequestedKeyboardActivation)
{
hasRequestedKeyboardActivation = true;
CopyToClipboardService.ActivateKeyboard(this);
}
DonateReminder.ShowDonateReminderIfAppropriate(this);
@@ -1668,7 +1686,7 @@ namespace keepass2android
//did we find a field?
if (!foundEmptyField)
{
Toast.MakeText(this, GetString(Resource.String.otp_discarded_no_space), ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, GetString(Resource.String.otp_discarded_no_space), MessageSeverity.Error);
}
}
@@ -1729,80 +1747,121 @@ namespace keepass2android
UsedFingerprintUnlock = false;
}
protected override View? SnackbarAnchorView => FindViewById(Resource.Id.main_content);
protected override void OnResume()
{
base.OnResume();
_activityDesign.ReapplyTheme();
{
base.OnResume();
_activityDesign.ReapplyTheme();
Kp2aLog.Log("starting: " + _starting + ", Finishing: " + IsFinishing + ", _performingLoad: " + _performingLoad);
Kp2aLog.Log("starting: " + _starting + ", Finishing: " + IsFinishing + ", _performingLoad: " +
_performingLoad);
CheckBox cbOfflineMode = (CheckBox)FindViewById(Resource.Id.work_offline);
App.Kp2a.OfflineMode = cbOfflineMode.Checked = App.Kp2a.OfflineModePreference; //this won't overwrite new user settings because every change is directly saved in settings
LinearLayout offlineModeContainer = FindViewById<LinearLayout>(Resource.Id.work_offline_container);
var cachingFileStorage = App.Kp2a.GetFileStorage(_ioConnection) as CachingFileStorage;
if ((cachingFileStorage != null) && cachingFileStorage.IsCached(_ioConnection))
{
offlineModeContainer.Visibility = ViewStates.Visible;
}
else
{
offlineModeContainer.Visibility = ViewStates.Gone;
App.Kp2a.OfflineMode = false;
}
CheckBox cbOfflineMode = (CheckBox)FindViewById(Resource.Id.work_offline);
App.Kp2a.OfflineMode =
cbOfflineMode.Checked =
App.Kp2a
.OfflineModePreference; //this won't overwrite new user settings because every change is directly saved in settings
CheckBox cbSyncInBackground = (CheckBox)FindViewById(Resource.Id.sync_in_background)!;
cbSyncInBackground.Checked = App.Kp2a.SyncInBackgroundPreference;
UpdateInternalCacheCheckboxesVisibility();
View killButton = FindViewById(Resource.Id.kill_app);
if (PreferenceManager.GetDefaultSharedPreferences(this)
.GetBoolean(GetString(Resource.String.show_kill_app_key), false))
{
killButton.Click += (sender, args) =>
{
_killOnDestroy = true;
View killButton = FindViewById(Resource.Id.kill_app);
if (PreferenceManager.GetDefaultSharedPreferences(this)
.GetBoolean(GetString(Resource.String.show_kill_app_key), false))
{
killButton.Click += (sender, args) =>
{
_killOnDestroy = true;
SetResult(Result.Canceled);
Finish();
Finish();
};
killButton.Visibility = ViewStates.Visible;
};
killButton.Visibility = ViewStates.Visible;
}
else
{
killButton.Visibility = ViewStates.Gone;
}
}
else
{
killButton.Visibility = ViewStates.Gone;
}
TryGetOtpFromClipboard();
TryGetOtpFromClipboard();
if (!_keepPasswordInOnResume)
{
if (
_lastOnPauseTime < DateTime.Now - TimeSpan.FromSeconds(5) //only clear when user left the app for more than 5 seconds (allows to use Yubiclip, also allows to switch shortly to another app)
&&
PreferenceManager.GetDefaultSharedPreferences(this)
.GetBoolean(GetString(Resource.String.ClearPasswordOnLeave_key), true))
{
ClearEnteredPassword();
}
if (!_keepPasswordInOnResume)
{
if (
_lastOnPauseTime <
DateTime.Now -
TimeSpan.FromSeconds(
5) //only clear when user left the app for more than 5 seconds (allows to use Yubiclip, also allows to switch shortly to another app)
&&
PreferenceManager.GetDefaultSharedPreferences(this)
.GetBoolean(GetString(Resource.String.ClearPasswordOnLeave_key), true))
{
ClearEnteredPassword();
}
}
}
_keepPasswordInOnResume = false;
_keepPasswordInOnResume = false;
MakePasswordMaskedOrVisible();
MakePasswordMaskedOrVisible();
UpdateOkButtonState();
UpdateOkButtonState();
if (KeyProviderTypes.Contains(KeyProviders.Challenge))
{
FindViewById(Resource.Id.otpInitView).Visibility = _challengeSecret == null ? ViewStates.Visible : ViewStates.Gone;
}
if (KeyProviderTypes.Contains(KeyProviders.Challenge))
{
FindViewById(Resource.Id.otpInitView).Visibility =
_challengeSecret == null ? ViewStates.Visible : ViewStates.Gone;
}
/*
Snackbar snackbar = Snackbar
.Make(FindViewById(Resource.Id.main_content),
"snack snack snack snack snack snack snack snack snack snack snack snack snack snack snacksnack snack snacksnack snack snacksnack snack snack snack snack snack snack snack snack snack snack snack snack snack snack snacksnack snack snacksnack snack snacksnack snack snack snack snack snacksnack snack snack ",
Snackbar.LengthLong);
snackbar.SetTextMaxLines(5);
snackbar.SetBackgroundTint(GetColor(Resource.Color.md_theme_secondaryContainer));
snackbar.SetTextColor(GetColor(Resource.Color.md_theme_onSecondaryContainer));
snackbar.SetAction("dismiss",
view => snackbar.SetBackgroundTint(GetColor(Resource.Color.md_theme_surfaceContainer)));
//use !IsFinishing to make sure we're not starting another activity when we're already finishing (e.g. due to TaskComplete in OnActivityResult)
//use !performingLoad to make sure we're not already loading the database (after ActivityResult from File-Prepare-Activity; this would cause _loadDbFileTask to exist when we reload later!)
if ( !IsFinishing && !_performingLoad)
snackbar.Show();
new Handler().PostDelayed(() =>
{
Snackbar snackbar2 = Snackbar
.Make(FindViewById(Resource.Id.main_content), "snack snack snack ",
Snackbar.LengthLong);
snackbar2.SetTextMaxLines(5);
snackbar2.SetBackgroundTint(GetColor(Resource.Color.md_theme_errorContainer));
snackbar2.SetTextColor(GetColor(Resource.Color.md_theme_onErrorContainer));
snackbar2.Show();
}, 1500);
new Handler().PostDelayed(() =>
{
Snackbar snackbar2 = Snackbar
.Make(FindViewById(Resource.Id.main_content), "snack snack warn ",
Snackbar.LengthLong);
snackbar2.SetTextMaxLines(5);
snackbar2.SetBackgroundTint(GetColor(Resource.Color.md_theme_inverseSurface));
snackbar2.SetTextColor(GetColor(Resource.Color.md_theme_inverseOnSurface));
snackbar2.Show();
}, 2500);*/
//use !IsFinishing to make sure we're not starting another activity when we're already finishing (e.g. due to TaskComplete in OnActivityResult)
//use !performingLoad to make sure we're not already loading the database (after ActivityResult from File-Prepare-Activity; this would cause _loadDbFileTask to exist when we reload later!)
if ( !IsFinishing && !_performingLoad)
{
@@ -1835,13 +1894,14 @@ namespace keepass2android
// to retry with typing the full password, but that's intended to avoid showing the password to a
// a potentially unauthorized user (feature request https://keepass2android.codeplex.com/workitem/274)
Handler handler = new Handler();
OnFinish onFinish = new AfterLoad(handler, this, _ioConnection);
OnOperationFinishedHandler onOperationFinishedHandler = new AfterLoad(handler, this, _ioConnection);
_performingLoad = true;
LoadDb task = new LoadDb(this, App.Kp2a, _ioConnection, _loadDbFileTask, compositeKeyForImmediateLoad, GetKeyProviderString(),
onFinish, false, _makeCurrent);
LoadDb loadOperation = new LoadDb(App.Kp2a, _ioConnection, _loadDbFileTask, compositeKeyForImmediateLoad, GetKeyProviderString(),
onOperationFinishedHandler, false, _makeCurrent);
_loadDbFileTask = null; // prevent accidental re-use
new ProgressTask(App.Kp2a, this, task).Run();
compositeKeyForImmediateLoad = null; //don't reuse or keep in memory
_lastLoadOperation = loadOperation;
OperationRunner.Instance.Run(App.Kp2a, loadOperation, true);
compositeKeyForImmediateLoad = null; //don't reuse or keep in memory
}
else
@@ -1954,7 +2014,7 @@ namespace keepass2android
btn.Tag = error;
Toast.MakeText(this, Resource.String.fingerprint_reenable2, ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, Resource.String.fingerprint_reenable2, MessageSeverity.Error);
_biometricDec = null;
return false;
@@ -1983,10 +2043,38 @@ namespace keepass2android
{
App.Kp2a.OfflineModePreference = App.Kp2a.OfflineMode = args.IsChecked;
};
}
private String LoadKeyProviderStringForIoc(String filename) {
CheckBox cbSyncInBackground = (CheckBox)FindViewById(Resource.Id.sync_in_background);
cbSyncInBackground.CheckedChange += (sender, args) =>
{
App.Kp2a.SyncInBackgroundPreference = args.IsChecked;
UpdateInternalCacheCheckboxesVisibility();
};
}
private void UpdateInternalCacheCheckboxesVisibility()
{
LinearLayout syncInBackgroundContainer = FindViewById<LinearLayout>(Resource.Id.sync_in_background_container)!;
LinearLayout offlineModeContainer = FindViewById<LinearLayout>(Resource.Id.work_offline_container)!;
var cachingFileStorage = App.Kp2a.GetFileStorage(_ioConnection) as CachingFileStorage;
if ((cachingFileStorage != null) && cachingFileStorage.IsCached(_ioConnection))
{
syncInBackgroundContainer.Visibility = ViewStates.Visible;
offlineModeContainer.Visibility =
App.Kp2a.SyncInBackgroundPreference ? ViewStates.Gone : ViewStates.Visible;
}
else
{
syncInBackgroundContainer.Visibility = offlineModeContainer.Visibility = ViewStates.Gone;
App.Kp2a.OfflineMode = false;
}
}
private String LoadKeyProviderStringForIoc(String filename) {
if ( _rememberKeyfile ) {
string keyfile = App.Kp2a.FileDbHelper.GetKeyFileForFile(filename);
if (String.IsNullOrEmpty(keyfile))
@@ -2024,7 +2112,7 @@ namespace keepass2android
/*
private void errorMessage(CharSequence text)
{
Toast.MakeText(this, text, ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, text, MessageSeverity.Error);
}
*/
@@ -2054,11 +2142,11 @@ namespace keepass2android
Finish();
}
private class AfterLoad : OnFinish {
private class AfterLoad : OnOperationFinishedHandler {
readonly PasswordActivity _act;
private readonly IOConnectionInfo _ioConnection;
public AfterLoad(Handler handler, PasswordActivity act, IOConnectionInfo ioConnection):base(act, handler)
public AfterLoad(Handler handler, PasswordActivity act, IOConnectionInfo ioConnection):base(App.Kp2a, handler)
{
_act = act;
_ioConnection = ioConnection;
@@ -2084,7 +2172,9 @@ namespace keepass2android
_act.LoadingErrorCount++;
}
if ((Exception != null) && (Exception.Message == KeePassLib.Resources.KLRes.FileCorrupted))
if ((Exception != null) && (Exception.Message == KeePassLib.Resources.KLRes.FileCorrupted))
{
Message = _act.GetString(Resource.String.CorruptDatabaseHelp);
}
@@ -2150,7 +2240,8 @@ namespace keepass2android
}
else
{
DisplayMessage(_act);
MessageSeverity severity = Success ? MessageSeverity.Info : MessageSeverity.Error;
App.Kp2a.ShowMessage(_act, Message, severity);
if (Success)
{
_act.LaunchNextActivity();
@@ -2162,7 +2253,7 @@ namespace keepass2android
if (!Success)
_act.InitFingerprintUnlock();
_act._lastLoadOperation = null;
_act._performingLoad = false;
}
@@ -2196,7 +2287,7 @@ namespace keepass2android
private readonly PasswordActivity _act;
public SaveOtpAuxFileAndLoadDb(IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey, string keyfileOrProvider, OnFinish finish, PasswordActivity act, bool updateLastUsageTimestamp, bool makeCurrent) : base(act, app, ioc, databaseData, compositeKey, keyfileOrProvider, finish,updateLastUsageTimestamp,makeCurrent)
public SaveOtpAuxFileAndLoadDb(IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey, string keyfileOrProvider, OnOperationFinishedHandler operationFinishedHandler, PasswordActivity act, bool updateLastUsageTimestamp, bool makeCurrent) : base(app, ioc, databaseData, compositeKey, keyfileOrProvider, operationFinishedHandler,updateLastUsageTimestamp,makeCurrent)
{
_act = act;
}
@@ -2218,7 +2309,7 @@ namespace keepass2android
{
Kp2aLog.LogUnexpectedError(e);
ShowError( _act.GetString(Resource.String.ErrorUpdatingOtpAuxFile) + " " + e.Message);
ShowError( _act.GetString(Resource.String.ErrorUpdatingOtpAuxFile) + " " + Util.GetErrorMessage(e));
}
@@ -2234,7 +2325,7 @@ namespace keepass2android
private void ShowError(string message)
{
App.Kp2a.ShowToast(message);
App.Kp2a.ShowToast(message, MessageSeverity.Error);
}
}
private class PasswordActivityBroadcastReceiver : BroadcastReceiver

View File

@@ -128,11 +128,11 @@ namespace keepass2android
Kp2aLog.LogUnexpectedError(e);
}
if (String.IsNullOrEmpty(_requestedUrl))
Toast.MakeText(this, GetString(Resource.String.query_credentials, new Java.Lang.Object[] {pluginDisplayName}), ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, GetString(Resource.String.query_credentials, new Java.Lang.Object[] {pluginDisplayName}), MessageSeverity.Info);
else
Toast.MakeText(this,
App.Kp2a.ShowMessage(this,
GetString(Resource.String.query_credentials_for_url,
new Java.Lang.Object[] { pluginDisplayName, _requestedUrl }), ToastLength.Long).Show(); ;
new Java.Lang.Object[] { pluginDisplayName, _requestedUrl }), MessageSeverity.Info); ;
}
private void StartQuery()

View File

@@ -35,6 +35,7 @@ using KeePassLib;
using KeePassLib.Serialization;
using Toolbar = AndroidX.AppCompat.Widget.Toolbar;
using AndroidX.Core.Content;
using keepass2android.Utils;
namespace keepass2android
{
@@ -203,7 +204,7 @@ namespace keepass2android
btn.SetImageResource(Resource.Drawable.baseline_fingerprint_24);
}, 1300);
Toast.MakeText(this, message, ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, message, MessageSeverity.Error);
}
@@ -325,7 +326,7 @@ namespace keepass2android
{
Kp2aLog.Log("QuickUnlock not successful!");
App.Kp2a.Lock(false);
Toast.MakeText(this, GetString(Resource.String.QuickUnlock_fail), ToastLength.Long).Show();
App.Kp2a.ShowMessage(this, GetString(Resource.String.QuickUnlock_fail), MessageSeverity.Error);
Finish();
}
@@ -338,10 +339,10 @@ namespace keepass2android
if (PreferenceManager.GetDefaultSharedPreferences(this)
.GetBoolean(GetString(Resource.String.SyncAfterQuickUnlock_key), false))
{
new SyncUtil(this).SynchronizeDatabase(Finish);
new SyncUtil(this).StartSynchronizeDatabase(App.Kp2a.CurrentDb.Ioc);
}
else
Finish();
Finish();
@@ -383,8 +384,9 @@ namespace keepass2android
{
base.OnResume();
_design.ReapplyTheme();
CheckIfUnloaded();
App.Kp2a.MessagePresenter = new ChainedSnackbarPresenter(FindViewById(Resource.Id.main_content));
CheckIfUnloaded();
InitFingerprintUnlock();
@@ -449,7 +451,8 @@ namespace keepass2android
protected override void OnPause()
{
if (_biometryIdentifier != null)
App.Kp2a.MessagePresenter = new NonePresenter();
if (_biometryIdentifier != null)
{
Kp2aLog.Log("FP: Stop listening");
_biometryIdentifier.StopListening();

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/background_ops_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/md_theme_surfaceVariant"
android:orientation="vertical"
>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<LinearLayout
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="@+id/background_ops_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="0dp"
android:layout_marginBottom="3dp"
android:text="" />
<TextView
android:id="@+id/background_ops_submessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="0dp"
android:layout_marginBottom="3dp"
android:textSize="12sp"
android:text="" />
</LinearLayout>
<Button
android:id="@+id/cancel_background"
style="?attr/materialIconButtonStyle"
app:icon="@drawable/baseline_close_24"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="6dip"
android:layout_margin="6dip"
android:layout_weight="0"
/>
</LinearLayout>
</LinearLayout>

View File

@@ -1,5 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<ScrollView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/entry_scroll"
android:background="?android:attr/colorBackground"
@@ -267,4 +273,5 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
</ScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -43,7 +43,6 @@
style="@style/EntryEditSingleLine_EditText"
android:layout_marginRight="0dip" />
</com.google.android.material.textfield.TextInputLayout>
>
<CheckBox
android:id="@+id/protection"

View File

@@ -75,6 +75,11 @@ android:layout_height="wrap_content">
android:layout_marginRight="0dip"
android:visibility="gone"
/>
<CheckBox
android:id="@+id/protection"
android:layout_width="0dip"
android:layout_height="0dip"
android:visibility="gone"/>
</LinearLayout>
</RelativeLayout>

View File

@@ -4,18 +4,35 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<LinearLayout android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<keepass2android.views.BackgroundOperationContainer
android:visibility="gone"
android:id="@+id/background_ops_container"
android:layout_below="@id/top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<ScrollView
android:id="@+id/entry_scroll"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true"
android:background="?android:attr/colorBackground"
android:scrollbarStyle="insideOverlay">
<keepass2android.view.EntryContentsView
android:id="@+id/entry_contents"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
</ScrollView>
</LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/entry_edit"
android:layout_width="wrap_content"

View File

@@ -354,13 +354,22 @@
android:layout_height="1dp"
android:layout_above="@id/bottom_bar"
android:background="#b8b8b8" />
<keepass2android.views.BackgroundOperationContainer
android:visibility="gone"
android:id="@+id/background_ops_container"
android:layout_below="@id/top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/divider2"
android:layout_below="@id/top"
android:layout_below="@id/background_ops_container"
android:fitsSystemWindows="true">
<fragment
android:name="keepass2android.GroupListFragment"

View File

@@ -318,6 +318,30 @@
android:text="@string/help_quickunlock"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/sync_in_background_container"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="@+id/sync_in_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/SyncOfflineCacheInBackground_title" />
<keepass2android.views.Kp2aShortHelpView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/TextAppearance_Help_Dense"
app:help_text="@string/SyncOfflineCacheInBackground_summary"
app:title_text="@string/SyncOfflineCacheInBackground_title"
android:text="@string/SyncOfflineCacheInBackground_summary"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/work_offline_container"
android:orientation="horizontal"
@@ -328,7 +352,7 @@
android:id="@+id/work_offline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/UseOfflineMode" />
<keepass2android.views.Kp2aShortHelpView
android:layout_width="wrap_content"
@@ -341,6 +365,7 @@
/>
</LinearLayout>
<Button
android:id="@+id/kill_app"
android:text="@string/kill_app_label"

View File

@@ -1,4 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
@@ -14,6 +19,7 @@
android:text="@string/switch_ime_text" />
<Button
android:id="@+id/btn_reopen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -25,4 +31,51 @@
android:layout_height="wrap_content"
android:text="@string/cancel" />
<LinearLayout android:orientation="vertical"
android:paddingTop="64dp"
android:id="@+id/settings_notes_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:paddingTop="12dp"
android:id="@+id/note_kp2a_switch_rooted"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/autoswitch_enabled_but_not_setup"
/>
<TextView
android:paddingTop="12dp"
android:id="@+id/note_AutoFillTotp_prefs_ActivateKeyboard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/switch_keyboard_for_totp_enabled"
/>
<TextView
android:paddingTop="12dp"
android:id="@+id/note_UseKp2aKeyboardInKp2a"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/switch_keyboard_inside_kp2a_enabled"
/>
<TextView
android:id="@+id/note_OpenKp2aKeyboardAutomatically"
android:paddingTop="12dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/switch_keyboard_on_search_enabled"
/>
<Button
style="?attr/materialButtonOutlinedStyle"
android:id="@+id/btn_open_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/IconVisibilityInfo_Android8_btnSettings" />
</LinearLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -546,6 +546,7 @@
<string name="filestoragename_dropboxKP2A">Dropbox (KP2A složka)</string>
<string name="filestoragehelp_dropboxKP2A">Pokud nechcete, aby měl KP2A plný přístup k vašemu Dropbox účtu, může vybrat tuto možnost. Bude vyžadován pouze přístup ke složce Apps/Keepass2Android. Toto je vhodné zejména při vytváření nové databáze. Pokud již máte databázi, zvolte na tuto možnost pro vytvoření složky, poté umístěte soubor (z PC) do této složky a zvolte tuto možnost znovu pro otevření souboru.</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragehelp_gdrive">Upozornění: Společnost Google omezuje přístup k Disku Google z aplikací pro stále více uživatelů. Pokud vestavěná implementace Disku Google nefunguje, použijte místo toho systémový výběr souborů a vyberte Disk Google tam!</string>
<string name="filestoragename_gdriveKP2A">Google Drive (KP2A soubory)</string>
<string name="filestoragehelp_gdriveKP2A">Nechcete-li dát aplikaci KeePass2Android přístup k celému vašemu Google Drive, použijte tuto možnost. Upozorňujeme, že nejprve musíte vytvořit soubor s databází položek, existující soubory nejsou viditelné pro aplikaci. Toto můžete udělat z obrazovky Vytvořit databázi, nebo exportem právě otevřené databáze, volbou této možnosti</string>
<string name="filestoragename_pcloud">PCloud (složka KP2A)</string>
@@ -723,6 +724,7 @@
<item>Aktualizováno na uživatelské rozhraní Material 3</item>
<item>Vylepšení automatického vyplňování pro práci s aplikacemi Compose</item>
<item>Oprava přiřazování názvů hostitelů v automatickém vyplňování a vyhledávání</item>
<item>Oprava problému s generátorem hesel</item>
</string-array>
<string-array name="ChangeLog_1_12_net">
<item>Aktualizováno OneDrive SDK na verzi 5.68</item>
@@ -1165,5 +1167,6 @@ První veřejné vydání
<string name="AutofillWarning_trustAsBrowser">Přijmout vždy v \"%1$s\"</string>
<string name="kp2a_switch_on_sendgodone">Přepnout zpět po dokončení</string>
<string name="kp2a_switch_on_sendgodone_summary">Přepnout zpět při stisknutí tlačítka odeslat/pokračovat/ukončit</string>
<string name="qr_scanning_error_no_google_play_services">Skenování QR kódu vyžaduje služby Google Play. Nainstalujte nebo aktualizujte služby Google Play ve svém zařízení.</string>
<string name="english_ime_settings">Nastavení klávesnice Keepass2Android</string>
</resources>

View File

@@ -642,7 +642,7 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="FileReadOnlyTitle">Datenbank ist schreibgeschützt</string>
<string name="FileReadOnlyMessagePre">Keepass2Android hat die aktuelle Datenbank im schreibgeschützten Modus geöffnet.</string>
<string name="ReadOnlyReason_PreKitKat">Es scheint, dass die Datei von einer externen Anwendung aus geöffnet wurde. Über diesen Weg ist das Schreiben nicht möglich. Wenn Änderungen an der Datenbank vorgenommen werden sollen, bitte die Datenbank schließen und „Datenbank wechseln“ wählen. Danach die Datei über eine der verfügbaren Optionen öffnen, sofern möglich.</string>
<string name="ReadOnlyReason_ReadOnlyFlag">Datei ist schreibgeschützt. Diese Attribut entfernen, wenn Änderungen an der Datenbank vorgenommen werden sollen.</string>
<string name="ReadOnlyReason_ReadOnlyFlag">Datei ist schreibgeschützt. Dieses Attribut entfernen, wenn Änderungen an der Datenbank vorgenommen werden sollen.</string>
<string name="ReadOnlyReason_ReadOnlyKitKat">Das Speichern ist aufgrund von Einschränkungen, die in Android KitKat eingeführt wurden, nicht möglich. Wenn Änderungen an der Datenbank vorgenommen werden sollen, bitte die Datenbank schließen und „Datenbank wechseln“ wählen. Danach die Datei über die systeminterne Dateiauswahl öffnen.</string>
<string name="ReadOnlyReason_LocalBackup">Lokale Sicherungskopien können nicht bearbeitet werden. Du kannst über \"Datenbank-Einstellungen\" - \"Datenbank exportieren\" dieses Backup an einen anderen Ort exportieren und dann von dort öffnen. Dann kannst du auch wieder Änderungen vornehmen.</string>
<string name="AddCustomIcon">Icon aus Datei hinzufügen...</string>
@@ -716,6 +716,20 @@ Anbei einige Tipps, die bei der Diagnose des Problems helfen können:\n
<string name="EntryChannel_desc">Benachrichtigung zum schnellen Zugriff auf den aktuell gewählten Eintrag.</string>
<string name="CloseDbAfterFailedAttempts">Datenbank nach drei fehlgeschlagenen biometrischen Entsperrversuchen schließen.</string>
<string name="WarnFingerprintInvalidated">Achtung! Die biometrische Authentifizierung kann von Android ungültig gemacht werden, z. B. nach dem Hinzufügen eines neuen Fingerabdrucks in den Geräteeinstellungen. Bitte sicherstellen, dass jederzeit klar ist, wie mit dem eigenen Hauptpasswort entsperrt werden kann!</string>
<string-array name="ChangeLog_1_12">
<item>Upgraded from Xamarin Android to .net 8</item>
<item>Upgrade auf Target-SDK 34</item>
<item>Upgraded to Material 3 user interface</item>
<item>Improve autofill to work with Compose apps</item>
<item>Fix hostname matching in autofill and search</item>
<item>Fix issue with password generator</item>
</string-array>
<string-array name="ChangeLog_1_12_net">
<item>OneDrive SDK auf Version 5.68 aktualisiert</item>
<item>Dropbox SDK auf Version 7.0.0 aktualisiert</item>
<item>Gradle, NewtonsoftJson, FluentFTP, MegaApiClient und okhttp aktualisiert</item>
<item>Bugfix in WebDav-Dateiauswahl</item>
</string-array>
<string-array name="ChangeLog_1_11">
<item>Buttons für die Suche und eine TOTP-Übersicht hinzugefügt (falls TOTP-Einträge vorhanden sind)</item>
<item>Darstellung von TOTP-Feldern verbessert: Timeout-Anzeige hinzugefügt und besser sichtbar gemacht.</item>
@@ -1146,5 +1160,6 @@ Erstes öffentliches Release</string>
<string name="AutofillWarning_trustAsBrowser">Für „%1$s“ immer akzeptieren</string>
<string name="kp2a_switch_on_sendgodone">Tastatur zurückwechseln wenn fertig</string>
<string name="kp2a_switch_on_sendgodone_summary">Beim Drücken von Senden/Los/Fertig zurückschalten</string>
<string name="qr_scanning_error_no_google_play_services">QR-Code-Scannen erfordert Google Play-Dienste. Bitte installiere oder aktualisiere Google Play-Dienste auf deinem Gerät.</string>
<string name="english_ime_settings">Android-Tastatureinstellungen</string>
</resources>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="about_feedback">Σχόλια</string>
<string name="AboutText">Το KP2A είναι ένας διαχειριστής συνθηματικών, που παρέχει την δυνατότητα ανάγνωσης/εγγραφής σε βάσεις δεδομένων του KeePass 2.x στο Android.</string>
<string name="AboutText">Το KP2A είναι ένας διαχειριστής συνθηματικών, που παρέχει δυνατότητα ανάγνωσης / εγγραφής σε βάσεις δεδομένων του KeePass 2.x στο Android.</string>
<string name="CreditsText">Το περιβάλλον εργασίας χρήστη βασίζεται σε μια έκδοση του KeepassDroid που αναπτύχθηκε από τον Brian Pellin. Ο κώδικας για τις λειτουργίες της βάσης δεδομένων βασίζεται στο KeePass του Dominik Reichl. Το ρομπότ Android αναπαράγεται ή τροποποιείται από εργασία που δημιουργήθηκε και διαμοιράστηκε από την Google και χρησιμοποιείται σύμφωνα με τους όρους που περιγράφονται στο το Creative Commons 3.0 Attribution License.</string>
<string name="CreditsTextSFTP">Το SFTP υποστηρίζεται με χρήση της βιβλιοθήκης JSch με άδεια BSD που δημιουργήθηκε από τη JCraft, Inc.</string>
<string name="CreditsIcons">Το εικονίδιο Σφυρί δημιουργήθηκε από τον John Caserta, το εικονίδιο Πιγκουίνος δημιουργήθηκε από τον Adriano Emerick, το εικονίδιο Φτερό δημιουργήθηκε από τον Jon Testa και το εικονίδιο της Apple δημιουργήθηκε από την Ava Rowell. Όλα τα προηγούμενα εικονίδια είναι από το Noun Project. Το εικονίδιο της Εικόνας είναι από το https://icons8.com/icon/5570/Picture.</string>
@@ -401,6 +401,14 @@
<string name="ShowSeparateNotifications_summary">Εμφάνιση χωριστών ειδοποιήσεων για την αντιγραφή του ονόματος χρήστη και του συνθηματικού στο πρόχειρο και ενεργοποίηση του πληκτρολογίου.</string>
<string name="AccServiceAutoFill_prefs">Αυτόματη συμπλήρωση υπηρεσίας προσβασιμότητας</string>
<string name="AutoFill_prefs">Λειτουργία αυτόματης συμπλήρωσης</string>
<string name="AutoFillTotp_prefs_ShowNotification_summary">Κατά την αυτόματη συμπλήρωση μιας καταχώρισης με TOTP, εμφανίστε την ειδοποίηση με ένα κουμπί Αντιγραφή TOTP</string>
<string name="AutoFillTotp_prefs_ShowNotification_title">Εμφάνιση ειδοποίησης καταχώρισης</string>
<string name="AutoFillTotp_prefs_title">Αυτόματη συμπλήρωση για καταχωρίσεις TOTP</string>
<string name="AutoFillTotp_prefs_CopyTotpToClipboard_title">Αντιγραφή TOTP στο πρόχειρο</string>
<string name="AutoFillTotp_prefs_CopyTotpToClipboard_summary">Κατά την αυτόματη συμπλήρωση μιας καταχώρισης με TOTP, αντιγράφει το TOTP στο πρόχειρο</string>
<string name="AutoFillTotp_prefs_ActivateKeyboard_summary">Κατά την αυτόματη συμπλήρωση μιας καταχώρισης με TOTP, ενεργοποιήστε το ενσωματωμένο πληκτρολόγιο. Το πληκτρολόγιο έχει ένα κουμπί TOTP.</string>
<string name="AutoFillTotp_prefs_ActivateKeyboard_title">Ενεργοποίηση ενσωματωμένου πληκτρολογίου</string>
<string name="TotpCopiedToClipboard">Αντιγράφηκε το TOTP στο πρόχειρο</string>
<string name="ShowKp2aKeyboardNotification_title">Ειδοποίηση πληκτρολογίου KP2A</string>
<string name="ShowKp2aKeyboardNotification_summary">Κάντε προσβάσιμη την πλήρη καταχώριση μέσω του πληκτρολογίου KP2A (συνιστάται).</string>
<string name="OpenKp2aKeyboardAutomatically_title">Εναλλαγή πληκτρολογίου</string>
@@ -538,6 +546,7 @@
<string name="filestoragename_dropboxKP2A">Dropbox (φάκελος KP2A)</string>
<string name="filestoragehelp_dropboxKP2A">Αν δεν θέλεις να δώσεις στο Keepass2Android πλήρη πρόσβαση στο Dropbox, μπορείς να διαλέξεις αυτή την επιλογή. Θα ζητήσει πρόσβαση μόνο στο φάκελο Apps/Keepass2Android. Ταιριάζει ειδικά στη δημιουργία νέας βάσης δεδομένων. Αν ήδη έχεις μια βάση δεδομένων, διάλεξε αυτή την επιλογή για να δημιουργήσεις το φάκελο, μετά τοποθέτησε το αρχείο σου μέσα στο φάκελο (από το PC) και μετά διάλεξε αυτή την επιλογή πάλι για να ανοίξεις το αρχείο. </string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragehelp_gdrive">Παρακαλώ σημειώστε: Η Google περιορίζει την πρόσβαση στο Google Drive από εφαρμογές για όλο και περισσότερους χρήστες. Εάν η ενσωματωμένη υλοποίηση του Google Drive δεν λειτουργεί, χρησιμοποιήστε τον επιλογέα αρχείων συστήματος και επιλέξτε το Google Drive εκεί!</string>
<string name="filestoragename_gdriveKP2A">Google Drive (αρχεία KP2A)</string>
<string name="filestoragehelp_gdriveKP2A">Αν δεν θέλετε να δώσετε πρόσβαση KP2A πρόσβαση σε όλο το Google Drive σας, μπορείτε να επιλέξετε αυτή την επιλογή. Σημειώστε ότι πρέπει να δημιουργήσετε ένα αρχείο βάσης δεδομένων πρώτα - τα υπάρχοντα αρχεία δεν είναι ορατά στην εφαρμογή. Επιλέξτε αυτή την επιλογή από την οθόνη Δημιουργία βάσης δεδομένων ή, αν έχετε ήδη ανοίξει μια βάση δεδομένων, με την εξαγωγή της βάσης δεδομένων επιλέγοντας αυτή την επιλογή.</string>
<string name="filestoragename_pcloud">PCloud (φάκελος KP2A)</string>
@@ -588,6 +597,7 @@
<string name="CouldntLoadChalAuxFile_Hint">Χρησιμοποιήστε το πρόσθετο KeeChallenge σε KeePass 2.x (PC) για να ρυθμίσετε τη βάση δεδομένων για χρήση με Πρόκληση-Απόκριση!</string>
<string name="ErrorUpdatingChalAuxFile">Σφάλμα ενημέρωσης βοηθητικού αρχείου OTP!</string>
<string name="TrayTotp_SeedField_title">Όνομα πεδίου για seed του TOTP</string>
<string name="TOTP">TOTP</string>
<string name="TrayTotp_SeedField_summary">Εάν χρησιμοποιείτε το πρόσθετο \"TrayTotp\" του Keepass 2 με μη προεπιλεγμένες ρυθμίσεις, πληκτρολογήστε το όνομα του πεδίου για το πεδίο seed σύμφωνα με τις ρυθμίσεις στον υπολογιστή.</string>
<string name="TrayTotp_SettingsField_title">Όνομα πεδίου στις ρυθμίσεις TOTP</string>
<string name="TrayTotp_SettingsField_summary">Εισάγετε όνομα πεδίου από ρυθμίσεις TrayTotp.</string>
@@ -705,6 +715,33 @@
<string name="EntryChannel_desc">Ειδοποίηση για απλοποιημένη πρόσβαση στην τρέχουσα καταχώριση.</string>
<string name="CloseDbAfterFailedAttempts">Κλείσιμο της βάσης δεδομένων μετά από 3 ανεπιτυχείς προσπάθειες βιομετρικού ξεκλειδώματος.</string>
<string name="WarnFingerprintInvalidated">Προσοχή! Ο βιομετρικός έλεγχος ταυτότητας μπορεί να ακυρωθεί από το Android, π.χ. μετά την προσθήκη ενός νέου δακτυλικού αποτυπώματος στις ρυθμίσεις της συσκευής σας. Βεβαιωθείτε ότι ξέρετε πάντα πώς να ξεκλειδώσετε με τον κύριο κωδικό πρόσβασης!</string>
<string-array name="ChangeLog_1_12">
<item>Αναβαθμίστηκε από Xamarin Android σε .ΝΕΤ 8</item>
<item>Αναβαθμίστηκε στοχεύοντας το SDK 34</item>
<item>Αναβαθμίστηκε σε διεπαφή χρήστη Material 3</item>
<item>Βελτιώστε την αυτόματη συμπλήρωση για να εργαστείτε με Compose Apps</item>
<item>Διόρθωση ονόματος host στην αυτόματη συμπλήρωση και αναζήτηση</item>
<item>Διόρθωση προβλήματος με τη γεννήτρια κωδικού πρόσβασης</item>
</string-array>
<string-array name="ChangeLog_1_12_net">
<item>Αναβαθμίστηκε το OneDrive SDK στην έκδοση 5.68</item>
<item>Αναβαθμίστηκε το Dropbox SDK στην έκδοση 7.0.0</item>
<item>Αναβαθμισμένο Gradle, NewtonsoftJson, FluentFTP, MegaApiClient και okhttp</item>
<item>Επιδιόρθωση σφαλμάτων στην επιλογή αρχείου WebDav</item>
</string-array>
<string-array name="ChangeLog_1_11">
<item>Προστέθηκαν τα αιωρούμενα κουμπιά ενεργειών για αναζήτηση και επισκόπηση TOTP (αν υπάρχουν καταχωρήσεις TOTP).</item>
<item>Βελτιωμένη εμφάνιση των πεδίων TOTP με την προσθήκη ενός δείκτη χρονικού ορίου και πιο εμφανή εμφάνιση.</item>
<item>Το TOTP εμφανίζεται από την προβολή ομάδας.</item>
<item>Αντιγραφή τιμής κειμένου στο πρόχειρο με παρατεταμένο πάτημα στην προβολή καταχωρίσεων.</item>
<item>Κάντε το TOTP πιο εύκολα προσβάσιμο στο ενσωματωμένο πληκτρολόγιο.</item>
<item>Εμφάνιση ειδοποίησης καταχώρησης κατά την αυτόματη συμπλήρωση μιας καταχώρισης TOTP. Επιτρέπει την αντιγραφή του TOTP στο πρόχειρο. Δείτε τις προτιμήσεις για τη ρύθμιση της συμπεριφοράς.</item>
<item>Ενημερώθηκε η υλοποίηση του TOTP για την επίλυση προβλημάτων συμβατότητας με το KeePass2 και το TrayTOTP</item>
<item>Μικρές βελτιώσεις</item>
</string-array>
<string-array name="ChangeLog_1_11_net">
<item>Ενημέρωση pCloud SDK για να παρέχει πρόσβαση σε κοινόχρηστους φακέλους</item>
</string-array>
<string-array name="ChangeLog_1_10">
<item>Προσθήκη υποστήριξης για δικαιώματα ειδοποίησης στο Android 13+</item>
<item>Βελτίωση της υλοποίησης FTP και SFTP</item>
@@ -826,8 +863,8 @@
</string-array>
<string-array name="sftp_auth_modes">
<item>Συνθηματικό</item>
<item>KP2A Private/Public key</item>
<item>Custom Private key</item>
<item>Ιδιωτικό / Δημόσιο κλειδί KP2A</item>
<item>Προσαρμοσμένο ιδιωτικό κλειδί</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Παράβλεψη αποτυχιών επικύρωσης πιστοποιητικού</item>
@@ -845,6 +882,9 @@
<string name="autofill_enable_for">Ενεργοποίηση AutoFill για %1$s</string>
<string name="invalid_link_association">Δεν σχετίζεται το web domain %1$s με την εφαρμογή %2$s</string>
<string name="enable_fingerprint_hint">Το Keepass2Android ανίχνευσε βιομετρικό εξοπλισμό. Θέλετε να ενεργοποιήσετε βιομετρικό ξεκλείδωμα για αυτή τη βάση δεδομένων;</string>
<string name="post_notifications_dialog_title">Να επιτρέπονται οι ειδοποιήσεις</string>
<string name="post_notifications_dialog_message">Το Keepass2Android μπορεί να εμφανίσει ειδοποιήσεις με κουμπιά για να αντιγράψετε τιμές, όπως κωδικούς πρόσβασης και TOTP στο πρόχειρο, ή για να εμφανιστεί το ενσωματωμένο πληκτρολόγιο. Αυτό είναι χρήσιμο για να μεταφέρετε τιμές σε άλλες εφαρμογές, χωρίς να μεταβείτε σε Keepass2Android επανειλημμένα. Θέλετε να ενεργοποιήσετε αυτές τις ειδοποιήσεις;</string>
<string name="post_notifications_dialog_allow">Να επιτρέπονται οι ειδοποιήσεις</string>
<string name="post_notifications_dialog_disable">Απενεργοποιήστε αυτό το χαρακτηριστικό</string>
<string name="post_notifications_dialog_notnow">Όχι τώρα</string>
<string name="understand">Καταλαβαίνω</string>
@@ -865,5 +905,8 @@
<string name="AutofillWarning_Intro">Πρόκειται να εισάγετε διαπιστευτήρια για τον τομέα \"%1$s\" στην εφαρμογή \"%2$s\".</string>
<string name="AutofillWarning_FillDomainInUntrustedApp">Εάν εμπιστεύεστε ότι το \"%2$s\" ανήκει στο \"%1$s\" ή εμπιστεύεστε ότι η εφαρμογή \"%2$s\" δεν καταχράται τα διαπιστευτήρια (πχ. επειδή είναι μια αξιόπιστη εφαρμογή περιήγησης), είναι εντάξει να συνεχίσετε. Αν όχι, ακυρώστε.</string>
<string name="AutofillWarning_trustAsBrowser">Αποδοχή πάντα στο \"%1$s\"</string>
<string name="kp2a_switch_on_sendgodone">Εναλλαγή μετά την ολοκλήρωση</string>
<string name="kp2a_switch_on_sendgodone_summary">Εναλλαγή πίσω όταν πατήσετε αποστολή / λήψη / ολοκλήρωση</string>
<string name="qr_scanning_error_no_google_play_services">Η σάρωση QR κώδικα απαιτεί Google Play Services. Παρακαλώ εγκαταστήστε ή ενημερώστε τις Google Play Services στη συσκευή σας.</string>
<string name="english_ime_settings">Ρυθμίσεις πληκτρολογίου Android</string>
</resources>

View File

@@ -725,6 +725,7 @@
<item>Mise à niveau vers l\'interface utilisateur Material 3</item>
<item>Improve autofill to work with Compose apps</item>
<item>Fix hostname matching in autofill and search</item>
<item>Fix issue with password generator</item>
</string-array>
<string-array name="ChangeLog_1_11">
<item>Ajout de boutons d\'action flottants pour la recherche et l\'aperçu TOTP (si des entrées TOTP sont présentes).</item>

View File

@@ -151,6 +151,7 @@
<string name="hint_keyfile">kulcsfájl</string>
<string name="hint_length">hosszúság</string>
<string name="hint_pass">jelszó</string>
<string name="hint_keyfile_path">SSH privátkulcs elérési útvonala</string>
<string name="hint_login_pass">Jelszó</string>
<string name="hint_title">név</string>
<string name="hint_url">URL</string>
@@ -400,6 +401,14 @@
<string name="ShowSeparateNotifications_summary">Külön értesítés jelenjen meg a felhasználónév és a jelszó vágólapra másolásáról és a billentyűzet aktiválásáról.</string>
<string name="AccServiceAutoFill_prefs">Automatikus kitöltés akadálymentesítési szolgáltatás</string>
<string name="AutoFill_prefs">Automatikus kitöltés szolgáltatás</string>
<string name="AutoFillTotp_prefs_ShowNotification_summary">Egy bejegyzés TOTP-vel való kitöltésekor értesítés megjelenítése, TOTP másolása gombbal</string>
<string name="AutoFillTotp_prefs_ShowNotification_title">Értesítés megjelenítése</string>
<string name="AutoFillTotp_prefs_title">Automatikus kitöltés TOTP bejegyzésekhez</string>
<string name="AutoFillTotp_prefs_CopyTotpToClipboard_title">TOTP másolása a vágólapra</string>
<string name="AutoFillTotp_prefs_CopyTotpToClipboard_summary">Egy bejegyzés TOTP-vel való kitöltésekor a TOTP másolása a vágólapra</string>
<string name="AutoFillTotp_prefs_ActivateKeyboard_summary">Egy bejegyzés TOTP-vel való kitöltésekor aktiválódjon a beépített billentyűzet</string>
<string name="AutoFillTotp_prefs_ActivateKeyboard_title"> A beépített billentyűzet aktiválása</string>
<string name="TotpCopiedToClipboard">TOTP a vágólapra másolva</string>
<string name="ShowKp2aKeyboardNotification_title">KP2A billentyűzet</string>
<string name="ShowKp2aKeyboardNotification_summary">A teljes bejegyzés elérhetővé tétele a KP2A billentyűzet segítségével (ajánlott).</string>
<string name="OpenKp2aKeyboardAutomatically_title">Billentyűzetváltás</string>
@@ -416,6 +425,7 @@
<string name="ShowUnlockedNotification_summary">Egy értesítési ikon megjelenítése, amíg az adatbázis hozzáférhető.</string>
<string name="IconVisibilityInfo_Android8_text">Az Android 8 bevezetett egy új működési módot az értesítésekhez. Ha el akarja rejteni a Keepass2Android értesítési ikonját, kérjük, ezt a rendszerbeállításokban tegye meg. Állítsa az értesítési kategória fontosságát minimumra.</string>
<string name="IconVisibilityInfo_Android8_btnSettings">Beállítások megnyitása</string>
<string name="PostNotificationsPermissionInfo_text">A Keepass2Android képes rendszerértesítést megjeleníteni, amíg az adatbázis nincs lezárva. Hogy ez működjön, kérjük, adja meg az engedélyt.</string>
<string name="DontCare">Nem érdekel</string>
<string name="DocumentAccessRevoked">A fájl már nem elérhető a Keepass2Android által. Vagy el lett távolítva, vagy a hozzáférési jogosultságok vissza lettek vonva. Kérjük használja a Fájl újra megnyitását, pl. az adatbázis-váltást használva.</string>
<string name="PreloadDatabaseEnabled_title">Adatbázisok előrehozott betöltése</string>
@@ -500,9 +510,27 @@
<string name="hint_sftp_host">szerver (pl. 192.168.0.1)</string>
<string name="hint_sftp_port">port</string>
<string name="initial_directory">Kezdőkönyvtár (opcionális):</string>
<string name="connect_timeout">Kapcsolódás időkorlátja, másodpercben (opcionális)</string>
<string name="enter_sftp_login_title">SFTP tárhely adatai:</string>
<string name="sftp_auth_mode">Hitelesítési mód</string>
<string name="send_public_key">A nyilvános kulcs küldése...</string>
<string name="select_private_keyfile">Privátkulcs kiválasztása</string>
<string name="hint_sftp_key_name">Új kulcs neve</string>
<string name="hint_sftp_key_content">Új kulcs tartalma</string>
<string name="private_key_saved">Privátkulcs elmentve</string>
<string name="private_key_save_failed">A privátkulcs mentése nem sikerült: %1$s</string>
<string name="private_key_info">Adja meg az elmentendő kulcs nevét és tartalmát</string>
<string name="private_key_delete">A privát kulcs törölve: %1$s</string>
<string name="private_key_delete_failed">A privát kulcs törlése sikertelen: %1$s</string>
<string name="save_key">Privát kulcs mentése</string>
<string name="delete_key">Privát kulcs törlése</string>
<string name="private_key_select">Privát kulcs kiválasztása</string>
<string name="private_key_create_new">[Új hozzáadása...]</string>
<string name="hint_sftp_key_passphrase">Kulcshoz tartozó jelmondata</string>
<string name="sftp_kex_title">Kulcscsere (KEX) algoritmus (opcionális)</string>
<string name="hint_sftp_kex">Nevek/specifikációk, vesszővel elválasztva</string>
<string name="sftp_shk_title">Szerverazonosításhoz használt kulcs algoritmusa(i) (opcionális)</string>
<string name="hint_sftp_shk">Nevek/specifikációk, vesszővel elválasztva</string>
<string name="enter_ftp_login_title">Adja meg az FTP bejelentkezési adatait:</string>
<string name="enter_mega_login_title">Adja meg a MEGA fiókjának belépési adatait:</string>
<string name="select_storage_type">Tárolási típus kiválasztása:</string>
@@ -518,9 +546,12 @@
<string name="filestoragename_dropboxKP2A">Dropbox (KPA2 mappa)</string>
<string name="filestoragehelp_dropboxKP2A">Válassza ezt az opciót, ha nem akarja, hogy a KP2A a teljes Dropbox tárhelyhez hozzáférjen. A KP2A csak az Apps/Keepass2Android könyvtárhoz fog hozzáférést igényelni. Ha már meglévő, de máshol tárolt adatbázist kíván ilyen módon elérni, akkor válassza ezt az opciót a könyvtár létrehozásához, majd helyezze át az adatbázisfájlt a létrehozott könyvtárba (például egy PC-ről), majd válassza még egyszer ezt az opciót a fájl megnyitásához.</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragehelp_gdrive">Figyelem! A Google egyre több felhasználó számára korlátozza a Google Drive-hoz való hozzáférést az alkalmazásokból. Ha a beépített Google Drive implementáció nem működik, kérjük, használja helyette a Alapértelmezett fájlválasztót és válassza ott a Google Drive-ot!</string>
<string name="filestoragename_gdriveKP2A">Google Drive (KP2A fájlok)</string>
<string name="filestoragehelp_gdriveKP2A">Válassza ezt az opciót, ha nem akarja, hogy a KP2A a teljes Google Drive-hoz hozzáférjen. Fontos, hogy először egy új adatbázist kell létrehoznia, a korábban létrehozott fájlokat az alkalmazás nem fogja látni. Vagy válassza ezt az opciót az Új adatbázis létrehozása képernyőn, vagy, ha már létezik az opció, exportálja azt ennek ezzel az opcióval.</string>
<string name="filestoragename_pcloud">PCloud (KP2A folder)</string>
<string name="filestoragehelp_pcloud">Ez a tárhelytípus csak az \"Applications/Keepass2Android\" mappához igényel hozzáférést. Ahhoz, hogy a PCloud tárhelyed egy meglévő adatbázisát használd, azt ebben az mappában kell elhelyezned.</string>
<string name="filestoragename_pcloudall">PCloud (teljes hozzáférés)</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
<string name="filestoragename_onedrive2_full">Az összes fájl és a megosztott fájlok</string>
@@ -567,12 +598,14 @@
<string name="CouldntLoadChalAuxFile_Hint">Kérjük, konfigurálja az adatbázist hitelesítő kérdés-válasszal való használthoz a KeePass 2.x (PC-s verzió) KeeChallenge beépülő modulja segítségével.</string>
<string name="ErrorUpdatingChalAuxFile">OTP kiegészítő fájl frissítése közben hiba történt!</string>
<string name="TrayTotp_SeedField_title">TOTP mezőnév</string>
<string name="TOTP">Idő alapú egyszer használatos jelszó (TOTP)</string>
<string name="TrayTotp_SeedField_summary">Ha a \"TrayTotp\" beépülő-modult nem alapértelmezett beállításokkal használja, adja meg annak a mezőnek a nevét, amit a jelszógenerálásnál használni kell (megegyezően a PC-n használt beállítással).</string>
<string name="TrayTotp_SettingsField_title">TOTP beállítások mező</string>
<string name="TrayTotp_SettingsField_summary">Adja meg a TrayTotp beállítások mezőjének nevét.</string>
<string name="TrayTotp_prefs">TrayTotp</string>
<string name="DebugLog_prefs_prefs">Naplófájl a hibakereséshez</string>
<string name="DebugLog_title">Naplófájl használata</string>
<string name="FtpDebug_title">FTP/SFTP naplózása</string>
<string name="DebugLog_summary">Az alkalmazás kimenetének egy helyi naplófájlba írása</string>
<string name="DebugLog_send">Hibakeresési naplóbejegyzések küldése...</string>
<string name="loading">Töltés...</string>
@@ -641,6 +674,21 @@
<string name="Continue">Folytatás</string>
<string name="NoFilenameWarning">A megadott URI nem tűnik fájlnévnek. Biztos benne, hogy jó URI-t adott meg?</string>
<string name="FirstInvalidCompositeKeyError">Az összetett kulcs érvénytelen! Próbálkozzon újra.</string>
<string name="RepeatedInvalidCompositeKeyHelp">Az összetett kulcs érvénytelen! Kérjük, próbálja meg a következő lépésekkel feloldani az adatbázisát:\n
- Győződjön meg róla, hogy helyesen adta meg a jelszót. Használja a szem ikont a beírt jelszó felfedéséhez.\n
- Győződjön meg róla, hogy a megfelelő jelszótípust választotta ki. Győződjön meg róla, hogy ez megegyezik az adatbázis létrehozásakor használt típussal.\n
- Győződjön meg róla, hogy a megfelelő adatbázisfájlt választotta ki.</string>
<string name="HintLocalBackupInvalidCompositeKey"> \n
- Tipp: Ha úgy gondolja, hogy az adatbázis-fájlja sérült lehet, vagy nem emlékszik a főkulcsra (jelszó) a módosítás után, akkor a „%1$s” gombra koppintva és a helyi biztonsági mentést kiválasztva próbálkozhat a fájl utolsó sikeresen megnyitott verziójával.</string>
<string name="HintLocalBackupOtherError"> \n
- Tipp: A Keepass2Android az utolsó sikeresen megnyitott fájlverziót a belső tárhelyen tárolja. Megnyithatja azt a „%1$s” koppintással és a helyi biztonsági mentés kiválasztásával.</string>
<string name="CorruptDatabaseHelp">A fájl sérült. \n
Néhány tipp, amely segíthet a probléma diagnosztizálásában:\n
- Ha a fájlt USB-n keresztül másolta (MTP-mód), próbálja meg újra egy olyan eszközzel, mint a MyPhoneExplorer. Az MTP bizonyos esetekben csonkítja a fájlokat.\n
- Ha nem tudja megnyitni a fájlt ugyanarról a helyről a számítógépéről sem, akkor nagyon valószínű, hogy a fájl valóban sérült. Kérjük, ekkor használja az adatbázis egy biztonsági mentését. Ha feltételezi, hogy a Keepass2Android okozta a sérülést, kérjük, lépjen kapcsolatba a támogatói csapatot.\n
- Ha még meg tudja nyitni a fájlt a számítógépén, kérjük, lépjen kapcsolatba a támogatói csapattal. Megpróbálhatja más beállításokkal elmenteni a számítógépen (például tömörítetlenül), és azt megnyitni a Keepass2Androidban.</string>
<string name="open_other_db">Egy másik adatbázis megnyitása…</string>
<string name="select_database">Adatbázis kiválasztása</string>
<string name="configure_child_dbs">Csatolt adatbázisok konfigurálása…</string>
@@ -729,8 +777,8 @@
</string-array>
<string-array name="sftp_auth_modes">
<item>Jelszó</item>
<item>KP2A Private/Public key</item>
<item>Custom Private key</item>
<item>KP2A privát/publikus kulcs</item>
<item>Egyedi privát kulcs</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Továbblépés, figyelmeztetés nélkül</item>
@@ -748,6 +796,11 @@
<string name="autofill_enable_for">Az automatikus kitöltés engedélyezése %1$s esetén</string>
<string name="invalid_link_association">Nem sikerült összerendelni a %1$s internettartományt a %2$s alkalmazással</string>
<string name="enable_fingerprint_hint">Biometrikus eszköz detektálva. Szeretné engedélyezni az adatbázis biometrikus feloldását?</string>
<string name="post_notifications_dialog_title">Értesítések engedélyezése</string>
<string name="post_notifications_dialog_message">A Keepass2Android képes értesítéseket megjeleníteni, ami lehetővé teszi, hogy többször vagy egyszer használatos jelszavakat másoljon a vágólapra, vagy aktiválja a beépített billentyűzetet. Ez hasznos többféle érték más alkalmazásokba történő átviteléhez, úgy, hogy ne kelljen többször is alkalmazást váltani. Szeretné engedélyezni az ilyen értesítéseket?</string>
<string name="post_notifications_dialog_allow">Értesítések engedélyezése</string>
<string name="post_notifications_dialog_disable">A lehetőség kikapcsolása</string>
<string name="post_notifications_dialog_notnow">Ne most</string>
<string name="understand">Értem</string>
<string name="dont_show_again">Ne mutassa többet</string>
<string name="masterkey_infotext_head">Emlékszik a főkulcsra (jelszó)?</string>
@@ -766,5 +819,8 @@
<string name="AutofillWarning_Intro">A(z) \"%1$s\" tartományhoz tartozó azonosítókat készül a(z) \"%2$s\" alkalmazás számára elérhetővé tenni.</string>
<string name="AutofillWarning_FillDomainInUntrustedApp">Ha megbízik abban, hogy a(z) \"%1$s\" tartomány a(z) \"%2$s\" alkalmazáshoz tartozik, vagy megbízik abban, hogy az alkalmazás nem él vissza a titkos adatokkal (mert például egy megbízható böngészőprogram), folytassa. Ha nem, szakítsa meg a folyamatot.</string>
<string name="AutofillWarning_trustAsBrowser">Mindig fogadja el itt: \"%1$s\"</string>
<string name="kp2a_switch_on_sendgodone">Visszakapcsolás, ha végzett</string>
<string name="kp2a_switch_on_sendgodone_summary">Visszaváltás a Küldés/Ugrás/Kész gombok megnyomásakor</string>
<string name="qr_scanning_error_no_google_play_services">A QR-kód beolvasásához szükséges a Google Play Service. Kérjük, telepítse vagy frissítse a szolgáltatást a készülékén.</string>
<string name="english_ime_settings">Keepass2Android billentyűzetbeállítások</string>
</resources>

View File

@@ -546,6 +546,7 @@
<string name="filestoragename_dropboxKP2A">Dropbox (cartella KP2A)</string>
<string name="filestoragehelp_dropboxKP2A">Se non vuoi dare a KP2A accesso completo al tuo Dropbox, puoi selezionare questa opzione. Richiederà solo l\'accesso alla cartella Applicazioni/Keepass2Android. Ciò è particolarmente indicato quando si crea un nuovo database. Se hai già un database, fai click su questa opzione per creare la cartella, poi spostaci dentro il file (dal PC), quindi seleziona di nuovo questa opzione per aprire il file.</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragehelp_gdrive">Nota: Google sta limitando l\'accesso a Google Drive dalle app per un numero sempre maggiore di utenti. Se la funzione integrata di Google Drive non funziona, si prega di utilizzare il selettore di file di sistema e selezionare Google Drive!</string>
<string name="filestoragename_gdriveKP2A">Google Drive (file KP2A)</string>
<string name="filestoragehelp_gdriveKP2A">Se non vuoi dare a KP2A accesso completo al tuo Google Drive completo, puoi selezionare questa opzione. Nota che è necessario prima creare un file di database, i file esistenti non sono visibili per l\'app. Scegli questa opzione dalla schermata Crea database o, se hai già aperto un database, esportando il database scegliendo questa opzione.</string>
<string name="filestoragename_pcloud">PCloud (KP2A folder)</string>
@@ -672,6 +673,22 @@
<string name="Continue">Continua</string>
<string name="NoFilenameWarning">L\'URI inserito non sembra il nome di un file. Sei sicuro che sia un file valido?</string>
<string name="FirstInvalidCompositeKeyError">Chiave composita non valida! Riprova.</string>
<string name="RepeatedInvalidCompositeKeyHelp">Chiave composta non valida! Per favore prova la seguente procedura per sbloccare il database: \n
• assicurati di aver inserito la password corretta. Usa l\'icona che rappresenta un occhio per vedere la password immessa.\n
• assicurati di aver selezionato il tipo di password corretto. Assicurati che coincida con il tipo usato quando hai creato il database.\n
• assicurati di aver selezionato il file del database corretto.</string>
<string name="HintLocalBackupInvalidCompositeKey"> \n
• Suggerimento: se pensi che il tuo file di database possa essere danneggiato oppure non ricordi la password principale dopo averla modificata, puoi provare con l\'ultima versione di file aperta correttamente facendo clic su \"%1$s\" e selezionando il backup locale.</string>
<string name="HintLocalBackupOtherError"> \n
• Suggerimento: Keepass2Android ha archiviato l\'ultima versione del file aperta correttamente nella memoria interna. È possibile aprirla facendo clic su \"%1$s\" e selezionando il backup locale.
</string>
<string name="CorruptDatabaseHelp">Il file è corrotto.\n
Ecco alcuni suggerimenti che ti potrebbero aiutare a diagnosticare il problema:\n
• se hai copiato il file tramite USB (MTP-Mode), riprova usando uno strumento come MyPhoneExplorer. (MTP a volte tronca i file.)\n
• se non puoi aprire il file dallo stesso percorso sul tuo PC, è molto probabile che il file sia effettivamente danneggiato. Per favore usa un backup del database. Se credi che Keepass2Android abbia corrotto il file, contatta il supporto.\n
• se puoi ancora aprire il file sul tuo PC, contatta il supporto. Puoi provare a salvarlo con impostazioni differenti (es: non compresso) sul PC e, poi, riprovare ad aprirlo in Keepass2Android. </string>
<string name="open_other_db">Apri un altro database…</string>
<string name="select_database">Seleziona il database</string>
<string name="configure_child_dbs">Configura i database figli…</string>
@@ -699,6 +716,20 @@
<string name="EntryChannel_desc">Notifica per semplificare l\'accesso alla voce attualmente selezionata.</string>
<string name="CloseDbAfterFailedAttempts">Chiudi il database dopo tre tentativi di sblocco biometrici falliti.</string>
<string name="WarnFingerprintInvalidated">Attenzione! L\'autenticazione biometrica può essere invalidata da Android, ad es. dopo aver aggiunto una nuova impronta digitale nelle impostazioni del dispositivo. Assicurati di sapere sempre come sbloccare con la tua password principale!</string>
<string-array name="ChangeLog_1_12">
<item>Aggiornato da Xamarin Android a .net 8</item>
<item>Aggiornato a Target SDK 34</item>
<item>Aggiornato all\'interfaccia utente Material 3</item>
<item>Migliora l\'autofill per funzionare con le app Compose</item>
<item>Corretta la corrispondenza con l\' hostname in riempimento automatico e ricerca</item>
<item>Fix issue with password generator</item>
</string-array>
<string-array name="ChangeLog_1_12_net">
<item>Aggiornato OneDrive SDK alla versione 5.68</item>
<item>Aggiornato Dropbox SDK alla versione 7.0.0</item>
<item>Aggiornati Gradle , NewtonsoftJson, FluentFTP, MegaApiClient e okhttp</item>
<item>Bugfix nella selezione dei file WebDav</item>
</string-array>
<string-array name="ChangeLog_1_11">
<item>Aggiunti pulsanti di azione fluttuanti per la ricerca e TOTP panoramica (se sono presenti voci TOTP).</item>
<item>Vista migliorata dei campi TOTP aggiungendo un indicatore di tempo limite e mostrandolo in modo più evidente.</item>
@@ -902,6 +933,17 @@
* Puoi installare altri pacchetti di icone (le vecchie icone in stile Windows sono disponibili sul Play Store)\n
* Aggiunta richiesta di conferma prima di eliminare voci senza passare dal cestino\n
* Correzione errori (errata visualizzazione dell\'OTP-secret, icona dell\'app errata in certi posti)\n </string>
<string name="ChangeLog_0_9_8b">Versione 0.9.8b\n
* correzione di errori (il salvataggio di alcuni database fallisce, l\'esportazione su periferica locale non funziona, la selezione di alcune opzioni manda l\'app in crash)\n</string>
<string name="ChangeLog_0_9_8">Versione 0.9.8\n
* supporto per Storage Access Framework (permette a KP2A Offline di scrivere su scheda SD e Google Drive)\n
* tenta di rilevare errori dell\'utente nell\'inserimento degli URL di WebDAV (cartella al posto di file)\n
* cambiato il font della password\n
* consente di modificare l\'account di Dropbox\n
* bugfix: ora ricorda la password OTP</string>
<string name="ChangeLog_0_9_7b">Versione 0.9.7b\n
* aggiornate le traduzioni\n
* bugfix: font Password mancante nella versione 0.9.7; ordina per nome non ordinava i gruppi\n</string>
<string name="ChangeLog_keptDonate">Estesa la possibilità di donare una birra o qualcos\'altro</string>
<string-array name="clipboard_timeout_options">
<item>30 secondi</item>
@@ -951,8 +993,8 @@
</string-array>
<string-array name="sftp_auth_modes">
<item>Password</item>
<item>KP2A Private/Public key</item>
<item>Custom Private key</item>
<item>Chiave privata/pubblica di KP2A</item>
<item>Chiave privata personalizzata</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignora gli errori di convalida del certificato</item>
@@ -970,6 +1012,10 @@
<string name="autofill_enable_for">Attiva Compilazione automatica per %1$s</string>
<string name="invalid_link_association">Non è possibile associare il dominio web %1$s con l\'app %2$s</string>
<string name="enable_fingerprint_hint">Keepass2Android ha rilevato il sensore biometrico. Desideri attivare lo sblocco biometrico per questo database?</string>
<string name="post_notifications_dialog_title">Consenti le notifiche</string>
<string name="post_notifications_dialog_message">Keepass2Android può mostrare le notifiche con i pulsanti per copiare valori come password e TOTP negli appunti, o per far apparire la tastiera integrata. Questo è utile per trasferire i valori in altre applicazioni senza passare a Keepass2Android ripetutamente. Vuoi abilitare tali notifiche?</string>
<string name="post_notifications_dialog_allow">Consenti le notifiche</string>
<string name="post_notifications_dialog_disable">Disabilita questa funzione</string>
<string name="post_notifications_dialog_notnow">Non ora</string>
<string name="understand">Ho capito</string>
<string name="dont_show_again">Non mostrare più</string>
@@ -989,5 +1035,8 @@
<string name="AutofillWarning_Intro">Stai per inserire le credenziali per il dominio \"%1$s\" nell\'app \"%2$s\"</string>
<string name="AutofillWarning_FillDomainInUntrustedApp">Se ti fidi di \"%2$s\" ad appartenere a \"%1$s\" o se ti fidi dell\'app \"%2$s\" a non abusare delle credenziali (es. perché si tratta di un\'applicazione sicura), è possibile continuare. Altrimenti si prega di annullare.</string>
<string name="AutofillWarning_trustAsBrowser">Accetta sempre in \"%1$s\"</string>
<string name="kp2a_switch_on_sendgodone">Cambia tastiera quando terminato</string>
<string name="kp2a_switch_on_sendgodone_summary">Torna indietro quando premi invia/vai/fatto</string>
<string name="qr_scanning_error_no_google_play_services">La scansione del codice QR richiede Google Play Services. Installa o aggiorna Google Play Services sul tuo dispositivo.</string>
<string name="english_ime_settings">Impostazioni tastiera Keepass2Android</string>
</resources>

View File

@@ -724,6 +724,7 @@
<item>ユーザーインターフェイスを Material 3 にアップグレード</item>
<item>Compose アプリでの自動入力機能を改善</item>
<item>自動入力と検索でホスト名の一致条件を修正</item>
<item>Fix issue with password generator</item>
</string-array>
<string-array name="ChangeLog_1_12_net">
<item>OneDrive SDK をバージョン 5.68 にアップグレード</item>

View File

@@ -548,6 +548,7 @@
<string name="filestoragename_dropboxKP2A">Dropbox (pasta KP2A)</string>
<string name="filestoragehelp_dropboxKP2A">Se você não quiser dar acesso KP2A para o seu Dropbox completo, você pode selecionar esta opção. Ele irá solicitar somente acesso para a pasta Apps/Keepass2Android. Isto é especialmente adequado ao criar um novo banco de dados. Se você já tiver um banco de dados, clique nesta opção para criar a pasta, em seguida, colocar o arquivo dentro da pasta (a partir de seu PC) e em seguida, selecione esta opção novamente para abrir o arquivo.</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragehelp_gdrive">Observação: o Google está restringindo o acesso ao Google Drive a partir de aplicativos para cada vez mais usuários. Se a implementação integrada do Google Drive não funcionar, use o seletor de arquivos do sistema e selecione o Google Drive lá!</string>
<string name="filestoragename_gdriveKP2A">Google Drive (Arquivos KP2A)</string>
<string name="filestoragehelp_gdriveKP2A">Se você não quiser dar acesso KP2A ao seu Google Drive completo, você pode selecionar esta opção. Observe que você precisa criar um arquivo de banco de dados primeiro, os arquivos existentes não são visíveis para o aplicativo. Escolha esta opção na tela Criar banco de dados ou, se você já abriu um banco de dados, exportando o banco de dados escolhendo esta opção.</string>
<string name="filestoragename_pcloud">PCloud (Pasta KP2A)</string>
@@ -727,6 +728,7 @@
<item>Atualizado para a interface de usuário Material 3</item>
<item>Melhorado o preenchimento automático para trabalhar com aplicativos Compose</item>
<item>Corrigido a correspondência do nome do host em preenchimento automático e pesquisa</item>
<item>Corrigido um problema com gerador de senhas</item>
</string-array>
<string-array name="ChangeLog_1_12_net">
<item>Atualizado o OneDrive SDK para a versão 5.68</item>
@@ -1199,5 +1201,6 @@
<string name="AutofillWarning_trustAsBrowser">Aceitar sempre em \"%1$s\"</string>
<string name="kp2a_switch_on_sendgodone">Voltar quando terminar</string>
<string name="kp2a_switch_on_sendgodone_summary">Voltar ao pressionar enviar/ir/concluído</string>
<string name="qr_scanning_error_no_google_play_services">A leitura de código QR requer o Google Play Services. Instale ou atualize o Google Play Services no seu dispositivo.</string>
<string name="english_ime_settings">Configurações do teclado Android</string>
</resources>

View File

@@ -468,6 +468,7 @@
<string name="NoOverwrite">Nu, suprascrie</string>
<string name="UseOfflineMode">Funcționare doar cu cache intern</string>
<string name="UseOnlineMode">Sincronizează copia cache cu sursa</string>
<string name="InOfflineMode">Funcționare doar cu cache intern.</string>
<string name="SynchronizingCachedDatabase">Sincronizează baza de date din cache…</string>
<string name="DownloadingRemoteFile">Se încarcă fișierul sursă…</string>
<string name="UploadingFile">Salvează fișierul…</string>
@@ -476,6 +477,8 @@
<string name="SynchronizedDatabaseSuccessfully">Baza de date s-a sincronizat cu succes!</string>
<string name="CheckingDatabaseForChanges">Se verifică baza de date pentru modificări…</string>
<string name="UpdatedRemoteFileOnLoad">Fișier sursă actualizat.</string>
<string name="LoadedFromRemoteInSync">Fișierul sursă și cache-ul sunt sincronizate.</string>
<string name="UpdatedCachedFileOnLoad">S-a actualizat copia internă a cache-ului %1$s.</string>
<string name="RemoteDatabaseUnchanged">Nu s-au detectat modificări.</string>
<string name="SynchronizingOtpAuxFile">Se sincronizează fișierul auxiliar OTP…</string>
<string name="database_file">fișier bază de date</string>
@@ -498,11 +501,23 @@
<string name="hint_sftp_host">host (ex: 192.168.0.1)</string>
<string name="hint_sftp_port">port</string>
<string name="initial_directory">Folder inițial (opțional):</string>
<string name="connect_timeout">Secunde expirare conexiune (opțional)</string>
<string name="enter_sftp_login_title">Introduceţi datele de conectare SFTP:</string>
<string name="sftp_auth_mode">Mod autentificare</string>
<string name="send_public_key">Trimite cheia publică...</string>
<string name="select_private_keyfile">Selectează cheia privată...</string>
<string name="hint_sftp_key_name">Nume cheie nouă</string>
<string name="hint_sftp_key_content">Conținut cheie nouă</string>
<string name="private_key_saved">Cheie privată salvată</string>
<string name="private_key_save_failed">Salvarea cheii private a eșuat: %1$s</string>
<string name="private_key_info">Introduc numele cheii și conținutul pentru a salva</string>
<string name="private_key_delete">Cheie privată ștearsă: %1$s</string>
<string name="private_key_delete_failed">Ștergerea cheii private a eșuat: %1$s</string>
<string name="save_key">Salvează cheia privată</string>
<string name="delete_key">Ștergere cheie privată</string>
<string name="private_key_select">Selectează cheia privată</string>
<string name="private_key_create_new">[Adaugă nou...]</string>
<string name="hint_sftp_key_passphrase">Frază de acces pentru cheie (opțională)</string>
<string name="enter_ftp_login_title">Introdu datele de conectare FTP:</string>
<string name="select_storage_type">Selectaţi tipul de stocare:</string>
<string name="filestoragename_file">Fişier local</string>
@@ -517,12 +532,15 @@
<string name="filestoragename_dropboxKP2A">Dropbox (folder KP2A)</string>
<string name="filestoragehelp_dropboxKP2A">Dacă nu doriți să dați KP2A acces la Dropbox-ul complet, puteți selecta această opțiune. Va solicita acces doar la directorul Apps/Keepass2Android. Acest lucru este potrivit în special atunci când se creează o bază de date nouă. Dacă aveţi deja o bază de date, selectaţi această opţiune pentru a crea directorul, apoi plasați fișierul în director (de la PC) și apoi selectați din nou această opțiune pentru deschiderea fișierului.</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Google Drive (fișiere KP2A)</string>
<string name="filestoragename_pcloudall">PCloud (Acces complet)</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
<string name="filestoragename_onedrive2_full">Toate fișierele și fișierele partajate</string>
<string name="filestoragename_onedrive2_myfiles">Fișierele mele</string>
<string name="filestoragename_onedrive2_appfolder">Directorul Keepass2Android App</string>
<string name="filestoragename_sftp">SFTP (SSH File Transfer)</string>
<string name="filestoragename_mega">MEGA</string>
<string name="filestoragename_content">Selector de fisiere sistem</string>
<string name="filestorage_setup_title">Iniţializare acces fişier</string>
<string name="database_location">Locaţie bază de date</string>
@@ -549,6 +567,8 @@
<string name="OtpKeyError">Imposibil de creat cheia OTP! Asigură-te că ai introdus OTP-urile corecte.</string>
<string name="ErrorUpdatingOtpAuxFile">Eroare în actualizare fișier auxiliar OTP!</string>
<string name="SavingOtpAuxFile">Se salvează fișierul auxiliar OTP…</string>
<string name="PleaseInstallApp">Instalează %1$s de pe Google Play.</string>
<string name="AppOutdated">%1$s nu mai este suportat.</string>
<string name="bad_resp">Răspunsul la provocare este incorect.</string>
<string name="CouldntLoadChalAuxFile">Nu se poate încărca fișierul auxiliar de provocare!</string>
<string name="CouldntLoadChalAuxFile_Hint">Vă rugăm să folosiți plugin-ul KeeChallenge în KeePass 2,x (PC) pentru a configura data de baze cu răspunsuri-provocare!</string>
@@ -588,7 +608,9 @@
<string name="CopyFileRequired">Pentru a-l folosi, trebuie copiat într-o altă locație.</string>
<string name="CopyFileRequiredForEditing">Pentru a-l edita, trebuie copiat fișierul într-o altă locație.</string>
<string name="FileReadOnlyTitle">Baza de date permite doar citirea</string>
<string name="FileReadOnlyMessagePre">Keepass2Android a deschis baza de date curentă în modul doar pentru citire.</string>
<string name="ReadOnlyReason_PreKitKat">Se pare ca ai deschis fişierul dintr-o aplicaţie externă. În acest fel nu se acceptă modificări. Dacă doriţi să faceţi modificări în baza de date, închideţi baza de date şi selectaţi Schimbare bază de date. Apoi deschide fisierul folosind una dintre opţiunile disponibile.</string>
<string name="ReadOnlyReason_ReadOnlyFlag">Steagul doar-în-citire este setat. Elimină-l dacă dorești să faci modificări în baza de date.</string>
<string name="ReadOnlyReason_ReadOnlyKitKat">Modificarea nu este posibilă din cauza restricţiilor introduse în Android KitKat. Dacă doriţi să faceţi modificări în baza de date, închideți baza de date şi selectaţi Schimbare bază de date. Apoi deschide fisierul folosind selectorul de fisiere al sistemului.</string>
<string name="AddCustomIcon">Adaugă pictogramă din file...</string>
<string name="CopyingFile">Se copiază fișierul...</string>
@@ -612,6 +634,7 @@
<string name="TemplateTitle_Membership">Apartenenta</string>
<string name="ChangeLog_title">Jurnal modificări</string>
<string name="AskAddTemplatesTitle">Adauga template-uri?</string>
<string name="AskAddTemplatesMessage">Keepass2Android conține șabloane de intrare pentru conturi de e-mail, parole wireless-LAN, note sigure și multe altele. Dorești să le adaugi în baza de date? Dacă alegi Nu, le poți adăuga mai târziu în setările bazei de date.</string>
<string name="AddTemplates_pref">Adauga template-uri in baza de date</string>
<string name="Continue">Continuare</string>
<string name="NoFilenameWarning">URI introdus nu arata ca un nume de file. Sunteţi sigur că acesta este un file valid?</string>
@@ -628,8 +651,24 @@
<string name="child_db_Enabled_title">Deschide automat</string>
<string name="database_file_heading">Fișier bază de date</string>
<string name="if_device_text">Activează pentru %1$s</string>
<string name="restore_history">Restaurează această versiune</string>
<string name="remove_history">Elimină această versiune</string>
<string name="DbUnlockedChannel_name">Bază de date deblocată</string>
<string name="DbUnlockedChannel_desc">Notificare despre deblocarea bazei de date</string>
<string name="DbQuicklockedChannel_name">QuickUnlock</string>
<string name="DbQuicklockedChannel_desc">Notificare despre blocarea bazei de date cu QuickUnlock</string>
<string name="EntryChannel_name">Notificări intrare</string>
<string name="EntryChannel_desc">Notificare pentru simplificarea accesului la intrarea selectată în prezent.</string>
<string name="CloseDbAfterFailedAttempts">Închide baza de date după trei încercări de deblocare biometrică eșuate.</string>
<string name="WarnFingerprintInvalidated">Avertizare! Autentificarea biometrică poate fi invalidată de Android, de ex. după adăugarea unei amprente noi în setările dispozitivului. Asigură-te că știi întotdeauna cum să deblochezi cu parola principală!</string>
<string-array name="ChangeLog_1_12">
<item>Upgraded from Xamarin Android to .net 8</item>
<item>Upgraded to Target SDK 34</item>
<item>Upgraded to Material 3 user interface</item>
<item>Improve autofill to work with Compose apps</item>
<item>Fix hostname matching in autofill and search</item>
<item>S-a reparat problema cu generatorul de parole</item>
</string-array>
<string-array name="ChangeLog_1_11">
<item>Added floating action buttons for search and TOTP overview (if TOTP entries are present).</item>
<item>Improved display of TOTP fields by adding a timeout indicator and showing it more prominently.</item>
@@ -669,8 +708,8 @@
<string name="design_title">Design</string>
<string-array name="ftp_encryption_modes">
<item>Fără criptare (FTP)</item>
<item>Implicit encryption (FTP over TLS, FTPS)</item>
<item>Explicit encryption (FTP over TLS, FTPS)</item>
<item>Criptare implicită (FTP peste TLS, FTPS)</item>
<item>Criptare explicită (FTP prin TLS, FTPS)</item>
</string-array>
<string-array name="cred_remember_modes">
<item>Nu reține numele utilizator și parola</item>
@@ -684,30 +723,46 @@
<item>Parolă + OTP secret (modul de recuperare)</item>
<item>Parolă + Challenge-Response</item>
<item>Parolă + Challenge-Response secret (modul de recuperare)</item>
<item>Password + Challenge-Response for Keepass XC</item>
<item>Password + Key file + Challenge-Response for Keepass XC</item>
<item>Parolă + provocare-răspuns pentru Keepass XC</item>
<item>Parolă + fișier cheie + provocare-răspuns pentru Keepass XC</item>
</string-array>
<string-array name="sftp_auth_modes">
<item>Parolă</item>
<item>KP2A Private/Public key</item>
<item>Custom Private key</item>
<item>Cheie Privată/Publică KP2A</item>
<item>Cheie privată personalizată</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignoră eșecuri de validare a certificatului</item>
<item>Avertizează când validarea eșuează</item>
<item>Nu accepta certificate invalide</item>
</string-array>
<string name="ClearClipboardWarning">Asigură-te că aceasta funcționează pe sistemul tău, dacă nu, ia în considerare folosirea tastaturii încorporate.</string>
<string name="PluginDescription">Descrierea furnizată de plugin:</string>
<string name="autofill_hint">Keepass2Android suportă caracteristica de completare automată a Android, dar se pare că nu ai activat-o încă.</string>
<string name="autofill_enable">Activare autocompletare</string>
<string name="autofill_enable_failed">Ne pare rău, se pare că dispozitivul nu acceptă deschiderea setărilor din interiorul aplicației. Accesează manual setările de sistem pentru completare automată pentru a activa serviciul.</string>
<string name="show_autofill_help">Afișează ajutorul pentru completarea automată</string>
<string name="autofill_sign_in_prompt">Completează cu Keepass2Android</string>
<string name="autofill_disable">Dezactivează completarea automată pentru %1$s</string>
<string name="autofill_enable_for">Activează completare automată pentru %1$s</string>
<string name="invalid_link_association">Domeniul web %1$s nu a putut fi asociat cu aplicația %2$s</string>
<string name="enable_fingerprint_hint">Keepass2Android a detectat dispozitive biometrice. Doriţi să activaţi deblocarea biometrică pentru această bază de date?</string>
<string name="post_notifications_dialog_title">Permite notificări</string>
<string name="post_notifications_dialog_message">Keepass2Android poate afișa notificări cu butoane pentru a copia valori cum ar fi parole și coduri TOTP în clipboard, sau pentru a aduce tastatura integrată. Acest lucru este util pentru a transfera valori în alte aplicații fără a trece la Keepass2Android în mod repetat. Doriești să activezi astfel de notificări?</string>
<string name="post_notifications_dialog_allow">Permite notificări</string>
<string name="post_notifications_dialog_disable">Dezactivează această caracteristică</string>
<string name="post_notifications_dialog_notnow">Nu acum</string>
<string name="understand">Am înțeles</string>
<string name="dont_show_again">Nu mai afișa</string>
<string name="masterkey_infotext_head">Îți amintești parola principală?</string>
<string name="masterkey_infotext_main">Reține că nu vei putea deschide baza de date fără cheia principală. Nu există nici o modalitate de a \"reseta\" parola principală.</string>
<string name="masterkey_infotext_fingerprint_note">De asemenea, rețineți că Deblocarea biometrică funcționează prin stocarea cheii principale în spațiul de stocare securizat Android. Această stocare poate fi ștearsă de Android în orice moment, de ex. dacă adăugați o nouă amprentă în setările sistemului. Așa că nu vă bazați pe deblocarea biometrică ci retineți parola principală, vă rog!</string>
<string name="backup_infotext_head">S-a creat o copie de rezervă pentru baza de date?</string>
<string name="backup_infotext_main">Keepass2Android stochează parolele într-un fișier într-o locație la alegerea ta. Ești sigur că încă poți accesa acest fișier atunci când telefonul este pierdut sau furat sau când fișierul este distrus sau șters? Asigură-te că ai întotdeauna o copie de rezervă actualizată într-un loc sigur!</string>
<string name="backup_infotext_note">Pentru a crea o copie de rezervă acum, accesează %1$s &gt; %2$s &gt; %3$s.</string>
<string name="emergency_infotext_head">Ești pregătit pentru cazuri de urgență?</string>
<string name="emergency_infotext_main">Te-ai gândit vreodată ce se întâmplă dacă nu mai poți accesa baza de date a parolelor? Ce se întâmplă dacă ai un accident? Este o bună practică să transmiți cheia principală unei persoane de încredere pentru cazuri de urgență. În caz contrar, nimeni nu va avea acces la parole.</string>
<string name="no_secure_display">Afișajul valabil în prezent nu este marcat ca fiind sigur. Aceasta înseamnă că capturile de ecran ar putea fi luate de alte aplicații. Keepass2Android este configurat pentru a afișa informații sensibile doar pe afișaje securizate. Schimbă la un afișaj securizat (de exemplu, detașând un monitor HDMI) sau modifică setările aplicației.</string>
<string name="disable_secure_screen_check">Dezactivează acest mesaj</string>
<string name="switch_ime_text">Activează tastatura Keepass2Android.</string>
<string name="switch_ime_reopen">Reîncercați</string>
@@ -717,5 +772,6 @@
<string name="AutofillWarning_trustAsBrowser">Acceptați întotdeauna în \"%1$s\"</string>
<string name="kp2a_switch_on_sendgodone">Comută înapoi când ai terminat</string>
<string name="kp2a_switch_on_sendgodone_summary">Comută înapoi după apăsarea trimitere/înainte/terminat</string>
<string name="qr_scanning_error_no_google_play_services">Scanarea codului QR necesită servicii Google Play. Instalează sau actualizează serviciile Google Play de pe dispozitiv.</string>
<string name="english_ime_settings">Setările tastaturii Keepass2Android</string>
</resources>

View File

@@ -546,6 +546,7 @@
<string name="filestoragename_dropboxKP2A">Dropbox (priečinok KP2A)</string>
<string name="filestoragehelp_dropboxKP2A">Túto voľbu môžete aktivovať ak udelíte KP2A prístup do celého Dropboxu. Vyžaduje sa prístup iba do priečinka Apps/Keepass2Android. Je to osobitne vhodné pri vytváraní novej databázy. Ak už máte databázu, tak aktivovaním tejto voľby vytvoríte priečinok, potom premiestnite súbor do priečinka (zo svojho PC) a potom znovu aktivujte voľbu, pre otvorenie súboru.</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragehelp_gdrive">Pamätajte na to, že: Google čoraz viac obmedzuje prístup ku Google Drive z aplikácií. Ak vám nefunguje zabudovaná integrácia s Google Drive, použite výber súboru priamo v systéme a vyberte Google Drive tam!</string>
<string name="filestoragename_gdriveKP2A">Google Drive (súbory KP2A)</string>
<string name="filestoragehelp_gdriveKP2A">Ak nechcete poskytnúť KP2A prístup k celému úložisku Google Drive, môžete vybrať túto možnosť. Pamätajte na to, že najskôr musíte vytvoriť súbor s databázou, existujúce súbory aplikácia neuvidí. Buď vyberte takúto možnosť z obrazovky pre vytvorenie databázy, alebo už otvorenú databázu exportujte vybratím tejto možnosti.</string>
<string name="filestoragename_pcloud">PCloud (priečinok KP2A)</string>
@@ -725,6 +726,7 @@
<item>Aktualizované na používateľské rozhranie Material 3</item>
<item>Vylepšené automatické dopĺňanie kvôli fungovaniu s aplikáciami Compose</item>
<item>Oprava zhody mena hostiteľa v automatickom dopĺňaní a vyhľadávaní</item>
<item>Fix issue with password generator</item>
</string-array>
<string-array name="ChangeLog_1_12_net">
<item>Aktualizované na OneDrive SDK, verziu 5.68</item>
@@ -1199,5 +1201,6 @@
<string name="AutofillWarning_trustAsBrowser">Vždy súhlasiť v \"%1$s\"</string>
<string name="kp2a_switch_on_sendgodone">Po dokončení prepnúť späť</string>
<string name="kp2a_switch_on_sendgodone_summary">Po stlačení Odoslať/Prejsť/Hotovo prejsť späť</string>
<string name="qr_scanning_error_no_google_play_services">Na skenovanie QR kódov sú potrebné služby Google Play. Nainštalujte alebo aktualizujte si ich vo vašom zariadení.</string>
<string name="english_ime_settings">Nastavenia klávesnice Keepass2Android</string>
</resources>

View File

@@ -546,6 +546,7 @@
<string name="filestoragename_dropboxKP2A">Dropbox (mapa KP2A)</string>
<string name="filestoragehelp_dropboxKP2A">Če dostopa KP2A ne želite dati celemu Dropboxu, lahko izberete to možnost. Zahtevala bo samo dostop do mape Apps/Keepass2Android. To je posebej primerno ob ustvarjanju nove podatkovne zbirke. Če jo že imate, tapnite na to možnost, da ustvarite mapo, v njo vstavite svojo datoteko (iz računalnika) in ponovno izberete to možnost, da datoteko odprete.</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragehelp_gdrive">Opomba: Google omejuje dostop do storitve Google Drive iz aplikacij za vse več uporabnikov. Če vgrajena implementacija Googlovega pogona ne deluje, uporabite sistemski izbirnik datotek in tam izberite Googlov pogon!</string>
<string name="filestoragename_gdriveKP2A">Google Drive (KP2A datoteke)</string>
<string name="filestoragehelp_gdriveKP2A">Če KP2A ne želite omogočiti dostopa do celotnega Google Drive, lahko izberete to možnost. Upoštevajte, da morate najprej ustvariti novo datoteko baze podatkov, obstoječe datoteke niso vidne aplikaciji. To možnost izberite na zaslonu Ustvari bazo podatkov ali, če ste že odprli bazo podatkov, izvozite bazo podatkov in izberete to možnost.</string>
<string name="filestoragename_pcloud">PCloud (KP2A mapa)</string>
@@ -725,6 +726,7 @@
<item>Nadgrajen na uporabniški vmesnik Material 3</item>
<item>Izboljšanje samodejnega izpolnjevanja za delovanje z aplikacijami Compose</item>
<item>Popravek ujemanja gostiteljskega imena v samodejnem izpolnjevanju in iskanju</item>
<item>Odpravljena težava z generatorjem gesel</item>
</string-array>
<string-array name="ChangeLog_1_12_net">
<item>Nadgradnja OneDrive SDK na različico 5.68</item>
@@ -1197,5 +1199,6 @@
<string name="AutofillWarning_trustAsBrowser">Sprejmi vedno v \"%1$s\"</string>
<string name="kp2a_switch_on_sendgodone">Preklopi nazaj, ko je končano</string>
<string name="kp2a_switch_on_sendgodone_summary">Preklopi nazaj, ko pritisnete gumb za pošiljanje/prehajanje/končano</string>
<string name="qr_scanning_error_no_google_play_services">Za optično branje kode QR so potrebne storitve Google Play. V svojo napravo namestite ali posodobite storitve Google Play.</string>
<string name="english_ime_settings">Nastavitve tipkovnice Keepass2Android</string>
</resources>

View File

@@ -546,6 +546,7 @@
<string name="filestoragename_dropboxKP2A">Dropbox (thư mục KP2A)</string>
<string name="filestoragehelp_dropboxKP2A">Nếu bạn không muốn cấp cho KP2A quyền truy cập vào Dropbox đầy đủ của mình, bạn có thể chọn tùy chọn này. Nó sẽ chỉ yêu cầu quyền truy cập vào thư mục Apps/Keepass2Android. Điều này đặc biệt phù hợp khi tạo cơ sở dữ liệu mới. Nếu bạn đã có cơ sở dữ liệu, hãy chọn tùy chọn này để tạo thư mục, sau đó đặt tệp của bạn bên trong thư mục (từ PC của bạn) và sau đó chọn lại tùy chọn này để mở tệp.</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragehelp_gdrive">Xin lưu ý: Google ngày càng giới hạn quyền truy cập Google Drive của các ứng dụng đối với nhiều người dùng hơn. Nếu mục Google Drive không dùng được, hãy chọn Trình chọn tập tin của hệ thống sau đó chọn mục Google Drive!</string>
<string name="filestoragename_gdriveKP2A">Google Drive (tệp KP2A)</string>
<string name="filestoragehelp_gdriveKP2A">Nếu bạn không muốn cấp cho KP2A quyền truy cập vào Google Drive đầy đủ của mình, bạn có thể chọn tùy chọn này. Lưu ý rằng trước tiên bạn cần tạo tệp cơ sở dữ liệu, các tệp hiện có sẽ không hiển thị với ứng dụng. Chọn tùy chọn này từ màn hình Tạo cơ sở dữ liệu hoặc, nếu bạn đã mở cơ sở dữ liệu, bằng cách xuất cơ sở dữ liệu chọn tùy chọn này.</string>
<string name="filestoragename_pcloud">PCloud (thư mục KP2A)</string>
@@ -672,6 +673,26 @@
<string name="Continue">Tiếp tục</string>
<string name="NoFilenameWarning">URI bạn đã nhập không có dạng tên tập tin. Bạn có chắc chắn đây là tập tin hợp lệ không?</string>
<string name="FirstInvalidCompositeKeyError">Khoá hỗn hợp không hợp lệ. Xin thử lại.</string>
<string name="RepeatedInvalidCompositeKeyHelp"> Khoá tổng hợp không hợp lệ! Hãy thử các bước sau để mở khoá cơ sở dữ liệu của bạn:\n
• Nhập đúng mật khẩu. Ấn vào biểu tượng con mắt để hiện mật khẩu đã nhập.\n
• Chọn đúng loại mật khẩu. Loại mật khẩu phải giống với loại mật khẩu đã chọn khi tạo cơ sở dữ liệu.\n
• Chọn đúng tập tin cơ sở dữ liệu.
</string>
<string name="HintLocalBackupInvalidCompositeKey"> \n
• Gợi ý: Nếu bạn nghĩ rằng tập tin cơ sở dữ liệu bị hỏng hoặc bạn đã quên khoá chính sau khi thay đổi nó, bạn có thể nhấn \"%1$s\" và chọn bản sao lưu cục bộ để mở phiên bản tập tin được mở thành công gần đây nhất.
</string>
<string name="HintLocalBackupOtherError"> \n
• Gợi ý: Keepass2Android đã lưu trữ phiên bản tập tin được mở thành công gần đây nhất trên bộ nhớ trong. Bạn có thể mở nó bằng cách nhấn vào \"%1$s\" và chọn bản sao lưu cục bộ.
</string>
<string name="CorruptDatabaseHelp"> Tập tin bị hỏng. \n
Có một số mẹo giúp chẩn đoán vấn đề như sau:\n
• Nếu bạn sao chép tập tin qua USB (sử dụng chế độ MTP), hãy thử sao chép lại bằng một phần mềm như MyPhoneExplorer. Trong một số trường hợp, MTP có thể cắt bớt nội dung tập tin.\n
• Nếu không mở được tập tin ở cùng một vị trí trên máy tính, rất có thể tập tin đó thực sự bị hỏng. Trong trường hợp này, hãy sử dụng một bản sao lưu cơ sở dữ liệu. Nếu bạn nghi ngờ rằng Keepass2Android làm hỏng tập tin, hãy liên hệ với bộ phận hỗ trợ.\n
• Nếu tập tin vẫn mở được bằng máy tính, hãy liên hệ với bộ phận hỗ trợ. Bạn có thể thử lưu lại tập tin trên máy tính với cài đặt khác (ví dụ như không nén) sau đó thử mở lại bằng Keepass2Android.
</string>
<string name="open_other_db">Mở cơ sở dữ liệu khác…</string>
<string name="select_database">Chọn cơ sở dữ liệu</string>
<string name="configure_child_dbs">Định cấu hình cơ sở dữ liệu con…</string>

View File

@@ -546,6 +546,7 @@
<string name="filestoragename_dropboxKP2A">DropboxKP2A 文件夹)</string>
<string name="filestoragehelp_dropboxKP2A">如果您不想让 KP2A 访问整个 Dropbox您可以选中此选项。它将只有访问 Apps/Keepass2Android 文件夹的权限。特别适合创建一个新的数据库。如果您已经有一个数据库,触击此选项将创建该新文件夹,然后将您在电脑上的数据库文件放在此文件夹内,然后再选该选项,以打开该文件。</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragehelp_gdrive">请注意谷歌正在限制越来越多的用户从应用程序访问Google Drive。 如果内置的 Google Drive 实现无法正常工作,请使用系统文件选择器并在那里选择 Google Drive </string>
<string name="filestoragename_gdriveKP2A">Google Drive (KP2A文件)</string>
<string name="filestoragehelp_gdriveKP2A">如果您不想给予KP2A对您Google Drive的完整访问权您可以选择此选项。 请注意,您需要先创建一个数据库文件,现有文件对本应用不可见。 您可以从“创建数据库”屏幕中选择此选项,或者选择此选项导出数据库,前提是您已经打开了一个数据库。</string>
<string name="filestoragename_pcloud">PCloud (KP2A 文件夹)</string>
@@ -1158,5 +1159,6 @@ Initial public release
<string name="AutofillWarning_trustAsBrowser">总是在 \"%1$s 中接受</string>
<string name="kp2a_switch_on_sendgodone">完成后切换回来</string>
<string name="kp2a_switch_on_sendgodone_summary">按下发送/转到/完成时切换回来</string>
<string name="qr_scanning_error_no_google_play_services">二维码扫描需要 Google Play 服务。请在您的设备上安装或更新 Google Play 服务。</string>
<string name="english_ime_settings">键盘设置</string>
</resources>

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