Merge branch 'master' of https://github.com/PhilippC/keepass2android
# Resolved Conflicts: # src/keepass2android/Resources/values/strings.xml
This commit is contained in:
@@ -77,6 +77,10 @@ The KP2A keyboard is meant to quickly "paste" or "type" values from your databas
|
||||
## Is it safe to store my kdbx file in the cloud?
|
||||
While it may happen that someone gets access to your kdbx file in the cloud, there is still no need to worry: the purpose of encryption is to protect the data even in case someone gets the kdbx file! As long as you are using a safe master key, you're safe! [Key files](https://keepass.info/help/base/keys.html#keyfiles) can help with securing the database even more.
|
||||
|
||||
## Doesn't Keepass2Android create automatic backups?
|
||||
Yes and no. Yes: Keepass2Android stores the last successfully opened file as a read-only backup locally on the phone (unless you disable this is in the settings). This should make sure that even if the file gets destroyed during a save operation or gets deleted by accident, you should always have a version that can be opened. (Don't mix this up with the internal file cache which is not meant as a backup and can easily be overwritten even with a corrupt file. This internal file cache is meant for providing writable access even when the original file is not reachable, e.g. when you're offline.)
|
||||
No: The local backup has two shortcomings: It is only one backup and does not allow to revert to older versions. So if you deleted an entry from the database, it might be deleted in the local backup soon as well. The even more important shortcoming is that it is just a local backup. It won't help when your phone gets lost or broken. Please create additional backups on seperate storage!
|
||||
|
||||
## How do I backup the database?
|
||||
If you have stored your database on the cloud, you might rely on your cloud storage providers backups. Make sure they allow you to revert to older revisions in case the file gets corrupted for some reason.
|
||||
If you are working with a local database file, make sure you create regular backups. I suggest you have an aumotated mechanism, e.g. with FolderSync (Lite) which can copy local files from your device to other locations, e.g. your PC in a local network. You can also use USB or tools like MyPhoneExploror to transfer data to your PC. Or, you use a removable storage like an SD card which you keep in a safe place after making the backup.
|
||||
|
||||
@@ -375,13 +375,16 @@ namespace KeePassLib.Serialization
|
||||
|
||||
// See also KeePassKdb2x3.Export (KDBX 3.1 export module)
|
||||
uint minVersionForKeys = m_pwDatabase.MasterKey.UserKeys.Select(key => key.GetMinKdbxVersion()).Max();
|
||||
|
||||
|
||||
uint minRequiredVersion = Math.Max(minVersionForKeys, m_uFileVersion); //don't save a version lower than what we read
|
||||
|
||||
|
||||
AesKdf kdfAes = new AesKdf();
|
||||
if(!kdfAes.Uuid.Equals(m_pwDatabase.KdfParameters.KdfUuid))
|
||||
return Math.Max(FileVersion32, minVersionForKeys);
|
||||
return Math.Max(FileVersion32, minRequiredVersion);
|
||||
|
||||
if(m_pwDatabase.PublicCustomData.Count > 0)
|
||||
return Math.Max(FileVersion32, minVersionForKeys);
|
||||
return Math.Max(FileVersion32, minRequiredVersion);
|
||||
|
||||
|
||||
|
||||
@@ -401,9 +404,9 @@ namespace KeePassLib.Serialization
|
||||
gh(m_pwDatabase.RootGroup);
|
||||
m_pwDatabase.RootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
|
||||
if(bCustomData)
|
||||
return Math.Max(FileVersion32, minVersionForKeys);
|
||||
return Math.Max(FileVersion32, minRequiredVersion);
|
||||
|
||||
return Math.Max(FileVersion32_3, minVersionForKeys); ; // KDBX 3.1 is sufficient
|
||||
return Math.Max(FileVersion32_3, minRequiredVersion); ; // KDBX 3.1 is sufficient
|
||||
}
|
||||
|
||||
private void ComputeKeys(out byte[] pbCipherKey, int cbCipherKey,
|
||||
|
||||
@@ -31,15 +31,17 @@ namespace keepass2android
|
||||
private readonly PwEntry _entry;
|
||||
private readonly PwGroup _parentGroup;
|
||||
private readonly Activity _ctx;
|
||||
|
||||
public static AddEntry GetInstance(Activity ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish) {
|
||||
private readonly Database _db;
|
||||
|
||||
return new AddEntry(ctx, app, entry, parentGroup, finish);
|
||||
public static AddEntry GetInstance(Activity ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish, Database db) {
|
||||
|
||||
return new AddEntry(ctx, db, app, entry, parentGroup, finish);
|
||||
}
|
||||
|
||||
public AddEntry(Activity ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish):base(ctx, finish) {
|
||||
public AddEntry(Activity ctx, Database db, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish):base(ctx, finish) {
|
||||
_ctx = ctx;
|
||||
_parentGroup = parentGroup;
|
||||
_db = db;
|
||||
_parentGroup = parentGroup;
|
||||
_app = app;
|
||||
_entry = entry;
|
||||
|
||||
@@ -57,10 +59,13 @@ namespace keepass2android
|
||||
{
|
||||
_parentGroup.AddEntry(_entry, true);
|
||||
}
|
||||
|
||||
|
||||
// Commit to disk
|
||||
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
|
||||
|
||||
// Add entry to global
|
||||
_db.EntriesById[_entry.Uuid] = _entry;
|
||||
_db.Elements.Add(_entry);
|
||||
|
||||
// Commit to disk
|
||||
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
save.Run();
|
||||
}
|
||||
@@ -86,9 +91,7 @@ namespace keepass2android
|
||||
// Mark parent group dirty
|
||||
_app.DirtyGroups.Add(parent);
|
||||
|
||||
// Add entry to global
|
||||
_db.EntriesById[_entry.Uuid] = _entry;
|
||||
_db.Elements.Add(_entry);
|
||||
|
||||
|
||||
} else
|
||||
{
|
||||
|
||||
@@ -16,8 +16,8 @@ namespace keepass2android.database.edit
|
||||
{
|
||||
public class CopyEntry: AddEntry
|
||||
{
|
||||
public CopyEntry(Activity ctx, IKp2aApp app, PwEntry entry, OnFinish finish)
|
||||
: base(ctx, app, CreateCopy(entry, app), entry.ParentGroup, finish)
|
||||
public CopyEntry(Activity ctx, IKp2aApp app, PwEntry entry, OnFinish finish, Database db)
|
||||
: base(ctx, db, app, CreateCopy(entry, app), entry.ParentGroup, finish)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ 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, newEntry,item.Entry.ParentGroup,new ActionOnFinish(this, (success, message, activity) => ((ConfigureChildDatabasesActivity)activity).Update()));
|
||||
var addTask = new AddEntry(this, App.Kp2a.CurrentDb, App.Kp2a, newEntry,item.Entry.ParentGroup,new ActionOnFinish(this, (success, message, activity) => ((ConfigureChildDatabasesActivity)activity).Update()));
|
||||
|
||||
ProgressTask pt = new ProgressTask(App.Kp2a, this, addTask);
|
||||
pt.Run();
|
||||
@@ -366,7 +366,7 @@ namespace keepass2android
|
||||
{KeeAutoExecExt.ThisDeviceId, true}
|
||||
})));
|
||||
|
||||
var addTask = new AddEntry(this, App.Kp2a, newEntry, autoOpenGroup, new ActionOnFinish(this, (success, message, activity) => (activity as ConfigureChildDatabasesActivity)?.Update()));
|
||||
var addTask = new AddEntry(this, db, App.Kp2a, newEntry, autoOpenGroup, new ActionOnFinish(this, (success, message, activity) => (activity as ConfigureChildDatabasesActivity)?.Update()));
|
||||
|
||||
ProgressTask pt = new ProgressTask(App.Kp2a, this, addTask);
|
||||
pt.Run();
|
||||
|
||||
@@ -506,7 +506,7 @@ namespace keepass2android
|
||||
},closeOrShowError);
|
||||
|
||||
if ( State.IsNew ) {
|
||||
runnable = AddEntry.GetInstance(this, App.Kp2a, newEntry, State.ParentGroup, afterAddEntry);
|
||||
runnable = AddEntry.GetInstance(this, App.Kp2a, newEntry, State.ParentGroup, afterAddEntry, db);
|
||||
} else {
|
||||
runnable = new UpdateEntry(this, App.Kp2a, initialEntry, newEntry, closeOrShowError);
|
||||
}
|
||||
|
||||
@@ -1390,7 +1390,7 @@ namespace keepass2android
|
||||
case Resource.Id.menu_copy:
|
||||
|
||||
var copyTask = new CopyEntry((GroupBaseActivity)Activity, App.Kp2a, (PwEntry)checkedItems.First(),
|
||||
new GroupBaseActivity.RefreshTask(handler, ((GroupBaseActivity)Activity)));
|
||||
new GroupBaseActivity.RefreshTask(handler, ((GroupBaseActivity)Activity)), App.Kp2a.CurrentDb);
|
||||
|
||||
ProgressTask pt = new ProgressTask(App.Kp2a, Activity, copyTask);
|
||||
pt.Run();
|
||||
|
||||
@@ -54,13 +54,9 @@ namespace keepass2android
|
||||
{
|
||||
_design.ApplyTheme();
|
||||
base.OnCreate(savedInstanceState);
|
||||
|
||||
|
||||
if (PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(
|
||||
GetString(Resource.String.ViewDatabaseSecure_key), true))
|
||||
{
|
||||
Window.SetFlags(WindowManagerFlags.Secure, WindowManagerFlags.Secure);
|
||||
}
|
||||
|
||||
Util.MakeSecureDisplay(this);
|
||||
|
||||
|
||||
_ioc = App.Kp2a.CurrentDb?.Ioc;
|
||||
|
||||
@@ -43,13 +43,9 @@ namespace keepass2android
|
||||
{
|
||||
_design.ApplyTheme();
|
||||
base.OnCreate(savedInstanceState);
|
||||
|
||||
|
||||
if (PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(
|
||||
GetString(Resource.String.ViewDatabaseSecure_key), true))
|
||||
{
|
||||
Window.SetFlags(WindowManagerFlags.Secure, WindowManagerFlags.Secure);
|
||||
}
|
||||
|
||||
Util.MakeSecureDisplay(this);
|
||||
|
||||
_ioc = App.Kp2a.CurrentDb.Ioc;
|
||||
|
||||
|
||||
54
src/keepass2android/NoSecureDisplayActivity.cs
Normal file
54
src/keepass2android/NoSecureDisplayActivity.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Support.V7.App;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
[Activity(Label = AppNames.AppName, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden, Theme = "@style/MyTheme_Blue",
|
||||
LaunchMode = LaunchMode.SingleInstance)]
|
||||
public class NoSecureDisplayActivity : AppCompatActivity
|
||||
{
|
||||
private readonly ActivityDesign _design;
|
||||
|
||||
public NoSecureDisplayActivity()
|
||||
{
|
||||
_design = new ActivityDesign(this);
|
||||
}
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
_design.ApplyTheme();
|
||||
base.OnCreate(savedInstanceState);
|
||||
SetContentView(Resource.Layout.no_secure_display_layout);
|
||||
FindViewById<Button>(Resource.Id.btn_goto_settings).Click += (sender, args) =>
|
||||
{
|
||||
AppSettingsActivity.Launch(this);
|
||||
};
|
||||
|
||||
var toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.mytoolbar);
|
||||
|
||||
SetSupportActionBar(toolbar);
|
||||
|
||||
SupportActionBar.Title = AppNames.AppName;
|
||||
}
|
||||
|
||||
protected override void OnResume()
|
||||
{
|
||||
base.OnResume();
|
||||
_design.ReapplyTheme();
|
||||
//close if displays changed
|
||||
if (!Util.SecureDisplayConfigured(this) || !Util.HasUnsecureDisplay(this))
|
||||
Finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -646,16 +646,12 @@ namespace keepass2android
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.AddAction(Intent.ActionScreenOff);
|
||||
RegisterReceiver(_intentReceiver, filter);
|
||||
|
||||
|
||||
//use FlagSecure to make sure the last (revealed) character of the master password is not visible in recent apps
|
||||
if (PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(
|
||||
GetString(Resource.String.ViewDatabaseSecure_key), true))
|
||||
{
|
||||
Window.SetFlags(WindowManagerFlags.Secure, WindowManagerFlags.Secure);
|
||||
}
|
||||
|
||||
Intent i = Intent;
|
||||
|
||||
//use FlagSecure to make sure the last (revealed) character of the master password is not visible in recent apps
|
||||
Util.MakeSecureDisplay(this);
|
||||
|
||||
Intent i = Intent;
|
||||
|
||||
|
||||
String action = i.Action;
|
||||
@@ -2147,7 +2143,7 @@ namespace keepass2android
|
||||
KeyProviderQueryContext ctx = new KeyProviderQueryContext(_act._ioConnection, false, false);
|
||||
|
||||
if (!OathHotpKeyProv.CreateAuxFile(_act._otpInfo, ctx, _act._otpAuxIoc))
|
||||
Toast.MakeText(_act, _act.GetString(Resource.String.ErrorUpdatingOtpAuxFile), ToastLength.Long).Show();
|
||||
ShowError(_act.GetString(Resource.String.ErrorUpdatingOtpAuxFile));
|
||||
|
||||
|
||||
}
|
||||
@@ -2155,8 +2151,7 @@ namespace keepass2android
|
||||
{
|
||||
Kp2aLog.LogUnexpectedError(e);
|
||||
|
||||
Toast.MakeText(_act, _act.GetString(Resource.String.ErrorUpdatingOtpAuxFile) + " " + e.Message,
|
||||
ToastLength.Long).Show();
|
||||
ShowError( _act.GetString(Resource.String.ErrorUpdatingOtpAuxFile) + " " + e.Message);
|
||||
}
|
||||
|
||||
|
||||
@@ -2169,7 +2164,11 @@ namespace keepass2android
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void ShowError(string message)
|
||||
{
|
||||
App.Kp2a.ShowToast(message);
|
||||
}
|
||||
}
|
||||
private class PasswordActivityBroadcastReceiver : BroadcastReceiver
|
||||
{
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace keepass2android
|
||||
String nameX = x.Strings.ReadSafe(PwDefs.TitleField);
|
||||
String nameY = y.Strings.ReadSafe(PwDefs.TitleField);
|
||||
if (nameX.ToLower() != nameY.ToLower())
|
||||
return String.Compare(nameX, nameY, StringComparison.OrdinalIgnoreCase);
|
||||
return String.Compare(nameX, nameY, StringComparison.CurrentCultureIgnoreCase);
|
||||
else
|
||||
{
|
||||
if (PwDefs.IsTanEntry(x) && PwDefs.IsTanEntry(y))
|
||||
@@ -138,7 +138,7 @@ namespace keepass2android
|
||||
{
|
||||
//ignore
|
||||
}
|
||||
return String.Compare(userX, userY, StringComparison.OrdinalIgnoreCase);
|
||||
return String.Compare(userX, userY, StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace keepass2android
|
||||
|
||||
public int CompareGroups(PwGroup a, PwGroup b)
|
||||
{
|
||||
return String.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase);
|
||||
return String.Compare(a.Name, b.Name, StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,11 +59,7 @@ namespace keepass2android
|
||||
base.OnCreate(bundle);
|
||||
|
||||
//use FlagSecure to make sure the last (revealed) character of the password is not visible in recent apps
|
||||
if (PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(
|
||||
GetString(Resource.String.ViewDatabaseSecure_key), true))
|
||||
{
|
||||
Window.SetFlags(WindowManagerFlags.Secure, WindowManagerFlags.Secure);
|
||||
}
|
||||
Util.MakeSecureDisplay(this);
|
||||
|
||||
_ioc = App.Kp2a.GetDbForQuickUnlock()?.Ioc;
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?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"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:paddingTop="16dp">
|
||||
<android.support.v7.widget.Toolbar android:id="@+id/mytoolbar"
|
||||
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
|
||||
app:titleTextStyle="@style/MyTitleTextStyle"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:background="#00000000" />
|
||||
|
||||
<TextView android:text="@string/no_secure_display" android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<Button android:id="@+id/btn_goto_settings" android:text="@string/IconVisibilityInfo_Android8_btnSettings" android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1217,6 +1217,7 @@ Initial public release
|
||||
<string name="emergency_infotext_head">Are you prepared for emergency cases?</string>
|
||||
<string name="emergency_infotext_main">Did you ever consider what happens if you are no longer able to access your password database? What if you have an accident? It is good practice to pass your master key to some trusted person for emergency cases. Nobody will have access to your passwords otherwise.</string>
|
||||
|
||||
<string name="no_secure_display">The currently valid display is not marked as secure. This means that screenshots might be taken by other apps. Keepass2Android is configured to display sensitive information on secure displays only. Please change to a secure display (e.g. by detaching an HDMI monitor) or change the app settings.</string>
|
||||
<string name="no_secure_display">The currently valid display is not marked as secure. This means that screenshots might be taken by other apps. Keepass2Android is configured to display sensitive information on secure displays only. Please change to a secure display (e.g. by detaching an HDMI monitor) or change the app settings.</string>
|
||||
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -329,6 +329,7 @@ namespace keepass2android
|
||||
//database loaded
|
||||
if (App.Kp2a.QuickLocked)
|
||||
{
|
||||
AppTask.CanActivateSearchViewOnStart = true;
|
||||
var i = new Intent(this, typeof(QuickUnlock));
|
||||
Util.PutIoConnectionToIntent(App.Kp2a.GetDbForQuickUnlock().Ioc, i);
|
||||
Kp2aLog.Log("Starting QuickUnlock");
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace PluginTOTP
|
||||
{
|
||||
class UpdateTotpTimerTask: TimerTask
|
||||
{
|
||||
private const string _totp = "TOTP";
|
||||
public const string TotpKey = "TOTP";
|
||||
private readonly Context _context;
|
||||
private readonly ITotpPluginAdapter _adapter;
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace PluginTOTP
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Android.Util.Log.Debug(_totp, e.ToString());
|
||||
Android.Util.Log.Debug(TotpKey, e.ToString());
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace PluginTOTP
|
||||
private void UpdateEntryData(string totp)
|
||||
{
|
||||
//update the Entry output in the App database and notify the CopyToClipboard service
|
||||
App.Kp2a.LastOpenedEntry.OutputStrings.Set(_totp, new ProtectedString(true, totp));
|
||||
App.Kp2a.LastOpenedEntry.OutputStrings.Set(TotpKey, new ProtectedString(true, totp));
|
||||
Intent updateKeyboardIntent = new Intent(_context, typeof(CopyToClipboardService));
|
||||
updateKeyboardIntent.SetAction(Intents.UpdateKeyboard);
|
||||
updateKeyboardIntent.PutExtra(EntryActivity.KeyEntry, new ElementAndDatabaseId(App.Kp2a.FindDatabaseForElement(App.Kp2a.LastOpenedEntry.Entry), App.Kp2a.LastOpenedEntry.Entry).FullId);
|
||||
@@ -74,7 +74,7 @@ namespace PluginTOTP
|
||||
i.PutExtra(Strings.ExtraSender, _context.PackageName);
|
||||
i.PutExtra(Strings.ExtraFieldValue, totp);
|
||||
i.PutExtra(Strings.ExtraEntryId, App.Kp2a.LastOpenedEntry.Entry.Uuid.ToHexString());
|
||||
i.PutExtra(Strings.ExtraFieldId, _totp);
|
||||
i.PutExtra(Strings.ExtraFieldId, TotpKey);
|
||||
i.PutExtra(Strings.ExtraFieldProtected, true);
|
||||
|
||||
_context.SendBroadcast(i);
|
||||
|
||||
@@ -19,6 +19,7 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Database;
|
||||
@@ -31,6 +32,7 @@ using Android.Content.PM;
|
||||
using Android.Content.Res;
|
||||
using Android.Graphics;
|
||||
using Android.Graphics.Drawables;
|
||||
using Android.Hardware.Display;
|
||||
using Android.Util;
|
||||
using KeePassLib.Serialization;
|
||||
using Uri = Android.Net.Uri;
|
||||
@@ -129,13 +131,41 @@ namespace keepass2android
|
||||
|
||||
public static void CopyToClipboard(Context context, String text) {
|
||||
Android.Content.ClipboardManager clipboardManager = (ClipboardManager)context.GetSystemService(Context.ClipboardService);
|
||||
if (text == "")
|
||||
text = "***";
|
||||
ClipData clipData = Android.Content.ClipData.NewPlainText("KP2A", text);
|
||||
clipboardManager.PrimaryClip = clipData;
|
||||
}
|
||||
|
||||
public static void GotoUrl(Context context, String url) {
|
||||
if (text == "")
|
||||
{
|
||||
//on some devices, adding empty text does not seem to work. Try again with some garbage.
|
||||
clipData = Android.Content.ClipData.NewPlainText("KP2A", "***");
|
||||
clipboardManager.PrimaryClip = clipData;
|
||||
//seems to work better on some devices:
|
||||
try
|
||||
{
|
||||
clipboardManager.Text = text;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Kp2aLog.LogUnexpectedError(exception);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static readonly Regex ARC_DEVICE_PATTERN = new Regex(".+_cheets|cheets_.+");
|
||||
|
||||
public static bool IsChromeOS(Context context)
|
||||
{
|
||||
return
|
||||
context.PackageManager.HasSystemFeature(
|
||||
"org.chromium.arc.device_management") // https://stackoverflow.com/a/39843396/292233
|
||||
|| (Build.Device != null && ARC_DEVICE_PATTERN.IsMatch(Build.Device))
|
||||
;
|
||||
}
|
||||
|
||||
public static void GotoUrl(Context context, String url) {
|
||||
if ( !string.IsNullOrEmpty(url) ) {
|
||||
|
||||
if (url.StartsWith("androidapp://"))
|
||||
@@ -567,6 +597,43 @@ namespace keepass2android
|
||||
"file" : displayPath.Substring(0, protocolSeparatorPos);
|
||||
return protocolId;
|
||||
}
|
||||
|
||||
public static void MakeSecureDisplay(Activity context)
|
||||
{
|
||||
if (SecureDisplayConfigured(context))
|
||||
{
|
||||
var hasUnsecureDisplay = HasUnsecureDisplay(context);
|
||||
if (hasUnsecureDisplay)
|
||||
{
|
||||
var intent = new Intent(context, typeof(NoSecureDisplayActivity));
|
||||
intent.AddFlags(ActivityFlags.SingleTop | ActivityFlags.ClearTop);
|
||||
context.StartActivityForResult(intent,9999);
|
||||
}
|
||||
context.Window.SetFlags(WindowManagerFlags.Secure, WindowManagerFlags.Secure);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SecureDisplayConfigured(Activity context)
|
||||
{
|
||||
return PreferenceManager.GetDefaultSharedPreferences(context).GetBoolean(
|
||||
context.GetString(Resource.String.ViewDatabaseSecure_key), true);
|
||||
}
|
||||
|
||||
public static bool HasUnsecureDisplay(Activity context)
|
||||
{
|
||||
bool hasUnsecureDisplay = false;
|
||||
if ((int) Build.VERSION.SdkInt >= 17)
|
||||
{
|
||||
foreach (var display in ((DisplayManager) context.GetSystemService(Context.DisplayService)).GetDisplays())
|
||||
{
|
||||
if ((display.Flags & DisplayFlags.Secure) == 0)
|
||||
{
|
||||
hasUnsecureDisplay = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasUnsecureDisplay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,8 @@ namespace keepass2android
|
||||
|
||||
public const String CopyUsername = "keepass2android.copy_username";
|
||||
public const String CopyPassword = "keepass2android.copy_password";
|
||||
public const String CheckKeyboard = "keepass2android.check_keyboard";
|
||||
public const String CopyTotp = "keepass2android.copy_totp";
|
||||
public const String CheckKeyboard = "keepass2android.check_keyboard";
|
||||
|
||||
public const String StartWithOtp = "keepass2android.startWithOtp";
|
||||
public const String OtpExtraKey = "keepass2android.Otp";
|
||||
|
||||
@@ -315,6 +315,7 @@
|
||||
<Compile Include="KpEntryTemplatedEdit.cs" />
|
||||
<Compile Include="MeasuringRelativeLayout.cs" />
|
||||
<Compile Include="NfcOtpActivity.cs" />
|
||||
<Compile Include="NoSecureDisplayActivity.cs" />
|
||||
<Compile Include="PasswordFont.cs" />
|
||||
<Compile Include="pluginhost\PluginArrayAdapter.cs" />
|
||||
<Compile Include="pluginhost\PluginDatabase.cs" />
|
||||
@@ -2021,6 +2022,11 @@
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_storage_onedrive2.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\layout\no_secure_display_layout.axml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" />
|
||||
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
|
||||
|
||||
@@ -84,11 +84,24 @@ namespace keepass2android.search
|
||||
if (intent.Action == Intent.ActionView)
|
||||
{
|
||||
var entryIntent = new Intent(this, typeof(EntryActivity));
|
||||
entryIntent.PutExtra(EntryActivity.KeyEntry, intent.Data.LastPathSegment);
|
||||
entryIntent.AddFlags(ActivityFlags.ForwardResult);
|
||||
Finish(); // Close this activity so that the entry activity is navigated to from the main activity, not this one.
|
||||
StartActivity(entryIntent);
|
||||
}
|
||||
ElementAndDatabaseId id;
|
||||
try
|
||||
{
|
||||
id = new ElementAndDatabaseId(intent.Data.LastPathSegment);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Kp2aLog.Log("Failed to transform " + intent.Data.LastPathSegment + " to an ElementAndDatabaseId object. ");
|
||||
Toast.MakeText(this, "Bad path passed. Please provide database and element ID.", ToastLength.Long).Show();
|
||||
Finish();
|
||||
return;
|
||||
}
|
||||
entryIntent.PutExtra(EntryActivity.KeyEntry, id.FullId);
|
||||
entryIntent.AddFlags(ActivityFlags.ForwardResult);
|
||||
Finish(); // Close this activity so that the entry activity is navigated to from the main activity, not this one.
|
||||
StartActivity(entryIntent);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Action may either by ActionSearch (from search widget) or null (if called from SearchActivity directly)
|
||||
|
||||
@@ -36,6 +36,7 @@ using KeePassLib.Utility;
|
||||
using Android.Views.InputMethods;
|
||||
using KeePass.Util.Spr;
|
||||
using KeePassLib.Serialization;
|
||||
using PluginTOTP;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
@@ -61,6 +62,7 @@ namespace keepass2android
|
||||
|
||||
private bool _hasPassword;
|
||||
private bool _hasUsername;
|
||||
private bool _hasTotp;
|
||||
private bool _hasKeyboard;
|
||||
|
||||
public void AddPasswordAccess()
|
||||
@@ -72,6 +74,10 @@ namespace keepass2android
|
||||
{
|
||||
_hasUsername = true;
|
||||
}
|
||||
public void AddTotpAccess()
|
||||
{
|
||||
_hasTotp = true;
|
||||
}
|
||||
|
||||
public void AddKeyboardAccess()
|
||||
{
|
||||
@@ -96,7 +102,7 @@ namespace keepass2android
|
||||
|
||||
private int CreateCombinedNotification(string entryName, Bitmap entryIcon)
|
||||
{
|
||||
if ((!_hasUsername) && (!_hasPassword) && (!_hasKeyboard))
|
||||
if ((!_hasUsername) && (!_hasPassword) && (!_hasKeyboard) && (!_hasTotp))
|
||||
return 0;
|
||||
|
||||
NotificationCompat.Builder notificationBuilder;
|
||||
@@ -121,6 +127,10 @@ namespace keepass2android
|
||||
notificationBuilder.AddAction(new NotificationCompat.Action(Resource.Drawable.ic_action_password,
|
||||
_ctx.GetString(Resource.String.menu_copy_pass),
|
||||
GetPendingIntent(Intents.CopyPassword, Resource.String.menu_copy_pass)));
|
||||
if (_hasTotp)
|
||||
notificationBuilder.AddAction(new NotificationCompat.Action(Resource.Drawable.ic_action_password,
|
||||
_ctx.GetString(Resource.String.menu_copy_totp),
|
||||
GetPendingIntent(Intents.CopyTotp, Resource.String.menu_copy_totp)));
|
||||
|
||||
notificationBuilder.SetPriority((int)Android.App.NotificationPriority.Max);
|
||||
var notification = notificationBuilder.Build();
|
||||
@@ -151,6 +161,15 @@ namespace keepass2android
|
||||
_notificationManager.Notify(NotifyUsername, username);
|
||||
numNotifications++;
|
||||
}
|
||||
if (_hasTotp)
|
||||
{
|
||||
// only show notification if totp is available
|
||||
Notification totp = GetNotification(Intents.CopyTotp, Resource.String.copy_totp,
|
||||
Resource.Drawable.ic_action_password, entryName, entryIcon);
|
||||
totp.DeleteIntent = CreateDeleteIntent(NotifyTotp);
|
||||
_notificationManager.Notify(NotifyTotp, totp);
|
||||
numNotifications++;
|
||||
}
|
||||
if (_hasKeyboard)
|
||||
{
|
||||
// only show notification if username is available
|
||||
@@ -222,6 +241,8 @@ namespace keepass2android
|
||||
pending = PendingIntent.GetBroadcast(_ctx, descResId, intent, PendingIntentFlags.CancelCurrent);
|
||||
return pending;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public const int NotifyUsername = 1;
|
||||
@@ -229,6 +250,7 @@ namespace keepass2android
|
||||
public const int NotifyKeyboard = 3;
|
||||
public const int ClearClipboard = 4;
|
||||
public const int NotifyCombined = 5;
|
||||
public const int NotifyTotp = 6;
|
||||
|
||||
static public void CopyValueToClipboardWithTimeout(Context ctx, string text)
|
||||
{
|
||||
@@ -442,6 +464,10 @@ namespace keepass2android
|
||||
{
|
||||
notBuilder.AddUsernameAccess();
|
||||
}
|
||||
if (entry.OutputStrings.ReadSafe(UpdateTotpTimerTask.TotpKey).Length > 0)
|
||||
{
|
||||
notBuilder.AddTotpAccess();
|
||||
}
|
||||
}
|
||||
|
||||
bool hasKeyboardDataNow = false;
|
||||
@@ -884,6 +910,15 @@ namespace keepass2android
|
||||
}
|
||||
context.SendBroadcast(new Intent(Intent.ActionCloseSystemDialogs)); //close notification drawer
|
||||
}
|
||||
else if (action.Equals(Intents.CopyTotp))
|
||||
{
|
||||
String totp = App.Kp2a.LastOpenedEntry.OutputStrings.ReadSafe(UpdateTotpTimerTask.TotpKey);
|
||||
if (totp.Length > 0)
|
||||
{
|
||||
CopyToClipboardService.CopyValueToClipboardWithTimeout(context, totp);
|
||||
}
|
||||
context.SendBroadcast(new Intent(Intent.ActionCloseSystemDialogs)); //close notification drawer
|
||||
}
|
||||
else if (action.Equals(Intents.CheckKeyboard))
|
||||
{
|
||||
CopyToClipboardService.ActivateKeyboard(context);
|
||||
|
||||
Reference in New Issue
Block a user