mirror of
https://github.com/moparisthebest/keepass2android
synced 2024-12-23 07:28:48 -05:00
Inline Search with suggestions
This commit is contained in:
parent
4f49b073d8
commit
30cbc16051
@ -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;
|
||||
|
||||
@ -706,6 +711,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; }
|
||||
@ -716,7 +733,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;
|
||||
}
|
||||
@ -748,7 +765,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;
|
||||
@ -773,7 +790,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();
|
||||
@ -856,42 +873,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)
|
||||
{
|
||||
@ -899,7 +916,7 @@ namespace KeePassLib
|
||||
{
|
||||
if(listStorage.UCount != uInitialResults) break; // Match
|
||||
|
||||
SearchEvalAdd(sp, strTag, rx, pe, listStorage);
|
||||
SearchEvalAdd(sp, strTag, rx, pe, listStorage, resultContexts, SearchContextTags);
|
||||
}
|
||||
}
|
||||
|
||||
@ -913,28 +930,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()
|
||||
|
@ -125,7 +125,7 @@ namespace keepass2android
|
||||
entryId = new KeePassLib.PwUuid(MemUtil.HexStringToByteArray(uuidBytes));
|
||||
|
||||
State.parentGroup = null;
|
||||
if (entryId == PwUuid.Zero)
|
||||
if (entryId.EqualsValue(PwUuid.Zero))
|
||||
{
|
||||
String groupId = i.GetStringExtra(KEY_PARENT);
|
||||
|
||||
|
@ -197,7 +197,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;
|
||||
}
|
||||
|
||||
@ -241,8 +246,9 @@ namespace keepass2android
|
||||
SetResult(KeePass.EXIT_LOCK);
|
||||
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"
|
||||
|
@ -61,6 +61,7 @@
|
||||
<string name="entry_user_name">User Name</string>
|
||||
<string name="entry_extra_strings">Extra string fields</string>
|
||||
<string name="entry_binaries">File attachments</string>
|
||||
<string name="entry_notes">Notes</string>
|
||||
<string name="error_arc4">The ArcFour stream cipher is not supported.</string>
|
||||
<string name="error_can_not_handle_uri">Keepass2Android cannot handle this uri.</string>
|
||||
<string name="error_could_not_create_group">Error creating group.</string>
|
||||
@ -129,6 +130,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"
|
||||
/>
|
@ -59,13 +59,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);
|
||||
} else {
|
||||
return getIconDrawable (res, icon);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void initBlank (Resources res)
|
||||
{
|
||||
if (blank == null) {
|
||||
@ -92,7 +92,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 = null;
|
||||
|
@ -86,6 +86,7 @@
|
||||
<Compile Include="Database.cs" />
|
||||
<Compile Include="fileselect\FileSelectActivity.cs" />
|
||||
<Compile Include="fileselect\FileDbHelper.cs" />
|
||||
<Compile Include="search\SearchProvider.cs" />
|
||||
<Compile Include="Utils\Util.cs" />
|
||||
<Compile Include="Utils\Interaction.cs" />
|
||||
<Compile Include="intents\Intents.cs" />
|
||||
|
@ -50,9 +50,9 @@ namespace keepass2android
|
||||
SearchParameters sp = new SearchParameters();
|
||||
sp.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
|
||||
@ -67,7 +67,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;
|
||||
|
309
src/keepass2android/search/SearchProvider.cs
Normal file
309
src/keepass2android/search/SearchProvider.cs
Normal file
@ -0,0 +1,309 @@
|
||||
/*
|
||||
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";
|
||||
//public static readonly String AUTHORITY = "keepass2android.search.SearchProvider";
|
||||
//public static readonly Android.Net.Uri CONTENT_URI = Android.Net.Uri.Parse("content://" + AUTHORITY + "/dictionary");
|
||||
|
||||
private Database mDb;
|
||||
|
||||
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()
|
||||
{
|
||||
mDb = App.getDB();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Android.Database.ICursor Query(Android.Net.Uri uri, string[] projection, string selection, string[] selectionArgs, string sortOrder)
|
||||
{
|
||||
if (mDb.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 = mDb.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 = mDb.drawFactory.getIconDrawable(App.Context.Resources, mDb.pm, 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_notes;
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -27,10 +27,11 @@ using Android.Views;
|
||||
using Android.Widget;
|
||||
using keepass2android.view;
|
||||
using KeePassLib;
|
||||
using Android.Support.V4.App;
|
||||
|
||||
namespace keepass2android.search
|
||||
{
|
||||
[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
|
||||
@ -46,20 +47,40 @@ namespace keepass2android.search
|
||||
}
|
||||
|
||||
SetResult(KeePass.EXIT_NORMAL);
|
||||
|
||||
|
||||
ProcessIntent(Intent);
|
||||
}
|
||||
|
||||
protected override void OnNewIntent(Intent intent)
|
||||
{
|
||||
ProcessIntent(intent);
|
||||
}
|
||||
|
||||
private void ProcessIntent(Intent intent)
|
||||
{
|
||||
mDb = App.getDB();
|
||||
|
||||
|
||||
// Likely the app has been killed exit the activity
|
||||
if ( ! mDb.Open ) {
|
||||
if (!mDb.Open)
|
||||
{
|
||||
Finish();
|
||||
}
|
||||
|
||||
query(getSearch(Intent));
|
||||
|
||||
if (intent.Action == Intent.ActionView)
|
||||
{
|
||||
var entryIntent = new Intent(this, typeof(EntryActivity));
|
||||
entryIntent.PutExtra(EntryActivity.KEY_ENTRY, 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 {
|
||||
|
Loading…
Reference in New Issue
Block a user