mirror of
https://github.com/moparisthebest/keepass2android
synced 2024-11-22 17:22:17 -05:00
Merge https://git01.codeplex.com/forks/alexvallat/keepass2androidperfopt into AlexVallat/Keepass2AndroidPerfOpt
This commit is contained in:
commit
d90407fd2d
@ -40,8 +40,14 @@ namespace KeePassLib.Collections
|
||||
IDeepCloneable<ProtectedBinaryDictionary>,
|
||||
IEnumerable<KeyValuePair<string, ProtectedBinary>>
|
||||
{
|
||||
/*
|
||||
private SortedDictionary<string, ProtectedBinary> m_vBinaries =
|
||||
new SortedDictionary<string, ProtectedBinary>();
|
||||
*/
|
||||
|
||||
private Dictionary<string, ProtectedBinary> m_vBinaries =
|
||||
new Dictionary<string, ProtectedBinary>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of binaries in this entry.
|
||||
|
@ -40,8 +40,9 @@ namespace KeePassLib.Collections
|
||||
IDeepCloneable<ProtectedStringDictionary>,
|
||||
IEnumerable<KeyValuePair<string, ProtectedString>>
|
||||
{
|
||||
private SortedDictionary<string, ProtectedString> m_vStrings =
|
||||
new SortedDictionary<string, ProtectedString>();
|
||||
/*private SortedDictionary<string, ProtectedString> m_vStrings =
|
||||
new SortedDictionary<string, ProtectedString>();*/
|
||||
private Dictionary<string, ProtectedString> m_vStrings = new Dictionary<string, ProtectedString>();
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of strings in this entry.
|
||||
|
@ -101,5 +101,19 @@ namespace KeePassLib.Interfaces
|
||||
/// </summary>
|
||||
/// <param name="bModified">Update last modification time.</param>
|
||||
void Touch(bool bModified);
|
||||
|
||||
#region Set times lazily
|
||||
// Passing xml datetime string to be parsed only on demand
|
||||
|
||||
void SetLazyLastModificationTime(string xmlDateTime);
|
||||
|
||||
void SetLazyCreationTime(string xmlDateTime);
|
||||
|
||||
void SetLazyLastAccessTime(string xmlDateTime);
|
||||
|
||||
void SetLazyExpiryTime(string xmlDateTime);
|
||||
|
||||
void SetLazyLocationChanged(string xmlDateTime);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>False</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<DefineConstants>TRACE;DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>False</ConsolePause>
|
||||
|
@ -40,6 +40,7 @@ namespace KeePassLib
|
||||
private PwUuid m_uuid = PwUuid.Zero;
|
||||
private PwGroup m_pParentGroup = null;
|
||||
private DateTime m_tParentGroupLastMod = PwDefs.DtDefaultNow;
|
||||
private string m_tParentGroupLastModLazy;
|
||||
|
||||
private ProtectedStringDictionary m_listStrings = new ProtectedStringDictionary();
|
||||
private ProtectedBinaryDictionary m_listBinaries = new ProtectedBinaryDictionary();
|
||||
@ -56,6 +57,12 @@ namespace KeePassLib
|
||||
private DateTime m_tLastMod = PwDefs.DtDefaultNow;
|
||||
private DateTime m_tLastAccess = PwDefs.DtDefaultNow;
|
||||
private DateTime m_tExpire = PwDefs.DtDefaultNow;
|
||||
|
||||
private string m_tCreationLazy;
|
||||
private string m_tLastModLazy;
|
||||
private string m_tLastAccessLazy;
|
||||
private string m_tExpireLazy;
|
||||
|
||||
private bool m_bExpires = false;
|
||||
private ulong m_uUsageCount = 0;
|
||||
|
||||
@ -92,8 +99,13 @@ namespace KeePassLib
|
||||
/// </summary>
|
||||
public DateTime LocationChanged
|
||||
{
|
||||
get { return m_tParentGroupLastMod; }
|
||||
set { m_tParentGroupLastMod = value; }
|
||||
get { return GetLazyTime(ref m_tParentGroupLastModLazy, ref m_tParentGroupLastMod); }
|
||||
set { m_tParentGroupLastMod = value; m_tParentGroupLastModLazy = null; }
|
||||
}
|
||||
|
||||
public void SetLazyLocationChanged(string xmlDateTime)
|
||||
{
|
||||
m_tParentGroupLastModLazy = xmlDateTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -195,8 +207,13 @@ namespace KeePassLib
|
||||
/// </summary>
|
||||
public DateTime CreationTime
|
||||
{
|
||||
get { return m_tCreation; }
|
||||
set { m_tCreation = value; }
|
||||
get { return GetLazyTime(ref m_tCreationLazy, ref m_tCreation); }
|
||||
set { m_tCreation = value; m_tCreationLazy = null; }
|
||||
}
|
||||
|
||||
public void SetLazyCreationTime(string xmlDateTime)
|
||||
{
|
||||
m_tCreationLazy = xmlDateTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -204,8 +221,13 @@ namespace KeePassLib
|
||||
/// </summary>
|
||||
public DateTime LastAccessTime
|
||||
{
|
||||
get { return m_tLastAccess; }
|
||||
set { m_tLastAccess = value; }
|
||||
get { return GetLazyTime(ref m_tLastAccessLazy, ref m_tLastAccess); }
|
||||
set { m_tLastAccess = value; m_tLastAccessLazy = null; }
|
||||
}
|
||||
|
||||
public void SetLazyLastAccessTime(string xmlDateTime)
|
||||
{
|
||||
m_tLastAccessLazy = xmlDateTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -213,8 +235,13 @@ namespace KeePassLib
|
||||
/// </summary>
|
||||
public DateTime LastModificationTime
|
||||
{
|
||||
get { return m_tLastMod; }
|
||||
set { m_tLastMod = value; }
|
||||
get { return GetLazyTime(ref m_tLastModLazy, ref m_tLastMod); }
|
||||
set { m_tLastMod = value; m_tLastModLazy = null; }
|
||||
}
|
||||
|
||||
public void SetLazyLastModificationTime(string xmlDateTime)
|
||||
{
|
||||
m_tLastModLazy = xmlDateTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -223,8 +250,13 @@ namespace KeePassLib
|
||||
/// </summary>
|
||||
public DateTime ExpiryTime
|
||||
{
|
||||
get { return m_tExpire; }
|
||||
set { m_tExpire = value; }
|
||||
get { return GetLazyTime(ref m_tExpireLazy, ref m_tExpire); }
|
||||
set { m_tExpire = value; m_tExpireLazy = null; }
|
||||
}
|
||||
|
||||
public void SetLazyExpiryTime(string xmlDateTime)
|
||||
{
|
||||
m_tExpireLazy = xmlDateTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -333,6 +365,7 @@ namespace KeePassLib
|
||||
peNew.m_uuid = m_uuid; // PwUuid is immutable
|
||||
peNew.m_pParentGroup = m_pParentGroup;
|
||||
peNew.m_tParentGroupLastMod = m_tParentGroupLastMod;
|
||||
peNew.m_tParentGroupLastModLazy = m_tParentGroupLastModLazy;
|
||||
|
||||
peNew.m_listStrings = m_listStrings.CloneDeep();
|
||||
peNew.m_listBinaries = m_listBinaries.CloneDeep();
|
||||
@ -351,6 +384,11 @@ namespace KeePassLib
|
||||
peNew.m_tExpire = m_tExpire;
|
||||
peNew.m_bExpires = m_bExpires;
|
||||
peNew.m_uUsageCount = m_uUsageCount;
|
||||
|
||||
peNew.m_tCreationLazy = m_tCreationLazy;
|
||||
peNew.m_tLastModLazy = m_tLastModLazy;
|
||||
peNew.m_tLastAccessLazy = m_tLastAccessLazy;
|
||||
peNew.m_tExpireLazy = m_tExpireLazy;
|
||||
|
||||
peNew.m_strOverrideUrl = m_strOverrideUrl;
|
||||
|
||||
@ -365,6 +403,7 @@ namespace KeePassLib
|
||||
|
||||
peNew.m_uuid = m_uuid; // PwUuid is immutable
|
||||
peNew.m_tParentGroupLastMod = m_tParentGroupLastMod;
|
||||
peNew.m_tParentGroupLastModLazy = m_tParentGroupLastModLazy;
|
||||
// Do not assign m_pParentGroup
|
||||
|
||||
return peNew;
|
||||
@ -417,7 +456,7 @@ namespace KeePassLib
|
||||
if((pwOpt & PwCompareOptions.IgnoreParentGroup) == PwCompareOptions.None)
|
||||
{
|
||||
if(m_pParentGroup != pe.m_pParentGroup) return false;
|
||||
if(!bIgnoreLastMod && (m_tParentGroupLastMod != pe.m_tParentGroupLastMod))
|
||||
if(!bIgnoreLastMod && (LocationChanged != pe.LocationChanged))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -461,10 +500,10 @@ namespace KeePassLib
|
||||
if(m_clrForeground != pe.m_clrForeground) return false;
|
||||
if(m_clrBackground != pe.m_clrBackground) return false;
|
||||
|
||||
if(m_tCreation != pe.m_tCreation) return false;
|
||||
if(!bIgnoreLastMod && (m_tLastMod != pe.m_tLastMod)) return false;
|
||||
if(!bIgnoreLastAccess && (m_tLastAccess != pe.m_tLastAccess)) return false;
|
||||
if(m_tExpire != pe.m_tExpire) return false;
|
||||
if(CreationTime != pe.CreationTime) return false;
|
||||
if(!bIgnoreLastMod && (LastModificationTime != pe.LastModificationTime)) return false;
|
||||
if(!bIgnoreLastAccess && (LastAccessTime != pe.LastAccessTime)) return false;
|
||||
if(ExpiryTime != pe.ExpiryTime) return false;
|
||||
if(m_bExpires != pe.m_bExpires) return false;
|
||||
if(!bIgnoreLastAccess && (m_uUsageCount != pe.m_uUsageCount)) return false;
|
||||
|
||||
@ -494,14 +533,14 @@ namespace KeePassLib
|
||||
{
|
||||
Debug.Assert(peTemplate != null); if(peTemplate == null) throw new ArgumentNullException("peTemplate");
|
||||
|
||||
if(bOnlyIfNewer && (peTemplate.m_tLastMod < m_tLastMod)) return;
|
||||
if(bOnlyIfNewer && (peTemplate.LastModificationTime < LastModificationTime)) return;
|
||||
|
||||
// Template UUID should be the same as the current one
|
||||
Debug.Assert(m_uuid.EqualsValue(peTemplate.m_uuid));
|
||||
m_uuid = peTemplate.m_uuid;
|
||||
|
||||
if(bAssignLocationChanged)
|
||||
m_tParentGroupLastMod = peTemplate.m_tParentGroupLastMod;
|
||||
m_tParentGroupLastMod = peTemplate.LocationChanged;
|
||||
|
||||
m_listStrings = peTemplate.m_listStrings;
|
||||
m_listBinaries = peTemplate.m_listBinaries;
|
||||
@ -514,10 +553,10 @@ namespace KeePassLib
|
||||
m_clrForeground = peTemplate.m_clrForeground;
|
||||
m_clrBackground = peTemplate.m_clrBackground;
|
||||
|
||||
m_tCreation = peTemplate.m_tCreation;
|
||||
m_tLastMod = peTemplate.m_tLastMod;
|
||||
m_tLastAccess = peTemplate.m_tLastAccess;
|
||||
m_tExpire = peTemplate.m_tExpire;
|
||||
m_tCreation = peTemplate.CreationTime;
|
||||
m_tLastMod = peTemplate.LastModificationTime;
|
||||
m_tLastAccess = peTemplate.LastAccessTime;
|
||||
m_tExpire = peTemplate.ExpiryTime;
|
||||
m_bExpires = peTemplate.m_bExpires;
|
||||
m_uUsageCount = peTemplate.m_uUsageCount;
|
||||
|
||||
@ -844,6 +883,16 @@ namespace KeePassLib
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime GetLazyTime(ref string lazyTime, ref DateTime dateTime)
|
||||
{
|
||||
if (lazyTime != null)
|
||||
{
|
||||
dateTime = TimeUtil.DeserializeUtcOrDefault(lazyTime, dateTime);
|
||||
lazyTime = null;
|
||||
}
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PwEntryComparer : IComparer<PwEntry>
|
||||
|
@ -35,6 +35,11 @@ namespace KeePassLib
|
||||
/// </summary>
|
||||
public sealed class PwGroup : ITimeLogger, IStructureItem, IDeepCloneable<PwGroup>
|
||||
{
|
||||
private const int SearchContextStringMaxLength = 50; // Note, doesn't include elipsis, if added
|
||||
public const string SearchContextUuid = "Uuid";
|
||||
public const string SearchContextParentGroup = "Parent Group";
|
||||
public const string SearchContextTags = "Tags";
|
||||
|
||||
public const bool DefaultAutoTypeEnabled = true;
|
||||
public const bool DefaultSearchingEnabled = true;
|
||||
|
||||
@ -42,6 +47,7 @@ namespace KeePassLib
|
||||
private PwObjectList<PwEntry> m_listEntries = new PwObjectList<PwEntry>();
|
||||
private PwGroup m_pParentGroup = null;
|
||||
private DateTime m_tParentGroupLastMod = PwDefs.DtDefaultNow;
|
||||
private string m_tParentGroupLastModLazy;
|
||||
|
||||
private PwUuid m_uuid = PwUuid.Zero;
|
||||
private string m_strName = string.Empty;
|
||||
@ -54,6 +60,12 @@ namespace KeePassLib
|
||||
private DateTime m_tLastMod = PwDefs.DtDefaultNow;
|
||||
private DateTime m_tLastAccess = PwDefs.DtDefaultNow;
|
||||
private DateTime m_tExpire = PwDefs.DtDefaultNow;
|
||||
|
||||
private string m_tCreationLazy;
|
||||
private string m_tLastModLazy;
|
||||
private string m_tLastAccessLazy;
|
||||
private string m_tExpireLazy;
|
||||
|
||||
private bool m_bExpires = false;
|
||||
private ulong m_uUsageCount = 0;
|
||||
|
||||
@ -146,8 +158,13 @@ namespace KeePassLib
|
||||
/// </summary>
|
||||
public DateTime LocationChanged
|
||||
{
|
||||
get { return m_tParentGroupLastMod; }
|
||||
set { m_tParentGroupLastMod = value; }
|
||||
get { return GetLazyTime(ref m_tParentGroupLastModLazy, ref m_tParentGroupLastMod); }
|
||||
set { m_tParentGroupLastMod = value; m_tParentGroupLastModLazy = null; }
|
||||
}
|
||||
|
||||
public void SetLazyLocationChanged(string xmlDateTime)
|
||||
{
|
||||
m_tParentGroupLastModLazy = xmlDateTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -165,17 +182,13 @@ namespace KeePassLib
|
||||
/// </summary>
|
||||
public DateTime CreationTime
|
||||
{
|
||||
get { return m_tCreation; }
|
||||
set { m_tCreation = value; }
|
||||
get { return GetLazyTime(ref m_tCreationLazy, ref m_tCreation); }
|
||||
set { m_tCreation = value; m_tCreationLazy = null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date/time when this group was last modified.
|
||||
/// </summary>
|
||||
public DateTime LastModificationTime
|
||||
public void SetLazyCreationTime(string xmlDateTime)
|
||||
{
|
||||
get { return m_tLastMod; }
|
||||
set { m_tLastMod = value; }
|
||||
m_tCreationLazy = xmlDateTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -183,17 +196,42 @@ namespace KeePassLib
|
||||
/// </summary>
|
||||
public DateTime LastAccessTime
|
||||
{
|
||||
get { return m_tLastAccess; }
|
||||
set { m_tLastAccess = value; }
|
||||
get { return GetLazyTime(ref m_tLastAccessLazy, ref m_tLastAccess); }
|
||||
set { m_tLastAccess = value; m_tLastAccessLazy = null; }
|
||||
}
|
||||
|
||||
public void SetLazyLastAccessTime(string xmlDateTime)
|
||||
{
|
||||
m_tLastAccessLazy = xmlDateTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date/time when this group expires.
|
||||
/// The date/time when this group was last modified.
|
||||
/// </summary>
|
||||
public DateTime LastModificationTime
|
||||
{
|
||||
get { return GetLazyTime(ref m_tLastModLazy, ref m_tLastMod); }
|
||||
set { m_tLastMod = value; m_tLastModLazy = null; }
|
||||
}
|
||||
|
||||
public void SetLazyLastModificationTime(string xmlDateTime)
|
||||
{
|
||||
m_tLastModLazy = xmlDateTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date/time when this group expires. Use the <c>Expires</c> property
|
||||
/// to specify if the group does actually expire or not.
|
||||
/// </summary>
|
||||
public DateTime ExpiryTime
|
||||
{
|
||||
get { return m_tExpire; }
|
||||
set { m_tExpire = value; }
|
||||
get { return GetLazyTime(ref m_tExpireLazy, ref m_tExpire); }
|
||||
set { m_tExpire = value; m_tExpireLazy = null; }
|
||||
}
|
||||
|
||||
public void SetLazyExpiryTime(string xmlDateTime)
|
||||
{
|
||||
m_tExpireLazy = xmlDateTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -344,6 +382,7 @@ namespace KeePassLib
|
||||
pg.m_listEntries = m_listEntries.CloneDeep();
|
||||
pg.m_pParentGroup = m_pParentGroup;
|
||||
pg.m_tParentGroupLastMod = m_tParentGroupLastMod;
|
||||
pg.m_tParentGroupLastModLazy = m_tParentGroupLastModLazy;
|
||||
|
||||
pg.m_strName = m_strName;
|
||||
pg.m_strNotes = m_strNotes;
|
||||
@ -358,6 +397,11 @@ namespace KeePassLib
|
||||
pg.m_bExpires = m_bExpires;
|
||||
pg.m_uUsageCount = m_uUsageCount;
|
||||
|
||||
pg.m_tCreationLazy = m_tCreationLazy;
|
||||
pg.m_tLastModLazy = m_tLastModLazy;
|
||||
pg.m_tLastAccessLazy = m_tLastAccessLazy;
|
||||
pg.m_tExpireLazy = m_tExpireLazy;
|
||||
|
||||
pg.m_bIsExpanded = m_bIsExpanded;
|
||||
pg.m_bVirtual = m_bVirtual;
|
||||
|
||||
@ -398,7 +442,7 @@ namespace KeePassLib
|
||||
{
|
||||
Debug.Assert(pgTemplate != null); if(pgTemplate == null) throw new ArgumentNullException("pgTemplate");
|
||||
|
||||
if(bOnlyIfNewer && (pgTemplate.m_tLastMod < m_tLastMod)) return;
|
||||
if(bOnlyIfNewer && (pgTemplate.LastModificationTime < LastModificationTime)) return;
|
||||
|
||||
// Template UUID should be the same as the current one
|
||||
Debug.Assert(m_uuid.EqualsValue(pgTemplate.m_uuid));
|
||||
@ -413,10 +457,10 @@ namespace KeePassLib
|
||||
m_pwIcon = pgTemplate.m_pwIcon;
|
||||
m_pwCustomIconID = pgTemplate.m_pwCustomIconID;
|
||||
|
||||
m_tCreation = pgTemplate.m_tCreation;
|
||||
m_tLastMod = pgTemplate.m_tLastMod;
|
||||
m_tLastAccess = pgTemplate.m_tLastAccess;
|
||||
m_tExpire = pgTemplate.m_tExpire;
|
||||
m_tCreation = pgTemplate.CreationTime;
|
||||
m_tLastMod = pgTemplate.LastModificationTime;
|
||||
m_tLastAccess = pgTemplate.LastAccessTime;
|
||||
m_tExpire = pgTemplate.ExpiryTime;
|
||||
m_bExpires = pgTemplate.m_bExpires;
|
||||
m_uUsageCount = pgTemplate.m_uUsageCount;
|
||||
|
||||
@ -673,6 +717,18 @@ namespace KeePassLib
|
||||
/// be stored.</param>
|
||||
public void SearchEntries(SearchParameters sp, PwObjectList<PwEntry> listStorage,
|
||||
IStatusLogger slStatus)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Search this group and all subgroups for entries.
|
||||
/// </summary>
|
||||
/// <param name="sp">Specifies the search method.</param>
|
||||
/// <param name="listStorage">Entry list in which the search results will
|
||||
/// be stored.</param>
|
||||
/// <param name="resultContexts">Dictionary that will be populated with text fragments indicating the context of why each entry (keyed by Uuid) was returned</param>
|
||||
public void SearchEntries(SearchParameters sp, PwObjectList<PwEntry> listStorage, IDictionary<PwUuid, String> resultContexts,
|
||||
IStatusLogger slStatus)
|
||||
{
|
||||
if(sp == null) { Debug.Assert(false); return; }
|
||||
if(listStorage == null) { Debug.Assert(false); return; }
|
||||
@ -683,7 +739,7 @@ namespace KeePassLib
|
||||
if((lTerms.Count <= 1) || sp.RegularExpression)
|
||||
{
|
||||
if(slStatus != null) uTotalEntries = GetEntriesCount(true);
|
||||
SearchEntriesSingle(sp, listStorage, slStatus, ref uCurEntries,
|
||||
SearchEntriesSingle(sp, listStorage, resultContexts , slStatus, ref uCurEntries,
|
||||
uTotalEntries);
|
||||
return;
|
||||
}
|
||||
@ -715,7 +771,7 @@ namespace KeePassLib
|
||||
bNegate = (sp.SearchString.Length > 0);
|
||||
}
|
||||
|
||||
if(!pg.SearchEntriesSingle(sp, pgNew.Entries, slStatus,
|
||||
if(!pg.SearchEntriesSingle(sp, pgNew.Entries, resultContexts, slStatus,
|
||||
ref uCurEntries, uTotalEntries))
|
||||
{
|
||||
pg = null;
|
||||
@ -740,7 +796,7 @@ namespace KeePassLib
|
||||
}
|
||||
|
||||
private bool SearchEntriesSingle(SearchParameters spIn,
|
||||
PwObjectList<PwEntry> listStorage, IStatusLogger slStatus,
|
||||
PwObjectList<PwEntry> listStorage, IDictionary<PwUuid, String> resultContexts, IStatusLogger slStatus,
|
||||
ref ulong uCurEntries, ulong uTotalEntries)
|
||||
{
|
||||
SearchParameters sp = spIn.Clone();
|
||||
@ -823,42 +879,42 @@ namespace KeePassLib
|
||||
if(strKey == PwDefs.TitleField)
|
||||
{
|
||||
if(bTitle) SearchEvalAdd(sp, kvp.Value.ReadString(),
|
||||
rx, pe, listStorage);
|
||||
rx, pe, listStorage, resultContexts, strKey);
|
||||
}
|
||||
else if(strKey == PwDefs.UserNameField)
|
||||
{
|
||||
if(bUserName) SearchEvalAdd(sp, kvp.Value.ReadString(),
|
||||
rx, pe, listStorage);
|
||||
rx, pe, listStorage, resultContexts, strKey);
|
||||
}
|
||||
else if(strKey == PwDefs.PasswordField)
|
||||
{
|
||||
if(bPassword) SearchEvalAdd(sp, kvp.Value.ReadString(),
|
||||
rx, pe, listStorage);
|
||||
rx, pe, listStorage, resultContexts, strKey);
|
||||
}
|
||||
else if(strKey == PwDefs.UrlField)
|
||||
{
|
||||
if(bUrl) SearchEvalAdd(sp, kvp.Value.ReadString(),
|
||||
rx, pe, listStorage);
|
||||
rx, pe, listStorage, resultContexts, strKey);
|
||||
}
|
||||
else if(strKey == PwDefs.NotesField)
|
||||
{
|
||||
if(bNotes) SearchEvalAdd(sp, kvp.Value.ReadString(),
|
||||
rx, pe, listStorage);
|
||||
rx, pe, listStorage, resultContexts, strKey);
|
||||
}
|
||||
else if(bOther)
|
||||
SearchEvalAdd(sp, kvp.Value.ReadString(),
|
||||
rx, pe, listStorage);
|
||||
rx, pe, listStorage, resultContexts, strKey);
|
||||
|
||||
// An entry can match only once => break if we have added it
|
||||
if(listStorage.UCount > uInitialResults) break;
|
||||
}
|
||||
|
||||
if(bUuids && (listStorage.UCount == uInitialResults))
|
||||
SearchEvalAdd(sp, pe.Uuid.ToHexString(), rx, pe, listStorage);
|
||||
SearchEvalAdd(sp, pe.Uuid.ToHexString(), rx, pe, listStorage, resultContexts, SearchContextUuid);
|
||||
|
||||
if(bGroupName && (listStorage.UCount == uInitialResults) &&
|
||||
(pe.ParentGroup != null))
|
||||
SearchEvalAdd(sp, pe.ParentGroup.Name, rx, pe, listStorage);
|
||||
SearchEvalAdd(sp, pe.ParentGroup.Name, rx, pe, listStorage, resultContexts, SearchContextParentGroup);
|
||||
|
||||
if(bTags)
|
||||
{
|
||||
@ -866,7 +922,7 @@ namespace KeePassLib
|
||||
{
|
||||
if(listStorage.UCount != uInitialResults) break; // Match
|
||||
|
||||
SearchEvalAdd(sp, strTag, rx, pe, listStorage);
|
||||
SearchEvalAdd(sp, strTag, rx, pe, listStorage, resultContexts, SearchContextTags);
|
||||
}
|
||||
}
|
||||
|
||||
@ -880,28 +936,59 @@ namespace KeePassLib
|
||||
}
|
||||
|
||||
private static void SearchEvalAdd(SearchParameters sp, string strDataField,
|
||||
Regex rx, PwEntry pe, PwObjectList<PwEntry> lResults)
|
||||
Regex rx, PwEntry pe, PwObjectList<PwEntry> lResults, IDictionary<PwUuid, String> resultContexts, string contextFieldName)
|
||||
{
|
||||
bool bMatch = false;
|
||||
int matchPos;
|
||||
|
||||
if(rx == null)
|
||||
bMatch = (strDataField.IndexOf(sp.SearchString,
|
||||
sp.ComparisonMode) >= 0);
|
||||
else bMatch = rx.IsMatch(strDataField);
|
||||
if (rx == null)
|
||||
{
|
||||
matchPos = strDataField.IndexOf(sp.SearchString, sp.ComparisonMode);
|
||||
bMatch = matchPos >= 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var match = rx.Match(strDataField);
|
||||
bMatch = match.Success;
|
||||
matchPos = match.Index;
|
||||
}
|
||||
|
||||
if(!bMatch && (sp.DataTransformationFn != null))
|
||||
{
|
||||
string strCmp = sp.DataTransformationFn(strDataField, pe);
|
||||
if(!object.ReferenceEquals(strCmp, strDataField))
|
||||
{
|
||||
if(rx == null)
|
||||
bMatch = (strCmp.IndexOf(sp.SearchString,
|
||||
sp.ComparisonMode) >= 0);
|
||||
else bMatch = rx.IsMatch(strCmp);
|
||||
if (rx == null)
|
||||
{
|
||||
matchPos = strCmp.IndexOf(sp.SearchString, sp.ComparisonMode);
|
||||
bMatch = matchPos >= 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var match = rx.Match(strCmp);
|
||||
bMatch = match.Success;
|
||||
matchPos = match.Index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(bMatch) lResults.Add(pe);
|
||||
if (bMatch)
|
||||
{
|
||||
lResults.Add(pe);
|
||||
|
||||
if (resultContexts != null)
|
||||
{
|
||||
// Trim the value if necessary
|
||||
var contextString = strDataField;
|
||||
if (contextString.Length > SearchContextStringMaxLength)
|
||||
{
|
||||
// Start 10% before actual data, and don't run over
|
||||
var startPos = Math.Min(matchPos - (SearchContextStringMaxLength / 10), contextString.Length - SearchContextStringMaxLength);
|
||||
contextString = "… " + contextString.Substring(startPos, SearchContextStringMaxLength) + ((startPos + SearchContextStringMaxLength < contextString.Length) ? " …" : null);
|
||||
}
|
||||
resultContexts[pe.Uuid] = contextFieldName + ": " + contextString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> BuildEntryTagsList()
|
||||
@ -1422,6 +1509,16 @@ namespace KeePassLib
|
||||
}
|
||||
m_listGroups.Clear();
|
||||
}
|
||||
|
||||
private DateTime GetLazyTime(ref string lazyTime, ref DateTime dateTime)
|
||||
{
|
||||
if (lazyTime != null)
|
||||
{
|
||||
dateTime = TimeUtil.DeserializeUtcOrDefault(lazyTime, m_tLastMod);
|
||||
lazyTime = null;
|
||||
}
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PwGroupComparer : IComparer<PwGroup>
|
||||
|
@ -497,19 +497,19 @@ namespace KeePassLib.Serialization
|
||||
Debug.Assert(tl != null);
|
||||
|
||||
if(xr.Name == ElemLastModTime)
|
||||
tl.LastModificationTime = ReadTime(xr);
|
||||
tl.SetLazyLastModificationTime(ReadString(xr));
|
||||
else if(xr.Name == ElemCreationTime)
|
||||
tl.CreationTime = ReadTime(xr);
|
||||
tl.SetLazyCreationTime(ReadString(xr));
|
||||
else if(xr.Name == ElemLastAccessTime)
|
||||
tl.LastAccessTime = ReadTime(xr);
|
||||
tl.SetLazyLastAccessTime(ReadString(xr));
|
||||
else if(xr.Name == ElemExpiryTime)
|
||||
tl.ExpiryTime = ReadTime(xr);
|
||||
tl.SetLazyExpiryTime(ReadString(xr));
|
||||
else if(xr.Name == ElemExpires)
|
||||
tl.Expires = ReadBool(xr, false);
|
||||
else if(xr.Name == ElemUsageCount)
|
||||
tl.UsageCount = ReadULong(xr, 0);
|
||||
else if(xr.Name == ElemLocationChanged)
|
||||
tl.LocationChanged = ReadTime(xr);
|
||||
tl.SetLazyLocationChanged(ReadString(xr));
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
|
@ -167,6 +167,22 @@ namespace KeePassLib.Utility
|
||||
return bResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a UTC XML DateTime to a local time, or returns defaultValue if it could not be parsed
|
||||
/// </summary>
|
||||
public static DateTime DeserializeUtcOrDefault(string xmlDateTimeString, DateTime defaultValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
return System.Xml.XmlConvert.ToDateTime(xmlDateTimeString, System.Xml.XmlDateTimeSerializationMode.Local);
|
||||
}
|
||||
catch(FormatException)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static DateTime? m_dtUnixRoot = null;
|
||||
public static DateTime ConvertUnixTime(double dtUnix)
|
||||
{
|
||||
|
@ -15,8 +15,9 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using System;
|
||||
using KeePassLib;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Collections;
|
||||
using KeePassLib.Interfaces;
|
||||
using KeePassLib.Utility;
|
||||
@ -40,9 +41,9 @@ namespace keepass2android
|
||||
{
|
||||
SearchParameters sp = new SearchParameters {SearchString = str};
|
||||
|
||||
return Search(database, sp);
|
||||
return Search(database, sp, null);
|
||||
}
|
||||
public PwGroup Search(Database database, SearchParameters sp)
|
||||
public PwGroup Search(Database database, SearchParameters sp, IDictionary<PwUuid, String> resultContexts)
|
||||
{
|
||||
|
||||
if(sp.RegularExpression) // Validate regular expression
|
||||
@ -56,7 +57,7 @@ namespace keepass2android
|
||||
PwObjectList<PwEntry> listResults = pgResults.Entries;
|
||||
|
||||
|
||||
database.Root.SearchEntries(sp, listResults, new NullStatusLogger());
|
||||
database.Root.SearchEntries(sp, listResults, resultContexts, new NullStatusLogger());
|
||||
|
||||
|
||||
return pgResults;
|
||||
|
@ -160,9 +160,9 @@ namespace keepass2android
|
||||
|
||||
}
|
||||
|
||||
public PwGroup Search(SearchParameters searchParams)
|
||||
public PwGroup Search(SearchParameters searchParams, IDictionary<PwUuid, String> resultContexts)
|
||||
{
|
||||
return SearchHelper.Search(this, searchParams);
|
||||
return SearchHelper.Search(this, searchParams, resultContexts);
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,6 +18,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
@ -151,7 +152,7 @@ namespace keepass2android
|
||||
Finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String getDateTime(DateTime dt) {
|
||||
return dt.ToString ("g", CultureInfo.CurrentUICulture);
|
||||
@ -178,16 +179,12 @@ namespace keepass2android
|
||||
extraGroup.RemoveAllViews();
|
||||
}
|
||||
bool hasExtraFields = false;
|
||||
foreach (KeyValuePair<string, ProtectedString> pair in Entry.Strings)
|
||||
foreach (var view in from pair in Entry.Strings where !PwDefs.IsStandardField(pair.Key) orderby pair.Key
|
||||
select CreateEditSection(pair.Key, pair.Value.ReadString()))
|
||||
{
|
||||
String key = pair.Key;
|
||||
if (!PwDefs.IsStandardField(key))
|
||||
{
|
||||
//View view = new EntrySection(this, null, key, pair.Value.ReadString());
|
||||
View view = CreateEditSection(key, pair.Value.ReadString());
|
||||
extraGroup.AddView(view);
|
||||
hasExtraFields = true;
|
||||
}
|
||||
//View view = new EntrySection(this, null, key, pair.Value.ReadString());
|
||||
extraGroup.AddView(view);
|
||||
hasExtraFields = true;
|
||||
}
|
||||
FindViewById(Resource.Id.entry_extra_strings_label).Visibility = hasExtraFields ? ViewStates.Visible : ViewStates.Gone;
|
||||
}
|
||||
@ -276,8 +273,8 @@ namespace keepass2android
|
||||
return Android.Net.Uri.Parse("content://" + AttachmentContentProvider.Authority + "/"
|
||||
+ filename);
|
||||
}
|
||||
return fileUri;
|
||||
}
|
||||
return fileUri;
|
||||
}
|
||||
|
||||
void OpenBinaryFile(Android.Net.Uri uri)
|
||||
{
|
||||
@ -323,17 +320,17 @@ namespace keepass2android
|
||||
builder.SetMessage(GetString(Resource.String.SaveAttachmentDialog_text));
|
||||
|
||||
builder.SetPositiveButton(GetString(Resource.String.SaveAttachmentDialog_save), (dlgSender, dlgEvt) =>
|
||||
{
|
||||
{
|
||||
WriteBinaryToFile(btnSender.Text, false);
|
||||
});
|
||||
|
||||
builder.SetNegativeButton(GetString(Resource.String.SaveAttachmentDialog_open), (dlgSender, dlgEvt) =>
|
||||
{
|
||||
{
|
||||
Android.Net.Uri newUri = WriteBinaryToFile(btnSender.Text, true);
|
||||
if (newUri != null)
|
||||
{
|
||||
if (newUri != null)
|
||||
{
|
||||
OpenBinaryFile(newUri);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Dialog dialog = builder.Create();
|
||||
|
@ -118,7 +118,7 @@ namespace keepass2android
|
||||
entryId = new PwUuid(MemUtil.HexStringToByteArray(uuidBytes));
|
||||
|
||||
State.ParentGroup = null;
|
||||
if (entryId == PwUuid.Zero)
|
||||
if (entryId.EqualsValue(PwUuid.Zero))
|
||||
{
|
||||
String groupId = i.GetStringExtra(KeyParent);
|
||||
|
||||
@ -487,12 +487,12 @@ namespace keepass2android
|
||||
builder.SetMessage(GetString(Resource.String.AskOverwriteBinary));
|
||||
|
||||
builder.SetPositiveButton(GetString(Resource.String.AskOverwriteBinary_yes), (dlgSender, dlgEvt) =>
|
||||
{
|
||||
{
|
||||
AddBinary(filename, true);
|
||||
});
|
||||
|
||||
builder.SetNegativeButton(GetString(Resource.String.AskOverwriteBinary_no), (dlgSender, dlgEvt) =>
|
||||
{
|
||||
{
|
||||
AddBinary(filename, false);
|
||||
});
|
||||
|
||||
@ -532,9 +532,9 @@ namespace keepass2android
|
||||
try
|
||||
{
|
||||
byte[] vBytes = File.ReadAllBytes(filename);
|
||||
ProtectedBinary pb = new ProtectedBinary(false, vBytes);
|
||||
ProtectedBinary pb = new ProtectedBinary(false, vBytes);
|
||||
State.Entry.Binaries.Set(strItem, pb);
|
||||
}
|
||||
}
|
||||
catch(Exception exAttach)
|
||||
{
|
||||
Toast.MakeText(this, GetString(Resource.String.AttachFailed)+" "+exAttach.Message, ToastLength.Long).Show();
|
||||
@ -562,13 +562,13 @@ namespace keepass2android
|
||||
builder.SetMessage(GetString(Resource.String.AskDiscardChanges));
|
||||
|
||||
builder.SetPositiveButton(GetString(Android.Resource.String.Yes), (dlgSender, dlgEvt) =>
|
||||
{
|
||||
Finish();
|
||||
{
|
||||
Finish();
|
||||
|
||||
});
|
||||
|
||||
builder.SetNegativeButton(GetString(Android.Resource.String.No), (dlgSender, dlgEvt) =>
|
||||
{
|
||||
{
|
||||
|
||||
});
|
||||
|
||||
|
@ -194,7 +194,12 @@ namespace keepass2android
|
||||
|
||||
MenuInflater inflater = MenuInflater;
|
||||
inflater.Inflate(Resource.Menu.group, menu);
|
||||
|
||||
var searchManager = (SearchManager)GetSystemService(Context.SearchService);
|
||||
var searchView = (SearchView)menu.FindItem(Resource.Id.menu_search).ActionView;
|
||||
|
||||
searchView.SetSearchableInfo(searchManager.GetSearchableInfo(ComponentName));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -238,8 +243,9 @@ namespace keepass2android
|
||||
SetResult(KeePass.ExitLock);
|
||||
Finish();
|
||||
return true;
|
||||
|
||||
|
||||
case Resource.Id.menu_search:
|
||||
case Resource.Id.menu_search_advanced:
|
||||
OnSearchRequested();
|
||||
return true;
|
||||
|
||||
|
@ -20,6 +20,12 @@
|
||||
android:icon="@android:drawable/ic_menu_search"
|
||||
android:title="@string/menu_search"
|
||||
android:showAsAction="ifRoom"
|
||||
android:actionViewClass="android.widget.SearchView"
|
||||
/>
|
||||
<item android:id="@+id/menu_search_advanced"
|
||||
android:icon="@android:drawable/ic_menu_search"
|
||||
android:title="@string/menu_search_advanced"
|
||||
android:showAsAction="never"
|
||||
/>
|
||||
<item android:id="@+id/menu_lock"
|
||||
android:icon="@android:drawable/ic_lock_lock"
|
||||
|
@ -24,9 +24,16 @@
|
||||
android:icon="@android:drawable/ic_lock_lock"
|
||||
android:title="@string/menu_lock"
|
||||
/>
|
||||
<item android:id="@+id/menu_search"
|
||||
android:icon="@android:drawable/ic_menu_search"
|
||||
android:title="@string/menu_search"
|
||||
<item android:id="@+id/menu_search"
|
||||
android:icon="@android:drawable/ic_menu_search"
|
||||
android:title="@string/menu_search"
|
||||
android:showAsAction="ifRoom"
|
||||
android:actionViewClass="android.widget.SearchView"
|
||||
/>
|
||||
<item android:id="@+id/menu_search_advanced"
|
||||
android:icon="@android:drawable/ic_menu_search"
|
||||
android:title="@string/menu_search_advanced"
|
||||
android:showAsAction="never"
|
||||
/>
|
||||
<item android:id="@+id/menu_app_settings"
|
||||
android:icon="@android:drawable/ic_menu_preferences"
|
||||
|
@ -129,6 +129,7 @@
|
||||
<string name="menu_open">Open</string>
|
||||
<string name="menu_rename">Rename</string>
|
||||
<string name="menu_search">Search</string>
|
||||
<string name="menu_search_advanced">Advanced Search</string>
|
||||
<string name="menu_url">Go to URL</string>
|
||||
<string name="minus">Minus</string>
|
||||
<string name="never">Never</string>
|
||||
|
@ -21,4 +21,9 @@
|
||||
android:label="@string/search_label"
|
||||
android:hint="@string/search_hint"
|
||||
android:searchMode="showSearchLabelAsBadge"
|
||||
android:searchSuggestAuthority="keepass2android.search.SearchProvider"
|
||||
android:searchSuggestSelection=" ?"
|
||||
android:searchSuggestThreshold="2"
|
||||
android:searchSuggestIntentAction="android.intent.action.VIEW"
|
||||
android:searchSuggestIntentData="content://keepass2android.EntryActivity"
|
||||
/>
|
@ -53,13 +53,13 @@ namespace keepass2android
|
||||
|
||||
public Drawable GetIconDrawable (Resources res, PwDatabase db, PwIcon icon, PwUuid customIconId)
|
||||
{
|
||||
if (customIconId != PwUuid.Zero) {
|
||||
if (!customIconId.EqualsValue(PwUuid.Zero)) {
|
||||
return GetIconDrawable (res, db, customIconId);
|
||||
}
|
||||
return GetIconDrawable (res, icon);
|
||||
}
|
||||
|
||||
private static void InitBlank (Resources res)
|
||||
private static void InitBlank (Resources res)
|
||||
{
|
||||
if (_blank == null) {
|
||||
_blank = res.GetDrawable (Resource.Drawable.ic99_blank);
|
||||
@ -85,7 +85,7 @@ namespace keepass2android
|
||||
public Drawable GetIconDrawable (Resources res, PwDatabase db, PwUuid icon)
|
||||
{
|
||||
InitBlank (res);
|
||||
if (icon == PwUuid.Zero) {
|
||||
if (icon.EqualsValue(PwUuid.Zero)) {
|
||||
return _blank;
|
||||
}
|
||||
Drawable draw;
|
||||
|
@ -85,6 +85,7 @@
|
||||
<Compile Include="app\App.cs" />
|
||||
<Compile Include="fileselect\FileSelectActivity.cs" />
|
||||
<Compile Include="fileselect\FileDbHelper.cs" />
|
||||
<Compile Include="search\SearchProvider.cs" />
|
||||
<Compile Include="Utils\Util.cs" />
|
||||
<Compile Include="intents\Intents.cs" />
|
||||
<Compile Include="fileselect\BrowserDialog.cs" />
|
||||
|
307
src/keepass2android/search/SearchProvider.cs
Normal file
307
src/keepass2android/search/SearchProvider.cs
Normal file
@ -0,0 +1,307 @@
|
||||
/*
|
||||
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 System.Linq;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.Res;
|
||||
using Android.Database;
|
||||
using Android.Graphics;
|
||||
using Android.Graphics.Drawables;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
|
||||
using KeePassLib;
|
||||
using KeePassLib.Utility;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace keepass2android.search
|
||||
{
|
||||
[ContentProvider(new [] { SearchProvider.Authority })]
|
||||
public class SearchProvider : ContentProvider
|
||||
{
|
||||
private enum UriMatches
|
||||
{
|
||||
NoMatch = UriMatcher.NoMatch,
|
||||
GetIcon,
|
||||
GetSuggestions
|
||||
}
|
||||
public const string Authority = "keepass2android.search.SearchProvider";
|
||||
|
||||
private const string GetIconPathQuery = "get_icon";
|
||||
private const string IconIdParameter = "IconId";
|
||||
private const string CustomIconUuidParameter = "CustomIconUuid";
|
||||
|
||||
private Database _db;
|
||||
|
||||
private static UriMatcher UriMatcher = BuildUriMatcher();
|
||||
|
||||
static UriMatcher BuildUriMatcher()
|
||||
{
|
||||
var matcher = new UriMatcher(UriMatcher.NoMatch);
|
||||
|
||||
// to get definitions...
|
||||
matcher.AddURI(Authority, GetIconPathQuery, (int)UriMatches.GetIcon);
|
||||
matcher.AddURI(Authority, SearchManager.SuggestUriPathQuery, (int)UriMatches.GetSuggestions);
|
||||
|
||||
return matcher;
|
||||
}
|
||||
|
||||
public override bool OnCreate()
|
||||
{
|
||||
_db = App.Kp2a.GetDb();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Android.Database.ICursor Query(Android.Net.Uri uri, string[] projection, string selection, string[] selectionArgs, string sortOrder)
|
||||
{
|
||||
if (_db.Open) // Can't show suggestions if the database is locked!
|
||||
{
|
||||
switch ((UriMatches)UriMatcher.Match(uri))
|
||||
{
|
||||
case UriMatches.GetSuggestions:
|
||||
var searchString = selectionArgs[0];
|
||||
if (!String.IsNullOrEmpty(searchString))
|
||||
{
|
||||
try
|
||||
{
|
||||
var resultsContexts = new Dictionary<PwUuid, String>();
|
||||
var result = _db.Search(new SearchParameters { SearchString = searchString }, resultsContexts );
|
||||
return new GroupCursor(result, resultsContexts);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("Failed to search for suggestions: " + e.Message);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UriMatches.GetIcon:
|
||||
return null; // This will be handled by OpenAssetFile
|
||||
|
||||
default:
|
||||
return null;
|
||||
//throw new ArgumentException("Unknown Uri: " + uri, "uri");
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override ParcelFileDescriptor OpenFile(Android.Net.Uri uri, string mode)
|
||||
{
|
||||
switch ((UriMatches)UriMatcher.Match(uri))
|
||||
{
|
||||
case UriMatches.GetIcon:
|
||||
var iconId = (PwIcon)Enum.Parse(typeof(PwIcon), uri.GetQueryParameter(IconIdParameter));
|
||||
var customIconUuid = new PwUuid(MemUtil.HexStringToByteArray(uri.GetQueryParameter(CustomIconUuidParameter)));
|
||||
|
||||
var iconDrawable = _db.DrawableFactory.GetIconDrawable(App.Context.Resources, _db.KpDatabase, iconId, customIconUuid) as BitmapDrawable;
|
||||
if (iconDrawable != null)
|
||||
{
|
||||
var pipe = ParcelFileDescriptor.CreatePipe();
|
||||
var outStream = new OutputStreamInvoker(new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]));
|
||||
|
||||
ThreadPool.QueueUserWorkItem(state =>
|
||||
{
|
||||
iconDrawable.Bitmap.Compress(Bitmap.CompressFormat.Png, 100, outStream);
|
||||
outStream.Close();
|
||||
});
|
||||
|
||||
return pipe[0];
|
||||
}
|
||||
|
||||
// Couldn't get an icon for some reason.
|
||||
return null;
|
||||
default:
|
||||
throw new ArgumentException("Unknown Uri: " + uri, "uri");
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetType(Android.Net.Uri uri)
|
||||
{
|
||||
switch ((UriMatches)UriMatcher.Match(uri))
|
||||
{
|
||||
case UriMatches.GetSuggestions:
|
||||
return SearchManager.SuggestMimeType;
|
||||
case UriMatches.GetIcon:
|
||||
return "image/png";
|
||||
|
||||
default:
|
||||
throw new ArgumentException("Unknown Uri: " + uri, "uri");
|
||||
}
|
||||
}
|
||||
|
||||
#region Unimplemented
|
||||
public override int Delete(Android.Net.Uri uri, string selection, string[] selectionArgs)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override Android.Net.Uri Insert(Android.Net.Uri uri, ContentValues values)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override int Update(Android.Net.Uri uri, ContentValues values, string selection, string[] selectionArgs)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
private class GroupCursor : AbstractCursor
|
||||
{
|
||||
private static readonly string[] ColumnNames = new[] { Android.Provider.BaseColumns.Id,
|
||||
SearchManager.SuggestColumnText1,
|
||||
SearchManager.SuggestColumnText2,
|
||||
SearchManager.SuggestColumnIcon1,
|
||||
SearchManager.SuggestColumnIntentDataId,
|
||||
};
|
||||
|
||||
private readonly PwGroup mGroup;
|
||||
private readonly IDictionary<PwUuid, String> mResultContexts;
|
||||
|
||||
public GroupCursor(PwGroup group, IDictionary<PwUuid, String> resultContexts)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(!group.Groups.Any(), "Expecting a flat list of groups");
|
||||
|
||||
mGroup = group;
|
||||
mResultContexts = resultContexts;
|
||||
}
|
||||
|
||||
public override int Count
|
||||
{
|
||||
get { return (int)Math.Min(mGroup.GetEntriesCount(false), int.MaxValue); }
|
||||
}
|
||||
|
||||
public override string[] GetColumnNames()
|
||||
{
|
||||
return ColumnNames;
|
||||
}
|
||||
|
||||
public override FieldType GetType(int column)
|
||||
{
|
||||
switch (column)
|
||||
{
|
||||
case 0: // _ID
|
||||
return FieldType.Integer;
|
||||
default:
|
||||
return base.GetType(column); // Ends up as string
|
||||
}
|
||||
}
|
||||
|
||||
private PwEntry CurrentEntry
|
||||
{
|
||||
get
|
||||
{
|
||||
return mGroup.Entries.GetAt((uint)MPos);
|
||||
}
|
||||
}
|
||||
|
||||
public override long GetLong(int column)
|
||||
{
|
||||
switch (column)
|
||||
{
|
||||
case 0: // _ID
|
||||
return MPos;
|
||||
default:
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetString(int column)
|
||||
{
|
||||
switch (column)
|
||||
{
|
||||
case 0: // _ID
|
||||
return MPos.ToString();
|
||||
case 1: // SuggestColumnText1
|
||||
return CurrentEntry.Strings.ReadSafe(PwDefs.TitleField);
|
||||
case 2: // SuggestColumnText2
|
||||
string context;
|
||||
if (mResultContexts.TryGetValue(CurrentEntry.Uuid, out context))
|
||||
{
|
||||
context = Internationalise(context);
|
||||
return context;
|
||||
}
|
||||
return null;
|
||||
case 3: // SuggestColumnIcon1
|
||||
var builder = new Android.Net.Uri.Builder();
|
||||
builder.Scheme(ContentResolver.SchemeContent);
|
||||
builder.Authority(Authority);
|
||||
builder.Path(GetIconPathQuery);
|
||||
builder.AppendQueryParameter(IconIdParameter, CurrentEntry.IconId.ToString());
|
||||
builder.AppendQueryParameter(CustomIconUuidParameter, CurrentEntry.CustomIconUuid.ToHexString());
|
||||
return builder.Build().ToString();
|
||||
case 4: // SuggestColumnIntentDataId
|
||||
return CurrentEntry.Uuid.ToHexString();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private string Internationalise(string context)
|
||||
{
|
||||
// Some context names can be internationalised.
|
||||
var splitPos = context.IndexOf(':');
|
||||
var rawName = context.Substring(0, splitPos);
|
||||
int intlResourceId = 0;
|
||||
switch (rawName)
|
||||
{
|
||||
case PwDefs.TitleField:
|
||||
// We will already be showing Title, so ignore it entirely so it doesn't double-appear
|
||||
return null;
|
||||
case PwDefs.UserNameField:
|
||||
intlResourceId = Resource.String.entry_user_name;
|
||||
break;
|
||||
case PwDefs.UrlField:
|
||||
intlResourceId = Resource.String.entry_url;
|
||||
break;
|
||||
case PwDefs.NotesField:
|
||||
intlResourceId = Resource.String.entry_comment;
|
||||
break;
|
||||
case PwGroup.SearchContextTags:
|
||||
intlResourceId = Resource.String.entry_tags;
|
||||
break;
|
||||
default:
|
||||
// Other fields aren't part of the default SearchParameters, so we won't ever get them as context anyway
|
||||
break;
|
||||
}
|
||||
|
||||
if (intlResourceId > 0)
|
||||
{
|
||||
return App.Context.GetString(intlResourceId) + context.Substring(splitPos);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
public override bool IsNull(int column)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#region Data types appearing in no columns
|
||||
public override int GetInt(int column) { throw new FormatException(); }
|
||||
public override double GetDouble(int column) { throw new FormatException(); }
|
||||
public override float GetFloat(int column) { throw new FormatException(); }
|
||||
public override short GetShort(int column) { throw new FormatException(); }
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -22,13 +22,14 @@ using Android.OS;
|
||||
using Android.Widget;
|
||||
using keepass2android.view;
|
||||
using KeePassLib;
|
||||
using Android.Support.V4.App;
|
||||
|
||||
namespace keepass2android.search
|
||||
{
|
||||
/// <summary>
|
||||
/// Activity to show search results
|
||||
/// </summary>
|
||||
[Activity (Label = "@string/app_name", Theme="@style/NoTitleBar")]
|
||||
[Activity (Label = "@string/app_name", Theme="@style/NoTitleBar", LaunchMode=Android.Content.PM.LaunchMode.SingleTop)]
|
||||
[MetaData("android.app.searchable",Resource="@xml/searchable")]
|
||||
[IntentFilter(new[]{Intent.ActionSearch}, Categories=new[]{Intent.CategoryDefault})]
|
||||
public class SearchResults : GroupBaseActivity
|
||||
@ -44,24 +45,44 @@ namespace keepass2android.search
|
||||
}
|
||||
|
||||
SetResult(KeePass.ExitNormal);
|
||||
|
||||
|
||||
ProcessIntent(Intent);
|
||||
}
|
||||
|
||||
protected override void OnNewIntent(Intent intent)
|
||||
{
|
||||
ProcessIntent(intent);
|
||||
}
|
||||
|
||||
private void ProcessIntent(Intent intent)
|
||||
{
|
||||
_db = App.Kp2a.GetDb();
|
||||
|
||||
|
||||
// Likely the app has been killed exit the activity
|
||||
if ( ! _db.Open ) {
|
||||
Finish();
|
||||
}
|
||||
|
||||
Query(getSearch(Intent));
|
||||
|
||||
if (intent.Action == Intent.ActionView)
|
||||
{
|
||||
var entryIntent = new Intent(this, typeof(EntryActivity));
|
||||
entryIntent.PutExtra(EntryActivity.KeyEntry, intent.Data.LastPathSegment);
|
||||
|
||||
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)
|
||||
Query(getSearch(intent));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void Query (SearchParameters searchParams)
|
||||
{
|
||||
try {
|
||||
Group = _db.Search (searchParams);
|
||||
Group = _db.Search (searchParams, null);
|
||||
} catch (Exception e) {
|
||||
Toast.MakeText(this,e.Message, ToastLength.Long).Show();
|
||||
Finish();
|
||||
@ -111,8 +132,8 @@ namespace keepass2android.search
|
||||
Finish();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user